Merge "Fix test_port_security_macspoofing_port" into mcp/caracal
diff --git a/requirements.txt b/requirements.txt
index 6e66046..c6dd58a 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -23,3 +23,4 @@
 debtcollector>=1.2.0 # Apache-2.0
 defusedxml>=0.7.1 # PSFL
 fasteners>=0.16.0 # Apache-2.0
+tenacity>=4.4.0 # Apache-2.0
diff --git a/tempest/api/compute/admin/test_volume.py b/tempest/api/compute/admin/test_volume.py
index dc631e5..b0f20f6 100644
--- a/tempest/api/compute/admin/test_volume.py
+++ b/tempest/api/compute/admin/test_volume.py
@@ -13,6 +13,8 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import testtools
+
 from tempest.api.compute import base
 from tempest.common import waiters
 from tempest import config
@@ -41,6 +43,9 @@
 class AttachSCSIVolumeTestJSON(BaseAttachSCSIVolumeTest):
     """Test attaching scsi volume to server"""
 
+    @testtools.skipIf(
+        CONF.compute_feature_enabled.barbican_integration_enabled,
+        "Not supported when barbican integration enabled.")
     @decorators.idempotent_id('777e468f-17ca-4da4-b93d-b7dbf56c0494')
     def test_attach_scsi_disk_with_config_drive(self):
         """Test the attach/detach volume with config drive/scsi disk
diff --git a/tempest/api/compute/admin/test_volume_swap.py b/tempest/api/compute/admin/test_volume_swap.py
index 9576b74..fa416b1 100644
--- a/tempest/api/compute/admin/test_volume_swap.py
+++ b/tempest/api/compute/admin/test_volume_swap.py
@@ -36,6 +36,10 @@
             raise cls.skipException("Cinder is not available")
         if not CONF.compute_feature_enabled.swap_volume:
             raise cls.skipException("Swapping volumes is not supported.")
+        if CONF.compute_feature_enabled.attach_encrypted_volume:
+            raise cls.skipException(
+                'Volume swap is not available for OS configurations '
+                'with crypted volumes.')
 
     def wait_for_server_volume_swap(self, server_id, old_volume_id,
                                     new_volume_id):
diff --git a/tempest/api/compute/servers/test_attach_interfaces.py b/tempest/api/compute/servers/test_attach_interfaces.py
index 8984d1d..688b31b 100644
--- a/tempest/api/compute/servers/test_attach_interfaces.py
+++ b/tempest/api/compute/servers/test_attach_interfaces.py
@@ -109,21 +109,24 @@
         """
         port = self.ports_client.show_port(port_id)['port']
         device_id = port['device_id']
+        dns_name = port.get('dns_name')
         start = int(time.time())
 
         # NOTE(mriedem): Nova updates the port's device_id to '' rather than
         # None, but it's not contractual so handle Falsey either way.
-        while device_id:
+        while any([device_id, dns_name]):
             time.sleep(self.build_interval)
             port = self.ports_client.show_port(port_id)['port']
             device_id = port['device_id']
+            dns_name = port.get('dns_name')
 
             timed_out = int(time.time()) - start >= self.build_timeout
 
-            if device_id and timed_out:
-                message = ('Port %s failed to detach (device_id %s) within '
-                           'the required time (%s s).' %
-                           (port_id, device_id, self.build_timeout))
+            if any([device_id, dns_name]) and timed_out:
+                message = ('Port %s failed to detach (device_id %s), '
+                           '(dns_name %s) within the required time (%s s).' %
+                           (port_id, device_id or 'is out',
+                            dns_name or 'is out', self.build_timeout))
                 raise lib_exc.TimeoutException(message)
 
         return port
diff --git a/tempest/api/compute/servers/test_novnc.py b/tempest/api/compute/servers/test_novnc.py
index 1308b19..c90aea8 100644
--- a/tempest/api/compute/servers/test_novnc.py
+++ b/tempest/api/compute/servers/test_novnc.py
@@ -64,7 +64,15 @@
 
     def _validate_novnc_html(self, vnc_url):
         """Verify we can connect to novnc and get back the javascript."""
-        resp = urllib3.PoolManager().request('GET', vnc_url)
+        cert_params = {}
+
+        if CONF.identity.disable_ssl_certificate_validation:
+            cert_params['cert_reqs'] = "CERT_NONE"
+        else:
+            cert_params["cert_reqs"] = "CERT_REQUIRED"
+            cert_params["ca_certs"] = CONF.identity.ca_certificates_file
+
+        resp = urllib3.PoolManager(**cert_params).request('GET', vnc_url)
         # Make sure that the GET request was accepted by the novncproxy
         self.assertEqual(resp.status, 200, 'Got a Bad HTTP Response on the '
                          'initial call: ' + str(resp.status))
diff --git a/tempest/api/compute/servers/test_server_actions.py b/tempest/api/compute/servers/test_server_actions.py
index 21ed0cd..301db05 100644
--- a/tempest/api/compute/servers/test_server_actions.py
+++ b/tempest/api/compute/servers/test_server_actions.py
@@ -854,7 +854,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_user,
+            self.ssh_alt_user,
             password=None,
             pkey=self.validation_resources['keypair']['private_key'],
             server=server,
diff --git a/tempest/api/compute/servers/test_server_rescue.py b/tempest/api/compute/servers/test_server_rescue.py
index 97c2774..5e82835 100644
--- a/tempest/api/compute/servers/test_server_rescue.py
+++ b/tempest/api/compute/servers/test_server_rescue.py
@@ -74,6 +74,7 @@
 class ServerRescueTestJSONUnderV235(ServerRescueTestBase):
     """Test server rescue with compute microversion less than 2.36"""
 
