blob: 51f52bbdb59dea05130b7fa78daf534c070ffdae [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
Alexe9908f72020-05-19 16:04:53 -050034 def __init__(
35 self,
36 errors_class=None,
37 skip_list=None,
38 skip_list_file=None
39 ):
Alexe0c5b9e2019-04-23 18:51:23 -050040 logger_cli.info("# Initializing mapper")
Alex6b633ec2019-06-06 19:44:34 -050041 # init networks and nodes
Alexe0c5b9e2019-04-23 18:51:23 -050042 self.networks = {}
Alexe9908f72020-05-19 16:04:53 -050043 self.nodes = salt_master.get_nodes(
44 skip_list=skip_list,
45 skip_list_file=skip_list_file
46 )
Alex836fac82019-08-22 13:36:16 -050047 self.cluster = salt_master.get_info()
Alexe9908f72020-05-19 16:04:53 -050048 self.domain = salt_master.domain
Alex6b633ec2019-06-06 19:44:34 -050049 # init and pre-populate interfaces
50 self.interfaces = {k: {} for k in self.nodes}
51 # Init errors class
Alexe0c5b9e2019-04-23 18:51:23 -050052 if errors_class:
53 self.errors = errors_class
54 else:
55 logger_cli.debug("... init error logs folder")
56 self.errors = NetworkErrors()
57
Alex836fac82019-08-22 13:36:16 -050058 def prepare_all_maps(self):
59 self.map_network(self.RECLASS)
60 self.map_network(self.RUNTIME)
61 self.map_network(self.CONFIG)
62
Alexe0c5b9e2019-04-23 18:51:23 -050063 # adding net data to tree
64 def _add_data(self, _list, _n, _h, _d):
65 if _n not in _list:
66 _list[_n] = {}
67 _list[_n][_h] = [_d]
68 elif _h not in _list[_n]:
69 # there is no such host, just create it
70 _list[_n][_h] = [_d]
71 else:
72 # there is such host... this is an error
73 self.errors.add_error(
74 self.errors.NET_DUPLICATE_IF,
75 host=_h,
76 dup_if=_d['name']
77 )
78 _list[_n][_h].append(_d)
79
80 # TODO: refactor map creation. Build one map instead of two separate
81 def _map_network_for_host(self, host, if_class, net_list, data):
82 # filter networks for this IF IP
83 _nets = [n for n in net_list.keys() if if_class.ip in n]
84 _masks = [n.netmask for n in _nets]
85 if len(_nets) > 1:
86 # There a multiple network found for this IP, Error
87 self.errors.add_error(
88 self.errors.NET_SUBNET_INTERSECT,
89 host=host,
90 ip=str(if_class.exploded),
91 networks="; ".join([str(_n) for _n in _nets])
92 )
93 # check mask match
94 if len(_nets) > 0 and if_class.netmask not in _masks:
95 self.errors.add_error(
96 self.errors.NET_MASK_MISMATCH,
97 host=host,
98 if_name=data['name'],
99 if_cidr=if_class.exploded,
100 if_mapped_networks=", ".join([str(_n) for _n in _nets])
101 )
102
103 if len(_nets) < 1:
104 self._add_data(net_list, if_class.network, host, data)
105 else:
106 # add all data
107 for net in _nets:
108 self._add_data(net_list, net, host, data)
109
110 return net_list
111
112 def _map_reclass_networks(self):
113 # class uses nodes from self.nodes dict
114 _reclass = {}
115 # Get required pillars
116 salt_master.get_specific_pillar_for_nodes("linux:network")
117 for node in salt_master.nodes.keys():
118 # check if this node
119 if not salt_master.is_node_available(node):
120 continue
121 # get the reclass value
122 _pillar = salt_master.nodes[node]['pillars']['linux']['network']
123 # we should be ready if there is no interface in reclass for a node
Alex92e07ce2019-05-31 16:00:03 -0500124 # for example on APT node
Alexe0c5b9e2019-04-23 18:51:23 -0500125 if 'interface' in _pillar:
126 _pillar = _pillar['interface']
127 else:
128 logger_cli.info(
129 "... node '{}' skipped, no IF section in reclass".format(
130 node
131 )
132 )
133 continue
Alex92e07ce2019-05-31 16:00:03 -0500134
Alex6b633ec2019-06-06 19:44:34 -0500135 # build map based on IPs and save info too
Alex3bc95f62020-03-05 17:00:04 -0600136 for if_name, _dat in _pillar.items():
Alexb3dc8592019-06-11 13:20:36 -0500137 # get proper IF name
138 _if_name = if_name if 'name' not in _dat else _dat['name']
139 # place it
Alex6b633ec2019-06-06 19:44:34 -0500140 if _if_name not in self.interfaces[node]:
141 self.interfaces[node][_if_name] = deepcopy(_network_item)
Alexb3dc8592019-06-11 13:20:36 -0500142 self.interfaces[node][_if_name]['reclass'] = deepcopy(_dat)
Alex6b633ec2019-06-06 19:44:34 -0500143 # map network if any
Alexb3dc8592019-06-11 13:20:36 -0500144 if 'address' in _dat:
Alexe0c5b9e2019-04-23 18:51:23 -0500145 _if = ipaddress.IPv4Interface(
Alexb3dc8592019-06-11 13:20:36 -0500146 _dat['address'] + '/' + _dat['netmask']
Alexe0c5b9e2019-04-23 18:51:23 -0500147 )
Alexb3dc8592019-06-11 13:20:36 -0500148 _dat['name'] = _if_name
149 _dat['ifs'] = [_if]
Alexe0c5b9e2019-04-23 18:51:23 -0500150
151 _reclass = self._map_network_for_host(
152 node,
153 _if,
154 _reclass,
Alexb3dc8592019-06-11 13:20:36 -0500155 _dat
Alexe0c5b9e2019-04-23 18:51:23 -0500156 )
157
158 return _reclass
159
160 def _map_configured_networks(self):
161 # class uses nodes from self.nodes dict
162 _confs = {}
163
Alex92e07ce2019-05-31 16:00:03 -0500164 # TODO: parse /etc/network/interfaces
165
Alexe0c5b9e2019-04-23 18:51:23 -0500166 return _confs
167
168 def _map_runtime_networks(self):
169 # class uses nodes from self.nodes dict
170 _runtime = {}
171 logger_cli.info("# Mapping node runtime network data")
172 salt_master.prepare_script_on_active_nodes("ifs_data.py")
173 _result = salt_master.execute_script_on_active_nodes(
174 "ifs_data.py",
175 args=["json"]
176 )
177 for key in salt_master.nodes.keys():
178 # check if we are to work with this node
179 if not salt_master.is_node_available(key):
180 continue
181 # due to much data to be passed from salt_master,
182 # it is happening in order
183 if key in _result:
184 _text = _result[key]
185 if '{' in _text and '}' in _text:
186 _text = _text[_text.find('{'):]
187 else:
188 raise InvalidReturnException(
189 "Non-json object returned: '{}'".format(
190 _text
191 )
192 )
193 _dict = json.loads(_text[_text.find('{'):])
194 salt_master.nodes[key]['routes'] = _dict.pop("routes")
195 salt_master.nodes[key]['networks'] = _dict
196 else:
197 salt_master.nodes[key]['networks'] = {}
198 salt_master.nodes[key]['routes'] = {}
199 logger_cli.debug("... {} has {} networks".format(
200 key,
201 len(salt_master.nodes[key]['networks'].keys())
202 ))
203 logger_cli.info("-> done collecting networks data")
204
205 logger_cli.info("-> mapping IPs")
206 # match interfaces by IP subnets
Alex3bc95f62020-03-05 17:00:04 -0600207 for host, node_data in salt_master.nodes.items():
Alexe0c5b9e2019-04-23 18:51:23 -0500208 if not salt_master.is_node_available(host):
209 continue
210
Alex3bc95f62020-03-05 17:00:04 -0600211 for net_name, net_data in node_data['networks'].items():
Alexb3dc8592019-06-11 13:20:36 -0500212 # cut net name
213 _i = net_name.find('@')
214 _name = net_name if _i < 0 else net_name[:_i]
Alexe0c5b9e2019-04-23 18:51:23 -0500215 # get ips and calculate subnets
Alexb3dc8592019-06-11 13:20:36 -0500216 if _name in ['lo']:
Alexe0c5b9e2019-04-23 18:51:23 -0500217 # skip the localhost
218 continue
Alex6b633ec2019-06-06 19:44:34 -0500219 else:
220 # add collected data to interface storage
Alexb3dc8592019-06-11 13:20:36 -0500221 if _name not in self.interfaces[host]:
222 self.interfaces[host][_name] = \
Alex6b633ec2019-06-06 19:44:34 -0500223 deepcopy(_network_item)
Alexb3dc8592019-06-11 13:20:36 -0500224 self.interfaces[host][_name]['runtime'] = \
Alex6b633ec2019-06-06 19:44:34 -0500225 deepcopy(net_data)
226
Alexe0c5b9e2019-04-23 18:51:23 -0500227 # get data and make sure that wide mask goes first
228 _ip4s = sorted(
229 net_data['ipv4'],
230 key=lambda s: s[s.index('/'):]
231 )
232 for _ip_str in _ip4s:
233 # create interface class
234 _if = ipaddress.IPv4Interface(_ip_str)
235 # check if this is a VIP
236 # ...all those will have /32 mask
237 net_data['vip'] = None
238 if _if.network.prefixlen == 32:
239 net_data['vip'] = str(_if.exploded)
240 if 'name' not in net_data:
Alexb3dc8592019-06-11 13:20:36 -0500241 net_data['name'] = _name
Alexe0c5b9e2019-04-23 18:51:23 -0500242 if 'ifs' not in net_data:
243 net_data['ifs'] = [_if]
244 # map it
245 _runtime = self._map_network_for_host(
246 host,
247 _if,
248 _runtime,
249 net_data
250 )
251 else:
252 # data is already there, just add VIP
253 net_data['ifs'].append(_if)
254
Alex1839bbf2019-08-22 17:17:21 -0500255 def process_interface(lvl, interface, tree, res):
256 # get childs for each root
257 # tree row item (<if_name>, [<parents>], [<childs>])
258 if lvl not in tree:
259 # - no level - add it
260 tree[lvl] = {}
261 # there is such interface in this level?
262 if interface not in tree[lvl]:
263 # - IF not present
Alexf3dbe862019-10-07 15:17:04 -0500264 _n = ''
265 if interface not in res:
266 _n = 'unknown IF'
267 _p = None
268 _c = None
269 else:
270 # -- get parents, add
271 _p = res[interface]['lower']
272 # -- get childs, add
273 _c = res[interface]['upper']
274
Alex1839bbf2019-08-22 17:17:21 -0500275 # if None, put empty list
276 _p = _p if _p else []
Alex1839bbf2019-08-22 17:17:21 -0500277 # if None, put empty list
278 _c = _c if _c else []
279 tree[lvl].update({
280 interface: {
Alexf3dbe862019-10-07 15:17:04 -0500281 "note": _n,
Alex1839bbf2019-08-22 17:17:21 -0500282 "parents": _p,
283 "children": _c,
284 "size": len(_p) if len(_p) > len(_c) else len(_c)
285 }
286 })
287 for p_if in tree[lvl][interface]["parents"]:
288 # -- cycle: execute process for next parent, lvl-1
289 process_interface(lvl-1, p_if, tree, res)
290 for c_if in tree[lvl][interface]["children"]:
291 # -- cycle: execute process for next child, lvl+1
292 process_interface(lvl+1, c_if, tree, res)
293 else:
294 # - IF present - exit (been here already)
295 return
296
297 def _put(cNet, cIndex, _list):
Alexf3dbe862019-10-07 15:17:04 -0500298 _added = False
299 _actual_index = -1
300 # Check list len
301 _len = len(_list)
302 if cIndex >= _len:
303 # grow list to meet index
304 _list = _list + [''] * (cIndex - _len + 1)
305 _len = len(_list)
306
307 for _cI in range(cIndex, _len):
Alex1839bbf2019-08-22 17:17:21 -0500308 # add child per index
309 # if space is free
310 if not _list[_cI]:
311 _list[_cI] = cNet
Alexf3dbe862019-10-07 15:17:04 -0500312 _added = True
313 _actual_index = _cI
Alex1839bbf2019-08-22 17:17:21 -0500314 break
Alexf3dbe862019-10-07 15:17:04 -0500315 if not _added:
316 # grow list by one entry
317 _list = _list + [cNet]
318 _actual_index = len(_list) - 1
319 return _actual_index, _list
Alex1839bbf2019-08-22 17:17:21 -0500320
321 # build network hierachy
322 nr = node_data['networks']
323 # walk interface tree
324 for _ifname in node_data['networks']:
325 _tree = {}
326 _level = 0
327 process_interface(_level, _ifname, _tree, nr)
328 # save tree for node/if
329 node_data['networks'][_ifname]['tree'] = _tree
330
331 # debug, print built tree
332 # logger_cli.debug("# '{}'".format(_ifname))
Alex3bc95f62020-03-05 17:00:04 -0600333 lvls = list(_tree.keys())
Alex1839bbf2019-08-22 17:17:21 -0500334 lvls.sort()
335 n = len(lvls)
336 m = max([len(_tree[k].keys()) for k in _tree.keys()])
337 matrix = [["" for i in range(m)] for j in range(n)]
338 x = 0
339 while True:
340 _lv = lvls.pop(0)
341 # get all interfaces on this level
Alex3bc95f62020-03-05 17:00:04 -0600342 nets = iter(_tree[_lv].keys())
Alex1839bbf2019-08-22 17:17:21 -0500343 while True:
344 y = 0
345 # get next interface
Alex3bc95f62020-03-05 17:00:04 -0600346 try:
347 _net = next(nets)
348 except StopIteration:
349 break
Alex1839bbf2019-08-22 17:17:21 -0500350 # all nets
351 _a = [_net]
352 # put current interface if this is only one left
353 if not _tree[_lv][_net]['children']:
354 if _net not in matrix[x]:
Alexf3dbe862019-10-07 15:17:04 -0500355 _, matrix[x] = _put(
356 _net,
357 y,
358 matrix[x]
359 )
Alex1839bbf2019-08-22 17:17:21 -0500360 y += 1
361 else:
362 # get all nets with same child
363 for _c in _tree[_lv][_net]['children']:
364 for _o_net in nets:
365 if _c in _tree[_lv][_o_net]['children']:
366 _a.append(_o_net)
367 # flush collected nets
368 for idx in range(len(_a)):
369 if _a[idx] in matrix[x]:
370 # there is such interface on this level
371 # get index
372 _nI = matrix[x].index(_a[idx])
Alexf3dbe862019-10-07 15:17:04 -0500373 _, matrix[x+1] = _put(
374 _c,
375 _nI,
376 matrix[x+1]
377 )
Alex1839bbf2019-08-22 17:17:21 -0500378 else:
379 # there is no such interface
380 # add it
Alexf3dbe862019-10-07 15:17:04 -0500381 _t, matrix[x] = _put(
382 _a[idx],
383 0,
384 matrix[x]
385 )
386 # also, put child
387 _, matrix[x+1] = _put(
388 _c,
389 _t,
390 matrix[x+1]
391 )
Alex1839bbf2019-08-22 17:17:21 -0500392 # remove collected nets from processing
393 if _a[idx] in nets:
394 nets.remove(_a[idx])
395 y += len(_a)
396 if not nets:
397 x += 1
398 break
399 if not lvls:
400 break
401
402 lines = []
403 _columns = [len(max([i for i in li])) for li in matrix]
404 for idx_y in range(m):
405 line = ""
406 for idx_x in range(n):
Alex9b2c1d12020-03-19 09:32:35 -0500407 _len = _columns[idx_x] if _columns[idx_x] else 1
408 _fmt = "{" + ":{}".format(_len) + "} "
Alex1839bbf2019-08-22 17:17:21 -0500409 line += _fmt.format(matrix[idx_x][idx_y])
410 lines.append(line)
411 node_data['networks'][_ifname]['matrix'] = matrix
412 node_data['networks'][_ifname]['lines'] = lines
Alexe0c5b9e2019-04-23 18:51:23 -0500413 return _runtime
414
415 def map_network(self, source):
416 # maps target network using given source
417 _networks = None
418
419 if source == self.RECLASS:
420 _networks = self._map_reclass_networks()
421 elif source == self.CONFIG:
422 _networks = self._map_configured_networks()
423 elif source == self.RUNTIME:
424 _networks = self._map_runtime_networks()
425
426 self.networks[source] = _networks
427 return _networks
428
Alex836fac82019-08-22 13:36:16 -0500429 def create_map(self):
430 """Create all needed elements for map output
Alexe0c5b9e2019-04-23 18:51:23 -0500431
432 :return: none
433 """
434 _runtime = self.networks[self.RUNTIME]
435 _reclass = self.networks[self.RECLASS]
Alex836fac82019-08-22 13:36:16 -0500436
437 # main networks, target vars
438 _map = {}
Alex6b633ec2019-06-06 19:44:34 -0500439 # No matter of proto, at least one IP will be present for the network
Alex836fac82019-08-22 13:36:16 -0500440 # we interested in, since we are to make sure that L3 level
441 # is configured according to reclass model
Alexe0c5b9e2019-04-23 18:51:23 -0500442 for network in _reclass:
443 # shortcuts
444 _net = str(network)
Alex836fac82019-08-22 13:36:16 -0500445 _map[_net] = {}
Alexe0c5b9e2019-04-23 18:51:23 -0500446 if network not in _runtime:
447 # reclass has network that not found in runtime
448 self.errors.add_error(
449 self.errors.NET_NO_RUNTIME_NETWORK,
450 reclass_net=str(network)
451 )
Alex1839bbf2019-08-22 17:17:21 -0500452 logger_cli.warn(
453 "WARN: {}: {}".format(
454 " No runtime network ", str(network)
455 )
456 )
Alexe0c5b9e2019-04-23 18:51:23 -0500457 continue
Alex6b633ec2019-06-06 19:44:34 -0500458 # hostnames
Alexe0c5b9e2019-04-23 18:51:23 -0500459 names = sorted(_runtime[network].keys())
460 for hostname in names:
Alex836fac82019-08-22 13:36:16 -0500461 _notes = []
Alex6b633ec2019-06-06 19:44:34 -0500462 node = hostname.split('.')[0]
Alexe0c5b9e2019-04-23 18:51:23 -0500463 if not salt_master.is_node_available(hostname, log=False):
464 logger_cli.info(
Alex6b633ec2019-06-06 19:44:34 -0500465 " {0:8} {1}".format(node, "node not available")
Alexe0c5b9e2019-04-23 18:51:23 -0500466 )
467 # add non-responsive node erorr
468 self.errors.add_error(
469 self.errors.NET_NODE_NON_RESPONSIVE,
470 host=hostname
471 )
Alex836fac82019-08-22 13:36:16 -0500472 _notes.append(
473 self.errors.get_error_type_text(
474 self.errors.NET_NODE_NON_RESPONSIVE
475 )
476 )
Alexe0c5b9e2019-04-23 18:51:23 -0500477 continue
Alex6b633ec2019-06-06 19:44:34 -0500478 # lookup interface name on node using network CIDR
479 _if_name = _runtime[network][hostname][0]["name"]
Alex836fac82019-08-22 13:36:16 -0500480 _raw = self.interfaces[hostname][_if_name]['runtime']
Alex6b633ec2019-06-06 19:44:34 -0500481 # get proper reclass
482 _r = self.interfaces[hostname][_if_name]['reclass']
Alex6b633ec2019-06-06 19:44:34 -0500483 _if_name_suffix = ""
484 # get the proto value
Alex3b8e5432019-06-11 15:21:59 -0500485 if _r:
486 _if_rc = ""
487 else:
488 self.errors.add_error(
489 self.errors.NET_NODE_UNEXPECTED_IF,
490 host=hostname,
491 if_name=_if_name
492 )
Alex836fac82019-08-22 13:36:16 -0500493 _notes.append(
494 self.errors.get_error_type_text(
495 self.errors.NET_NODE_UNEXPECTED_IF
496 )
497 )
Alex3b8e5432019-06-11 15:21:59 -0500498 _if_rc = "*"
499
Alex6b633ec2019-06-06 19:44:34 -0500500 if "proto" in _r:
501 _proto = _r['proto']
Alexe0c5b9e2019-04-23 18:51:23 -0500502 else:
Alex6b633ec2019-06-06 19:44:34 -0500503 _proto = "-"
Alexe0c5b9e2019-04-23 18:51:23 -0500504
Alex6b633ec2019-06-06 19:44:34 -0500505 if "type" in _r:
506 _if_name_suffix += _r["type"]
507 if "use_interfaces" in _r:
508 _if_name_suffix += "->" + ",".join(_r["use_interfaces"])
509
510 if _if_name_suffix:
511 _if_name_suffix = "({})".format(_if_name_suffix)
512
Alex6b633ec2019-06-06 19:44:34 -0500513 # get gate and routes if proto is static
514 if _proto == 'static':
515 # get the gateway for current net
516 _routes = salt_master.nodes[hostname]['routes']
517 _route = _routes[_net] if _net in _routes else None
Alex6b633ec2019-06-06 19:44:34 -0500518 # get the default gateway
519 if 'default' in _routes:
520 _d_gate = ipaddress.IPv4Address(
521 _routes['default']['gateway']
522 )
523 else:
524 _d_gate = None
Alexb3dc8592019-06-11 13:20:36 -0500525 _d_gate_str = str(_d_gate) if _d_gate else "No default!"
526 # match route with default
527 if not _route:
528 _gate = "?"
529 else:
530 _gate = _route['gateway'] if _route['gateway'] else "-"
Alex6b633ec2019-06-06 19:44:34 -0500531 else:
532 # in case of manual and dhcp, no check possible
533 _gate = "-"
534 _d_gate = "-"
Alex4067f002019-06-11 10:47:16 -0500535 _d_gate_str = "-"
Alex6b633ec2019-06-06 19:44:34 -0500536 # iterate through interfaces
Alexe0c5b9e2019-04-23 18:51:23 -0500537 _a = _runtime[network][hostname]
538 for _host in _a:
539 for _if in _host['ifs']:
Alexe0c5b9e2019-04-23 18:51:23 -0500540 _ip_str = str(_if.exploded)
Alexab232e42019-06-06 19:44:34 -0500541 _gate_error = ""
542 _up_error = ""
543 _mtu_error = ""
Alexe0c5b9e2019-04-23 18:51:23 -0500544
Alexb3dc8592019-06-11 13:20:36 -0500545 # Match gateway
Alexab232e42019-06-06 19:44:34 -0500546 if _proto == 'static':
Alexb3dc8592019-06-11 13:20:36 -0500547 # default reclass gate
Alex6b633ec2019-06-06 19:44:34 -0500548 _r_gate = "-"
549 if "gateway" in _r:
550 _r_gate = _r["gateway"]
Alexb3dc8592019-06-11 13:20:36 -0500551
Alexab232e42019-06-06 19:44:34 -0500552 # if values not match, put *
Alexb3dc8592019-06-11 13:20:36 -0500553 if _gate != _r_gate and _d_gate_str != _r_gate:
554 # if values not match, check if default match
Alex3b8e5432019-06-11 15:21:59 -0500555 self.errors.add_error(
556 self.errors.NET_UNEXPECTED_GATEWAY,
557 host=hostname,
558 if_name=_if_name,
559 ip=_ip_str,
560 gateway=_gate
561 )
Alex836fac82019-08-22 13:36:16 -0500562 _notes.append(
563 self.errors.get_error_type_text(
564 self.errors.NET_UNEXPECTED_GATEWAY
565 )
566 )
Alexab232e42019-06-06 19:44:34 -0500567 _gate_error = "*"
Alexe0c5b9e2019-04-23 18:51:23 -0500568
569 # IF status in reclass
Alex6b633ec2019-06-06 19:44:34 -0500570 _e = "enabled"
Alexab232e42019-06-06 19:44:34 -0500571 if _e not in _r:
Alex3b8e5432019-06-11 15:21:59 -0500572 self.errors.add_error(
573 self.errors.NET_NO_RC_IF_STATUS,
574 host=hostname,
575 if_name=_if_name
576 )
Alex836fac82019-08-22 13:36:16 -0500577 _notes.append(
578 self.errors.get_error_type_text(
579 self.errors.NET_NO_RC_IF_STATUS
580 )
581 )
Alexab232e42019-06-06 19:44:34 -0500582 _up_error = "*"
Alexe0c5b9e2019-04-23 18:51:23 -0500583
Alexe0c5b9e2019-04-23 18:51:23 -0500584 _rc_mtu = _r['mtu'] if 'mtu' in _r else None
Alexab232e42019-06-06 19:44:34 -0500585 _rc_mtu_s = ""
Alexe0c5b9e2019-04-23 18:51:23 -0500586 # check if this is a VIP address
587 # no checks needed if yes.
588 if _host['vip'] != _ip_str:
589 if _rc_mtu:
Alex3b8e5432019-06-11 15:21:59 -0500590 _rc_mtu_s = str(_rc_mtu)
Alexe0c5b9e2019-04-23 18:51:23 -0500591 # if there is an MTU value, match it
592 if _host['mtu'] != _rc_mtu_s:
593 self.errors.add_error(
594 self.errors.NET_MTU_MISMATCH,
595 host=hostname,
Alex6b633ec2019-06-06 19:44:34 -0500596 if_name=_if_name,
Alexe0c5b9e2019-04-23 18:51:23 -0500597 if_cidr=_ip_str,
598 reclass_mtu=_rc_mtu,
599 runtime_mtu=_host['mtu']
600 )
Alex836fac82019-08-22 13:36:16 -0500601 _notes.append(
602 self.errors.get_error_type_text(
603 self.errors.NET_MTU_MISMATCH
604 )
605 )
Alexb3dc8592019-06-11 13:20:36 -0500606 _rc_mtu_s = "/" + _rc_mtu_s
Alexab232e42019-06-06 19:44:34 -0500607 _mtu_error = "*"
608 else:
609 # empty the matched value
610 _rc_mtu_s = ""
Alex3b8e5432019-06-11 15:21:59 -0500611 elif _host['mtu'] != '1500' and \
612 _proto not in ["-", "dhcp"]:
Alexe0c5b9e2019-04-23 18:51:23 -0500613 # there is no MTU value in reclass
614 # and runtime value is not default
615 self.errors.add_error(
616 self.errors.NET_MTU_EMPTY,
617 host=hostname,
Alex6b633ec2019-06-06 19:44:34 -0500618 if_name=_if_name,
Alexe0c5b9e2019-04-23 18:51:23 -0500619 if_cidr=_ip_str,
620 if_mtu=_host['mtu']
621 )
Alex836fac82019-08-22 13:36:16 -0500622 _notes.append(
623 self.errors.get_error_type_text(
624 self.errors.NET_MTU_EMPTY
625 )
626 )
Alexab232e42019-06-06 19:44:34 -0500627 _mtu_error = "*"
Alexe0c5b9e2019-04-23 18:51:23 -0500628 else:
629 # this is a VIP
Alex6b633ec2019-06-06 19:44:34 -0500630 _if_name = " "*7
Alex6b633ec2019-06-06 19:44:34 -0500631 _if_name_suffix = ""
Alexe0c5b9e2019-04-23 18:51:23 -0500632 _ip_str += " VIP"
Alex836fac82019-08-22 13:36:16 -0500633 # Save all data
634 _values = {
635 "interface": _if_name,
636 "interface_error": _if_rc,
637 "interface_note": _if_name_suffix,
Alex1839bbf2019-08-22 17:17:21 -0500638 "interface_map": "\n".join(_host['lines']),
639 "interface_matrix": _host['matrix'],
Alex836fac82019-08-22 13:36:16 -0500640 "ip_address": _ip_str,
641 "address_type": _proto,
642 "rt_mtu": _host['mtu'],
643 "rc_mtu": _rc_mtu_s,
644 "mtu_error": _mtu_error,
645 "status": _host['state'],
646 "status_error": _up_error,
647 "subnet_gateway": _gate,
648 "subnet_gateway_error": _gate_error,
649 "default_gateway": _d_gate_str,
650 "raw_data": _raw,
651 "error_note": " and ".join(_notes)
652 }
653 if node in _map[_net]:
654 # add if to host
655 _map[_net][node].append(_values)
656 else:
657 _map[_net][node] = [_values]
658 _notes = []
659
660 # save map
661 self.map = _map
662 # other runtime networks found
663 # docker, etc
664
665 return
666
667 def print_map(self):
668 """
669 Create text report for CLI
670
671 :return: none
672 """
673 logger_cli.info("# Networks")
674 logger_cli.info(
675 " {0:8} {1:25} {2:25} {3:6} {4:10} {5:10} {6}/{7}".format(
676 "Host",
677 "IF",
678 "IP",
679 "Proto",
680 "MTU",
681 "State",
682 "Gate",
683 "Def.Gate"
684 )
685 )
686 for network in self.map.keys():
687 logger_cli.info("-> {}".format(network))
688 for hostname in self.map[network].keys():
689 node = hostname.split('.')[0]
690 _n = self.map[network][hostname]
691 for _i in _n:
692 # Host IF IP Proto MTU State Gate Def.Gate
693 _text = "{:7} {:17} {:25} {:6} {:10} " \
694 "{:10} {} / {}".format(
695 _i['interface'] + _i['interface_error'],
696 _i['interface_note'],
697 _i['ip_address'],
698 _i['address_type'],
699 _i['rt_mtu'] + _i['rc_mtu'] + _i['mtu_error'],
700 _i['status'] + _i['status_error'],
701 _i['subnet_gateway'] +
702 _i['subnet_gateway_error'],
703 _i['default_gateway']
Alexe0c5b9e2019-04-23 18:51:23 -0500704 )
Alexe0c5b9e2019-04-23 18:51:23 -0500705 logger_cli.info(
Alex836fac82019-08-22 13:36:16 -0500706 " {0:8} {1}".format(
707 node,
708 _text
709 )
Alexe0c5b9e2019-04-23 18:51:23 -0500710 )
Alex836fac82019-08-22 13:36:16 -0500711
712 # logger_cli.info("\n# Other networks")
713 # _other = [n for n in _runtime if n not in _reclass]
714 # for network in _other:
715 # logger_cli.info("-> {}".format(str(network)))
716 # names = sorted(_runtime[network].keys())
717
718 # for hostname in names:
719 # for _n in _runtime[network][hostname]:
720 # _ifs = [str(ifs.ip) for ifs in _n['ifs']]
721 # _text = "{:25} {:25} {:6} {:10} {}".format(
722 # _n['name'],
723 # ", ".join(_ifs),
724 # "-",
725 # _n['mtu'],
726 # _n['state']
727 # )
728 # logger_cli.info(
729 # " {0:8} {1}".format(hostname.split('.')[0], _text)
730 # )
731 # logger_cli.info("\n")