blob: 59f37816c75a07c026e981b697933c304a8fca51 [file] [log] [blame]
Alexe0c5b9e2019-04-23 18:51:23 -05001import ipaddress
2import json
Alex6b633ec2019-06-06 19:44:34 -05003from copy import deepcopy
Alexe0c5b9e2019-04-23 18:51:23 -05004
5from cfg_checker.common import logger_cli
6from cfg_checker.common.exception import InvalidReturnException
7from cfg_checker.modules.network.network_errors import NetworkErrors
8from cfg_checker.nodes import salt_master
9
10# TODO: use templated approach
11# net interface structure should be the same
12_if_item = {
13 "name": "unnamed interface",
14 "mac": "",
15 "routes": {},
Alex6b633ec2019-06-06 19:44:34 -050016 "proto": "",
Alexe0c5b9e2019-04-23 18:51:23 -050017 "ip": [],
18 "parameters": {}
19}
20
21# collection of configurations
22_network_item = {
23 "runtime": {},
24 "config": {},
25 "reclass": {}
26}
27
28
29class NetworkMapper(object):
30 RECLASS = "reclass"
31 CONFIG = "config"
32 RUNTIME = "runtime"
33
34 def __init__(self, errors_class=None):
35 logger_cli.info("# Initializing mapper")
Alex6b633ec2019-06-06 19:44:34 -050036 # init networks and nodes
Alexe0c5b9e2019-04-23 18:51:23 -050037 self.networks = {}
38 self.nodes = salt_master.get_nodes()
Alex836fac82019-08-22 13:36:16 -050039 self.cluster = salt_master.get_info()
Alex6b633ec2019-06-06 19:44:34 -050040 # init and pre-populate interfaces
41 self.interfaces = {k: {} for k in self.nodes}
42 # Init errors class
Alexe0c5b9e2019-04-23 18:51:23 -050043 if errors_class:
44 self.errors = errors_class
45 else:
46 logger_cli.debug("... init error logs folder")
47 self.errors = NetworkErrors()
48
Alex836fac82019-08-22 13:36:16 -050049 def prepare_all_maps(self):
50 self.map_network(self.RECLASS)
51 self.map_network(self.RUNTIME)
52 self.map_network(self.CONFIG)
53
Alexe0c5b9e2019-04-23 18:51:23 -050054 # adding net data to tree
55 def _add_data(self, _list, _n, _h, _d):
56 if _n not in _list:
57 _list[_n] = {}
58 _list[_n][_h] = [_d]
59 elif _h not in _list[_n]:
60 # there is no such host, just create it
61 _list[_n][_h] = [_d]
62 else:
63 # there is such host... this is an error
64 self.errors.add_error(
65 self.errors.NET_DUPLICATE_IF,
66 host=_h,
67 dup_if=_d['name']
68 )
69 _list[_n][_h].append(_d)
70
71 # TODO: refactor map creation. Build one map instead of two separate
72 def _map_network_for_host(self, host, if_class, net_list, data):
73 # filter networks for this IF IP
74 _nets = [n for n in net_list.keys() if if_class.ip in n]
75 _masks = [n.netmask for n in _nets]
76 if len(_nets) > 1:
77 # There a multiple network found for this IP, Error
78 self.errors.add_error(
79 self.errors.NET_SUBNET_INTERSECT,
80 host=host,
81 ip=str(if_class.exploded),
82 networks="; ".join([str(_n) for _n in _nets])
83 )
84 # check mask match
85 if len(_nets) > 0 and if_class.netmask not in _masks:
86 self.errors.add_error(
87 self.errors.NET_MASK_MISMATCH,
88 host=host,
89 if_name=data['name'],
90 if_cidr=if_class.exploded,
91 if_mapped_networks=", ".join([str(_n) for _n in _nets])
92 )
93
94 if len(_nets) < 1:
95 self._add_data(net_list, if_class.network, host, data)
96 else:
97 # add all data
98 for net in _nets:
99 self._add_data(net_list, net, host, data)
100
101 return net_list
102
103 def _map_reclass_networks(self):
104 # class uses nodes from self.nodes dict
105 _reclass = {}
106 # Get required pillars
107 salt_master.get_specific_pillar_for_nodes("linux:network")
108 for node in salt_master.nodes.keys():
109 # check if this node
110 if not salt_master.is_node_available(node):
111 continue
112 # get the reclass value
113 _pillar = salt_master.nodes[node]['pillars']['linux']['network']
114 # we should be ready if there is no interface in reclass for a node
Alex92e07ce2019-05-31 16:00:03 -0500115 # for example on APT node
Alexe0c5b9e2019-04-23 18:51:23 -0500116 if 'interface' in _pillar:
117 _pillar = _pillar['interface']
118 else:
119 logger_cli.info(
120 "... node '{}' skipped, no IF section in reclass".format(
121 node
122 )
123 )
124 continue
Alex92e07ce2019-05-31 16:00:03 -0500125
Alex6b633ec2019-06-06 19:44:34 -0500126 # build map based on IPs and save info too
Alex3bc95f62020-03-05 17:00:04 -0600127 for if_name, _dat in _pillar.items():
Alexb3dc8592019-06-11 13:20:36 -0500128 # get proper IF name
129 _if_name = if_name if 'name' not in _dat else _dat['name']
130 # place it
Alex6b633ec2019-06-06 19:44:34 -0500131 if _if_name not in self.interfaces[node]:
132 self.interfaces[node][_if_name] = deepcopy(_network_item)
Alexb3dc8592019-06-11 13:20:36 -0500133 self.interfaces[node][_if_name]['reclass'] = deepcopy(_dat)
Alex6b633ec2019-06-06 19:44:34 -0500134 # map network if any
Alexb3dc8592019-06-11 13:20:36 -0500135 if 'address' in _dat:
Alexe0c5b9e2019-04-23 18:51:23 -0500136 _if = ipaddress.IPv4Interface(
Alexb3dc8592019-06-11 13:20:36 -0500137 _dat['address'] + '/' + _dat['netmask']
Alexe0c5b9e2019-04-23 18:51:23 -0500138 )
Alexb3dc8592019-06-11 13:20:36 -0500139 _dat['name'] = _if_name
140 _dat['ifs'] = [_if]
Alexe0c5b9e2019-04-23 18:51:23 -0500141
142 _reclass = self._map_network_for_host(
143 node,
144 _if,
145 _reclass,
Alexb3dc8592019-06-11 13:20:36 -0500146 _dat
Alexe0c5b9e2019-04-23 18:51:23 -0500147 )
148
149 return _reclass
150
151 def _map_configured_networks(self):
152 # class uses nodes from self.nodes dict
153 _confs = {}
154
Alex92e07ce2019-05-31 16:00:03 -0500155 # TODO: parse /etc/network/interfaces
156
Alexe0c5b9e2019-04-23 18:51:23 -0500157 return _confs
158
159 def _map_runtime_networks(self):
160 # class uses nodes from self.nodes dict
161 _runtime = {}
162 logger_cli.info("# Mapping node runtime network data")
163 salt_master.prepare_script_on_active_nodes("ifs_data.py")
164 _result = salt_master.execute_script_on_active_nodes(
165 "ifs_data.py",
166 args=["json"]
167 )
168 for key in salt_master.nodes.keys():
169 # check if we are to work with this node
170 if not salt_master.is_node_available(key):
171 continue
172 # due to much data to be passed from salt_master,
173 # it is happening in order
174 if key in _result:
175 _text = _result[key]
176 if '{' in _text and '}' in _text:
177 _text = _text[_text.find('{'):]
178 else:
179 raise InvalidReturnException(
180 "Non-json object returned: '{}'".format(
181 _text
182 )
183 )
184 _dict = json.loads(_text[_text.find('{'):])
185 salt_master.nodes[key]['routes'] = _dict.pop("routes")
186 salt_master.nodes[key]['networks'] = _dict
187 else:
188 salt_master.nodes[key]['networks'] = {}
189 salt_master.nodes[key]['routes'] = {}
190 logger_cli.debug("... {} has {} networks".format(
191 key,
192 len(salt_master.nodes[key]['networks'].keys())
193 ))
194 logger_cli.info("-> done collecting networks data")
195
196 logger_cli.info("-> mapping IPs")
197 # match interfaces by IP subnets
Alex3bc95f62020-03-05 17:00:04 -0600198 for host, node_data in salt_master.nodes.items():
Alexe0c5b9e2019-04-23 18:51:23 -0500199 if not salt_master.is_node_available(host):
200 continue
201
Alex3bc95f62020-03-05 17:00:04 -0600202 for net_name, net_data in node_data['networks'].items():
Alexb3dc8592019-06-11 13:20:36 -0500203 # cut net name
204 _i = net_name.find('@')
205 _name = net_name if _i < 0 else net_name[:_i]
Alexe0c5b9e2019-04-23 18:51:23 -0500206 # get ips and calculate subnets
Alexb3dc8592019-06-11 13:20:36 -0500207 if _name in ['lo']:
Alexe0c5b9e2019-04-23 18:51:23 -0500208 # skip the localhost
209 continue
Alex6b633ec2019-06-06 19:44:34 -0500210 else:
211 # add collected data to interface storage
Alexb3dc8592019-06-11 13:20:36 -0500212 if _name not in self.interfaces[host]:
213 self.interfaces[host][_name] = \
Alex6b633ec2019-06-06 19:44:34 -0500214 deepcopy(_network_item)
Alexb3dc8592019-06-11 13:20:36 -0500215 self.interfaces[host][_name]['runtime'] = \
Alex6b633ec2019-06-06 19:44:34 -0500216 deepcopy(net_data)
217
Alexe0c5b9e2019-04-23 18:51:23 -0500218 # get data and make sure that wide mask goes first
219 _ip4s = sorted(
220 net_data['ipv4'],
221 key=lambda s: s[s.index('/'):]
222 )
223 for _ip_str in _ip4s:
224 # create interface class
225 _if = ipaddress.IPv4Interface(_ip_str)
226 # check if this is a VIP
227 # ...all those will have /32 mask
228 net_data['vip'] = None
229 if _if.network.prefixlen == 32:
230 net_data['vip'] = str(_if.exploded)
231 if 'name' not in net_data:
Alexb3dc8592019-06-11 13:20:36 -0500232 net_data['name'] = _name
Alexe0c5b9e2019-04-23 18:51:23 -0500233 if 'ifs' not in net_data:
234 net_data['ifs'] = [_if]
235 # map it
236 _runtime = self._map_network_for_host(
237 host,
238 _if,
239 _runtime,
240 net_data
241 )
242 else:
243 # data is already there, just add VIP
244 net_data['ifs'].append(_if)
245
Alex1839bbf2019-08-22 17:17:21 -0500246 def process_interface(lvl, interface, tree, res):
247 # get childs for each root
248 # tree row item (<if_name>, [<parents>], [<childs>])
249 if lvl not in tree:
250 # - no level - add it
251 tree[lvl] = {}
252 # there is such interface in this level?
253 if interface not in tree[lvl]:
254 # - IF not present
Alexf3dbe862019-10-07 15:17:04 -0500255 _n = ''
256 if interface not in res:
257 _n = 'unknown IF'
258 _p = None
259 _c = None
260 else:
261 # -- get parents, add
262 _p = res[interface]['lower']
263 # -- get childs, add
264 _c = res[interface]['upper']
265
Alex1839bbf2019-08-22 17:17:21 -0500266 # if None, put empty list
267 _p = _p if _p else []
Alex1839bbf2019-08-22 17:17:21 -0500268 # if None, put empty list
269 _c = _c if _c else []
270 tree[lvl].update({
271 interface: {
Alexf3dbe862019-10-07 15:17:04 -0500272 "note": _n,
Alex1839bbf2019-08-22 17:17:21 -0500273 "parents": _p,
274 "children": _c,
275 "size": len(_p) if len(_p) > len(_c) else len(_c)
276 }
277 })
278 for p_if in tree[lvl][interface]["parents"]:
279 # -- cycle: execute process for next parent, lvl-1
280 process_interface(lvl-1, p_if, tree, res)
281 for c_if in tree[lvl][interface]["children"]:
282 # -- cycle: execute process for next child, lvl+1
283 process_interface(lvl+1, c_if, tree, res)
284 else:
285 # - IF present - exit (been here already)
286 return
287
288 def _put(cNet, cIndex, _list):
Alexf3dbe862019-10-07 15:17:04 -0500289 _added = False
290 _actual_index = -1
291 # Check list len
292 _len = len(_list)
293 if cIndex >= _len:
294 # grow list to meet index
295 _list = _list + [''] * (cIndex - _len + 1)
296 _len = len(_list)
297
298 for _cI in range(cIndex, _len):
Alex1839bbf2019-08-22 17:17:21 -0500299 # add child per index
300 # if space is free
301 if not _list[_cI]:
302 _list[_cI] = cNet
Alexf3dbe862019-10-07 15:17:04 -0500303 _added = True
304 _actual_index = _cI
Alex1839bbf2019-08-22 17:17:21 -0500305 break
Alexf3dbe862019-10-07 15:17:04 -0500306 if not _added:
307 # grow list by one entry
308 _list = _list + [cNet]
309 _actual_index = len(_list) - 1
310 return _actual_index, _list
Alex1839bbf2019-08-22 17:17:21 -0500311
312 # build network hierachy
313 nr = node_data['networks']
314 # walk interface tree
315 for _ifname in node_data['networks']:
316 _tree = {}
317 _level = 0
318 process_interface(_level, _ifname, _tree, nr)
319 # save tree for node/if
320 node_data['networks'][_ifname]['tree'] = _tree
321
322 # debug, print built tree
323 # logger_cli.debug("# '{}'".format(_ifname))
Alex3bc95f62020-03-05 17:00:04 -0600324 lvls = list(_tree.keys())
Alex1839bbf2019-08-22 17:17:21 -0500325 lvls.sort()
326 n = len(lvls)
327 m = max([len(_tree[k].keys()) for k in _tree.keys()])
328 matrix = [["" for i in range(m)] for j in range(n)]
329 x = 0
330 while True:
331 _lv = lvls.pop(0)
332 # get all interfaces on this level
Alex3bc95f62020-03-05 17:00:04 -0600333 nets = iter(_tree[_lv].keys())
Alex1839bbf2019-08-22 17:17:21 -0500334 while True:
335 y = 0
336 # get next interface
Alex3bc95f62020-03-05 17:00:04 -0600337 try:
338 _net = next(nets)
339 except StopIteration:
340 break
Alex1839bbf2019-08-22 17:17:21 -0500341 # all nets
342 _a = [_net]
343 # put current interface if this is only one left
344 if not _tree[_lv][_net]['children']:
345 if _net not in matrix[x]:
Alexf3dbe862019-10-07 15:17:04 -0500346 _, matrix[x] = _put(
347 _net,
348 y,
349 matrix[x]
350 )
Alex1839bbf2019-08-22 17:17:21 -0500351 y += 1
352 else:
353 # get all nets with same child
354 for _c in _tree[_lv][_net]['children']:
355 for _o_net in nets:
356 if _c in _tree[_lv][_o_net]['children']:
357 _a.append(_o_net)
358 # flush collected nets
359 for idx in range(len(_a)):
360 if _a[idx] in matrix[x]:
361 # there is such interface on this level
362 # get index
363 _nI = matrix[x].index(_a[idx])
Alexf3dbe862019-10-07 15:17:04 -0500364 _, matrix[x+1] = _put(
365 _c,
366 _nI,
367 matrix[x+1]
368 )
Alex1839bbf2019-08-22 17:17:21 -0500369 else:
370 # there is no such interface
371 # add it
Alexf3dbe862019-10-07 15:17:04 -0500372 _t, matrix[x] = _put(
373 _a[idx],
374 0,
375 matrix[x]
376 )
377 # also, put child
378 _, matrix[x+1] = _put(
379 _c,
380 _t,
381 matrix[x+1]
382 )
Alex1839bbf2019-08-22 17:17:21 -0500383 # remove collected nets from processing
384 if _a[idx] in nets:
385 nets.remove(_a[idx])
386 y += len(_a)
387 if not nets:
388 x += 1
389 break
390 if not lvls:
391 break
392
393 lines = []
394 _columns = [len(max([i for i in li])) for li in matrix]
395 for idx_y in range(m):
396 line = ""
397 for idx_x in range(n):
398 _fmt = "{" + ":{}".format(_columns[idx_x]) + "} "
399 line += _fmt.format(matrix[idx_x][idx_y])
400 lines.append(line)
401 node_data['networks'][_ifname]['matrix'] = matrix
402 node_data['networks'][_ifname]['lines'] = lines
Alexe0c5b9e2019-04-23 18:51:23 -0500403 return _runtime
404
405 def map_network(self, source):
406 # maps target network using given source
407 _networks = None
408
409 if source == self.RECLASS:
410 _networks = self._map_reclass_networks()
411 elif source == self.CONFIG:
412 _networks = self._map_configured_networks()
413 elif source == self.RUNTIME:
414 _networks = self._map_runtime_networks()
415
416 self.networks[source] = _networks
417 return _networks
418
Alex836fac82019-08-22 13:36:16 -0500419 def create_map(self):
420 """Create all needed elements for map output
Alexe0c5b9e2019-04-23 18:51:23 -0500421
422 :return: none
423 """
424 _runtime = self.networks[self.RUNTIME]
425 _reclass = self.networks[self.RECLASS]
Alex836fac82019-08-22 13:36:16 -0500426
427 # main networks, target vars
428 _map = {}
Alex6b633ec2019-06-06 19:44:34 -0500429 # No matter of proto, at least one IP will be present for the network
Alex836fac82019-08-22 13:36:16 -0500430 # we interested in, since we are to make sure that L3 level
431 # is configured according to reclass model
Alexe0c5b9e2019-04-23 18:51:23 -0500432 for network in _reclass:
433 # shortcuts
434 _net = str(network)
Alex836fac82019-08-22 13:36:16 -0500435 _map[_net] = {}
Alexe0c5b9e2019-04-23 18:51:23 -0500436 if network not in _runtime:
437 # reclass has network that not found in runtime
438 self.errors.add_error(
439 self.errors.NET_NO_RUNTIME_NETWORK,
440 reclass_net=str(network)
441 )
Alex1839bbf2019-08-22 17:17:21 -0500442 logger_cli.warn(
443 "WARN: {}: {}".format(
444 " No runtime network ", str(network)
445 )
446 )
Alexe0c5b9e2019-04-23 18:51:23 -0500447 continue
Alex6b633ec2019-06-06 19:44:34 -0500448 # hostnames
Alexe0c5b9e2019-04-23 18:51:23 -0500449 names = sorted(_runtime[network].keys())
450 for hostname in names:
Alex836fac82019-08-22 13:36:16 -0500451 _notes = []
Alex6b633ec2019-06-06 19:44:34 -0500452 node = hostname.split('.')[0]
Alexe0c5b9e2019-04-23 18:51:23 -0500453 if not salt_master.is_node_available(hostname, log=False):
454 logger_cli.info(
Alex6b633ec2019-06-06 19:44:34 -0500455 " {0:8} {1}".format(node, "node not available")
Alexe0c5b9e2019-04-23 18:51:23 -0500456 )
457 # add non-responsive node erorr
458 self.errors.add_error(
459 self.errors.NET_NODE_NON_RESPONSIVE,
460 host=hostname
461 )
Alex836fac82019-08-22 13:36:16 -0500462 _notes.append(
463 self.errors.get_error_type_text(
464 self.errors.NET_NODE_NON_RESPONSIVE
465 )
466 )
Alexe0c5b9e2019-04-23 18:51:23 -0500467 continue
Alex6b633ec2019-06-06 19:44:34 -0500468 # lookup interface name on node using network CIDR
469 _if_name = _runtime[network][hostname][0]["name"]
Alex836fac82019-08-22 13:36:16 -0500470 _raw = self.interfaces[hostname][_if_name]['runtime']
Alex6b633ec2019-06-06 19:44:34 -0500471 # get proper reclass
472 _r = self.interfaces[hostname][_if_name]['reclass']
Alex6b633ec2019-06-06 19:44:34 -0500473 _if_name_suffix = ""
474 # get the proto value
Alex3b8e5432019-06-11 15:21:59 -0500475 if _r:
476 _if_rc = ""
477 else:
478 self.errors.add_error(
479 self.errors.NET_NODE_UNEXPECTED_IF,
480 host=hostname,
481 if_name=_if_name
482 )
Alex836fac82019-08-22 13:36:16 -0500483 _notes.append(
484 self.errors.get_error_type_text(
485 self.errors.NET_NODE_UNEXPECTED_IF
486 )
487 )
Alex3b8e5432019-06-11 15:21:59 -0500488 _if_rc = "*"
489
Alex6b633ec2019-06-06 19:44:34 -0500490 if "proto" in _r:
491 _proto = _r['proto']
Alexe0c5b9e2019-04-23 18:51:23 -0500492 else:
Alex6b633ec2019-06-06 19:44:34 -0500493 _proto = "-"
Alexe0c5b9e2019-04-23 18:51:23 -0500494
Alex6b633ec2019-06-06 19:44:34 -0500495 if "type" in _r:
496 _if_name_suffix += _r["type"]
497 if "use_interfaces" in _r:
498 _if_name_suffix += "->" + ",".join(_r["use_interfaces"])
499
500 if _if_name_suffix:
501 _if_name_suffix = "({})".format(_if_name_suffix)
502
Alex6b633ec2019-06-06 19:44:34 -0500503 # get gate and routes if proto is static
504 if _proto == 'static':
505 # get the gateway for current net
506 _routes = salt_master.nodes[hostname]['routes']
507 _route = _routes[_net] if _net in _routes else None
Alex6b633ec2019-06-06 19:44:34 -0500508 # get the default gateway
509 if 'default' in _routes:
510 _d_gate = ipaddress.IPv4Address(
511 _routes['default']['gateway']
512 )
513 else:
514 _d_gate = None
Alexb3dc8592019-06-11 13:20:36 -0500515 _d_gate_str = str(_d_gate) if _d_gate else "No default!"
516 # match route with default
517 if not _route:
518 _gate = "?"
519 else:
520 _gate = _route['gateway'] if _route['gateway'] else "-"
Alex6b633ec2019-06-06 19:44:34 -0500521 else:
522 # in case of manual and dhcp, no check possible
523 _gate = "-"
524 _d_gate = "-"
Alex4067f002019-06-11 10:47:16 -0500525 _d_gate_str = "-"
Alex6b633ec2019-06-06 19:44:34 -0500526 # iterate through interfaces
Alexe0c5b9e2019-04-23 18:51:23 -0500527 _a = _runtime[network][hostname]
528 for _host in _a:
529 for _if in _host['ifs']:
Alexe0c5b9e2019-04-23 18:51:23 -0500530 _ip_str = str(_if.exploded)
Alexab232e42019-06-06 19:44:34 -0500531 _gate_error = ""
532 _up_error = ""
533 _mtu_error = ""
Alexe0c5b9e2019-04-23 18:51:23 -0500534
Alexb3dc8592019-06-11 13:20:36 -0500535 # Match gateway
Alexab232e42019-06-06 19:44:34 -0500536 if _proto == 'static':
Alexb3dc8592019-06-11 13:20:36 -0500537 # default reclass gate
Alex6b633ec2019-06-06 19:44:34 -0500538 _r_gate = "-"
539 if "gateway" in _r:
540 _r_gate = _r["gateway"]
Alexb3dc8592019-06-11 13:20:36 -0500541
Alexab232e42019-06-06 19:44:34 -0500542 # if values not match, put *
Alexb3dc8592019-06-11 13:20:36 -0500543 if _gate != _r_gate and _d_gate_str != _r_gate:
544 # if values not match, check if default match
Alex3b8e5432019-06-11 15:21:59 -0500545 self.errors.add_error(
546 self.errors.NET_UNEXPECTED_GATEWAY,
547 host=hostname,
548 if_name=_if_name,
549 ip=_ip_str,
550 gateway=_gate
551 )
Alex836fac82019-08-22 13:36:16 -0500552 _notes.append(
553 self.errors.get_error_type_text(
554 self.errors.NET_UNEXPECTED_GATEWAY
555 )
556 )
Alexab232e42019-06-06 19:44:34 -0500557 _gate_error = "*"
Alexe0c5b9e2019-04-23 18:51:23 -0500558
559 # IF status in reclass
Alex6b633ec2019-06-06 19:44:34 -0500560 _e = "enabled"
Alexab232e42019-06-06 19:44:34 -0500561 if _e not in _r:
Alex3b8e5432019-06-11 15:21:59 -0500562 self.errors.add_error(
563 self.errors.NET_NO_RC_IF_STATUS,
564 host=hostname,
565 if_name=_if_name
566 )
Alex836fac82019-08-22 13:36:16 -0500567 _notes.append(
568 self.errors.get_error_type_text(
569 self.errors.NET_NO_RC_IF_STATUS
570 )
571 )
Alexab232e42019-06-06 19:44:34 -0500572 _up_error = "*"
Alexe0c5b9e2019-04-23 18:51:23 -0500573
Alexe0c5b9e2019-04-23 18:51:23 -0500574 _rc_mtu = _r['mtu'] if 'mtu' in _r else None
Alexab232e42019-06-06 19:44:34 -0500575 _rc_mtu_s = ""
Alexe0c5b9e2019-04-23 18:51:23 -0500576 # check if this is a VIP address
577 # no checks needed if yes.
578 if _host['vip'] != _ip_str:
579 if _rc_mtu:
Alex3b8e5432019-06-11 15:21:59 -0500580 _rc_mtu_s = str(_rc_mtu)
Alexe0c5b9e2019-04-23 18:51:23 -0500581 # if there is an MTU value, match it
582 if _host['mtu'] != _rc_mtu_s:
583 self.errors.add_error(
584 self.errors.NET_MTU_MISMATCH,
585 host=hostname,
Alex6b633ec2019-06-06 19:44:34 -0500586 if_name=_if_name,
Alexe0c5b9e2019-04-23 18:51:23 -0500587 if_cidr=_ip_str,
588 reclass_mtu=_rc_mtu,
589 runtime_mtu=_host['mtu']
590 )
Alex836fac82019-08-22 13:36:16 -0500591 _notes.append(
592 self.errors.get_error_type_text(
593 self.errors.NET_MTU_MISMATCH
594 )
595 )
Alexb3dc8592019-06-11 13:20:36 -0500596 _rc_mtu_s = "/" + _rc_mtu_s
Alexab232e42019-06-06 19:44:34 -0500597 _mtu_error = "*"
598 else:
599 # empty the matched value
600 _rc_mtu_s = ""
Alex3b8e5432019-06-11 15:21:59 -0500601 elif _host['mtu'] != '1500' and \
602 _proto not in ["-", "dhcp"]:
Alexe0c5b9e2019-04-23 18:51:23 -0500603 # there is no MTU value in reclass
604 # and runtime value is not default
605 self.errors.add_error(
606 self.errors.NET_MTU_EMPTY,
607 host=hostname,
Alex6b633ec2019-06-06 19:44:34 -0500608 if_name=_if_name,
Alexe0c5b9e2019-04-23 18:51:23 -0500609 if_cidr=_ip_str,
610 if_mtu=_host['mtu']
611 )
Alex836fac82019-08-22 13:36:16 -0500612 _notes.append(
613 self.errors.get_error_type_text(
614 self.errors.NET_MTU_EMPTY
615 )
616 )
Alexab232e42019-06-06 19:44:34 -0500617 _mtu_error = "*"
Alexe0c5b9e2019-04-23 18:51:23 -0500618 else:
619 # this is a VIP
Alex6b633ec2019-06-06 19:44:34 -0500620 _if_name = " "*7
Alex6b633ec2019-06-06 19:44:34 -0500621 _if_name_suffix = ""
Alexe0c5b9e2019-04-23 18:51:23 -0500622 _ip_str += " VIP"
Alex836fac82019-08-22 13:36:16 -0500623 # Save all data
624 _values = {
625 "interface": _if_name,
626 "interface_error": _if_rc,
627 "interface_note": _if_name_suffix,
Alex1839bbf2019-08-22 17:17:21 -0500628 "interface_map": "\n".join(_host['lines']),
629 "interface_matrix": _host['matrix'],
Alex836fac82019-08-22 13:36:16 -0500630 "ip_address": _ip_str,
631 "address_type": _proto,
632 "rt_mtu": _host['mtu'],
633 "rc_mtu": _rc_mtu_s,
634 "mtu_error": _mtu_error,
635 "status": _host['state'],
636 "status_error": _up_error,
637 "subnet_gateway": _gate,
638 "subnet_gateway_error": _gate_error,
639 "default_gateway": _d_gate_str,
640 "raw_data": _raw,
641 "error_note": " and ".join(_notes)
642 }
643 if node in _map[_net]:
644 # add if to host
645 _map[_net][node].append(_values)
646 else:
647 _map[_net][node] = [_values]
648 _notes = []
649
650 # save map
651 self.map = _map
652 # other runtime networks found
653 # docker, etc
654
655 return
656
657 def print_map(self):
658 """
659 Create text report for CLI
660
661 :return: none
662 """
663 logger_cli.info("# Networks")
664 logger_cli.info(
665 " {0:8} {1:25} {2:25} {3:6} {4:10} {5:10} {6}/{7}".format(
666 "Host",
667 "IF",
668 "IP",
669 "Proto",
670 "MTU",
671 "State",
672 "Gate",
673 "Def.Gate"
674 )
675 )
676 for network in self.map.keys():
677 logger_cli.info("-> {}".format(network))
678 for hostname in self.map[network].keys():
679 node = hostname.split('.')[0]
680 _n = self.map[network][hostname]
681 for _i in _n:
682 # Host IF IP Proto MTU State Gate Def.Gate
683 _text = "{:7} {:17} {:25} {:6} {:10} " \
684 "{:10} {} / {}".format(
685 _i['interface'] + _i['interface_error'],
686 _i['interface_note'],
687 _i['ip_address'],
688 _i['address_type'],
689 _i['rt_mtu'] + _i['rc_mtu'] + _i['mtu_error'],
690 _i['status'] + _i['status_error'],
691 _i['subnet_gateway'] +
692 _i['subnet_gateway_error'],
693 _i['default_gateway']
Alexe0c5b9e2019-04-23 18:51:23 -0500694 )
Alexe0c5b9e2019-04-23 18:51:23 -0500695 logger_cli.info(
Alex836fac82019-08-22 13:36:16 -0500696 " {0:8} {1}".format(
697 node,
698 _text
699 )
Alexe0c5b9e2019-04-23 18:51:23 -0500700 )
Alex836fac82019-08-22 13:36:16 -0500701
702 # logger_cli.info("\n# Other networks")
703 # _other = [n for n in _runtime if n not in _reclass]
704 # for network in _other:
705 # logger_cli.info("-> {}".format(str(network)))
706 # names = sorted(_runtime[network].keys())
707
708 # for hostname in names:
709 # for _n in _runtime[network][hostname]:
710 # _ifs = [str(ifs.ip) for ifs in _n['ifs']]
711 # _text = "{:25} {:25} {:6} {:10} {}".format(
712 # _n['name'],
713 # ", ".join(_ifs),
714 # "-",
715 # _n['mtu'],
716 # _n['state']
717 # )
718 # logger_cli.info(
719 # " {0:8} {1}".format(hostname.split('.')[0], _text)
720 # )
721 # logger_cli.info("\n")