+    min_microversion = '2.1'
     max_microversion = '2.35'
 
     # TODO(zhufl): After 2.35 we should switch to neutron client to create
@@ -123,6 +124,9 @@
         if not CONF.compute_feature_enabled.stable_rescue:
             msg = "Stable rescue not available."
             raise cls.skipException(msg)
+        if CONF.compute_feature_enabled.barbican_integration_enabled:
+            msg = "Rescue not supported with barbican integration."
+            raise cls.skipException(msg)
 
     @classmethod
     def setup_credentials(cls):
diff --git a/tempest/api/compute/test_quotas.py b/tempest/api/compute/test_quotas.py
index 38ca53b..286e0a5 100644
--- a/tempest/api/compute/test_quotas.py
+++ b/tempest/api/compute/test_quotas.py
@@ -13,10 +13,15 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import testtools
+
 from tempest.api.compute import base
 from tempest.common import tempest_fixtures as fixtures
+from tempest import config
 from tempest.lib import decorators
 
+CONF = config.CONF
+
 
 class QuotasTestJSON(base.BaseV2ComputeTest):
     """Test compute quotas"""
@@ -76,6 +81,8 @@
         for quota in expected_quota_set:
             self.assertIn(quota, quota_set.keys())
 
+    @testtools.skipIf(not CONF.auth.use_dynamic_credentials,
+                      'does not support static credentials')
     @decorators.idempotent_id('cd65d997-f7e4-4966-a7e9-d5001b674fdc')
     def test_compare_tenant_quotas_with_default_quotas(self):
         """Test tenants are created with the default compute quota values"""
diff --git a/tempest/api/compute/volumes/test_attach_volume.py b/tempest/api/compute/volumes/test_attach_volume.py
index 7ea8f09..f7432d0 100644
--- a/tempest/api/compute/volumes/test_attach_volume.py
+++ b/tempest/api/compute/volumes/test_attach_volume.py
@@ -267,6 +267,12 @@
         """
         server, validation_resources = self._create_server()
         volume = self.create_volume()
+
+        if volume['multiattach']:
+            raise self.skipException(
+                "Attaching multiattach volumes is not supported "
+                "for shelved-offloaded instances.")
+
         num_vol = self._count_volumes(server, validation_resources)
         self._shelve_server(server, validation_resources)
         attachment = self.attach_volume(server, volume)
@@ -277,8 +283,7 @@
 
         # Get volume attachment of the server
         volume_attachment = self.servers_client.show_volume_attachment(
-            server['id'],
-            attachment['id'])['volumeAttachment']
+            server['id'], attachment['id'])['volumeAttachment']
         self.assertEqual(server['id'], volume_attachment['serverId'])
         self.assertEqual(attachment['id'], volume_attachment['id'])
         # Check the mountpoint is not None after unshelve server even in
@@ -298,6 +303,12 @@
         """
         server, validation_resources = self._create_server()
         volume = self.create_volume()
+
+        if volume['multiattach']:
+            raise self.skipException(
+                "Attaching multiattach volumes is not supported for "
+                "shelved-offloaded instances.")
+
         num_vol = self._count_volumes(server, validation_resources)
         self._shelve_server(server, validation_resources)
 
@@ -307,8 +318,8 @@
         waiters.wait_for_volume_resource_status(self.volumes_client,
                                                 volume['id'], 'available')
 
-        # Unshelve the instance and check that we have the expected number of
-        # volume(s)
+        # Unshelve the instance and check that we have
+        # the expected number of volume(s)
         self._unshelve_server_and_check_volumes(
             server, validation_resources, num_vol)
 
diff --git a/tempest/api/image/v2/admin/test_images.py b/tempest/api/image/v2/admin/test_images.py
index 2b1c4fb..2077d35 100644
--- a/tempest/api/image/v2/admin/test_images.py
+++ b/tempest/api/image/v2/admin/test_images.py
@@ -105,7 +105,7 @@
         # NOTE(gmann): Skip if copy-image import method and multistore
         # are not available.
         if ('copy-image' not in available_import_methods or
-            not available_stores):
+            len(available_stores) < 2):
             raise self.skipException('Either copy-image import method or '
                                      'multistore is not available')
         uuid = data_utils.rand_uuid()
diff --git a/tempest/api/network/base.py b/tempest/api/network/base.py
index 99742cc..fa30b40 100644
--- a/tempest/api/network/base.py
+++ b/tempest/api/network/base.py
@@ -19,6 +19,7 @@
 from tempest import exceptions
 from tempest.lib.common.utils import data_utils
 from tempest.lib.common.utils import test_utils
+from tempest.lib.common import waiters as lib_waiters
 from tempest.lib import exceptions as lib_exc
 import tempest.test
 
@@ -224,6 +225,9 @@
             test_utils.call_and_ignore_notfound_exc(
                 cls.routers_client.remove_router_interface, router['id'],
                 subnet_id=i['fixed_ips'][0]['subnet_id'])
+            lib_waiters.wait_router_interface_removed(
+                cls.ports_client, router['id'],
+                subnet_id=i['fixed_ips'][0]['subnet_id'])
         cls.routers_client.delete_router(router['id'])
 
 
