Merge "Decouple resources and preconditions in secgroup scenario"
diff --git a/HACKING.rst b/HACKING.rst
index fd63d64..8fbc9bb 100644
--- a/HACKING.rst
+++ b/HACKING.rst
@@ -36,11 +36,11 @@
 
 In most cases the very first issue is the most important information.
 
-Try to avoid using ``try`` blocks in the test cases, both the ``except``
-and ``finally`` block could replace the original exception,
+Try to avoid using ``try`` blocks in the test cases, as both the ``except``
+and ``finally`` blocks could replace the original exception,
 when the additional operations leads to another exception.
 
-Just letting an exception to propagate, is not bad idea in a test case,
+Just letting an exception to propagate, is not a bad idea in a test case,
 at all.
 
 Try to avoid using any exception handling construct which can hide the errors
@@ -54,10 +54,10 @@
 exceptions and still ensure resources are correctly cleaned up if the
 test fails part way through.
 
-Use the ``self.assert*`` methods provided by the unit test framework
-the signal failures early.
+Use the ``self.assert*`` methods provided by the unit test framework.
+This signals the failures early on.
 
-Avoid using the ``self.fail`` alone, it's stack trace will signal
+Avoid using the ``self.fail`` alone, its stack trace will signal
 the ``self.fail`` line as the origin of the error.
 
 Avoid constructing complex boolean expressions for assertion.
@@ -69,7 +69,7 @@
 Most other assert method can include more information by default.
 For example ``self.assertIn`` can include the whole set.
 
-Recommended to use testtools matcher for more tricky assertion.
+It is recommended to use testtools matcher for the more tricky assertions.
 `[doc] <http://testtools.readthedocs.org/en/latest/for-test-authors.html#matchers>`_
 
 You can implement your own specific matcher as well.
@@ -77,8 +77,8 @@
 
 If the test case fails you can see the related logs and the information
 carried by the exception (exception class, backtrack and exception info).
-This and the service logs are your only guide to find the root cause of flaky
-issue.
+This and the service logs are your only guide to finding the root cause of flaky
+issues.
 
 Test cases are independent
 --------------------------
@@ -87,7 +87,7 @@
 
 Test cases MAY depend on commonly initialized resources/facilities, like
 credentials management, testresources and so on. These facilities, MUST be able
-to work even if just one ``test_method`` selected for execution.
+to work even if just one ``test_method`` is selected for execution.
 
 Service Tagging
 ---------------
diff --git a/tempest/api/compute/servers/test_delete_server.py b/tempest/api/compute/servers/test_delete_server.py
index 6a5da58..55931a4 100644
--- a/tempest/api/compute/servers/test_delete_server.py
+++ b/tempest/api/compute/servers/test_delete_server.py
@@ -116,6 +116,7 @@
         self.assertEqual('204', resp['status'])
         self.client.wait_for_server_termination(server['id'])
 
+    @test.services('volume')
     @test.attr(type='gate')
     def test_delete_server_while_in_attached_volume(self):
         # Delete a server while a volume is attached to it
diff --git a/tempest/api/compute/servers/test_server_rescue.py b/tempest/api/compute/servers/test_server_rescue.py
index a984ade..5986f41 100644
--- a/tempest/api/compute/servers/test_server_rescue.py
+++ b/tempest/api/compute/servers/test_server_rescue.py
@@ -45,12 +45,6 @@
                                                              cls.sg_desc)
         cls.sg_id = cls.sg['id']
 
-        # Create a volume and wait for it to become ready for attach
-        resp, cls.volume = cls.volumes_extensions_client.create_volume(
-            1, display_name=data_utils.rand_name(cls.__name__ + '_volume'))
-        cls.volumes_extensions_client.wait_for_volume_status(
-            cls.volume['id'], 'available')
-
         # Server for positive tests
         resp, server = cls.create_test_server(wait_until='BUILD')
         cls.server_id = server['id']
@@ -64,8 +58,6 @@
     def resource_cleanup(cls):
         # Deleting the floating IP which is created in this method
         cls.floating_ips_client.delete_floating_ip(cls.floating_ip_id)
-        if getattr(cls, 'volume', None):
-            cls.delete_volume(cls.volume['id'])
         resp, cls.sg = cls.security_groups_client.delete_security_group(
             cls.sg_id)
         super(ServerRescueTestJSON, cls).resource_cleanup()
diff --git a/tempest/api/compute/servers/test_server_rescue_negative.py b/tempest/api/compute/servers/test_server_rescue_negative.py
index 0d29968..de43164 100644
--- a/tempest/api/compute/servers/test_server_rescue_negative.py
+++ b/tempest/api/compute/servers/test_server_rescue_negative.py
@@ -35,12 +35,6 @@
         super(ServerRescueNegativeTestJSON, cls).resource_setup()
         cls.device = CONF.compute.volume_device_name
 
-        # Create a volume and wait for it to become ready for attach
-        resp, cls.volume = cls.volumes_extensions_client.create_volume(
-            1, display_name=data_utils.rand_name(cls.__name__ + '_volume'))
-        cls.volumes_extensions_client.wait_for_volume_status(
-            cls.volume['id'], 'available')
-
         # Server for negative tests
         resp, server = cls.create_test_server(wait_until='BUILD')
         resp, resc_server = cls.create_test_server(wait_until='ACTIVE')
@@ -54,11 +48,14 @@
         cls.servers_client.wait_for_server_status(cls.rescue_id, 'RESCUE')
         cls.servers_client.wait_for_server_status(cls.server_id, 'ACTIVE')
 
-    @classmethod
-    def resource_cleanup(cls):
-        if getattr(cls, 'volume', None):
-            cls.delete_volume(cls.volume['id'])
-        super(ServerRescueNegativeTestJSON, cls).resource_cleanup()
+    def _create_volume(self):
+        resp, volume = self.volumes_extensions_client.create_volume(
+            1, display_name=data_utils.rand_name(
+                self.__class__.__name__ + '_volume'))
+        self.addCleanup(self.delete_volume, volume['id'])
+        self.volumes_extensions_client.wait_for_volume_status(
+            volume['id'], 'available')
+        return volume
 
     def _detach(self, server_id, volume_id):
         self.servers_client.detach_volume(server_id, volume_id)
@@ -108,8 +105,11 @@
                           self.rescue_id,
                           self.image_ref_alt)
 
+    @test.services('volume')
     @test.attr(type=['negative', 'gate'])
     def test_rescued_vm_attach_volume(self):
+        volume = self._create_volume()
+
         # Rescue the server
         self.servers_client.rescue_server(self.server_id,
                                           adminPass=self.password)
