Merge "Add tests for user-initiated migrations" into mcp/antelope
diff --git a/requirements.txt b/requirements.txt
index abe25b5..83410e2 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -3,7 +3,6 @@
 # process, which may cause wedges in the gate later.
 pbr!=2.1.0,>=2.0.0 # Apache-2.0
 cliff!=2.9.0,>=2.8.0 # Apache-2.0
-ddt>=1.6.0 # MIT
 jsonschema>=3.2.0 # MIT
 testtools>=2.2.0 # MIT
 paramiko>=2.7.0 # LGPLv2.1+
diff --git a/tempest/api/compute/admin/test_live_migration.py b/tempest/api/compute/admin/test_live_migration.py
index cae9c06..369b4b1 100644
--- a/tempest/api/compute/admin/test_live_migration.py
+++ b/tempest/api/compute/admin/test_live_migration.py
@@ -15,8 +15,6 @@
 
 import time
 
-import ddt
-
 from oslo_log import log as logging
 import testtools
 
@@ -51,6 +49,11 @@
                 "Less than 2 compute nodes, skipping migration test.")
 
     @classmethod
+    def setup_credentials(cls):
+        cls.prepare_instance_network()
+        super(LiveMigrationTestBase, cls).setup_credentials()
+
+    @classmethod
     def setup_clients(cls):
         super(LiveMigrationTestBase, cls).setup_clients()
         cls.admin_migration_client = cls.os_admin.migrations_client
@@ -96,17 +99,6 @@
             self.assertEqual(target_host, self.get_host_for_server(server_id),
                              msg)
 
