Merge "Bring in tests against spice console" into mcp/epoxy
diff --git a/tempest/api/compute/admin/test_create_server.py b/tempest/api/compute/admin/test_create_server.py
index 3cc2928..aa358d7 100644
--- a/tempest/api/compute/admin/test_create_server.py
+++ b/tempest/api/compute/admin/test_create_server.py
@@ -172,3 +172,60 @@
         console_log = self.client.get_console_output(server['id'])['output']
         self.assertTrue(console_log, "Console output was empty.")
         self.assertIn(uefi_boot_loader, console_log)
+
+
+class WindowsServersBaseTest(base.BaseV2ComputeAdminTest):
+    """Test Windows OS guest servers"""
+
+    image_id = None
+    flavor_id = None
+
+    @classmethod
+    def skip_checks(cls):
+        super(WindowsServersBaseTest, cls).skip_checks()
+
+        if not (cls.image_id and cls.flavor_id):
+            skip_msg = ("Environment is not prepared for testing "
+                        "Windows servers")
+            raise cls.skipException(skip_msg)
+
+    @classmethod
+    def setup_credentials(cls):
+        cls.prepare_instance_network()
+        super(WindowsServersBaseTest, cls).setup_credentials()
+
+    @classmethod
+    def setup_clients(cls):
+        super(WindowsServersBaseTest, cls).setup_clients()
+        cls.client = cls.servers_client
+
+    def _test_create_server(self):
+        # Create the server and wait for it to become ready
+        validation_resources = self.get_class_validation_resources(
+            self.os_primary)
+        self.create_test_server(
+            image_id=self.image_id,
+            flavor=self.flavor_id,
+            validatable=True,
+            validation_resources=validation_resources,
+            wait_until='PINGABLE')
+
+
+class WindowsServers10Test(WindowsServersBaseTest):
+
+    image_id = CONF.compute.windows10_image_ref
+    flavor_id = CONF.compute.windows10_flavor_ref
+
+    @decorators.idempotent_id('4d54bcfa-08d3-48eb-b7a1-3568db4fc607')
+    def test_create_server(self):
+        self._test_create_server()
+
+
+class WindowsServers11Test(WindowsServersBaseTest):
+
+    image_id = CONF.compute.windows11_image_ref
+    flavor_id = CONF.compute.windows11_flavor_ref
+
+    @decorators.idempotent_id('1cff7fea-f251-4a05-a667-9b946913a3c5')
+    def test_create_server(self):
+        self._test_create_server()
diff --git a/tempest/api/compute/admin/test_live_migration.py b/tempest/api/compute/admin/test_live_migration.py
index f6a1ae9..20fa7bd 100644
--- a/tempest/api/compute/admin/test_live_migration.py
+++ b/tempest/api/compute/admin/test_live_migration.py
@@ -381,3 +381,6 @@
     min_microversion = '2.25'
     max_microversion = 'latest'
     block_migration = 'auto'
+
+    if CONF.compute_feature_enabled.volume_multiattach:
+        min_microversion = '2.60'
diff --git a/tempest/api/compute/admin/test_volume.py b/tempest/api/compute/admin/test_volume.py
index b0f20f6..9092f9c 100644
--- a/tempest/api/compute/admin/test_volume.py
+++ b/tempest/api/compute/admin/test_volume.py
@@ -43,6 +43,9 @@
 class AttachSCSIVolumeTestJSON(BaseAttachSCSIVolumeTest):
     """Test attaching scsi volume to server"""
 
+    if CONF.compute_feature_enabled.volume_multiattach:
+        min_microversion = '2.60'
+
     @testtools.skipIf(
         CONF.compute_feature_enabled.barbican_integration_enabled,
         "Not supported when barbican integration enabled.")
diff --git a/tempest/api/compute/admin/test_volume_swap.py b/tempest/api/compute/admin/test_volume_swap.py
index 0d8370d..d7abf72 100644
--- a/tempest/api/compute/admin/test_volume_swap.py
+++ b/tempest/api/compute/admin/test_volume_swap.py
@@ -80,6 +80,9 @@
 class TestVolumeSwap(TestVolumeSwapBase):
     """The test suite for swapping of volume with admin user"""
 