@@ -120,31 +120,34 @@
         self.assertRaises(exceptions.Conflict,
                           self.servers_client.attach_volume,
                           self.server_id,
-                          self.volume['id'],
+                          volume['id'],
                           device='/dev/%s' % self.device)
 
+    @test.services('volume')
     @test.attr(type=['negative', 'gate'])
     def test_rescued_vm_detach_volume(self):
+        volume = self._create_volume()
+
         # Attach the volume to the server
         self.servers_client.attach_volume(self.server_id,
-                                          self.volume['id'],
+                                          volume['id'],
                                           device='/dev/%s' % self.device)
         self.volumes_extensions_client.wait_for_volume_status(
-            self.volume['id'], 'in-use')
+            volume['id'], 'in-use')
 
         # Rescue the server
         self.servers_client.rescue_server(self.server_id,
                                           adminPass=self.password)
         self.servers_client.wait_for_server_status(self.server_id, 'RESCUE')
         # addCleanup is a LIFO queue
-        self.addCleanup(self._detach, self.server_id, self.volume['id'])
+        self.addCleanup(self._detach, self.server_id, volume['id'])
         self.addCleanup(self._unrescue, self.server_id)
 
         # Detach the volume from the server expecting failure
         self.assertRaises(exceptions.Conflict,
                           self.servers_client.detach_volume,
                           self.server_id,
-                          self.volume['id'])
+                          volume['id'])
 
 
 class ServerRescueNegativeTestXML(ServerRescueNegativeTestJSON):
diff --git a/tempest/api/compute/volumes/test_attach_volume.py b/tempest/api/compute/volumes/test_attach_volume.py
index 484c34d..75f9795 100644
--- a/tempest/api/compute/volumes/test_attach_volume.py
+++ b/tempest/api/compute/volumes/test_attach_volume.py
@@ -27,9 +27,7 @@
 
     def __init__(self, *args, **kwargs):
         super(AttachVolumeTestJSON, self).__init__(*args, **kwargs)
-        self.server = None
-        self.volume = None
-        self.attached = False
+        self.attachment = None
 
     @classmethod
     def resource_setup(cls):
@@ -41,13 +39,15 @@
             raise cls.skipException(skip_msg)
 
     def _detach(self, server_id, volume_id):
-        if self.attached:
+        if self.attachment:
             self.servers_client.detach_volume(server_id, volume_id)
             self.volumes_client.wait_for_volume_status(volume_id, 'available')
 
     def _delete_volume(self):
+        # Delete the created Volumes
         if self.volume:
             self.volumes_client.delete_volume(self.volume['id'])
+            self.volumes_client.wait_for_resource_deletion(self.volume['id'])
             self.volume = None
 
     def _create_and_attach(self):
@@ -57,8 +57,8 @@
                                                  adminPass=admin_pass)
 
         # Record addresses so that we can ssh later
-        _, self.server['addresses'] = \
-            self.servers_client.list_addresses(self.server['id'])
+        _, self.server['addresses'] = (
+            self.servers_client.list_addresses(self.server['id']))
 
         # Create a volume and wait for it to become ready
         _, self.volume = self.volumes_client.create_volume(
@@ -68,12 +68,12 @@
                                                    'available')
 
         # Attach the volume to the server
-        self.servers_client.attach_volume(self.server['id'],
-                                          self.volume['id'],
-                                          device='/dev/%s' % self.device)
+        _, self.attachment = self.servers_client.attach_volume(
+            self.server['id'],
+            self.volume['id'],
+            device='/dev/%s' % self.device)
         self.volumes_client.wait_for_volume_status(self.volume['id'], 'in-use')
 
-        self.attached = True
         self.addCleanup(self._detach, self.server['id'], self.volume['id'])
 
     @testtools.skipUnless(CONF.compute.run_ssh, 'SSH required for this test')
@@ -97,8 +97,7 @@
         self.assertIn(self.device, partitions)
 
         self._detach(self.server['id'], self.volume['id'])
-        self.attached = False
-
+        self.attachment = None
         self.servers_client.stop(self.server['id'])
         self.servers_client.wait_for_server_status(self.server['id'],
                                                    'SHUTOFF')
@@ -112,6 +111,25 @@
         partitions = linux_client.get_partitions()
         self.assertNotIn(self.device, partitions)
 
+    @test.skip_because(bug="1323591", interface="xml")
+    @test.attr(type='gate')
+    def test_list_get_volume_attachments(self):
+        # Create Server, Volume and attach that Volume to Server
+        self._create_and_attach()
+        # List Volume attachment of the server
+        _, body = self.servers_client.list_volume_attachments(
+            self.server['id'])
+        self.assertEqual(1, len(body))
+        self.assertIn(self.attachment, body)
+
+        # Get Volume attachment of the server
+        _, body = self.servers_client.get_volume_attachment(
+            self.server['id'],
+            self.attachment['id'])
+        self.assertEqual(self.server['id'], body['serverId'])
+        self.assertEqual(self.volume['id'], body['volumeId'])
+        self.assertEqual(self.attachment['id'], body['id'])
+
 
 class AttachVolumeTestXML(AttachVolumeTestJSON):
     _interface = 'xml'
diff --git a/tempest/api/network/base.py b/tempest/api/network/base.py
index c6480a1..7ba68f7 100644
--- a/tempest/api/network/base.py
+++ b/tempest/api/network/base.py
@@ -155,33 +155,35 @@
         return network
 
     @classmethod
-    def create_subnet(cls, network, gateway=None, cidr=None, mask_bits=None,
-                      **kwargs):
+    def create_subnet(cls, network, gateway='', cidr=None, mask_bits=None,
+                      ip_version=None, **kwargs):
         """Wrapper utility that returns a test subnet."""
         # The cidr and mask_bits depend on the ip version.
-        if cls._ip_version == 4:
+        ip_version = ip_version if ip_version is not None else cls._ip_version
+        gateway_not_set = gateway == ''
+        if ip_version == 4:
             cidr = cidr or netaddr.IPNetwork(CONF.network.tenant_network_cidr)
             mask_bits = mask_bits or CONF.network.tenant_network_mask_bits
-        elif cls._ip_version == 6:
+        elif ip_version == 6:
             cidr = (
                 cidr or netaddr.IPNetwork(CONF.network.tenant_network_v6_cidr))
             mask_bits = mask_bits or CONF.network.tenant_network_v6_mask_bits
         # Find a cidr that is not in use yet and create a subnet with it
         for subnet_cidr in cidr.subnet(mask_bits):