-
-@ddt.ddt
-class LiveMigrationTest(LiveMigrationTestBase):
-    max_microversion = '2.24'
-    block_migration = None
-
-    @classmethod
-    def setup_credentials(cls):
-        cls.prepare_instance_network()
-        super(LiveMigrationTest, cls).setup_credentials()
-
     def _test_live_migration(
             self, state='ACTIVE', volume_backed=False, image_id=None):
         """Tests live migration between two hosts.
@@ -145,16 +137,19 @@
             LOG.info("Live migrate back to source %s", source_host)
             self._live_migrate(server_id, source_host, state, volume_backed)
 
+
+class LiveMigrationTest(LiveMigrationTestBase):
+    max_microversion = '2.24'
+    block_migration = None
+
     @decorators.attr(type='multinode')
     @decorators.idempotent_id('1dce86b8-eb04-4c03-a9d8-9c1dc3ee0c7b')
     @testtools.skipUnless(CONF.compute_feature_enabled.
                           block_migration_for_live_migration,
                           'Block Live migration not available')
-    @ddt.data('image_ref', 'image_raw_ref')
-    def test_live_block_migration(self, image):
+    def test_live_block_migration(self):
         """Test live migrating an active server"""
-        image_id = getattr(CONF.compute, image)
-        self._test_live_migration(image_id=image_id)
+        self._test_live_migration()
 
     @decorators.attr(type='multinode')
     @decorators.idempotent_id('1e107f21-61b2-4988-8f22-b196e938ab88')
@@ -385,3 +380,53 @@
 
     if CONF.compute_feature_enabled.volume_multiattach:
         min_microversion = '2.60'
+
+
+class LiveMigrationTestRaw(LiveMigrationTestBase):
+    max_microversion = '2.24'
+    block_migration = None
+    image_id = CONF.compute.image_raw_ref
+
+    @classmethod
+    def skip_checks(cls):
+        super(LiveMigrationTestRaw, cls).skip_checks()
+
+        if not cls.image_id:
+            skip_msg = ("Environment doesn't have raw type image")
+            raise cls.skipException(skip_msg)
+
+    @decorators.attr(type='multinode')
+    @decorators.idempotent_id('6916bb20-fbf1-42df-958d-fa19acbbc09d')
+    @testtools.skipUnless(CONF.compute_feature_enabled.
+                          block_migration_for_live_migration,
+                          'Block Live migration not available')
+    def test_live_block_migration(self):
+        """Test live migrating an active server"""
+        self._test_live_migration(image_id=self.image_id)
+
+
+class LiveAutoBlockMigrationV225TestRaw(LiveMigrationTestBase):
+    min_microversion = '2.25'
+    max_microversion = 'latest'
+    block_migration = 'auto'
+    image_id = CONF.compute.image_raw_ref
+
+    if CONF.compute_feature_enabled.volume_multiattach:
+        min_microversion = '2.60'
+
+    @classmethod
+    def skip_checks(cls):
+        super(LiveAutoBlockMigrationV225TestRaw, cls).skip_checks()
+
+        if not cls.image_id:
+            skip_msg = ("Environment doesn't have raw type image")
+            raise cls.skipException(skip_msg)
+
+    @decorators.attr(type='multinode')
+    @decorators.idempotent_id('f7aeae41-83af-4574-87d1-bdd11d2f994e')
+    @testtools.skipUnless(CONF.compute_feature_enabled.
+                          block_migration_for_live_migration,
+                          'Block Live migration not available')
+    def test_live_block_migration(self):
+        """Test live migrating an active server"""
+        self._test_live_migration(image_id=self.image_id)
diff --git a/tempest/api/compute/admin/test_migrations.py b/tempest/api/compute/admin/test_migrations.py
index 551ea9d..0e9ea5a 100644
--- a/tempest/api/compute/admin/test_migrations.py
+++ b/tempest/api/compute/admin/test_migrations.py
@@ -12,7 +12,6 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import ddt
 import testtools
 
 from tempest.api.compute import base
@@ -25,15 +24,43 @@
 CONF = config.CONF
 
 
-@ddt.ddt
-class MigrationsAdminTest(base.BaseV2ComputeAdminTest):
+class MigrationsAdminTestBase(base.BaseV2ComputeAdminTest):
     """Test migration operations supported by admin user"""
 
     @classmethod
     def setup_clients(cls):
-        super(MigrationsAdminTest, cls).setup_clients()
+        super(MigrationsAdminTestBase, cls).setup_clients()
         cls.client = cls.os_admin.migrations_client
 
+    def _test_cold_migrate_server(self, revert=False, image_id=None):
+        if CONF.compute.min_compute_nodes < 2:
+            msg = "Less than 2 compute nodes, skipping multinode tests."
+            raise self.skipException(msg)
+
+        server = self.create_test_server(image_id=image_id,
+                                         wait_until="ACTIVE")
+        src_host = self.get_host_for_server(server['id'])
+
+        self.admin_servers_client.migrate_server(server['id'])
+
+        waiters.wait_for_server_status(self.servers_client,
+                                       server['id'], 'VERIFY_RESIZE')
+
+        if revert:
+            self.servers_client.revert_resize_server(server['id'])
+            assert_func = self.assertEqual
+        else:
+            self.servers_client.confirm_resize_server(server['id'])
+            assert_func = self.assertNotEqual
+
+        waiters.wait_for_server_status(self.servers_client,
+                                       server['id'], 'ACTIVE')
+        dst_host = self.get_host_for_server(server['id'])
+        assert_func(src_host, dst_host)
+
+
+class MigrationsAdminTest(MigrationsAdminTestBase):
+
     @decorators.idempotent_id('75c0b83d-72a0-4cf8-a153-631e83e7d53f')
     def test_list_migrations(self):
         """Test admin user can get the migrations list"""
@@ -137,48 +164,47 @@
                        'flavor!' % key)
                 self.assertEqual(pre_flavor[key], server['flavor'][key], msg)
 
-    def _test_cold_migrate_server(self, revert=False, image_id=None):
-        if CONF.compute.min_compute_nodes < 2:
-            msg = "Less than 2 compute nodes, skipping multinode tests."
-            raise self.skipException(msg)
-
-        server = self.create_test_server(image_id=image_id,
-                                         wait_until="ACTIVE")
-        src_host = self.get_host_for_server(server['id'])
-
-        self.admin_servers_client.migrate_server(server['id'])
-
-        waiters.wait_for_server_status(self.servers_client,
-                                       server['id'], 'VERIFY_RESIZE')
-
-        if revert:
-            self.servers_client.revert_resize_server(server['id'])
-            assert_func = self.assertEqual
-        else:
-            self.servers_client.confirm_resize_server(server['id'])
-            assert_func = self.assertNotEqual
-
-        waiters.wait_for_server_status(self.servers_client,
-                                       server['id'], 'ACTIVE')
-        dst_host = self.get_host_for_server(server['id'])
-        assert_func(src_host, dst_host)
-
     @decorators.attr(type='multinode')
     @decorators.idempotent_id('4bf0be52-3b6f-4746-9a27-3143636fe30d')
     @testtools.skipUnless(CONF.compute_feature_enabled.cold_migration,
                           'Cold migration not available.')
-    @ddt.data('image_ref', 'image_raw_ref')
-    def test_cold_migration(self, image):
+    def test_cold_migration(self):
         """Test cold migrating server and then confirm the migration"""
-        image_id = getattr(CONF.compute, image)
-        self._test_cold_migrate_server(revert=False, image_id=image_id)
+        self._test_cold_migrate_server(revert=False)
 
     @decorators.attr(type='multinode')
     @decorators.idempotent_id('caa1aa8b-f4ef-4374-be0d-95f001c2ac2d')
     @testtools.skipUnless(CONF.compute_feature_enabled.cold_migration,
                           'Cold migration not available.')
-    @ddt.data('image_ref', 'image_raw_ref')
-    def test_revert_cold_migration(self, image):
+    def test_revert_cold_migration(self):
         """Test cold migrating server and then revert the migration"""
-        image_id = getattr(CONF.compute, image)
-        self._test_cold_migrate_server(revert=True, image_id=image_id)
+        self._test_cold_migrate_server(revert=True)
+
+
+class MigrationsAdminTestRaw(MigrationsAdminTestBase):
+
+    image_id = CONF.compute.image_raw_ref
+
+    @classmethod
+    def skip_checks(cls):
+        super(MigrationsAdminTestRaw, cls).skip_checks()
+
+        if not cls.image_id:
+            skip_msg = ("Environment doesn't have raw type image")
+            raise cls.skipException(skip_msg)
+
+    @decorators.attr(type='multinode')
+    @decorators.idempotent_id('17f41ec8-734d-41cf-b18c-6c2fe3eb65c3')
+    @testtools.skipUnless(CONF.compute_feature_enabled.cold_migration,
+                          'Cold migration not available.')
+    def test_cold_migration(self):
+        """Test cold migrating server and then confirm the migration"""
+        self._test_cold_migrate_server(revert=False, image_id=self.image_id)
+
+    @decorators.attr(type='multinode')
+    @decorators.idempotent_id('c9d5dc79-d7fc-4f81-9a8d-e6e104ec4f98')
+    @testtools.skipUnless(CONF.compute_feature_enabled.cold_migration,
+                          'Cold migration not available.')
+    def test_revert_cold_migration(self):
+        """Test cold migrating server and then revert the migration"""
+        self._test_cold_migrate_server(revert=True, image_id=self.image_id)
diff --git a/tempest/api/compute/servers/test_server_actions.py b/tempest/api/compute/servers/test_server_actions.py
index f87e353..f8b621b 100644
--- a/tempest/api/compute/servers/test_server_actions.py
+++ b/tempest/api/compute/servers/test_server_actions.py
@@ -34,14 +34,14 @@
 LOG = logging.getLogger(__name__)
 
 
-class ServerActionsBaseTestJSON(base.BaseV2ComputeTest):
+class ServerActionsTestJSONBase(base.BaseV2ComputeTest):
 
     image_id = None
     if CONF.compute_feature_enabled.volume_multiattach:
         min_microversion = '2.60'
 
     def setUp(self):
-        super(ServerActionsBaseTestJSON, self).setUp()
+        super(ServerActionsTestJSONBase, self).setUp()
         # Instead of reusing an instance which had been created before a new
         # instance is created in favour of increasing stability of tests.
         self.validation_resources = self.get_test_validation_resources(
@@ -57,11 +57,11 @@
     @classmethod
     def setup_credentials(cls):
         cls.prepare_instance_network()
-        super(ServerActionsBaseTestJSON, cls).setup_credentials()
+        super(ServerActionsTestJSONBase, cls).setup_credentials()
 
     @classmethod
     def setup_clients(cls):
-        super(ServerActionsBaseTestJSON, cls).setup_clients()
+        super(ServerActionsTestJSONBase, cls).setup_clients()
         cls.client = cls.servers_client
 
     def _test_reboot_server(self, reboot_type):
@@ -218,7 +218,7 @@
         self.assertIn(parsed_url.scheme, valid_scheme)
 
 
-class ServerActionsTestJSON(ServerActionsBaseTestJSON):
+class ServerActionsTestJSON(ServerActionsTestJSONBase):
     """Test server actions"""
 
     @decorators.idempotent_id('6158df09-4b82-4ab3-af6d-29cf36af858d')
@@ -714,9 +714,6 @@
         self.assertNotEqual('', body['url'])
         self._validate_url(body['url'])
 
-
-class ServerActionsBaseImageTestJSON(ServerActionsBaseTestJSON):
-
     @decorators.attr(type='smoke')
     @decorators.idempotent_id('2cb1baf6-ac8d-4429-bf0d-ba8a0ba53e32')
     def test_reboot_server_hard(self):
@@ -773,10 +770,73 @@
         waiters.wait_for_server_status(self.client, self.server_id, 'ACTIVE')
 
 
-class ServerActionsRawImageTestJSON(ServerActionsBaseImageTestJSON):
+class ServerActionsTestJSONRaw(ServerActionsTestJSONBase):
     """Test server actions with raw image type"""
     image_id = CONF.compute.image_raw_ref
 
+    @classmethod
+    def skip_checks(cls):
+        super(ServerActionsTestJSONRaw, cls).skip_checks()
+
+        if not cls.image_id:
+            skip_msg = ("Environment doesn't have raw type image")
+            raise cls.skipException(skip_msg)
+
+    @decorators.attr(type='smoke')
+    @decorators.idempotent_id('b6a39851-a0d9-413f-bb2a-d260557201c2')
+    def test_reboot_server_hard(self):
+        """Test hard rebooting server
+
+        The server should be power cycled.
+        """
+        self._test_reboot_server('HARD')
+
+    @decorators.idempotent_id('38aa6f34-7160-44ad-bb95-c46d7760a838')
+    @testtools.skipUnless(CONF.compute_feature_enabled.shelve,
+                          'Shelve is not available.')
+    @utils.services('image')
+    def test_shelve_unshelve_server(self):
+        """Test shelving and unshelving server"""
+        if CONF.image_feature_enabled.api_v2:
+            glance_client = self.os_primary.image_client_v2
+        elif CONF.image_feature_enabled.api_v1:
+            glance_client = self.os_primary.image_client
+        else:
+            raise lib_exc.InvalidConfiguration(
+                'Either api_v1 or api_v2 must be True in '
+                '[image-feature-enabled].')
+        compute.shelve_server(self.client, self.server_id,
+                              force_shelve_offload=True)
+
+        def _unshelve_server():
+            server_info = self.client.show_server(self.server_id)['server']
+            if 'SHELVED' in server_info['status']:
+                self.client.unshelve_server(self.server_id)
+        self.addCleanup(_unshelve_server)
+
+        server = self.client.show_server(self.server_id)['server']
+        image_name = server['name'] + '-shelved'
+        params = {'name': image_name}
+        if CONF.image_feature_enabled.api_v2:
+            images = glance_client.list_images(params)['images']
+        elif CONF.image_feature_enabled.api_v1:
+            images = glance_client.list_images(
+                detail=True, **params)['images']
+        self.assertEqual(1, len(images))
+        self.assertEqual(image_name, images[0]['name'])
+
+        self.client.unshelve_server(self.server_id)
+        waiters.wait_for_server_status(self.client, self.server_id, 'ACTIVE')
+        glance_client.wait_for_resource_deletion(images[0]['id'])
+
+    @decorators.idempotent_id('441a3ae1-7f1e-4c99-9a3b-288c538ca192')
+    def test_stop_start_server(self):
+        """Test stopping and starting server"""
+        self.client.stop_server(self.server_id)
+        waiters.wait_for_server_status(self.client, self.server_id, 'SHUTOFF')
+        self.client.start_server(self.server_id)
+        waiters.wait_for_server_status(self.client, self.server_id, 'ACTIVE')
+
 
 class ServersAaction247Test(base.BaseV2ComputeTest):
     """Test compute server with microversion greater than 2.47