diff --git a/tempest/api/network/test_subnetpools_extensions.py b/tempest/api/network/test_subnetpools_extensions.py
index 689844b..f398062 100644
--- a/tempest/api/network/test_subnetpools_extensions.py
+++ b/tempest/api/network/test_subnetpools_extensions.py
@@ -45,6 +45,9 @@
         if not utils.is_extension_enabled('subnet_allocation', 'network'):
             msg = "subnet_allocation extension not enabled."
             raise cls.skipException(msg)
+        if not utils.is_extension_enabled('default-subnetpools', 'network'):
+            msg = "default-subnetpools extension not enabled."
+            raise cls.skipException(msg)
 
     @decorators.attr(type='smoke')
     @decorators.idempotent_id('62595970-ab1c-4b7f-8fcc-fddfe55e9811')
diff --git a/tempest/api/object_storage/test_container_quotas.py b/tempest/api/object_storage/test_container_quotas.py
index f055d19..d4bf0d7 100644
--- a/tempest/api/object_storage/test_container_quotas.py
+++ b/tempest/api/object_storage/test_container_quotas.py
@@ -88,9 +88,12 @@
         nafter = self._get_bytes_used()
         self.assertEqual(nbefore, nafter)
 
+    # NOTE(vsaienko): the quotas imlementation on active/active rgw deployment
+    # have no coordination, as result quota overflow might happen, since
+    # this is upstream and we can't fix downstream. Remove test from smoke
+    # Related-Prod: PRODX-11581
     @decorators.idempotent_id('3a387039-697a-44fc-a9c0-935de31f426b')
     @utils.requires_ext(extension='container_quotas', service='object')
-    @decorators.attr(type="smoke")
     def test_upload_too_many_objects(self):
         """Attempts to upload many objects that exceeds the count limit."""
         for _ in range(QUOTA_COUNT):
diff --git a/tempest/api/volume/admin/test_volume_manage.py b/tempest/api/volume/admin/test_volume_manage.py
index 609ec15..3d7cb15 100644
--- a/tempest/api/volume/admin/test_volume_manage.py
+++ b/tempest/api/volume/admin/test_volume_manage.py
@@ -79,8 +79,11 @@
             new_vol_id)['volume']
         self.assertNotIn(new_vol_id, [org_vol_id])
         self.assertEqual(new_vol_info['name'], new_vol_name)
-        for key in ['size',
-                    'volume_type',
-                    'availability_zone',
-                    'os-vol-host-attr:host']:
+        check_attrs = ['size',
+                       'volume_type',
+                       'availability_zone'
+                       ]
+        if CONF.volume.storage_protocol != 'ceph':
+            check_attrs.append('os-vol-host-attr:host')
+        for key in check_attrs:
             self.assertEqual(new_vol_info[key], org_vol_info[key])
diff --git a/tempest/api/volume/admin/test_volume_retype.py b/tempest/api/volume/admin/test_volume_retype.py
index 7c25f3d..4cb2262 100644
--- a/tempest/api/volume/admin/test_volume_retype.py
+++ b/tempest/api/volume/admin/test_volume_retype.py
@@ -179,10 +179,11 @@
         keys_with_change = ('volume_type',)
 
         # NOTE(vsaienko): with active-active cluster deployment volume
-        # services registered with different hostname.
-        if CONF.volume_feature_enabled.cluster_active_active:
-            keys_with_change += ('os-vol-host-attr:host',)
-        else:
+        # services registered with different hostname since we don't know
+        # which service process request host might or might not be changed.
+        # TODO(vsaienko): Revisit logic when is fixed
+        # https://bugs.launchpad.net/cinder/+bug/1874414
+        if not CONF.volume_feature_enabled.cluster_active_active:
             keys_with_no_change += ('os-vol-host-attr:host',)
 
         # Check the volume information after the retype
diff --git a/tempest/api/volume/test_volumes_negative.py b/tempest/api/volume/test_volumes_negative.py
index d8480df..7c319db 100644
--- a/tempest/api/volume/test_volumes_negative.py
+++ b/tempest/api/volume/test_volumes_negative.py
@@ -264,6 +264,13 @@
     @decorators.idempotent_id('449c4ed2-ecdd-47bb-98dc-072aeccf158c')
     def test_reserve_volume_with_negative_volume_status(self):
         """Test reserving already reserved volume should fail"""
+
+        # Skip test if the volume has "multiattach" property
+        volume = self.volumes_client.show_volume(self.volume['id'])
+        if volume['multiattach']:
+            raise self.skipException('Reserving multiattach volumes is not'
+                                     ' supported.')
+
         # Mark volume as reserved.
         self.volumes_client.reserve_volume(self.volume['id'])
         # Mark volume which is marked as reserved before
diff --git a/tempest/config.py b/tempest/config.py
index ae065ef..167a0c6 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -290,6 +290,15 @@
                help="Valid secondary image reference to be used in tests. "
                     "This is a required option, but if only one image is "
                     "available duplicate the value of image_ref above"),