-            if not gateway:
-                gateway = str(netaddr.IPAddress(subnet_cidr) + 1)
+            if gateway_not_set:
+                gateway_ip = str(netaddr.IPAddress(subnet_cidr) + 1)
+            else:
+                gateway_ip = gateway
             try:
                 resp, body = cls.client.create_subnet(
                     network_id=network['id'],
                     cidr=str(subnet_cidr),
-                    ip_version=cls._ip_version,
-                    gateway_ip=gateway,
+                    ip_version=ip_version,
+                    gateway_ip=gateway_ip,
                     **kwargs)
                 break
             except exceptions.BadRequest as e:
                 is_overlapping_cidr = 'overlaps with another subnet' in str(e)
-                # Unset gateway value if there is an overlapping subnet
-                gateway = None
                 if not is_overlapping_cidr:
                     raise
         else:
diff --git a/tempest/api/network/test_networks.py b/tempest/api/network/test_networks.py
index 986a2c8..dd81a09 100644
--- a/tempest/api/network/test_networks.py
+++ b/tempest/api/network/test_networks.py
@@ -280,6 +280,10 @@
         self.subnets.pop()
 
     @test.attr(type='smoke')
+    def test_create_delete_subnet_without_gateway(self):
+        self._create_verify_delete_subnet()
+
+    @test.attr(type='smoke')
     def test_create_delete_subnet_with_gw(self):
         self._create_verify_delete_subnet(
             **self.subnet_dict(['gateway']))
@@ -492,7 +496,7 @@
         self.assertEqual(subnet['gateway_ip'], gateway)
 
     @test.attr(type='smoke')
-    def test_create_delete_subnet_without_gw(self):
+    def test_create_delete_subnet_with_default_gw(self):
         net = netaddr.IPNetwork(CONF.network.tenant_network_v6_cidr)
         gateway_ip = str(netaddr.IPAddress(net.first + 1))
         name = data_utils.rand_name('network-')
@@ -501,16 +505,62 @@
         # Verifies Subnet GW in IPv6
         self.assertEqual(subnet['gateway_ip'], gateway_ip)
 
+    @test.attr(type='smoke')
+    def test_create_list_subnet_with_no_gw64_one_network(self):
+        name = data_utils.rand_name('network-')
+        network = self.create_network(name)
+        ipv6_gateway = self.subnet_dict(['gateway'])['gateway']
+        subnet1 = self.create_subnet(network,
+                                     ip_version=6,
+                                     gateway=ipv6_gateway)
+        self.assertEqual(netaddr.IPNetwork(subnet1['cidr']).version, 6,
+                         'The created subnet is not IPv6')
+        subnet2 = self.create_subnet(network,
+                                     gateway=None,
+                                     ip_version=4)
+        self.assertEqual(netaddr.IPNetwork(subnet2['cidr']).version, 4,
+                         'The created subnet is not IPv4')
+        # Verifies Subnet GW is set in IPv6
+        self.assertEqual(subnet1['gateway_ip'], ipv6_gateway)
+        # Verifies Subnet GW is None in IPv4
+        self.assertEqual(subnet2['gateway_ip'], None)
+        # Verifies all 2 subnets in the same network
+        _, body = self.client.list_subnets()
+        subnets = [sub['id'] for sub in body['subnets']
+                   if sub['network_id'] == network['id']]
+        test_subnet_ids = [sub['id'] for sub in (subnet1, subnet2)]
+        self.assertItemsEqual(subnets,
+                              test_subnet_ids,
+                              'Subnet are not in the same network')
+
     @testtools.skipUnless(CONF.network_feature_enabled.ipv6_subnet_attributes,
                           "IPv6 extended attributes for subnets not "
                           "available")
     @test.attr(type='smoke')
-    def test_create_delete_subnet_with_v6_attributes(self):
+    def test_create_delete_subnet_with_v6_attributes_stateful(self):
         self._create_verify_delete_subnet(
             gateway=self._subnet_data[self._ip_version]['gateway'],
+            ipv6_ra_mode='dhcpv6-stateful',
+            ipv6_address_mode='dhcpv6-stateful')
+
+    @testtools.skipUnless(CONF.network_feature_enabled.ipv6_subnet_attributes,
+                          "IPv6 extended attributes for subnets not "
+                          "available")
+    @test.attr(type='smoke')
+    def test_create_delete_subnet_with_v6_attributes_slaac(self):
+        self._create_verify_delete_subnet(
             ipv6_ra_mode='slaac',
             ipv6_address_mode='slaac')
 
+    @testtools.skipUnless(CONF.network_feature_enabled.ipv6_subnet_attributes,
+                          "IPv6 extended attributes for subnets not "
+                          "available")
+    @test.attr(type='smoke')
+    def test_create_delete_subnet_with_v6_attributes_stateless(self):
+        self._create_verify_delete_subnet(
+            ipv6_ra_mode='dhcpv6-stateless',
+            ipv6_address_mode='dhcpv6-stateless')
+
 
 class NetworksIpV6TestXML(NetworksIpV6TestJSON):
     _interface = 'xml'
diff --git a/tempest/api/volume/admin/test_multi_backend.py b/tempest/api/volume/admin/test_multi_backend.py
index 042cde9..9e24993 100644
--- a/tempest/api/volume/admin/test_multi_backend.py
+++ b/tempest/api/volume/admin/test_multi_backend.py
@@ -66,13 +66,14 @@
 
         params = {self.name_field: vol_name, 'volume_type': type_name}
 
-        _, self.volume = self.volume_client.create_volume(size=1, **params)
+        _, self.volume = self.admin_volume_client.create_volume(size=1,
+                                                                **params)
         if with_prefix:
             self.volume_id_list_with_prefix.append(self.volume['id'])
         else:
             self.volume_id_list_without_prefix.append(
                 self.volume['id'])
