Adding network resource grab script.
Useful during environment setup.
Change-Id: Ic8d4d82cc6f6afb0ac2a6e4285c3b681bb895b6d
diff --git a/_modules/net_checks.py b/_modules/net_checks.py
new file mode 100644
index 0000000..cb0f4fc
--- /dev/null
+++ b/_modules/net_checks.py
@@ -0,0 +1,279 @@
+from os import listdir, path
+from subprocess import Popen,PIPE
+from re import findall as refindall
+from re import search as research
+import salt.utils
+import socket, struct, fcntl
+import logging
+
+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 not mesh 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