Merge "Update microversion for for the tests which fail due to multiattach enabled" into mcp/caracal
diff --git a/tempest/api/compute/servers/test_server_actions.py b/tempest/api/compute/servers/test_server_actions.py
index 44c2197..9b30582 100644
--- a/tempest/api/compute/servers/test_server_actions.py
+++ b/tempest/api/compute/servers/test_server_actions.py
@@ -857,7 +857,7 @@
         # 4.Plain username/password auth, if a password was given.
         linux_client = remote_client.RemoteClient(
             self.get_server_ip(server, self.validation_resources),
-            self.ssh_alt_user,
+            self.ssh_user,
             password=None,
             pkey=self.validation_resources['keypair']['private_key'],
             server=server,
diff --git a/tempest/api/image/v2/admin/test_images.py b/tempest/api/image/v2/admin/test_images.py
index 2077d35..1190c52 100644
--- a/tempest/api/image/v2/admin/test_images.py
+++ b/tempest/api/image/v2/admin/test_images.py
@@ -12,6 +12,7 @@
 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 #    License for the specific language governing permissions and limitations
 #    under the License.
+import time
 
 import io
 
@@ -235,3 +236,45 @@
             observed_image,
             stores,
             first_image_store_deleted)
+
+
+class ImageWebUploadAdminTest(base.BaseV2ImageAdminTest):
+    @classmethod
+    def skip_checks(cls):
+        super(ImageWebUploadAdminTest, cls).skip_checks()
+        enabled_methods = CONF.image_feature_enabled.enabled_import_methods
+        if "web-download" not in enabled_methods:
+            raise cls.skipException(
+                "Glance image upload via url feature disabled")
+
+    @decorators.idempotent_id('5b2ce43c-924c-4bae-bac0-f5d6ed69d72e')
+    def test_image_upload_via_url(self):
+        # Create image
+        image_name = data_utils.rand_name("image")
+        container_format = CONF.image.container_formats[0]
+        disk_format = CONF.image.disk_formats[0]
+        image = self.create_image(name=image_name,
+                                  container_format=container_format,
+                                  disk_format=disk_format,
+                                  visibility='private')
+        self.assertEqual('queued', image['status'])
+
+        # Upload image via url
+        image_uri = CONF.image.http_image
+        method = {"name": "web-download", "uri": image_uri}
+        self.admin_client.import_image(image_id=image["id"], method=method)
+
+        timeout = CONF.image.build_timeout
+        interval = CONF.image.build_interval
+
+        start_time = int(time.time())
+        while True:
+            body = self.admin_client.show_image(image['id'])
+            if body["status"] == "active":
+                break
+            if int(time.time()) - start_time >= timeout:
+                message = ('Image %(id)s failed to become active within '
+                           'the required time (%(timeout)s s).' %
+                           {'id': image['id'], 'timeout': timeout})
+                raise lib_exc.TimeoutException(message)
+            time.sleep(interval)
diff --git a/tempest/api/network/test_tags.py b/tempest/api/network/test_tags.py
index bd3e360..af0a8b0 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/common/credentials_factory.py b/tempest/common/credentials_factory.py
index 8d699ab..2e07c80 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/config.py b/tempest/config.py
index 47667ba..d57864d 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -749,6 +749,9 @@
     cfg.BoolOpt('os_glance_reserved',
                 default=False,
                 help="Should we check that os_glance namespace is reserved"),