-        self.volume_client.wait_for_volume_status(
+        self.admin_volume_client.wait_for_volume_status(
             self.volume['id'], 'available')
 
     @classmethod
@@ -80,13 +81,13 @@
         # volumes deletion
         vid_prefix = getattr(cls, 'volume_id_list_with_prefix', [])
         for volume_id in vid_prefix:
-            cls.volume_client.delete_volume(volume_id)
-            cls.volume_client.wait_for_resource_deletion(volume_id)
+            cls.admin_volume_client.delete_volume(volume_id)
+            cls.admin_volume_client.wait_for_resource_deletion(volume_id)
 
         vid_no_pre = getattr(cls, 'volume_id_list_without_prefix', [])
         for volume_id in vid_no_pre:
-            cls.volume_client.delete_volume(volume_id)
-            cls.volume_client.wait_for_resource_deletion(volume_id)
+            cls.admin_volume_client.delete_volume(volume_id)
+            cls.admin_volume_client.wait_for_resource_deletion(volume_id)
 
         # volume types deletion
         volume_type_id_list = getattr(cls, 'volume_type_id_list', [])
@@ -130,7 +131,7 @@
         # the multi backend feature has been enabled
         # if multi-backend is enabled: os-vol-attr:host should be like:
         # host@backend_name
-        _, volume = self.volume_client.get_volume(volume_id)
+        _, volume = self.admin_volume_client.get_volume(volume_id)
 
         volume1_host = volume['os-vol-host-attr:host']
         msg = ("multi-backend reporting incorrect values for volume %s" %
@@ -141,10 +142,10 @@
         # this test checks that the two volumes created at setUp don't
         # belong to the same backend (if they are, than the
         # volume backend distinction is not working properly)
-        _, volume = self.volume_client.get_volume(volume1_id)
+        _, volume = self.admin_volume_client.get_volume(volume1_id)
         volume1_host = volume['os-vol-host-attr:host']
 
-        _, volume = self.volume_client.get_volume(volume2_id)
+        _, volume = self.admin_volume_client.get_volume(volume2_id)
         volume2_host = volume['os-vol-host-attr:host']
 
         msg = ("volumes %s and %s were created in the same backend" %
diff --git a/tempest/api/volume/admin/test_volume_quotas.py b/tempest/api/volume/admin/test_volume_quotas.py
index ece4299..1189c8f 100644
--- a/tempest/api/volume/admin/test_volume_quotas.py
+++ b/tempest/api/volume/admin/test_volume_quotas.py
@@ -29,7 +29,6 @@
     @classmethod
     def resource_setup(cls):
         super(VolumeQuotasAdminTestJSON, cls).resource_setup()
-        cls.admin_volume_client = cls.os_adm.volumes_client
         cls.demo_tenant_id = cls.isolated_creds.get_primary_creds().tenant_id
 
     @test.attr(type='gate')
diff --git a/tempest/api/volume/admin/test_volumes_actions.py b/tempest/api/volume/admin/test_volumes_actions.py
index f85718b..3857fdb 100644
--- a/tempest/api/volume/admin/test_volumes_actions.py
+++ b/tempest/api/volume/admin/test_volumes_actions.py
@@ -26,9 +26,6 @@
         super(VolumesActionsTest, cls).resource_setup()
         cls.client = cls.volumes_client
 
-        # Create admin volume client
-        cls.admin_volume_client = cls.os_adm.volumes_client
-
         # Create a test shared volume for tests
         vol_name = utils.rand_name(cls.__name__ + '-Volume-')
 
diff --git a/tempest/api/volume/admin/test_volumes_backup.py b/tempest/api/volume/admin/test_volumes_backup.py
index 8b90b07..bf014a8 100644
--- a/tempest/api/volume/admin/test_volumes_backup.py
+++ b/tempest/api/volume/admin/test_volumes_backup.py
@@ -33,7 +33,6 @@
         if not CONF.volume_feature_enabled.backup:
             raise cls.skipException("Cinder backup feature disabled")
 
-        cls.volumes_adm_client = cls.os_adm.volumes_client
         cls.backups_adm_client = cls.os_adm.backups_client
         cls.volume = cls.create_volume()
 
@@ -47,8 +46,8 @@
         self.addCleanup(self.backups_adm_client.delete_backup,
                         backup['id'])
         self.assertEqual(backup_name, backup['name'])
-        self.volumes_adm_client.wait_for_volume_status(self.volume['id'],
-                                                       'available')
+        self.admin_volume_client.wait_for_volume_status(
+            self.volume['id'], 'available')
         self.backups_adm_client.wait_for_backup_status(backup['id'],
                                                        'available')
 
@@ -65,10 +64,10 @@
         _, restore = self.backups_adm_client.restore_backup(backup['id'])
 
         # Delete backup
-        self.addCleanup(self.volumes_adm_client.delete_volume,
+        self.addCleanup(self.admin_volume_client.delete_volume,
                         restore['volume_id'])
         self.assertEqual(backup['id'], restore['backup_id'])
         self.backups_adm_client.wait_for_backup_status(backup['id'],
                                                        'available')
-        self.volumes_adm_client.wait_for_volume_status(restore['volume_id'],
-                                                       'available')
+        self.admin_volume_client.wait_for_volume_status(
+            restore['volume_id'], 'available')
diff --git a/tempest/api/volume/base.py b/tempest/api/volume/base.py
index d78ddb6..8170cbf 100644
--- a/tempest/api/volume/base.py
+++ b/tempest/api/volume/base.py
@@ -174,14 +174,14 @@
                 raise cls.skipException(msg)
             cls.volume_qos_client = cls.os_adm.volume_qos_client
             cls.volume_types_client = cls.os_adm.volume_types_client
-            cls.volume_client = cls.os_adm.volumes_client
+            cls.admin_volume_client = cls.os_adm.volumes_client
         elif cls._api_version == 2:
             if not CONF.volume_feature_enabled.api_v2:
                 msg = "Volume API v2 is disabled"
                 raise cls.skipException(msg)
             cls.volume_qos_client = cls.os_adm.volume_qos_v2_client
             cls.volume_types_client = cls.os_adm.volume_types_v2_client
-            cls.volume_client = cls.os_adm.volumes_v2_client
+            cls.admin_volume_client = cls.os_adm.volumes_v2_client
 
     @classmethod
     def resource_cleanup(cls):
diff --git a/tempest/api_schema/response/compute/availability_zone.py b/tempest/api_schema/response/compute/availability_zone.py
index c1abc64..ab3e2ea 100644
--- a/tempest/api_schema/response/compute/availability_zone.py
+++ b/tempest/api_schema/response/compute/availability_zone.py
@@ -27,7 +27,7 @@
                     'properties': {
                         'available': {'type': 'boolean'},
                         'active': {'type': 'boolean'},
-                        'updated_at': {'type': 'string'}
+                        'updated_at': {'type': ['string', 'null']}
                     },
                     'required': ['available', 'active', 'updated_at']
                 }
diff --git a/tempest/api_schema/response/compute/services.py b/tempest/api_schema/response/compute/services.py
index eaba129..fc42b89 100644
--- a/tempest/api_schema/response/compute/services.py
+++ b/tempest/api_schema/response/compute/services.py
@@ -28,7 +28,7 @@
                         'state': {'type': 'string'},
                         'binary': {'type': 'string'},
                         'status': {'type': 'string'},
-                        'updated_at': {'type': 'string'},
+                        'updated_at': {'type': ['string', 'null']},
                         'disabled_reason': {'type': ['string', 'null']}
                     },
                     'required': ['id', 'zone', 'host', 'state', 'binary',
diff --git a/tempest/api_schema/response/compute/v2/servers.py b/tempest/api_schema/response/compute/v2/servers.py
index 5fc2008..09abaed 100644
--- a/tempest/api_schema/response/compute/v2/servers.py
+++ b/tempest/api_schema/response/compute/v2/servers.py
@@ -117,21 +117,23 @@
     }
 }
 
+common_attach_volume_info = {
+    'type': 'object',
+    'properties': {
+        'id': {'type': 'string'},
+        'device': {'type': 'string'},
+        'volumeId': {'type': 'string'},
+        'serverId': {'type': ['integer', 'string']}
+    },
+    'required': ['id', 'device', 'volumeId', 'serverId']
+}
+
 attach_volume = {
     'status_code': [200],
     'response_body': {
         'type': 'object',
         'properties': {
-            'volumeAttachment': {
-                'type': 'object',
-                'properties': {
-                    'id': {'type': 'string'},
-                    'device': {'type': 'string'},
-                    'volumeId': {'type': 'string'},
-                    'serverId': {'type': ['integer', 'string']}
-                },
-                'required': ['id', 'device', 'volumeId', 'serverId']
-            }
+            'volumeAttachment': common_attach_volume_info
         },
         'required': ['volumeAttachment']
     }
@@ -141,6 +143,27 @@
     'status_code': [202]
 }
 
+get_volume_attachment = copy.deepcopy(attach_volume)
+get_volume_attachment['response_body']['properties'][
+    'volumeAttachment']['properties'].update({'serverId': {'type': 'string'}})
+
+list_volume_attachments = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'volumeAttachments': {
+                'type': 'array',
+                'items': common_attach_volume_info
+            }
+        },
+        'required': ['volumeAttachments']
+    }
+}
+list_volume_attachments['response_body']['properties'][
+    'volumeAttachments']['items']['properties'].update(
+    {'serverId': {'type': 'string'}})
+
 set_get_server_metadata_item = {
     'status_code': [200],
     'response_body': {
diff --git a/tempest/clients.py b/tempest/clients.py
index 19b4e11..4269812 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -291,7 +291,8 @@
                 self.telemetry_client = TelemetryClientXML(
                     self.auth_provider)
             self.token_client = TokenClientXML()
-            self.token_v3_client = V3TokenClientXML()
+            if CONF.identity_feature_enabled.api_v3:
+                self.token_v3_client = V3TokenClientXML()
             self.volume_availability_zone_client = \
                 VolumeAvailabilityZoneClientXML(self.auth_provider)
             self.volume_v2_availability_zone_client = \
@@ -307,8 +308,6 @@
             self.servers_v3_client = ServersV3ClientJSON(self.auth_provider)
             self.limits_client = LimitsClientJSON(self.auth_provider)
             self.images_client = ImagesClientJSON(self.auth_provider)
-            self.keypairs_v3_client = KeyPairsV3ClientJSON(
-                self.auth_provider)
             self.keypairs_client = KeyPairsClientJSON(self.auth_provider)
             self.keypairs_v3_client = KeyPairsV3ClientJSON(
                 self.auth_provider)
@@ -397,7 +396,8 @@
                 self.telemetry_client = TelemetryClientJSON(
                     self.auth_provider)
             self.token_client = TokenClientJSON()
-            self.token_v3_client = V3TokenClientJSON()
+            if CONF.identity_feature_enabled.api_v3:
+                self.token_v3_client = V3TokenClientJSON()
             self.negative_client = rest_client.NegativeRestClient(
                 self.auth_provider)
             self.negative_client.service = service
diff --git a/tempest/cmd/javelin.py b/tempest/cmd/javelin.py
index 0adc7e0..6879db9 100755
--- a/tempest/cmd/javelin.py
+++ b/tempest/cmd/javelin.py
@@ -26,6 +26,7 @@
 import sys
 import unittest
 
+import netaddr
 import yaml
 
 import tempest.auth
@@ -34,14 +35,17 @@
 from tempest.openstack.common import log as logging
 from tempest.openstack.common import timeutils
 from tempest.services.compute.json import flavors_client
+from tempest.services.compute.json import security_groups_client
 from tempest.services.compute.json import servers_client
 from tempest.services.identity.json import identity_client
 from tempest.services.image.v2.json import image_client
+from tempest.services.network.json import network_client
 from tempest.services.object_storage import container_client
 from tempest.services.object_storage import object_client
 from tempest.services.telemetry.json import telemetry_client
 from tempest.services.volume.json import volumes_client
 
+CONF = config.CONF
 OPTS = {}
 USERS = {}
 RES = collections.defaultdict(list)
@@ -69,7 +73,9 @@
         self.images = image_client.ImageClientV2JSON(_auth)
         self.flavors = flavors_client.FlavorsClientJSON(_auth)
         self.telemetry = telemetry_client.TelemetryClientJSON(_auth)
+        self.secgroups = security_groups_client.SecurityGroupsClientJSON(_auth)
         self.volumes = volumes_client.VolumesClientJSON(_auth)
+        self.networks = network_client.NetworkClientJSON(_auth)
 
 
 def load_resources(fname):
@@ -90,6 +96,10 @@
     else:
         LOG.error("%s not found in USERS: %s" % (name, USERS))
 
+
+def resp_ok(response):
+    return 200 >= int(response['status']) < 300
+
 ###################
 #
 # TENANTS
@@ -212,12 +222,36 @@
     def runTest(self, *args):
         pass
 
+    def _ping_ip(self, ip_addr, count, namespace=None):
+        if namespace is None:
+            ping_cmd = "ping -c1 " + ip_addr
+        else:
+            ping_cmd = "sudo ip netns exec %s ping -c1 %s" % (namespace,
+                                                              ip_addr)
+        for current in range(count):
+            return_code = os.system(ping_cmd)
+            if return_code is 0:
+                break
+        self.assertNotEqual(current, count - 1,
+                            "Server is not pingable at %s" % ip_addr)
+
     def check(self):
         self.check_users()
         self.check_objects()
         self.check_servers()
         self.check_volumes()
         self.check_telemetry()
+        self.check_secgroups()
+
+        # validate neutron is enabled and ironic disabled:
+        # Tenant network isolation is not supported when using ironic.
+        # "admin" has set up a neutron flat network environment within a shared
+        # fixed network for all tenants to use.
+        # In this case, network/subnet/router creation can be skipped and the
+        # server booted the same as nova network.
+        if (CONF.service_available.neutron and
+                not CONF.baremetal.driver_enabled):
+            self.check_networking()
 
     def check_users(self):
         """Check that the users we expect to exist, do.
@@ -264,15 +298,32 @@
                 "Couldn't find expected server %s" % server['name'])
 
             r, found = client.servers.get_server(found['id'])
-            # get the ipv4 address
-            addr = found['addresses']['private'][0]['addr']
-            for count in range(60):
-                return_code = os.system("ping -c1 " + addr)
-                if return_code is 0:
-                    break
-            self.assertNotEqual(count, 59,
-                                "Server %s is not pingable at %s" % (
-                                    server['name'], addr))
+            # validate neutron is enabled and ironic disabled:
+            if (CONF.service_available.neutron and
+                    not CONF.baremetal.driver_enabled):
+                for network_name, body in found['addresses'].items():
+                    for addr in body:
+                        ip = addr['addr']
+                        if addr.get('OS-EXT-IPS:type', 'fixed') == 'fixed':
+                            namespace = _get_router_namespace(client,
+                                                              network_name)
+                            self._ping_ip(ip, 60, namespace)
+                        else:
+                            self._ping_ip(ip, 60)
+            else:
+                addr = found['addresses']['private'][0]['addr']
+                self._ping_ip(addr, 60)
+
+    def check_secgroups(self):
+        """Check that the security groups are still existing."""
+        LOG.info("Checking security groups")
+        for secgroup in self.res['secgroups']:
+            client = client_for_user(secgroup['owner'])
+            found = _get_resource_by_name(client.secgroups, 'security_groups',
+                                          secgroup['name'])
+            self.assertIsNotNone(
+                found,
+                "Couldn't find expected secgroup %s" % secgroup['name'])
 
     def check_telemetry(self):
         """Check that ceilometer provides a sane sample.
@@ -334,6 +385,17 @@
                 'timestamp should come before start of second javelin run'
             )
 
+    def check_networking(self):
+        """Check that the networks are still there."""
+        for res_type in ('networks', 'subnets', 'routers'):
+            for res in self.res[res_type]:
+                client = client_for_user(res['owner'])
+                found = _get_resource_by_name(client.networks, res_type,
+                                              res['name'])
+                self.assertIsNotNone(
+                    found,
+                    "Couldn't find expected resource %s" % res['name'])
+
 
 #######################
 #
@@ -440,6 +502,147 @@
 
 #######################
 #
+# NETWORKS
+#
+#######################
+
+def _get_router_namespace(client, network):
+    network_id = _get_resource_by_name(client.networks,
+                                       'networks', network)['id']
+    resp, n_body = client.networks.list_routers()
+    if not resp_ok(resp):
+        raise ValueError("unable to routers list: [%s] %s" % (resp, n_body))
+    for router in n_body['routers']:
+        router_id = router['id']
+        resp, r_body = client.networks.list_router_interfaces(router_id)
+        if not resp_ok(resp):
+            raise ValueError("unable to router interfaces list: [%s] %s" %
+                             (resp, r_body))
+        for port in r_body['ports']:
+            if port['network_id'] == network_id:
+                return "qrouter-%s" % router_id
+
+
+def _get_resource_by_name(client, resource, name):
+    get_resources = getattr(client, 'list_%s' % resource)
+    if get_resources is None:
+        raise AttributeError("client doesn't have method list_%s" % resource)
+    r, body = get_resources()
+    if not resp_ok(r):
+        raise ValueError("unable to list %s: [%s] %s" % (resource, r, body))
+    if isinstance(body, dict):
+        body = body[resource]
+    for res in body:
+        if name == res['name']:
+            return res
+    raise ValueError('%s not found in %s resources' % (name, resource))
+
+
+def create_networks(networks):
+    LOG.info("Creating networks")
+    for network in networks:
+        client = client_for_user(network['owner'])
+
+        # only create a network if the name isn't here
+        r, body = client.networks.list_networks()
+        if any(item['name'] == network['name'] for item in body['networks']):
+            LOG.warning("Dupplicated network name: %s" % network['name'])
+            continue
+
+        client.networks.create_network(name=network['name'])
+
+
+def destroy_networks(networks):
+    LOG.info("Destroying subnets")
+    for network in networks:
+        client = client_for_user(network['owner'])
+        network_id = _get_resource_by_name(client.networks, 'networks',
+                                           network['name'])['id']
+        client.networks.delete_network(network_id)
+
+
+def create_subnets(subnets):
+    LOG.info("Creating subnets")
+    for subnet in subnets:
+        client = client_for_user(subnet['owner'])
+
+        network = _get_resource_by_name(client.networks, 'networks',
+                                        subnet['network'])
+        ip_version = netaddr.IPNetwork(subnet['range']).version
+        # ensure we don't overlap with another subnet in the network
+        try:
+            client.networks.create_subnet(network_id=network['id'],
+                                          cidr=subnet['range'],
+                                          name=subnet['name'],
+                                          ip_version=ip_version)
+        except exceptions.BadRequest as e:
+            is_overlapping_cidr = 'overlaps with another subnet' in str(e)
+            if not is_overlapping_cidr:
+                raise
+
+
+def destroy_subnets(subnets):
+    LOG.info("Destroying subnets")
+    for subnet in subnets:
+        client = client_for_user(subnet['owner'])
+        subnet_id = _get_resource_by_name(client.networks,
+                                          'subnets', subnet['name'])['id']
+        client.networks.delete_subnet(subnet_id)
+
+
+def create_routers(routers):
+    LOG.info("Creating routers")
+    for router in routers:
+        client = client_for_user(router['owner'])
+
+        # only create a router if the name isn't here
+        r, body = client.networks.list_routers()
+        if any(item['name'] == router['name'] for item in body['routers']):
+            LOG.warning("Dupplicated router name: %s" % router['name'])
+            continue
+
+        client.networks.create_router(router['name'])
+
+
+def destroy_routers(routers):
+    LOG.info("Destroying routers")
+    for router in routers:
+        client = client_for_user(router['owner'])
+        router_id = _get_resource_by_name(client.networks,
+                                          'routers', router['name'])['id']
+        for subnet in router['subnet']:
+            subnet_id = _get_resource_by_name(client.networks,
+                                              'subnets', subnet)['id']
+            client.networks.remove_router_interface_with_subnet_id(router_id,
+                                                                   subnet_id)
+        client.networks.delete_router(router_id)
+
+
+def add_router_interface(routers):
+    for router in routers:
+        client = client_for_user(router['owner'])
+        router_id = _get_resource_by_name(client.networks,
+                                          'routers', router['name'])['id']
+
+        for subnet in router['subnet']:
+            subnet_id = _get_resource_by_name(client.networks,
+                                              'subnets', subnet)['id']
+            # connect routers to their subnets
+            client.networks.add_router_interface_with_subnet_id(router_id,
+                                                                subnet_id)
+        # connect routers to exteral network if set to "gateway"
+        if router['gateway']:
+            if CONF.network.public_network_id:
+                ext_net = CONF.network.public_network_id
+                client.networks._update_router(
+                    router_id, set_enable_snat=True,
+                    external_gateway_info={"network_id": ext_net})
+            else:
+                raise ValueError('public_network_id is not configured.')
+
+
+#######################
+#
 # SERVERS
 #
 #######################
@@ -473,10 +676,21 @@
 
         image_id = _get_image_by_name(client, server['image'])['id']
         flavor_id = _get_flavor_by_name(client, server['flavor'])['id']
-        resp, body = client.servers.create_server(server['name'], image_id,
-                                                  flavor_id)
+        # validate neutron is enabled and ironic disabled
+        kwargs = dict()
+        if (CONF.service_available.neutron and
+                not CONF.baremetal.driver_enabled and server.get('networks')):
+            get_net_id = lambda x: (_get_resource_by_name(
+                client.networks, 'networks', x)['id'])
+            kwargs['networks'] = [{'uuid': get_net_id(network)}
+                                  for network in server['networks']]
+        resp, body = client.servers.create_server(
+            server['name'], image_id, flavor_id, **kwargs)
         server_id = body['id']
         client.servers.wait_for_server_status(server_id, 'ACTIVE')
+        # create to security group(s) after server spawning
+        for secgroup in server['secgroups']:
+            client.servers.add_security_group(server_id, secgroup)
 
 
 def destroy_servers(servers):
@@ -496,6 +710,44 @@
                                                    ignore_error=True)
 
 