diff --git a/tempest/api/volume/admin/test_multi_backend.py b/tempest/api/volume/admin/test_multi_backend.py
index 83733bd..4bc2326 100644
--- a/tempest/api/volume/admin/test_multi_backend.py
+++ b/tempest/api/volume/admin/test_multi_backend.py
@@ -30,8 +30,9 @@
             raise cls.skipException("Cinder multi-backend feature disabled")
 
         if len(set(CONF.volume.backend_names)) < 2:
-            raise cls.skipException("Requires at least two different "
-                                    "backend names")
+            raise cls.skipException(
+                "Requires at least two different "
+                "backend names")
 
     @classmethod
     def resource_setup(cls):
@@ -54,29 +55,42 @@
     @classmethod
     def _create_type_and_volume(cls, backend_name_key, with_prefix):
         # Volume/Type creation
-        type_name = data_utils.rand_name(cls.__name__ + '-Type')
-        vol_name = data_utils.rand_name(cls.__name__ + '-Volume')
+        type_name = data_utils.rand_name(cls.__name__ + "-Type")
+        vol_name = data_utils.rand_name(cls.__name__ + "-Volume")
         spec_key_with_prefix = "capabilities:volume_backend_name"
         spec_key_without_prefix = "volume_backend_name"
         if with_prefix:
             extra_specs = {spec_key_with_prefix: backend_name_key}
         else:
             extra_specs = {spec_key_without_prefix: backend_name_key}
