Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1import ipaddress 

2import json 

3 

4from cfg_checker.common import logger_cli 

5from cfg_checker.helpers.console_utils import Progress 

6from cfg_checker.modules.network.mapper import NetworkMapper 

7from cfg_checker.modules.network.network_errors import NetworkErrors 

8from cfg_checker.nodes import salt_master 

9 

10 

11# This is independent class with a salt.nodes input 

12class NetworkPinger(object): 

13 def __init__(self, mtu=None, detailed=False, errors_class=None): 

14 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 

23 if errors_class: 

24 self.errors = errors_class 

25 else: 

26 logger_cli.debug("... init error logs folder") 

27 self.errors = NetworkErrors() 

28 

29 def _collect_node_addresses(self, target_net): 

30 # use reclass model and standard methods 

31 # to create list of nodes with target network 

32 _mapper = NetworkMapper(errors_class=self.errors) 

33 _reclass = _mapper.map_network(_mapper.RUNTIME) 

34 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(str(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.items(): 

73 _t = _packets[src_host]["targets"] 

74 for tgt_if in tgt_data: 

75 tgt_if_name = tgt_if['name'] 

76 _ip_index = 0 

77 for tgt_ip in tgt_if['ifs']: 

78 _ip = str(tgt_ip.ip) 

79 if _ip not in src_ips: 

80 if tgt_host not in _t: 

81 _t[tgt_host] = [] 

82 _tgt = { 

83 "ip": _ip, 

84 "tgt_host": tgt_host, 

85 "ip_index": _ip_index, 

86 "if_name": tgt_if_name, 

87 "mtu": self.target_mtu, 

88 "size": self.packet_size 

89 } 

90 _t[tgt_host].append( 

91 _tgt 

92 ) 

93 _count += 1 

94 _ip_index += 1 

95 else: 

96 pass 

97 logger_cli.info("-> {} packets to send".format(_count)) 

98 

99 if not _count: 

100 logger_cli.warning( 

101 "\n# WARNING: No packets to send for '{}', " 

102 "check network configuration\n".format(network_cidr_str) 

103 ) 

104 

105 return -1 

106 

107 # do ping of packets 

108 logger_cli.info("# Pinging nodes: MTU={}".format(self.target_mtu)) 

109 salt_master.prepare_script_on_active_nodes("ping.py") 

110 _progress = Progress(_count) 

111 _progress_index = 0 

112 _node_index = 0 

113 for src, src_data in _packets.items(): 

114 _targets = src_data["targets"] 

115 _node_index += 1 

116 # create 'targets.json' on source host 

117 _path = salt_master.prepare_json_on_node( 

118 src, 

119 _targets, 

120 "targets.json" 

121 ) 

122 # execute ping.py 

123 _results = salt_master.execute_script_on_node( 

124 src, 

125 "ping.py", 

126 args=[_path] 

127 ) 

128 _progress_index += len(_targets) 

129 # print progress 

130 _progress.write_progress( 

131 _progress_index, 

132 note='/ {}/{} nodes / current {}'.format( 

133 _node_index, 

134 _nodes_total, 

135 src 

136 ) 

137 ) 

138 # Parse salt output 

139 _result = _results[src] 

140 try: 

141 _result = json.loads(_result) 

142 except (ValueError, TypeError): 

143 _progress.clearline() 

144 logger_cli.error( 

145 "# ERROR: Unexpected salt return for '{}': '{}'\n".format( 

146 src, 

147 _result 

148 ) 

149 ) 

150 self.errors.add_error( 

151 self.errors.NET_NODE_NON_RESPONSIVE, 

152 node=src, 

153 response=_result 

154 ) 

155 continue 

156 # Handle return codes 

157 for tgt_node, _tgt_ips in _result.items(): 

158 for _params in _tgt_ips: 

159 _body = "{}({}) --{}--> {}({}@{})\n".format( 

160 src, 

161 src_data["if_name"], 

162 _params["returncode"], 

163 tgt_node, 

164 _params["if_name"], 

165 _params["ip"] 

166 ) 

167 _stdout = "" 

168 _stderr = "" 

169 if len(_params["stdout"]) > 0: 

170 _stdout = "stdout:\n{}\n".format(_params["stdout"]) 

171 if len(_params["stderr"]) > 0: 

172 _stderr = "stderr:\n{}\n".format(_params["stderr"]) 

173 

174 if not _params["returncode"]: 

175 # 0 

176 self.errors.add_error( 

177 self.errors.NET_PING_SUCCESS, 

178 ping_path=_body, 

179 stdout=_stdout, 

180 stderr=_stderr 

181 ) 

182 elif _params["returncode"] == 68: 

183 # 68 is a 'can't resove host error' 

184 self.errors.add_error( 

185 self.errors.NET_PING_NOT_RESOLVED, 

186 ping_path=_body, 

187 stdout=_stdout, 

188 stderr=_stderr 

189 ) 

190 elif _params["returncode"] > 1: 

191 # >1 is when no actial (any) response 

192 self.errors.add_error( 

193 self.errors.NET_PING_ERROR, 

194 ping_path=_body, 

195 stdout=_stdout, 

196 stderr=_stderr 

197 ) 

198 else: 

199 # 1 is for timeouts amd/or packet lost 

200 self.errors.add_error( 

201 self.errors.NET_PING_TIMEOUT, 

202 ping_path=_body, 

203 stdout=_stdout, 

204 stderr=_stderr 

205 ) 

206 

207 # Parse results back in place 

208 src_data["targets"] = _result 

209 

210 _progress.end() 

211 

212 return 0 

213 

214 def print_summary(self): 

215 logger_cli.info(self.errors.get_summary(print_zeros=False)) 

216 

217 def print_details(self): 

218 # Detailed errors 

219 logger_cli.info( 

220 "\n{}\n".format( 

221 self.errors.get_errors() 

222 ) 

223 )