+def create_secgroups(secgroups):
+    LOG.info("Creating security groups")
+    for secgroup in secgroups:
+        client = client_for_user(secgroup['owner'])
+
+        # only create a security group if the name isn't here
+        # i.e. a security group may be used by another server
+        # only create a router if the name isn't here
+        r, body = client.secgroups.list_security_groups()
+        if any(item['name'] == secgroup['name'] for item in body):
+            LOG.warning("Security group '%s' already exists" %
+                        secgroup['name'])
+            continue
+
+        resp, body = client.secgroups.create_security_group(
+            secgroup['name'], secgroup['description'])
+        if not resp_ok(resp):
+            raise ValueError("Failed to create security group: [%s] %s" %
+                             (resp, body))
+        secgroup_id = body['id']
+        # for each security group, create the rules
+        for rule in secgroup['rules']:
+            ip_proto, from_port, to_port, cidr = rule.split()
+            client.secgroups.create_security_group_rule(
+                secgroup_id, ip_proto, from_port, to_port, cidr=cidr)
+
+
+def destroy_secgroups(secgroups):
+    LOG.info("Destroying security groups")
+    for secgroup in secgroups:
+        client = client_for_user(secgroup['owner'])
+        sg_id = _get_resource_by_name(client.secgroups,
+                                      'security_groups',
+                                      secgroup['name'])
+        # sg rules are deleted automatically
+        client.secgroups.delete_security_group(sg_id['id'])
+
+
 #######################
 #
 # VOLUMES