+    cfg.StrOpt('image_full_ref',
+               help="This is image with full OS like ubuntu/centos used"
+                    "in some tests. When not set related tests will be "
+                    "skipped"),
+    cfg.StrOpt('image_full_username',
+               default="ubuntu",
+               help="Username for image_full_ref authentication."),
+    cfg.StrOpt('image_full_flavor_ref',
+               help="Flavor to boot image_full_ref."),
     cfg.StrOpt('certified_image_ref',
                help="Valid image reference to be used in image certificate "
                     "validation tests when enabled. This image must also "
@@ -915,7 +924,7 @@
                default="root",
                help="User name used to authenticate to an instance."),
     cfg.StrOpt('image_alt_ssh_user',
-               default="root",
+               default="cirros",
                help="User name used to authenticate to an alt instance."),
     cfg.StrOpt('image_ssh_password',
                default="password",
@@ -1108,7 +1117,10 @@
     cfg.BoolOpt('instance_locality_enabled',
                 default=False,
                 help='The boolean flag to run instance locality  tests '
-                     'on environment.')
+                     'on environment.'),
+    cfg.ListOpt('supported_crypto_providers',
+                default=['luks'],
+                help='A list of enabled cryptoproviders for volumes'),
 ]
 
 
diff --git a/tempest/lib/api_schema/response/compute/v2_19/servers.py b/tempest/lib/api_schema/response/compute/v2_19/servers.py
index ba3d787..5d1f315 100644
--- a/tempest/lib/api_schema/response/compute/v2_19/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_19/servers.py
@@ -14,9 +14,12 @@
 
 import copy
 
+from tempest.lib.api_schema.response.compute.v2_1 import servers \
+    as servers
 from tempest.lib.api_schema.response.compute.v2_16 import servers \
     as serversv216
 
+
 # Compute microversion 2.19:
 # 1. New attributes in 'server' dict.
 #      'description'
@@ -63,3 +66,4 @@
 list_volume_attachments = copy.deepcopy(serversv216.list_volume_attachments)
 show_instance_action = copy.deepcopy(serversv216.show_instance_action)
 create_backup = copy.deepcopy(serversv216.create_backup)
+list_instance_actions = copy.deepcopy(servers.list_instance_actions)
diff --git a/tempest/lib/api_schema/response/compute/v2_58/servers.py b/tempest/lib/api_schema/response/compute/v2_58/servers.py
index 637b765..5c22c79 100644
--- a/tempest/lib/api_schema/response/compute/v2_58/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_58/servers.py
@@ -12,8 +12,10 @@
 import copy
 
 from tempest.lib.api_schema.response.compute.v2_1 import parameter_types
+from tempest.lib.api_schema.response.compute.v2_1 import servers as servers
 from tempest.lib.api_schema.response.compute.v2_57 import servers as servers257
 
