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')