Updates for network checker

 - New checks for uniform MTU and Duplicate IPs
 - Updated mapping for K8s envs
 - K8s envs can skip networks using IF name keywords. Defaults to "docker"
 - Network listing now gives details on Names and used MTU

 Related-PROD: PROD-35288

Change-Id: Ida345ce1762038f744c460805d607d1439e434b8
diff --git a/cfg_checker/modules/network/__init__.py b/cfg_checker/modules/network/__init__.py
index 2b806d6..714c170 100644
--- a/cfg_checker/modules/network/__init__.py
+++ b/cfg_checker/modules/network/__init__.py
@@ -46,6 +46,14 @@
         help="Print error details after summary"
     )
 
+    net_check_parser.add_argument(
+        '--skip-ifs',
+        metavar='skip_ifs', default="docker",
+        help="String with keywords to skip networks which has interfaces "
+        "names with keywords as substrings. Example: 'eno' keyword will "
+        "cause to skip interface named 'eno1np0'. Example: 'docker'"
+    )
+
     net_report_parser = net_subparsers.add_parser(
         'report',
         help="Generate network check report"
@@ -101,6 +109,15 @@
         args,
         config
     )
+    # prepare skip keywords
+    _skip_ifs_keywords = []
+    for _str in args.skip_ifs.split(","):
+        _skip_ifs_keywords.append(_str)
+    logger_cli.info(
+        "-> Interface keywords skip list is '{}'".format(
+            ", ".join(_skip_ifs_keywords)
+        )
+    )
     # Start command
     logger_cli.info("# Network check to console")
     _skip, _skip_file = args_utils.get_skip_args(args)
@@ -110,7 +127,7 @@
         skip_list=_skip,
         skip_list_file=_skip_file
     )
-    netChecker.check_networks()
+    netChecker.check_networks(skip_keywords=_skip_ifs_keywords)
 
     # save what was collected
     netChecker.errors.save_iteration_data()
@@ -199,10 +216,22 @@
         logger_cli.info("\n".join(_s))
 
     runtime = _map.map_network(_map.RUNTIME)
-    _s = [str(_n) for _n in runtime.keys()]
     logger_cli.info("\n# Runtime networks list")
-    logger_cli.info("\n".join(_s))
-
+    for _net, _nodes in runtime.items():
+        _names = set()
+        _mtus = set()
+        for _node_name, _interfaces in _nodes.items():
+            for _if in _interfaces:
+                _names.add(_if["name"])
+                _mtus.add(_if["mtu"])
+        logger_cli.info(
+            "{:21}: {}, {}".format(
+                str(_net),
+                "/".join(list(_names)),
+                "/".join(list(_mtus))
+            )
+        )
+    # Done
     return
 
 
diff --git a/cfg_checker/modules/network/checker.py b/cfg_checker/modules/network/checker.py
index b5809f3..0b336f2 100644
--- a/cfg_checker/modules/network/checker.py
+++ b/cfg_checker/modules/network/checker.py
@@ -10,9 +10,78 @@
         logger_cli.debug("... init error logs folder")
         self.errors = NetworkErrors()
 
-    def check_networks(self, map=True):
+    def _check_duplicate_ips(self):
+        # shortcuts
+        logger_cli.debug("... checking for duplicate ips")
+        _map = self.mapper.map
+        for _net in _map.keys():
+            _ips = set()
+            for _node_name, _interfaces in _map[_net].items():
+                for _if in _interfaces:
+                    if _if["ip_address"] not in _ips:
+                        # there was no such ip yet
+                        _ips.add(_if["ip_address"])
+                    else:
+                        # this ip already used
+                        logger_cli.warning(
+                            "Warning: Duplicate ip address: "
+                            "'{}: {}' at {} -> {}".format(
+                                _if["interface"],
+                                _if["ip_address"],
+                                _net,
+                                _node_name
+                            )
+                        )
+                        self.errors.add_error(
+                            self.errors.NET_DUPLICATE_IP,
+                            network=_net,
+                            node_name=_node_name,
+                            if_name=_if["interface"],
+                            ip_address=_if["ip_address"]
+                        )
+        return
+
+    def _check_non_uniform_mtu(self):
+        # shortcuts
+        logger_cli.debug("... checking for duplicate ips")
+        _map = self.mapper.map
+        for _net in _map.keys():
+            _mtus = set()
+            for _node_name, _interfaces in _map[_net].items():
+                for _if in _interfaces:
+                    if len(_mtus) < 1:
+                        # this is the 1st iteration
+                        _mtus.add(_if["rt_mtu"])
+                    elif _if["rt_mtu"] not in _mtus:
+                        # this ip already used
+                        logger_cli.warning(
+                            "Non-uniform MTU value of '{}' in '{}': "
+                            "{}:{}:{}".format(
+                                _net,
+                                _node_name,
+                                _if["interface"],
+                                _if["ip_address"],
+                                _if["rt_mtu"]
+                            )
+                        )
+                        self.errors.add_error(
+                            self.errors.NET_DUPLICATE_IP,
+                            network=_net,
+                            node_name=_node_name,
+                            if_name=_if["interface"],
+                            ip_address=_if["ip_address"],
+                            mtu=_if["rt_mtu"]
+                        )
+        return
+
+    def check_networks(self, map=True, skip_keywords=None):
+        # Load map
         self.mapper.map_networks()
