blob: 6d0287b417bd5d7b01af6c45f4aa06020ec449e8 [file] [log] [blame]
Alexe0c5b9e2019-04-23 18:51:23 -05001import ipaddress
2import json
3
4from cfg_checker.common import logger, logger_cli
5from cfg_checker.helpers.console_utils import Progress
6from cfg_checker.modules.network.mapper import NetworkMapper
7from cfg_checker.nodes import salt_master
8
9
10# ping -s 9072 -M do -n -c 1 -w 1 -W 1 ctl01
11
12
13# This is independent class with a salt.nodes input
14class NetworkPinger(object):
15 def __init__(self, mtu=None, detailed=False):
16 logger_cli.info("# Initializing")
17 # all active nodes in the cloud
18 self.target_nodes = salt_master.get_nodes()
19 # default MTU value
20 self.target_mtu = mtu if mtu else 64
21 # only data
22 self.packet_size = int(self.target_mtu) - 20 - 8
23 self.detailed_summary = detailed
24
25 def _collect_node_addresses(self, target_net):
26 # use reclass model and standard methods
27 # to create list of nodes with target network
28 _mapper = NetworkMapper()
29 _reclass = _mapper.map_network(_mapper.RECLASS)
30 if target_net in _reclass:
31 return _reclass[target_net]
32 else:
33 logger_cli.info(
34 "# Target network of {} not found in reclass".format(
35 target_net.exploded
36 )
37 )
38 return None
39
40 def ping_nodes(self, network_cidr_str):
41 # Conduct actual ping using network CIDR
42 logger_cli.info("# Collecting node pairs")
43 _fake_if = ipaddress.IPv4Interface(unicode(network_cidr_str))
44 _net = _fake_if.network
45 # collect nodes and ips from reclass
46 nodes = self._collect_node_addresses(_net)
47 # build list of packets to be sent
48 # source -> target
49 _count = 0
50 _packets = {}
51 _nodes = sorted(nodes.keys())
52 _nodes_total = len(_nodes)
53 logger_cli.info("-> {} nodes found within subnet of '{}'".format(
54 _nodes_total,
55 network_cidr_str
56 ))
57 while len(_nodes) > 0:
58 src_host = _nodes.pop()
59 src_data = nodes[src_host]
60 src_if_name = src_data[0]['name']
61 src_ips = [str(_if.ip) for _if in src_data[0]['ifs']]
62 _packets[src_host] = {
63 "ip": src_ips[0],
64 "if_name": src_if_name,
65 "targets": {}
66 }
67
68 for tgt_host, tgt_data in nodes.iteritems():
69 for tgt_if in tgt_data:
70 tgt_if_name = tgt_if['name']
71 _ip_index = 0
72 for tgt_ip in tgt_if['ifs']:
73 _ip = str(tgt_ip.ip)
74 if _ip not in src_ips:
75 _packets[src_host]["targets"][tgt_host] = []
76 _tgt = {
77 "ip": _ip,
78 "tgt_host": tgt_host,
79 "ip_index": _ip_index,
80 "if_name": tgt_if_name,
81 "mtu": self.target_mtu,
82 "size": self.packet_size
83 }
84 _packets[src_host]["targets"][tgt_host].append(
85 _tgt
86 )
87 _count += 1
88 _ip_index += 1
89 else:
90 pass
91 logger_cli.info("-> {} packets to send".format(_count))
92
93 # do ping of packets
94 logger_cli.info("# Pinging nodes: MTU={}".format(self.target_mtu))
95 salt_master.prepare_script_on_active_nodes("ping.py")
96 _errors = []
97 _success = []
98 _progress = Progress(_count)
99 _progress_index = 0
100 _node_index = 0
101 for src, src_data in _packets.iteritems():
102 _targets = src_data["targets"]
103 _node_index += 1
104 # create 'targets.json' on source host
105 _path = salt_master.prepare_json_on_node(
106 src,
107 _targets,
108 "targets.json"
109 )
110 # execute ping.py
111 _results = salt_master.execute_script_on_node(
112 src,
113 "ping.py",
114 args=[_path]
115 )
116 _progress_index += len(_targets)
117 # print progress
118 _progress.write_progress(
119 _progress_index,
120 note='/ {}/{} nodes / current {}'.format(
121 _node_index,
122 _nodes_total,
123 src
124 )
125 )
126 # Parse salt output
127 _result = _results[src]
128 _result = json.loads(_result)
129 # Handle return codes
130 for tgt_node, _tgt_ips in _result.iteritems():
131 for _params in _tgt_ips:
132 _body = "{}({}) --{}--> {}({}@{})\n".format(
133 src,
134 src_data["if_name"],
135 _params["returncode"],
136 tgt_node,
137 _params["if_name"],
138 _params["ip"]
139 )
140 _stdout = ""
141 _stderr = ""
142 if len(_params["stdout"]) > 0:
143 _stdout = "stdout:\n{}\n".format(_params["stdout"])
144 if len(_params["stderr"]) > 0:
145 _stderr = "stderr:\n{}\n".format(_params["stderr"])
146
147 if _params["returncode"]:
148 _errors.append("FAIL: {}{}{}".format(
149 _body,
150 _stdout,
151 _stderr
152 ))
153 else:
154 _success.append("PASS: {}{}{}".format(
155 _body,
156 _stdout,
157 _stderr
158 ))
159
160 # Parse results back in place
161 src_data["targets"] = _result
162
Alexd9fd85e2019-05-16 16:58:24 -0500163 _progress.end()
Alexe0c5b9e2019-04-23 18:51:23 -0500164
165 if self.detailed_summary:
166 logger_cli.info("\n{:=^8s}".format("PASS"))
167 logger_cli.info("\n".join(_success))
168 else:
169 logger.info("\n{:=^8s}".format("PASS"))
170 logger.info("\n".join(_success))
171 if len(_errors) > 0:
172 logger_cli.info("\n{:=^8s}".format("FAIL"))
173 logger_cli.info("\n".join(_errors))
174
175 logger_cli.info(
176 "# {} failed, {} passed".format(
177 len(_errors),
178 len(_success)
179 )
180 )