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