+    if CONF.compute_feature_enabled.volume_multiattach:
+        min_microversion = '2.60'
+
     # NOTE(mriedem): This is an uncommon scenario to call the compute API
     # to swap volumes directly; swap volume is primarily only for volume
     # live migration and retype callbacks from the volume service, and is slow
diff --git a/tempest/api/compute/admin/test_volumes_negative.py b/tempest/api/compute/admin/test_volumes_negative.py
index 8b07d9a..32892a6 100644
--- a/tempest/api/compute/admin/test_volumes_negative.py
+++ b/tempest/api/compute/admin/test_volumes_negative.py
@@ -27,6 +27,10 @@
 
     create_default_network = True
 
+    if CONF.compute_feature_enabled.volume_multiattach:
+        min_microversion = '2.60'
+        max_microversion = 'latest'
+
     @classmethod
     def setup_credentials(cls):
         cls.prepare_instance_network()
diff --git a/tempest/api/compute/images/test_images.py b/tempest/api/compute/images/test_images.py
index a90d500..8ce4434 100644
--- a/tempest/api/compute/images/test_images.py
+++ b/tempest/api/compute/images/test_images.py
@@ -157,6 +157,9 @@
         self.addCleanup(self.client.delete_image, image['id'])
         self.assertEqual(snapshot_name, image['name'])
 
+    @testtools.skipIf(
+        CONF.compute_feature_enabled.barbican_integration_enabled,
+        "Not supported when barbican integration enabled.")
     @decorators.idempotent_id('f3cac456-e3fe-4183-a7a7-a59f7f017088')
     def test_create_server_from_snapshot(self):
         # Create one server normally
diff --git a/tempest/api/compute/servers/test_delete_server.py b/tempest/api/compute/servers/test_delete_server.py
index 596d2bd..dfbed3b 100644
--- a/tempest/api/compute/servers/test_delete_server.py
+++ b/tempest/api/compute/servers/test_delete_server.py
@@ -29,6 +29,9 @@
     """Test deleting servers in various states"""
     create_default_network = True
 
+    if CONF.compute_feature_enabled.volume_multiattach:
+        min_microversion = '2.60'
+
     # NOTE: Server creations of each test class should be under 10
     # for preventing "Quota exceeded for instances"
 
diff --git a/tempest/api/compute/servers/test_device_tagging.py b/tempest/api/compute/servers/test_device_tagging.py
index d2fdd52..5f9bb4f 100644
--- a/tempest/api/compute/servers/test_device_tagging.py
+++ b/tempest/api/compute/servers/test_device_tagging.py
@@ -83,6 +83,10 @@
     # 2.32 and 2.36 inclusive; the 2.37 microversion broke tags for networks.
     max_microversion = '2.32'
 
+    if CONF.compute_feature_enabled.volume_multiattach:
+        min_microversion = '2.60'
+        max_microversion = 'latest'
+
     def verify_device_metadata(self, md_json):
         try:
             md_dict = json.loads(md_json)
@@ -297,6 +301,10 @@
     min_microversion = '2.42'
     max_microversion = 'latest'
 
+    if CONF.compute_feature_enabled.volume_multiattach:
+        min_microversion = '2.60'
+        max_microversion = 'latest'
+
 
 class TaggedAttachmentsTest(DeviceTaggingBase):
     """Test tagged attachments with compute microversion greater than 2.48"""
@@ -304,6 +312,10 @@
     min_microversion = '2.49'
     max_microversion = 'latest'
 
+    if CONF.compute_feature_enabled.volume_multiattach:
+        min_microversion = '2.60'
+        max_microversion = 'latest'
+
     @classmethod
     def skip_checks(cls):
         super(TaggedAttachmentsTest, cls).skip_checks()
diff --git a/tempest/api/compute/servers/test_server_actions.py b/tempest/api/compute/servers/test_server_actions.py
index 938e4b4..67f50ba 100644
--- a/tempest/api/compute/servers/test_server_actions.py
+++ b/tempest/api/compute/servers/test_server_actions.py
@@ -37,6 +37,9 @@
 class ServerActionsBase(base.BaseV2ComputeTest):
     """Test server actions"""
 
+    if CONF.compute_feature_enabled.volume_multiattach:
+        min_microversion = '2.60'
+
     def setUp(self):
         # NOTE(afazekas): Normally we use the same server with all test cases,
         # but if it has an issue, we build a new one