+    cfg.ListOpt('enabled_import_methods',
+                default=[],
+                help="List of enabled image import methods"),
     cfg.BoolOpt('manage_locations',
                 default=False,
                 help=('Is show_multiple_locations enabled in glance. '
@@ -842,6 +845,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',
@@ -951,7 +958,7 @@
                default="root",
                help="User name used to authenticate to an instance."),
     cfg.StrOpt('image_alt_ssh_user',
-               default="cirros",
+               default="root",
                help="User name used to authenticate to an alt instance."),
     cfg.StrOpt('image_ssh_password',
                default="password",
@@ -1088,6 +1095,18 @@
     cfg.ListOpt('scheduler_default_filters',
                 default=[],
                 help="The list of enabled scheduler filters.",),
+    cfg.StrOpt('volume_type_luks',
+               default='luks',
+               help="The name of volume type used by tests to create"
+                    "volumes with luks encryption.",),
+    cfg.StrOpt('volume_type_luks_v2',
+               default='luks2',
+               help="The name of volume type used by tests to create"
+                    "volumes with luks v2 encryption.",),
+    cfg.StrOpt('volume_type_cryptsetup',
+               default='cryptsetup',
+               help="The name of volume type used by tests to create"
+                    "volumes with cryptsetup encryption.",),
 ]
 
 volume_feature_group = cfg.OptGroup(name='volume-feature-enabled',
@@ -1692,6 +1711,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/common/cred_provider.py b/tempest/lib/common/cred_provider.py
index 2da206f..84f5264 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
@@ -133,11 +135,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 9994ee6..9eb3c22 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/services/image/v2/images_client.py b/tempest/lib/services/image/v2/images_client.py
index 0608d47..3963fee 100644
--- a/tempest/lib/services/image/v2/images_client.py
+++ b/tempest/lib/services/image/v2/images_client.py
@@ -55,6 +55,19 @@
         body = json.loads(body)
         return rest_client.ResponseBody(resp, body)
 
+    def import_image(self, image_id, **kwargs):
+        """Import image.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://docs.openstack.org/api-ref/image/v2/#import-an-image
+        """
+        data = json.dumps(kwargs)
+        url = 'images/%s/import' % image_id
+        resp, body = self.post(url, data)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
     def deactivate_image(self, image_id):
         """Deactivate image.
 
diff --git a/tempest/scenario/test_encrypted_cinder_volumes.py b/tempest/scenario/test_encrypted_cinder_volumes.py
index cb5e673..62b2823 100644
--- a/tempest/scenario/test_encrypted_cinder_volumes.py
+++ b/tempest/scenario/test_encrypted_cinder_volumes.py
@@ -60,9 +60,10 @@
     @utils.services('compute', 'volume', 'image')
     def test_encrypted_cinder_volumes_luks(self):
         """LUKs v1 decrypts volume through libvirt."""
-        volume = self.create_encrypted_volume('luks',
-                                              volume_type='luks',
-                                              wait_until=None)
+        volume = self.create_encrypted_volume(
+            'luks',
+            volume_type=CONF.volume.volume_type_luks,
+            wait_until=None)
         server = self.launch_instance()
         waiters.wait_for_volume_resource_status(self.volumes_client,
                                                 volume['id'], 'available')
@@ -100,9 +101,10 @@
         'plain cryptoprovider is not supported.')
     @utils.services('compute', 'volume', 'image')
     def test_encrypted_cinder_volumes_cryptsetup(self):
-        volume = self.create_encrypted_volume('plain',
-                                              volume_type='cryptsetup',
-                                              wait_until=None)
+        volume = self.create_encrypted_volume(
+            'plain',
+            volume_type=CONF.volume.volume_type_cryptsetup,
+            wait_until=None)
         server = self.launch_instance()
         waiters.wait_for_volume_resource_status(self.volumes_client,
                                                 volume['id'], 'available')
diff --git a/tempest/scenario/test_volume_boot_pattern.py b/tempest/scenario/test_volume_boot_pattern.py
index 7ef7488..1c3c8bf 100644
--- a/tempest/scenario/test_volume_boot_pattern.py
+++ b/tempest/scenario/test_volume_boot_pattern.py
@@ -314,7 +314,9 @@
     @utils.services('compute', 'volume')
     def test_boot_server_from_encrypted_volume_luks(self):
         """LUKs v1 decrypts volume through libvirt."""
-        self._do_test_boot_server_from_encrypted_volume_luks('luks')
+        self._do_test_boot_server_from_encrypted_volume_luks(
+            CONF.volume.volume_type_luks
+        )
 
     @decorators.idempotent_id('5ab6100f-1b31-4dd0-a774-68cfd837ef77')
     @testtools.skipIf(CONF.volume.storage_protocol == 'ceph',
@@ -327,4 +329,6 @@
     @utils.services('compute', 'volume')
     def test_boot_server_from_encrypted_volume_luksv2(self):
         """LUKs v2 decrypts volume through os-brick."""
-        self._do_test_boot_server_from_encrypted_volume_luks('luks2')
+        self._do_test_boot_server_from_encrypted_volume_luks(
+            CONF.volume.volume_type_luks_v2
+        )