@@ -563,6 +815,15 @@
     # next create resources in a well known order
     create_objects(RES['objects'])
     create_images(RES['images'])
+
+    # validate neutron is enabled and ironic is disabled
+    if CONF.service_available.neutron and not CONF.baremetal.driver_enabled:
+        create_networks(RES['networks'])
+        create_subnets(RES['subnets'])
+        create_routers(RES['routers'])
+        add_router_interface(RES['routers'])
+
+    create_secgroups(RES['secgroups'])
     create_servers(RES['servers'])
     create_volumes(RES['volumes'])
     attach_volumes(RES['volumes'])
@@ -575,6 +836,11 @@
     destroy_images(RES['images'])
     destroy_objects(RES['objects'])
     destroy_volumes(RES['volumes'])
+    if CONF.service_available.neutron and not CONF.baremetal.driver_enabled:
+        destroy_routers(RES['routers'])
+        destroy_subnets(RES['subnets'])
+        destroy_networks(RES['networks'])
+    destroy_secgroups(RES['secgroups'])
     destroy_users(RES['users'])
     destroy_tenants(RES['tenants'])
     LOG.warn("Destroy mode incomplete")
diff --git a/tempest/cmd/resources.yaml b/tempest/cmd/resources.yaml
index 2d5e686..2d6664c 100644
--- a/tempest/cmd/resources.yaml
+++ b/tempest/cmd/resources.yaml
@@ -17,11 +17,17 @@
     tenant: discuss
 
 secgroups:
