Merge "Test image <-> volume <-> server dependency"
diff --git a/releasenotes/notes/Add-http_qcow2_image-config-option-a9dca410897c3044.yaml b/releasenotes/notes/Add-http_qcow2_image-config-option-a9dca410897c3044.yaml
new file mode 100644
index 0000000..c1b0033
--- /dev/null
+++ b/releasenotes/notes/Add-http_qcow2_image-config-option-a9dca410897c3044.yaml
@@ -0,0 +1,8 @@
+---
+features:
+ - |
+ Added a new config option in the `image` section, `http_qcow2_image`,
+ which will use `qcow2` format image to download from the external
+ source specified and use it for image conversion in glance tests. By
+ default it will download
+ `http://download.cirros-cloud.net/0.6.2/cirros-0.6.2-x86_64-disk.img`
diff --git a/releasenotes/notes/add-placement-resource-provider-traits-api-calls-9f4b0455afec9afb.yaml b/releasenotes/notes/add-placement-resource-provider-traits-api-calls-9f4b0455afec9afb.yaml
new file mode 100644
index 0000000..1d1811c
--- /dev/null
+++ b/releasenotes/notes/add-placement-resource-provider-traits-api-calls-9f4b0455afec9afb.yaml
@@ -0,0 +1,4 @@
+---
+features:
+ - |
+ Adds API calls for traits in ResourceProvidersClient.
diff --git a/releasenotes/notes/add-placement-traits-api-calls-087061f5455f0b12.yaml b/releasenotes/notes/add-placement-traits-api-calls-087061f5455f0b12.yaml
new file mode 100644
index 0000000..77d0b38
--- /dev/null
+++ b/releasenotes/notes/add-placement-traits-api-calls-087061f5455f0b12.yaml
@@ -0,0 +1,4 @@
+---
+features:
+ - |
+ Adds API calls for traits in PlacementClient.
diff --git a/releasenotes/notes/cleanup-attr-decorator-alias-78ce21eb20d87e01.yaml b/releasenotes/notes/cleanup-attr-decorator-alias-78ce21eb20d87e01.yaml
new file mode 100644
index 0000000..43091e1
--- /dev/null
+++ b/releasenotes/notes/cleanup-attr-decorator-alias-78ce21eb20d87e01.yaml
@@ -0,0 +1,5 @@
+---
+upgrade:
+ - |
+ The ``attr`` decorator is no longer available in the ``tempest.test``
+ module. Use the ``tempest.lib.decorators`` module instead.
diff --git a/tempest/api/compute/servers/test_delete_server.py b/tempest/api/compute/servers/test_delete_server.py
index ee25a22..596d2bd 100644
--- a/tempest/api/compute/servers/test_delete_server.py
+++ b/tempest/api/compute/servers/test_delete_server.py
@@ -99,11 +99,14 @@
def test_delete_server_while_in_verify_resize_state(self):
"""Test deleting a server while it's VM state is VERIFY_RESIZE"""
server = self.create_test_server(wait_until='ACTIVE')
- self.client.resize_server(server['id'], self.flavor_ref_alt)
- waiters.wait_for_server_status(self.client, server['id'],
- 'VERIFY_RESIZE')
- self.client.delete_server(server['id'])
- waiters.wait_for_server_termination(self.client, server['id'])
+ body = self.client.resize_server(server['id'], self.flavor_ref_alt)
+ request_id = body.response['x-openstack-request-id']
+ waiters.wait_for_server_status(
+ self.client, server['id'], 'VERIFY_RESIZE', request_id=request_id)
+ body = self.client.delete_server(server['id'])
+ request_id = body.response['x-openstack-request-id']
+ waiters.wait_for_server_termination(
+ self.client, server['id'], request_id=request_id)
@decorators.idempotent_id('d0f3f0d6-d9b6-4a32-8da4-23015dcab23c')
@utils.services('volume')
diff --git a/tempest/api/image/base.py b/tempest/api/image/base.py
index 89d5f91..0cc088a 100644
--- a/tempest/api/image/base.py
+++ b/tempest/api/image/base.py
@@ -100,10 +100,9 @@
"""Create Image & stage image file for glance-direct import method."""
image_name = data_utils.rand_name('test-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,
+ disk_format='raw',
visibility='private')
self.assertEqual('queued', image['status'])
diff --git a/tempest/api/image/v2/test_images.py b/tempest/api/image/v2/test_images.py
index e468e32..5bb8eef 100644
--- a/tempest/api/image/v2/test_images.py
+++ b/tempest/api/image/v2/test_images.py
@@ -76,7 +76,7 @@
'%s import method' % method)
def _stage_and_check(self):
- image = self._create_image()
+ image = self._create_image(disk_format='raw')
# Stage image data
file_content = data_utils.random_bytes()
image_file = io.BytesIO(file_content)
@@ -131,7 +131,7 @@
self.assertEqual(image['id'], body['id'])
self.assertEqual('queued', body['status'])
# import image from web to backend
- image_uri = CONF.image.http_image
+ image_uri = CONF.image.http_qcow2_image
self.client.image_import(image['id'], method='web-download',
import_params={'uri': image_uri})
waiters.wait_for_image_imported_to_stores(self.client, image['id'])
diff --git a/tempest/api/network/test_networks.py b/tempest/api/network/test_networks.py
index fd93779..b1fba2d 100644
--- a/tempest/api/network/test_networks.py
+++ b/tempest/api/network/test_networks.py
@@ -389,17 +389,20 @@
# belong to other tests and their state may have changed during this
# test
body = self.subnets_client.list_subnets(network_id=public_network_id)
+ extensions = [
+ ext['alias'] for ext in
+ self.network_extensions_client.list_extensions()['extensions']]
+ is_sen_ext = 'subnet-external-network' in extensions
# check subnet visibility of external_network
- if external_network['shared']:
- self.assertNotEmpty(body['subnets'], "Subnets should be visible "
- "for shared public network %s"
- % public_network_id)
+ if external_network['shared'] or is_sen_ext:
+ self.assertNotEmpty(body['subnets'],
+ 'Subnets should be visible for shared or '
+ 'external networks %s' % public_network_id)
else:
- self.assertEmpty(body['subnets'], "Subnets should not be visible "
- "for non-shared public "
- "network %s"
- % public_network_id)
+ self.assertEmpty(body['subnets'],
+ 'Subnets should not be visible for non-shared or'
+ 'non-external networks %s' % public_network_id)
@decorators.idempotent_id('c72c1c0c-2193-4aca-ccc4-b1442640bbbb')
@utils.requires_ext(extension="standard-attr-description",
diff --git a/tempest/common/waiters.py b/tempest/common/waiters.py
index d65b491..e249f35 100644
--- a/tempest/common/waiters.py
+++ b/tempest/common/waiters.py
@@ -103,7 +103,8 @@
old_task_state = task_state
-def wait_for_server_termination(client, server_id, ignore_error=False):
+def wait_for_server_termination(client, server_id, ignore_error=False,
+ request_id=None):
"""Waits for server to reach termination."""
try:
body = client.show_server(server_id)['server']
@@ -126,9 +127,13 @@
'/'.join((server_status, str(task_state))),
time.time() - start_time)
if server_status == 'ERROR' and not ignore_error:
- raise lib_exc.DeleteErrorException(
- "Server %s failed to delete and is in ERROR status" %
- server_id)
+ details = ("Server %s failed to delete and is in ERROR status." %
+ server_id)
+ if 'fault' in body:
+ details += ' Fault: %s.' % body['fault']
+ if request_id:
+ details += ' Server delete request ID: %s.' % request_id
+ raise lib_exc.DeleteErrorException(details, server_id=server_id)
if server_status == 'SOFT_DELETED':
# Soft-deleted instances need to be forcibly deleted to
diff --git a/tempest/config.py b/tempest/config.py
index 4b5330a..a98082a 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -635,12 +635,17 @@
cfg.BoolOpt('image_caching_enabled',
default=False,
help=("Flag to enable if caching is enabled by image "
- "service, operator should set this parameter to True"
+ "service, operator should set this parameter to True "
"if 'image_cache_dir' is set in glance-api.conf")),
cfg.StrOpt('http_image',
default='http://download.cirros-cloud.net/0.3.1/'
'cirros-0.3.1-x86_64-uec.tar.gz',
help='http accessible image'),
+ cfg.StrOpt('http_qcow2_image',
+ default='http://download.cirros-cloud.net/0.6.2/'
+ 'cirros-0.6.2-x86_64-disk.img',
+ help='http qcow2 accessible image which will be used '
+ 'for image conversion if enabled.'),
cfg.IntOpt('build_timeout',
default=300,
help="Timeout in seconds to wait for an image to "
diff --git a/tempest/lib/services/placement/placement_client.py b/tempest/lib/services/placement/placement_client.py
index 216ac08..f272cbf 100644
--- a/tempest/lib/services/placement/placement_client.py
+++ b/tempest/lib/services/placement/placement_client.py
@@ -49,3 +49,39 @@
self.expected_success(200, resp.status)
body = json.loads(body)
return rest_client.ResponseBody(resp, body)
+
+ def list_traits(self, **params):
+ """API ref https://docs.openstack.org/api-ref/placement/#traits
+ """
+ url = "/traits"
+ if params:
+ url += '?%s' % urllib.urlencode(params)
+ resp, body = self.get(url)
+ self.expected_success(200, resp.status)
+ body = json.loads(body)
+ return rest_client.ResponseBody(resp, body)
+
+ def show_trait(self, name, **params):
+ url = "/traits"
+ if params:
+ url += '?%s' % urllib.urlencode(params)
+ resp, body = self.get(url)
+ body = json.loads(body)
+ self.expected_success(200, resp.status)
+ return rest_client.ResponseBody(resp, body)
+ url = f"{url}/{name}"
+ resp, _ = self.get(url)
+ self.expected_success(204, resp.status)
+ return resp.status
+
+ def create_trait(self, name, **params):
+ url = f"/traits/{name}"
+ json_body = json.dumps(params)
+ resp, _ = self.put(url, body=json_body)
+ return resp.status
+
+ def delete_trait(self, name):
+ url = f"/traits/{name}"
+ resp, _ = self.delete(url)
+ self.expected_success(204, resp.status)
+ return resp.status
diff --git a/tempest/lib/services/placement/resource_providers_client.py b/tempest/lib/services/placement/resource_providers_client.py
index 3214053..a336500 100644
--- a/tempest/lib/services/placement/resource_providers_client.py
+++ b/tempest/lib/services/placement/resource_providers_client.py
@@ -121,3 +121,29 @@
resp, body = self.delete(url)
self.expected_success(204, resp.status)
return rest_client.ResponseBody(resp, body)
+
+ def list_resource_provider_traits(self, rp_uuid, **kwargs):
+ """https://docs.openstack.org/api-ref/placement/#resource-provider-traits
+ """
+ url = f"/resource_providers/{rp_uuid}/traits"
+ if kwargs:
+ url += '?%s' % urllib.urlencode(kwargs)
+ resp, body = self.get(url)
+ self.expected_success(200, resp.status)
+ body = json.loads(body)
+ return rest_client.ResponseBody(resp, body)
+
+ def update_resource_provider_traits(self, rp_uuid, **kwargs):
+ url = f"/resource_providers/{rp_uuid}/traits"
+ data = json.dumps(kwargs)
+ resp, body = self.put(url, data)
+ self.expected_success(200, resp.status)
+ body = json.loads(body)
+ return rest_client.ResponseBody(resp, body)
+
+ def delete_resource_provider_traits(self, rp_uuid):
+ url = f"/resource_providers/{rp_uuid}/traits"
+ resp, body = self.delete(url)
+ self.expected_success(204, resp.status)
+ body = json.loads(body)
+ return rest_client.ResponseBody(resp, body)
diff --git a/tempest/scenario/test_instances_with_cinder_volumes.py b/tempest/scenario/test_instances_with_cinder_volumes.py
index a907acd..b9ac2c8 100644
--- a/tempest/scenario/test_instances_with_cinder_volumes.py
+++ b/tempest/scenario/test_instances_with_cinder_volumes.py
@@ -181,21 +181,24 @@
server=server
)
+ server_name = server['name'].split('-')[-1]
+
# run write test on all volumes
for volume in attached_volumes:
- # get the mount path
- dev_name = volume['attachments'][0]['device'][5:]
-
# dev name volume['attachments'][0]['device'][5:] is like
# /dev/vdb, we need to remove /dev/ -> first 5 chars
+ dev_name = volume['attachments'][0]['device'][5:]
+
+ mount_path = f"/mnt/{server_name}"
+
timestamp_before = self.create_timestamp(
ssh_ip, private_key=keypair['private_key'], server=server,
- dev_name=dev_name,
+ dev_name=dev_name, mount_path=mount_path,
)
timestamp_after = self.get_timestamp(
ssh_ip, private_key=keypair['private_key'], server=server,
- dev_name=dev_name,
+ dev_name=dev_name, mount_path=mount_path,
)
self.assertEqual(timestamp_before, timestamp_after)
diff --git a/tempest/test.py b/tempest/test.py
index 173bfab..85a6c36 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -31,7 +31,6 @@
from tempest.lib.common import fixed_network
from tempest.lib.common import profiler
from tempest.lib.common import validation_resources as vr
-from tempest.lib import decorators
from tempest.lib import exceptions as lib_exc
LOG = logging.getLogger(__name__)
@@ -39,11 +38,6 @@
CONF = config.CONF
-attr = debtcollector.moves.moved_function(
- decorators.attr, 'attr', __name__,
- version='Pike', removal_version='?')
-
-
at_exit_set = set()
diff --git a/tempest/tests/lib/services/placement/test_placement_client.py b/tempest/tests/lib/services/placement/test_placement_client.py
index 1396a85..bb57bb0 100644
--- a/tempest/tests/lib/services/placement/test_placement_client.py
+++ b/tempest/tests/lib/services/placement/test_placement_client.py
@@ -87,3 +87,77 @@
def test_list_allocations_with_bytes_body(self):
self._test_list_allocations(bytes_body=True)
+
+ FAKE_ALL_TRAITS = {
+ "traits": [
+ "CUSTOM_HW_FPGA_CLASS1",
+ "CUSTOM_HW_FPGA_CLASS2",
+ "CUSTOM_HW_FPGA_CLASS3"
+ ]
+ }
+
+ FAKE_ASSOCIATED_TRAITS = {
+ "traits": [
+ "CUSTOM_HW_FPGA_CLASS1",
+ "CUSTOM_HW_FPGA_CLASS2"
+ ]
+ }
+
+ def test_list_traits(self):
+ self.check_service_client_function(
+ self.client.list_traits,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_ALL_TRAITS)
+
+ self.check_service_client_function(
+ self.client.list_traits,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_ASSOCIATED_TRAITS,
+ **{
+ "associated": "true"
+ })
+
+ self.check_service_client_function(
+ self.client.list_traits,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_ALL_TRAITS,
+ **{
+ "associated": "true",
+ "name": "startswith:CUSTOM_HW_FPGPA"
+ })
+
+ def test_show_traits(self):
+ self.check_service_client_function(
+ self.client.show_trait,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ 204, status=204,
+ name="CUSTOM_HW_FPGA_CLASS1")
+
+ self.check_service_client_function(
+ self.client.show_trait,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ 404, status=404,
+ # trait with this name does not exists
+ name="CUSTOM_HW_FPGA_CLASS4")
+
+ def test_create_traits(self):
+ self.check_service_client_function(
+ self.client.create_trait,
+ 'tempest.lib.common.rest_client.RestClient.put',
+ 204, status=204,
+ # try to create trait with existing name
+ name="CUSTOM_HW_FPGA_CLASS1")
+
+ self.check_service_client_function(
+ self.client.create_trait,
+ 'tempest.lib.common.rest_client.RestClient.put',
+ 201, status=201,
+ # create new trait
+ name="CUSTOM_HW_FPGA_CLASS4")
+
+ def test_delete_traits(self):
+ self.check_service_client_function(
+ self.client.delete_trait,
+ 'tempest.lib.common.rest_client.RestClient.delete',
+ 204, status=204,
+ name="CUSTOM_HW_FPGA_CLASS1")
diff --git a/tempest/tests/lib/services/placement/test_resource_providers_client.py b/tempest/tests/lib/services/placement/test_resource_providers_client.py
index 2871395..399f323 100644
--- a/tempest/tests/lib/services/placement/test_resource_providers_client.py
+++ b/tempest/tests/lib/services/placement/test_resource_providers_client.py
@@ -204,3 +204,40 @@
def test_show_resource_provider_usages_with_with_bytes_body(self):
self._test_list_resource_provider_inventories(bytes_body=True)
+
+ FAKE_ALL_RESOURCE_PROVIDER_TRAITS = {
+ "resource_provider_generation": 0,
+ "traits": [
+ "CUSTOM_HW_FPGA_CLASS1",
+ "CUSTOM_HW_FPGA_CLASS2"
+ ]
+ }
+ FAKE_NEW_RESOURCE_PROVIDER_TRAITS = {
+ "resource_provider_generation": 1,
+ "traits": [
+ "CUSTOM_HW_FPGA_CLASS1",
+ "CUSTOM_HW_FPGA_CLASS2"
+ ]
+ }
+
+ def test_list_resource_provider_traits(self):
+ self.check_service_client_function(
+ self.client.list_resource_provider_traits,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_ALL_RESOURCE_PROVIDER_TRAITS,
+ rp_uuid=self.FAKE_RESOURCE_PROVIDER_UUID)
+
+ def test_update_resource_provider_traits(self):
+ self.check_service_client_function(
+ self.client.update_resource_provider_traits,
+ 'tempest.lib.common.rest_client.RestClient.put',
+ self.FAKE_NEW_RESOURCE_PROVIDER_TRAITS,
+ rp_uuid=self.FAKE_RESOURCE_PROVIDER_UUID,
+ **self.FAKE_NEW_RESOURCE_PROVIDER_TRAITS)
+
+ def test_delete_resource_provider_traits(self):
+ self.check_service_client_function(
+ self.client.delete_resource_provider_traits,
+ 'tempest.lib.common.rest_client.RestClient.delete',
+ self.FAKE_ALL_RESOURCE_PROVIDER_TRAITS, status=204,
+ rp_uuid=self.FAKE_RESOURCE_PROVIDER_UUID)
diff --git a/tox.ini b/tox.ini
index fcdf6ff..e3c8fcf 100644
--- a/tox.ini
+++ b/tox.ini
@@ -197,7 +197,7 @@
# tests listed in exclude-list file:
commands =
find . -type f -name "*.pyc" -delete
- tempest run --regex {[testenv:integrated-compute]regex1} --exclude-list ./tools/tempest-integrated-gate-compute-exclude-list.txt {posargs}
+ tempest run --slowest --regex {[testenv:integrated-compute]regex1} --exclude-list ./tools/tempest-integrated-gate-compute-exclude-list.txt {posargs}
tempest run --combine --serial --slowest --regex {[testenv:integrated-compute]regex2} --exclude-list ./tools/tempest-integrated-gate-compute-exclude-list.txt {posargs}
[testenv:integrated-placement]
diff --git a/zuul.d/integrated-gate.yaml b/zuul.d/integrated-gate.yaml
index 2fd6e36..1343a7c 100644
--- a/zuul.d/integrated-gate.yaml
+++ b/zuul.d/integrated-gate.yaml
@@ -65,6 +65,10 @@
branches:
regex: ^.*/(victoria|wallaby)$
negate: true
+ # NOTE(sean-k-mooney): this job and its descendants frequently times out
+ # run on rax-* providers with a timeout of 2 hours. temporary increase
+ # the timeout to 2.5 hours.
+ timeout: 9000
description: |
Base integration test with Neutron networking, horizon, swift enable,
and py3.
@@ -78,6 +82,8 @@
# end up 6 in upstream CI. Higher concurrency means high parallel
# requests to services and can cause more oom issues. To avoid the
# oom issue, setting the concurrency to 4 in this job.
+ # NOTE(sean-k-mooney): now that we use zswap we should be able to
+ # increase the concurrency to 6.
tempest_concurrency: 4
tox_envlist: integrated-full
devstack_localrc:
@@ -133,11 +139,18 @@
This job runs integration tests for compute. This is
subset of 'tempest-full-py3' job and run Nova, Neutron, Cinder (except backup tests)
and Glance related tests. This is meant to be run on Nova gate only.
+ # NOTE(sean-k-mooney): this job and its descendants frequently times out
+ # when run on rax-* providers, recent optimizations have reduced the
+ # runtime of the job but it still times out. temporary increase the
+ # timeout to 2.5 hours.
+ timeout: 9000
vars:
# NOTE(gmann): Default concurrency is higher (number of cpu -2) which
# end up 6 in upstream CI. Higher concurrency means high parallel
# requests to services and can cause more oom issues. To avoid the
# oom issue, setting the concurrency to 4 in this job.
+ # NOTE(sean-k-mooney): now that we use zswap we should be able to
+ # increase the concurrency to 6.
tempest_concurrency: 4
tox_envlist: integrated-compute
tempest_exclude_regex: ""