-        cls.create_volume_type(name=type_name,
-                               extra_specs=extra_specs)
+        cls.create_volume_type(
+            name=type_name, extra_specs=extra_specs)
+        # Pick up AZ from volume_type
+        services = cls.admin_volume_services_client.list_services()
+        vol_svrs = [
+            srv
+            for srv in services.get("services")
+            if srv["binary"] == "cinder-volume" and backend_name_key
+                                in srv["host"]
+        ]
+        vol_type_zone = vol_svrs[0]["zone"]
 
-        params = {'name': vol_name, 'volume_type': type_name,
-                  'size': CONF.volume.volume_size}
+        params = {
+            "name": vol_name,
+            "volume_type": type_name,
+            "size": CONF.volume.volume_size,
+            "availability_zone": vol_type_zone,
+        }
         cls.volume = cls.create_volume(**params)
         if with_prefix:
-            cls.volume_id_list_with_prefix.append(cls.volume['id'])
+            cls.volume_id_list_with_prefix.append(cls.volume["id"])
         else:
-            cls.volume_id_list_without_prefix.append(
-                cls.volume['id'])
-        waiters.wait_for_volume_resource_status(cls.admin_volume_client,
-                                                cls.volume['id'], 'available')
+            cls.volume_id_list_without_prefix.append(cls.volume["id"])
+        waiters.wait_for_volume_resource_status(
+            cls.admin_volume_client, cls.volume["id"], "available"
+        )
 