-  - angon:
+  - name: angon
     owner: javelin
+    description: angon
     rules:
       - 'icmp -1 -1 0.0.0.0/0'
       - 'tcp 22 22 0.0.0.0/0'
+  - name: baobab
+    owner: javelin
+    description: baobab
+    rules:
+      - 'tcp 80 80 0.0.0.0/0'
 
 # resources that we want to create
 images:
@@ -43,15 +49,45 @@
     owner: javelin
     gb: 2
     device: /dev/vdb
+networks:
+  - name: world1
+    owner: javelin
+  - name: world2
+    owner: javelin
+subnets:
+  - name: subnet1
+    range: 10.1.0.0/24
+    network: world1
+    owner: javelin
+  - name: subnet2
+    range: 192.168.1.0/24
+    network: world2
+    owner: javelin
+routers:
+  - name: connector
+    owner: javelin
+    gateway: true
+    subnet:
+      - subnet1
+      - subnet2
 servers:
   - name: peltast
     owner: javelin
     flavor: m1.small
     image: javelin_cirros
+    networks:
+      - world1
+    secgroups:
+      - angon
+      - baobab
   - name: hoplite
     owner: javelin
     flavor: m1.medium
     image: javelin_cirros
+    networks:
+      - world2
+    secgroups:
+      - angon
 objects:
   - container: jc1
     name: javelin1
diff --git a/tempest/common/utils/linux/remote_client.py b/tempest/common/utils/linux/remote_client.py
index 89904b2..6a238d0 100644
--- a/tempest/common/utils/linux/remote_client.py
+++ b/tempest/common/utils/linux/remote_client.py
@@ -91,7 +91,7 @@
         return self.exec_command(cmd)
 
     def get_mac_address(self):