diff --git a/tempest/api/compute/servers/test_server_rescue.py b/tempest/api/compute/servers/test_server_rescue.py
index 9404ebd..646f1cd 100644
--- a/tempest/api/compute/servers/test_server_rescue.py
+++ b/tempest/api/compute/servers/test_server_rescue.py
@@ -29,6 +29,9 @@
 class ServerRescueTestBase(base.BaseV2ComputeTest):
     create_default_network = True
 
+    if CONF.compute_feature_enabled.volume_multiattach:
+        min_microversion = '2.60'
+
     @classmethod
     def skip_checks(cls):
         super(ServerRescueTestBase, cls).skip_checks()
diff --git a/tempest/api/compute/servers/test_server_rescue_negative.py b/tempest/api/compute/servers/test_server_rescue_negative.py
index fd05ec6..ba1a7fc 100644
--- a/tempest/api/compute/servers/test_server_rescue_negative.py
+++ b/tempest/api/compute/servers/test_server_rescue_negative.py
@@ -30,6 +30,9 @@
 class ServerRescueNegativeTestJSON(base.BaseV2ComputeTest):
     """Negative tests of server rescue"""
 
+    if CONF.compute_feature_enabled.volume_multiattach:
+        min_microversion = '2.60'
+
     @classmethod
     def skip_checks(cls):
         super(ServerRescueNegativeTestJSON, cls).skip_checks()
diff --git a/tempest/api/compute/volumes/test_attach_volume.py b/tempest/api/compute/volumes/test_attach_volume.py
index 81b5c9d..22a8e74 100644
--- a/tempest/api/compute/volumes/test_attach_volume.py
+++ b/tempest/api/compute/volumes/test_attach_volume.py
@@ -61,6 +61,9 @@
 class AttachVolumeTestJSON(BaseAttachVolumeTest):
     """Test attaching volume to server"""
 
+    if CONF.compute_feature_enabled.volume_multiattach:
+        min_microversion = '2.60'
+
     @decorators.idempotent_id('52e9045a-e90d-4c0d-9087-79d657faffff')
     # This test is conditionally marked slow if SSH validation is enabled.
     @decorators.attr(type='slow', condition=CONF.validation.run_validation)
diff --git a/tempest/api/compute/volumes/test_attach_volume_negative.py b/tempest/api/compute/volumes/test_attach_volume_negative.py
index 43b4bf5..09bf488 100644
--- a/tempest/api/compute/volumes/test_attach_volume_negative.py
+++ b/tempest/api/compute/volumes/test_attach_volume_negative.py
@@ -23,6 +23,9 @@
 class AttachVolumeNegativeTest(test_attach_volume.BaseAttachVolumeTest):
     """Negative tests of volume attaching"""
 
+    if CONF.compute_feature_enabled.volume_multiattach:
+        min_microversion = '2.60'
+
     @decorators.attr(type=['negative'])
     @decorators.related_bug('1630783', status_code=500)
     @decorators.idempotent_id('a313b5cd-fbd0-49cc-94de-870e99f763c7')
diff --git a/tempest/api/network/test_tags.py b/tempest/api/network/test_tags.py
index a0c6342..3e7b2d5 100644
--- a/tempest/api/network/test_tags.py
+++ b/tempest/api/network/test_tags.py
@@ -113,7 +113,10 @@
 
     # NOTE(felipemonteiro): The supported resource names are plural. Use
     # the singular case for the corresponding class resource object.
-    SUPPORTED_RESOURCES = ['subnets', 'ports', 'routers', 'subnetpools']
+    if config.is_tungstenfabric_backend_enabled():
+        SUPPORTED_RESOURCES = ['subnets', 'ports', 'routers']
+    else:
+        SUPPORTED_RESOURCES = ['subnets', 'ports', 'routers', 'subnetpools']
 
     @classmethod
     def skip_checks(cls):
@@ -134,6 +137,9 @@
         cls.port = cls.create_port(cls.network)
         cls.router = cls.create_router()
 
+        if config.is_tungstenfabric_backend_enabled():
+            return
+
         subnetpool_name = data_utils.rand_name(
             cls.__name__ + '-Subnetpool', prefix=CONF.resource_name_prefix)
         prefix = CONF.network.default_network
diff --git a/tempest/api/volume/admin/test_multi_backend.py b/tempest/api/volume/admin/test_multi_backend.py
index 028bf1a..b45824e 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):
@@ -66,21 +67,34 @@
             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
 
