Saltstack Official Linux Formula
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

280 line
8.1KB

  1. from os import listdir, path
  2. from subprocess import Popen,PIPE
  3. from re import findall as refindall
  4. from re import search as research
  5. import salt.utils
  6. import socket, struct, fcntl
  7. import logging
  8. logger = logging.getLogger(__name__)
  9. stream = logging.StreamHandler()
  10. logger.addHandler(stream)
  11. def get_ip(iface='ens2'):
  12. ''' Get ip address from an interface if applicable
  13. :param iface: Interface name. Type: str
  14. '''
  15. sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  16. sockfd = sock.fileno()
  17. SIOCGIFADDR = 0x8915
  18. ifreq = struct.pack('16sH14s', iface, socket.AF_INET, '\x00'*14)
  19. try:
  20. res = fcntl.ioctl(sockfd, SIOCGIFADDR, ifreq)
  21. except:
  22. logger.debug("No ip addresses assigned to %s" % iface)
  23. return None
  24. ip = struct.unpack('16sH2x4s8x', res)[2]
  25. return socket.inet_ntoa(ip)
  26. def get_nics():
  27. ''' List nics '''
  28. nics = []
  29. nics_list = listdir('/sys/class/net/')
  30. for nic_name in nics_list:
  31. if research('(br|bond|ens|enp|eth|one|ten|fourty)[0-9]+', nic_name):
  32. # Interface should be in "up" state in order to get carrier status
  33. Popen("ip li set dev " + nic_name + " up", shell=True, stdout=PIPE)
  34. with open("/sys/class/net/" + nic_name + "/carrier", 'r') as f:
  35. try:
  36. carrier = int(f.read())
  37. except:
  38. carrier = 0
  39. bond = ""
  40. if path.isfile("/sys/class/net/" + nic_name + "/master/uevent"):
  41. with open("/sys/class/net/" + nic_name + "/master/uevent", 'r') as f:
  42. for line in f:
  43. sline = line.strip()
  44. if 'INTERFACE=bond' in sline:
  45. bond = sline.split('=')[1]
  46. if len(bond) == 0:
  47. with open("/sys/class/net/" + nic_name + "/address", 'r') as f:
  48. macaddr = f.read().strip()
  49. else:
  50. with open("/proc/net/bonding/" + bond, 'r') as f:
  51. line = f.readline()
  52. if_struct = False
  53. while line:
  54. sline = line.strip()
  55. if 'Slave Interface: ' + nic_name in sline and not if_struct:
  56. if_struct = True
  57. if 'Permanent HW addr: ' in sline and if_struct:
  58. macaddr = sline.split()[3]
  59. break
  60. line = f.readline()
  61. with open("/sys/class/net/" + nic_name + "/mtu", 'r') as f:
  62. mtu = f.read()
  63. ip = str(get_ip(nic_name))
  64. nics.append([nic_name, ip, macaddr, carrier, mtu])
  65. return sorted(nics)
  66. def get_ten_pci():
  67. ''' List ten nics pci addresses '''
  68. nics = []
  69. nics_list = listdir('/sys/class/net/')
  70. for nic_name in nics_list:
  71. if research('ten[0-9]+', nic_name):
  72. with open("/sys/class/net/" + nic_name + "/device/uevent", 'r') as f:
  73. for line in f:
  74. sline = line.strip()
  75. if "PCI_SLOT_NAME=" in sline:
  76. nics.append([nic_name , sline.split("=")[1]])
  77. return sorted(nics)
  78. def mesh_ping(mesh):
  79. ''' One to many ICMP check
  80. :param hosts: Target hosts. Type: list of ip addresses
  81. '''
  82. io = []
  83. minion_id = __salt__['config.get']('id')
  84. for host, hostobj in mesh:
  85. if host == minion_id:
  86. for mesh_net, addr, targets in hostobj:
  87. if addr in targets:
  88. targets.remove(addr)
  89. for tgt in targets:
  90. # This one will run in parallel with everyone else
  91. worker = Popen("ping -c 1 -w 1 -W 1 " + str(tgt), \
  92. shell=True, stdout=PIPE, stderr=PIPE)
  93. ping_out = worker.communicate()[0]
  94. if worker.returncode != 0:
  95. io.append(mesh_net + ': ' + addr + ' -> ' + tgt + ': Failed')
  96. return io
  97. def minion_list():
  98. ''' List registered minions '''
  99. return listdir('/etc/salt/pki/master/minions/')
  100. def verify_addresses():
  101. ''' Verify addresses taken from pillars '''
  102. nodes = nodes_addresses()
  103. verifier = {}
  104. failed = []
  105. for node, nodeobj in nodes:
  106. for item in nodeobj:
  107. addr = item[1]
  108. if addr in verifier:
  109. failed.append([node,verifier[addr],addr])
  110. else:
  111. verifier[addr] = node
  112. if failed:
  113. logger.error("FAILED. Duplicates found")
  114. logger.error(failed)
  115. return False
  116. else:
  117. logger.setLevel(logging.INFO)
  118. logger.info(["PASSED"])
  119. return True
  120. def nodes_addresses():
  121. ''' List servers addresses '''
  122. compound = 'linux:network:interface'
  123. out = __salt__['saltutil.cmd']( tgt='I@' + compound,
  124. tgt_type='compound',
  125. fun='pillar.get',
  126. arg=[compound],
  127. timeout=10
  128. ) or None
  129. servers = []
  130. for minion in minion_list():
  131. addresses = []
  132. if minion in out:
  133. ifaces = out[minion]['ret']
  134. for iface in ifaces:
  135. ifobj = ifaces[iface]
  136. if ifobj['enabled'] and 'address' in ifobj:
  137. if 'mesh' in ifobj:
  138. mesh = ifobj['mesh']
  139. else:
  140. mesh = 'default'
  141. addresses.append([mesh, ifobj['address']])
  142. servers.append([minion,addresses])
  143. return servers
  144. def get_mesh():
  145. ''' Build addresses mesh '''
  146. full_mesh = {}
  147. nodes = nodes_addresses()
  148. for node, nodeobj in nodes:
  149. for item in nodeobj:
  150. mesh = item[0]
  151. addr = item[1]
  152. if not mesh in full_mesh:
  153. full_mesh[mesh] = []
  154. full_mesh[mesh].append(addr)
  155. for node, nodeobj in nodes:
  156. for item in nodeobj:
  157. mesh = item[0]
  158. tgts = full_mesh[mesh]
  159. item.append(tgts)
  160. return nodes
  161. def ping_check():
  162. ''' Ping addresses in a mesh '''
  163. mesh = get_mesh()
  164. out = __salt__['saltutil.cmd']( tgt='*',
  165. tgt_type='glob',
  166. fun='net_checks.mesh_ping',
  167. arg=[mesh],
  168. timeout=10
  169. ) or None
  170. failed = []
  171. if out:
  172. for minion in out:
  173. ret = out[minion]['ret']
  174. if ret:
  175. failed.append(ret)
  176. else:
  177. failed = ["No response from minions"]
  178. if failed:
  179. logger.error("FAILED")
  180. logger.error('\n'.join(str(x) for x in failed))
  181. return False
  182. else:
  183. logger.setLevel(logging.INFO)
  184. logger.info(["PASSED"])
  185. return True
  186. def get_nics_csv(delim=","):
  187. ''' List nics in csv format
  188. :param delim: Delimiter char. Type: str
  189. '''
  190. header = "server,nic_name,ip_addr,mac_addr,link,chassis_id,chassis_name,port_mac,port_descr\n"
  191. io = ""
  192. # Try to reuse lldp output if possible
  193. try:
  194. lldp_info = Popen("lldpcli -f keyvalue s n s", shell=True, stdout=PIPE).communicate()[0]
  195. except:
  196. lldp_info = ""
  197. for nic in get_nics():
  198. lldp = ""
  199. nic_name = nic[0]
  200. if research('(one|ten|fourty)[0-9]+', nic_name):
  201. # Check if we can fetch lldp data for that nic
  202. for line in lldp_info.splitlines():
  203. chassis = 'lldp.' + nic[0] + '.chassis'
  204. port = 'lldp.' + nic[0] + '.port'
  205. if chassis in line or port in line:
  206. lldp += delim + line.split('=')[1]
  207. if not lldp:
  208. lldp = delim + delim + delim + delim
  209. io += __salt__['config.get']('id') + \
  210. delim + nic_name + \
  211. delim + str(nic[1]).strip() + \
  212. delim + str(nic[2]).strip() + \
  213. delim + str(nic[3]).strip() + \
  214. delim + str(nic[4]).strip() + \
  215. lldp + "\n"
  216. return header + io