| import fcntl | 
 | import logging | 
 | import socket | 
 | import struct | 
 | from os import listdir, path | 
 | from re import search as research | 
 | from subprocess import PIPE, Popen | 
 |  | 
 | logger = logging.getLogger(__name__) | 
 | stream = logging.StreamHandler() | 
 | logger.addHandler(stream) | 
 |  | 
 |  | 
 | def get_ip(iface='ens2'): | 
 |  | 
 |     ''' Get ip address from an interface if applicable | 
 |  | 
 |     :param iface: Interface name. Type: str | 
 |  | 
 |     ''' | 
 |  | 
 |     sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | 
 |     sockfd = sock.fileno() | 
 |     SIOCGIFADDR = 0x8915 | 
 |     ifreq = struct.pack('16sH14s', iface, socket.AF_INET, '\x00'*14) | 
 |  | 
 |     try: | 
 |         res = fcntl.ioctl(sockfd, SIOCGIFADDR, ifreq) | 
 |     except: | 
 |         logger.debug("No ip addresses assigned to %s" % iface) | 
 |         return None | 
 |  | 
 |     ip = struct.unpack('16sH2x4s8x', res)[2] | 
 |     return socket.inet_ntoa(ip) | 
 |  | 
 |  | 
 | def get_nics(): | 
 |  | 
 |     ''' List nics ''' | 
 |  | 
 |     nics = [] | 
 |     nics_list = listdir('/sys/class/net/') | 
 |     for nic_name in nics_list: | 
 |         if research('(br|bond|ens|enp|eth|one|ten|fourty)[0-9]+', nic_name): | 
 |  | 
 |             # Interface should be in "up" state in order to get carrier status | 
 |             Popen("ip li set dev " + nic_name + " up", shell=True, stdout=PIPE) | 
 |  | 
 |             with open("/sys/class/net/" + nic_name + "/carrier", 'r') as f: | 
 |                 try: | 
 |                     carrier = int(f.read()) | 
 |                 except: | 
 |                     carrier = 0 | 
 |  | 
 |             bond = "" | 
 |             if path.isfile("/sys/class/net/" + nic_name + "/master/uevent"): | 
 |                 with open( | 
 |                     "/sys/class/net/" + nic_name + "/master/uevent", 'r' | 
 |                 ) as f: | 
 |                     for line in f: | 
 |                         sline = line.strip() | 
 |                         if 'INTERFACE=bond' in sline: | 
 |                             bond = sline.split('=')[1] | 
 |             if len(bond) == 0: | 
 |                 with open("/sys/class/net/" + nic_name + "/address", 'r') as f: | 
 |                     macaddr = f.read().strip() | 
 |             else: | 
 |                 with open("/proc/net/bonding/" + bond, 'r') as f: | 
 |                     line = f.readline() | 
 |                     if_struct = False | 
 |                     while line: | 
 |                         sline = line.strip() | 
 |                         if 'Slave Interface: ' + nic_name in sline and not if_struct: | 
 |                             if_struct = True | 
 |                         if 'Permanent HW addr: ' in sline and if_struct: | 
 |                             macaddr = sline.split()[3] | 
 |                             break | 
 |                         line = f.readline() | 
 |  | 
 |             with open("/sys/class/net/" + nic_name + "/mtu", 'r') as f: | 
 |                 mtu = f.read() | 
 |  | 
 |             ip = str(get_ip(nic_name)) | 
 |  | 
 |             nics.append([nic_name, ip, macaddr, carrier, mtu]) | 
 |  | 
 |     return sorted(nics) | 
 |  | 
 |  | 
 | def get_ten_pci(): | 
 |  | 
 |     ''' List ten nics pci addresses ''' | 
 |  | 
 |     nics = [] | 
 |     nics_list = listdir('/sys/class/net/') | 
 |     for nic_name in nics_list: | 
 |         if research('ten[0-9]+', nic_name): | 
 |             with open( | 
 |                 "/sys/class/net/" + nic_name + "/device/uevent", 'r' | 
 |             ) as f: | 
 |                 for line in f: | 
 |                     sline = line.strip() | 
 |                     if "PCI_SLOT_NAME=" in sline: | 
 |                         nics.append([nic_name, sline.split("=")[1]]) | 
 |  | 
 |     return sorted(nics) | 
 |  | 
 |  | 
 | def mesh_ping(mesh): | 
 |  | 
 |     ''' One to many ICMP check | 
 |  | 
 |     :param hosts: Target hosts. Type: list of ip addresses | 
 |  | 
 |     ''' | 
 |  | 
 |     io = [] | 
 |     minion_id = __salt__['config.get']('id') | 
 |  | 
 |     for host, hostobj in mesh: | 
 |         if host == minion_id: | 
 |             for mesh_net, addr, targets in hostobj: | 
 |                 if addr in targets: | 
 |                     targets.remove(addr) | 
 |                 for tgt in targets: | 
 |                     # This one will run in parallel with everyone else | 
 |                     worker = Popen( | 
 |                         "ping -c 1 -w 1 -W 1 " + str(tgt), | 
 |                         shell=True, stdout=PIPE, stderr=PIPE | 
 |                     ) | 
 |                     ping_out = worker.communicate()[0] | 
 |                     if worker.returncode != 0: | 
 |                         io.append( | 
 |                             mesh_net + ': ' + addr + ' -> ' + tgt + ': Failed' | 
 |                         ) | 
 |  | 
 |     return io | 
 |  | 
 |  | 
 | def minion_list(): | 
 |  | 
 |     ''' List registered minions ''' | 
 |  | 
 |     return listdir('/etc/salt/pki/master/minions/') | 
 |  | 
 |  | 
 | def verify_addresses(): | 
 |  | 
 |     ''' Verify addresses taken from pillars ''' | 
 |  | 
 |     nodes = nodes_addresses() | 
 |     verifier = {} | 
 |     failed = [] | 
 |  | 
 |     for node, nodeobj in nodes: | 
 |         for item in nodeobj: | 
 |             addr = item[1] | 
 |             if addr in verifier: | 
 |                 failed.append([node, verifier[addr], addr]) | 
 |             else: | 
 |                 verifier[addr] = node | 
 |  | 
 |     if failed: | 
 |         logger.error("FAILED. Duplicates found") | 
 |         logger.error(failed) | 
 |         return False | 
 |     else: | 
 |         logger.setLevel(logging.INFO) | 
 |         logger.info(["PASSED"]) | 
 |         return True | 
 |  | 
 |  | 
 | def nodes_addresses(): | 
 |  | 
 |     ''' List servers addresses ''' | 
 |  | 
 |     compound = 'linux:network:interface' | 
 |     out = __salt__['saltutil.cmd']( | 
 |         tgt='I@' + compound, | 
 |         tgt_type='compound', | 
 |         fun='pillar.get', | 
 |         arg=[compound], | 
 |         timeout=10 | 
 |     ) or None | 
 |  | 
 |     servers = [] | 
 |     for minion in minion_list(): | 
 |         addresses = [] | 
 |         if minion in out: | 
 |             ifaces = out[minion]['ret'] | 
 |             for iface in ifaces: | 
 |                 ifobj = ifaces[iface] | 
 |                 if ifobj['enabled'] and 'address' in ifobj: | 
 |                     if 'mesh' in ifobj: | 
 |                         mesh = ifobj['mesh'] | 
 |                     else: | 
 |                         mesh = 'default' | 
 |                     addresses.append([mesh, ifobj['address']]) | 
 |             servers.append([minion, addresses]) | 
 |  | 
 |     return servers | 
 |  | 
 |  | 
 | def get_mesh(): | 
 |  | 
 |     ''' Build addresses mesh ''' | 
 |  | 
 |     full_mesh = {} | 
 |     nodes = nodes_addresses() | 
 |  | 
 |     for node, nodeobj in nodes: | 
 |         for item in nodeobj: | 
 |             mesh = item[0] | 
 |             addr = item[1] | 
 |             if mesh not in full_mesh: | 
 |                 full_mesh[mesh] = [] | 
 |             full_mesh[mesh].append(addr) | 
 |  | 
 |     for node, nodeobj in nodes: | 
 |         for item in nodeobj: | 
 |             mesh = item[0] | 
 |             tgts = full_mesh[mesh] | 
 |             item.append(tgts) | 
 |  | 
 |     return nodes | 
 |  | 
 |  | 
 | def ping_check(): | 
 |  | 
 |     ''' Ping addresses in a mesh ''' | 
 |  | 
 |     mesh = get_mesh() | 
 |     out = __salt__['saltutil.cmd']( | 
 |         tgt='*', | 
 |         tgt_type='glob', | 
 |         fun='net_checks.mesh_ping', | 
 |         arg=[mesh], | 
 |         timeout=10 | 
 |     ) or None | 
 |  | 
 |     failed = [] | 
 |  | 
 |     if out: | 
 |         for minion in out: | 
 |             ret = out[minion]['ret'] | 
 |             if ret: | 
 |                 failed.append(ret) | 
 |     else: | 
 |         failed = ["No response from minions"] | 
 |  | 
 |     if failed: | 
 |         logger.error("FAILED") | 
 |         logger.error('\n'.join(str(x) for x in failed)) | 
 |         return False | 
 |     else: | 
 |         logger.setLevel(logging.INFO) | 
 |         logger.info(["PASSED"]) | 
 |         return True | 
 |  | 
 |  | 
 | def get_nics_csv(delim=","): | 
 |  | 
 |     ''' List nics in csv format | 
 |  | 
 |     :param delim: Delimiter char. Type: str | 
 |  | 
 |     ''' | 
 |  | 
 |     header = "server,nic_name,ip_addr,mac_addr,link,chassis_id,chassis_name,port_mac,port_descr\n" | 
 |     io = "" | 
 |  | 
 |     # Try to reuse lldp output if possible | 
 |     try: | 
 |         lldp_info = Popen( | 
 |             "lldpcli -f keyvalue s n s", | 
 |             shell=True, | 
 |             stdout=PIPE | 
 |         ).communicate()[0] | 
 |     except: | 
 |         lldp_info = "" | 
 |  | 
 |     for nic in get_nics(): | 
 |         lldp = "" | 
 |         nic_name = nic[0] | 
 |         if research('(one|ten|fourty)[0-9]+', nic_name): | 
 |             # Check if we can fetch lldp data for that nic | 
 |             for line in lldp_info.splitlines(): | 
 |                 chassis = 'lldp.' + nic[0] + '.chassis' | 
 |                 port = 'lldp.' + nic[0] + '.port' | 
 |                 if chassis in line or port in line: | 
 |                     lldp += delim + line.split('=')[1] | 
 |         if not lldp: | 
 |             lldp = delim + delim + delim + delim | 
 |  | 
 |         io += __salt__['config.get']('id') + \ | 
 |               delim + nic_name + \ | 
 |               delim + str(nic[1]).strip() + \ | 
 |               delim + str(nic[2]).strip() + \ | 
 |               delim + str(nic[3]).strip() + \ | 
 |               delim + str(nic[4]).strip() + \ | 
 |               lldp + "\n" | 
 |  | 
 |     return header + io |