blob: 7a654dc3cabda6a150cb0784e713d257a5fab4cb [file] [log] [blame]
Alex Savatieiev9b2f6512019-02-20 18:05:00 -06001import json
2import os
3import sys
Alex Savatieieve9613992019-02-21 18:20:35 -06004import ipaddress
Alex Savatieiev9b2f6512019-02-20 18:05:00 -06005
6from copy import deepcopy
7
Alex Savatieievf526dc02019-03-06 10:11:32 -06008from cfg_checker.reports import reporter
Alex Savatieiev9b2f6512019-02-20 18:05:00 -06009from cfg_checker.common import utils, const
10from cfg_checker.common import config, logger, logger_cli, pkg_dir
11from cfg_checker.common import salt_utils
12from cfg_checker.nodes import SaltNodes, node_tmpl
13
14
15class NetworkChecker(SaltNodes):
Alex Savatieiev01f0d7f2019-03-07 17:53:29 -060016 @staticmethod
17 def _map_network_for_host(host, if_class, net_list, data):
18 if not any(if_class.ip in net for net in net_list.keys()):
19 # IP not fits into existing networks
20 if if_class.network not in net_list.keys():
21 # create subnet key
22 net_list[if_class.network] = {}
23 # add the host to the dict
24 net_list[if_class.network][host] = data
25 else:
26 # There is a network that ip fits into
27 for _net in net_list.keys():
28 if if_class.ip in _net:
29 net_list[_net][host] = data
30
31 return net_list
32
Alex Savatieiev9b2f6512019-02-20 18:05:00 -060033 def collect_network_info(self):
34 """
35 Collects info on the network using ifs_data.py script
36
37 :return: none
38 """
Alex Savatieiev42b89fa2019-03-07 18:45:26 -060039 logger_cli.info("# Mapping node runtime network data")
Alex Savatieiev01f0d7f2019-03-07 17:53:29 -060040 _result = self.execute_script_on_active_nodes("ifs_data.py", args=["json"])
Alex Savatieiev9b2f6512019-02-20 18:05:00 -060041
42 for key in self.nodes.keys():
43 # due to much data to be passed from salt, it is happening in order
44 if key in _result:
45 _text = _result[key]
46 _dict = json.loads(_text[_text.find('{'):])
Alex Savatieievd79dde12019-03-13 19:07:46 -050047 self.nodes[key]['routes'] = _dict.pop("routes")
Alex Savatieiev9b2f6512019-02-20 18:05:00 -060048 self.nodes[key]['networks'] = _dict
49 else:
50 self.nodes[key]['networks'] = {}
Alex Savatieievd79dde12019-03-13 19:07:46 -050051 self.nodes[key]['routes'] = {}
Alex Savatieiev42b89fa2019-03-07 18:45:26 -060052 logger_cli.debug("... {} has {} networks".format(
Alex Savatieiev9b2f6512019-02-20 18:05:00 -060053 key,
54 len(self.nodes[key]['networks'].keys())
55 ))
Alex Savatieievf808cd22019-03-01 13:17:59 -060056 logger_cli.info("-> done collecting networks data")
Alex Savatieiev9b2f6512019-02-20 18:05:00 -060057
Alex Savatieieve9613992019-02-21 18:20:35 -060058 logger_cli.info("### Building network tree")
Alex Savatieiev01f0d7f2019-03-07 17:53:29 -060059 # match interfaces by IP subnets
Alex Savatieieve9613992019-02-21 18:20:35 -060060 _all_nets = {}
Alex Savatieieva05921f2019-02-21 18:21:39 -060061 for host, node_data in self.nodes.iteritems():
Alex Savatieieve9613992019-02-21 18:20:35 -060062 for net_name, net_data in node_data['networks'].iteritems():
63 # get ips and calculate subnets
Alex Savatieievd79dde12019-03-13 19:07:46 -050064 if net_name in ['lo']:
Alex Savatieiev01f0d7f2019-03-07 17:53:29 -060065 # skip the localhost
Alex Savatieieve9613992019-02-21 18:20:35 -060066 continue
67 _ip4s = net_data['ipv4']
68 for _ip_str in _ip4s.keys():
Alex Savatieiev01f0d7f2019-03-07 17:53:29 -060069 # create interface class
Alex Savatieieve9613992019-02-21 18:20:35 -060070 _if = ipaddress.IPv4Interface(_ip_str)
Alex Savatieiev01f0d7f2019-03-07 17:53:29 -060071 net_data['name'] = net_name
72 net_data['if'] = _if
73
74 _all_nets = self._map_network_for_host(
75 host,
76 _if,
77 _all_nets,
78 net_data
79 )
Alex Savatieieve9613992019-02-21 18:20:35 -060080
81 # save collected info
Alex Savatieiev42b89fa2019-03-07 18:45:26 -060082 self.all_nets = _all_nets
Alex Savatieiev9b2f6512019-02-20 18:05:00 -060083
Alex Savatieiev9c642112019-02-26 13:55:43 -060084
Alex Savatieiev01f0d7f2019-03-07 17:53:29 -060085 def collect_reclass_networks(self):
Alex Savatieiev42b89fa2019-03-07 18:45:26 -060086 logger_cli.info("# Mapping reclass networks")
Alex Savatieiev01f0d7f2019-03-07 17:53:29 -060087 # Get networks from reclass and mark them
88 _reclass_nets = {}
89 # Get required pillars
90 self.get_specific_pillar_for_nodes("linux:network")
91 for node in self.nodes.keys():
92 _pillar = self.nodes[node]['pillars']['linux']['network']['interface']
93 for _if_name, _if_data in _pillar.iteritems():
94 if 'address' in _if_data:
95 _if = ipaddress.IPv4Interface(
96 _if_data['address'] + '/' + _if_data['netmask']
97 )
98 _if_data['name'] = _if_name
99 _if_data['if'] = _if
100
101 _reclass_nets = self._map_network_for_host(
102 node,
103 _if,
104 _reclass_nets,
105 _if_data
106 )
107
108 self.reclass_nets = _reclass_nets
109
Alex Savatieiev9c642112019-02-26 13:55:43 -0600110
Alex Savatieiev9b2f6512019-02-20 18:05:00 -0600111 def print_network_report(self):
112 """
113 Create text report for CLI
114
115 :return: none
116 """
Alex Savatieiev42b89fa2019-03-07 18:45:26 -0600117 _all_nets = self.all_nets.keys()
118 logger_cli.info("# Reclass networks")
Alex Savatieievd79dde12019-03-13 19:07:46 -0500119 _text = " {0:17} {1:25}: {2:19} {3:5}{4:10} {5}{6} {7}/{8}/{9}".format(
120 "Hostname",
121 "IF name",
122 "IP",
123 "Runtime MTU",
124 "Reclass MTU",
125 "Runtime State",
126 "Reclass State",
127 "Runtime gate",
128 "Runtime def. gate",
129 "Reclass gate"
130 )
131
Alex Savatieiev42b89fa2019-03-07 18:45:26 -0600132 _reclass = [n for n in _all_nets if n in self.reclass_nets]
133 for network in _reclass:
134 # shortcuts
Alex Savatieievd79dde12019-03-13 19:07:46 -0500135 _net = str(network)
136 logger_cli.info("-> {}".format(_net))
Alex Savatieiev42b89fa2019-03-07 18:45:26 -0600137 names = sorted(self.all_nets[network].keys())
Alex Savatieieve9613992019-02-21 18:20:35 -0600138 for hostname in names:
Alex Savatieievd79dde12019-03-13 19:07:46 -0500139 # get the gateway for current net
140 _routes = self.nodes[hostname]['routes']
141 _route = _routes[_net] if _net in _routes else None
142 _gate = _route['gateway'] if _route['gateway'] else "empty"
143
144 # get the default gateway
145 if 'default' in _routes:
146 _d_gate = ipaddress.IPv4Address(
147 _routes['default']['gateway']
148 )
149 else:
150 _d_gate = None
151 _d_gate_str = _d_gate if _d_gate else "No default gateway!"
152
Alex Savatieiev42b89fa2019-03-07 18:45:26 -0600153 _a = self.all_nets[network][hostname]
154 _r = self.reclass_nets[network][hostname]
Alex Savatieievd79dde12019-03-13 19:07:46 -0500155
156 # Take gateway parameter for this IF
157 # from corresponding reclass record
158 _pillar = self.nodes[hostname]['pillars']
159 _rd = _pillar['linux']['network']['interface'][_a['name']]
160 _r_gate = _rd['gateway'] if 'gateway' in _rd else "empty"
161
162 _text = "{0:25}: {1:19} {2:5}{3:10} {4:4}{5:10} {6}/{7}/{8}".format(
Alex Savatieiev42b89fa2019-03-07 18:45:26 -0600163 _a['name'],
164 str(_a['if'].ip),
165 _a['mtu'],
166 '('+str(_r['mtu'])+')' if 'mtu' in _r else '(unset!)',
167 _a['state'],
Alex Savatieievd79dde12019-03-13 19:07:46 -0500168 "(enabled)" if _r['enabled'] else "(disabled)",
169 _gate,
170 _d_gate_str,
171 _r_gate
Alex Savatieiev01f0d7f2019-03-07 17:53:29 -0600172 )
Alex Savatieieve9613992019-02-21 18:20:35 -0600173 logger_cli.info(
Alex Savatieiev588b2c42019-03-11 10:39:21 -0500174 " {0:17} {1}".format(hostname.split('.')[0], _text)
Alex Savatieieve9613992019-02-21 18:20:35 -0600175 )
Alex Savatieiev42b89fa2019-03-07 18:45:26 -0600176
177 logger_cli.info("\n# Other networks")
178 _other = [n for n in _all_nets if n not in self.reclass_nets]
179 for network in _other:
180 logger_cli.info("-> {}".format(str(network)))
181 names = sorted(self.all_nets[network].keys())
182
183 for hostname in names:
Alex Savatieiev588b2c42019-03-11 10:39:21 -0500184 _text = "{0:25}: {1:19} {2:5} {3:4}".format(
Alex Savatieiev42b89fa2019-03-07 18:45:26 -0600185 self.all_nets[network][hostname]['name'],
186 str(self.all_nets[network][hostname]['if'].ip),
187 self.all_nets[network][hostname]['mtu'],
188 self.all_nets[network][hostname]['state']
189 )
190 logger_cli.info(
Alex Savatieiev588b2c42019-03-11 10:39:21 -0500191 " {0:17} {1}".format(hostname.split('.')[0], _text)
Alex Savatieiev42b89fa2019-03-07 18:45:26 -0600192 )
193
Alex Savatieiev9b2f6512019-02-20 18:05:00 -0600194
195 def create_html_report(self, filename):
196 """
197 Create static html showing network schema-like report
198
199 :return: none
200 """
201 logger_cli.info("### Generating report to '{}'".format(filename))
202 _report = reporter.ReportToFile(
203 reporter.HTMLNetworkReport(),
204 filename
205 )
206 _report({
207 "nodes": self.nodes,
208 "diffs": {}
209 })
210 logger_cli.info("-> Done")