-        cmd = "/sbin/ifconfig | awk '/HWaddr/ {print $5}'"
+        cmd = "/bin/ip addr | awk '/ether/ {print $2}'"
         return self.exec_command(cmd)
 
     def get_ip_list(self):
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index 990a392..2ebfdd1 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -411,9 +411,8 @@
 
     def nova_volume_attach(self):
         # TODO(andreaf) Device should be here CONF.compute.volume_device_name
-        _, volume_attachment = self.servers_client.attach_volume(
+        _, volume = self.servers_client.attach_volume(
             self.server['id'], self.volume['id'], '/dev/vdb')
-        volume = volume_attachment['volumeAttachment']
         self.assertEqual(self.volume['id'], volume['id'])
         self.volumes_client.wait_for_volume_status(volume['id'], 'in-use')
         # Refresh the volume after the attachment
diff --git a/tempest/scenario/test_minimum_basic.py b/tempest/scenario/test_minimum_basic.py
index 3725477..ead021e 100644
--- a/tempest/scenario/test_minimum_basic.py
+++ b/tempest/scenario/test_minimum_basic.py
@@ -78,9 +78,8 @@
 
     def nova_volume_attach(self):
         volume_device_path = '/dev/' + CONF.compute.volume_device_name
-        _, volume_attachment = self.servers_client.attach_volume(
+        _, volume = self.servers_client.attach_volume(
             self.server['id'], self.volume['id'], volume_device_path)
-        volume = volume_attachment['volumeAttachment']
         self.assertEqual(self.volume['id'], volume['id'])
         self.volumes_client.wait_for_volume_status(volume['id'], 'in-use')
         # Refresh the volume after the attachment
diff --git a/tempest/scenario/test_stamp_pattern.py b/tempest/scenario/test_stamp_pattern.py
index 8ea2814..7fc1edf 100644
--- a/tempest/scenario/test_stamp_pattern.py
+++ b/tempest/scenario/test_stamp_pattern.py
@@ -115,7 +115,6 @@
         # TODO(andreaf) we should use device from config instead if vdb
         _, attached_volume = self.servers_client.attach_volume(
             server['id'], volume['id'], device='/dev/vdb')
-        attached_volume = attached_volume['volumeAttachment']
         self.assertEqual(volume['id'], attached_volume['id'])
         self._wait_for_volume_status(attached_volume, 'in-use')
 
diff --git a/tempest/services/compute/json/servers_client.py b/tempest/services/compute/json/servers_client.py
index 947ba7a..4268b1a 100644
--- a/tempest/services/compute/json/servers_client.py
+++ b/tempest/services/compute/json/servers_client.py
@@ -369,7 +369,7 @@
                                post_body)
         body = json.loads(body)
         self.validate_response(schema.attach_volume, resp, body)
-        return resp, body
+        return resp, body['volumeAttachment']
 
     def detach_volume(self, server_id, volume_id):
         """Detaches a volume from a server instance."""
@@ -378,6 +378,22 @@
         self.validate_response(schema.detach_volume, resp, body)
         return resp, body
 
+    def get_volume_attachment(self, server_id, attach_id):
+        """Return details about the given volume attachment."""
+        resp, body = self.get('servers/%s/os-volume_attachments/%s' % (
+            str(server_id), attach_id))
+        body = json.loads(body)
+        self.validate_response(schema.get_volume_attachment, resp, body)
+        return resp, body['volumeAttachment']
+
+    def list_volume_attachments(self, server_id):
+        """Returns the list of volume attachments for a given instance."""
+        resp, body = self.get('servers/%s/os-volume_attachments' % (
+            str(server_id)))
+        body = json.loads(body)
+        self.validate_response(schema.list_volume_attachments, resp, body)
+        return resp, body['volumeAttachments']
+
     def add_security_group(self, server_id, name):
         """Adds a security group to the server."""
         return self.action(server_id, 'addSecurityGroup', None, name=name)
diff --git a/tempest/test.py b/tempest/test.py
index 1c6265d..db8736e 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -342,10 +342,12 @@
         """
         force_tenant_isolation = getattr(cls, 'force_tenant_isolation', None)
 
-        cls.isolated_creds = credentials.get_isolated_credentials(
-            name=cls.__name__, network_resources=cls.network_resources,
-            force_tenant_isolation=force_tenant_isolation,
-        )
+        if (not hasattr(cls, 'isolated_creds') or
+            not cls.isolated_creds.name == cls.__name__):
+            cls.isolated_creds = credentials.get_isolated_credentials(
+                name=cls.__name__, network_resources=cls.network_resources,
+                force_tenant_isolation=force_tenant_isolation,
+            )
 
         creds = cls.isolated_creds.get_primary_creds()
         params = dict(credentials=creds, service=cls._service)
diff --git a/tempest/tests/common/utils/linux/test_remote_client.py b/tempest/tests/common/utils/linux/test_remote_client.py
index 0db4cfa..e8650c5 100644
--- a/tempest/tests/common/utils/linux/test_remote_client.py
+++ b/tempest/tests/common/utils/linux/test_remote_client.py
@@ -117,7 +117,7 @@
 
         self.assertEqual(self.conn.get_mac_address(), macs)
         self._assert_exec_called_with(
-            "/sbin/ifconfig | awk '/HWaddr/ {print $5}'")
+            "/bin/ip addr | awk '/ether/ {print $2}'")
 
     def test_get_ip_list(self):
         ips = """1: lo: <LOOPBACK,UP,LOWER_UP> mtu 16436 qdisc noqueue
diff --git a/tools/check_logs.py b/tools/check_logs.py
index 7cf9d85..c8d3a1a 100755
--- a/tools/check_logs.py
+++ b/tools/check_logs.py
@@ -31,7 +31,7 @@
 is_grenade = os.environ.get('DEVSTACK_GATE_GRENADE') is not None
 dump_all_errors = True
 
-# As logs are made clean, add to this set
+# As logs are made clean, remove from this set
 allowed_dirty = set([
     'c-api',
     'ceilometer-acentral',
diff --git a/tox.ini b/tox.ini
index 9f52f0d..f75e868 100644
--- a/tox.ini
+++ b/tox.ini
@@ -91,7 +91,7 @@
 setenv = {[tempestenv]setenv}
 deps = {[tempestenv]deps}
 commands =
-    run-tempest-stress '{posargs}'
+    run-tempest-stress {posargs}
 
 [testenv:venv]
 commands = {posargs}