Improve logging of network state on the test host

Now router, arp entries and IP addresses from all namespaces on
the host are logged in case of failure in connectivity or
remote connectivity check.
Also all this data will be now logged if checking hostname or
running nc will fail.

Change-Id: Id2c45cbd3ec6d1ae0e27bd5e47407faecb06b395
diff --git a/neutron_tempest_plugin/common/ip.py b/neutron_tempest_plugin/common/ip.py
index 83cd3d9..d981770 100644
--- a/neutron_tempest_plugin/common/ip.py
+++ b/neutron_tempest_plugin/common/ip.py
@@ -36,13 +36,19 @@
     sudo = 'sudo'
     ip_path = '/sbin/ip'
 
-    def __init__(self, ssh_client=None, timeout=None):
+    def __init__(self, ssh_client=None, timeout=None, namespace=None):
         self.ssh_client = ssh_client
         self.timeout = timeout
+        self.namespace = namespace
 
     def get_command(self, obj, *command):
-        command_line = '{sudo!s} {ip_path!r} {object!s} {command!s}'.format(
-            sudo=self.sudo, ip_path=self.ip_path, object=obj,
+        command_line = '{sudo!s} {ip_path!r} '.format(sudo=self.sudo,
+                                                     ip_path=self.ip_path)
+        if self.namespace:
+            command_line += 'netns exec {ns_name!s} {ip_path!r} '.format(
+                ns_name=self.namespace, ip_path=self.ip_path)
+        command_line += '{object!s} {command!s}'.format(
+            object=obj,
             command=subprocess.list2cmdline([str(c) for c in command]))
         return command_line
 
@@ -84,6 +90,13 @@
             self.add_address(address=subport_ip, device=subport_device)
         return subport_device
 
+    def list_namespaces(self):
+        namespaces_output = self.execute("netns")
+        ns_list = []
+        for ns_line in namespaces_output.split("\n"):
+            ns_list.append(ns_line.split(" ", 1)[0])
+        return ns_list
+
     def list_addresses(self, device=None, ip_addresses=None, port=None,
                        subnets=None):
         command = ['list']
@@ -308,20 +321,24 @@
                    netaddr.IPNetwork(subnet['cidr']).prefixlen)
 
 
-def arp_table():
+def arp_table(namespace=None):
     # 192.168.0.16  0x1  0x2  dc:a6:32:06:56:51  *  enp0s31f6
     regex_str = (r"([^ ]+)\s+(0x\d+)\s+(0x\d+)\s+(\w{2}\:\w{2}\:\w{2}\:\w{2}\:"
                  r"\w{2}\:\w{2})\s+([\w+\*]+)\s+([\-\w]+)")
     regex = re.compile(regex_str)
     arp_table = []
-    with open('/proc/net/arp', 'r') as proc_file:
-        for line in proc_file.readlines():
-            m = regex.match(line)
-            if m:
-                arp_table.append(ARPregister(
-                    ip_address=m.group(1), hw_type=m.group(2),
-                    flags=m.group(3), mac_address=m.group(4),
-                    mask=m.group(5), device=m.group(6)))
+    cmd = ""
+    if namespace:
+        cmd = "sudo ip netns exec %s " % namespace
+    cmd += "cat /proc/net/arp"
+    arp_entries = shell.execute(cmd).stdout.split("\n")
+    for line in arp_entries:
+        m = regex.match(line)
+        if m:
+            arp_table.append(ARPregister(
+                ip_address=m.group(1), hw_type=m.group(2),
+                flags=m.group(3), mac_address=m.group(4),
+                mask=m.group(5), device=m.group(6)))
     return arp_table
 
 
diff --git a/neutron_tempest_plugin/scenario/base.py b/neutron_tempest_plugin/scenario/base.py
index 78b766b..85d048a 100644
--- a/neutron_tempest_plugin/scenario/base.py
+++ b/neutron_tempest_plugin/scenario/base.py
@@ -294,10 +294,20 @@
                           "for the console log", server['id'])
 
     def _log_local_network_status(self):
-        local_routes = ip_utils.IPCommand().list_routes()
-        LOG.debug('Local routes:\n%s', '\n'.join(str(r) for r in local_routes))
+        self._log_ns_network_status()
+        for ns_name in ip_utils.IPCommand().list_namespaces():
+            self._log_ns_network_status(ns_name=ns_name)
+
+    def _log_ns_network_status(self, ns_name=None):
+        local_ips = ip_utils.IPCommand(namespace=ns_name).list_addresses()
+        LOG.debug('Namespace %s; IP Addresses:\n%s',
+                  ns_name, '\n'.join(str(r) for r in local_ips))
+        local_routes = ip_utils.IPCommand(namespace=ns_name).list_routes()
+        LOG.debug('Namespace %s; Local routes:\n%s',
+                  ns_name, '\n'.join(str(r) for r in local_routes))
         arp_table = ip_utils.arp_table()
-        LOG.debug('Local ARP table:\n%s', '\n'.join(str(r) for r in arp_table))
+        LOG.debug('Namespace %s; Local ARP table:\n%s',
+                  ns_name, '\n'.join(str(r) for r in arp_table))
 
     def _check_remote_connectivity(self, source, dest, count,
                                    should_succeed=True,
@@ -382,9 +392,11 @@
         except lib_exc.SSHTimeout as ssh_e:
             LOG.debug(ssh_e)
             self._log_console_output(servers)
+            self._log_local_network_status()
             raise
         except AssertionError:
             self._log_console_output(servers)
+            self._log_local_network_status()
             raise
 
     def ping_ip_address(self, ip_address, should_succeed=True,
@@ -475,11 +487,13 @@
             LOG.debug(ssh_e)
             if log_errors:
                 self._log_console_output(servers)
+                self._log_local_network_status()
             raise
         except AssertionError as assert_e:
             LOG.debug(assert_e)
             if log_errors:
                 self._log_console_output(servers)
+                self._log_local_network_status()
             raise
 
     def ensure_nc_listen(self, ssh_client, port, protocol, echo_msg=None,
@@ -507,6 +521,7 @@
         except lib_exc.SSHTimeout as ssh_e:
             LOG.debug(ssh_e)
             self._log_console_output(servers)
+            self._log_local_network_status()
             raise
 
     def nc_client(self, ip_address, port, protocol):