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/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()
             )
         )