-        self.mapper.create_map()
+        self.mapper.create_map(skip_keywords=skip_keywords)
+        # Check for errors that is not detectable during mapping
+        self._check_duplicate_ips()
+        self._check_non_uniform_mtu()
+        # print map if requested
         if map:
             self.mapper.print_map()
 
diff --git a/cfg_checker/modules/network/mapper.py b/cfg_checker/modules/network/mapper.py
index fff6bb6..f0fd78d 100644
--- a/cfg_checker/modules/network/mapper.py
+++ b/cfg_checker/modules/network/mapper.py
@@ -196,7 +196,7 @@
             ))
         logger_cli.info("-> done collecting networks data")
 
-        logger_cli.info("-> mapping IPs")
+        logger_cli.info("-> mapping runtime network IPs")
         # match interfaces by IP subnets
         for host, node_data in self.master.nodes.items():
             if not self.master.is_node_available(host):
@@ -437,7 +437,9 @@
         return _result
 
     def map_networks(self):
+        logger_cli.info("-> Mapping reclass networks")
         self.map_network(self.RECLASS)
+        logger_cli.info("-> Mapping runtime networks")
         self.map_network(self.RUNTIME)
 
     def map_network(self, source):
@@ -455,7 +457,7 @@
         self.networks[source] = _networks
         return _networks
 
-    def create_map(self):
+    def create_map(self, skip_keywords=None):
         """Create all needed elements for map output
 
         :return: none
@@ -804,6 +806,7 @@
         return _result
 
     def map_networks(self):
+        logger_cli.info("-> Mapping runtime networks")
         self.map_network(self.RUNTIME)
 
     def map_network(self, source):
@@ -822,13 +825,15 @@
         self.networks[source] = _networks
         return _networks
 
-    def create_map(self):
+    def create_map(self, skip_keywords=None):
         """Create all needed elements for map output
 
         :return: none
         """
+        # shortcut
         _runtime = self.networks[self.RUNTIME]
-
+        # networks to skip
+        _net_skip_list = []
         # main networks, target vars
         _map = {}
         # No matter of proto, at least one IP will be present for the network
@@ -866,6 +871,11 @@
                 for _host in _a:
                     for _if in _host['ifs']:
                         _ip_str = str(_if.exploded)
+                        # Make sure we print VIP properly
+                        if _host['vip'] == _ip_str:
+                            _if_name = " "*7
+                            _if_name_suffix = ""
+                            _ip_str += " VIP"
 
                         # Save all data
                         _values = {
@@ -876,6 +886,7 @@
                             "ip_address": _ip_str,
                             "rt_mtu": _host['mtu'],
                             "status": _host['state'],
+                            "type": _host['type'],
                             "raw_data": _raw,
                         }
                         if node in _map[_net]:
@@ -884,7 +895,15 @@
                         else:
                             _map[_net][node] = [_values]
                         _notes = []
+                # process skips: if key substring found in interface name
+                # then skip the whole network.
+                if any([True for _w in skip_keywords if _w in _if_name]):
+                    _net_skip_list.append(_net)
 
+        # remove skipped networks from list
+        _net_skip_list = list(set(_net_skip_list))
+        for _net in _net_skip_list:
+            _map.pop(_net)
         # save map
         self.map = _map
         return
@@ -897,7 +916,7 @@
         """
         logger_cli.info("# Networks")
         logger_cli.info(
-            "    {0:8} {1:25} {2:25} {3:10} {4:10}".format(
+            "    {0:47} {1:12} {2:25} {3:5} {4:4}".format(
                 "Host",
                 "IF",
                 "IP",
@@ -912,7 +931,7 @@
                 _n = self.map[network][hostname]
                 for _i in _n:
                     # Host IF IP Proto MTU State Gate Def.Gate
-                    _text = "{:7} {:17} {:25} {:5} {:10}".format(
+                    _text = "{:10} {:2} {:25} {:5} {:4}".format(
                                 _i['interface'],
                                 _i['interface_note'],
                                 _i['ip_address'],
@@ -920,7 +939,7 @@
                                 _i['status']
                             )
                     logger_cli.info(
-                        "    {0:8} {1}".format(
+                        "    {0:47} {1}".format(
                             node,
                             _text
                         )
diff --git a/cfg_checker/modules/network/network_errors.py b/cfg_checker/modules/network/network_errors.py
index 7159a36..63966a8 100644
--- a/cfg_checker/modules/network/network_errors.py
+++ b/cfg_checker/modules/network/network_errors.py
@@ -12,6 +12,7 @@
     NET_MTU_EMPTY = next(_c)
     NET_NO_RC_IF_STATUS = next(_c)
     NET_DUPLICATE_IF = next(_c)
+    NET_DUPLICATE_IP = next(_c)
     NET_SUBNET_INTERSECT = next(_c)
     NET_MASK_MISMATCH = next(_c)
     NET_NODE_NON_RESPONSIVE = next(_c)
@@ -43,6 +44,10 @@
             "Duplicate interface specified"
         )
         self.add_error_type(
+            self.NET_DUPLICATE_IP,
+            "Duplicate ip address used on the subnet"
+        )
+        self.add_error_type(
             self.NET_SUBNET_INTERSECT,
             "Subnets intersection detected"
         )