-    @decorators.idempotent_id('c1a41f3f-9dad-493e-9f09-3ff197d477cc')
+    @decorators.idempotent_id("c1a41f3f-9dad-493e-9f09-3ff197d477cc")
     def test_backend_name_reporting(self):
         """Test backend name reporting for volume when type is without prefix
 
@@ -88,7 +102,7 @@
         for volume_id in self.volume_id_list_without_prefix:
             self._test_backend_name_reporting_by_volume_id(volume_id)
 
-    @decorators.idempotent_id('f38e647f-ab42-4a31-a2e7-ca86a6485215')
+    @decorators.idempotent_id("f38e647f-ab42-4a31-a2e7-ca86a6485215")
     def test_backend_name_reporting_with_prefix(self):
         """Test backend name reporting for volume when type is with prefix
 
@@ -101,7 +115,7 @@
         for volume_id in self.volume_id_list_with_prefix:
             self._test_backend_name_reporting_by_volume_id(volume_id)
 
-    @decorators.idempotent_id('46435ab1-a0af-4401-8373-f14e66b0dd58')
+    @decorators.idempotent_id("46435ab1-a0af-4401-8373-f14e66b0dd58")
     def test_backend_name_distinction(self):
         """Test volume backend distinction when type is without prefix
 
@@ -112,7 +126,7 @@
         """
         self._test_backend_name_distinction(self.volume_id_list_without_prefix)
 
-    @decorators.idempotent_id('4236305b-b65a-4bfc-a9d2-69cb5b2bf2ed')
+    @decorators.idempotent_id("4236305b-b65a-4bfc-a9d2-69cb5b2bf2ed")
     def test_backend_name_distinction_with_prefix(self):
         """Test volume backend distinction when type is with prefix
 
@@ -124,28 +138,29 @@
         self._test_backend_name_distinction(self.volume_id_list_with_prefix)
 
     def _get_volume_host(self, volume_id):
-        return self.admin_volume_client.show_volume(
-            volume_id)['volume']['os-vol-host-attr:host']
+        return self.admin_volume_client.show_volume(volume_id)["volume"][
+            "os-vol-host-attr:host"
+        ]
 
     def _test_backend_name_reporting_by_volume_id(self, volume_id):
         # this test checks if os-vol-attr:host is populated correctly after
         # the multi backend feature has been enabled
         # if multi-backend is enabled: os-vol-attr:host should be like:
         # host@backend_name
-        volume = self.admin_volume_client.show_volume(volume_id)['volume']
+        volume = self.admin_volume_client.show_volume(volume_id)["volume"]
 
-        volume1_host = volume['os-vol-host-attr:host']
-        msg = ("multi-backend reporting incorrect values for volume %s" %
-               volume_id)
+        volume1_host = volume["os-vol-host-attr:host"]
+        msg = ("multi-backend reporting incorrect values for volume %s"
+               % volume_id)
         self.assertGreater(len(volume1_host.split("@")), 1, msg)
 
     def _test_backend_name_distinction(self, volume_id_list):
         # this test checks that the volumes created at setUp don't
         # belong to the same backend (if they are, than the
         # volume backend distinction is not working properly)
-        volume_hosts = [self._get_volume_host(volume) for volume in
-                        volume_id_list]
+        volume_hosts = [self._get_volume_host(volume)
+                        for volume in volume_id_list]
         # assert that volumes are each created on separate hosts:
-        msg = ("volumes %s were created in the same backend" % ", "
-               .join(volume_hosts))
+        msg = "volumes %s were created in the same backend" % ", ".join(
+            volume_hosts)
         self.assertCountEqual(volume_hosts, set(volume_hosts), msg)
diff --git a/tempest/api/volume/test_volume_transfers.py b/tempest/api/volume/test_volume_transfers.py
index 62cb203..d4634e6 100644
--- a/tempest/api/volume/test_volume_transfers.py
+++ b/tempest/api/volume/test_volume_transfers.py
@@ -32,10 +32,23 @@
         cls.alt_client = cls.os_alt.volume_transfers_client_latest
         cls.alt_volumes_client = cls.os_alt.volumes_client_latest
         cls.adm_volumes_client = cls.os_admin.volumes_client_latest
+        cls.volume_type_client = cls.os_admin.volume_types_client_latest
+        cls.encryption_client = cls.os_admin.encryption_types_client_latest
+
+    def _check_default_volume_type(self):
+        default_volume_type = self.volume_type_client.\
+            show_default_volume_type()["volume_type"]["id"]
+        volume_encryption = self.encryption_client.show_encryption_type(
+            default_volume_type)
+        if volume_encryption and volume_encryption.get("provider"):
+            raise self.skipException("Not allowed to run this test with "
+                                     "encrypted volume")
 
     @decorators.idempotent_id('4d75b645-a478-48b1-97c8-503f64242f1a')
     def test_create_get_list_accept_volume_transfer(self):
         """Test creating, getting, listing and accepting of volume transfer"""
+        self._check_default_volume_type()
+
         # Create a volume first
         volume = self.create_volume()
         self.addCleanup(self.delete_volume,
@@ -77,6 +90,8 @@
     @decorators.idempotent_id('ab526943-b725-4c07-b875-8e8ef87a2c30')
     def test_create_list_delete_volume_transfer(self):
         """Test creating, listing and deleting volume transfer"""
+        self._check_default_volume_type()
+
         # Create a volume first
         volume = self.create_volume()
         self.addCleanup(self.delete_volume,
diff --git a/tempest/config.py b/tempest/config.py
index 58576f9..352a754 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -895,6 +895,9 @@
                default=120,
                help="Total time in seconds to keep retrying a request that "
                     "returns HTTP 409 (Conflict)."),
+    cfg.IntOpt('service_ports_number',
+               default=0,
+               help="Number of neutron service ports created per network"),
 ]
 
 network_feature_group = cfg.OptGroup(name='network-feature-enabled',
diff --git a/tempest/lib/api_schema/response/volume/volume_types.py b/tempest/lib/api_schema/response/volume/volume_types.py
index 51b3a72..4d09bcd 100644
--- a/tempest/lib/api_schema/response/volume/volume_types.py
+++ b/tempest/lib/api_schema/response/volume/volume_types.py
@@ -31,8 +31,7 @@
         'qos_specs_id': {'type': ['string', 'null'], 'format': 'uuid'}
     },
     'additionalProperties': False,
-    'required': ['name', 'is_public', 'description', 'id',
-                 'os-volume-type-access:is_public']
+    'required': ['name', 'is_public', 'description', 'id']
 }
 
 show_volume_type = {
diff --git a/tempest/scenario/test_server_basic_ops.py b/tempest/scenario/test_server_basic_ops.py
index 32dbd3c..cfb3a59 100644
--- a/tempest/scenario/test_server_basic_ops.py
+++ b/tempest/scenario/test_server_basic_ops.py
@@ -13,8 +13,6 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import ddt
-
 from oslo_serialization import jsonutils as json
 
 from tempest.common import utils
@@ -29,8 +27,7 @@
 CONF = config.CONF
 
 
-@ddt.ddt
-class TestServerBasicOps(manager.ScenarioTestWithNetwork):
+class TestServerBasicOpsBase(manager.ScenarioTestWithNetwork):
 
     """The test suite for server basic operations
 
@@ -46,7 +43,7 @@
     """
 
     def setUp(self):
-        super(TestServerBasicOps, self).setUp()
+        super(TestServerBasicOpsBase, self).setUp()
         self.run_ssh = CONF.validation.run_validation
         self.ssh_user = CONF.validation.image_ssh_user
 
@@ -128,17 +125,51 @@
             # TODO(clarkb) construct network_data from known network
             # instance info and do direct comparison.
 
+
+class TestServerBasicOps(TestServerBasicOpsBase):
+
     @decorators.idempotent_id('7fff3fb3-91d8-4fd0-bd7d-0204f1f180ba')
     @decorators.attr(type='smoke')
-    @ddt.data('image_ref', 'image_raw_ref')
     @utils.services('compute', 'network')
-    def test_server_basic_ops(self, image):
+    def test_server_basic_ops(self):
         keypair = self.create_keypair()
         security_group = self.create_security_group()
         self.md = {'meta1': 'data1', 'meta2': 'data2', 'metaN': 'dataN'}
-        image_id = getattr(CONF.compute, image)
         self.instance = self.create_server(
-            image_id=image_id,
+            key_name=keypair['name'],
+            security_groups=[{'name': security_group['name']}],
+            config_drive=CONF.compute_feature_enabled.config_drive,
+            metadata=self.md)
+        self.verify_ssh(keypair)
+        self.verify_metadata()
+        self.verify_metadata_on_config_drive()
+        self.verify_networkdata_on_config_drive()
+        self.servers_client.delete_server(self.instance['id'])
+        waiters.wait_for_server_termination(
+            self.servers_client, self.instance['id'], ignore_error=False)
+
+
+class TestServerBasicOpsRaw(TestServerBasicOpsBase):
+
+    image_id = CONF.compute.image_raw_ref
+
+    @classmethod
+    def skip_checks(cls):
+        super(TestServerBasicOpsRaw, cls).skip_checks()
+
+        if not cls.image_id:
+            skip_msg = ("Environment doesn't have raw type image")
+            raise cls.skipException(skip_msg)
+
+    @decorators.idempotent_id('2d203d40-c3d4-4e2f-b22f-380c1a165f79')
+    @decorators.attr(type='smoke')
+    @utils.services('compute', 'network')
+    def test_server_basic_ops(self):
+        keypair = self.create_keypair()
+        security_group = self.create_security_group()
+        self.md = {'meta1': 'data1', 'meta2': 'data2', 'metaN': 'dataN'}
+        self.instance = self.create_server(
+            image_id=self.image_id,
             key_name=keypair['name'],
             security_groups=[{'name': security_group['name']}],
             config_drive=CONF.compute_feature_enabled.config_drive,
diff --git a/tempest/scenario/test_shelve_instance.py b/tempest/scenario/test_shelve_instance.py
index 4273a3a..3ba301a 100644
--- a/tempest/scenario/test_shelve_instance.py
+++ b/tempest/scenario/test_shelve_instance.py
@@ -13,7 +13,6 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import ddt
 import testtools
 
 from tempest.common import compute
@@ -26,8 +25,7 @@
 CONF = config.CONF
 
 
-@ddt.ddt
-class TestShelveInstance(manager.ScenarioTest):
+class TestShelveInstanceBase(manager.ScenarioTest):
     """This test shelves then unshelves a Nova instance
 
     The following is the scenario outline:
@@ -44,12 +42,12 @@
 
     @classmethod
     def setup_clients(cls):
-        super(TestShelveInstance, cls).setup_clients()
+        super(TestShelveInstanceBase, cls).setup_clients()
         cls.admin_servers_client = cls.os_admin.servers_client
 
     @classmethod
     def skip_checks(cls):
-        super(TestShelveInstance, cls).skip_checks()
+        super(TestShelveInstanceBase, cls).skip_checks()
         if not CONF.compute_feature_enabled.shelve:
             raise cls.skipException("Shelve is not available.")
 
@@ -106,15 +104,16 @@
                                         server=server)
         self.assertEqual(timestamp, timestamp2)
 
+
+class TestShelveInstance(TestShelveInstanceBase):
+
     @decorators.attr(type='slow')
     @decorators.idempotent_id('1164e700-0af0-4a4c-8792-35909a88743c')
     @testtools.skipUnless(CONF.network.public_network_id,
                           'The public_network_id option must be specified.')
-    @ddt.data('image_ref', 'image_raw_ref')
     @utils.services('compute', 'network', 'image')
-    def test_shelve_instance(self, image):
-        image_id = getattr(CONF.compute, image)
-        self._create_server_then_shelve_and_unshelve(image_id=image_id)
+    def test_shelve_instance(self):
+        self._create_server_then_shelve_and_unshelve()
 
     @decorators.attr(type='slow')
     @decorators.idempotent_id('c1b6318c-b9da-490b-9c67-9339b627271f')
@@ -138,3 +137,24 @@
     @utils.services('compute', 'network', 'image')
     def test_cold_migrate_unshelved_instance(self):
         self._create_server_then_shelve_and_unshelve(cold_migrate=True)
+
+
+class TestShelveInstanceRaw(TestShelveInstanceBase):
+
+    image_id = CONF.compute.image_raw_ref
+
+    @classmethod
+    def skip_checks(cls):
+        super(TestShelveInstanceRaw, cls).skip_checks()
+
+        if not cls.image_id:
+            skip_msg = ("Environment doesn't have raw type image")
+            raise cls.skipException(skip_msg)
+
+    @decorators.attr(type='slow')
+    @decorators.idempotent_id('ee0b7d90-54e3-4f50-abd0-cef03c793914')
+    @testtools.skipUnless(CONF.network.public_network_id,
+                          'The public_network_id option must be specified.')
+    @utils.services('compute', 'network', 'image')
+    def test_shelve_instance(self):
+        self._create_server_then_shelve_and_unshelve(image_id=self.image_id)
diff --git a/tox.ini b/tox.ini
index 6fa7faf..d2e2288 100644
--- a/tox.ini
+++ b/tox.ini
@@ -409,8 +409,7 @@
 
 [testenv:pep8]
 deps =
-    -r{toxinidir}/test-requirements.txt
-    -c{env:UPPER_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master}
+    {[testenv]deps}
     autopep8
 commands =
     autopep8 --exit-code --max-line-length=79 --experimental --diff -r tempest setup.py