@@ -92,7 +106,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
 
@@ -105,7 +119,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
 
@@ -116,7 +130,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
 
@@ -128,28 +142,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/base.py b/tempest/api/volume/base.py
index 7a08545..b93ade5 100644
--- a/tempest/api/volume/base.py
+++ b/tempest/api/volume/base.py
@@ -267,6 +267,12 @@
             kwargs['validatable'] = True
 
         tenant_network = self.get_tenant_network()
+
+        # Since microversion v2.37 'networks' field is required
+        if self.compute_request_microversion >= '2.37' and 'networks'\
+                not in kwargs:
+            kwargs['networks'] = 'none'
+
         body, _ = compute.create_test_server(
             self.os_primary,
             tenant_network=tenant_network,
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/api/volume/test_volumes_backup.py b/tempest/api/volume/test_volumes_backup.py
index a3ba974..eb581ee 100644
--- a/tempest/api/volume/test_volumes_backup.py
+++ b/tempest/api/volume/test_volumes_backup.py
@@ -29,6 +29,8 @@
     """Test volumes backup"""
 
     create_default_network = True
+    if CONF.compute_feature_enabled.volume_multiattach:
+        min_microversion = '2.60'
 
     @classmethod
     def skip_checks(cls):
diff --git a/tempest/api/volume/test_volumes_extend.py b/tempest/api/volume/test_volumes_extend.py
index c766db8..02b8745 100644
--- a/tempest/api/volume/test_volumes_extend.py
+++ b/tempest/api/volume/test_volumes_extend.py
@@ -180,6 +180,9 @@
 
 class VolumesExtendAttachedTest(BaseVolumesExtendAttachedTest):
 
+    if CONF.compute_feature_enabled.volume_multiattach:
+        min_microversion = '2.60'
+
     @classmethod
     def skip_checks(cls):
         super(VolumesExtendAttachedTest, cls).skip_checks()
diff --git a/tempest/api/volume/test_volumes_snapshots.py b/tempest/api/volume/test_volumes_snapshots.py
index 35afffd..3e06304 100644
--- a/tempest/api/volume/test_volumes_snapshots.py
+++ b/tempest/api/volume/test_volumes_snapshots.py
@@ -29,6 +29,9 @@
 
     create_default_network = True
 
+    if CONF.compute_feature_enabled.volume_multiattach:
+        min_microversion = '2.60'
+
     @classmethod
     def skip_checks(cls):
         super(VolumesSnapshotTestJSON, cls).skip_checks()
diff --git a/tempest/common/credentials_factory.py b/tempest/common/credentials_factory.py
index 2d486a7..449bb90 100644
--- a/tempest/common/credentials_factory.py
+++ b/tempest/common/credentials_factory.py
@@ -87,7 +87,8 @@
         ('create_networks', (CONF.auth.create_isolated_networks and not
                              CONF.network.shared_physical_network)),
         ('resource_prefix', 'tempest'),
-        ('identity_admin_endpoint_type', endpoint_type)
+        ('identity_admin_endpoint_type', endpoint_type),
+        ('networking_timeout_409', CONF.network.timeout_409)
     ]))
 
 
diff --git a/tempest/common/waiters.py b/tempest/common/waiters.py
index 6801f97..a13aff4 100644
--- a/tempest/common/waiters.py
+++ b/tempest/common/waiters.py
@@ -713,6 +713,19 @@
     raise lib_exc.TimeoutException()
 
 
+def wait_for_cloudinit(ssh_client, timeout=60):
+    """Waits for cloud-init completed"""
+    start_time = int(time.time())
+    while int(time.time()) - start_time < timeout:
+        try:
+            ssh_client.check_cloudinit()
+            return
+        except Exception:
+            pass
+        time.sleep(5)
+    raise lib_exc.TimeoutException()
+
+
 def wait_for_caching(client, cache_client, image_id):
     """Waits until image is cached"""
     start = int(time.time())
diff --git a/tempest/config.py b/tempest/config.py
index 2e51eb3..09a7dbd 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -323,12 +323,24 @@
                 help="A list of trusted certificates to be used when the "
                      "image certificate validation compute feature is "
                      "enabled."),
