Parsing Ping return codes and saving iteration details
- ping command detects error types and saves events
- exit on no packets to send
- ping now uses runtime map (as reclass can have DHCP set)
Change-Id: Iad66bd90d0c5a43e04fd785f02f8e1c2769dda62
Related-PROD: PROD-28199
diff --git a/cfg_checker/helpers/errors.py b/cfg_checker/helpers/errors.py
index 1f962eb..eaaa1eb 100644
--- a/cfg_checker/helpers/errors.py
+++ b/cfg_checker/helpers/errors.py
@@ -1,4 +1,5 @@
import os
+from configparser import NoSectionError
from cfg_checker.common import file_utils as fu
from cfg_checker.common import logger, logger_cli
@@ -62,9 +63,19 @@
self._area_code.lower(),
filepath=self._conf_filename
)
- # it is loaded, update iteration from file
- self._iteration = self.conf.get_value('iteration', value_type=int)
- self._iteration += 1
+ # check if there is any values there
+ try:
+ self._iteration = self.conf.get_value(
+ 'iteration',
+ value_type=int
+ )
+ self._iteration += 1
+ except NoSectionError:
+ self._iteration += 1
+ self.conf.set_value('iteration', self._iteration)
+ self.conf.save_config(filepath=self._conf_filename)
+ logger_cli.debug("... updated config file")
+
logger_cli.debug(" ... starting iteration {}".format(self._iteration))
def save_iteration_data(self):
@@ -189,14 +200,14 @@
_total_errors = self.get_errors_total()
_list.append('-'*20)
- _list.append("{:5d} total errors found\n".format(_total_errors))
+ _list.append("{:5d} total events found\n".format(_total_errors))
if as_list:
return _list
else:
return "\n".join(_list)
def get_errors(self, as_list=False):
- _list = ["# Errors"]
+ _list = ["# Events"]
# Detailed errors
if self.get_errors_total() > 0:
# create list of strings with error messages
@@ -204,7 +215,7 @@
_list.append(self._format_error(_idx))
_list.append("\n")
else:
- _list.append("-> No errors")
+ _list.append("-> No events saved")
if as_list:
return _list
diff --git a/cfg_checker/modules/network/__init__.py b/cfg_checker/modules/network/__init__.py
index b1664a4..6b06022 100644
--- a/cfg_checker/modules/network/__init__.py
+++ b/cfg_checker/modules/network/__init__.py
@@ -125,8 +125,13 @@
# Should generate network map to console or HTML
_map = mapper.NetworkMapper()
reclass = _map.map_network(_map.RECLASS)
+ runtime = _map.map_network(_map.RUNTIME)
+
_s = [str(_n) for _n in reclass.keys()]
- logger_cli.info("# Reclass networks list")
+ logger_cli.info("\n# Reclass networks list")
+ logger_cli.info("\n".join(_s))
+ _s = [str(_n) for _n in runtime.keys()]
+ logger_cli.info("\n# Runtime networks list")
logger_cli.info("\n".join(_s))
return
@@ -141,10 +146,23 @@
_cidr = args_utils.get_arg(args, "cidr")
_pinger = pinger.NetworkPinger(mtu=args.mtu, detailed=args.detailed)
- # TODO: Simple ping based on parameters
- _pinger.ping_nodes(_cidr)
+ _ret = _pinger.ping_nodes(_cidr)
- return
+ if _ret < 0:
+ # no need to save the iterations and summary
+ return
+ else:
+ # save what was collected
+ _pinger.errors.save_iteration_data()
+
+ # print a report
+ _pinger.print_summary()
+
+ # if set, print details
+ if args.detailed:
+ _pinger.print_error_details()
+
+ return
def do_trace(args):
diff --git a/cfg_checker/modules/network/mapper.py b/cfg_checker/modules/network/mapper.py
index c44775f..08cc99f 100644
--- a/cfg_checker/modules/network/mapper.py
+++ b/cfg_checker/modules/network/mapper.py
@@ -100,7 +100,7 @@
# get the reclass value
_pillar = salt_master.nodes[node]['pillars']['linux']['network']
# we should be ready if there is no interface in reclass for a node
- # for example on APT nohde
+ # for example on APT node
if 'interface' in _pillar:
_pillar = _pillar['interface']
else:
@@ -110,6 +110,8 @@
)
)
continue
+
+ # build map based on IPs
for _if_name, _if_data in _pillar.iteritems():
if 'address' in _if_data:
_if = ipaddress.IPv4Interface(
@@ -131,6 +133,8 @@
# class uses nodes from self.nodes dict
_confs = {}
+ # TODO: parse /etc/network/interfaces
+
return _confs
def _map_runtime_networks(self):
diff --git a/cfg_checker/modules/network/network_errors.py b/cfg_checker/modules/network/network_errors.py
index 6086be0..80f1199 100644
--- a/cfg_checker/modules/network/network_errors.py
+++ b/cfg_checker/modules/network/network_errors.py
@@ -16,6 +16,10 @@
NET_NODE_NON_RESPONSIVE = next(_c)
NET_NODE_UNEXPECTED_IF = next(_c)
NET_NO_RUNTIME_NETWORK = next(_c)
+ NET_PING_SUCCESS = next(_c)
+ NET_PING_TIMEOUT = next(_c)
+ NET_PING_ERROR = next(_c)
+ NET_PING_NOT_RESOLVED = next(_c)
def __init__(self):
super(NetworkErrors, self).__init__("NET")
@@ -52,6 +56,22 @@
self.NET_NO_RUNTIME_NETWORK,
"Reclass network not found in Runtime"
)
+ self.add_error_type(
+ self.NET_PING_SUCCESS,
+ "Network Ping successfull"
+ )
+ self.add_error_type(
+ self.NET_PING_TIMEOUT,
+ "Ping Timeout from source to target"
+ )
+ self.add_error_type(
+ self.NET_PING_ERROR,
+ "Error while conducting ping"
+ )
+ self.add_error_type(
+ self.NET_PING_NOT_RESOLVED,
+ "Host not resolved while conducting Ping"
+ )
del _c
diff --git a/cfg_checker/modules/network/pinger.py b/cfg_checker/modules/network/pinger.py
index 6d0287b..351c05a 100644
--- a/cfg_checker/modules/network/pinger.py
+++ b/cfg_checker/modules/network/pinger.py
@@ -1,18 +1,16 @@
import ipaddress
import json
-from cfg_checker.common import logger, logger_cli
+from cfg_checker.common import logger_cli
from cfg_checker.helpers.console_utils import Progress
from cfg_checker.modules.network.mapper import NetworkMapper
+from cfg_checker.modules.network.network_errors import NetworkErrors
from cfg_checker.nodes import salt_master
-# ping -s 9072 -M do -n -c 1 -w 1 -W 1 ctl01
-
-
# This is independent class with a salt.nodes input
class NetworkPinger(object):
- def __init__(self, mtu=None, detailed=False):
+ def __init__(self, mtu=None, detailed=False, errors_class=None):
logger_cli.info("# Initializing")
# all active nodes in the cloud
self.target_nodes = salt_master.get_nodes()
@@ -22,11 +20,17 @@
self.packet_size = int(self.target_mtu) - 20 - 8
self.detailed_summary = detailed
+ if errors_class:
+ self.errors = errors_class
+ else:
+ logger_cli.debug("... init error logs folder")
+ self.errors = NetworkErrors()
+
def _collect_node_addresses(self, target_net):
# use reclass model and standard methods
# to create list of nodes with target network
- _mapper = NetworkMapper()
- _reclass = _mapper.map_network(_mapper.RECLASS)
+ _mapper = NetworkMapper(errors_class=self.errors)
+ _reclass = _mapper.map_network(_mapper.RUNTIME)
if target_net in _reclass:
return _reclass[target_net]
else:
@@ -90,11 +94,17 @@
pass
logger_cli.info("-> {} packets to send".format(_count))
+ if not _count:
+ logger_cli.warning(
+ "\n# WARNING: No packets to send for '{}', "
+ "check network configuration\n".format(network_cidr_str)
+ )
+
+ return -1
+
# do ping of packets
logger_cli.info("# Pinging nodes: MTU={}".format(self.target_mtu))
salt_master.prepare_script_on_active_nodes("ping.py")
- _errors = []
- _success = []
_progress = Progress(_count)
_progress_index = 0
_node_index = 0
@@ -144,37 +154,53 @@
if len(_params["stderr"]) > 0:
_stderr = "stderr:\n{}\n".format(_params["stderr"])
- if _params["returncode"]:
- _errors.append("FAIL: {}{}{}".format(
- _body,
- _stdout,
- _stderr
- ))
+ if not _params["returncode"]:
+ # 0
+ self.errors.add_error(
+ self.errors.NET_PING_SUCCESS,
+ ping_path=_body,
+ stdout=_stdout,
+ stderr=_stderr
+ )
+ elif _params["returncode"] == 68:
+ # 68 is a 'can't resove host error'
+ self.errors.add_error(
+ self.errors.NET_PING_NOT_RESOLVED,
+ ping_path=_body,
+ stdout=_stdout,
+ stderr=_stderr
+ )
+ elif _params["returncode"] > 1:
+ # >1 is when no actial (any) response
+ self.errors.add_error(
+ self.errors.NET_PING_ERROR,
+ ping_path=_body,
+ stdout=_stdout,
+ stderr=_stderr
+ )
else:
- _success.append("PASS: {}{}{}".format(
- _body,
- _stdout,
- _stderr
- ))
+ # 1 is for timeouts amd/or packet lost
+ self.errors.add_error(
+ self.errors.NET_PING_TIMEOUT,
+ ping_path=_body,
+ stdout=_stdout,
+ stderr=_stderr
+ )
# Parse results back in place
src_data["targets"] = _result
_progress.end()
- if self.detailed_summary:
- logger_cli.info("\n{:=^8s}".format("PASS"))
- logger_cli.info("\n".join(_success))
- else:
- logger.info("\n{:=^8s}".format("PASS"))
- logger.info("\n".join(_success))
- if len(_errors) > 0:
- logger_cli.info("\n{:=^8s}".format("FAIL"))
- logger_cli.info("\n".join(_errors))
+ return 0
+ def print_summary(self):
+ logger_cli.info(self.errors.get_summary(print_zeros=False))
+
+ def print_details(self):
+ # Detailed errors
logger_cli.info(
- "# {} failed, {} passed".format(
- len(_errors),
- len(_success)
+ "\n{}\n".format(
+ self.errors.get_errors()
)
)
diff --git a/cfg_checker/modules/packages/repos.py b/cfg_checker/modules/packages/repos.py
index 334cbb5..025703e 100644
--- a/cfg_checker/modules/packages/repos.py
+++ b/cfg_checker/modules/packages/repos.py
@@ -821,29 +821,19 @@
# <10symbols> \t <md5> \t sorted headers with no tag
# ...
# section
- _ss = _p.keys()
- _ss.sort()
- for _s in _ss:
- _apps = _p[_s].keys()
- _apps.sort()
+ for _s in sorted(_p):
# app
- for _a in _apps:
+ for _a in sorted(_p[_s]):
_o = ""
_mm = []
# get and sort tags
- _vs = _p[_s][_a].keys()
- _vs.sort()
- for _v in _vs:
+ for _v in sorted(_p[_s][_a]):
_o += "\n" + " "*8 + _v + ':\n'
# get and sort tags
- _mds = _p[_s][_a][_v].keys()
- _mds.sort()
- for _md5 in _mds:
+ for _md5 in sorted(_p[_s][_a][_v]):
_o += " "*16 + _md5 + "\n"
# get and sort repo headers
- _rr = _p[_s][_a][_v][_md5].keys()
- _rr.sort()
- for _r in _rr:
+ for _r in sorted(_p[_s][_a][_v][_md5]):
_o += " "*24 + _r.replace('_', ' ') + '\n'
_m = _p[_s][_a][_v][_md5][_r]["maintainer"]
if _m not in _mm: