Merge "Adding cleanup of floating ips"
diff --git a/neutron/tests/tempest/api/test_network_ip_availability.py b/neutron/tests/tempest/api/test_network_ip_availability.py
index 324c3fd..b3f2182 100644
--- a/neutron/tests/tempest/api/test_network_ip_availability.py
+++ b/neutron/tests/tempest/api/test_network_ip_availability.py
@@ -16,6 +16,7 @@
 import netaddr
 
 from tempest.lib.common.utils import data_utils
+from tempest.lib.common.utils import test_utils
 from tempest.lib import exceptions as lib_exc
 from tempest import test
 
@@ -154,7 +155,7 @@
             return used_ip - 1 == used_ip_after_port_delete
 
         self.assertTrue(
-            test.call_until_true(
+            test_utils.call_until_true(
                 get_net_availability, DELETE_TIMEOUT, DELETE_SLEEP),
             msg="IP address did not become available after port delete")
 
diff --git a/neutron/tests/tempest/scenario/base.py b/neutron/tests/tempest/scenario/base.py
index 351e587..6cf73c9 100644
--- a/neutron/tests/tempest/scenario/base.py
+++ b/neutron/tests/tempest/scenario/base.py
@@ -18,6 +18,7 @@
 from tempest.common import waiters
 from tempest.lib.common import ssh
 from tempest.lib.common.utils import data_utils
+from tempest.lib import exceptions as lib_exc
 
 from neutron.tests.tempest.api import base as base_api
 from neutron.tests.tempest import config
@@ -149,11 +150,6 @@
         return fip
 
     @classmethod
-    def check_connectivity(cls, host, ssh_user, ssh_key=None):
-        ssh_client = ssh.Client(host, ssh_user, pkey=ssh_key)
-        ssh_client.test_connection_auth()
-
-    @classmethod
     def setup_network_and_server(cls, router=None, **kwargs):
         """Create network resources and a server.
 
@@ -189,3 +185,30 @@
                                      device_id=cls.server[
                                           'server']['id'])['ports'][0]
         cls.fip = cls.create_and_associate_floatingip(port['id'])
+
+    def check_connectivity(self, host, ssh_user, ssh_key, servers=None):
+        ssh_client = ssh.Client(host, ssh_user, pkey=ssh_key)
+        try:
+            ssh_client.test_connection_auth()
+        except lib_exc.SSHTimeout as ssh_e:
+            LOG.debug(ssh_e)
+            self._log_console_output(servers)
+            raise
+
+    def _log_console_output(self, servers=None):
+        if not CONF.compute_feature_enabled.console_output:
+            LOG.debug('Console output not supported, cannot log')
+            return
+        if not servers:
+            servers = self.manager.servers_client.list_servers()
+            servers = servers['servers']
+        for server in servers:
+            try:
+                console_output = (
+                    self.manager.servers_client.get_console_output(
+                        server['id'])['output'])
+                LOG.debug('Console output for %s\nbody=\n%s',
+                          server['id'], console_output)
+            except lib_exc.NotFound:
+                LOG.debug("Server %s disappeared(deleted) while looking "
+                          "for the console log", server['id'])
diff --git a/neutron/tests/tempest/scenario/test_qos.py b/neutron/tests/tempest/scenario/test_qos.py
index b558438..8b8e90a 100644
--- a/neutron/tests/tempest/scenario/test_qos.py
+++ b/neutron/tests/tempest/scenario/test_qos.py
@@ -38,7 +38,6 @@
         client_socket = socket.socket(socket.AF_INET,
                                       socket.SOCK_STREAM)
         client_socket.connect((host_ip, port))
-        client_socket.setblocking(0)
         return client_socket
     except socket.error as serr:
         if serr.errno == errno.ECONNREFUSED:
@@ -97,10 +96,6 @@
                 file=QoSTest.FILE_PATH)
 
     def _check_bw(self, ssh_client, host, port):
-        total_bytes_read = 0
-        cycle_start_time = time.time()
-        cycle_data_read = 0
-
         cmd = "killall -q nc"
         try:
             ssh_client.exec_command(cmd)
@@ -109,36 +104,26 @@
         cmd = ("(nc -ll -p %(port)d < %(file_path)s > /dev/null &)" % {
                 'port': port, 'file_path': QoSTest.FILE_PATH})
         ssh_client.exec_command(cmd)
+
+        start_time = time.time()
         client_socket = _connect_socket(host, port)
+        total_bytes_read = 0
 
         while total_bytes_read < QoSTest.FILE_SIZE:
-            try:
-                data = client_socket.recv(QoSTest.BUFFER_SIZE)
-            except socket.error as e:
-                if e.args[0] in [errno.EAGAIN, errno.EWOULDBLOCK]:
-                    continue
-                else:
-                    raise
+            data = client_socket.recv(QoSTest.BUFFER_SIZE)
             total_bytes_read += len(data)
-            cycle_data_read += len(data)
-            time_elapsed = time.time() - cycle_start_time
-            should_check = (time_elapsed >= 5 or
-                            total_bytes_read == QoSTest.FILE_SIZE)
-            if should_check:
-                LOG.debug("time_elapsed =  %(time_elapsed)d,"
-                          "total_bytes_read = %(bytes_read)d,"
-                          "cycle_data_read = %(cycle_data)d",
-                          {"time_elapsed": time_elapsed,
-                           "bytes_read": total_bytes_read,
-                           "cycle_data": cycle_data_read})
 
-                if cycle_data_read / time_elapsed > QoSTest.LIMIT_BYTES_SEC:
-                    # Limit reached
-                    return False
-                else:
-                    cycle_start_time = time.time()
-                    cycle_data_read = 0
-        return True
+        time_elapsed = time.time() - start_time
+        bytes_per_second = total_bytes_read / time_elapsed
+
+        LOG.debug("time_elapsed = %(time_elapsed)d, "
+                  "total_bytes_read = %(total_bytes_read)d, "
+                  "bytes_per_second = %(bytes_per_second)d",
+                  {'time_elapsed': time_elapsed,
+                   'total_bytes_read': total_bytes_read,
+                   'bytes_per_second': bytes_per_second})
+
+        return bytes_per_second <= QoSTest.LIMIT_BYTES_SEC
 
     @test.idempotent_id('1f7ed39b-428f-410a-bd2b-db9f465680df')
     def test_qos(self):
diff --git a/neutron/tests/tempest/scenario/test_trunk.py b/neutron/tests/tempest/scenario/test_trunk.py
index 408ef79..195882d 100644
--- a/neutron/tests/tempest/scenario/test_trunk.py
+++ b/neutron/tests/tempest/scenario/test_trunk.py
@@ -12,7 +12,8 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from oslo_log import log as logging
+import netaddr
+from tempest.common.utils.linux import remote_client
 from tempest.common import waiters
 from tempest.lib.common.utils import data_utils
 from tempest import test
@@ -23,7 +24,17 @@
 from neutron.tests.tempest.scenario import constants
 
 CONF = config.CONF
-LOG = logging.getLogger(__name__)
+
+CONFIGURE_VLAN_INTERFACE_COMMANDS = (
+    'IFACE=$(ip l | grep "^[0-9]*: e" | cut -d \: -f 2) && '
+    'sudo su -c '
+    '"ip l a link $IFACE name $IFACE.%(tag)d type vlan id %(tag)d && '
+    'ip l s up dev $IFACE.%(tag)d && '
+    'dhclient $IFACE.%(tag)d"')
+
+
+def get_next_subnet(cidr):
+    return netaddr.IPNetwork(cidr).next()
 
 
 class TrunkTest(base.BaseTempestTestCase):
@@ -50,18 +61,24 @@
         port = self.create_port(self.network, security_groups=[
             self.secgroup['security_group']['id']])
         trunk = self.client.create_trunk(port['id'], subports=[])['trunk']
-        fip = self.create_and_associate_floatingip(port['id'])
-        server = self.create_server(
-            flavor_ref=CONF.compute.flavor_ref,
-            image_ref=CONF.compute.image_ref,
-            key_name=self.keypair['name'],
-            networks=[{'port': port['id']}],
-            security_groups=[{'name': self.secgroup[
-                'security_group']['name']}])['server']
+        server, fip = self._create_server_with_fip(port['id'])
         self.addCleanup(self._detach_and_delete_trunk, server, trunk)
         return {'port': port, 'trunk': trunk, 'fip': fip,
                 'server': server}
 
+    def _create_server_with_fip(self, port_id, **server_kwargs):
+        fip = self.create_and_associate_floatingip(port_id)
+        return (
+            self.create_server(
+                flavor_ref=CONF.compute.flavor_ref,
+                image_ref=CONF.compute.image_ref,
+                key_name=self.keypair['name'],
+                networks=[{'port': port_id}],
+                security_groups=[{'name': self.secgroup[
+                    'security_group']['name']}],
+                **server_kwargs)['server'],
+            fip)
+
     def _detach_and_delete_trunk(self, server, trunk):
         # we have to detach the interface from the server before
         # the trunk can be deleted.
@@ -86,6 +103,44 @@
         t = self.client.show_trunk(trunk_id)['trunk']
         return t['status'] == 'ACTIVE'
 
+    def _create_server_with_port_and_subport(self, vlan_network, vlan_tag):
+        parent_port = self.create_port(self.network, security_groups=[
+            self.secgroup['security_group']['id']])
+        port_for_subport = self.create_port(
+            vlan_network,
+            security_groups=[self.secgroup['security_group']['id']],
+            mac_address=parent_port['mac_address'])
+        subport = {
+            'port_id': port_for_subport['id'],
+            'segmentation_type': 'vlan',
+            'segmentation_id': vlan_tag}
+        trunk = self.client.create_trunk(
+            parent_port['id'], subports=[subport])['trunk']
+
+        server, fip = self._create_server_with_fip(parent_port['id'])
+        self.addCleanup(self._detach_and_delete_trunk, server, trunk)
+
+        server_ssh_client = remote_client.RemoteClient(
+            fip['floating_ip_address'],
+            CONF.validation.image_ssh_user,
+            pkey=self.keypair['private_key'],
+            server=server)
+
+        return {
+            'server': server,
+            'fip': fip,
+            'ssh_client': server_ssh_client,
+            'subport': port_for_subport,
+        }
+
+    def _wait_for_server(self, server):
+        waiters.wait_for_server_status(self.manager.servers_client,
+                                       server['server']['id'],
+                                       constants.SERVER_STATUS_ACTIVE)
+        self.check_connectivity(server['fip']['floating_ip_address'],
+                                CONF.validation.image_ssh_user,
+                                self.keypair['private_key'])
+
     @test.idempotent_id('bb13fe28-f152-4000-8131-37890a40c79e')
     def test_trunk_subport_lifecycle(self):
         """Test trunk creation and subport transition to ACTIVE status.
@@ -102,12 +157,7 @@
         server1 = self._create_server_with_trunk_port()
         server2 = self._create_server_with_trunk_port()
         for server in (server1, server2):
-            waiters.wait_for_server_status(self.manager.servers_client,
-                                           server['server']['id'],
-                                           constants.SERVER_STATUS_ACTIVE)
-            self.check_connectivity(server['fip']['floating_ip_address'],
-                                    CONF.validation.image_ssh_user,
-                                    self.keypair['private_key'])
+            self._wait_for_server(server)
         trunk1_id, trunk2_id = server1['trunk']['id'], server2['trunk']['id']
         # trunks should transition to ACTIVE without any subports
         utils.wait_until_true(
@@ -167,3 +217,26 @@
         self.check_connectivity(server2['fip']['floating_ip_address'],
                                 CONF.validation.image_ssh_user,
                                 self.keypair['private_key'])
+
+    @test.idempotent_id('a8a02c9b-b453-49b5-89a2-cce7da66aafb')
+    def test_subport_connectivity(self):
+        vlan_tag = 10
+
+        vlan_network = self.create_network()
+        new_subnet_cidr = get_next_subnet(
+            config.safe_get_config_value('network', 'project_network_cidr'))
+        self.create_subnet(vlan_network, cidr=new_subnet_cidr)
+
+        servers = [
+            self._create_server_with_port_and_subport(vlan_network, vlan_tag)
+            for i in range(2)]
+
+        for server in servers:
+            self._wait_for_server(server)
+            # Configure VLAN interfaces on server
+            command = CONFIGURE_VLAN_INTERFACE_COMMANDS % {'tag': vlan_tag}
+            server['ssh_client'].exec_command(command)
+
+        # Ping from server1 to server2 via VLAN interface
+        servers[0]['ssh_client'].ping_host(
+            servers[1]['subport']['fixed_ips'][0]['ip_address'])