+    cfg.StrOpt('windows10_image_ref',
+               default=None,
+               help="Valid image reference to be used in Windows 10 tests."),
+    cfg.StrOpt('windows11_image_ref',
+               default=None,
+               help="Valid image reference to be used in Windows 11 tests."),
     cfg.StrOpt('flavor_ref',
                default="1",
                help="Valid primary flavor to use in tests."),
     cfg.StrOpt('flavor_ref_alt',
                default="2",
                help='Valid secondary flavor to be used in tests.'),
+    cfg.StrOpt('windows10_flavor_ref',
+               default=None,
+               help="Valid flavor to be used for Windows 10 tests."),
+    cfg.StrOpt('windows11_flavor_ref',
+               default=None,
+               help="Valid flavor to be used for Windows 11 tests."),
     cfg.IntOpt('build_interval',
                default=1,
                help="Time in seconds between build status checks."),
@@ -845,6 +857,10 @@
     cfg.IntOpt('service_ports_number',
                default=0,
                help="Number of neutron service ports created per network"),
+    cfg.IntOpt('timeout_409',
+               default=120,
+               help="Total time in seconds to keep retrying a request that "
+                    "returns HTTP 409 (Conflict)."),
 ]
 
 network_feature_group = cfg.OptGroup(name='network-feature-enabled',
@@ -991,7 +1007,7 @@
                choices=['ecdsa', 'rsa'],
                help='Type of key to use for ssh connections.'),
     cfg.FloatOpt('allowed_network_downtime',
-                 default=5.0,
+                 default=10.0,
                  help="Allowed VM network connection downtime during live "
                       "migration, in seconds. "
                       "When the measured downtime exceeds this value, an "
@@ -1714,6 +1730,17 @@
     return _parameters
 
 
+def is_tungstenfabric_backend_enabled():
+    """Return True if TungstenFabric is used as a backend."""
+    try:
+        sdn = getattr(CONF, 'sdn')
+        service_name = getattr(sdn, 'service_name')
+        if service_name == 'tungstenfabric':
+            return True
+    except cfg.NoSuchOptError:
+        return False
+
+
 def _register_tempest_service_clients():
     # Register tempest own service clients using the same mechanism used
     # for external plugins.
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/lib/common/cred_provider.py b/tempest/lib/common/cred_provider.py
index 93b9586..48fef94 100644
--- a/tempest/lib/common/cred_provider.py
+++ b/tempest/lib/common/cred_provider.py
@@ -13,6 +13,8 @@
 #    limitations under the License.
 
 import abc
+import time
+
 from oslo_log import log as logging
 
 from tempest.lib import auth
@@ -141,11 +143,24 @@
             name="default")
         secgroups_to_delete = resp_body['security_groups']
         for secgroup in secgroups_to_delete:
-            try:
-                security_group_client.delete_security_group(secgroup['id'])
-            except exceptions.NotFound:
-                LOG.warning('Security group %s, id %s not found for clean-up',
-                            secgroup['name'], secgroup['id'])
+            # Workaround for PRODX-4003
+            start_time = time.time()
+            while True:
+                try:
+                    security_group_client.delete_security_group(secgroup['id'])
+                    break
+                except exceptions.NotFound:
+                    LOG.warning('Security group %s, id %s not found for '
+                                'clean-up', secgroup['name'], secgroup['id'])
+                    break
+                except exceptions.Conflict:
+                    LOG.warning('Conflict with state of security group %s, '
+                                'id %s.', secgroup['name'], secgroup['id'])
+                    if (time.time() - self.networking_timeout_409) > \
+                            start_time:
+                        raise
+                    else:
+                        time.sleep(5)
 
 
 class TestResources(object):
diff --git a/tempest/lib/common/dynamic_creds.py b/tempest/lib/common/dynamic_creds.py
index d4bd302..4e87f70 100644
--- a/tempest/lib/common/dynamic_creds.py
+++ b/tempest/lib/common/dynamic_creds.py
@@ -76,7 +76,8 @@
                  neutron_available=False, create_networks=True,
                  project_network_cidr=None, project_network_mask_bits=None,
                  public_network_id=None, resource_prefix=None,
-                 identity_admin_endpoint_type='public', identity_uri=None):
+                 identity_admin_endpoint_type='public', identity_uri=None,
+                 networking_timeout_409=120):
         super(DynamicCredentialProvider, self).__init__(
             identity_version=identity_version, identity_uri=identity_uri,
             admin_role=admin_role, name=name,
@@ -121,6 +122,7 @@
             self.roles_admin_client,
             self.domains_admin_client,
             self.creds_domain_name)
