blob: 982ee895b8bb08331db31e9a28070df6658651e7 [file] [log] [blame]
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