blob: 351c05ab68611924464dc9adb7fca9278d87fa96 [file] [log] [blame]
Alexe0c5b9e2019-04-23 18:51:23 -05001import ipaddress
2import json
3
Alex92e07ce2019-05-31 16:00:03 -05004from cfg_checker.common import logger_cli
Alexe0c5b9e2019-04-23 18:51:23 -05005from cfg_checker.helpers.console_utils import Progress
6from cfg_checker.modules.network.mapper import NetworkMapper
Alex92e07ce2019-05-31 16:00:03 -05007from cfg_checker.modules.network.network_errors import NetworkErrors
Alexe0c5b9e2019-04-23 18:51:23 -05008from cfg_checker.nodes import salt_master
9
10
Alexe0c5b9e2019-04-23 18:51:23 -050011# This is independent class with a salt.nodes input
12class NetworkPinger(object):
Alex92e07ce2019-05-31 16:00:03 -050013 def __init__(self, mtu=None, detailed=False, errors_class=None):
Alexe0c5b9e2019-04-23 18:51:23 -050014 logger_cli.info("# Initializing")
15 # all active nodes in the cloud
16 self.target_nodes = salt_master.get_nodes()
17 # default MTU value
18 self.target_mtu = mtu if mtu else 64
19 # only data
20 self.packet_size = int(self.target_mtu) - 20 - 8
21 self.detailed_summary = detailed
22
Alex92e07ce2019-05-31 16:00:03 -050023 if errors_class:
24 self.errors = errors_class
25 else:
26 logger_cli.debug("... init error logs folder")
27 self.errors = NetworkErrors()
28
Alexe0c5b9e2019-04-23 18:51:23 -050029 def _collect_node_addresses(self, target_net):
30 # use reclass model and standard methods
31 # to create list of nodes with target network
Alex92e07ce2019-05-31 16:00:03 -050032 _mapper = NetworkMapper(errors_class=self.errors)
33 _reclass = _mapper.map_network(_mapper.RUNTIME)
Alexe0c5b9e2019-04-23 18:51:23 -050034 if target_net in _reclass:
35 return _reclass[target_net]
36 else:
37 logger_cli.info(
38 "# Target network of {} not found in reclass".format(
39 target_net.exploded
40 )
41 )
42 return None
43
44 def ping_nodes(self, network_cidr_str):
45 # Conduct actual ping using network CIDR
46 logger_cli.info("# Collecting node pairs")
47 _fake_if = ipaddress.IPv4Interface(unicode(network_cidr_str))
48 _net = _fake_if.network
49 # collect nodes and ips from reclass
50 nodes = self._collect_node_addresses(_net)
51 # build list of packets to be sent
52 # source -> target
53 _count = 0
54 _packets = {}
55 _nodes = sorted(nodes.keys())
56 _nodes_total = len(_nodes)
57 logger_cli.info("-> {} nodes found within subnet of '{}'".format(
58 _nodes_total,
59 network_cidr_str
60 ))
61 while len(_nodes) > 0:
62 src_host = _nodes.pop()
63 src_data = nodes[src_host]
64 src_if_name = src_data[0]['name']
65 src_ips = [str(_if.ip) for _if in src_data[0]['ifs']]
66 _packets[src_host] = {
67 "ip": src_ips[0],
68 "if_name": src_if_name,
69 "targets": {}
70 }
71
72 for tgt_host, tgt_data in nodes.iteritems():
73 for tgt_if in tgt_data:
74 tgt_if_name = tgt_if['name']
75 _ip_index = 0
76 for tgt_ip in tgt_if['ifs']:
77 _ip = str(tgt_ip.ip)
78 if _ip not in src_ips:
79 _packets[src_host]["targets"][tgt_host] = []
80 _tgt = {
81 "ip": _ip,
82 "tgt_host": tgt_host,
83 "ip_index": _ip_index,
84 "if_name": tgt_if_name,
85 "mtu": self.target_mtu,
86 "size": self.packet_size
87 }
88 _packets[src_host]["targets"][tgt_host].append(
89 _tgt
90 )
91 _count += 1
92 _ip_index += 1
93 else:
94 pass
95 logger_cli.info("-> {} packets to send".format(_count))
96
Alex92e07ce2019-05-31 16:00:03 -050097 if not _count:
98 logger_cli.warning(
99 "\n# WARNING: No packets to send for '{}', "
100 "check network configuration\n".format(network_cidr_str)
101 )
102
103 return -1
104
Alexe0c5b9e2019-04-23 18:51:23 -0500105 # do ping of packets
106 logger_cli.info("# Pinging nodes: MTU={}".format(self.target_mtu))
107 salt_master.prepare_script_on_active_nodes("ping.py")
Alexe0c5b9e2019-04-23 18:51:23 -0500108 _progress = Progress(_count)
109 _progress_index = 0
110 _node_index = 0
111 for src, src_data in _packets.iteritems():
112 _targets = src_data["targets"]
113 _node_index += 1
114 # create 'targets.json' on source host
115 _path = salt_master.prepare_json_on_node(
116 src,
117 _targets,
118 "targets.json"
119 )
120 # execute ping.py
121 _results = salt_master.execute_script_on_node(
122 src,
123 "ping.py",
124 args=[_path]
125 )
126 _progress_index += len(_targets)
127 # print progress
128 _progress.write_progress(
129 _progress_index,
130 note='/ {}/{} nodes / current {}'.format(
131 _node_index,
132 _nodes_total,
133 src
134 )
135 )
136 # Parse salt output
137 _result = _results[src]
138 _result = json.loads(_result)
139 # Handle return codes
140 for tgt_node, _tgt_ips in _result.iteritems():
141 for _params in _tgt_ips:
142 _body = "{}({}) --{}--> {}({}@{})\n".format(
143 src,
144 src_data["if_name"],
145 _params["returncode"],
146 tgt_node,
147 _params["if_name"],
148 _params["ip"]
149 )
150 _stdout = ""
151 _stderr = ""
152 if len(_params["stdout"]) > 0:
153 _stdout = "stdout:\n{}\n".format(_params["stdout"])
154 if len(_params["stderr"]) > 0:
155 _stderr = "stderr:\n{}\n".format(_params["stderr"])
156
Alex92e07ce2019-05-31 16:00:03 -0500157 if not _params["returncode"]:
158 # 0
159 self.errors.add_error(
160 self.errors.NET_PING_SUCCESS,
161 ping_path=_body,
162 stdout=_stdout,
163 stderr=_stderr
164 )
165 elif _params["returncode"] == 68:
166 # 68 is a 'can't resove host error'
167 self.errors.add_error(
168 self.errors.NET_PING_NOT_RESOLVED,
169 ping_path=_body,
170 stdout=_stdout,
171 stderr=_stderr
172 )
173 elif _params["returncode"] > 1:
174 # >1 is when no actial (any) response
175 self.errors.add_error(
176 self.errors.NET_PING_ERROR,
177 ping_path=_body,
178 stdout=_stdout,
179 stderr=_stderr
180 )
Alexe0c5b9e2019-04-23 18:51:23 -0500181 else:
Alex92e07ce2019-05-31 16:00:03 -0500182 # 1 is for timeouts amd/or packet lost
183 self.errors.add_error(
184 self.errors.NET_PING_TIMEOUT,
185 ping_path=_body,
186 stdout=_stdout,
187 stderr=_stderr
188 )
Alexe0c5b9e2019-04-23 18:51:23 -0500189
190 # Parse results back in place
191 src_data["targets"] = _result
192
Alexd9fd85e2019-05-16 16:58:24 -0500193 _progress.end()
Alexe0c5b9e2019-04-23 18:51:23 -0500194
Alex92e07ce2019-05-31 16:00:03 -0500195 return 0
Alexe0c5b9e2019-04-23 18:51:23 -0500196
Alex92e07ce2019-05-31 16:00:03 -0500197 def print_summary(self):
198 logger_cli.info(self.errors.get_summary(print_zeros=False))
199
200 def print_details(self):
201 # Detailed errors
Alexe0c5b9e2019-04-23 18:51:23 -0500202 logger_cli.info(
Alex92e07ce2019-05-31 16:00:03 -0500203 "\n{}\n".format(
204 self.errors.get_errors()
Alexe0c5b9e2019-04-23 18:51:23 -0500205 )
206 )