Merge "Uncap jsonschema"
diff --git a/.gitreview b/.gitreview
index 84b5114..a475594 100644
--- a/.gitreview
+++ b/.gitreview
@@ -1,4 +1,4 @@
[gerrit]
-host=review.openstack.org
+host=review.opendev.org
port=29418
project=openstack/tempest.git
diff --git a/.zuul.yaml b/.zuul.yaml
index 7d77b71..e3210fe 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -8,10 +8,10 @@
test setup. To run a multi-node test inherit from devstack-tempest and
set the nodeset to a multi-node one.
required-projects:
- - git.openstack.org/openstack/tempest
+ - opendev.org/openstack/tempest
timeout: 7200
roles:
- - zuul: git.openstack.org/openstack-dev/devstack
+ - zuul: opendev.org/openstack/devstack
vars:
devstack_services:
tempest: true
@@ -55,10 +55,10 @@
description: |
Base Tempest IPv6 job.
required-projects:
- - git.openstack.org/openstack/tempest
+ - opendev.org/openstack/tempest
timeout: 7200
roles:
- - zuul: git.openstack.org/openstack-dev/devstack
+ - zuul: opendev.org/openstack/devstack
vars:
devstack_services:
tempest: true
@@ -113,24 +113,24 @@
periodic-tempest-dsvm-oslo-latest-full-master.
timeout: 10800
required-projects:
- - git.openstack.org/openstack/oslo.cache
- - git.openstack.org/openstack/oslo.concurrency
- - git.openstack.org/openstack/oslo.config
- - git.openstack.org/openstack/oslo.context
- - git.openstack.org/openstack/oslo.db
- - git.openstack.org/openstack/oslo.i18n
- - git.openstack.org/openstack/oslo.log
- - git.openstack.org/openstack/oslo.messaging
- - git.openstack.org/openstack/oslo.middleware
- - git.openstack.org/openstack/oslo.policy
- - git.openstack.org/openstack/oslo.privsep
- - git.openstack.org/openstack/oslo.reports
- - git.openstack.org/openstack/oslo.rootwrap
- - git.openstack.org/openstack/oslo.serialization
- - git.openstack.org/openstack/oslo.service
- - git.openstack.org/openstack/oslo.utils
- - git.openstack.org/openstack/oslo.versionedobjects
- - git.openstack.org/openstack/oslo.vmware
+ - opendev.org/openstack/oslo.cache
+ - opendev.org/openstack/oslo.concurrency
+ - opendev.org/openstack/oslo.config
+ - opendev.org/openstack/oslo.context
+ - opendev.org/openstack/oslo.db
+ - opendev.org/openstack/oslo.i18n
+ - opendev.org/openstack/oslo.log
+ - opendev.org/openstack/oslo.messaging
+ - opendev.org/openstack/oslo.middleware
+ - opendev.org/openstack/oslo.policy
+ - opendev.org/openstack/oslo.privsep
+ - opendev.org/openstack/oslo.reports
+ - opendev.org/openstack/oslo.rootwrap
+ - opendev.org/openstack/oslo.serialization
+ - opendev.org/openstack/oslo.service
+ - opendev.org/openstack/oslo.utils
+ - opendev.org/openstack/oslo.versionedobjects
+ - opendev.org/openstack/oslo.vmware
- job:
name: tempest-full-parallel
@@ -354,82 +354,82 @@
- ^tempest/hacking/.*$
- ^tempest/tests/.*$
required-projects:
- - git.openstack.org/openstack/airship-tempest-plugin
- - git.openstack.org/openstack/almanach
- - git.openstack.org/openstack/aodh
- - git.openstack.org/openstack/barbican-tempest-plugin
- - git.openstack.org/openstack/blazar-tempest-plugin
- - git.openstack.org/openstack/ceilometer
- - git.openstack.org/openstack/cinder-tempest-plugin
- - git.openstack.org/openstack/cloudkitty-tempest-plugin
- - git.openstack.org/openstack/congress-tempest-plugin
- - git.openstack.org/openstack/designate-tempest-plugin
- - git.openstack.org/openstack/ec2api-tempest-plugin
- - git.openstack.org/openstack/freezer
- - git.openstack.org/openstack/freezer-api
- - git.openstack.org/openstack/freezer-tempest-plugin
- - git.openstack.org/openstack/gabbi-tempest
- - git.openstack.org/openstack/gce-api
- - git.openstack.org/openstack/glare
- - git.openstack.org/openstack/heat-tempest-plugin
- - git.openstack.org/openstack/intel-nfv-ci-tests
- - git.openstack.org/openstack/ironic-tempest-plugin
- - git.openstack.org/openstack/ironic-inspector
- - git.openstack.org/openstack/keystone-tempest-plugin
- - git.openstack.org/openstack/kingbird
- - git.openstack.org/openstack/kuryr-tempest-plugin
- - git.openstack.org/openstack/magnum
- - git.openstack.org/openstack/magnum-tempest-plugin
- - git.openstack.org/openstack/manila
- - git.openstack.org/openstack/manila-tempest-plugin
- - git.openstack.org/openstack/mistral-tempest-plugin
- - git.openstack.org/openstack/mogan
- - git.openstack.org/openstack/monasca-api
- - git.openstack.org/openstack/monasca-log-api
- - git.openstack.org/openstack/monasca-tempest-plugin
- - git.openstack.org/openstack/murano-tempest-plugin
- - git.openstack.org/openstack/networking-ansible
- - git.openstack.org/openstack/networking-bgpvpn
- - git.openstack.org/openstack/networking-cisco
- - git.openstack.org/openstack/networking-fortinet
- - git.openstack.org/openstack/networking-generic-switch
- - git.openstack.org/openstack/networking-l2gw-tempest-plugin
- - git.openstack.org/openstack/networking-midonet
- - git.openstack.org/openstack/networking-sfc
- - git.openstack.org/openstack/networking-spp
- - git.openstack.org/openstack/neutron
- - git.openstack.org/openstack/neutron-dynamic-routing
- - git.openstack.org/openstack/neutron-fwaas
- - git.openstack.org/openstack/neutron-lbaas
- - git.openstack.org/openstack/neutron-tempest-plugin
- - git.openstack.org/openstack/neutron-vpnaas
- - git.openstack.org/openstack/nova-lxd
- - git.openstack.org/openstack/novajoin-tempest-plugin
- - git.openstack.org/openstack/octavia
- - git.openstack.org/openstack/octavia-tempest-plugin
- - git.openstack.org/openstack/oswin-tempest-plugin
- - git.openstack.org/openstack/panko
- - git.openstack.org/openstack/patrole
- - git.openstack.org/openstack/python-watcherclient
- - git.openstack.org/openstack/qinling
- - git.openstack.org/openstack/requirements
- - git.openstack.org/openstack/sahara-tests
- - git.openstack.org/openstack/senlin
- - git.openstack.org/openstack/senlin-tempest-plugin
- - git.openstack.org/openstack/solum-tempest-plugin
- - git.openstack.org/openstack/tap-as-a-service
- - git.openstack.org/openstack/telemetry-tempest-plugin
- - git.openstack.org/openstack/tempest-horizon
- - git.openstack.org/openstack/tobiko
- - git.openstack.org/openstack/trio2o
- - git.openstack.org/openstack/tripleo-common-tempest-plugin
- - git.openstack.org/openstack/trove-tempest-plugin
- - git.openstack.org/openstack/valet
- - git.openstack.org/openstack/vitrage-tempest-plugin
- - git.openstack.org/openstack/vmware-nsx-tempest-plugin
- - git.openstack.org/openstack/watcher-tempest-plugin
- - git.openstack.org/openstack/zaqar-tempest-plugin
- - git.openstack.org/openstack/zun-tempest-plugin
+ - opendev.org/airship/tempest-plugin
+ - opendev.org/x/almanach
+ - opendev.org/openstack/aodh
+ - opendev.org/openstack/barbican-tempest-plugin
+ - opendev.org/openstack/blazar-tempest-plugin
+ - opendev.org/openstack/ceilometer
+ - opendev.org/openstack/cinder-tempest-plugin
+ - opendev.org/openstack/cloudkitty-tempest-plugin
+ - opendev.org/openstack/congress-tempest-plugin
+ - opendev.org/openstack/designate-tempest-plugin
+ - opendev.org/openstack/ec2api-tempest-plugin
+ - opendev.org/openstack/freezer
+ - opendev.org/openstack/freezer-api
+ - opendev.org/openstack/freezer-tempest-plugin
+ - opendev.org/x/gabbi-tempest
+ - opendev.org/x/gce-api
+ - opendev.org/x/glare
+ - opendev.org/openstack/heat-tempest-plugin
+ - opendev.org/x/intel-nfv-ci-tests
+ - opendev.org/openstack/ironic-tempest-plugin
+ - opendev.org/openstack/ironic-inspector
+ - opendev.org/openstack/keystone-tempest-plugin
+ - opendev.org/x/kingbird
+ - opendev.org/openstack/kuryr-tempest-plugin
+ - opendev.org/openstack/magnum
+ - opendev.org/openstack/magnum-tempest-plugin
+ - opendev.org/openstack/manila
+ - opendev.org/openstack/manila-tempest-plugin
+ - opendev.org/openstack/mistral-tempest-plugin
+ - opendev.org/x/mogan
+ - opendev.org/openstack/monasca-api
+ - opendev.org/openstack/monasca-log-api
+ - opendev.org/openstack/monasca-tempest-plugin
+ - opendev.org/openstack/murano-tempest-plugin
+ - opendev.org/x/networking-ansible
+ - opendev.org/openstack/networking-bgpvpn
+ - opendev.org/x/networking-cisco
+ - opendev.org/x/networking-fortinet
+ - opendev.org/openstack/networking-generic-switch
+ - opendev.org/openstack/networking-l2gw-tempest-plugin
+ - opendev.org/openstack/networking-midonet
+ - opendev.org/openstack/networking-sfc
+ - opendev.org/x/networking-spp
+ - opendev.org/openstack/neutron
+ - opendev.org/openstack/neutron-dynamic-routing
+ - opendev.org/openstack/neutron-fwaas
+ - opendev.org/openstack/neutron-lbaas
+ - opendev.org/openstack/neutron-tempest-plugin
+ - opendev.org/openstack/neutron-vpnaas
+ - opendev.org/x/nova-lxd
+ - opendev.org/x/novajoin-tempest-plugin
+ - opendev.org/openstack/octavia
+ - opendev.org/openstack/octavia-tempest-plugin
+ - opendev.org/openstack/oswin-tempest-plugin
+ - opendev.org/openstack/panko
+ - opendev.org/openstack/patrole
+ - opendev.org/openstack/python-watcherclient
+ - opendev.org/openstack/qinling
+ - opendev.org/openstack/requirements
+ - opendev.org/openstack/sahara-tests
+ - opendev.org/openstack/senlin
+ - opendev.org/openstack/senlin-tempest-plugin
+ - opendev.org/openstack/solum-tempest-plugin
+ - opendev.org/x/tap-as-a-service
+ - opendev.org/openstack/telemetry-tempest-plugin
+ - opendev.org/openstack/tempest-horizon
+ - opendev.org/x/tobiko
+ - opendev.org/x/trio2o
+ - opendev.org/openstack/tripleo-common-tempest-plugin
+ - opendev.org/openstack/trove-tempest-plugin
+ - opendev.org/x/valet
+ - opendev.org/openstack/vitrage-tempest-plugin
+ - opendev.org/x/vmware-nsx-tempest-plugin
+ - opendev.org/openstack/watcher-tempest-plugin
+ - opendev.org/openstack/zaqar-tempest-plugin
+ - opendev.org/openstack/zun-tempest-plugin
- job:
name: tempest-cinder-v2-api
@@ -510,7 +510,6 @@
- tempest-full-parallel:
# Define list of irrelevant files to use everywhere else
irrelevant-files: &tempest-irrelevant-files
- - ^(test-|)requirements.txt$
- ^.*\.rst$
- ^doc/.*$
- ^etc/.*$
@@ -544,7 +543,6 @@
irrelevant-files: *tempest-irrelevant-files
- tempest-tox-plugin-sanity-check:
irrelevant-files:
- - ^(test-|)requirements.txt$
- ^.*\.rst$
- ^doc/.*$
- ^etc/.*$
diff --git a/releasenotes/notes/add-unstable_test-decorator-a73cf97d4ffcc796.yaml b/releasenotes/notes/add-unstable_test-decorator-a73cf97d4ffcc796.yaml
new file mode 100644
index 0000000..2203fd1
--- /dev/null
+++ b/releasenotes/notes/add-unstable_test-decorator-a73cf97d4ffcc796.yaml
@@ -0,0 +1,11 @@
+---
+features:
+ - |
+ New decorator ``unstable_test`` is added to ``tempest.lib.decorators``.
+ It can be used to mark some test as unstable thus it will be still run
+ by tempest but job will not fail if this test will fail. Such test will
+ be skipped in case of failure.
+ It can be used for example when there is known bug related which cause
+ irregular tests failures. Marking such test as unstable will help other
+ developers to get their job done and still run this test to get additional
+ debug data or to confirm if some potential fix really solved the issue.
diff --git a/tempest/api/compute/admin/test_volume_swap.py b/tempest/api/compute/admin/test_volume_swap.py
index cc83c04..371b506 100644
--- a/tempest/api/compute/admin/test_volume_swap.py
+++ b/tempest/api/compute/admin/test_volume_swap.py
@@ -142,6 +142,12 @@
if not CONF.compute_feature_enabled.volume_multiattach:
raise cls.skipException('Volume multi-attach is not available.')
+ @classmethod
+ def setup_clients(cls):
+ super(TestMultiAttachVolumeSwap, cls).setup_clients()
+ # Need this to set readonly volumes.
+ cls.admin_volumes_client = cls.os_admin.volumes_client_latest
+
# NOTE(mriedem): This is an uncommon scenario to call the compute API
# to swap volumes directly; swap volume is primarily only for volume
# live migration and retype callbacks from the volume service, and is slow
@@ -162,6 +168,13 @@
# volumes cleanup can happen successfully irrespective of which volume
# is attached to server.
volume1 = self.create_volume(multiattach=True)
+ # Make volume1 read-only since you can't swap from a volume with
+ # multiple read/write attachments, and you can't change the readonly
+ # flag on an in-use volume so we have to do this before attaching
+ # volume1 to anything. If the compute API ever supports per-attachment
+ # attach modes, then we can handle this differently.
+ self.admin_volumes_client.update_volume_readonly(
+ volume1['id'], readonly=True)
volume2 = self.create_volume(multiattach=True)
# Create two servers and wait for them to be ACTIVE.
diff --git a/tempest/api/identity/admin/v3/test_roles.py b/tempest/api/identity/admin/v3/test_roles.py
index b67de95..5ba4c9f 100644
--- a/tempest/api/identity/admin/v3/test_roles.py
+++ b/tempest/api/identity/admin/v3/test_roles.py
@@ -25,6 +25,10 @@
class RolesV3TestJSON(base.BaseIdentityV3AdminTest):
+ # NOTE: force_tenant_isolation is true in the base class by default but
+ # overridden to false here to allow test execution for clouds using the
+ # pre-provisioned credentials provider.
+ force_tenant_isolation = False
@classmethod
def resource_setup(cls):
diff --git a/tempest/api/volume/admin/test_volume_retype.py b/tempest/api/volume/admin/test_volume_retype.py
index 1c56eb2..9136139 100644
--- a/tempest/api/volume/admin/test_volume_retype.py
+++ b/tempest/api/volume/admin/test_volume_retype.py
@@ -36,7 +36,7 @@
# process is finished.
fetched_list = self.admin_volume_client.list_volumes(
params={'all_tenants': True,
- 'display_name': vol['name']})['volumes']
+ 'name': vol['name']})['volumes']
for fetched_vol in fetched_list:
if fetched_vol['id'] != vol['id']:
diff --git a/tempest/api/volume/test_volumes_backup.py b/tempest/api/volume/test_volumes_backup.py
index c178272..6ce5d3e 100644
--- a/tempest/api/volume/test_volumes_backup.py
+++ b/tempest/api/volume/test_volumes_backup.py
@@ -50,6 +50,7 @@
'available')
return restored_volume
+ @decorators.skip_because(bug="1483434")
@testtools.skipIf(CONF.volume.storage_protocol == 'ceph',
'ceph does not support arbitrary container names')
@decorators.idempotent_id('a66eb488-8ee1-47d4-8e9f-575a095728c6')
diff --git a/tempest/cmd/cleanup_service.py b/tempest/cmd/cleanup_service.py
index 3aed4e8..104958a 100644
--- a/tempest/cmd/cleanup_service.py
+++ b/tempest/cmd/cleanup_service.py
@@ -158,7 +158,7 @@
try:
client.delete_snapshot(snap['id'])
except Exception:
- LOG.exception("Delete Snapshot exception.")
+ LOG.exception("Delete Snapshot %s exception.", snap['id'])
def dry_run(self):
snaps = self.list()
@@ -195,7 +195,7 @@
try:
client.delete_server(server['id'])
except Exception:
- LOG.exception("Delete Server exception.")
+ LOG.exception("Delete Server %s exception.", server['id'])
def dry_run(self):
servers = self.list()
@@ -227,7 +227,7 @@
try:
client.delete_server_group(sg['id'])
except Exception:
- LOG.exception("Delete Server Group exception.")
+ LOG.exception("Delete Server Group %s exception.", sg['id'])
def dry_run(self):
sgs = self.list()
@@ -260,11 +260,11 @@
client = self.client
keypairs = self.list()
for k in keypairs:
+ name = k['keypair']['name']
try:
- name = k['keypair']['name']
client.delete_keypair(name)
except Exception:
- LOG.exception("Delete Keypairs exception.")
+ LOG.exception("Delete Keypair %s exception.", name)
def dry_run(self):
keypairs = self.list()
@@ -300,7 +300,7 @@
try:
client.delete_volume(v['id'])
except Exception:
- LOG.exception("Delete Volume exception.")
+ LOG.exception("Delete Volume %s exception.", v['id'])
def dry_run(self):
vols = self.list()
@@ -323,7 +323,8 @@
try:
client.delete_quota_set(self.project_id)
except Exception:
- LOG.exception("Delete Volume Quotas exception.")
+ LOG.exception("Delete Volume Quotas exception for 'project %s'.",
+ self.project_id)
def dry_run(self):
quotas = self.client.show_quota_set(
@@ -342,7 +343,8 @@
try:
client.delete_quota_set(self.project_id)
except Exception:
- LOG.exception("Delete Quotas exception.")
+ LOG.exception("Delete Quotas exception for 'project %s'.",
+ self.project_id)
def dry_run(self):
client = self.limits_client
@@ -397,7 +399,7 @@
try:
client.delete_network(n['id'])
except Exception:
- LOG.exception("Delete Network exception.")
+ LOG.exception("Delete Network %s exception.", n['id'])
def dry_run(self):
networks = self.list()
@@ -431,7 +433,8 @@
try:
client.delete_floatingip(flip['id'])
except Exception:
- LOG.exception("Delete Network Floating IP exception.")
+ LOG.exception("Delete Network Floating IP %s exception.",
+ flip['id'])
def dry_run(self):
flips = self.list()
@@ -467,16 +470,20 @@
ports_client = self.ports_client
routers = self.list()
for router in routers:
- try:
- rid = router['id']
- ports = [port for port
- in ports_client.list_ports(device_id=rid)['ports']
- if net_info.is_router_interface_port(port)]
- for port in ports:
+ rid = router['id']
+ ports = [port for port
+ in ports_client.list_ports(device_id=rid)['ports']
+ if net_info.is_router_interface_port(port)]
+ for port in ports:
+ try:
client.remove_router_interface(rid, port_id=port['id'])
+ except Exception:
+ LOG.exception("Delete Router Interface exception for "
+ "'port %s' of 'router %s'.", port['id'], rid)
+ try:
client.delete_router(rid)
except Exception:
- LOG.exception("Delete Router exception.")
+ LOG.exception("Delete Router %s exception.", rid)
def dry_run(self):
routers = self.list()
@@ -511,7 +518,8 @@
try:
client.delete_metering_label_rule(rule['id'])
except Exception:
- LOG.exception("Delete Metering Label Rule exception.")
+ LOG.exception("Delete Metering Label Rule %s exception.",
+ rule['id'])
def dry_run(self):
rules = self.list()
@@ -546,7 +554,8 @@
try:
client.delete_metering_label(label['id'])
except Exception:
- LOG.exception("Delete Metering Label exception.")
+ LOG.exception("Delete Metering Label %s exception.",
+ label['id'])
def dry_run(self):
labels = self.list()
@@ -585,7 +594,7 @@
try:
client.delete_port(port['id'])
except Exception:
- LOG.exception("Delete Port exception.")
+ LOG.exception("Delete Port %s exception.", port['id'])
def dry_run(self):
ports = self.list()
@@ -626,7 +635,8 @@
try:
client.delete_security_group(secgroup['id'])
except Exception:
- LOG.exception("Delete security_group exception.")
+ LOG.exception("Delete security_group %s exception.",
+ secgroup['id'])
def dry_run(self):
secgroups = self.list()
@@ -661,7 +671,7 @@
try:
client.delete_subnet(subnet['id'])
except Exception:
- LOG.exception("Delete Subnet exception.")
+ LOG.exception("Delete Subnet %s exception.", subnet['id'])
def dry_run(self):
subnets = self.list()
@@ -696,7 +706,7 @@
try:
client.delete_subnetpool(pool['id'])
except Exception:
- LOG.exception("Delete Subnet Pool exception.")
+ LOG.exception("Delete Subnet Pool %s exception.", pool['id'])
def dry_run(self):
pools = self.list()
@@ -736,7 +746,7 @@
try:
client.delete_flavor(flavor['id'])
except Exception:
- LOG.exception("Delete Flavor exception.")
+ LOG.exception("Delete Flavor %s exception.", flavor['id'])
def dry_run(self):
flavors = self.list()
@@ -773,7 +783,7 @@
try:
client.delete_image(image['id'])
except Exception:
- LOG.exception("Delete Image exception.")
+ LOG.exception("Delete Image %s exception.", image['id'])
def dry_run(self):
images = self.list()
@@ -816,7 +826,7 @@
try:
self.client.delete_user(user['id'])
except Exception:
- LOG.exception("Delete User exception.")
+ LOG.exception("Delete User %s exception.", user['id'])
def dry_run(self):
users = self.list()
@@ -856,7 +866,7 @@
try:
self.client.delete_role(role['id'])
except Exception:
- LOG.exception("Delete Role exception.")
+ LOG.exception("Delete Role %s exception.", role['id'])
def dry_run(self):
roles = self.list()
@@ -898,7 +908,7 @@
try:
self.client.delete_project(project['id'])
except Exception:
- LOG.exception("Delete project exception.")
+ LOG.exception("Delete project %s exception.", project['id'])
def dry_run(self):
projects = self.list()
@@ -935,7 +945,7 @@
client.update_domain(domain['id'], enabled=False)
client.delete_domain(domain['id'])
except Exception:
- LOG.exception("Delete Domain exception.")
+ LOG.exception("Delete Domain %s exception.", domain['id'])
def dry_run(self):
domains = self.list()
diff --git a/tempest/lib/common/api_version_utils.py b/tempest/lib/common/api_version_utils.py
index 709c319..d29362d 100644
--- a/tempest/lib/common/api_version_utils.py
+++ b/tempest/lib/common/api_version_utils.py
@@ -12,6 +12,7 @@
# License for the specific language governing permissions and limitations
# under the License.
+import six
import testtools
from tempest.lib.common import api_version_request
@@ -108,10 +109,12 @@
:param api_microversion_header_name: Microversion header name
Example- "X-OpenStack-Nova-API-Version"
- :param api_microversion: Microversion number like "2.10"
+ :param api_microversion: Microversion number like "2.10", type str.
:param response_header: Response header where microversion is
expected to be present.
"""
+ if not isinstance(api_microversion, six.string_types):
+ raise TypeError('api_microversion must be a string')
api_microversion_header_name = api_microversion_header_name.lower()
if (api_microversion_header_name not in response_header or
api_microversion != response_header[api_microversion_header_name]):
diff --git a/tempest/lib/decorators.py b/tempest/lib/decorators.py
index 4064401..808e0fb 100644
--- a/tempest/lib/decorators.py
+++ b/tempest/lib/decorators.py
@@ -154,3 +154,45 @@
return f
return decorator
+
+
+def unstable_test(*args, **kwargs):
+ """A decorator useful to run tests hitting known bugs and skip it if fails
+
+ This decorator can be used in cases like:
+
+ * We have skipped tests with some bug and now bug is claimed to be fixed.
+ Now we want to check the test stability so we use this decorator.
+ The number of skipped cases with that bug can be counted to mark test
+ stable again.
+ * There is test which is failing often, but not always. If there is known
+ bug related to it, and someone is working on fix, this decorator can be
+ used instead of "skip_because". That will ensure that test is still run
+ so new debug data can be collected from jobs' logs but it will not make
+ life of other developers harder by forcing them to recheck jobs more
+ often.
+
+ ``bug`` must be a number for the test to skip.
+
+ :param bug: bug number causing the test to skip (launchpad or storyboard)
+ :param bug_type: 'launchpad' or 'storyboard', default 'launchpad'
+ :raises: testtools.TestCase.skipException if test actually fails,
+ and ``bug`` is included
+ """
+ def decor(f):
+ @functools.wraps(f)
+ def inner(self, *func_args, **func_kwargs):
+ try:
+ return f(self, *func_args, **func_kwargs)
+ except Exception as e:
+ if "bug" in kwargs:
+ bug = kwargs['bug']
+ bug_type = kwargs.get('bug_type', 'launchpad')
+ bug_url = _get_bug_url(bug, bug_type)
+ msg = ("Marked as unstable and skipped because of bug: "
+ "%s, failure was: %s") % (bug_url, e)
+ raise testtools.TestCase.skipException(msg)
+ else:
+ raise e
+ return inner
+ return decor
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index d2fd021..d09f20c 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -324,6 +324,25 @@
snapshot['id'])['snapshot']
return snapshot
+ def _cleanup_volume_type(self, volume_type):
+ """Clean up a given volume type.
+
+ Ensuring all volumes associated to a type are first removed before
+ attempting to remove the type itself. This includes any image volume
+ cache volumes stored in a separate tenant to the original volumes
+ created from the type.
+ """
+ admin_volume_type_client = self.os_admin.volume_types_client_latest
+ admin_volumes_client = self.os_admin.volumes_client_latest
+ volumes = admin_volumes_client.list_volumes(
+ detail=True, params={'all_tenants': 1})['volumes']
+ type_name = volume_type['name']
+ for volume in [v for v in volumes if v['volume_type'] == type_name]:
+ test_utils.call_and_ignore_notfound_exc(
+ admin_volumes_client.delete_volume, volume['id'])
+ admin_volumes_client.wait_for_resource_deletion(volume['id'])
+ admin_volume_type_client.delete_volume_type(volume_type['id'])
+
def create_volume_type(self, client=None, name=None, backend_name=None):
if not client:
client = self.os_admin.volume_types_client_latest
@@ -338,7 +357,7 @@
volume_type = client.create_volume_type(
name=randomized_name, extra_specs=extra_specs)['volume_type']
- self.addCleanup(client.delete_volume_type, volume_type['id'])
+ self.addCleanup(self._cleanup_volume_type, volume_type)
return volume_type
def _create_loginable_secgroup_rule(self, secgroup_id=None):
diff --git a/tempest/tests/lib/test_decorators.py b/tempest/tests/lib/test_decorators.py
index 3e6160e..9c6cac7 100644
--- a/tempest/tests/lib/test_decorators.py
+++ b/tempest/tests/lib/test_decorators.py
@@ -13,7 +13,10 @@
# License for the specific language governing permissions and limitations
# under the License.
+import abc
+
import mock
+import six
import testtools
from tempest.lib import base as test
@@ -66,9 +69,36 @@
condition=True)
-class TestSkipBecauseDecorator(base.TestCase):
- def _test_skip_because_helper(self, expected_to_skip=True,
- **decorator_args):
+@six.add_metaclass(abc.ABCMeta)
+class BaseSkipDecoratorTests(object):
+
+ @abc.abstractmethod
+ def _test_skip_helper(self, raise_exception=True, expected_to_skip=True,
+ **decorator_args):
+ return
+
+ def test_skip_launchpad_bug(self):
+ self._test_skip_helper(bug='12345')
+
+ def test_skip_storyboard_bug(self):
+ self._test_skip_helper(bug='1992', bug_type='storyboard')
+
+ def test_skip_bug_without_bug_never_skips(self):
+ """Never skip without a bug parameter."""
+ self._test_skip_helper(
+ raise_exception=False, expected_to_skip=False, condition=True)
+ self._test_skip_helper(
+ raise_exception=False, expected_to_skip=False)
+
+ def test_skip_invalid_bug_number(self):
+ """Raise InvalidParam if with an invalid bug number"""
+ self.assertRaises(lib_exc.InvalidParam, self._test_skip_helper,
+ bug='critical_bug')
+
+
+class TestSkipBecauseDecorator(base.TestCase, BaseSkipDecoratorTests):
+ def _test_skip_helper(self, raise_exception=True, expected_to_skip=True,
+ **decorator_args):
class TestFoo(test.BaseTestCase):
_interface = 'json'
@@ -90,38 +120,56 @@
# assert that test_bar returned 0
self.assertEqual(TestFoo('test_bar').test_bar(), 0)
- def test_skip_because_launchpad_bug(self):
- self._test_skip_because_helper(bug='12345')
-
def test_skip_because_launchpad_bug_and_condition_true(self):
- self._test_skip_because_helper(bug='12348', condition=True)
+ self._test_skip_helper(bug='12348', condition=True)
def test_skip_because_launchpad_bug_and_condition_false(self):
- self._test_skip_because_helper(expected_to_skip=False,
- bug='12349', condition=False)
-
- def test_skip_because_storyboard_bug(self):
- self._test_skip_because_helper(bug='1992', bug_type='storyboard')
-
- def test_skip_because_storyboard_bug_and_condition_true(self):
- self._test_skip_because_helper(bug='1992', bug_type='storyboard',
- condition=True)
+ self._test_skip_helper(expected_to_skip=False,
+ bug='12349', condition=False)
def test_skip_because_storyboard_bug_and_condition_false(self):
- self._test_skip_because_helper(expected_to_skip=False,
- bug='1992', bug_type='storyboard',
- condition=False)
+ self._test_skip_helper(expected_to_skip=False,
+ bug='1992', bug_type='storyboard',
+ condition=False)
- def test_skip_because_bug_without_bug_never_skips(self):
- """Never skip without a bug parameter."""
- self._test_skip_because_helper(expected_to_skip=False,
- condition=True)
- self._test_skip_because_helper(expected_to_skip=False)
+ def test_skip_because_storyboard_bug_and_condition_true(self):
+ self._test_skip_helper(bug='1992', bug_type='storyboard',
+ condition=True)
- def test_skip_because_invalid_bug_number(self):
- """Raise InvalidParam if with an invalid bug number"""
- self.assertRaises(lib_exc.InvalidParam, self._test_skip_because_helper,
- bug='critical_bug')
+
+class TestUnstableTestDecorator(base.TestCase, BaseSkipDecoratorTests):
+
+ def _test_skip_helper(self, raise_exception=True, expected_to_skip=True,
+ **decorator_args):
+ fail_test_reason = "test_bar failed"
+
+ class TestFoo(test.BaseTestCase):
+
+ @decorators.unstable_test(**decorator_args)
+ def test_bar(self):
+ if raise_exception:
+ raise Exception(fail_test_reason)
+ else:
+ return 0
+
+ t = TestFoo('test_bar')
+ if expected_to_skip:
+ e = self.assertRaises(testtools.TestCase.skipException, t.test_bar)
+ bug = decorator_args['bug']
+ bug_type = decorator_args.get('bug_type', 'launchpad')
+ self.assertRegex(
+ str(e),
+ r'Marked as unstable and skipped because of bug\: %s.*, '
+ 'failure was: %s' % (decorators._get_bug_url(bug, bug_type),
+ fail_test_reason)
+ )
+ else:
+ # assert that test_bar returned 0
+ self.assertEqual(TestFoo('test_bar').test_bar(), 0)
+
+ def test_skip_bug_given_exception_not_raised(self):
+ self._test_skip_helper(raise_exception=False, expected_to_skip=False,
+ bug='1234')
class TestIdempotentIdDecorator(base.TestCase):