+        self.networking_timeout_409 = networking_timeout_409
 
     def _get_admin_clients(self, endpoint_type):
         """Returns a tuple with instances of the following admin clients
diff --git a/tempest/lib/common/utils/linux/remote_client.py b/tempest/lib/common/utils/linux/remote_client.py
index bdf35e7..2f019ce 100644
--- a/tempest/lib/common/utils/linux/remote_client.py
+++ b/tempest/lib/common/utils/linux/remote_client.py
@@ -120,6 +120,17 @@
         """
         self.ssh_client.test_connection_auth()
 
+    @debug_ssh
+    def check_cloudinit(self):
+        """Check cloud-init is completed
+
+           This method raises an Exception when the status is not 'done'.
+        """
+        out = self.ssh_client.exec_command("cloud-init status")
+        res = [s.strip() for s in out.split(' ')]
+        if res[1] != "done":
+            raise ValueError("Cloud init is not done, {res}".format(res=res))
+
     def ping_host(self, host, count=None, size=None, nic=None):
         if count is None:
             count = self.ping_count
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index feb2cf1..20abbb7 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -52,7 +52,10 @@
 
     credentials = ['primary', 'admin']
 
-    compute_min_microversion = None
+    if CONF.compute_feature_enabled.volume_multiattach:
+        compute_min_microversion = '2.60'
+    else:
+        compute_min_microversion = None
     compute_max_microversion = LATEST_MICROVERSION
     volume_min_microversion = None
     volume_max_microversion = LATEST_MICROVERSION
@@ -1400,6 +1403,14 @@
 
         return self.create_server(**create_kwargs)
 
