blob: 2e925f20f0ab363cf484ea5b35e54552f62059d9 [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
Alex205546c2020-12-30 19:22:30 -06008from cfg_checker.nodes import SaltNodes, KubeNodes
Alexe0c5b9e2019-04-23 18:51:23 -05009
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,
Alex9a4ad212020-10-01 18:04:25 -050036 config,
Alexe9908f72020-05-19 16:04:53 -050037 errors_class=None,
38 skip_list=None,
39 skip_list_file=None
40 ):
Alexe0c5b9e2019-04-23 18:51:23 -050041 logger_cli.info("# Initializing mapper")
Alex205546c2020-12-30 19:22:30 -060042 self.env_config = config
Alex6b633ec2019-06-06 19:44:34 -050043 # init networks and nodes
Alexe0c5b9e2019-04-23 18:51:23 -050044 self.networks = {}
Alex205546c2020-12-30 19:22:30 -060045 self.nodes = self.master.get_nodes(
Alexe9908f72020-05-19 16:04:53 -050046 skip_list=skip_list,
47 skip_list_file=skip_list_file
48 )
Alex205546c2020-12-30 19:22:30 -060049 self.cluster = self.master.get_info()
50 self.domain = self.master.domain
Alex6b633ec2019-06-06 19:44:34 -050051 # init and pre-populate interfaces
52 self.interfaces = {k: {} for k in self.nodes}
53 # Init errors class
Alexe0c5b9e2019-04-23 18:51:23 -050054 if errors_class:
55 self.errors = errors_class
56 else:
57 logger_cli.debug("... init error logs folder")
58 self.errors = NetworkErrors()
59
Alex836fac82019-08-22 13:36:16 -050060 def prepare_all_maps(self):
61 self.map_network(self.RECLASS)
62 self.map_network(self.RUNTIME)
63 self.map_network(self.CONFIG)
64
Alexe0c5b9e2019-04-23 18:51:23 -050065 # adding net data to tree
66 def _add_data(self, _list, _n, _h, _d):
67 if _n not in _list:
68 _list[_n] = {}
69 _list[_n][_h] = [_d]
70 elif _h not in _list[_n]:
71 # there is no such host, just create it
72 _list[_n][_h] = [_d]
73 else:
74 # there is such host... this is an error
75 self.errors.add_error(
76 self.errors.NET_DUPLICATE_IF,
77 host=_h,
78 dup_if=_d['name']
79 )
80 _list[_n][_h].append(_d)
81
82 # TODO: refactor map creation. Build one map instead of two separate
83 def _map_network_for_host(self, host, if_class, net_list, data):
84 # filter networks for this IF IP
85 _nets = [n for n in net_list.keys() if if_class.ip in n]
86 _masks = [n.netmask for n in _nets]
87 if len(_nets) > 1:
88 # There a multiple network found for this IP, Error
89 self.errors.add_error(
90 self.errors.NET_SUBNET_INTERSECT,
91 host=host,
92 ip=str(if_class.exploded),
93 networks="; ".join([str(_n) for _n in _nets])
94 )
95 # check mask match
96 if len(_nets) > 0 and if_class.netmask not in _masks:
97 self.errors.add_error(
98 self.errors.NET_MASK_MISMATCH,
99 host=host,
100 if_name=data['name'],
101 if_cidr=if_class.exploded,
102 if_mapped_networks=", ".join([str(_n) for _n in _nets])
103 )
104
105 if len(_nets) < 1:
106 self._add_data(net_list, if_class.network, host, data)
107 else:
108 # add all data
109 for net in _nets:
110 self._add_data(net_list, net, host, data)
111
112 return net_list
113
114 def _map_reclass_networks(self):
115 # class uses nodes from self.nodes dict
116 _reclass = {}
117 # Get required pillars
Alex205546c2020-12-30 19:22:30 -0600118 self.master.get_specific_pillar_for_nodes("linux:network")
119 for node in self.master.nodes.keys():
Alexe0c5b9e2019-04-23 18:51:23 -0500120 # check if this node
Alex205546c2020-12-30 19:22:30 -0600121 if not self.master.is_node_available(node):
Alexe0c5b9e2019-04-23 18:51:23 -0500122 continue
123 # get the reclass value
Alex9a4ad212020-10-01 18:04:25 -0500124 _pillar = \
Alex205546c2020-12-30 19:22:30 -0600125 self.master.nodes[node]['pillars']['linux']['network']
Alexe0c5b9e2019-04-23 18:51:23 -0500126 # we should be ready if there is no interface in reclass for a node
Alex92e07ce2019-05-31 16:00:03 -0500127 # for example on APT node
Alexe0c5b9e2019-04-23 18:51:23 -0500128 if 'interface' in _pillar:
129 _pillar = _pillar['interface']
130 else:
131 logger_cli.info(
132 "... node '{}' skipped, no IF section in reclass".format(
133 node
134 )
135 )
136 continue
Alex92e07ce2019-05-31 16:00:03 -0500137
Alex6b633ec2019-06-06 19:44:34 -0500138 # build map based on IPs and save info too
Alex3bc95f62020-03-05 17:00:04 -0600139 for if_name, _dat in _pillar.items():
Alexb3dc8592019-06-11 13:20:36 -0500140 # get proper IF name
141 _if_name = if_name if 'name' not in _dat else _dat['name']
142 # place it
Alex6b633ec2019-06-06 19:44:34 -0500143 if _if_name not in self.interfaces[node]:
144 self.interfaces[node][_if_name] = deepcopy(_network_item)
Alexb3dc8592019-06-11 13:20:36 -0500145 self.interfaces[node][_if_name]['reclass'] = deepcopy(_dat)
Alex6b633ec2019-06-06 19:44:34 -0500146 # map network if any
Alexb3dc8592019-06-11 13:20:36 -0500147 if 'address' in _dat:
Alexe0c5b9e2019-04-23 18:51:23 -0500148 _if = ipaddress.IPv4Interface(
Alexb3dc8592019-06-11 13:20:36 -0500149 _dat['address'] + '/' + _dat['netmask']
Alexe0c5b9e2019-04-23 18:51:23 -0500150 )
Alexb3dc8592019-06-11 13:20:36 -0500151 _dat['name'] = _if_name
152 _dat['ifs'] = [_if]
Alexe0c5b9e2019-04-23 18:51:23 -0500153
154 _reclass = self._map_network_for_host(
155 node,
156 _if,
157 _reclass,
Alexb3dc8592019-06-11 13:20:36 -0500158 _dat
Alexe0c5b9e2019-04-23 18:51:23 -0500159 )
160
161 return _reclass
162
163 def _map_configured_networks(self):
164 # class uses nodes from self.nodes dict
165 _confs = {}
166
Alex92e07ce2019-05-31 16:00:03 -0500167 # TODO: parse /etc/network/interfaces
168
Alexe0c5b9e2019-04-23 18:51:23 -0500169 return _confs
170
171 def _map_runtime_networks(self):
172 # class uses nodes from self.nodes dict
173 _runtime = {}
174 logger_cli.info("# Mapping node runtime network data")
Alex205546c2020-12-30 19:22:30 -0600175 self.master.prepare_script_on_active_nodes("ifs_data.py")
176 _result = self.master.execute_script_on_active_nodes(
Alexe0c5b9e2019-04-23 18:51:23 -0500177 "ifs_data.py",
178 args=["json"]
179 )
Alex205546c2020-12-30 19:22:30 -0600180 for key in self.master.nodes.keys():
Alexe0c5b9e2019-04-23 18:51:23 -0500181 # check if we are to work with this node
Alex205546c2020-12-30 19:22:30 -0600182 if not self.master.is_node_available(key):
Alexe0c5b9e2019-04-23 18:51:23 -0500183 continue
Alex205546c2020-12-30 19:22:30 -0600184 # due to much data to be passed from master,
Alexe0c5b9e2019-04-23 18:51:23 -0500185 # it is happening in order
186 if key in _result:
187 _text = _result[key]
188 if '{' in _text and '}' in _text:
189 _text = _text[_text.find('{'):]
190 else:
191 raise InvalidReturnException(
192 "Non-json object returned: '{}'".format(
193 _text
194 )
195 )
196 _dict = json.loads(_text[_text.find('{'):])
Alex205546c2020-12-30 19:22:30 -0600197 self.master.nodes[key]['routes'] = _dict.pop("routes")
198 self.master.nodes[key]['networks'] = _dict
Alexe0c5b9e2019-04-23 18:51:23 -0500199 else:
Alex205546c2020-12-30 19:22:30 -0600200 self.master.nodes[key]['networks'] = {}
201 self.master.nodes[key]['routes'] = {}
Alexe0c5b9e2019-04-23 18:51:23 -0500202 logger_cli.debug("... {} has {} networks".format(
203 key,
Alex205546c2020-12-30 19:22:30 -0600204 len(self.master.nodes[key]['networks'].keys())
Alexe0c5b9e2019-04-23 18:51:23 -0500205 ))
206 logger_cli.info("-> done collecting networks data")
207
208 logger_cli.info("-> mapping IPs")
209 # match interfaces by IP subnets
Alex205546c2020-12-30 19:22:30 -0600210 for host, node_data in self.master.nodes.items():
211 if not self.master.is_node_available(host):
Alexe0c5b9e2019-04-23 18:51:23 -0500212 continue
213
Alex3bc95f62020-03-05 17:00:04 -0600214 for net_name, net_data in node_data['networks'].items():
Alexb3dc8592019-06-11 13:20:36 -0500215 # cut net name
216 _i = net_name.find('@')
217 _name = net_name if _i < 0 else net_name[:_i]
Alexe0c5b9e2019-04-23 18:51:23 -0500218 # get ips and calculate subnets
Alexb3dc8592019-06-11 13:20:36 -0500219 if _name in ['lo']:
Alexe0c5b9e2019-04-23 18:51:23 -0500220 # skip the localhost
221 continue
Alex6b633ec2019-06-06 19:44:34 -0500222 else:
223 # add collected data to interface storage
Alexb3dc8592019-06-11 13:20:36 -0500224 if _name not in self.interfaces[host]:
225 self.interfaces[host][_name] = \
Alex6b633ec2019-06-06 19:44:34 -0500226 deepcopy(_network_item)
Alexb3dc8592019-06-11 13:20:36 -0500227 self.interfaces[host][_name]['runtime'] = \
Alex6b633ec2019-06-06 19:44:34 -0500228 deepcopy(net_data)
229
Alexe0c5b9e2019-04-23 18:51:23 -0500230 # get data and make sure that wide mask goes first
231 _ip4s = sorted(
232 net_data['ipv4'],
233 key=lambda s: s[s.index('/'):]
234 )
235 for _ip_str in _ip4s:
236 # create interface class
237 _if = ipaddress.IPv4Interface(_ip_str)
238 # check if this is a VIP
239 # ...all those will have /32 mask
240 net_data['vip'] = None
241 if _if.network.prefixlen == 32:
242 net_data['vip'] = str(_if.exploded)
243 if 'name' not in net_data:
Alexb3dc8592019-06-11 13:20:36 -0500244 net_data['name'] = _name
Alexe0c5b9e2019-04-23 18:51:23 -0500245 if 'ifs' not in net_data:
246 net_data['ifs'] = [_if]
247 # map it
248 _runtime = self._map_network_for_host(
249 host,
250 _if,
251 _runtime,
252 net_data
253 )
254 else:
255 # data is already there, just add VIP
256 net_data['ifs'].append(_if)
257
Alex1839bbf2019-08-22 17:17:21 -0500258 def process_interface(lvl, interface, tree, res):
259 # get childs for each root
260 # tree row item (<if_name>, [<parents>], [<childs>])
261 if lvl not in tree:
262 # - no level - add it
263 tree[lvl] = {}
264 # there is such interface in this level?
265 if interface not in tree[lvl]:
266 # - IF not present
Alexf3dbe862019-10-07 15:17:04 -0500267 _n = ''
268 if interface not in res:
269 _n = 'unknown IF'
270 _p = None
271 _c = None
272 else:
273 # -- get parents, add
274 _p = res[interface]['lower']
275 # -- get childs, add
276 _c = res[interface]['upper']
277
Alex1839bbf2019-08-22 17:17:21 -0500278 # if None, put empty list
279 _p = _p if _p else []
Alex1839bbf2019-08-22 17:17:21 -0500280 # if None, put empty list
281 _c = _c if _c else []
282 tree[lvl].update({
283 interface: {
Alexf3dbe862019-10-07 15:17:04 -0500284 "note": _n,
Alex1839bbf2019-08-22 17:17:21 -0500285 "parents": _p,
286 "children": _c,
287 "size": len(_p) if len(_p) > len(_c) else len(_c)
288 }
289 })
290 for p_if in tree[lvl][interface]["parents"]:
291 # -- cycle: execute process for next parent, lvl-1
292 process_interface(lvl-1, p_if, tree, res)
293 for c_if in tree[lvl][interface]["children"]:
294 # -- cycle: execute process for next child, lvl+1
295 process_interface(lvl+1, c_if, tree, res)
296 else:
297 # - IF present - exit (been here already)
298 return
299
300 def _put(cNet, cIndex, _list):
Alexf3dbe862019-10-07 15:17:04 -0500301 _added = False
302 _actual_index = -1
303 # Check list len
304 _len = len(_list)
305 if cIndex >= _len:
306 # grow list to meet index
307 _list = _list + [''] * (cIndex - _len + 1)
308 _len = len(_list)
309
310 for _cI in range(cIndex, _len):
Alex1839bbf2019-08-22 17:17:21 -0500311 # add child per index
312 # if space is free
313 if not _list[_cI]:
314 _list[_cI] = cNet
Alexf3dbe862019-10-07 15:17:04 -0500315 _added = True
316 _actual_index = _cI
Alex1839bbf2019-08-22 17:17:21 -0500317 break
Alexf3dbe862019-10-07 15:17:04 -0500318 if not _added:
319 # grow list by one entry
320 _list = _list + [cNet]
321 _actual_index = len(_list) - 1
322 return _actual_index, _list
Alex1839bbf2019-08-22 17:17:21 -0500323
324 # build network hierachy
325 nr = node_data['networks']
326 # walk interface tree
327 for _ifname in node_data['networks']:
328 _tree = {}
329 _level = 0
330 process_interface(_level, _ifname, _tree, nr)
331 # save tree for node/if
332 node_data['networks'][_ifname]['tree'] = _tree
333
334 # debug, print built tree
335 # logger_cli.debug("# '{}'".format(_ifname))
Alex3bc95f62020-03-05 17:00:04 -0600336 lvls = list(_tree.keys())
Alex1839bbf2019-08-22 17:17:21 -0500337 lvls.sort()
338 n = len(lvls)
339 m = max([len(_tree[k].keys()) for k in _tree.keys()])
340 matrix = [["" for i in range(m)] for j in range(n)]
341 x = 0
342 while True:
343 _lv = lvls.pop(0)
344 # get all interfaces on this level
Alex3bc95f62020-03-05 17:00:04 -0600345 nets = iter(_tree[_lv].keys())
Alex1839bbf2019-08-22 17:17:21 -0500346 while True:
347 y = 0
348 # get next interface
Alex3bc95f62020-03-05 17:00:04 -0600349 try:
350 _net = next(nets)
351 except StopIteration:
352 break
Alex1839bbf2019-08-22 17:17:21 -0500353 # all nets
354 _a = [_net]
355 # put current interface if this is only one left
356 if not _tree[_lv][_net]['children']:
357 if _net not in matrix[x]:
Alexf3dbe862019-10-07 15:17:04 -0500358 _, matrix[x] = _put(
359 _net,
360 y,
361 matrix[x]
362 )
Alex1839bbf2019-08-22 17:17:21 -0500363 y += 1
364 else:
365 # get all nets with same child
366 for _c in _tree[_lv][_net]['children']:
367 for _o_net in nets:
368 if _c in _tree[_lv][_o_net]['children']:
369 _a.append(_o_net)
370 # flush collected nets
371 for idx in range(len(_a)):
372 if _a[idx] in matrix[x]:
373 # there is such interface on this level
374 # get index
375 _nI = matrix[x].index(_a[idx])
Alexf3dbe862019-10-07 15:17:04 -0500376 _, matrix[x+1] = _put(
377 _c,
378 _nI,
379 matrix[x+1]
380 )
Alex1839bbf2019-08-22 17:17:21 -0500381 else:
382 # there is no such interface
383 # add it
Alexf3dbe862019-10-07 15:17:04 -0500384 _t, matrix[x] = _put(
385 _a[idx],
386 0,
387 matrix[x]
388 )
389 # also, put child
390 _, matrix[x+1] = _put(
391 _c,
392 _t,
393 matrix[x+1]
394 )
Alex1839bbf2019-08-22 17:17:21 -0500395 # remove collected nets from processing
396 if _a[idx] in nets:
397 nets.remove(_a[idx])
398 y += len(_a)
399 if not nets:
400 x += 1
401 break
402 if not lvls:
403 break
404
405 lines = []
406 _columns = [len(max([i for i in li])) for li in matrix]
407 for idx_y in range(m):
408 line = ""
409 for idx_x in range(n):
Alex9b2c1d12020-03-19 09:32:35 -0500410 _len = _columns[idx_x] if _columns[idx_x] else 1
411 _fmt = "{" + ":{}".format(_len) + "} "
Alex1839bbf2019-08-22 17:17:21 -0500412 line += _fmt.format(matrix[idx_x][idx_y])
413 lines.append(line)
414 node_data['networks'][_ifname]['matrix'] = matrix
415 node_data['networks'][_ifname]['lines'] = lines
Alexe0c5b9e2019-04-23 18:51:23 -0500416 return _runtime
417
418 def map_network(self, source):
419 # maps target network using given source
420 _networks = None
421
422 if source == self.RECLASS:
423 _networks = self._map_reclass_networks()
424 elif source == self.CONFIG:
425 _networks = self._map_configured_networks()
426 elif source == self.RUNTIME:
427 _networks = self._map_runtime_networks()
428
429 self.networks[source] = _networks
430 return _networks
431
Alex836fac82019-08-22 13:36:16 -0500432 def create_map(self):
433 """Create all needed elements for map output
Alexe0c5b9e2019-04-23 18:51:23 -0500434
435 :return: none
436 """
437 _runtime = self.networks[self.RUNTIME]
438 _reclass = self.networks[self.RECLASS]
Alex836fac82019-08-22 13:36:16 -0500439
440 # main networks, target vars
441 _map = {}
Alex6b633ec2019-06-06 19:44:34 -0500442 # No matter of proto, at least one IP will be present for the network
Alex836fac82019-08-22 13:36:16 -0500443 # we interested in, since we are to make sure that L3 level
444 # is configured according to reclass model
Alexe0c5b9e2019-04-23 18:51:23 -0500445 for network in _reclass:
446 # shortcuts
447 _net = str(network)
Alex836fac82019-08-22 13:36:16 -0500448 _map[_net] = {}
Alexe0c5b9e2019-04-23 18:51:23 -0500449 if network not in _runtime:
450 # reclass has network that not found in runtime
451 self.errors.add_error(
452 self.errors.NET_NO_RUNTIME_NETWORK,
453 reclass_net=str(network)
454 )
Alex1839bbf2019-08-22 17:17:21 -0500455 logger_cli.warn(
456 "WARN: {}: {}".format(
457 " No runtime network ", str(network)
458 )
459 )
Alexe0c5b9e2019-04-23 18:51:23 -0500460 continue
Alex6b633ec2019-06-06 19:44:34 -0500461 # hostnames
Alexe0c5b9e2019-04-23 18:51:23 -0500462 names = sorted(_runtime[network].keys())
463 for hostname in names:
Alex836fac82019-08-22 13:36:16 -0500464 _notes = []
Alex6b633ec2019-06-06 19:44:34 -0500465 node = hostname.split('.')[0]
Alex205546c2020-12-30 19:22:30 -0600466 if not self.master.is_node_available(hostname, log=False):
Alexe0c5b9e2019-04-23 18:51:23 -0500467 logger_cli.info(
Alex6b633ec2019-06-06 19:44:34 -0500468 " {0:8} {1}".format(node, "node not available")
Alexe0c5b9e2019-04-23 18:51:23 -0500469 )
470 # add non-responsive node erorr
471 self.errors.add_error(
472 self.errors.NET_NODE_NON_RESPONSIVE,
473 host=hostname
474 )
Alex836fac82019-08-22 13:36:16 -0500475 _notes.append(
476 self.errors.get_error_type_text(
477 self.errors.NET_NODE_NON_RESPONSIVE
478 )
479 )
Alexe0c5b9e2019-04-23 18:51:23 -0500480 continue
Alex6b633ec2019-06-06 19:44:34 -0500481 # lookup interface name on node using network CIDR
482 _if_name = _runtime[network][hostname][0]["name"]
Alex836fac82019-08-22 13:36:16 -0500483 _raw = self.interfaces[hostname][_if_name]['runtime']
Alex6b633ec2019-06-06 19:44:34 -0500484 # get proper reclass
485 _r = self.interfaces[hostname][_if_name]['reclass']
Alex6b633ec2019-06-06 19:44:34 -0500486 _if_name_suffix = ""
487 # get the proto value
Alex3b8e5432019-06-11 15:21:59 -0500488 if _r:
489 _if_rc = ""
490 else:
491 self.errors.add_error(
492 self.errors.NET_NODE_UNEXPECTED_IF,
493 host=hostname,
494 if_name=_if_name
495 )
Alex836fac82019-08-22 13:36:16 -0500496 _notes.append(
497 self.errors.get_error_type_text(
498 self.errors.NET_NODE_UNEXPECTED_IF
499 )
500 )
Alex3b8e5432019-06-11 15:21:59 -0500501 _if_rc = "*"
502
Alex6b633ec2019-06-06 19:44:34 -0500503 if "proto" in _r:
504 _proto = _r['proto']
Alexe0c5b9e2019-04-23 18:51:23 -0500505 else:
Alex6b633ec2019-06-06 19:44:34 -0500506 _proto = "-"
Alexe0c5b9e2019-04-23 18:51:23 -0500507
Alex6b633ec2019-06-06 19:44:34 -0500508 if "type" in _r:
509 _if_name_suffix += _r["type"]
510 if "use_interfaces" in _r:
511 _if_name_suffix += "->" + ",".join(_r["use_interfaces"])
512
513 if _if_name_suffix:
514 _if_name_suffix = "({})".format(_if_name_suffix)
515
Alex6b633ec2019-06-06 19:44:34 -0500516 # get gate and routes if proto is static
517 if _proto == 'static':
518 # get the gateway for current net
Alex205546c2020-12-30 19:22:30 -0600519 _routes = self.master.nodes[hostname]['routes']
Alex6b633ec2019-06-06 19:44:34 -0500520 _route = _routes[_net] if _net in _routes else None
Alex6b633ec2019-06-06 19:44:34 -0500521 # get the default gateway
522 if 'default' in _routes:
523 _d_gate = ipaddress.IPv4Address(
524 _routes['default']['gateway']
525 )
526 else:
527 _d_gate = None
Alexb3dc8592019-06-11 13:20:36 -0500528 _d_gate_str = str(_d_gate) if _d_gate else "No default!"
529 # match route with default
530 if not _route:
531 _gate = "?"
532 else:
533 _gate = _route['gateway'] if _route['gateway'] else "-"
Alex6b633ec2019-06-06 19:44:34 -0500534 else:
535 # in case of manual and dhcp, no check possible
536 _gate = "-"
537 _d_gate = "-"
Alex4067f002019-06-11 10:47:16 -0500538 _d_gate_str = "-"
Alex6b633ec2019-06-06 19:44:34 -0500539 # iterate through interfaces
Alexe0c5b9e2019-04-23 18:51:23 -0500540 _a = _runtime[network][hostname]
541 for _host in _a:
542 for _if in _host['ifs']:
Alexe0c5b9e2019-04-23 18:51:23 -0500543 _ip_str = str(_if.exploded)
Alexab232e42019-06-06 19:44:34 -0500544 _gate_error = ""
545 _up_error = ""
546 _mtu_error = ""
Alexe0c5b9e2019-04-23 18:51:23 -0500547
Alexb3dc8592019-06-11 13:20:36 -0500548 # Match gateway
Alexab232e42019-06-06 19:44:34 -0500549 if _proto == 'static':
Alexb3dc8592019-06-11 13:20:36 -0500550 # default reclass gate
Alex6b633ec2019-06-06 19:44:34 -0500551 _r_gate = "-"
552 if "gateway" in _r:
553 _r_gate = _r["gateway"]
Alexb3dc8592019-06-11 13:20:36 -0500554
Alexab232e42019-06-06 19:44:34 -0500555 # if values not match, put *
Alexb3dc8592019-06-11 13:20:36 -0500556 if _gate != _r_gate and _d_gate_str != _r_gate:
557 # if values not match, check if default match
Alex3b8e5432019-06-11 15:21:59 -0500558 self.errors.add_error(
559 self.errors.NET_UNEXPECTED_GATEWAY,
560 host=hostname,
561 if_name=_if_name,
562 ip=_ip_str,
563 gateway=_gate
564 )
Alex836fac82019-08-22 13:36:16 -0500565 _notes.append(
566 self.errors.get_error_type_text(
567 self.errors.NET_UNEXPECTED_GATEWAY
568 )
569 )
Alexab232e42019-06-06 19:44:34 -0500570 _gate_error = "*"
Alexe0c5b9e2019-04-23 18:51:23 -0500571
572 # IF status in reclass
Alex6b633ec2019-06-06 19:44:34 -0500573 _e = "enabled"
Alexab232e42019-06-06 19:44:34 -0500574 if _e not in _r:
Alex3b8e5432019-06-11 15:21:59 -0500575 self.errors.add_error(
576 self.errors.NET_NO_RC_IF_STATUS,
577 host=hostname,
578 if_name=_if_name
579 )
Alex836fac82019-08-22 13:36:16 -0500580 _notes.append(
581 self.errors.get_error_type_text(
582 self.errors.NET_NO_RC_IF_STATUS
583 )
584 )
Alexab232e42019-06-06 19:44:34 -0500585 _up_error = "*"
Alexe0c5b9e2019-04-23 18:51:23 -0500586
Alexe0c5b9e2019-04-23 18:51:23 -0500587 _rc_mtu = _r['mtu'] if 'mtu' in _r else None
Alexab232e42019-06-06 19:44:34 -0500588 _rc_mtu_s = ""
Alexe0c5b9e2019-04-23 18:51:23 -0500589 # check if this is a VIP address
590 # no checks needed if yes.
591 if _host['vip'] != _ip_str:
592 if _rc_mtu:
Alex3b8e5432019-06-11 15:21:59 -0500593 _rc_mtu_s = str(_rc_mtu)
Alexe0c5b9e2019-04-23 18:51:23 -0500594 # if there is an MTU value, match it
595 if _host['mtu'] != _rc_mtu_s:
596 self.errors.add_error(
597 self.errors.NET_MTU_MISMATCH,
598 host=hostname,
Alex6b633ec2019-06-06 19:44:34 -0500599 if_name=_if_name,
Alexe0c5b9e2019-04-23 18:51:23 -0500600 if_cidr=_ip_str,
601 reclass_mtu=_rc_mtu,
602 runtime_mtu=_host['mtu']
603 )
Alex836fac82019-08-22 13:36:16 -0500604 _notes.append(
605 self.errors.get_error_type_text(
606 self.errors.NET_MTU_MISMATCH
607 )
608 )
Alexb3dc8592019-06-11 13:20:36 -0500609 _rc_mtu_s = "/" + _rc_mtu_s
Alexab232e42019-06-06 19:44:34 -0500610 _mtu_error = "*"
611 else:
612 # empty the matched value
613 _rc_mtu_s = ""
Alex3b8e5432019-06-11 15:21:59 -0500614 elif _host['mtu'] != '1500' and \
615 _proto not in ["-", "dhcp"]:
Alexe0c5b9e2019-04-23 18:51:23 -0500616 # there is no MTU value in reclass
617 # and runtime value is not default
618 self.errors.add_error(
619 self.errors.NET_MTU_EMPTY,
620 host=hostname,
Alex6b633ec2019-06-06 19:44:34 -0500621 if_name=_if_name,
Alexe0c5b9e2019-04-23 18:51:23 -0500622 if_cidr=_ip_str,
623 if_mtu=_host['mtu']
624 )
Alex836fac82019-08-22 13:36:16 -0500625 _notes.append(
626 self.errors.get_error_type_text(
627 self.errors.NET_MTU_EMPTY
628 )
629 )
Alexab232e42019-06-06 19:44:34 -0500630 _mtu_error = "*"
Alexe0c5b9e2019-04-23 18:51:23 -0500631 else:
632 # this is a VIP
Alex6b633ec2019-06-06 19:44:34 -0500633 _if_name = " "*7
Alex6b633ec2019-06-06 19:44:34 -0500634 _if_name_suffix = ""
Alexe0c5b9e2019-04-23 18:51:23 -0500635 _ip_str += " VIP"
Alex836fac82019-08-22 13:36:16 -0500636 # Save all data
637 _values = {
638 "interface": _if_name,
639 "interface_error": _if_rc,
640 "interface_note": _if_name_suffix,
Alex1839bbf2019-08-22 17:17:21 -0500641 "interface_map": "\n".join(_host['lines']),
642 "interface_matrix": _host['matrix'],
Alex836fac82019-08-22 13:36:16 -0500643 "ip_address": _ip_str,
644 "address_type": _proto,
645 "rt_mtu": _host['mtu'],
646 "rc_mtu": _rc_mtu_s,
647 "mtu_error": _mtu_error,
648 "status": _host['state'],
649 "status_error": _up_error,
650 "subnet_gateway": _gate,
651 "subnet_gateway_error": _gate_error,
652 "default_gateway": _d_gate_str,
653 "raw_data": _raw,
654 "error_note": " and ".join(_notes)
655 }
656 if node in _map[_net]:
657 # add if to host
658 _map[_net][node].append(_values)
659 else:
660 _map[_net][node] = [_values]
661 _notes = []
662
663 # save map
664 self.map = _map
665 # other runtime networks found
666 # docker, etc
667
668 return
669
670 def print_map(self):
671 """
672 Create text report for CLI
673
674 :return: none
675 """
676 logger_cli.info("# Networks")
677 logger_cli.info(
678 " {0:8} {1:25} {2:25} {3:6} {4:10} {5:10} {6}/{7}".format(
679 "Host",
680 "IF",
681 "IP",
682 "Proto",
683 "MTU",
684 "State",
685 "Gate",
686 "Def.Gate"
687 )
688 )
689 for network in self.map.keys():
690 logger_cli.info("-> {}".format(network))
691 for hostname in self.map[network].keys():
692 node = hostname.split('.')[0]
693 _n = self.map[network][hostname]
694 for _i in _n:
695 # Host IF IP Proto MTU State Gate Def.Gate
696 _text = "{:7} {:17} {:25} {:6} {:10} " \
697 "{:10} {} / {}".format(
698 _i['interface'] + _i['interface_error'],
699 _i['interface_note'],
700 _i['ip_address'],
701 _i['address_type'],
702 _i['rt_mtu'] + _i['rc_mtu'] + _i['mtu_error'],
703 _i['status'] + _i['status_error'],
704 _i['subnet_gateway'] +
705 _i['subnet_gateway_error'],
706 _i['default_gateway']
Alexe0c5b9e2019-04-23 18:51:23 -0500707 )
Alexe0c5b9e2019-04-23 18:51:23 -0500708 logger_cli.info(
Alex836fac82019-08-22 13:36:16 -0500709 " {0:8} {1}".format(
710 node,
711 _text
712 )
Alexe0c5b9e2019-04-23 18:51:23 -0500713 )
Alex836fac82019-08-22 13:36:16 -0500714
715 # logger_cli.info("\n# Other networks")
716 # _other = [n for n in _runtime if n not in _reclass]
717 # for network in _other:
718 # logger_cli.info("-> {}".format(str(network)))
719 # names = sorted(_runtime[network].keys())
720
721 # for hostname in names:
722 # for _n in _runtime[network][hostname]:
723 # _ifs = [str(ifs.ip) for ifs in _n['ifs']]
724 # _text = "{:25} {:25} {:6} {:10} {}".format(
725 # _n['name'],
726 # ", ".join(_ifs),
727 # "-",
728 # _n['mtu'],
729 # _n['state']
730 # )
731 # logger_cli.info(
732 # " {0:8} {1}".format(hostname.split('.')[0], _text)
733 # )
734 # logger_cli.info("\n")
Alex205546c2020-12-30 19:22:30 -0600735
736
737class SaltNetworkMapper(NetworkMapper):
738 def __init__(
739 self,
740 config,
741 errors_class=None,
742 skip_list=None,
743 skip_list_file=None
744 ):
745 self.master = SaltNodes(config)
746 super(SaltNetworkMapper, self).__init__(
747 config,
748 errors_class=errors_class,
749 skip_list=skip_list,
750 skip_list_file=skip_list_file
751 )
752
753
754class KubeNetworkMapper(NetworkMapper):
755 def __init__(
756 self,
757 config,
758 errors_class=None,
759 skip_list=None,
760 skip_list_file=None
761 ):
762 self.master = KubeNodes(config)
763 super(KubeNetworkMapper, self).__init__(
764 config,
765 errors_class=errors_class,
766 skip_list=skip_list,
767 skip_list_file=skip_list_file
768 )