+
 # microversion 2.58 added updated_at to the response
 show_instance_action = copy.deepcopy(servers257.show_instance_action)
 show_instance_action['response_body']['properties']['instanceAction'][
@@ -21,6 +23,14 @@
 show_instance_action['response_body']['properties']['instanceAction'][
     'required'].append('updated_at')
 
+# microversion 2.58 added updated_at to the response
+list_instance_actions = copy.deepcopy(servers.list_instance_actions)
+list_instance_actions['response_body']['properties']['instanceActions'][
+    'items']['properties'].update({'updated_at': parameter_types.date_time})
+list_instance_actions['response_body']['properties']['instanceActions'][
+    'items']['required'].append('updated_at')
+
+
 # Below are the unchanged schema in this microversion. We need
 # to keep this schema in this file to have the generic way to select the
 # right schema based on self.schema_versions_info mapping in service client.
diff --git a/tempest/lib/common/constants.py b/tempest/lib/common/constants.py
new file mode 100644
index 0000000..57fdd93
--- /dev/null
+++ b/tempest/lib/common/constants.py
@@ -0,0 +1,5 @@
+# Retry constants
+RETRY_ATTEMPTS = 30
+RETRY_INITIAL_DELAY = 1
+RETRY_BACKOFF = 3
+RETRY_MAX = 10
diff --git a/tempest/lib/common/dynamic_creds.py b/tempest/lib/common/dynamic_creds.py
index eb18aad..9994ee6 100644
--- a/tempest/lib/common/dynamic_creds.py
+++ b/tempest/lib/common/dynamic_creds.py
@@ -16,11 +16,15 @@
 
 import netaddr
 from oslo_log import log as logging
+import tenacity
 
+import tempest.lib.common.constants as const
 from tempest.lib.common import cred_client
 from tempest.lib.common import cred_provider
 from tempest.lib.common.utils import data_utils
+from tempest.lib.common import waiters as lib_waiters
 from tempest.lib import exceptions as lib_exc
+
 from tempest.lib.services import clients
 
 LOG = logging.getLogger(__name__)
@@ -533,6 +537,11 @@
             del self._creds[creds_name]
         return self.get_credentials(roles, scope=scope, by_role=True)
 
+    @tenacity.retry(
+        retry=tenacity.retry_if_exception_type(lib_exc.Conflict),
+        wait=tenacity.wait_incrementing(
+            const.RETRY_INITIAL_DELAY, const.RETRY_BACKOFF, const.RETRY_MAX),
+        stop=tenacity.stop_after_attempt(const.RETRY_ATTEMPTS))
     def _clear_isolated_router(self, router_id, router_name):
         client = self.routers_admin_client
         try:
@@ -574,6 +583,9 @@
                     client.remove_router_interface(
                         creds.router['id'],
                         subnet_id=creds.subnet['id'])
+                    lib_waiters.wait_router_interface_removed(
+                        self.ports_admin_client, creds.router['id'],
+                        subnet_id=creds.subnet['id'])
                 except lib_exc.NotFound:
                     LOG.warning('router with name: %s not found for delete',
                                 creds.router['name'])
diff --git a/tempest/lib/common/rest_client.py b/tempest/lib/common/rest_client.py
index a2f2931..4e1dc59 100644
--- a/tempest/lib/common/rest_client.py
+++ b/tempest/lib/common/rest_client.py
@@ -94,6 +94,7 @@
         self.build_interval = build_interval
         self.build_timeout = build_timeout
         self.trace_requests = trace_requests
+        self.ca_certs = ca_certs
 
         self._skip_path = False
         self.general_header_lc = set(('cache-control', 'connection',
diff --git a/tempest/lib/common/utils/linux/remote_client.py b/tempest/lib/common/utils/linux/remote_client.py
index 662b452..bdf35e7 100644
--- a/tempest/lib/common/utils/linux/remote_client.py
+++ b/tempest/lib/common/utils/linux/remote_client.py
@@ -31,35 +31,33 @@
             return function(self, *args, **kwargs)
         except Exception as e:
             caller = test_utils.find_test_caller() or "not found"
-            if not isinstance(e, tempest.lib.exceptions.SSHTimeout):
-                message = ('Executing command on %(ip)s failed. '
-                           'Error: %(error)s' % {'ip': self.ip_address,
-                                                 'error': e})
-                message = '(%s) %s' % (caller, message)
-                LOG.error(message)
-                raise
-            else:
-                try:
-                    original_exception = sys.exc_info()
-                    if self.server:
+            message = ('Executing command on %(ip)s failed. '
+                       'Error: %(error)s' % {'ip': self.ip_address,
+                                             'error': e})
+            message = '(%s) %s' % (caller, message)
+            LOG.error(message)
+            try:
+                original_exception = sys.exc_info()
+                if self.server:
+                    if isinstance(e, tempest.lib.exceptions.SSHTimeout):
                         msg = 'Caller: %s. Timeout trying to ssh to server %s'
                         LOG.debug(msg, caller, self.server)
-                        if self.console_output_enabled and self.servers_client:
-                            try:
-                                msg = 'Console log for server %s: %s'
-                                console_log = (
-                                    self.servers_client.get_console_output(
-                                        self.server['id'])['output'])
-                                LOG.debug(msg, self.server['id'], console_log)
-                            except Exception:
-                                msg = 'Could not get console_log for server %s'
-                                LOG.debug(msg, self.server['id'])
-                    # raise the original ssh timeout exception
-                    raise
-                finally:
-                    # Delete the traceback to avoid circular references
-                    _, _, trace = original_exception
-                    del trace
+                    if self.console_output_enabled and self.servers_client:
+                        try:
+                            msg = 'Console log for server %s: %s'
+                            console_log = (
+                                self.servers_client.get_console_output(
+                                    self.server['id'])['output'])
+                            LOG.debug(msg, self.server['id'], console_log)
+                        except Exception:
+                            msg = 'Could not get console_log for server %s'
+                            LOG.debug(msg, self.server['id'])
+                # raise the original ssh exception
+                raise
+            finally:
+                # Delete the traceback to avoid circular references
+                _, _, trace = original_exception
+                del trace
     return wrapper
 
 
diff --git a/tempest/lib/common/waiters.py b/tempest/lib/common/waiters.py
new file mode 100644
index 0000000..1f0bdc9
--- /dev/null
+++ b/tempest/lib/common/waiters.py
@@ -0,0 +1,32 @@
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    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
+
+from tempest.lib import exceptions as lib_exc
+
+
+def wait_router_interface_removed(
+    ports_client, router_id, subnet_id, timeout=30, interval=3):
+    """Waits for router inface is removed"""
+    start_time = int(time.time())
+    while int(time.time()) - start_time < timeout:
+        try:
+            ports = ports_client.list_ports(
+                device_id=router_id,
+                fixed_ips=f"subnet_id={subnet_id}")['ports']
+            if len(ports) == 0:
+                return
+            time.sleep(interval)
+        except Exception:
+            pass
+    raise lib_exc.TimeoutException()
diff --git a/tempest/lib/services/compute/servers_client.py b/tempest/lib/services/compute/servers_client.py
index 58008aa..274e62d 100644
--- a/tempest/lib/services/compute/servers_client.py
+++ b/tempest/lib/services/compute/servers_client.py
@@ -721,6 +721,8 @@
         resp, body = self.get("servers/%s/os-instance-actions" %
                               server_id)
         body = json.loads(body)
+        # select proper schema depending on microverion
+        schema = self.get_schema(self.schema_versions_info)
         self.validate_response(schema.list_instance_actions, resp, body)
         return rest_client.ResponseBody(resp, body)
 
diff --git a/tempest/lib/services/object_storage/object_client.py b/tempest/lib/services/object_storage/object_client.py
index 65e8227..c7ac80f 100644
--- a/tempest/lib/services/object_storage/object_client.py
+++ b/tempest/lib/services/object_storage/object_client.py
@@ -167,11 +167,14 @@
         :param parsed_url: parsed url of the remote location
         """
         context = None
-        # If CONF.identity.disable_ssl_certificate_validation is true,
-        # do not check ssl certification.
-        if self.dscv:
-            context = ssl._create_unverified_context()
         if parsed_url.scheme == 'https':
+            # If CONF.identity.disable_ssl_certificate_validation is true,
+            # do not check ssl certification.
+            if self.dscv:
+                context = ssl._create_unverified_context()
+            else:
+                context = ssl.create_default_context(
+                    cafile=self.ca_certs)
             conn = httplib.HTTPSConnection(parsed_url.netloc,
                                            context=context)
         else:
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index 5f30909..3105e85 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -777,7 +777,20 @@
         linux_client = remote_client.RemoteClient(
             ip_address, username, pkey=private_key, password=password,
             server=server, servers_client=self.servers_client)
-        linux_client.validate_authentication()
+        try:
+            linux_client.validate_authentication()
+        except Exception as e:
+            message = ('Initializing SSH connection to %(ip)s failed. '
+                       'Error: %(error)s' % {'ip': ip_address,
+                                             'error': e})
+            caller = test_utils.find_test_caller()
+            if caller:
+                message = '(%s) %s' % (caller, message)
+            LOG.exception(message)
+            servers = (server,) if server else None
+            self.log_console_output(servers=servers)
+            raise
+
         return linux_client
 
     def image_create(self, name='scenario-img', **kwargs):
@@ -1343,6 +1356,16 @@
         return self.create_volume(name=name, imageRef=image_id, **kwargs)
 
 
+class ScenarioTestWithNetwork(ScenarioTest):
+    """Base class for tests with default network"""
+
+    @classmethod
+    def setup_credentials(cls):
+        cls.set_network_resources(network=True, subnet=True,
+                                  dhcp=True, router=True)
+        super(ScenarioTestWithNetwork, cls).setup_credentials()
+
+
 class NetworkScenarioTest(ScenarioTest):
     """Base class for network scenario tests.
 
@@ -1697,7 +1720,8 @@
         return network, subnet, router
 
 
-class EncryptionScenarioTest(ScenarioTest):
+class EncryptionScenarioTest(ScenarioTestWithNetwork):
+
     """Base class for encryption scenario tests"""
 
     @classmethod
diff --git a/tempest/scenario/test_encrypted_cinder_volumes.py b/tempest/scenario/test_encrypted_cinder_volumes.py
index 753e64f..4cc9e9c 100644
--- a/tempest/scenario/test_encrypted_cinder_volumes.py
+++ b/tempest/scenario/test_encrypted_cinder_volumes.py
@@ -54,6 +54,9 @@
 
     @decorators.idempotent_id('79165fb4-5534-4b9d-8429-97ccffb8f86e')
     @decorators.attr(type='slow')
+    @testtools.skipUnless(
+        'luks' in CONF.volume_feature_enabled.supported_crypto_providers,
+        'luks cryptoprovider is not supported.')
     @utils.services('compute', 'volume', 'image')
     def test_encrypted_cinder_volumes_luks(self):
         """LUKs v1 decrypts volume through libvirt."""
@@ -90,6 +93,9 @@
 
     @decorators.idempotent_id('cbc752ed-b716-4717-910f-956cce965722')
     @decorators.attr(type='slow')
+    @testtools.skipUnless(
+        'plain' in CONF.volume_feature_enabled.supported_crypto_providers,
+        'plain cryptoprovider is not supported.')
     @utils.services('compute', 'volume', 'image')
     def test_encrypted_cinder_volumes_cryptsetup(self):
         volume = self.create_encrypted_volume('plain',
diff --git a/tempest/scenario/test_server_basic_ops.py b/tempest/scenario/test_server_basic_ops.py
index 3830fbc..ad86d0f 100644
--- a/tempest/scenario/test_server_basic_ops.py
+++ b/tempest/scenario/test_server_basic_ops.py
@@ -27,7 +27,7 @@
 CONF = config.CONF
 
 
-class TestServerBasicOps(manager.ScenarioTest):
+class TestServerBasicOps(manager.ScenarioTestWithNetwork):
 
     """The test suite for server basic operations
 
diff --git a/tempest/scenario/test_snapshot_pattern.py b/tempest/scenario/test_snapshot_pattern.py
index d04cb9a..db8f533 100644
--- a/tempest/scenario/test_snapshot_pattern.py
+++ b/tempest/scenario/test_snapshot_pattern.py
@@ -41,6 +41,11 @@
         super(TestSnapshotPattern, cls).skip_checks()
         if not CONF.compute_feature_enabled.snapshot:
             raise cls.skipException("Snapshotting is not available.")
+        if not all([CONF.compute.image_full_ref,
+                    CONF.compute.image_full_username,
+                    CONF.compute.image_full_flavor_ref]):
+            raise cls.skipException(
+                "Test requires image_full_* options to be set.")
 
     @decorators.idempotent_id('608e604b-1d63-4a82-8e3e-91bc665c90b4')
     @decorators.attr(type='slow')
@@ -51,16 +56,20 @@
         # prepare for booting an instance
         keypair = self.create_keypair()
         security_group = self.create_security_group()
+        username = CONF.compute.image_full_username
 
         # boot an instance and create a timestamp file in it
         server = self.create_server(
             key_name=keypair['name'],
-            security_groups=[{'name': security_group['name']}])
+            security_groups=[{'name': security_group['name']}],
+            flavor=CONF.compute.image_full_flavor_ref,
+            image_id=CONF.compute.image_full_ref)
 
         instance_ip = self.get_server_ip(server)
         timestamp = self.create_timestamp(instance_ip,
                                           private_key=keypair['private_key'],
-                                          server=server)
+                                          server=server,
+                                          username=username)
 
         # snapshot the instance
         snapshot_image = self.create_server_snapshot(server=server)
@@ -74,13 +83,15 @@
         server_from_snapshot = self.create_server(
             image_id=snapshot_image['id'],
             key_name=keypair['name'],
-            security_groups=[{'name': security_group['name']}])
+            security_groups=[{'name': security_group['name']}],
+            flavor=CONF.compute.image_full_flavor_ref)
 
         # check the existence of the timestamp file in the second instance
         server_from_snapshot_ip = self.get_server_ip(server_from_snapshot)
         timestamp2 = 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, timestamp2)
 
         # snapshot the instance again
diff --git a/tempest/scenario/test_volume_backup_restore.py b/tempest/scenario/test_volume_backup_restore.py
index 07ca38a..ca563da 100644
--- a/tempest/scenario/test_volume_backup_restore.py
+++ b/tempest/scenario/test_volume_backup_restore.py
@@ -22,7 +22,7 @@
 CONF = config.CONF
 
 
-class TestVolumeBackupRestore(manager.ScenarioTest):
+class TestVolumeBackupRestore(manager.ScenarioTestWithNetwork):
     """Test cinder backup and restore
 
     This testcase verifies content preservation after backup and restore
diff --git a/tempest/scenario/test_volume_boot_pattern.py b/tempest/scenario/test_volume_boot_pattern.py
index 5e28ecd..46b2f4b 100644
--- a/tempest/scenario/test_volume_boot_pattern.py
+++ b/tempest/scenario/test_volume_boot_pattern.py
@@ -276,6 +276,9 @@
     @decorators.idempotent_id('cb78919a-e553-4bab-b73b-10cf4d2eb125')
     @testtools.skipUnless(CONF.compute_feature_enabled.attach_encrypted_volume,
                           'Encrypted volume attach is not supported')
+    @testtools.skipUnless(
+        'luks' in CONF.volume_feature_enabled.supported_crypto_providers,
+        'luks cryptoprovider is not supported.')
     @utils.services('compute', 'volume')
     def test_boot_server_from_encrypted_volume_luks(self):
         """LUKs v1 decrypts volume through libvirt."""
@@ -286,6 +289,9 @@
                       'Ceph only supports LUKSv2 if doing host attach.')
     @testtools.skipUnless(CONF.compute_feature_enabled.attach_encrypted_volume,
                           'Encrypted volume attach is not supported')
+    @testtools.skipUnless(
+        'luks2' in CONF.volume_feature_enabled.supported_crypto_providers,
+        'luks2 cryptoprovider is not supported.')
     @utils.services('compute', 'volume')
     def test_boot_server_from_encrypted_volume_luksv2(self):
         """LUKs v2 decrypts volume through os-brick."""
diff --git a/tempest/serial_tests/api/admin/test_aggregates.py b/tempest/serial_tests/api/admin/test_aggregates.py
index cedeec0..ce54957 100644
--- a/tempest/serial_tests/api/admin/test_aggregates.py
+++ b/tempest/serial_tests/api/admin/test_aggregates.py
@@ -222,6 +222,9 @@
     @decorators.idempotent_id('96be03c7-570d-409c-90f8-e4db3c646996')
     def test_aggregate_add_host_create_server_with_az(self):
         """Test adding a host to the given aggregate and creating a server"""
+        if CONF.production:
+            raise self.skipException("Not allowed to run this test "
+                                     "on production environment")
         self.useFixture(fixtures.LockFixture('availability_zone'))
         az_name = data_utils.rand_name(
             prefix=CONF.resource_name_prefix, name=self.az_name_prefix)
@@ -235,12 +238,20 @@
             if agg['availability_zone']:
                 hosts_in_zone.extend(agg['hosts'])
         hosts = [v for v in self.hosts_available if v not in hosts_in_zone]
-        if not hosts:
+        hosts_available = []
+        for host in hosts:
+            hypervisor_servers = (
+                self.os_admin.hypervisor_client.list_servers_on_hypervisor(
+                    host)["hypervisors"][0].get("servers", None))
+            if not hypervisor_servers:
+                hosts_available.append(host)
+        if not hosts_available:
             raise self.skipException("All hosts are already in other "
-                                     "availability zones, so can't add "
+                                     "availability zones or have running "
+                                     "instances, so can't add "
                                      "host to aggregate. \nAggregates list: "
                                      "%s" % aggregates)
-        host = hosts[0]
+        host = hosts_available[0]
 
         self.client.add_host(aggregate['id'], host=host)
         self.addCleanup(self.client.remove_host, aggregate['id'], host=host)
diff --git a/tempest/serial_tests/scenario/test_aggregates_basic_ops.py b/tempest/serial_tests/scenario/test_aggregates_basic_ops.py
index a831fe5..cc45297 100644
--- a/tempest/serial_tests/scenario/test_aggregates_basic_ops.py
+++ b/tempest/serial_tests/scenario/test_aggregates_basic_ops.py
@@ -63,7 +63,11 @@
         hosts_available = []
         for host in svc_list:
             if (host['state'] == 'up' and host['status'] == 'enabled'):
-                hosts_available.append(host['host'])
+                hypervisor_servers = (
+                    self.os_admin.hypervisor_client.list_servers_on_hypervisor(
+                        host["host"])["hypervisors"][0].get("servers", None))
+                if not hypervisor_servers:
+                    hosts_available.append(host["host"])
         aggregates = self.aggregates_client.list_aggregates()['aggregates']
         hosts_in_zone = []
         for agg in aggregates:
@@ -72,7 +76,8 @@
         hosts = [v for v in hosts_available if v not in hosts_in_zone]
         if not hosts:
             raise self.skipException("All hosts are already in other "
-                                     "availability zones, so can't add "
+                                     "availability zones or have running "
+                                     "instances, so can't add "
                                      "host to aggregate. \nAggregates list: "
                                      "%s" % aggregates)
         return hosts[0]
@@ -120,6 +125,9 @@
     @decorators.attr(type='slow')
     @utils.services('compute')
     def test_aggregate_basic_ops(self):
+        if CONF.production:
+            raise self.skipException("Not allowed to run this test "
+                                     "on production environment")
         self.useFixture(fixtures.LockFixture('availability_zone'))
         az = 'foo_zone'
         aggregate_name = data_utils.rand_name(
diff --git a/tempest/tests/common/utils/linux/test_remote_client.py b/tempest/tests/common/utils/linux/test_remote_client.py
index 937f93a..5801f04 100644
--- a/tempest/tests/common/utils/linux/test_remote_client.py
+++ b/tempest/tests/common/utils/linux/test_remote_client.py
@@ -180,9 +180,7 @@
     def test_validate_debug_ssh_console(self):
         self.assertRaises(lib_exc.SSHTimeout,
                           self.conn.validate_authentication)
-        msg = 'Caller: %s. Timeout trying to ssh to server %s' % (
-            'TestRemoteClientWithServer:test_validate_debug_ssh_console',
-            self.server)
+        msg = 'Executing command on 127.0.0.1 failed.'
         self.assertIn(msg, self.log.output)
         self.assertIn('Console output for', self.log.output)
 
@@ -190,9 +188,7 @@
         self.assertRaises(lib_exc.SSHTimeout,
                           self.conn.exec_command, 'fake command')
         self.assertIn('fake command', self.log.output)
-        msg = 'Caller: %s. Timeout trying to ssh to server %s' % (
-            'TestRemoteClientWithServer:test_exec_command_debug_ssh_console',
-            self.server)
+        msg = 'Executing command on 127.0.0.1 failed.'
         self.assertIn(msg, self.log.output)
         self.assertIn('Console output for', self.log.output)
 
@@ -204,9 +200,7 @@
     def test_validate_debug_ssh_console(self):
         self.assertRaises(lib_exc.SSHTimeout,
                           self.conn.validate_authentication)
-        msg = 'Caller: %s. Timeout trying to ssh to server %s' % (
-            'TestRemoteClientWithBrokenServer:test_validate_debug_ssh_console',
-            self.server)
+        msg = 'Executing command on 127.0.0.1 failed.'
         self.assertIn(msg, self.log.output)
         msg = 'Could not get console_log for server %s' % self.server['id']
         self.assertIn(msg, self.log.output)
@@ -215,10 +209,7 @@
         self.assertRaises(lib_exc.SSHTimeout,
                           self.conn.exec_command, 'fake command')
         self.assertIn('fake command', self.log.output)
-        caller = ":".join(['TestRemoteClientWithBrokenServer',
-                           'test_exec_command_debug_ssh_console'])
-        msg = 'Caller: %s. Timeout trying to ssh to server %s' % (
-            caller, self.server)
+        msg = 'Executing command on 127.0.0.1 failed.'
         self.assertIn(msg, self.log.output)
         msg = 'Could not get console_log for server %s' % self.server['id']
         self.assertIn(msg, self.log.output)
diff --git a/tempest/tests/lib/common/test_dynamic_creds.py b/tempest/tests/lib/common/test_dynamic_creds.py
index d3d01c0..646ec9b 100644
--- a/tempest/tests/lib/common/test_dynamic_creds.py
+++ b/tempest/tests/lib/common/test_dynamic_creds.py
@@ -741,6 +741,7 @@
         router_interface_mock = self.patch(
             'tempest.lib.services.network.routers_client.RoutersClient.'
             'add_router_interface')
+        self.patch('tempest.lib.common.waiters.wait_router_interface_removed')
         creds.get_primary_creds()
         creds.get_project_admin_creds()
         router_interface_mock.assert_called_once_with('1234', subnet_id='1234')
diff --git a/tempest/tests/lib/common/utils/linux/test_remote_client.py b/tempest/tests/lib/common/utils/linux/test_remote_client.py
index df23e63..c41f178 100644
--- a/tempest/tests/lib/common/utils/linux/test_remote_client.py
+++ b/tempest/tests/lib/common/utils/linux/test_remote_client.py
@@ -15,6 +15,8 @@
 
 from unittest import mock
 
+import fixtures
+
 from tempest.lib.common import ssh
 from tempest.lib.common.utils.linux import remote_client
 from tempest.lib import exceptions as lib_exc
@@ -29,6 +31,12 @@
 
 class TestRemoteClient(base.TestCase):
 
+    def setUp(self):
+        super(TestRemoteClient, self).setUp()
+        self.log = self.useFixture(fixtures.FakeLogger(
+            name='tempest.lib.common.utils.linux.remote_client',
+            level='DEBUG'))
+
     @mock.patch.object(ssh.Client, 'exec_command', return_value='success')
     def test_exec_command(self, mock_ssh_exec_command):
         client = remote_client.RemoteClient('192.168.1.10', 'username')
@@ -50,9 +58,8 @@
         client = remote_client.RemoteClient('192.168.1.10', 'username',
                                             server=server)
         self.assertRaises(lib_exc.SSHTimeout, client.exec_command, 'ls')
-        mock_debug.assert_called_with(
-            'Caller: %s. Timeout trying to ssh to server %s',
-            'TestRemoteClient:test_debug_ssh_without_console', server)
+        msg = 'Executing command on 192.168.1.10 failed.'
+        self.assertIn(msg, self.log.output)
 
     @mock.patch.object(remote_client.LOG, 'debug')
     @mock.patch.object(ssh.Client, 'exec_command')