+    def wait_for_cloud_init(
+            self, ip_address, server, private_key, username, timeout=60):
+        ssh_client = self.get_remote_client(ip_address,
+                                            private_key=private_key,
+                                            server=server,
+                                            username=username)
+        waiters.wait_for_cloudinit(ssh_client, timeout)
+
     def create_volume_from_image(self, **kwargs):
         """Create volume from image.
 
@@ -1417,6 +1428,26 @@
                 prefix=CONF.resource_name_prefix, name=namestart)
         return self.create_volume(name=name, imageRef=image_id, **kwargs)
 
+    def run_sync(self, ip_address, private_key=None, server=None,
+                 username=None):
+        """Syncs server filesystem cached writes
+
+        This wrapper utility does ssh and syncs server's filesystem caches
+        to persistent storage.
+
+        :param ip_address: The floating IP or fixed IP of the remote server
+        :param private_key: The SSH private key to use for authentication
+        :param server: Server dict, used for debugging purposes
+        :param username: Name of the Linux account on the remote server
+        """
+
+        ssh_client = self.get_remote_client(ip_address,
+                                            private_key=private_key,
+                                            server=server,
+                                            username=username)
+
+        ssh_client.exec_command('sudo sh -c "sync"')
+
 
 class ScenarioTestWithNetwork(ScenarioTest):
     """Base class for tests with default network"""
diff --git a/tempest/scenario/test_encrypted_cinder_volumes.py b/tempest/scenario/test_encrypted_cinder_volumes.py
index 4cc9e9c..cb5e673 100644
--- a/tempest/scenario/test_encrypted_cinder_volumes.py
+++ b/tempest/scenario/test_encrypted_cinder_volumes.py
@@ -92,6 +92,8 @@
         self.attach_detach_volume(server, volume)
 
     @decorators.idempotent_id('cbc752ed-b716-4717-910f-956cce965722')
+    @testtools.skipIf(CONF.volume.storage_protocol == 'ceph',
+                      'Skip because ceph does not support Provider plain.')
     @decorators.attr(type='slow')
     @testtools.skipUnless(
         'plain' in CONF.volume_feature_enabled.supported_crypto_providers,
diff --git a/tempest/scenario/test_network_basic_ops.py b/tempest/scenario/test_network_basic_ops.py
index 23e5ae6..c7a5d81 100644
--- a/tempest/scenario/test_network_basic_ops.py
+++ b/tempest/scenario/test_network_basic_ops.py
@@ -156,7 +156,7 @@
             self.assertIn(self.router['id'],
                           seen_router_ids)
 
-    def _create_server(self, network, port_id=None):
+    def _create_server(self, network, port_id=None, **kwargs):
         keypair = self.create_keypair()
         self.keypairs[keypair['name']] = keypair
         security_groups = [
@@ -169,7 +169,8 @@
         server = self.create_server(
             networks=[network],
             key_name=keypair['name'],
-            security_groups=security_groups)
+            security_groups=security_groups,
+            **kwargs)
         self.servers.append(server)
         return server
 
@@ -396,6 +397,21 @@
             router['id'], **kwargs)['router']
         self.assertEqual(admin_state_up, router['admin_state_up'])
 
+    def _live_migrate_server(self, server, host_id=None):
+        src_host = self.get_host_for_server(server['id'])
+
+        self.os_adm.servers_client.live_migrate_server(
+            server_id=server['id'],
+            block_migration=False,
+            host=host_id)
+
+        waiters.wait_for_server_status(
+            self.servers_client, server['id'], 'ACTIVE')
+
+        dst_host = self.get_host_for_server(server['id'])
+        self.assertNotEqual(src_host, dst_host,
+                            msg="Live migration failed, servers are equal")
+
     @decorators.attr(type='smoke')
     @decorators.idempotent_id('f323b3ba-82f8-4db7-8ea6-6a895869ec49')
     @utils.services('compute', 'network')
@@ -925,3 +941,56 @@
                                       security_groups=[])
         self.check_remote_connectivity(ssh_client, dest=peer_address,
                                        nic=spoof_nic, should_succeed=True)
+
+    @decorators.idempotent_id('463caa51-0967-4d6d-8ee9-11db1557c710')
+    @decorators.attr(type='slow')
+    @utils.services('compute', 'network')
+    def test_connectivity_between_vms_after_live_migration(self):
+        """Test the live-migration of the instances and ping
+
+        1. Create server 1 and 2 on the same host
+        2. Ping server 1 from server 2
+        3. Live migrate server 1 to other host
+        4. Ping server 1 from server 2
+        5. Migrate back server 1 to the first host
+        6. Ping server 1 from server 2
+        """
+
+        # Create server 1 with network, subnetwork, router, host
+        # and ping server 1
+        self._setup_network_and_servers()
+
+        server01 = self.servers[0]
+        hints = {'same_host': server01['id']}
+
+        # Create server 2 with network on the same host
+        self._create_new_network(create_gateway=True)
+        server02 = self._create_server(self.network,
+                                       scheduler_hints=hints)
+        server02_ip = [addr['addr'] for addr in
+                       server02['addresses'][self.network['name']]]
+
+        # Check if both instances are on the same host
+        host01_id = self.get_host_for_server(server01['id'])
+        host02_id = self.get_host_for_server(server02['id'])
+
+        self.assertEqual(host01_id, host02_id,
+                         message="Created servers have different hosts")
+
+        # Check ping between servers before live migration
+        self._check_server_connectivity(self.floating_ip_tuple.floating_ip,
+                                        server02_ip, should_connect=True)
+
+        # Live migrate server 1 to the new host
+        self._live_migrate_server(server=server01)
+
+        # Check ping between servers after live migration
+        self._check_server_connectivity(self.floating_ip_tuple.floating_ip,
+                                        server02_ip, should_connect=True)
+
+        # Migrate back server 1 to the first host, wait for status Active
+        self._live_migrate_server(server=server01, host_id=host01_id)
+
+        # Check ping between servers after live migration
+        self._check_server_connectivity(self.floating_ip_tuple.floating_ip,
+                                        server02_ip, should_connect=True)
diff --git a/tempest/scenario/test_volume_boot_pattern.py b/tempest/scenario/test_volume_boot_pattern.py
index 8a2641f..bab2582 100644
--- a/tempest/scenario/test_volume_boot_pattern.py
+++ b/tempest/scenario/test_volume_boot_pattern.py
@@ -58,6 +58,11 @@
                           'The public_network_id option must be specified.')
     @testtools.skipUnless(CONF.volume_feature_enabled.snapshot,
                           'Cinder volume snapshots are disabled')
+    @testtools.skipUnless(all([
+        CONF.compute.image_full_ref,
+        CONF.compute.image_full_username,
+        CONF.compute.image_full_flavor_ref]),
+        'Test requires image_full_* options to be set.')
     @utils.services('compute', 'volume', 'image')
     def test_volume_boot_pattern(self):
         """This test case attempts to reproduce the following steps:
