blob: 24d751f56fadaeadb48c6dc24e4866e1c5677ebc [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('{'):])
47 self.nodes[key]['networks'] = _dict
48 else:
49 self.nodes[key]['networks'] = {}
Alex Savatieiev42b89fa2019-03-07 18:45:26 -060050 logger_cli.debug("... {} has {} networks".format(
Alex Savatieiev9b2f6512019-02-20 18:05:00 -060051 key,
52 len(self.nodes[key]['networks'].keys())
53 ))
Alex Savatieievf808cd22019-03-01 13:17:59 -060054 logger_cli.info("-> done collecting networks data")
Alex Savatieiev9b2f6512019-02-20 18:05:00 -060055
Alex Savatieieve9613992019-02-21 18:20:35 -060056 # dump collected data to speed up coding
57 # with open('dump.json', 'w+') as ff:
58 # ff.write(json.dumps(self.nodes))
59
60 # load dump data
61 # with open('dump.json', 'r') as ff:
62 # _nodes = json.loads(ff.read())
63
64 logger_cli.info("### Building network tree")
Alex Savatieiev01f0d7f2019-03-07 17:53:29 -060065 # match interfaces by IP subnets
Alex Savatieieve9613992019-02-21 18:20:35 -060066 _all_nets = {}
Alex Savatieieva05921f2019-02-21 18:21:39 -060067 for host, node_data in self.nodes.iteritems():
Alex Savatieieve9613992019-02-21 18:20:35 -060068 for net_name, net_data in node_data['networks'].iteritems():
69 # get ips and calculate subnets
70 if net_name == 'lo':
Alex Savatieiev01f0d7f2019-03-07 17:53:29 -060071 # skip the localhost
Alex Savatieieve9613992019-02-21 18:20:35 -060072 continue
73 _ip4s = net_data['ipv4']
74 for _ip_str in _ip4s.keys():
Alex Savatieiev01f0d7f2019-03-07 17:53:29 -060075 # create interface class
Alex Savatieieve9613992019-02-21 18:20:35 -060076 _if = ipaddress.IPv4Interface(_ip_str)
Alex Savatieiev01f0d7f2019-03-07 17:53:29 -060077 net_data['name'] = net_name
78 net_data['if'] = _if
79
80 _all_nets = self._map_network_for_host(
81 host,
82 _if,
83 _all_nets,
84 net_data
85 )
Alex Savatieieve9613992019-02-21 18:20:35 -060086
87 # save collected info
Alex Savatieiev42b89fa2019-03-07 18:45:26 -060088 self.all_nets = _all_nets
Alex Savatieiev9b2f6512019-02-20 18:05:00 -060089
Alex Savatieiev9c642112019-02-26 13:55:43 -060090
Alex Savatieiev01f0d7f2019-03-07 17:53:29 -060091 def collect_reclass_networks(self):
Alex Savatieiev42b89fa2019-03-07 18:45:26 -060092 logger_cli.info("# Mapping reclass networks")
Alex Savatieiev01f0d7f2019-03-07 17:53:29 -060093 # Get networks from reclass and mark them
94 _reclass_nets = {}
95 # Get required pillars
96 self.get_specific_pillar_for_nodes("linux:network")
97 for node in self.nodes.keys():
98 _pillar = self.nodes[node]['pillars']['linux']['network']['interface']
99 for _if_name, _if_data in _pillar.iteritems():
100 if 'address' in _if_data:
101 _if = ipaddress.IPv4Interface(
102 _if_data['address'] + '/' + _if_data['netmask']
103 )
104 _if_data['name'] = _if_name
105 _if_data['if'] = _if
106
107 _reclass_nets = self._map_network_for_host(
108 node,
109 _if,
110 _reclass_nets,
111 _if_data
112 )
113
114 self.reclass_nets = _reclass_nets
115
Alex Savatieiev9c642112019-02-26 13:55:43 -0600116
Alex Savatieiev9b2f6512019-02-20 18:05:00 -0600117 def print_network_report(self):
118 """
119 Create text report for CLI
120
121 :return: none
122 """
Alex Savatieiev42b89fa2019-03-07 18:45:26 -0600123 _all_nets = self.all_nets.keys()
124 logger_cli.info("# Reclass networks")
125 _reclass = [n for n in _all_nets if n in self.reclass_nets]
126 for network in _reclass:
127 # shortcuts
Alex Savatieieve9613992019-02-21 18:20:35 -0600128 logger_cli.info("-> {}".format(str(network)))
Alex Savatieiev42b89fa2019-03-07 18:45:26 -0600129 names = sorted(self.all_nets[network].keys())
Alex Savatieieve9613992019-02-21 18:20:35 -0600130
131 for hostname in names:
Alex Savatieiev42b89fa2019-03-07 18:45:26 -0600132 _a = self.all_nets[network][hostname]
133 _r = self.reclass_nets[network][hostname]
134 _text = "{0:30}: {1:19} {2:5}{3:10} {4}{5}".format(
135 _a['name'],
136 str(_a['if'].ip),
137 _a['mtu'],
138 '('+str(_r['mtu'])+')' if 'mtu' in _r else '(unset!)',
139 _a['state'],
140 "(enabled)" if _r['enabled'] else "(disabled)"
Alex Savatieiev01f0d7f2019-03-07 17:53:29 -0600141 )
Alex Savatieieve9613992019-02-21 18:20:35 -0600142 logger_cli.info(
Alex Savatieiev01f0d7f2019-03-07 17:53:29 -0600143 "\t{0:10} {1}".format(hostname.split('.')[0], _text)
Alex Savatieieve9613992019-02-21 18:20:35 -0600144 )
Alex Savatieiev42b89fa2019-03-07 18:45:26 -0600145
146 logger_cli.info("\n# Other networks")
147 _other = [n for n in _all_nets if n not in self.reclass_nets]
148 for network in _other:
149 logger_cli.info("-> {}".format(str(network)))
150 names = sorted(self.all_nets[network].keys())
151
152 for hostname in names:
153 _text = "{0:30}: {1:19} {2:5} {3:4}".format(
154 self.all_nets[network][hostname]['name'],
155 str(self.all_nets[network][hostname]['if'].ip),
156 self.all_nets[network][hostname]['mtu'],
157 self.all_nets[network][hostname]['state']
158 )
159 logger_cli.info(
160 "\t{0:10} {1}".format(hostname.split('.')[0], _text)
161 )
162
Alex Savatieiev9b2f6512019-02-20 18:05:00 -0600163
164 def create_html_report(self, filename):
165 """
166 Create static html showing network schema-like report
167
168 :return: none
169 """
170 logger_cli.info("### Generating report to '{}'".format(filename))
171 _report = reporter.ReportToFile(
172 reporter.HTMLNetworkReport(),
173 filename
174 )
175 _report({
176 "nodes": self.nodes,
177 "diffs": {}
178 })
179 logger_cli.info("-> Done")