@@ -71,27 +76,35 @@
         * Boot an additional instance from the new snapshot based volume
         * Check written content in the instance booted from snapshot
         """
-
         LOG.info("Creating keypair and security group")
         keypair = self.create_keypair()
         security_group = self.create_security_group()
+        username = CONF.compute.image_full_username
 
         # create an instance from volume
         LOG.info("Booting instance 1 from volume")
-        volume_origin = self.create_volume_from_image()
+        volume_origin = self.create_volume_from_image(
+            image_id=CONF.compute.image_full_ref)
         instance_1st = self.boot_instance_from_resource(
             source_id=volume_origin['id'],
             source_type='volume',
             keypair=keypair,
-            security_group=security_group)
+            security_group=security_group,
+            flavor=CONF.compute.image_full_flavor_ref)
         LOG.info("Booted first instance: %s", instance_1st)
 
         # write content to volume on instance
         LOG.info("Setting timestamp in instance %s", instance_1st)
         ip_instance_1st = self.get_server_ip(instance_1st)
+        self.wait_for_cloud_init(
+            ip_instance_1st,
+            private_key=keypair['private_key'],
+            server=instance_1st,
+            username=username)
         timestamp = self.create_timestamp(ip_instance_1st,
                                           private_key=keypair['private_key'],
-                                          server=instance_1st)
+                                          server=instance_1st,
+                                          username=username)
 
         # delete instance
         LOG.info("Deleting first instance: %s", instance_1st)
@@ -102,17 +115,29 @@
             source_id=volume_origin['id'],
             source_type='volume',
             keypair=keypair,
-            security_group=security_group)
+            security_group=security_group,
+            flavor=CONF.compute.image_full_flavor_ref)
         LOG.info("Booted second instance %s", instance_2nd)
 
         # check the content of written file
         LOG.info("Getting timestamp in instance %s", instance_2nd)
         ip_instance_2nd = self.get_server_ip(instance_2nd)
+        self.wait_for_cloud_init(
+            ip_instance_2nd,
+            private_key=keypair['private_key'],
+            server=instance_2nd,
+            username=username)
         timestamp2 = self.get_timestamp(ip_instance_2nd,
                                         private_key=keypair['private_key'],
-                                        server=instance_2nd)
+                                        server=instance_2nd,
+                                        username=username)
         self.assertEqual(timestamp, timestamp2)
 
+        # Sync filesystem caches to persistent storage before doing snapshot
+        self.run_sync(ip_instance_2nd,
+                      private_key=keypair['private_key'],
+                      server=instance_2nd,
+                      username=username)
         # snapshot a volume
         LOG.info("Creating snapshot from volume: %s", volume_origin['id'])
         snapshot = self.create_volume_snapshot(volume_origin['id'], force=True)
@@ -123,19 +148,26 @@
                                     size=snapshot['size'])
         LOG.info("Booting third instance from snapshot")
         server_from_snapshot = (
-            self.boot_instance_from_resource(source_id=volume['id'],
-                                             source_type='volume',
-                                             keypair=keypair,
-                                             security_group=security_group))
+            self.boot_instance_from_resource(
+                source_id=volume['id'],
+                source_type='volume', keypair=keypair,
+                security_group=security_group,
+                flavor=CONF.compute.image_full_flavor_ref))
         LOG.info("Booted third instance %s", server_from_snapshot)
 
         # check the content of written file
         LOG.info("Logging into third instance to get timestamp: %s",
                  server_from_snapshot)
         server_from_snapshot_ip = self.get_server_ip(server_from_snapshot)
+        self.wait_for_cloud_init(
+            server_from_snapshot_ip,
+            private_key=keypair['private_key'],
+            server=server_from_snapshot,
+            username=username)
         timestamp3 = self.get_timestamp(server_from_snapshot_ip,
                                         private_key=keypair['private_key'],
-                                        server=server_from_snapshot)
+                                        server=server_from_snapshot,
+                                        username=username)
         self.assertEqual(timestamp, timestamp3)
 
     @decorators.idempotent_id('e3f4f2fc-5c6a-4be6-9c54-aedfc0954da7')