Merge "Removing unused attributes from test_run.py"
diff --git a/.zuul.yaml b/.zuul.yaml
index fd3aa2a..52c11c9 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -9,10 +9,10 @@
test setup. To run a multi-node test inherit from devstack-tempest and
set the nodeset to a multi-node one.
required-projects:
- - openstack/tempest
+ - git.openstack.org/openstack/tempest
timeout: 7200
roles:
- - zuul: openstack-dev/devstack
+ - zuul: git.openstack.org/openstack-dev/devstack
vars:
devstack_services:
tempest: true
@@ -101,13 +101,14 @@
- master
description: |
Base multinode integration test with Neutron networking and py27.
- Former names for this job where:
+ Former names for this job were:
* neutron-tempest-multinode-full
* legacy-tempest-dsvm-neutron-multinode-full
* gate-tempest-dsvm-neutron-multinode-full-ubuntu-xenial-nv
This job includes two nodes, controller / tempest plus a subnode, but
it can be used with different topologies, as long as a controller node
and a tempest one exist.
+ timeout: 10800
vars:
tox_envlist: full
devstack_localrc:
@@ -142,21 +143,25 @@
Base integration test with Neutron networking and py36.
voting: false
-# TODO(gmann): needs to migrate this to zuulv3
- job:
- name: tempest-scenario-all
- parent: legacy-dsvm-base-multinode
+ name: tempest-slow
+ parent: tempest-multinode-full
+ branches:
+ - master
description: |
- This job will run all scenario tests including slow tests
- with lvm multibackend setup. This job will not run any API tests.
- run: playbooks/tempest-scenario-multinode-lvm-multibackend/run.yaml
- post-run: playbooks/tempest-scenario-multinode-lvm-multibackend/post.yaml
+ This multinode integration job will run all the tests tagged as slow.
+ It enables the lvm multibackend setup to cover few scenario tests.
+ This job will run only slow tests(API or Scenario) serially.
+
+ Former names for this job were:
+ * legacy-tempest-dsvm-neutron-scenario-multinode-lvm-multibackend
+ * tempest-scenario-multinode-lvm-multibackend
timeout: 10800
- required-projects:
- - openstack-infra/devstack-gate
- - openstack/neutron
- - openstack/tempest
- nodeset: ubuntu-xenial-2-node
+ vars:
+ tox_envlist: slow-serial
+ devstack_localrc:
+ CINDER_ENABLED_BACKENDS: lvm:lvmdriver-1,lvm:lvmdriver-2
+ tempest_concurrency: 2
- job:
name: tempest-full-queens
@@ -191,70 +196,70 @@
- ^tempest/hacking/.*$
- ^tempest/tests/.*$
required-projects:
- - openstack/almanach
- - openstack/aodh
- - openstack/barbican-tempest-plugin
- - openstack/ceilometer
- - openstack/cinder
- - openstack/congress
- - openstack/designate-tempest-plugin
- - openstack/ec2-api
- - openstack/freezer
- - openstack/freezer-api
- - openstack/freezer-tempest-plugin
- - openstack/gce-api
- - openstack/glare
- - openstack/heat
- - openstack/intel-nfv-ci-tests
- - openstack/ironic
- - openstack/ironic-inspector
- - openstack/keystone-tempest-plugin
- - openstack/kingbird
- - openstack/kuryr-tempest-plugin
- - openstack/magnum
- - openstack/magnum-tempest-plugin
- - openstack/manila
- - openstack/manila-tempest-plugin
- - openstack/mistral
- - openstack/mogan
- - openstack/monasca-api
- - openstack/monasca-log-api
- - openstack/murano
- - openstack/networking-bgpvpn
- - openstack/networking-cisco
- - openstack/networking-fortinet
- - openstack/networking-generic-switch
- - openstack/networking-l2gw
- - openstack/networking-midonet
- - openstack/networking-plumgrid
- - openstack/networking-sfc
- - openstack/neutron
- - openstack/neutron-dynamic-routing
- - openstack/neutron-fwaas
- - openstack/neutron-lbaas
- - openstack/neutron-tempest-plugin
- - openstack/neutron-vpnaas
- - openstack/nova-lxd
- - openstack/novajoin-tempest-plugin
- - openstack/octavia-tempest-plugin
- - openstack/oswin-tempest-plugin
- - openstack/panko
- - openstack/patrole
- - openstack/qinling
- - openstack/requirements
- - openstack/sahara-tests
- - openstack/senlin
- - openstack/senlin-tempest-plugin
- - openstack/tap-as-a-service
- - openstack/tempest-horizon
- - openstack/trio2o
- - openstack/trove
- - openstack/valet
- - openstack/vitrage
- - openstack/vmware-nsx-tempest-plugin
- - openstack/watcher-tempest-plugin
- - openstack/zaqar-tempest-plugin
- - openstack/zun-tempest-plugin
+ - git.openstack.org/openstack/almanach
+ - git.openstack.org/openstack/aodh
+ - git.openstack.org/openstack/barbican-tempest-plugin
+ - git.openstack.org/openstack/ceilometer
+ - git.openstack.org/openstack/cinder
+ - git.openstack.org/openstack/congress
+ - git.openstack.org/openstack/designate-tempest-plugin
+ - git.openstack.org/openstack/ec2-api
+ - git.openstack.org/openstack/freezer
+ - git.openstack.org/openstack/freezer-api
+ - git.openstack.org/openstack/freezer-tempest-plugin
+ - git.openstack.org/openstack/gce-api
+ - git.openstack.org/openstack/glare
+ - git.openstack.org/openstack/heat
+ - git.openstack.org/openstack/intel-nfv-ci-tests
+ - git.openstack.org/openstack/ironic
+ - 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
+ - git.openstack.org/openstack/mogan
+ - git.openstack.org/openstack/monasca-api
+ - git.openstack.org/openstack/monasca-log-api
+ - git.openstack.org/openstack/murano
+ - 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
+ - git.openstack.org/openstack/networking-midonet
+ - git.openstack.org/openstack/networking-plumgrid
+ - git.openstack.org/openstack/networking-sfc
+ - 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-tempest-plugin
+ - git.openstack.org/openstack/oswin-tempest-plugin
+ - git.openstack.org/openstack/panko
+ - git.openstack.org/openstack/patrole
+ - 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/tap-as-a-service
+ - git.openstack.org/openstack/tempest-horizon
+ - git.openstack.org/openstack/trio2o
+ - git.openstack.org/openstack/trove
+ - git.openstack.org/openstack/valet
+ - git.openstack.org/openstack/vitrage
+ - 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
- job:
name: tempest-cinder-v2-api
@@ -277,7 +282,16 @@
- ^playbooks/
- ^roles/
- ^.zuul.yaml$
- - nova-multiattach
+ - nova-multiattach:
+ irrelevant-files:
+ - ^(test-|)requirements.txt$
+ - ^.*\.rst$
+ - ^doc/.*$
+ - ^etc/.*$
+ - ^releasenotes/.*$
+ - ^setup.cfg$
+ - ^tempest/hacking/.*$
+ - ^tempest/tests/.*$
- tempest-full-parallel:
irrelevant-files:
- ^(test-|)requirements.txt$
@@ -339,7 +353,7 @@
- ^tempest/hacking/.*$
- ^tempest/tests/.*$
- tempest-tox-plugin-sanity-check
- - tempest-scenario-all:
+ - tempest-slow:
irrelevant-files:
- ^(test-|)requirements.txt$
- ^.*\.rst$
@@ -349,7 +363,6 @@
- ^setup.cfg$
- ^tempest/hacking/.*$
- ^tempest/tests/.*$
- - ^tempest/api/.*$
- nova-cells-v1:
irrelevant-files:
- ^(test-|)requirements.txt$
@@ -373,8 +386,7 @@
- ^tempest/tests/.*$
gate:
jobs:
- - nova-multiattach
- - tempest-scenario-all:
+ - nova-multiattach:
irrelevant-files:
- ^(test-|)requirements.txt$
- ^.*\.rst$
@@ -384,7 +396,16 @@
- ^setup.cfg$
- ^tempest/hacking/.*$
- ^tempest/tests/.*$
- - ^tempest/api/.*$
+ - tempest-slow:
+ irrelevant-files:
+ - ^(test-|)requirements.txt$
+ - ^.*\.rst$
+ - ^doc/.*$
+ - ^etc/.*$
+ - ^releasenotes/.*$
+ - ^setup.cfg$
+ - ^tempest/hacking/.*$
+ - ^tempest/tests/.*$
experimental:
jobs:
- tempest-cinder-v2-api:
diff --git a/HACKING.rst b/HACKING.rst
index 2a7ae1d..5b9c0f1 100644
--- a/HACKING.rst
+++ b/HACKING.rst
@@ -25,6 +25,8 @@
- [T115] Check that admin tests should exist under admin path
- [N322] Method's default argument shouldn't be mutable
- [T116] Unsupported 'message' Exception attribute in PY3
+- [T117] Check negative tests have ``@decorators.attr(type=['negative'])``
+ applied.
Test Data/Configuration
-----------------------
@@ -146,11 +148,6 @@
This attribute must be applied to each test that belongs to a negative test
class, i.e. a test class name ending with "Negative.*" substring.
-.. todo::
-
- Add a hacking check for ensuring that all classes that contain substring
- "Negative" have the negative attribute decorator applied above each test.
-
Slow Attribute
^^^^^^^^^^^^^^
The ``type='slow'`` attribute is used to signify that a test takes a long time
diff --git a/doc/source/data/tempest-plugins-registry.header b/doc/source/data/tempest-plugins-registry.header
index 9821e8e..0de12b7 100644
--- a/doc/source/data/tempest-plugins-registry.header
+++ b/doc/source/data/tempest-plugins-registry.header
@@ -17,7 +17,3 @@
The following are plugins that a script has found in the openstack/
namespace, which includes but is not limited to official OpenStack
projects.
-
-+----------------------------+-------------------------------------------------------------------------+
-|Plugin Name |URL |
-+----------------------------+-------------------------------------------------------------------------+
diff --git a/doc/source/microversion_testing.rst b/doc/source/microversion_testing.rst
index 6dd00d3..ce9bbb5 100644
--- a/doc/source/microversion_testing.rst
+++ b/doc/source/microversion_testing.rst
@@ -338,10 +338,18 @@
.. _2.32: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id29
+ * `2.36`_
+
+ .. _2.36: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#microversion
+
* `2.37`_
.. _2.37: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id34
+ * `2.39`_
+
+ .. _2.39: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id35
+
* `2.42`_
.. _2.42: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#maximum-in-ocata
diff --git a/playbooks/tempest-scenario-multinode-lvm-multibackend/post.yaml b/playbooks/tempest-scenario-multinode-lvm-multibackend/post.yaml
deleted file mode 100644
index e07f551..0000000
--- a/playbooks/tempest-scenario-multinode-lvm-multibackend/post.yaml
+++ /dev/null
@@ -1,15 +0,0 @@
-- hosts: primary
- tasks:
-
- - name: Copy files from {{ ansible_user_dir }}/workspace/ on node
- synchronize:
- src: '{{ ansible_user_dir }}/workspace/'
- dest: '{{ zuul.executor.log_root }}'
- mode: pull
- copy_links: true
- verify_host: true
- rsync_opts:
- - --include=/logs/**
- - --include=*/
- - --exclude=*
- - --prune-empty-dirs
diff --git a/playbooks/tempest-scenario-multinode-lvm-multibackend/run.yaml b/playbooks/tempest-scenario-multinode-lvm-multibackend/run.yaml
deleted file mode 100644
index 57b4074..0000000
--- a/playbooks/tempest-scenario-multinode-lvm-multibackend/run.yaml
+++ /dev/null
@@ -1,65 +0,0 @@
-- hosts: primary
- name: Autoconverted job tempest-scenario-multinode-lvm-multibackend
- from old job gate-tempest-dsvm-neutron-scenario-multinode-lvm-multibackend-ubuntu-xenial-nv
- tasks:
-
- - name: Ensure legacy workspace directory
- file:
- path: '{{ ansible_user_dir }}/workspace'
- state: directory
-
- - shell:
- cmd: |
- set -e
- set -x
- cat > clonemap.yaml << EOF
- clonemap:
- - name: openstack-infra/devstack-gate
- dest: devstack-gate
- EOF
- /usr/zuul-env/bin/zuul-cloner -m clonemap.yaml --cache-dir /opt/git \
- git://git.openstack.org \
- openstack-infra/devstack-gate
- executable: /bin/bash
- chdir: '{{ ansible_user_dir }}/workspace'
- environment: '{{ zuul | zuul_legacy_vars }}'
-
- - shell:
- cmd: |
- set -e
- set -x
- cat << 'EOF' >>"/tmp/dg-local.conf"
- [[local|localrc]]
- ENABLE_IDENTITY_V2=False
- TEMPEST_USE_TEST_ACCOUNTS=True
- # Enable lvm multiple backends to run multi backend slow scenario tests.
- # Note: multi backend experimental job exclude the slow scenario tests.
- CINDER_ENABLED_BACKENDS=lvm:lvmdriver-1,lvm:lvmdriver-2
-
- EOF
- executable: /bin/bash
- chdir: '{{ ansible_user_dir }}/workspace'
- environment: '{{ zuul | zuul_legacy_vars }}'
-
- - shell:
- cmd: |
- set -e
- set -x
- export PYTHONUNBUFFERED=true
- export DEVSTACK_GATE_TEMPEST=1
- # Run all scenario tests including slow tests with concurrency 2
- export DEVSTACK_GATE_TEMPEST_REGEX='(^tempest\.(scenario))'
- export TEMPEST_CONCURRENCY=2
- export DEVSTACK_GATE_NEUTRON=1
- export DEVSTACK_GATE_TLSPROXY=1
- export BRANCH_OVERRIDE=default
- if [ "$BRANCH_OVERRIDE" != "default" ] ; then
- export OVERRIDE_ZUUL_BRANCH=$BRANCH_OVERRIDE
- fi
- export DEVSTACK_GATE_TOPOLOGY="multinode"
-
- cp devstack-gate/devstack-vm-gate-wrap.sh ./safe-devstack-vm-gate-wrap.sh
- ./safe-devstack-vm-gate-wrap.sh
- executable: /bin/bash
- chdir: '{{ ansible_user_dir }}/workspace'
- environment: '{{ zuul | zuul_legacy_vars }}'
diff --git a/releasenotes/notes/add-storyboard-in-skip-because-decorator-3e139aa8a4f7970f.yaml b/releasenotes/notes/add-storyboard-in-skip-because-decorator-3e139aa8a4f7970f.yaml
new file mode 100644
index 0000000..dd4a90b
--- /dev/null
+++ b/releasenotes/notes/add-storyboard-in-skip-because-decorator-3e139aa8a4f7970f.yaml
@@ -0,0 +1,17 @@
+---
+features:
+ - |
+ Add a new parameter called ``bug_type`` to
+ ``tempest.lib.decorators.related_bug`` and
+ ``tempest.lib.decorators.skip_because`` decorators, which accepts
+ 2 values:
+
+ * launchpad
+ * storyboard
+
+ This offers the possibility of tracking bugs related to tests using
+ launchpad or storyboard references. The default value is launchpad
+ for backward compatibility.
+
+ Passing in a non-digit ``bug`` value to either decorator will raise
+ a ``InvalidParam`` exception (previously ``ValueError``).
diff --git a/releasenotes/notes/cinder-use-os-endpoint-type-c11f63fd468ceb4c.yaml b/releasenotes/notes/cinder-use-os-endpoint-type-c11f63fd468ceb4c.yaml
new file mode 100644
index 0000000..1dda4e1
--- /dev/null
+++ b/releasenotes/notes/cinder-use-os-endpoint-type-c11f63fd468ceb4c.yaml
@@ -0,0 +1,6 @@
+---
+upgrade:
+ - |
+ Cinder CLI calls have now been updated to use the ``--os-endpoint-type``
+ option instead of ``--endpoint-type``. The latter had been deprecated in
+ Cinder and has been removed in the Rocky release.
diff --git a/releasenotes/notes/omit_X-Subject-Token_from_log-1bf5fef88c80334b.yaml b/releasenotes/notes/omit_X-Subject-Token_from_log-1bf5fef88c80334b.yaml
new file mode 100644
index 0000000..51c8f79
--- /dev/null
+++ b/releasenotes/notes/omit_X-Subject-Token_from_log-1bf5fef88c80334b.yaml
@@ -0,0 +1,7 @@
+---
+security:
+ - |
+ The x-subject-token of a response header is ommitted from log,
+ but clients specify the same token on a request header on
+ Keystone API and that was not omitted. In this release,
+ that has been omitted for a security reason.
diff --git a/roles/run-tempest/README.rst b/roles/run-tempest/README.rst
index 384ca38..71b8e4f 100644
--- a/roles/run-tempest/README.rst
+++ b/roles/run-tempest/README.rst
@@ -39,3 +39,20 @@
:default: smoke
The Tempest tox environment to run.
+
+.. zuul:rolevar:: tempest_black_regex
+ :default: ''
+
+ A regular expression used to skip the tests.
+
+ It works only when used with some specific tox environments
+ ('all', 'all-plugin'.)
+
+ Multi-line and commented regexs can be achieved by doing this:
+
+ ::
+ vars:
+ tempest_black_regex: |
+ (?x) # Ignore comments and whitespaces
+ # Line with only a comment.
+ (tempest.api.identity).*$
diff --git a/roles/run-tempest/defaults/main.yaml b/roles/run-tempest/defaults/main.yaml
index 85e94f2..c89eb93 100644
--- a/roles/run-tempest/defaults/main.yaml
+++ b/roles/run-tempest/defaults/main.yaml
@@ -1,3 +1,4 @@
devstack_base_dir: /opt/stack
tempest_test_regex: ''
tox_envlist: smoke
+tempest_black_regex: ''
diff --git a/roles/run-tempest/tasks/main.yaml b/roles/run-tempest/tasks/main.yaml
index b68507a..54ddc71 100644
--- a/roles/run-tempest/tasks/main.yaml
+++ b/roles/run-tempest/tasks/main.yaml
@@ -35,7 +35,9 @@
when: blacklist_stat.stat.exists
- name: Run Tempest
- command: tox -e {{tox_envlist}} -- {{tempest_test_regex|quote}} {{blacklist_option|default('')}} --concurrency={{tempest_concurrency|default(default_concurrency)}}
+ command: tox -e {{tox_envlist}} -- {{tempest_test_regex|quote}} {{blacklist_option|default('')}} \
+ --concurrency={{tempest_concurrency|default(default_concurrency)}} \
+ --black-regex={{tempest_black_regex|quote}}
args:
chdir: "{{devstack_base_dir}}/tempest"
become: true
diff --git a/tempest/api/compute/admin/test_aggregates.py b/tempest/api/compute/admin/test_aggregates.py
index 57d3983..d8faa33 100644
--- a/tempest/api/compute/admin/test_aggregates.py
+++ b/tempest/api/compute/admin/test_aggregates.py
@@ -48,11 +48,11 @@
if (hyper['hypervisor_type'] ==
CONF.compute.hypervisor_type)]
- hosts_available = [hyper['service']['host'] for hyper in hypers
- if (hyper['state'] == 'up' and
- hyper['status'] == 'enabled')]
- if hosts_available:
- cls.host = hosts_available[0]
+ cls.hosts_available = [hyper['service']['host'] for hyper in hypers
+ if (hyper['state'] == 'up' and
+ hyper['status'] == 'enabled')]
+ if cls.hosts_available:
+ cls.host = cls.hosts_available[0]
else:
msg = "no available compute node found"
if CONF.compute.hypervisor_type:
@@ -206,11 +206,23 @@
az_name = data_utils.rand_name(self.az_name_prefix)
aggregate = self._create_test_aggregate(availability_zone=az_name)
- self.client.add_host(aggregate['id'], host=self.host)
- self.addCleanup(self.client.remove_host, aggregate['id'],
- host=self.host)
+ # Find a host that has not been added to other zone,
+ # for one host can't be added to different zones.
+ aggregates = self.client.list_aggregates()['aggregates']
+ hosts_in_zone = []
+ for v in aggregates:
+ if v['availability_zone']:
+ hosts_in_zone.extend(v['hosts'])
+ hosts = [v for v in self.hosts_available if v not in hosts_in_zone]
+ if not hosts:
+ raise self.skipException("All hosts are already in other zones, "
+ "so can't add host to aggregate.")
+ host = hosts[0]
+
+ self.client.add_host(aggregate['id'], host=host)
+ self.addCleanup(self.client.remove_host, aggregate['id'], host=host)
admin_servers_client = self.os_admin.servers_client
server = self.create_test_server(availability_zone=az_name,
wait_until='ACTIVE')
body = admin_servers_client.show_server(server['id'])['server']
- self.assertEqual(self.host, body['OS-EXT-SRV-ATTR:host'])
+ self.assertEqual(host, body['OS-EXT-SRV-ATTR:host'])
diff --git a/tempest/api/compute/admin/test_flavors_microversions.py b/tempest/api/compute/admin/test_flavors_microversions.py
index 9f014e6..31b9217 100644
--- a/tempest/api/compute/admin/test_flavors_microversions.py
+++ b/tempest/api/compute/admin/test_flavors_microversions.py
@@ -33,14 +33,14 @@
disk=10,
id=flavor_id)['id']
# Checking show API response schema
- self.flavors_client.show_flavor(new_flavor_id)['flavor']
+ self.flavors_client.show_flavor(new_flavor_id)
# Checking update API response schema
self.admin_flavors_client.update_flavor(new_flavor_id,
- description='new')['flavor']
+ description='new')
# Checking list details API response schema
- self.flavors_client.list_flavors(detail=True)['flavors']
+ self.flavors_client.list_flavors(detail=True)
# Checking list API response schema
- self.flavors_client.list_flavors()['flavors']
+ self.flavors_client.list_flavors()
class FlavorsV261TestJSON(FlavorsV255TestJSON):
diff --git a/tempest/api/compute/admin/test_floating_ips_bulk.py b/tempest/api/compute/admin/test_floating_ips_bulk.py
index 72d09ed..2d7e1a7 100644
--- a/tempest/api/compute/admin/test_floating_ips_bulk.py
+++ b/tempest/api/compute/admin/test_floating_ips_bulk.py
@@ -25,6 +25,8 @@
CONF = config.CONF
+# TODO(stephenfin): Remove this test class once the nova queens branch goes
+# into extended maintenance mode.
class FloatingIPsBulkAdminTestJSON(base.BaseV2ComputeAdminTest):
"""Tests Floating IPs Bulk APIs that require admin privileges.
@@ -32,6 +34,7 @@
content/ext-os-floating-ips-bulk.html
"""
max_microversion = '2.35'
+ depends_on_nova_network = True
@classmethod
def setup_clients(cls):
diff --git a/tempest/api/compute/admin/test_live_migration.py b/tempest/api/compute/admin/test_live_migration.py
index bc38144..8350f7c 100644
--- a/tempest/api/compute/admin/test_live_migration.py
+++ b/tempest/api/compute/admin/test_live_migration.py
@@ -66,7 +66,8 @@
kwargs = dict()
block_migration = getattr(self, 'block_migration', None)
if self.block_migration is None:
- kwargs['disk_over_commit'] = False
+ if self.is_requested_microversion_compatible('2.24'):
+ kwargs['disk_over_commit'] = False
block_migration = (CONF.compute_feature_enabled.
block_migration_for_live_migration and
not volume_backed)
diff --git a/tempest/api/compute/admin/test_live_migration_negative.py b/tempest/api/compute/admin/test_live_migration_negative.py
index deabbc2..8327a3b 100644
--- a/tempest/api/compute/admin/test_live_migration_negative.py
+++ b/tempest/api/compute/admin/test_live_migration_negative.py
@@ -32,9 +32,10 @@
def _migrate_server_to(self, server_id, dest_host):
bmflm = CONF.compute_feature_enabled.block_migration_for_live_migration
- self.admin_servers_client.live_migrate_server(
- server_id, host=dest_host, block_migration=bmflm,
- disk_over_commit=False)
+ kwargs = dict(host=dest_host, block_migration=bmflm)
+ if self.is_requested_microversion_compatible('2.24'):
+ kwargs['disk_over_commit'] = False
+ self.admin_servers_client.live_migrate_server(server_id, **kwargs)
@decorators.attr(type=['negative'])
@decorators.idempotent_id('7fb7856e-ae92-44c9-861a-af62d7830bcb')
diff --git a/tempest/api/compute/admin/test_migrations.py b/tempest/api/compute/admin/test_migrations.py
index a6b71b2..e030575 100644
--- a/tempest/api/compute/admin/test_migrations.py
+++ b/tempest/api/compute/admin/test_migrations.py
@@ -106,7 +106,7 @@
'ACTIVE')
server = self.servers_client.show_server(server['id'])['server']
- self.assertEqual(flavor['id'], server['flavor']['id'])
+ self.assert_flavor_equal(flavor['id'], server['flavor'])
def _test_cold_migrate_server(self, revert=False):
if CONF.compute.min_compute_nodes < 2:
diff --git a/tempest/api/compute/admin/test_networks.py b/tempest/api/compute/admin/test_networks.py
index 87ce39d..99907a8 100644
--- a/tempest/api/compute/admin/test_networks.py
+++ b/tempest/api/compute/admin/test_networks.py
@@ -26,6 +26,7 @@
API docs:
https://developer.openstack.org/api-ref/compute/#networks-os-networks-deprecated
"""
+ max_microversion = '2.35'
@classmethod
def setup_clients(cls):
diff --git a/tempest/api/compute/admin/test_servers.py b/tempest/api/compute/admin/test_servers.py
index cdfc44a..170b2cc 100644
--- a/tempest/api/compute/admin/test_servers.py
+++ b/tempest/api/compute/admin/test_servers.py
@@ -176,7 +176,7 @@
self.assertEqual(self.s1_id, rebuilt_server['id'])
rebuilt_image_id = rebuilt_server['image']['id']
self.assertEqual(self.image_ref_alt, rebuilt_image_id)
- self.assertEqual(self.flavor_ref, rebuilt_server['flavor']['id'])
+ self.assert_flavor_equal(self.flavor_ref, rebuilt_server['flavor'])
waiters.wait_for_server_status(self.non_admin_client,
rebuilt_server['id'], 'ACTIVE',
raise_on_error=False)
diff --git a/tempest/api/compute/admin/test_services_negative.py b/tempest/api/compute/admin/test_services_negative.py
index 993c8ec..d264829 100644
--- a/tempest/api/compute/admin/test_services_negative.py
+++ b/tempest/api/compute/admin/test_services_negative.py
@@ -74,8 +74,6 @@
@classmethod
def resource_setup(cls):
super(ServicesAdminNegativeV253TestJSON, cls).resource_setup()
- # Nova returns 400 if `binary` is not nova-compute.
- cls.binary = 'nova-compute'
cls.fake_service_id = data_utils.rand_uuid()
@decorators.attr(type=['negative'])
diff --git a/tempest/api/compute/base.py b/tempest/api/compute/base.py
index d0c1973..7fbb994 100644
--- a/tempest/api/compute/base.py
+++ b/tempest/api/compute/base.py
@@ -428,21 +428,16 @@
except Exception:
LOG.exception('Failed to delete server %s', server_id)
- @classmethod
- def resize_server(cls, server_id, new_flavor_id, **kwargs):
+ def resize_server(self, server_id, new_flavor_id, **kwargs):
"""resize and confirm_resize an server, waits for it to be ACTIVE."""
- cls.servers_client.resize_server(server_id, new_flavor_id, **kwargs)
- waiters.wait_for_server_status(cls.servers_client, server_id,
+ self.servers_client.resize_server(server_id, new_flavor_id, **kwargs)
+ waiters.wait_for_server_status(self.servers_client, server_id,
'VERIFY_RESIZE')
- cls.servers_client.confirm_resize_server(server_id)
- waiters.wait_for_server_status(cls.servers_client, server_id, 'ACTIVE')
- server = cls.servers_client.show_server(server_id)['server']
- # Nova API > 2.46 no longer includes flavor.id
- if server['flavor'].get('id'):
- if new_flavor_id != server['flavor']['id']:
- msg = ('Flavor id of %s is not equal to new_flavor_id.'
- % server_id)
- raise lib_exc.TempestException(msg)
+ self.servers_client.confirm_resize_server(server_id)
+ waiters.wait_for_server_status(
+ self.servers_client, server_id, 'ACTIVE')
+ server = self.servers_client.show_server(server_id)['server']
+ self.assert_flavor_equal(new_flavor_id, server['flavor'])
@classmethod
def delete_volume(cls, volume_id):
@@ -561,6 +556,27 @@
volume['id'], 'in-use')
return attachment
+ def assert_flavor_equal(self, flavor_id, server_flavor):
+ """Check whether server_flavor equals to flavor.
+
+ :param flavor_id: flavor id
+ :param server_flavor: flavor info returned by show_server.
+ """
+ # Nova API > 2.46 no longer includes flavor.id, and schema check
+ # will cover whether 'id' should be in flavor
+ if server_flavor.get('id'):
+ msg = ('server flavor is not same as flavor!')
+ self.assertEqual(flavor_id, server_flavor['id'], msg)
+ else:
+ flavor = self.flavors_client.show_flavor(flavor_id)['flavor']
+ self.assertEqual(flavor['name'], server_flavor['original_name'],
+ "original_name in server flavor is not same as "
+ "flavor name!")
+ for key in ['ram', 'vcpus', 'disk']:
+ msg = ('attribute %s in server flavor is not same as '
+ 'flavor!' % key)
+ self.assertEqual(flavor[key], server_flavor[key], msg)
+
class BaseV2ComputeAdminTest(BaseV2ComputeTest):
"""Base test case class for Compute Admin API tests."""
diff --git a/tempest/api/compute/images/test_images_oneserver_negative.py b/tempest/api/compute/images/test_images_oneserver_negative.py
index a2e58c9..bebc6ca 100644
--- a/tempest/api/compute/images/test_images_oneserver_negative.py
+++ b/tempest/api/compute/images/test_images_oneserver_negative.py
@@ -33,8 +33,11 @@
def tearDown(self):
"""Terminate test instances created after a test is executed."""
- self.server_check_teardown()
super(ImagesOneServerNegativeTestJSON, self).tearDown()
+ # NOTE(zhufl): Because server_check_teardown will raise Exception
+ # which will prevent other cleanup steps from being executed, so
+ # server_check_teardown should be called after super's tearDown.
+ self.server_check_teardown()
def setUp(self):
# NOTE(afazekas): Normally we use the same server with all test cases,
diff --git a/tempest/api/compute/limits/test_absolute_limits.py b/tempest/api/compute/limits/test_absolute_limits.py
index 0585fec..8c2202e 100644
--- a/tempest/api/compute/limits/test_absolute_limits.py
+++ b/tempest/api/compute/limits/test_absolute_limits.py
@@ -18,6 +18,7 @@
class AbsoluteLimitsTestJSON(base.BaseV2ComputeTest):
+ max_microversion = '2.56'
@classmethod
def setup_clients(cls):
@@ -26,22 +27,14 @@
@decorators.idempotent_id('b54c66af-6ab6-4cf0-a9e5-a0cb58d75e0b')
def test_absLimits_get(self):
- # To check if all limits are present in the response
- limits = self.client.show_limits()['limits']
- absolute_limits = limits['absolute']
- expected_elements = ['maxImageMeta', 'maxPersonality',
- 'maxPersonalitySize',
- 'maxServerMeta', 'maxTotalCores',
- 'maxTotalFloatingIps', 'maxSecurityGroups',
- 'maxSecurityGroupRules', 'maxTotalInstances',
- 'maxTotalKeypairs', 'maxTotalRAMSize',
- 'maxServerGroups', 'maxServerGroupMembers',
- 'totalCoresUsed', 'totalFloatingIpsUsed',
- 'totalSecurityGroupsUsed', 'totalInstancesUsed',
- 'totalRAMUsed', 'totalServerGroupsUsed']
- # check whether all expected elements exist
- missing_elements =\
- [ele for ele in expected_elements if ele not in absolute_limits]
- self.assertEmpty(missing_elements,
- "Failed to find element %s in absolute limits list"
- % ', '.join(ele for ele in missing_elements))
+ # To check if all limits are present in the response (will be checked
+ # by schema)
+ self.client.show_limits()
+
+
+class AbsoluteLimitsV257TestJSON(base.BaseV2ComputeTest):
+ min_microversion = '2.57'
+ max_microversion = 'latest'
+
+ # NOTE(felipemonteiro): This class tests the Absolute Limits APIs
+ # response schema for the 2.57 microversion.
diff --git a/tempest/api/compute/limits/test_absolute_limits_negative.py b/tempest/api/compute/limits/test_absolute_limits_negative.py
index bef4eb5..500638a 100644
--- a/tempest/api/compute/limits/test_absolute_limits_negative.py
+++ b/tempest/api/compute/limits/test_absolute_limits_negative.py
@@ -33,15 +33,15 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('215cd465-d8ae-49c9-bf33-9c911913a5c8')
- def test_max_image_meta_exceed_limit(self):
- # We should not create vm with image meta over maxImageMeta limit
+ def test_max_metadata_exceed_limit(self):
+ # We should not create vm with metadata over maxServerMeta limit
# Get max limit value
limits = self.client.show_limits()['limits']
- max_meta = limits['absolute']['maxImageMeta']
+ max_meta = limits['absolute']['maxServerMeta']
# No point in running this test if there is no limit.
if max_meta == -1:
- raise self.skipException('no limit for maxImageMeta')
+ raise self.skipException('no limit for maxServerMeta')
# Create server should fail, since we are passing > metadata Limit!
max_meta_data = max_meta + 1
diff --git a/tempest/api/compute/servers/test_create_server.py b/tempest/api/compute/servers/test_create_server.py
index 122c4f5..4f0dbad 100644
--- a/tempest/api/compute/servers/test_create_server.py
+++ b/tempest/api/compute/servers/test_create_server.py
@@ -80,7 +80,7 @@
self.assertEqual("", self.server['image'])
else:
self.assertEqual(self.image_ref, self.server['image']['id'])
- self.assertEqual(self.flavor_ref, self.server['flavor']['id'])
+ self.assert_flavor_equal(self.flavor_ref, self.server['flavor'])
self.assertEqual(self.meta, self.server['metadata'])
@decorators.attr(type='smoke')
diff --git a/tempest/api/compute/servers/test_device_tagging.py b/tempest/api/compute/servers/test_device_tagging.py
index 5d9bf48..40681cb 100644
--- a/tempest/api/compute/servers/test_device_tagging.py
+++ b/tempest/api/compute/servers/test_device_tagging.py
@@ -382,5 +382,8 @@
waiters.wait_for_interface_detach(self.interfaces_client,
server['id'],
interface['port_id'])
- self.verify_metadata_from_api(server, ssh_client,
- self.verify_empty_devices)
+ # FIXME(mriedem): The assertion that the tagged devices are removed
+ # from the metadata for the server is being skipped until bug 1775947
+ # is fixed.
+ # self.verify_metadata_from_api(server, ssh_client,
+ # self.verify_empty_devices)
diff --git a/tempest/api/compute/servers/test_novnc.py b/tempest/api/compute/servers/test_novnc.py
index 1dfd0f9..5801db1 100644
--- a/tempest/api/compute/servers/test_novnc.py
+++ b/tempest/api/compute/servers/test_novnc.py
@@ -44,10 +44,13 @@
self._websocket = None
def tearDown(self):
- self.server_check_teardown()
super(NoVNCConsoleTestJSON, self).tearDown()
if self._websocket is not None:
self._websocket.close()
+ # NOTE(zhufl): Because server_check_teardown will raise Exception
+ # which will prevent other cleanup steps from being executed, so
+ # server_check_teardown should be called after super's tearDown.
+ self.server_check_teardown()
@classmethod
def setup_clients(cls):
@@ -58,6 +61,9 @@
def resource_setup(cls):
super(NoVNCConsoleTestJSON, cls).resource_setup()
cls.server = cls.create_test_server(wait_until="ACTIVE")
+ cls.use_get_remote_console = False
+ if not cls.is_requested_microversion_compatible('2.5'):
+ cls.use_get_remote_console = True
def _validate_novnc_html(self, vnc_url):
"""Verify we can connect to novnc and get back the javascript."""
@@ -170,8 +176,13 @@
@decorators.idempotent_id('c640fdff-8ab4-45a4-a5d8-7e6146cbd0dc')
def test_novnc(self):
- body = self.client.get_vnc_console(self.server['id'],
- type='novnc')['console']
+ if self.use_get_remote_console:
+ body = self.client.get_remote_console(
+ self.server['id'], console_type='novnc',
+ protocol='vnc')['remote_console']
+ else:
+ body = self.client.get_vnc_console(self.server['id'],
+ type='novnc')['console']
self.assertEqual('novnc', body['type'])
# Do the initial HTTP Request to novncproxy to get the NoVNC JavaScript
self._validate_novnc_html(body['url'])
@@ -184,8 +195,13 @@
@decorators.idempotent_id('f9c79937-addc-4aaa-9e0e-841eef02aeb7')
def test_novnc_bad_token(self):
- body = self.client.get_vnc_console(self.server['id'],
- type='novnc')['console']
+ if self.use_get_remote_console:
+ body = self.client.get_remote_console(
+ self.server['id'], console_type='novnc',
+ protocol='vnc')['remote_console']
+ else:
+ body = self.client.get_vnc_console(self.server['id'],
+ type='novnc')['console']
self.assertEqual('novnc', body['type'])
# Do the WebSockify HTTP Request to novncproxy with a bad token
url = body['url'].replace('token=', 'token=bad')
diff --git a/tempest/api/compute/servers/test_server_actions.py b/tempest/api/compute/servers/test_server_actions.py
index 350e8ba..f6494b5 100644
--- a/tempest/api/compute/servers/test_server_actions.py
+++ b/tempest/api/compute/servers/test_server_actions.py
@@ -59,8 +59,11 @@
self.server_id, validatable=True)
def tearDown(self):
- self.server_check_teardown()
super(ServerActionsTestJSON, self).tearDown()
+ # NOTE(zhufl): Because server_check_teardown will raise Exception
+ # which will prevent other cleanup steps from being executed, so
+ # server_check_teardown should be called after super's tearDown.
+ self.server_check_teardown()
@classmethod
def setup_credentials(cls):
@@ -197,7 +200,7 @@
self.assertEqual(self.server_id, rebuilt_server['id'])
rebuilt_image_id = rebuilt_server['image']['id']
self.assertTrue(self.image_ref_alt.endswith(rebuilt_image_id))
- self.assertEqual(self.flavor_ref, rebuilt_server['flavor']['id'])
+ self.assert_flavor_equal(self.flavor_ref, rebuilt_server['flavor'])
# Verify the server properties after the rebuild completes
waiters.wait_for_server_status(self.client,
@@ -251,7 +254,7 @@
self.assertEqual(self.server_id, rebuilt_server['id'])
rebuilt_image_id = rebuilt_server['image']['id']
self.assertEqual(new_image, rebuilt_image_id)
- self.assertEqual(self.flavor_ref, rebuilt_server['flavor']['id'])
+ self.assert_flavor_equal(self.flavor_ref, rebuilt_server['flavor'])
# Verify the server properties after the rebuild completes
waiters.wait_for_server_status(self.client,
@@ -303,7 +306,7 @@
expected_status)
server = self.client.show_server(server_id)['server']
- self.assertEqual(self.flavor_ref_alt, server['flavor']['id'])
+ self.assert_flavor_equal(self.flavor_ref_alt, server['flavor'])
if stop:
# NOTE(mriedem): tearDown requires the server to be started.
@@ -367,7 +370,7 @@
waiters.wait_for_server_status(self.client, self.server_id, 'ACTIVE')
server = self.client.show_server(self.server_id)['server']
- self.assertEqual(self.flavor_ref, server['flavor']['id'])
+ self.assert_flavor_equal(self.flavor_ref, server['flavor'])
@decorators.idempotent_id('fbbf075f-a812-4022-bc5c-ccb8047eef12')
@decorators.related_bug('1737599')
@@ -527,10 +530,10 @@
def _get_output(self):
output = self.client.get_console_output(
- self.server_id, length=10)['output']
+ self.server_id, length=3)['output']
self.assertTrue(output, "Console output was empty.")
lines = len(output.split('\n'))
- self.assertEqual(lines, 10)
+ self.assertEqual(lines, 3)
@decorators.idempotent_id('4b8867e6-fffa-4d54-b1d1-6fdda57be2f3')
@testtools.skipUnless(CONF.compute_feature_enabled.console_output,
@@ -561,8 +564,8 @@
# NOTE: This test tries to get full length console log, and the
# length should be bigger than the one of test_get_console_output.
- self.assertGreater(lines, 10, "Cannot get enough console log "
- "length. (lines: %s)" % lines)
+ self.assertGreater(lines, 3, "Cannot get enough console log "
+ "length. (lines: %s)" % lines)
self.wait_for(_check_full_length_console_log)
@@ -690,8 +693,13 @@
# Get the VNC console of type 'novnc' and 'xvpvnc'
console_types = ['novnc', 'xvpvnc']
for console_type in console_types:
- body = self.client.get_vnc_console(self.server_id,
- type=console_type)['console']
+ if self.is_requested_microversion_compatible('2.5'):
+ body = self.client.get_vnc_console(
+ self.server_id, type=console_type)['console']
+ else:
+ body = self.client.get_remote_console(
+ self.server_id, console_type=console_type,
+ protocol='vnc')['remote_console']
self.assertEqual(console_type, body['type'])
self.assertNotEqual('', body['url'])
self._validate_url(body['url'])
diff --git a/tempest/api/compute/servers/test_server_group.py b/tempest/api/compute/servers/test_server_group.py
index 5286c8f..1b7cb96 100644
--- a/tempest/api/compute/servers/test_server_group.py
+++ b/tempest/api/compute/servers/test_server_group.py
@@ -47,8 +47,16 @@
super(ServerGroupTestJSON, cls).resource_setup()
cls.policy = ['affinity']
- cls.created_server_group = cls.create_test_server_group(
- policy=cls.policy)
+ def setUp(self):
+ super(ServerGroupTestJSON, self).setUp()
+ # TODO(zhufl): After microversion 2.13 project_id and user_id are
+ # added to the body of server_group, and microversion is not used
+ # in resource_setup for now, so we should create server group in setUp
+ # in order to use the same microversion as in testcases till
+ # microversion support in resource_setup is fulfilled.
+ if not hasattr(self, 'created_server_group'):
+ self.__class__.created_server_group = \
+ self.create_test_server_group(policy=self.policy)
def _create_server_group(self, name, policy):
# create the test server-group with given policy
diff --git a/tempest/api/compute/servers/test_servers_negative.py b/tempest/api/compute/servers/test_servers_negative.py
index e944c28..0c1c05c 100644
--- a/tempest/api/compute/servers/test_servers_negative.py
+++ b/tempest/api/compute/servers/test_servers_negative.py
@@ -40,8 +40,11 @@
self.__class__.server_id = self.recreate_server(self.server_id)
def tearDown(self):
- self.server_check_teardown()
super(ServersNegativeTestJSON, self).tearDown()
+ # NOTE(zhufl): Because server_check_teardown will raise Exception
+ # which will prevent other cleanup steps from being executed, so
+ # server_check_teardown should be called after super's tearDown.
+ self.server_check_teardown()
@classmethod
def setup_clients(cls):
diff --git a/tempest/api/compute/test_networks.py b/tempest/api/compute/test_networks.py
index b8c79d7..76131e2 100644
--- a/tempest/api/compute/test_networks.py
+++ b/tempest/api/compute/test_networks.py
@@ -20,6 +20,8 @@
class ComputeNetworksTest(base.BaseV2ComputeTest):
+ max_microversion = '2.35'
+
@classmethod
def skip_checks(cls):
super(ComputeNetworksTest, cls).skip_checks()
diff --git a/tempest/api/identity/admin/v3/test_endpoints.py b/tempest/api/identity/admin/v3/test_endpoints.py
index 874aaa4..2cd8906 100644
--- a/tempest/api/identity/admin/v3/test_endpoints.py
+++ b/tempest/api/identity/admin/v3/test_endpoints.py
@@ -20,6 +20,10 @@
class EndPointsTestJSON(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 setup_clients(cls):
diff --git a/tempest/api/identity/admin/v3/test_endpoints_negative.py b/tempest/api/identity/admin/v3/test_endpoints_negative.py
index d54e222..4c3eb1c 100644
--- a/tempest/api/identity/admin/v3/test_endpoints_negative.py
+++ b/tempest/api/identity/admin/v3/test_endpoints_negative.py
@@ -1,4 +1,3 @@
-
# Copyright 2013 IBM Corp.
# All Rights Reserved.
#
@@ -21,6 +20,10 @@
class EndpointsNegativeTestJSON(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 setup_clients(cls):
diff --git a/tempest/api/identity/admin/v3/test_list_projects.py b/tempest/api/identity/admin/v3/test_list_projects.py
index 82664e8..148b368 100644
--- a/tempest/api/identity/admin/v3/test_list_projects.py
+++ b/tempest/api/identity/admin/v3/test_list_projects.py
@@ -18,7 +18,19 @@
from tempest.lib import decorators
-class ListProjectsTestJSON(base.BaseIdentityV3AdminTest):
+class BaseListProjectsTestJSON(base.BaseIdentityV3AdminTest):
+
+ def _list_projects_with_params(self, included, excluded, params, key):
+ # Validate that projects in ``included`` belongs to the projects
+ # returned that match ``params`` but not projects in ``excluded``
+ body = self.projects_client.list_projects(params)['projects']
+ for p in included:
+ self.assertIn(p[key], map(lambda x: x[key], body))
+ for p in excluded:
+ self.assertNotIn(p[key], map(lambda x: x[key], body))
+
+
+class ListProjectsTestJSON(BaseListProjectsTestJSON):
@classmethod
def resource_setup(cls):
@@ -61,17 +73,20 @@
def test_list_projects_with_domains(self):
# List projects with domain
self._list_projects_with_params(
- {'domain_id': self.domain['id']}, 'domain_id')
+ [self.p1], [self.p2, self.p3], {'domain_id': self.domain['id']},
+ 'domain_id')
@decorators.idempotent_id('0fe7a334-675a-4509-b00e-1c4b95d5dae8')
def test_list_projects_with_enabled(self):
# List the projects with enabled
- self._list_projects_with_params({'enabled': False}, 'enabled')
+ self._list_projects_with_params(
+ [self.p1], [self.p2, self.p3], {'enabled': False}, 'enabled')
@decorators.idempotent_id('fa178524-4e6d-4925-907c-7ab9f42c7e26')
def test_list_projects_with_name(self):
# List projects with name
- self._list_projects_with_params({'name': self.p1_name}, 'name')
+ self._list_projects_with_params(
+ [self.p1], [self.p2, self.p3], {'name': self.p1_name}, 'name')
@decorators.idempotent_id('6edc66f5-2941-4a17-9526-4073311c1fac')
def test_list_projects_with_parent(self):
@@ -82,8 +97,3 @@
self.assertNotEmpty(fetched_projects)
for project in fetched_projects:
self.assertEqual(self.p3['parent_id'], project['parent_id'])
-
- def _list_projects_with_params(self, params, key):
- body = self.projects_client.list_projects(params)['projects']
- self.assertIn(self.p1[key], map(lambda x: x[key], body))
- self.assertNotIn(self.p2[key], map(lambda x: x[key], body))
diff --git a/tempest/api/network/admin/test_external_network_extension.py b/tempest/api/network/admin/test_external_network_extension.py
index 49a9cdb..7e8cc8e 100644
--- a/tempest/api/network/admin/test_external_network_extension.py
+++ b/tempest/api/network/admin/test_external_network_extension.py
@@ -13,6 +13,7 @@
import testtools
from tempest.api.network import base
+from tempest.common import utils
from tempest import config
from tempest.lib.common.utils import data_utils
from tempest.lib.common.utils import test_utils
@@ -117,8 +118,15 @@
self.addCleanup(test_utils.call_and_ignore_notfound_exc,
self.admin_floating_ips_client.delete_floatingip,
created_floating_ip['id'])
- floatingip_list = self.admin_floating_ips_client.list_floatingips(
- network=external_network['id'])
+ if utils.is_extension_enabled('filter-validation', 'network'):
+ floatingip_list = self.admin_floating_ips_client.list_floatingips(
+ floating_network_id=external_network['id'])
+ else:
+ # NOTE(hongbin): This is for testing the backward-compatibility
+ # of neutron API although the parameter is a wrong filter
+ # for listing floating IPs.
+ floatingip_list = self.admin_floating_ips_client.list_floatingips(
+ invalid_filter=external_network['id'])
self.assertIn(created_floating_ip['id'],
(f['id'] for f in floatingip_list['floatingips']))
self.admin_networks_client.delete_network(external_network['id'])
diff --git a/tempest/api/volume/admin/test_volume_quotas.py b/tempest/api/volume/admin/test_volume_quotas.py
index e546bff..61a6df4 100644
--- a/tempest/api/volume/admin/test_volume_quotas.py
+++ b/tempest/api/volume/admin/test_volume_quotas.py
@@ -38,8 +38,8 @@
@classmethod
def setup_clients(cls):
super(BaseVolumeQuotasAdminTestJSON, cls).setup_clients()
- cls.transfer_client = cls.os_primary.volume_transfers_v2_client
- cls.alt_transfer_client = cls.os_alt.volume_transfers_v2_client
+ cls.transfer_client = cls.os_primary.volume_transfers_client_latest
+ cls.alt_transfer_client = cls.os_alt.volume_transfers_client_latest
@classmethod
def resource_setup(cls):
diff --git a/tempest/api/volume/admin/test_volume_services_negative.py b/tempest/api/volume/admin/test_volume_services_negative.py
index 6f3dbc6..3a863a1 100644
--- a/tempest/api/volume/admin/test_volume_services_negative.py
+++ b/tempest/api/volume/admin/test_volume_services_negative.py
@@ -23,10 +23,9 @@
@classmethod
def resource_setup(cls):
super(VolumeServicesNegativeTest, cls).resource_setup()
- cls.services = cls.admin_volume_services_client.list_services()[
- 'services']
- cls.host = cls.services[0]['host']
- cls.binary = cls.services[0]['binary']
+ services = cls.admin_volume_services_client.list_services()['services']
+ cls.host = services[0]['host']
+ cls.binary = services[0]['binary']
@decorators.attr(type='negative')
@decorators.idempotent_id('3246ce65-ba70-4159-aa3b-082c28e4b484')
diff --git a/tempest/api/volume/base.py b/tempest/api/volume/base.py
index 81fd6e6..64fe29a 100644
--- a/tempest/api/volume/base.py
+++ b/tempest/api/volume/base.py
@@ -74,24 +74,19 @@
if CONF.service_available.glance:
cls.images_client = cls.os_primary.image_client_v2
- if cls._api_version == 3:
- cls.backups_client = cls.os_primary.backups_v3_client
- cls.volumes_client = cls.os_primary.volumes_v3_client
- cls.messages_client = cls.os_primary.volume_v3_messages_client
- cls.versions_client = cls.os_primary.volume_v3_versions_client
- cls.groups_client = cls.os_primary.groups_v3_client
- cls.group_snapshots_client = (
- cls.os_primary.group_snapshots_v3_client)
- else:
- cls.backups_client = cls.os_primary.backups_v2_client
- cls.volumes_client = cls.os_primary.volumes_v2_client
-
- cls.snapshots_client = cls.os_primary.snapshots_v2_client
+ cls.backups_client = cls.os_primary.backups_client_latest
+ cls.volumes_client = cls.os_primary.volumes_client_latest
+ cls.messages_client = cls.os_primary.volume_messages_client_latest
+ cls.versions_client = cls.os_primary.volume_versions_client_latest
+ cls.groups_client = cls.os_primary.groups_client_latest
+ cls.group_snapshots_client = (
+ cls.os_primary.group_snapshots_client_latest)
+ cls.snapshots_client = cls.os_primary.snapshots_client_latest
cls.volumes_extension_client =\
- cls.os_primary.volumes_v2_extension_client
+ cls.os_primary.volumes_extension_client_latest
cls.availability_zone_client = (
- cls.os_primary.volume_v2_availability_zone_client)
- cls.volume_limits_client = cls.os_primary.volume_v2_limits_client
+ cls.os_primary.volume_availability_zone_client_latest)
+ cls.volume_limits_client = cls.os_primary.volume_limits_client_latest
def setUp(self):
super(BaseVolumeTest, self).setUp()
@@ -247,34 +242,34 @@
def setup_clients(cls):
super(BaseVolumeAdminTest, cls).setup_clients()
- cls.admin_volume_qos_client = cls.os_admin.volume_qos_v2_client
+ cls.admin_volume_qos_client = cls.os_admin.volume_qos_client_latest
cls.admin_volume_services_client = \
- cls.os_admin.volume_services_v2_client
- cls.admin_volume_types_client = cls.os_admin.volume_types_v2_client
- cls.admin_volume_manage_client = cls.os_admin.volume_manage_v2_client
- cls.admin_volume_client = cls.os_admin.volumes_v2_client
- if cls._api_version == 3:
- cls.admin_volume_client = cls.os_admin.volumes_v3_client
- cls.admin_groups_client = cls.os_admin.groups_v3_client
- cls.admin_messages_client = cls.os_admin.volume_v3_messages_client
- cls.admin_group_snapshots_client = \
- cls.os_admin.group_snapshots_v3_client
- cls.admin_group_types_client = cls.os_admin.group_types_v3_client
- cls.admin_hosts_client = cls.os_admin.volume_hosts_v2_client
+ cls.os_admin.volume_services_client_latest
+ cls.admin_volume_types_client = cls.os_admin.volume_types_client_latest
+ cls.admin_volume_manage_client = (
+ cls.os_admin.volume_manage_client_latest)
+ cls.admin_volume_client = cls.os_admin.volumes_client_latest
+ cls.admin_groups_client = cls.os_admin.groups_client_latest
+ cls.admin_messages_client = cls.os_admin.volume_messages_client_latest
+ cls.admin_group_snapshots_client = \
+ cls.os_admin.group_snapshots_client_latest
+ cls.admin_group_types_client = cls.os_admin.group_types_client_latest
+ cls.admin_hosts_client = cls.os_admin.volume_hosts_client_latest
cls.admin_snapshot_manage_client = \
- cls.os_admin.snapshot_manage_v2_client
- cls.admin_snapshots_client = cls.os_admin.snapshots_v2_client
- cls.admin_backups_client = cls.os_admin.backups_v2_client
+ cls.os_admin.snapshot_manage_client_latest
+ cls.admin_snapshots_client = cls.os_admin.snapshots_client_latest
+ cls.admin_backups_client = cls.os_admin.backups_client_latest
cls.admin_encryption_types_client = \
- cls.os_admin.encryption_types_v2_client
+ cls.os_admin.encryption_types_client_latest
cls.admin_quota_classes_client = \
- cls.os_admin.volume_quota_classes_v2_client
- cls.admin_quotas_client = cls.os_admin.volume_quotas_v2_client
- cls.admin_volume_limits_client = cls.os_admin.volume_v2_limits_client
+ cls.os_admin.volume_quota_classes_client_latest
+ cls.admin_quotas_client = cls.os_admin.volume_quotas_client_latest
+ cls.admin_volume_limits_client = (
+ cls.os_admin.volume_limits_client_latest)
cls.admin_capabilities_client = \
- cls.os_admin.volume_capabilities_v2_client
+ cls.os_admin.volume_capabilities_client_latest
cls.admin_scheduler_stats_client = \
- cls.os_admin.volume_scheduler_stats_v2_client
+ cls.os_admin.volume_scheduler_stats_client_latest
@classmethod
def create_test_qos_specs(cls, name=None, consumer=None, **kwargs):
diff --git a/tempest/api/volume/test_volume_transfers.py b/tempest/api/volume/test_volume_transfers.py
index 75e81b7..c85e0bc 100644
--- a/tempest/api/volume/test_volume_transfers.py
+++ b/tempest/api/volume/test_volume_transfers.py
@@ -27,10 +27,10 @@
def setup_clients(cls):
super(VolumesTransfersTest, cls).setup_clients()
- cls.client = cls.os_primary.volume_transfers_v2_client
- cls.alt_client = cls.os_alt.volume_transfers_v2_client
- cls.alt_volumes_client = cls.os_alt.volumes_v2_client
- cls.adm_volumes_client = cls.os_admin.volumes_v2_client
+ cls.client = cls.os_primary.volume_transfers_client_latest
+ cls.alt_client = cls.os_alt.volume_transfers_client_latest
+ cls.alt_volumes_client = cls.os_alt.volumes_client_latest
+ cls.adm_volumes_client = cls.os_admin.volumes_client_latest
@decorators.idempotent_id('4d75b645-a478-48b1-97c8-503f64242f1a')
def test_create_get_list_accept_volume_transfer(self):
diff --git a/tempest/api/volume/test_volumes_extend.py b/tempest/api/volume/test_volumes_extend.py
index 5d339c4..ac9a9c7 100644
--- a/tempest/api/volume/test_volumes_extend.py
+++ b/tempest/api/volume/test_volumes_extend.py
@@ -33,7 +33,7 @@
def test_volume_extend(self):
# Extend Volume Test.
volume = self.create_volume(image_ref=self.image_ref)
- extend_size = volume['size'] + 1
+ extend_size = volume['size'] * 2
self.volumes_client.extend_volume(volume['id'],
new_size=extend_size)
waiters.wait_for_volume_resource_status(self.volumes_client,
@@ -48,7 +48,7 @@
volume = self.create_volume()
self.create_snapshot(volume['id'])
- extend_size = volume['size'] + 1
+ extend_size = volume['size'] * 2
self.volumes_client.extend_volume(volume['id'], new_size=extend_size)
waiters.wait_for_volume_resource_status(self.volumes_client,
diff --git a/tempest/api/volume/test_volumes_negative.py b/tempest/api/volume/test_volumes_negative.py
index f139283..866bd87 100644
--- a/tempest/api/volume/test_volumes_negative.py
+++ b/tempest/api/volume/test_volumes_negative.py
@@ -103,21 +103,24 @@
def test_create_volume_with_nonexistent_volume_type(self):
# Should not be able to create volume with non-existent volume type
self.assertRaises(lib_exc.NotFound, self.volumes_client.create_volume,
- size='1', volume_type=data_utils.rand_uuid())
+ size=CONF.volume.volume_size,
+ volume_type=data_utils.rand_uuid())
@decorators.attr(type=['negative'])
@decorators.idempotent_id('0c36f6ae-4604-4017-b0a9-34fdc63096f9')
def test_create_volume_with_nonexistent_snapshot_id(self):
# Should not be able to create volume with non-existent snapshot
self.assertRaises(lib_exc.NotFound, self.volumes_client.create_volume,
- size='1', snapshot_id=data_utils.rand_uuid())
+ size=CONF.volume.volume_size,
+ snapshot_id=data_utils.rand_uuid())
@decorators.attr(type=['negative'])
@decorators.idempotent_id('47c73e08-4be8-45bb-bfdf-0c4e79b88344')
def test_create_volume_with_nonexistent_source_volid(self):
# Should not be able to create volume with non-existent source volume
self.assertRaises(lib_exc.NotFound, self.volumes_client.create_volume,
- size='1', source_volid=data_utils.rand_uuid())
+ size=CONF.volume.volume_size,
+ source_volid=data_utils.rand_uuid())
@decorators.attr(type=['negative'])
@decorators.idempotent_id('0186422c-999a-480e-a026-6a665744c30c')
diff --git a/tempest/api/volume/test_volumes_snapshots_negative.py b/tempest/api/volume/test_volumes_snapshots_negative.py
index ea5f036..0453c0a 100644
--- a/tempest/api/volume/test_volumes_snapshots_negative.py
+++ b/tempest/api/volume/test_volumes_snapshots_negative.py
@@ -50,7 +50,7 @@
@decorators.idempotent_id('677863d1-34f9-456d-b6ac-9924f667a7f4')
def test_volume_from_snapshot_decreasing_size(self):
# Creates a volume a snapshot passing a size different from the source
- src_size = CONF.volume.volume_size + 1
+ src_size = CONF.volume.volume_size * 2
src_vol = self.create_volume(size=src_size)
src_snap = self.create_snapshot(src_vol['id'])
@@ -58,7 +58,7 @@
# Destination volume smaller than source
self.assertRaises(lib_exc.BadRequest,
self.volumes_client.create_volume,
- size=src_size - 1,
+ size=CONF.volume.volume_size,
snapshot_id=src_snap['id'])
@decorators.attr(type=['negative'])
diff --git a/tempest/clients.py b/tempest/clients.py
index 2a07be9..4f2846e 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -241,6 +241,47 @@
# if only api_v3 is enabled, all these clients should be available
if (CONF.volume_feature_enabled.api_v2 or
CONF.volume_feature_enabled.api_v3):
+ self.backups_client_latest = self.volume_v3.BackupsClient()
+ self.encryption_types_client_latest = \
+ self.volume_v3.EncryptionTypesClient()
+ self.snapshot_manage_client_latest = \
+ self.volume_v3.SnapshotManageClient()
+ self.snapshots_client_latest = self.volume_v3.SnapshotsClient()
+ self.volume_capabilities_client_latest = \
+ self.volume_v3.CapabilitiesClient()
+ self.volume_manage_client_latest = (
+ self.volume_v3.VolumeManageClient())
+ self.volume_qos_client_latest = self.volume_v3.QosSpecsClient()
+ self.volume_services_client_latest = (
+ self.volume_v3.ServicesClient())
+ self.volume_types_client_latest = self.volume_v3.TypesClient()
+ self.volume_hosts_client_latest = self.volume_v3.HostsClient()
+ self.volume_quotas_client_latest = self.volume_v3.QuotasClient()
+ self.volume_quota_classes_client_latest = \
+ self.volume_v3.QuotaClassesClient()
+ self.volume_scheduler_stats_client_latest = \
+ self.volume_v3.SchedulerStatsClient()
+ self.volume_transfers_client_latest = \
+ self.volume_v3.TransfersClient()
+ self.volume_availability_zone_client_latest = \
+ self.volume_v3.AvailabilityZoneClient()
+ self.volume_limits_client_latest = self.volume_v3.LimitsClient()
+ self.volumes_client_latest = self.volume_v3.VolumesClient()
+ self.volumes_extension_client_latest = \
+ self.volume_v3.ExtensionsClient()
+ self.group_types_client_latest = self.volume_v3.GroupTypesClient()
+ self.groups_client_latest = self.volume_v3.GroupsClient()
+ self.group_snapshots_client_latest = \
+ self.volume_v3.GroupSnapshotsClient()
+ self.volume_messages_client_latest = (
+ self.volume_v3.MessagesClient())
+ self.volume_versions_client_latest = (
+ self.volume_v3.VersionsClient())
+
+ # TODO(gmann): Below alias for service clients have been
+ # deprecated and will be removed in future. Start using the alias
+ # defined above with suffix _latest.
+ # ****************Deprecated alias start from here***************
self.backups_v2_client = self.volume_v3.BackupsClient()
self.encryption_types_v2_client = \
self.volume_v3.EncryptionTypesClient()
@@ -268,11 +309,6 @@
self.volumes_v2_extension_client = \
self.volume_v3.ExtensionsClient()
- # Set default client for users that don't need explicit version
- self.volumes_client_latest = self.volumes_v2_client
- self.snapshots_client_latest = self.snapshots_v2_client
- self.backups_client_latest = self.backups_v2_client
-
if CONF.volume_feature_enabled.api_v3:
self.backups_v3_client = self.volume_v3.BackupsClient()
self.group_types_v3_client = self.volume_v3.GroupTypesClient()
@@ -283,11 +319,7 @@
self.volume_v3_messages_client = self.volume_v3.MessagesClient()
self.volume_v3_versions_client = self.volume_v3.VersionsClient()
self.volumes_v3_client = self.volume_v3.VolumesClient()
-
- # Set default client for users that don't need explicit version
- self.volumes_client_latest = self.volumes_v3_client
- self.snapshots_client_latest = self.snapshots_v3_client
- self.backups_client_latest = self.backups_v3_client
+ # ****************Deprecated alias end here***********************
def _set_object_storage_clients(self):
self.account_client = self.object_storage.AccountClient()
diff --git a/tempest/cmd/cleanup_service.py b/tempest/cmd/cleanup_service.py
index 27e1bc1..83cf42c 100644
--- a/tempest/cmd/cleanup_service.py
+++ b/tempest/cmd/cleanup_service.py
@@ -315,7 +315,7 @@
class VolumeQuotaService(BaseService):
def __init__(self, manager, **kwargs):
super(VolumeQuotaService, self).__init__(kwargs)
- self.client = manager.volume_quotas_v2_client
+ self.client = manager.volume_quotas_client_latest
def delete(self):
client = self.client
diff --git a/tempest/common/compute.py b/tempest/common/compute.py
index 68c4a10..f2730b3 100644
--- a/tempest/common/compute.py
+++ b/tempest/common/compute.py
@@ -165,7 +165,7 @@
if volume_backed:
volume_name = data_utils.rand_name(__name__ + '-volume')
- volumes_client = clients.volumes_v2_client
+ volumes_client = clients.volumes_client_latest
params = {'name': volume_name,
'imageRef': image_id,
'size': CONF.volume.volume_size}
diff --git a/tempest/hacking/checks.py b/tempest/hacking/checks.py
index a57a360..a72675e 100644
--- a/tempest/hacking/checks.py
+++ b/tempest/hacking/checks.py
@@ -34,6 +34,9 @@
METHOD_DELETE_RESOURCE = re.compile(r"^\s*def delete_.+")
CLASS = re.compile(r"^class .+")
EX_ATTRIBUTE = re.compile(r'(\s+|\()(e|ex|exc|exception).message(\s+|\))')
+NEGATIVE_TEST_DECORATOR = re.compile(
+ r'\s*@decorators\.attr\(type=.*negative.*\)')
+_HAVE_NEGATIVE_DECORATOR = False
def import_no_clients_in_api_and_scenario_tests(physical_line, filename):
@@ -306,6 +309,29 @@
yield(0, msg)
+def negative_test_attribute_always_applied_to_negative_tests(physical_line,
+ filename):
+ """Check ``@decorators.attr(type=['negative'])`` applied to negative tests.
+
+ T117
+ """
+ global _HAVE_NEGATIVE_DECORATOR
+
+ if re.match(r'.\/tempest\/api\/.*_negative.*', filename):
+
+ if NEGATIVE_TEST_DECORATOR.match(physical_line):
+ _HAVE_NEGATIVE_DECORATOR = True
+ return
+
+ if TEST_DEFINITION.match(physical_line):
+ if not _HAVE_NEGATIVE_DECORATOR:
+ return (
+ 0, "T117: Must apply `@decorators.attr(type=['negative'])`"
+ " to all negative API tests"
+ )
+ _HAVE_NEGATIVE_DECORATOR = False
+
+
def factory(register):
register(import_no_clients_in_api_and_scenario_tests)
register(scenario_tests_need_service_tags)
@@ -322,3 +348,4 @@
register(use_rand_uuid_instead_of_uuid4)
register(dont_put_admin_tests_on_nonadmin_path)
register(unsupported_exception_attribute_PY3)
+ register(negative_test_attribute_always_applied_to_negative_tests)
diff --git a/tempest/lib/api_schema/response/compute/v2_1/flavors.py b/tempest/lib/api_schema/response/compute/v2_1/flavors.py
index 43e80cc..bd5e3d6 100644
--- a/tempest/lib/api_schema/response/compute/v2_1/flavors.py
+++ b/tempest/lib/api_schema/response/compute/v2_1/flavors.py
@@ -60,7 +60,7 @@
},
'additionalProperties': False,
# 'OS-FLV-DISABLED', 'os-flavor-access', 'rxtx_factor' and
- # 'OS-FLV-EXT-DATA' are API extensions. So they are not 'required'.
+ # 'OS-FLV-EXT-DATA' are API extensions, so they are not 'required'.
'required': ['name', 'links', 'ram', 'vcpus', 'swap', 'disk', 'id']
}
@@ -74,7 +74,7 @@
'items': common_flavor_info
},
# NOTE(gmann): flavors_links attribute is not necessary
- # to be present always So it is not 'required'.
+ # to be present always so it is not 'required'.
'flavors_links': parameter_types.links
},
'additionalProperties': False,
diff --git a/tempest/lib/api_schema/response/compute/v2_1/server_groups.py b/tempest/lib/api_schema/response/compute/v2_1/server_groups.py
new file mode 100644
index 0000000..01db20b
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_1/server_groups.py
@@ -0,0 +1,65 @@
+# Copyright 2017 NTT Corporation. All rights reserved.
+#
+# 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.
+
+common_server_group = {
+ 'type': 'object',
+ 'properties': {
+ 'id': {'type': 'string'},
+ 'name': {'type': 'string'},
+ 'policies': {
+ 'type': 'array',
+ 'items': {'type': 'string'}
+ },
+ # 'members' attribute contains the array of instance's UUID of
+ # instances present in server group
+ 'members': {
+ 'type': 'array',
+ 'items': {'type': 'string'}
+ },
+ 'metadata': {'type': 'object'}
+ },
+ 'additionalProperties': False,
+ 'required': ['id', 'name', 'policies', 'members', 'metadata']
+}
+
+create_show_server_group = {
+ 'status_code': [200],
+ 'response_body': {
+ 'type': 'object',
+ 'properties': {
+ 'server_group': common_server_group
+ },
+ 'additionalProperties': False,
+ 'required': ['server_group']
+ }
+}
+
+delete_server_group = {
+ 'status_code': [204]
+}
+
+list_server_groups = {
+ 'status_code': [200],
+ 'response_body': {
+ 'type': 'object',
+ 'properties': {
+ 'server_groups': {
+ 'type': 'array',
+ 'items': common_server_group
+ }
+ },
+ 'additionalProperties': False,
+ 'required': ['server_groups']
+ }
+}
diff --git a/tempest/lib/api_schema/response/compute/v2_1/servers.py b/tempest/lib/api_schema/response/compute/v2_1/servers.py
index 2954de0..3300298 100644
--- a/tempest/lib/api_schema/response/compute/v2_1/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_1/servers.py
@@ -345,58 +345,6 @@
}
}
-common_server_group = {
- 'type': 'object',
- 'properties': {
- 'id': {'type': 'string'},
- 'name': {'type': 'string'},
- 'policies': {
- 'type': 'array',
- 'items': {'type': 'string'}
- },
- # 'members' attribute contains the array of instance's UUID of
- # instances present in server group
- 'members': {
- 'type': 'array',
- 'items': {'type': 'string'}
- },
- 'metadata': {'type': 'object'}
- },
- 'additionalProperties': False,
- 'required': ['id', 'name', 'policies', 'members', 'metadata']
-}
-
-create_show_server_group = {
- 'status_code': [200],
- 'response_body': {
- 'type': 'object',
- 'properties': {
- 'server_group': common_server_group
- },
- 'additionalProperties': False,
- 'required': ['server_group']
- }
-}
-
-delete_server_group = {
- 'status_code': [204]
-}
-
-list_server_groups = {
- 'status_code': [200],
- 'response_body': {
- 'type': 'object',
- 'properties': {
- 'server_groups': {
- 'type': 'array',
- 'items': common_server_group
- }
- },
- 'additionalProperties': False,
- 'required': ['server_groups']
- }
-}
-
instance_actions = {
'type': 'object',
'properties': {
@@ -430,8 +378,9 @@
'traceback': {'type': ['string', 'null']}
},
'additionalProperties': False,
- 'required': ['event', 'start_time', 'finish_time', 'result',
- 'traceback']
+ # NOTE(zhufl): events.traceback can only be seen by admin users
+ # with default policy.json, so it shouldn't be a required field.
+ 'required': ['event', 'start_time', 'finish_time', 'result']
}
}
diff --git a/tempest/lib/api_schema/response/compute/v2_13/servers.py b/tempest/lib/api_schema/response/compute/v2_13/server_groups.py
similarity index 74%
rename from tempest/lib/api_schema/response/compute/v2_13/servers.py
rename to tempest/lib/api_schema/response/compute/v2_13/server_groups.py
index a90f3e4..2b59e38 100644
--- a/tempest/lib/api_schema/response/compute/v2_13/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_13/server_groups.py
@@ -14,21 +14,22 @@
import copy
-from tempest.lib.api_schema.response.compute.v2_1 import servers
+from tempest.lib.api_schema.response.compute.v2_1 import server_groups
-common_server_group = copy.deepcopy(servers.common_server_group)
+common_server_group = copy.deepcopy(server_groups.common_server_group)
common_server_group['properties']['project_id'] = {'type': 'string'}
common_server_group['properties']['user_id'] = {'type': 'string'}
common_server_group['required'].append('project_id')
common_server_group['required'].append('user_id')
-create_show_server_group = copy.deepcopy(servers.create_show_server_group)
+create_show_server_group = copy.deepcopy(
+ server_groups.create_show_server_group)
create_show_server_group['response_body']['properties'][
'server_group'] = common_server_group
-delete_server_group = copy.deepcopy(servers.delete_server_group)
+delete_server_group = copy.deepcopy(server_groups.delete_server_group)
-list_server_groups = copy.deepcopy(servers.list_server_groups)
+list_server_groups = copy.deepcopy(server_groups.list_server_groups)
list_server_groups['response_body']['properties']['server_groups'][
'items'] = common_server_group
diff --git a/tempest/lib/api_schema/response/compute/v2_16/servers.py b/tempest/lib/api_schema/response/compute/v2_16/servers.py
index 3eb658f..fb1a2fc 100644
--- a/tempest/lib/api_schema/response/compute/v2_16/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_16/servers.py
@@ -157,4 +157,14 @@
}
}
+# NOTE(gmann): 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.
+# ****** Schemas unchanged since microversion 2.9 ******
list_servers = copy.deepcopy(servers.list_servers)
+update_server = copy.deepcopy(servers.update_server)
+rebuild_server = copy.deepcopy(servers.rebuild_server)
+rebuild_server_with_admin_pass = copy.deepcopy(
+ servers.rebuild_server_with_admin_pass)
+show_server_diagnostics = copy.deepcopy(servers.show_server_diagnostics)
+get_remote_consoles = copy.deepcopy(servers.get_remote_consoles)
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 fd9e933..e3e8ad1 100644
--- a/tempest/lib/api_schema/response/compute/v2_19/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_19/servers.py
@@ -16,10 +16,10 @@
from tempest.lib.api_schema.response.compute.v2_16 import servers \
as serversv216
-from tempest.lib.api_schema.response.compute.v2_9 import servers as serversv29
-list_servers = copy.deepcopy(serversv216.list_servers)
-
+# Compute microversion 2.19:
+# 1. New attributes in 'server' dict.
+# 'description'
get_server = copy.deepcopy(serversv216.get_server)
get_server['response_body']['properties']['server'][
'properties'].update({'description': {'type': ['string', 'null']}})
@@ -32,21 +32,29 @@
list_servers_detail['response_body']['properties']['servers']['items'][
'required'].append('description')
-update_server = copy.deepcopy(serversv29.update_server)
+update_server = copy.deepcopy(serversv216.update_server)
update_server['response_body']['properties']['server'][
'properties'].update({'description': {'type': ['string', 'null']}})
update_server['response_body']['properties']['server'][
'required'].append('description')
-rebuild_server = copy.deepcopy(serversv29.rebuild_server)
+rebuild_server = copy.deepcopy(serversv216.rebuild_server)
rebuild_server['response_body']['properties']['server'][
'properties'].update({'description': {'type': ['string', 'null']}})
rebuild_server['response_body']['properties']['server'][
'required'].append('description')
rebuild_server_with_admin_pass = copy.deepcopy(
- serversv29.rebuild_server_with_admin_pass)
+ serversv216.rebuild_server_with_admin_pass)
rebuild_server_with_admin_pass['response_body']['properties']['server'][
'properties'].update({'description': {'type': ['string', 'null']}})
rebuild_server_with_admin_pass['response_body']['properties']['server'][
'required'].append('description')
+
+# NOTE(gmann): 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.
+# ****** Schemas unchanged since microversion 2.16 ******
+list_servers = copy.deepcopy(serversv216.list_servers)
+show_server_diagnostics = copy.deepcopy(serversv216.show_server_diagnostics)
+get_remote_consoles = copy.deepcopy(serversv216.get_remote_consoles)
diff --git a/tempest/lib/api_schema/response/compute/v2_26/servers.py b/tempest/lib/api_schema/response/compute/v2_26/servers.py
index 5c35eab..8e62dc3 100644
--- a/tempest/lib/api_schema/response/compute/v2_26/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_26/servers.py
@@ -15,7 +15,6 @@
import copy
-from tempest.lib.api_schema.response.compute.v2_1 import servers as servers21
from tempest.lib.api_schema.response.compute.v2_19 import servers as servers219
# The 2.26 microversion changes the server GET and (detailed) LIST responses to
@@ -62,10 +61,6 @@
rebuild_server_with_admin_pass['response_body']['properties']['server'][
'required'].append('tags')
-# list response schema wasn't changed for v2.26 so use v2.1
-
-list_servers = copy.deepcopy(servers21.list_servers)
-
list_tags = {
'status_code': [200],
'response_body': {
@@ -98,3 +93,11 @@
}
delete_tag = {'status_code': [204]}
+
+# NOTE(gmann): 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.
+# ****** Schemas unchanged since microversion 2.19 ******
+list_servers = copy.deepcopy(servers219.list_servers)
+show_server_diagnostics = copy.deepcopy(servers219.show_server_diagnostics)
+get_remote_consoles = copy.deepcopy(servers219.get_remote_consoles)
diff --git a/tempest/lib/api_schema/response/compute/v2_3/servers.py b/tempest/lib/api_schema/response/compute/v2_3/servers.py
index f24103e..969df3b 100644
--- a/tempest/lib/api_schema/response/compute/v2_3/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_3/servers.py
@@ -163,4 +163,13 @@
}
}
+# NOTE: 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.
+# ****** Schemas unchanged since microversion 2.1 ***
list_servers = copy.deepcopy(servers.list_servers)
+update_server = copy.deepcopy(servers.update_server)
+rebuild_server = copy.deepcopy(servers.rebuild_server)
+rebuild_server_with_admin_pass = copy.deepcopy(
+ servers.rebuild_server_with_admin_pass)
+show_server_diagnostics = copy.deepcopy(servers.show_server_diagnostics)
diff --git a/tempest/tests/lib/services/volume/v2/__init__.py b/tempest/lib/api_schema/response/compute/v2_36/__init__.py
similarity index 100%
rename from tempest/tests/lib/services/volume/v2/__init__.py
rename to tempest/lib/api_schema/response/compute/v2_36/__init__.py
diff --git a/tempest/lib/api_schema/response/compute/v2_36/limits.py b/tempest/lib/api_schema/response/compute/v2_36/limits.py
new file mode 100644
index 0000000..8e94690
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_36/limits.py
@@ -0,0 +1,35 @@
+# Copyright 2018 ZTE Corporation. All rights reserved.
+#
+# 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 copy
+
+from tempest.lib.api_schema.response.compute.v2_1 import limits as limitv21
+
+# Compute microversion 2.36:
+# remove attributes in get_limit:
+# 'maxSecurityGroupRules',
+# 'maxSecurityGroups',
+# 'maxTotalFloatingIps',
+# 'totalFloatingIpsUsed',
+# 'totalSecurityGroupsUsed'
+
+get_limit = copy.deepcopy(limitv21.get_limit)
+
+for item in ['maxSecurityGroupRules', 'maxSecurityGroups',
+ 'maxTotalFloatingIps', 'totalFloatingIpsUsed',
+ 'totalSecurityGroupsUsed']:
+ get_limit['response_body']['properties']['limits']['properties'][
+ 'absolute']['properties'].pop(item)
+ get_limit['response_body']['properties']['limits']['properties'][
+ 'absolute']['required'].remove(item)
diff --git a/tempest/tests/lib/services/volume/v2/__init__.py b/tempest/lib/api_schema/response/compute/v2_39/__init__.py
similarity index 100%
copy from tempest/tests/lib/services/volume/v2/__init__.py
copy to tempest/lib/api_schema/response/compute/v2_39/__init__.py
diff --git a/tempest/lib/api_schema/response/compute/v2_39/limits.py b/tempest/lib/api_schema/response/compute/v2_39/limits.py
new file mode 100644
index 0000000..3df6616
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_39/limits.py
@@ -0,0 +1,29 @@
+# Copyright 2018 ZTE Corporation. All rights reserved.
+#
+# 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 copy
+
+from tempest.lib.api_schema.response.compute.v2_36 import limits as limitv236
+
+# Compute microversion 2.39:
+# remove attributes in get_limit:
+# 'maxImageMeta'
+
+get_limit = copy.deepcopy(limitv236.get_limit)
+
+get_limit['response_body']['properties']['limits']['properties']['absolute'][
+ 'properties'].pop('maxImageMeta')
+
+get_limit['response_body']['properties']['limits']['properties']['absolute'][
+ 'required'].remove('maxImageMeta')
diff --git a/tempest/lib/api_schema/response/compute/v2_47/servers.py b/tempest/lib/api_schema/response/compute/v2_47/servers.py
index 5d6d4c3..5922f76 100644
--- a/tempest/lib/api_schema/response/compute/v2_47/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_47/servers.py
@@ -53,3 +53,14 @@
servers226.rebuild_server_with_admin_pass)
rebuild_server_with_admin_pass['response_body']['properties']['server'][
'properties'].update({'flavor': flavor})
+
+# NOTE(zhufl): 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.
+# ****** Schemas unchanged since microversion 2.26 ***
+list_tags = copy.deepcopy(servers226.list_tags)
+update_all_tags = copy.deepcopy(servers226.update_all_tags)
+delete_all_tags = copy.deepcopy(servers226.delete_all_tags)
+check_tag_existence = copy.deepcopy(servers226.check_tag_existence)
+update_tag = copy.deepcopy(servers226.update_tag)
+delete_tag = copy.deepcopy(servers226.delete_tag)
diff --git a/tempest/lib/api_schema/response/compute/v2_48/servers.py b/tempest/lib/api_schema/response/compute/v2_48/servers.py
index 5904758..02b00d6 100644
--- a/tempest/lib/api_schema/response/compute/v2_48/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_48/servers.py
@@ -113,3 +113,14 @@
}
get_server = copy.deepcopy(servers247.get_server)
+
+# NOTE(zhufl): 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.
+# ****** Schemas unchanged since microversion 2.26 ***
+list_tags = copy.deepcopy(servers247.list_tags)
+update_all_tags = copy.deepcopy(servers247.update_all_tags)
+delete_all_tags = copy.deepcopy(servers247.delete_all_tags)
+check_tag_existence = copy.deepcopy(servers247.check_tag_existence)
+update_tag = copy.deepcopy(servers247.update_tag)
+delete_tag = copy.deepcopy(servers247.delete_tag)
diff --git a/tempest/lib/api_schema/response/compute/v2_54/servers.py b/tempest/lib/api_schema/response/compute/v2_54/servers.py
index c084696..e264186 100644
--- a/tempest/lib/api_schema/response/compute/v2_54/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_54/servers.py
@@ -39,11 +39,18 @@
rebuild_server_with_admin_pass['response_body']['properties']['server'][
'required'].append('key_name')
-# ****** Schemas unchanged in microversion 2.54 since microversion 2.47 ***
-
# NOTE(gmann): 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.
+# ****** Schemas unchanged in microversion 2.54 since microversion 2.47 ***
get_server = copy.deepcopy(servers247.get_server)
list_servers_detail = copy.deepcopy(servers247.list_servers_detail)
update_server = copy.deepcopy(servers247.update_server)
+
+# ****** Schemas unchanged since microversion 2.26 ***
+list_tags = copy.deepcopy(servers247.list_tags)
+update_all_tags = copy.deepcopy(servers247.update_all_tags)
+delete_all_tags = copy.deepcopy(servers247.delete_all_tags)
+check_tag_existence = copy.deepcopy(servers247.check_tag_existence)
+update_tag = copy.deepcopy(servers247.update_tag)
+delete_tag = copy.deepcopy(servers247.delete_tag)
diff --git a/tempest/lib/api_schema/response/compute/v2_55/flavors.py b/tempest/lib/api_schema/response/compute/v2_55/flavors.py
index 823190a..554f43b 100644
--- a/tempest/lib/api_schema/response/compute/v2_55/flavors.py
+++ b/tempest/lib/api_schema/response/compute/v2_55/flavors.py
@@ -11,7 +11,9 @@
# 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 copy
+from tempest.lib.api_schema.response.compute.v2_1 import flavors as flavorsv21
from tempest.lib.api_schema.response.compute.v2_1 import parameter_types
# Note(gmann): This is schema for microversion 2.55 which includes the
@@ -110,3 +112,9 @@
'required': ['flavor']
}
}
+
+# Note(zhufl): 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.
+# ****** Schemas unchanged since microversion 2.1 ***
+delete_flavor = copy.deepcopy(flavorsv21.delete_flavor)
diff --git a/tempest/lib/api_schema/response/compute/v2_57/limits.py b/tempest/lib/api_schema/response/compute/v2_57/limits.py
new file mode 100644
index 0000000..dcb8b3d
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_57/limits.py
@@ -0,0 +1,30 @@
+# Copyright 2018 ZTE Corporation. All rights reserved.
+#
+# 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 copy
+
+from tempest.lib.api_schema.response.compute.v2_39 import limits as limitv239
+
+# Compute microversion 2.57:
+# remove attributes in get_limit:
+# 'maxPersonality',
+# 'maxPersonalitySize'
+
+get_limit = copy.deepcopy(limitv239.get_limit)
+
+for item in ['maxPersonality', 'maxPersonalitySize']:
+ get_limit['response_body']['properties']['limits']['properties'][
+ 'absolute']['properties'].pop(item)
+ get_limit['response_body']['properties']['limits']['properties'][
+ 'absolute']['required'].remove(item)
diff --git a/tempest/lib/api_schema/response/compute/v2_57/servers.py b/tempest/lib/api_schema/response/compute/v2_57/servers.py
index ed1ca7d..d7de5fd 100644
--- a/tempest/lib/api_schema/response/compute/v2_57/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_57/servers.py
@@ -43,11 +43,18 @@
rebuild_server_with_admin_pass['response_body']['properties']['server'][
'required'].append('user_data')
-# ****** Schemas unchanged in microversion 2.57 since microversion 2.54 ***
-
# NOTE(gmann): Below are the unchanged schema in this microversion. We need
-# to keeo this schema in this file to have the generic way to select the
+# 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.
+# ****** Schemas unchanged in microversion 2.57 since microversion 2.54 ***
get_server = copy.deepcopy(servers254.get_server)
list_servers_detail = copy.deepcopy(servers254.list_servers_detail)
update_server = copy.deepcopy(servers254.update_server)
+
+# ****** Schemas unchanged since microversion 2.26 ***
+list_tags = copy.deepcopy(servers254.list_tags)
+update_all_tags = copy.deepcopy(servers254.update_all_tags)
+delete_all_tags = copy.deepcopy(servers254.delete_all_tags)
+check_tag_existence = copy.deepcopy(servers254.check_tag_existence)
+update_tag = copy.deepcopy(servers254.update_tag)
+delete_tag = copy.deepcopy(servers254.delete_tag)
diff --git a/tempest/lib/api_schema/response/compute/v2_6/servers.py b/tempest/lib/api_schema/response/compute/v2_6/servers.py
index 29b3e86..d5774de 100644
--- a/tempest/lib/api_schema/response/compute/v2_6/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_6/servers.py
@@ -16,9 +16,18 @@
from tempest.lib.api_schema.response.compute.v2_3 import servers
+# NOTE: 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.
+# ****** Schemas unchanged since microversion 2.3 ******
list_servers = copy.deepcopy(servers.list_servers)
get_server = copy.deepcopy(servers.get_server)
list_servers_detail = copy.deepcopy(servers.list_servers_detail)
+update_server = copy.deepcopy(servers.update_server)
+rebuild_server = copy.deepcopy(servers.rebuild_server)
+rebuild_server_with_admin_pass = copy.deepcopy(
+ servers.rebuild_server_with_admin_pass)
+show_server_diagnostics = copy.deepcopy(servers.show_server_diagnostics)
# NOTE: The consolidated remote console API got introduced with v2.6
# with bp/consolidate-console-api. See Nova commit 578bafeda
@@ -31,7 +40,7 @@
'type': 'object',
'properties': {
'protocol': {'enum': ['vnc', 'rdp', 'serial', 'spice']},
- 'type': {'enum': ['novnc', 'xpvnc', 'rdp-html5',
+ 'type': {'enum': ['novnc', 'xvpvnc', 'rdp-html5',
'spice-html5', 'serial']},
'url': {
'type': 'string',
diff --git a/tempest/lib/api_schema/response/compute/v2_61/flavors.py b/tempest/lib/api_schema/response/compute/v2_61/flavors.py
index 381fb64..5119466 100644
--- a/tempest/lib/api_schema/response/compute/v2_61/flavors.py
+++ b/tempest/lib/api_schema/response/compute/v2_61/flavors.py
@@ -99,4 +99,8 @@
# Note(gmann): 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.
+# ****** Schemas unchanged since microversion 2.55 ***
list_flavors = copy.deepcopy(flavorsv255.list_flavors)
+
+# ****** Schemas unchanged since microversion 2.1 ***
+delete_flavor = copy.deepcopy(flavorsv255.delete_flavor)
diff --git a/tempest/lib/api_schema/response/compute/v2_63/servers.py b/tempest/lib/api_schema/response/compute/v2_63/servers.py
index 5cdaf54..6a20890 100644
--- a/tempest/lib/api_schema/response/compute/v2_63/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_63/servers.py
@@ -63,3 +63,14 @@
'properties'].update({'trusted_image_certificates': trusted_certs})
get_server['response_body']['properties']['server'][
'required'].append('trusted_image_certificates')
+
+# NOTE(zhufl): 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.
+# ****** Schemas unchanged since microversion 2.26 ***
+list_tags = copy.deepcopy(servers257.list_tags)
+update_all_tags = copy.deepcopy(servers257.update_all_tags)
+delete_all_tags = copy.deepcopy(servers257.delete_all_tags)
+check_tag_existence = copy.deepcopy(servers257.check_tag_existence)
+update_tag = copy.deepcopy(servers257.update_tag)
+delete_tag = copy.deepcopy(servers257.delete_tag)
diff --git a/tempest/lib/api_schema/response/compute/v2_9/servers.py b/tempest/lib/api_schema/response/compute/v2_9/servers.py
index 7df02d5..f412839 100644
--- a/tempest/lib/api_schema/response/compute/v2_9/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_9/servers.py
@@ -17,7 +17,13 @@
from tempest.lib.api_schema.response.compute.v2_1 import servers as servers_21
from tempest.lib.api_schema.response.compute.v2_6 import servers
+# NOTE: 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.
+# ****** Schemas unchanged since microversion 2.6 ******
list_servers = copy.deepcopy(servers.list_servers)
+show_server_diagnostics = copy.deepcopy(servers.show_server_diagnostics)
+get_remote_consoles = copy.deepcopy(servers.get_remote_consoles)
get_server = copy.deepcopy(servers.get_server)
get_server['response_body']['properties']['server'][
diff --git a/tempest/lib/cli/base.py b/tempest/lib/cli/base.py
index 3fb56ec..d8c776b 100644
--- a/tempest/lib/cli/base.py
+++ b/tempest/lib/cli/base.py
@@ -269,7 +269,7 @@
:param merge_stderr: if True the stderr buffer is merged into stdout
:type merge_stderr: boolean
"""
- flags += ' --endpoint-type %s' % endpoint_type
+ flags += ' --os-endpoint-type %s' % endpoint_type
return self.cmd_with_auth(
'cinder', action, flags, params, fail_ok, merge_stderr)
diff --git a/tempest/lib/common/rest_client.py b/tempest/lib/common/rest_client.py
index 22276d4..bc9cfe2 100644
--- a/tempest/lib/common/rest_client.py
+++ b/tempest/lib/common/rest_client.py
@@ -416,6 +416,8 @@
resp_body=None, extra=None):
if 'X-Auth-Token' in req_headers:
req_headers['X-Auth-Token'] = '<omitted>'
+ if 'X-Subject-Token' in req_headers:
+ req_headers['X-Subject-Token'] = '<omitted>'
# A shallow copy is sufficient
resp_log = resp.copy()
if 'x-subject-token' in resp_log:
diff --git a/tempest/lib/decorators.py b/tempest/lib/decorators.py
index e99dd24..b399aa0 100644
--- a/tempest/lib/decorators.py
+++ b/tempest/lib/decorators.py
@@ -19,39 +19,83 @@
import six
import testtools
+from tempest.lib import exceptions as lib_exc
+
LOG = logging.getLogger(__name__)
+_SUPPORTED_BUG_TYPES = {
+ 'launchpad': 'https://launchpad.net/bugs/%s',
+ 'storyboard': 'https://storyboard.openstack.org/#!/story/%s',
+}
+
+
+def _validate_bug_and_bug_type(bug, bug_type):
+ """Validates ``bug`` and ``bug_type`` values.
+
+ :param bug: bug number causing the test to skip (launchpad or storyboard)
+ :param bug_type: 'launchpad' or 'storyboard', default 'launchpad'
+ :raises: InvalidParam if ``bug`` is not a digit or ``bug_type`` is not
+ a valid value
+ """
+ if not bug.isdigit():
+ invalid_param = '%s must be a valid %s number' % (bug, bug_type)
+ raise lib_exc.InvalidParam(invalid_param=invalid_param)
+ if bug_type not in _SUPPORTED_BUG_TYPES:
+ invalid_param = 'bug_type "%s" must be one of: %s' % (
+ bug_type, ', '.join(_SUPPORTED_BUG_TYPES.keys()))
+ raise lib_exc.InvalidParam(invalid_param=invalid_param)
+
+
+def _get_bug_url(bug, bug_type='launchpad'):
+ """Get the bug URL based on the ``bug_type`` and ``bug``
+
+ :param bug: The launchpad/storyboard bug number causing the test
+ :param bug_type: 'launchpad' or 'storyboard', default 'launchpad'
+ :returns: Bug URL corresponding to ``bug_type`` value
+ """
+ _validate_bug_and_bug_type(bug, bug_type)
+ return _SUPPORTED_BUG_TYPES[bug_type] % bug
+
def skip_because(*args, **kwargs):
"""A decorator useful to skip tests hitting known bugs
- @param bug: bug number causing the test to skip
- @param condition: optional condition to be True for the skip to have place
+ ``bug`` must be a number and ``condition`` must be true 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'
+ :param condition: optional condition to be True for the skip to have place
+ :raises: testtools.TestCase.skipException if ``condition`` is True and
+ ``bug`` is included
"""
def decorator(f):
@functools.wraps(f)
def wrapper(*func_args, **func_kwargs):
skip = False
+ msg = ''
if "condition" in kwargs:
if kwargs["condition"] is True:
skip = True
else:
skip = True
if "bug" in kwargs and skip is True:
- if not kwargs['bug'].isdigit():
- raise ValueError('bug must be a valid bug number')
- msg = "Skipped until Bug: %s is resolved." % kwargs["bug"]
+ bug = kwargs['bug']
+ bug_type = kwargs.get('bug_type', 'launchpad')
+ bug_url = _get_bug_url(bug, bug_type)
+ msg = "Skipped until bug: %s is resolved." % bug_url
raise testtools.TestCase.skipException(msg)
return f(*func_args, **func_kwargs)
return wrapper
return decorator
-def related_bug(bug, status_code=None):
- """A decorator useful to know solutions from launchpad bug reports
+def related_bug(bug, status_code=None, bug_type='launchpad'):
+ """A decorator useful to know solutions from launchpad/storyboard reports
- @param bug: The launchpad bug number causing the test
- @param status_code: The status code related to the bug report
+ :param bug: The launchpad/storyboard bug number causing the test bug
+ :param bug_type: 'launchpad' or 'storyboard', default 'launchpad'
+ :param status_code: The status code related to the bug report
"""
def decorator(f):
@functools.wraps(f)
@@ -61,9 +105,10 @@
except Exception as exc:
exc_status_code = getattr(exc, 'status_code', None)
if status_code is None or status_code == exc_status_code:
- LOG.error('Hints: This test was made for the bug %s. '
- 'The failure could be related to '
- 'https://launchpad.net/bugs/%s', bug, bug)
+ if bug:
+ LOG.error('Hints: This test was made for the bug_type '
+ '%s. The failure could be related to '
+ '%s', bug, _get_bug_url(bug, bug_type))
raise exc
return wrapper
return decorator
diff --git a/tempest/lib/services/clients.py b/tempest/lib/services/clients.py
index 8918a8c..833cfd6 100644
--- a/tempest/lib/services/clients.py
+++ b/tempest/lib/services/clients.py
@@ -331,7 +331,7 @@
self.region = region
# Check if passed or default credentials are valid
if not self.credentials.is_valid():
- raise exceptions.InvalidCredentials()
+ raise exceptions.InvalidCredentials(credentials)
# Get the identity classes matching the provided credentials
# TODO(andreaf) Define a new interface in Credentials to get
# the API version from an instance
@@ -340,7 +340,9 @@
isinstance(self.credentials, auth.IDENTITY_VERSION[k][0])]
# Zero matches or more than one are both not valid.
if len(identity) != 1:
- raise exceptions.InvalidCredentials()
+ msg = "Zero or %d ambiguous auth provider found. identity: %s, " \
+ "credentials: %s" % (len(identity), identity, credentials)
+ raise exceptions.InvalidCredentials(msg)
self.auth_version, auth_provider_class = identity[0]
self.dscv = disable_ssl_certificate_validation
self.ca_certs = ca_certs
diff --git a/tempest/lib/services/compute/limits_client.py b/tempest/lib/services/compute/limits_client.py
index efe9889..9af80c4 100644
--- a/tempest/lib/services/compute/limits_client.py
+++ b/tempest/lib/services/compute/limits_client.py
@@ -15,15 +15,25 @@
from oslo_serialization import jsonutils as json
-from tempest.lib.api_schema.response.compute.v2_1 import limits as schema
+from tempest.lib.api_schema.response.compute.v2_1 import limits as schemav21
+from tempest.lib.api_schema.response.compute.v2_36 import limits as schemav236
+from tempest.lib.api_schema.response.compute.v2_39 import limits as schemav239
+from tempest.lib.api_schema.response.compute.v2_57 import limits as schemav257
from tempest.lib.common import rest_client
from tempest.lib.services.compute import base_compute_client
class LimitsClient(base_compute_client.BaseComputeClient):
+ schema_versions_info = [
+ {'min': None, 'max': '2.35', 'schema': schemav21},
+ {'min': '2.36', 'max': '2.38', 'schema': schemav236},
+ {'min': '2.39', 'max': '2.56', 'schema': schemav239},
+ {'min': '2.57', 'max': None, 'schema': schemav257}]
+
def show_limits(self):
resp, body = self.get("limits")
body = json.loads(body)
+ schema = self.get_schema(self.schema_versions_info)
self.validate_response(schema.get_limit, resp, body)
return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/compute/server_groups_client.py b/tempest/lib/services/compute/server_groups_client.py
index 03cd645..0d440d5 100644
--- a/tempest/lib/services/compute/server_groups_client.py
+++ b/tempest/lib/services/compute/server_groups_client.py
@@ -16,8 +16,10 @@
from oslo_serialization import jsonutils as json
-from tempest.lib.api_schema.response.compute.v2_1 import servers as schema
-from tempest.lib.api_schema.response.compute.v2_13 import servers as schemav213
+from tempest.lib.api_schema.response.compute.v2_1 import server_groups \
+ as schema
+from tempest.lib.api_schema.response.compute.v2_13 import server_groups \
+ as schemav213
from tempest.lib.common import rest_client
from tempest.lib.services.compute import base_compute_client
diff --git a/tempest/lib/services/image/v2/images_client.py b/tempest/lib/services/image/v2/images_client.py
index ed6df47..3c38dba 100644
--- a/tempest/lib/services/image/v2/images_client.py
+++ b/tempest/lib/services/image/v2/images_client.py
@@ -32,7 +32,7 @@
For a full list of available parameters, please refer to the official
API reference:
- http://developer.openstack.org/api-ref/image/v2/index.html#update-an-image
+ https://developer.openstack.org/api-ref/image/v2/#update-image
"""
data = json.dumps(patch)
headers = {"Content-Type": "application/openstack-images-v2.0"
@@ -47,7 +47,7 @@
For a full list of available parameters, please refer to the official
API reference:
- http://developer.openstack.org/api-ref/image/v2/index.html#create-an-image
+ https://developer.openstack.org/api-ref/image/v2/#create-image
"""
data = json.dumps(kwargs)
resp, body = self.post('images', data)
@@ -84,7 +84,7 @@
For a full list of available parameters, please refer to the official
API reference:
- http://developer.openstack.org/api-ref/image/v2/#delete-an-image
+ https://developer.openstack.org/api-ref/image/v2/#delete-image
"""
url = 'images/%s' % image_id
resp, _ = self.delete(url)
@@ -96,7 +96,7 @@
For a full list of available parameters, please refer to the official
API reference:
- http://developer.openstack.org/api-ref/image/v2/#show-images
+ https://developer.openstack.org/api-ref/image/v2/#list-images
"""
url = 'images'
@@ -113,7 +113,7 @@
For a full list of available parameters, please refer to the official
API reference:
- http://developer.openstack.org/api-ref/image/v2/#show-image-details
+ https://developer.openstack.org/api-ref/image/v2/#show-image
"""
url = 'images/%s' % image_id
resp, body = self.get(url)
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index 145dcf1..cdc30b9 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -94,6 +94,10 @@
if not client:
client = self.ports_client
name = data_utils.rand_name(self.__class__.__name__)
+ if CONF.network.port_vnic_type and 'binding:vnic_type' not in kwargs:
+ kwargs['binding:vnic_type'] = CONF.network.port_vnic_type
+ if CONF.network.port_profile and 'binding:profile' not in kwargs:
+ kwargs['binding:profile'] = CONF.network.port_profile
result = client.create_port(
name=name,
network_id=network_id,
@@ -297,7 +301,7 @@
def create_volume_type(self, client=None, name=None, backend_name=None):
if not client:
- client = self.os_admin.volume_types_v2_client
+ client = self.os_admin.volume_types_client_latest
randomized_name = name or data_utils.rand_name(
'volume-type-' + self.__class__.__name__)
@@ -543,7 +547,7 @@
volume['id'], 'available')
def ping_ip_address(self, ip_address, should_succeed=True,
- ping_timeout=None, mtu=None):
+ ping_timeout=None, mtu=None, server=None):
timeout = ping_timeout or CONF.validation.ping_timeout
cmd = ['ping', '-c1', '-w1']
@@ -577,12 +581,16 @@
'caller': caller, 'ip': ip_address, 'timeout': timeout,
'result': 'expected' if result else 'unexpected'
})
+ if server:
+ self._log_console_output([server])
return result
def check_vm_connectivity(self, ip_address,
username=None,
private_key=None,
should_connect=True,
+ extra_msg="",
+ server=None,
mtu=None):
"""Check server connectivity
@@ -592,43 +600,36 @@
:param should_connect: True/False indicates positive/negative test
positive - attempt ping and ssh
negative - attempt ping and fail if succeed
+ :param extra_msg: Message to help with debugging if ``ping_ip_address``
+ fails
+ :param server: The server whose console to log for debugging
:param mtu: network MTU to use for connectivity validation
:raises: AssertError if the result of the connectivity check does
not match the value of the should_connect param
"""
+ LOG.debug('checking network connections to IP %s with user: %s',
+ ip_address, username)
if should_connect:
msg = "Timed out waiting for %s to become reachable" % ip_address
else:
msg = "ip address %s is reachable" % ip_address
+ if extra_msg:
+ msg = "%s\n%s" % (extra_msg, msg)
self.assertTrue(self.ping_ip_address(ip_address,
should_succeed=should_connect,
- mtu=mtu),
+ mtu=mtu, server=server),
msg=msg)
if should_connect:
# no need to check ssh for negative connectivity
- self.get_remote_client(ip_address, username, private_key)
-
- def check_public_network_connectivity(self, ip_address, username,
- private_key, should_connect=True,
- msg=None, servers=None, mtu=None):
- # The target login is assumed to have been configured for
- # key-based authentication by cloud-init.
- LOG.debug('checking network connections to IP %s with user: %s',
- ip_address, username)
- try:
- self.check_vm_connectivity(ip_address,
- username,
- private_key,
- should_connect=should_connect,
- mtu=mtu)
- except Exception:
- ex_msg = 'Public network connectivity check failed'
- if msg:
- ex_msg += ": " + msg
- LOG.exception(ex_msg)
- self._log_console_output(servers)
- raise
+ try:
+ self.get_remote_client(ip_address, username, private_key,
+ server=server)
+ except Exception:
+ if not extra_msg:
+ extra_msg = 'Failed to ssh to %s' % ip_address
+ LOG.exception(extra_msg)
+ raise
def create_floating_ip(self, thing, pool_name=None):
"""Create a floating IP and associates to a server on Nova"""
@@ -645,9 +646,10 @@
return floating_ip
def create_timestamp(self, ip_address, dev_name=None, mount_path='/mnt',
- private_key=None):
+ private_key=None, server=None):
ssh_client = self.get_remote_client(ip_address,
- private_key=private_key)
+ private_key=private_key,
+ server=server)
if dev_name is not None:
ssh_client.make_fs(dev_name)
ssh_client.exec_command('sudo mount /dev/%s %s' % (dev_name,
@@ -661,9 +663,10 @@
return timestamp
def get_timestamp(self, ip_address, dev_name=None, mount_path='/mnt',
- private_key=None):
+ private_key=None, server=None):
ssh_client = self.get_remote_client(ip_address,
- private_key=private_key)
+ private_key=private_key,
+ server=server)
if dev_name is not None:
ssh_client.mount(dev_name, mount_path)
timestamp = ssh_client.exec_command('sudo cat %s/timestamp'
@@ -809,8 +812,13 @@
return subnet
def _get_server_port_id_and_ip4(self, server, ip_addr=None):
- ports = self.os_admin.ports_client.list_ports(
- device_id=server['id'], fixed_ip=ip_addr)['ports']
+ if ip_addr:
+ ports = self.os_admin.ports_client.list_ports(
+ device_id=server['id'],
+ fixed_ips='ip_address=%s' % ip_addr)['ports']
+ else:
+ ports = self.os_admin.ports_client.list_ports(
+ device_id=server['id'])['ports']
# A port can have more than one IP address in some cases.
# If the network is dual-stack (IPv4 + IPv6), this port is associated
# with 2 subnets
@@ -1200,9 +1208,9 @@
@classmethod
def setup_clients(cls):
super(EncryptionScenarioTest, cls).setup_clients()
- cls.admin_volume_types_client = cls.os_admin.volume_types_v2_client
+ cls.admin_volume_types_client = cls.os_admin.volume_types_client_latest
cls.admin_encryption_types_client =\
- cls.os_admin.encryption_types_v2_client
+ cls.os_admin.encryption_types_client_latest
def create_encryption_type(self, client=None, type_id=None, provider=None,
key_size=None, cipher=None,
diff --git a/tempest/scenario/test_network_advanced_server_ops.py b/tempest/scenario/test_network_advanced_server_ops.py
index 87ce951..7452ee6 100644
--- a/tempest/scenario/test_network_advanced_server_ops.py
+++ b/tempest/scenario/test_network_advanced_server_ops.py
@@ -90,9 +90,10 @@
floating_ip_addr = floating_ip['floating_ip_address']
# Check FloatingIP status before checking the connectivity
self.check_floating_ip_status(floating_ip, 'ACTIVE')
- self.check_public_network_connectivity(floating_ip_addr, username,
- private_key, should_connect,
- servers=[server])
+ self.check_vm_connectivity(floating_ip_addr, username,
+ private_key, should_connect,
+ 'Public network connectivity check failed',
+ server)
def _wait_server_status_and_check_network_connectivity(self, server,
keypair,
@@ -196,7 +197,14 @@
'VERIFY_RESIZE')
self.servers_client.confirm_resize_server(server['id'])
server = self.servers_client.show_server(server['id'])['server']
- self.assertEqual(resize_flavor, server['flavor']['id'])
+ # Nova API > 2.46 no longer includes flavor.id, and schema check
+ # will cover whether 'id' should be in flavor
+ if server['flavor'].get('id'):
+ self.assertEqual(resize_flavor, server['flavor']['id'])
+ else:
+ flavor = self.flavors_client.show_flavor(resize_flavor)['flavor']
+ for key in ['original_name', 'ram', 'vcpus', 'disk']:
+ self.assertEqual(flavor[key], server['flavor'][key])
self._wait_server_status_and_check_network_connectivity(
server, keypair, floating_ip)
diff --git a/tempest/scenario/test_network_basic_ops.py b/tempest/scenario/test_network_basic_ops.py
index 8212e75..c1132cf 100644
--- a/tempest/scenario/test_network_basic_ops.py
+++ b/tempest/scenario/test_network_basic_ops.py
@@ -175,7 +175,7 @@
def _get_server_key(self, server):
return self.keypairs[server['key_name']]['private_key']
- def check_public_network_connectivity(
+ def _check_public_network_connectivity(
self, should_connect=True, msg=None,
should_check_floating_ip_status=True, mtu=None):
"""Verifies connectivty to a VM via public network and floating IP
@@ -199,13 +199,18 @@
if should_connect:
private_key = self._get_server_key(server)
floatingip_status = 'ACTIVE'
+
# Check FloatingIP Status before initiating a connection
if should_check_floating_ip_status:
self.check_floating_ip_status(floating_ip, floatingip_status)
- # call the common method in the parent class
- super(TestNetworkBasicOps, self).check_public_network_connectivity(
- ip_address, ssh_login, private_key, should_connect, msg,
- self.servers, mtu=mtu)
+
+ message = 'Public network connectivity check failed'
+ if msg:
+ message += '. Reason: %s' % msg
+
+ self.check_vm_connectivity(
+ ip_address, ssh_login, private_key, should_connect,
+ message, server, mtu=mtu)
def _disassociate_floating_ips(self):
floating_ip, _ = self.floating_ip_tuple
@@ -404,17 +409,17 @@
"""
self._setup_network_and_servers()
- self.check_public_network_connectivity(should_connect=True)
+ self._check_public_network_connectivity(should_connect=True)
self._check_network_internal_connectivity(network=self.network)
self._check_network_external_connectivity()
self._disassociate_floating_ips()
- self.check_public_network_connectivity(should_connect=False,
- msg="after disassociate "
- "floating ip")
+ self._check_public_network_connectivity(should_connect=False,
+ msg="after disassociate "
+ "floating ip")
self._reassociate_floating_ips()
- self.check_public_network_connectivity(should_connect=True,
- msg="after re-associate "
- "floating ip")
+ self._check_public_network_connectivity(should_connect=True,
+ msg="after re-associate "
+ "floating ip")
@decorators.idempotent_id('b158ea55-472e-4086-8fa9-c64ac0c6c1d0')
@testtools.skipUnless(utils.is_extension_enabled('net-mtu', 'network'),
@@ -425,10 +430,10 @@
"""Validate that network MTU sized frames fit through."""
self._setup_network_and_servers()
# first check that connectivity works in general for the instance
- self.check_public_network_connectivity(should_connect=True)
+ self._check_public_network_connectivity(should_connect=True)
# now that we checked general connectivity, test that full size frames
# can also pass between nodes
- self.check_public_network_connectivity(
+ self._check_public_network_connectivity(
should_connect=True, mtu=self.network['mtu'])
@decorators.idempotent_id('1546850e-fbaa-42f5-8b5f-03d8a6a95f15')
@@ -467,7 +472,7 @@
"""
self._setup_network_and_servers()
- self.check_public_network_connectivity(should_connect=True)
+ self._check_public_network_connectivity(should_connect=True)
self._check_network_internal_connectivity(network=self.network)
self._check_network_external_connectivity()
self._create_new_network(create_gateway=True)
@@ -502,7 +507,7 @@
"""
self._setup_network_and_servers()
- self.check_public_network_connectivity(should_connect=True)
+ self._check_public_network_connectivity(should_connect=True)
self._create_new_network()
self._hotplug_server()
self._check_network_internal_connectivity(network=self.new_net)
@@ -524,19 +529,19 @@
admin_state_up attribute of router to True
"""
self._setup_network_and_servers()
- self.check_public_network_connectivity(
+ self._check_public_network_connectivity(
should_connect=True, msg="before updating "
"admin_state_up of router to False")
self._update_router_admin_state(self.router, False)
# TODO(alokmaurya): Remove should_check_floating_ip_status=False check
# once bug 1396310 is fixed
- self.check_public_network_connectivity(
+ self._check_public_network_connectivity(
should_connect=False, msg="after updating "
"admin_state_up of router to False",
should_check_floating_ip_status=False)
self._update_router_admin_state(self.router, True)
- self.check_public_network_connectivity(
+ self._check_public_network_connectivity(
should_connect=True, msg="after updating "
"admin_state_up of router to True")
@@ -581,7 +586,7 @@
renew_timeout = CONF.network.build_timeout
self._setup_network_and_servers(dns_nameservers=[initial_dns_server])
- self.check_public_network_connectivity(should_connect=True)
+ self._check_public_network_connectivity(should_connect=True)
floating_ip, server = self.floating_ip_tuple
ip_address = floating_ip['floating_ip_address']
@@ -656,20 +661,20 @@
private_key=private_key,
server=server2)
- self.check_public_network_connectivity(
+ self._check_public_network_connectivity(
should_connect=True, msg="before updating "
"admin_state_up of instance port to False")
self.check_remote_connectivity(ssh_client, dest=server_pip,
should_succeed=True)
self.ports_client.update_port(port_id, admin_state_up=False)
- self.check_public_network_connectivity(
+ self._check_public_network_connectivity(
should_connect=False, msg="after updating "
"admin_state_up of instance port to False",
should_check_floating_ip_status=False)
self.check_remote_connectivity(ssh_client, dest=server_pip,
should_succeed=False)
self.ports_client.update_port(port_id, admin_state_up=True)
- self.check_public_network_connectivity(
+ self._check_public_network_connectivity(
should_connect=True, msg="after updating "
"admin_state_up of instance port to True")
self.check_remote_connectivity(ssh_client, dest=server_pip,
@@ -766,7 +771,7 @@
msg = "Rescheduling test does not apply to distributed routers."
raise self.skipException(msg)
- self.check_public_network_connectivity(should_connect=True)
+ self._check_public_network_connectivity(should_connect=True)
# remove resource from agents
hosting_agents = set(a["id"] for a in
@@ -783,7 +788,7 @@
'unscheduling router failed')
# verify resource is un-functional
- self.check_public_network_connectivity(
+ self._check_public_network_connectivity(
should_connect=False,
msg='after router unscheduling',
)
@@ -800,7 +805,7 @@
"target agent")
# verify resource is functional
- self.check_public_network_connectivity(
+ self._check_public_network_connectivity(
should_connect=True,
msg='After router rescheduling')
@@ -834,7 +839,7 @@
# Create server
self._setup_network_and_servers()
- self.check_public_network_connectivity(should_connect=True)
+ self._check_public_network_connectivity(should_connect=True)
self._create_new_network()
self._hotplug_server()
fip, server = self.floating_ip_tuple
diff --git a/tempest/scenario/test_security_groups_basic_ops.py b/tempest/scenario/test_security_groups_basic_ops.py
index e39afe0..f5805ef 100644
--- a/tempest/scenario/test_security_groups_basic_ops.py
+++ b/tempest/scenario/test_security_groups_basic_ops.py
@@ -369,7 +369,8 @@
self.floating_ips[tenant.access_point['id']]['floating_ip_address']
private_key = tenant.keypair['private_key']
access_point_ssh = self.get_remote_client(
- access_point_ssh, private_key=private_key)
+ access_point_ssh, private_key=private_key,
+ server=tenant.access_point)
return access_point_ssh
def _test_in_tenant_block(self, tenant):
diff --git a/tempest/scenario/test_shelve_instance.py b/tempest/scenario/test_shelve_instance.py
index 68f18d1..d6b6d14 100644
--- a/tempest/scenario/test_shelve_instance.py
+++ b/tempest/scenario/test_shelve_instance.py
@@ -63,7 +63,8 @@
instance_ip = self.get_server_ip(server)
timestamp = self.create_timestamp(instance_ip,
- private_key=keypair['private_key'])
+ private_key=keypair['private_key'],
+ server=server)
# Prevent bug #1257594 from coming back
# Unshelve used to boot the instance with the original image, not
@@ -71,7 +72,8 @@
self._shelve_then_unshelve_server(server)
timestamp2 = self.get_timestamp(instance_ip,
- private_key=keypair['private_key'])
+ private_key=keypair['private_key'],
+ server=server)
self.assertEqual(timestamp, timestamp2)
@decorators.attr(type='slow')
diff --git a/tempest/scenario/test_snapshot_pattern.py b/tempest/scenario/test_snapshot_pattern.py
index b51a781..a33d4d4 100644
--- a/tempest/scenario/test_snapshot_pattern.py
+++ b/tempest/scenario/test_snapshot_pattern.py
@@ -57,7 +57,8 @@
instance_ip = self.get_server_ip(server)
timestamp = self.create_timestamp(instance_ip,
- private_key=keypair['private_key'])
+ private_key=keypair['private_key'],
+ server=server)
# snapshot the instance
snapshot_image = self.create_server_snapshot(server=server)
@@ -71,5 +72,6 @@
# 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'])
+ private_key=keypair['private_key'],
+ server=server_from_snapshot)
self.assertEqual(timestamp, timestamp2)
diff --git a/tempest/scenario/test_stamp_pattern.py b/tempest/scenario/test_stamp_pattern.py
index ef369d6..2782119 100644
--- a/tempest/scenario/test_stamp_pattern.py
+++ b/tempest/scenario/test_stamp_pattern.py
@@ -96,7 +96,8 @@
keypair['private_key'])
timestamp = self.create_timestamp(ip_for_server,
CONF.compute.volume_device_name,
- private_key=keypair['private_key'])
+ private_key=keypair['private_key'],
+ server=server)
self.nova_volume_detach(server, volume)
# snapshot the volume
@@ -126,5 +127,6 @@
# check the existence of the timestamp file in the volume2
timestamp2 = self.get_timestamp(ip_for_snapshot,
CONF.compute.volume_device_name,
- private_key=keypair['private_key'])
+ private_key=keypair['private_key'],
+ server=server_from_snapshot)
self.assertEqual(timestamp, timestamp2)
diff --git a/tempest/scenario/test_volume_boot_pattern.py b/tempest/scenario/test_volume_boot_pattern.py
index 1564f25..79c2d14 100644
--- a/tempest/scenario/test_volume_boot_pattern.py
+++ b/tempest/scenario/test_volume_boot_pattern.py
@@ -108,7 +108,8 @@
LOG.info("Setting timestamp in instance %s", instance_1st)
ip_instance_1st = self.get_server_ip(instance_1st)
timestamp = self.create_timestamp(ip_instance_1st,
- private_key=keypair['private_key'])
+ private_key=keypair['private_key'],
+ server=instance_1st)
# delete instance
LOG.info("Deleting first instance: %s", instance_1st)
@@ -126,7 +127,8 @@
LOG.info("Getting timestamp in instance %s", instance_2nd)
ip_instance_2nd = self.get_server_ip(instance_2nd)
timestamp2 = self.get_timestamp(ip_instance_2nd,
- private_key=keypair['private_key'])
+ private_key=keypair['private_key'],
+ server=instance_2nd)
self.assertEqual(timestamp, timestamp2)
# snapshot a volume
@@ -150,7 +152,8 @@
server_from_snapshot)
server_from_snapshot_ip = self.get_server_ip(server_from_snapshot)
timestamp3 = self.get_timestamp(server_from_snapshot_ip,
- private_key=keypair['private_key'])
+ private_key=keypair['private_key'],
+ server=server_from_snapshot)
self.assertEqual(timestamp, timestamp3)
@decorators.idempotent_id('05795fb2-b2a7-4c9f-8fac-ff25aedb1489')
diff --git a/tempest/scenario/test_volume_migrate_attached.py b/tempest/scenario/test_volume_migrate_attached.py
index ff7996a..c54bb38 100644
--- a/tempest/scenario/test_volume_migrate_attached.py
+++ b/tempest/scenario/test_volume_migrate_attached.py
@@ -40,7 +40,7 @@
@classmethod
def setup_clients(cls):
super(TestVolumeMigrateRetypeAttached, cls).setup_clients()
- cls.admin_volumes_client = cls.os_admin.volumes_v2_client
+ cls.admin_volumes_client = cls.os_admin.volumes_client_latest
@classmethod
def skip_checks(cls):
@@ -114,7 +114,8 @@
LOG.info("Setting timestamp in instance %s", instance['id'])
ip_instance = self.get_server_ip(instance)
timestamp = self.create_timestamp(ip_instance,
- private_key=keypair['private_key'])
+ private_key=keypair['private_key'],
+ server=instance)
# retype volume with migration from backend #1 to backend #2
LOG.info("Retyping Volume %s to new type %s", volume_origin['id'],
@@ -125,5 +126,6 @@
LOG.info("Getting timestamp in postmigrated instance %s",
instance['id'])
timestamp2 = self.get_timestamp(ip_instance,
- private_key=keypair['private_key'])
+ private_key=keypair['private_key'],
+ server=instance)
self.assertEqual(timestamp, timestamp2)
diff --git a/tempest/tests/cmd/test_verify_tempest_config.py b/tempest/tests/cmd/test_verify_tempest_config.py
index 8641b63..32d6224 100644
--- a/tempest/tests/cmd/test_verify_tempest_config.py
+++ b/tempest/tests/cmd/test_verify_tempest_config.py
@@ -225,7 +225,7 @@
# This test verifies that wrong config api_v2 = True is detected
class FakeClient(object):
def get_versions(self):
- return (None, ['v1.0'])
+ return (None, ['v1.1'])
fake_os = mock.MagicMock()
fake_module = mock.MagicMock()
diff --git a/tempest/tests/lib/services/volume/v2/test_backups_client.py b/tempest/tests/lib/services/volume/v2/test_backups_client.py
deleted file mode 100644
index 14e5fb0..0000000
--- a/tempest/tests/lib/services/volume/v2/test_backups_client.py
+++ /dev/null
@@ -1,117 +0,0 @@
-# Copyright 2017 FiberHome Telecommunication Technologies CO.,LTD
-# All Rights Reserved.
-#
-# 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.
-
-from tempest.lib.services.volume.v2 import backups_client
-from tempest.tests.lib import fake_auth_provider
-from tempest.tests.lib.services import base
-
-
-class TestBackupsClient(base.BaseServiceTest):
-
- FAKE_BACKUP_LIST = {
- "backups": [
- {
- "id": "2ef47aee-8844-490c-804d-2a8efe561c65",
- "links": [
- {
- "href": "fake-url-1",
- "rel": "self"
- },
- {
- "href": "fake-url-2",
- "rel": "bookmark"
- }
- ],
- "name": "backup001"
- }
- ]
- }
-
- FAKE_BACKUP_LIST_WITH_DETAIL = {
- "backups": [
- {
- "availability_zone": "az1",
- "container": "volumebackups",
- "created_at": "2013-04-02T10:35:27.000000",
- "description": None,
- "fail_reason": None,
- "id": "2ef47aee-8844-490c-804d-2a8efe561c65",
- "links": [
- {
- "href": "fake-url-1",
- "rel": "self"
- },
- {
- "href": "fake-url-2",
- "rel": "bookmark"
- }
- ],
- "name": "backup001",
- "object_count": 22,
- "size": 1,
- "status": "available",
- "volume_id": "e5185058-943a-4cb4-96d9-72c184c337d6",
- "is_incremental": True,
- "has_dependent_backups": False
- }
- ]
- }
-
- def setUp(self):
- super(TestBackupsClient, self).setUp()
- fake_auth = fake_auth_provider.FakeAuthProvider()
- self.client = backups_client.BackupsClient(fake_auth,
- 'volume',
- 'regionOne')
-
- def _test_list_backups(self, detail=False, mock_args='backups',
- bytes_body=False, **params):
- if detail:
- resp_body = self.FAKE_BACKUP_LIST_WITH_DETAIL
- else:
- resp_body = self.FAKE_BACKUP_LIST
- self.check_service_client_function(
- self.client.list_backups,
- 'tempest.lib.common.rest_client.RestClient.get',
- resp_body,
- to_utf=bytes_body,
- mock_args=[mock_args],
- detail=detail,
- **params)
-
- def test_list_backups_with_str_body(self):
- self._test_list_backups()
-
- def test_list_backups_with_bytes_body(self):
- self._test_list_backups(bytes_body=True)
-
- def test_list_backups_with_detail_with_str_body(self):
- mock_args = "backups/detail"
- self._test_list_backups(detail=True, mock_args=mock_args)
-
- def test_list_backups_with_detail_with_bytes_body(self):
- mock_args = "backups/detail"
- self._test_list_backups(detail=True, mock_args=mock_args,
- bytes_body=True)
-
- def test_list_backups_with_params(self):
- # Run the test separately for each param, to avoid assertion error
- # resulting from randomized params order.
- mock_args = 'backups?sort_key=name'
- self._test_list_backups(mock_args=mock_args, sort_key='name')
-
- mock_args = 'backups/detail?limit=10'
- self._test_list_backups(detail=True, mock_args=mock_args,
- bytes_body=True, limit=10)
diff --git a/tempest/tests/lib/services/volume/v2/test_volumes_client.py b/tempest/tests/lib/services/volume/v2/test_volumes_client.py
deleted file mode 100644
index d7b042e..0000000
--- a/tempest/tests/lib/services/volume/v2/test_volumes_client.py
+++ /dev/null
@@ -1,127 +0,0 @@
-# Copyright 2017 FiberHome Telecommunication Technologies CO.,LTD
-# All Rights Reserved.
-#
-# 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.
-
-from oslo_serialization import jsonutils as json
-
-from tempest.lib.services.volume.v2 import volumes_client
-from tempest.tests.lib import fake_auth_provider
-from tempest.tests.lib.services import base
-
-
-class TestVolumesClient(base.BaseServiceTest):
-
- FAKE_VOLUME_METADATA_ITEM = {
- "meta": {
- "key1": "value1"
- }
- }
-
- FAKE_VOLUME_IMAGE_METADATA = {
- "metadata": {
- "container_format": "bare",
- "min_ram": "0",
- "disk_format": "raw",
- "image_name": "xly-ubuntu16-server",
- "image_id": "3e087b0c-10c5-4255-b147-6e8e9dbad6fc",
- "checksum": "008f5d22fe3cb825d714da79607a90f9",
- "min_disk": "0",
- "size": "8589934592"
- }
- }
-
- def setUp(self):
- super(TestVolumesClient, self).setUp()
- fake_auth = fake_auth_provider.FakeAuthProvider()
- self.client = volumes_client.VolumesClient(fake_auth,
- 'volume',
- 'regionOne')
-
- def _test_retype_volume(self, bytes_body=False):
- kwargs = {
- "new_type": "dedup-tier-replication",
- "migration_policy": "never"
- }
-
- self.check_service_client_function(
- self.client.retype_volume,
- 'tempest.lib.common.rest_client.RestClient.post',
- {},
- to_utf=bytes_body,
- status=202,
- volume_id="a3be971b-8de5-4bdf-bdb8-3d8eb0fb69f8",
- **kwargs
- )
-
- def _test_force_detach_volume(self, bytes_body=False):
- kwargs = {
- 'attachment_id': '6980e295-920f-412e-b189-05c50d605acd',
- 'connector': {
- 'initiator': 'iqn.2017-04.org.fake:01'
- }
- }
-
- self.check_service_client_function(
- self.client.force_detach_volume,
- 'tempest.lib.common.rest_client.RestClient.post',
- {},
- to_utf=bytes_body,
- status=202,
- volume_id="a3be971b-8de5-4bdf-bdb8-3d8eb0fb69f8",
- **kwargs
- )
-
- def _test_show_volume_metadata_item(self, bytes_body=False):
- self.check_service_client_function(
- self.client.show_volume_metadata_item,
- 'tempest.lib.common.rest_client.RestClient.get',
- self.FAKE_VOLUME_METADATA_ITEM,
- to_utf=bytes_body,
- volume_id="a3be971b-8de5-4bdf-bdb8-3d8eb0fb69f8",
- id="key1")
-
- def _test_show_volume_image_metadata(self, bytes_body=False):
- fake_volume_id = "a3be971b-8de5-4bdf-bdb8-3d8eb0fb69f8"
- self.check_service_client_function(
- self.client.show_volume_image_metadata,
- 'tempest.lib.common.rest_client.RestClient.post',
- self.FAKE_VOLUME_IMAGE_METADATA,
- to_utf=bytes_body,
- mock_args=['volumes/%s/action' % fake_volume_id,
- json.dumps({"os-show_image_metadata": {}})],
- volume_id=fake_volume_id)
-
- def test_force_detach_volume_with_str_body(self):
- self._test_force_detach_volume()
-
- def test_force_detach_volume_with_bytes_body(self):
- self._test_force_detach_volume(bytes_body=True)
-
- def test_show_volume_metadata_item_with_str_body(self):
- self._test_show_volume_metadata_item()
-
- def test_show_volume_metadata_item_with_bytes_body(self):
- self._test_show_volume_metadata_item(bytes_body=True)
-
- def test_show_volume_image_metadata_with_str_body(self):
- self._test_show_volume_image_metadata()
-
- def test_show_volume_image_metadata_with_bytes_body(self):
- self._test_show_volume_image_metadata(bytes_body=True)
-
- def test_retype_volume_with_str_body(self):
- self._test_retype_volume()
-
- def test_retype_volume_with_bytes_body(self):
- self._test_retype_volume(bytes_body=True)
diff --git a/tempest/tests/lib/services/volume/v2/test_availability_zone_client.py b/tempest/tests/lib/services/volume/v3/test_availability_zone_client.py
similarity index 96%
rename from tempest/tests/lib/services/volume/v2/test_availability_zone_client.py
rename to tempest/tests/lib/services/volume/v3/test_availability_zone_client.py
index 770565c..4827326 100644
--- a/tempest/tests/lib/services/volume/v2/test_availability_zone_client.py
+++ b/tempest/tests/lib/services/volume/v3/test_availability_zone_client.py
@@ -13,7 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
-from tempest.lib.services.volume.v2 import availability_zone_client
+from tempest.lib.services.volume.v3 import availability_zone_client
from tempest.tests.lib import fake_auth_provider
from tempest.tests.lib.services import base
diff --git a/tempest/tests/lib/services/volume/v3/test_backups_client.py b/tempest/tests/lib/services/volume/v3/test_backups_client.py
index f1ce987..5412064 100644
--- a/tempest/tests/lib/services/volume/v3/test_backups_client.py
+++ b/tempest/tests/lib/services/volume/v3/test_backups_client.py
@@ -20,6 +20,55 @@
class TestBackupsClient(base.BaseServiceTest):
+ FAKE_BACKUP_LIST = {
+ "backups": [
+ {
+ "id": "2ef47aee-8844-490c-804d-2a8efe561c65",
+ "links": [
+ {
+ "href": "fake-url-1",
+ "rel": "self"
+ },
+ {
+ "href": "fake-url-2",
+ "rel": "bookmark"
+ }
+ ],
+ "name": "backup001"
+ }
+ ]
+ }
+
+ FAKE_BACKUP_LIST_WITH_DETAIL = {
+ "backups": [
+ {
+ "availability_zone": "az1",
+ "container": "volumebackups",
+ "created_at": "2013-04-02T10:35:27.000000",
+ "description": None,
+ "fail_reason": None,
+ "id": "2ef47aee-8844-490c-804d-2a8efe561c65",
+ "links": [
+ {
+ "href": "fake-url-1",
+ "rel": "self"
+ },
+ {
+ "href": "fake-url-2",
+ "rel": "bookmark"
+ }
+ ],
+ "name": "backup001",
+ "object_count": 22,
+ "size": 1,
+ "status": "available",
+ "volume_id": "e5185058-943a-4cb4-96d9-72c184c337d6",
+ "is_incremental": True,
+ "has_dependent_backups": False
+ }
+ ]
+ }
+
FAKE_BACKUP_UPDATE = {
"backup": {
"id": "4c65c15f-a5c5-464b-b92a-90e4c04636a7",
@@ -35,6 +84,46 @@
'volume',
'regionOne')
+ def _test_list_backups(self, detail=False, mock_args='backups',
+ bytes_body=False, **params):
+ if detail:
+ resp_body = self.FAKE_BACKUP_LIST_WITH_DETAIL
+ else:
+ resp_body = self.FAKE_BACKUP_LIST
+ self.check_service_client_function(
+ self.client.list_backups,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ resp_body,
+ to_utf=bytes_body,
+ mock_args=[mock_args],
+ detail=detail,
+ **params)
+
+ def test_list_backups_with_str_body(self):
+ self._test_list_backups()
+
+ def test_list_backups_with_bytes_body(self):
+ self._test_list_backups(bytes_body=True)
+
+ def test_list_backups_with_detail_with_str_body(self):
+ mock_args = "backups/detail"
+ self._test_list_backups(detail=True, mock_args=mock_args)
+
+ def test_list_backups_with_detail_with_bytes_body(self):
+ mock_args = "backups/detail"
+ self._test_list_backups(detail=True, mock_args=mock_args,
+ bytes_body=True)
+
+ def test_list_backups_with_params(self):
+ # Run the test separately for each param, to avoid assertion error
+ # resulting from randomized params order.
+ mock_args = 'backups?sort_key=name'
+ self._test_list_backups(mock_args=mock_args, sort_key='name')
+
+ mock_args = 'backups/detail?limit=10'
+ self._test_list_backups(detail=True, mock_args=mock_args,
+ bytes_body=True, limit=10)
+
def _test_update_backup(self, bytes_body=False):
self.check_service_client_function(
self.client.update_backup,
diff --git a/tempest/tests/lib/services/volume/v2/test_capabilities_client.py b/tempest/tests/lib/services/volume/v3/test_capabilities_client.py
similarity index 97%
rename from tempest/tests/lib/services/volume/v2/test_capabilities_client.py
rename to tempest/tests/lib/services/volume/v3/test_capabilities_client.py
index 3d3f1e1..7efe1ff 100644
--- a/tempest/tests/lib/services/volume/v2/test_capabilities_client.py
+++ b/tempest/tests/lib/services/volume/v3/test_capabilities_client.py
@@ -13,7 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
-from tempest.lib.services.volume.v2 import capabilities_client
+from tempest.lib.services.volume.v3 import capabilities_client
from tempest.tests.lib import fake_auth_provider
from tempest.tests.lib.services import base
diff --git a/tempest/tests/lib/services/volume/v2/test_encryption_types_client.py b/tempest/tests/lib/services/volume/v3/test_encryption_types_client.py
similarity index 98%
rename from tempest/tests/lib/services/volume/v2/test_encryption_types_client.py
rename to tempest/tests/lib/services/volume/v3/test_encryption_types_client.py
index 8de9fb4..c788181 100644
--- a/tempest/tests/lib/services/volume/v2/test_encryption_types_client.py
+++ b/tempest/tests/lib/services/volume/v3/test_encryption_types_client.py
@@ -12,7 +12,7 @@
# License for the specific language governing permissions and limitations
# under the License.
-from tempest.lib.services.volume.v2 import encryption_types_client
+from tempest.lib.services.volume.v3 import encryption_types_client
from tempest.tests.lib import fake_auth_provider
from tempest.tests.lib.services import base
diff --git a/tempest/tests/lib/services/volume/v2/test_extensions_client.py b/tempest/tests/lib/services/volume/v3/test_extensions_client.py
similarity index 97%
rename from tempest/tests/lib/services/volume/v2/test_extensions_client.py
rename to tempest/tests/lib/services/volume/v3/test_extensions_client.py
index c0ee421..a8bbffd 100644
--- a/tempest/tests/lib/services/volume/v2/test_extensions_client.py
+++ b/tempest/tests/lib/services/volume/v3/test_extensions_client.py
@@ -13,7 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
-from tempest.lib.services.volume.v2 import extensions_client
+from tempest.lib.services.volume.v3 import extensions_client
from tempest.tests.lib import fake_auth_provider
from tempest.tests.lib.services import base
diff --git a/tempest/tests/lib/services/volume/v2/test_hosts_client.py b/tempest/tests/lib/services/volume/v3/test_hosts_client.py
similarity index 98%
rename from tempest/tests/lib/services/volume/v2/test_hosts_client.py
rename to tempest/tests/lib/services/volume/v3/test_hosts_client.py
index e107910..67ae4fd 100644
--- a/tempest/tests/lib/services/volume/v2/test_hosts_client.py
+++ b/tempest/tests/lib/services/volume/v3/test_hosts_client.py
@@ -13,7 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
-from tempest.lib.services.volume.v2 import hosts_client
+from tempest.lib.services.volume.v3 import hosts_client
from tempest.tests.lib import fake_auth_provider
from tempest.tests.lib.services import base
diff --git a/tempest/tests/lib/services/volume/v2/test_limits_client.py b/tempest/tests/lib/services/volume/v3/test_limits_client.py
similarity index 97%
rename from tempest/tests/lib/services/volume/v2/test_limits_client.py
rename to tempest/tests/lib/services/volume/v3/test_limits_client.py
index 202054c..f94fbe1 100644
--- a/tempest/tests/lib/services/volume/v2/test_limits_client.py
+++ b/tempest/tests/lib/services/volume/v3/test_limits_client.py
@@ -13,7 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
-from tempest.lib.services.volume.v2 import limits_client
+from tempest.lib.services.volume.v3 import limits_client
from tempest.tests.lib import fake_auth_provider
from tempest.tests.lib.services import base
diff --git a/tempest/tests/lib/services/volume/v2/test_quota_classes_client.py b/tempest/tests/lib/services/volume/v3/test_quota_classes_client.py
similarity index 97%
rename from tempest/tests/lib/services/volume/v2/test_quota_classes_client.py
rename to tempest/tests/lib/services/volume/v3/test_quota_classes_client.py
index e715fcc..6190733 100644
--- a/tempest/tests/lib/services/volume/v2/test_quota_classes_client.py
+++ b/tempest/tests/lib/services/volume/v3/test_quota_classes_client.py
@@ -15,7 +15,7 @@
import copy
-from tempest.lib.services.volume.v2 import quota_classes_client
+from tempest.lib.services.volume.v3 import quota_classes_client
from tempest.tests.lib import fake_auth_provider
from tempest.tests.lib.services import base
diff --git a/tempest/tests/lib/services/volume/v2/test_quotas_client.py b/tempest/tests/lib/services/volume/v3/test_quotas_client.py
similarity index 97%
rename from tempest/tests/lib/services/volume/v2/test_quotas_client.py
rename to tempest/tests/lib/services/volume/v3/test_quotas_client.py
index 6384350..aa5d251 100644
--- a/tempest/tests/lib/services/volume/v2/test_quotas_client.py
+++ b/tempest/tests/lib/services/volume/v3/test_quotas_client.py
@@ -12,7 +12,7 @@
# License for the specific language governing permissions and limitations
# under the License.
-from tempest.lib.services.volume.v2 import quotas_client
+from tempest.lib.services.volume.v3 import quotas_client
from tempest.tests.lib import fake_auth_provider
from tempest.tests.lib.services import base
diff --git a/tempest/tests/lib/services/volume/v2/test_scheduler_stats_client.py b/tempest/tests/lib/services/volume/v3/test_scheduler_stats_client.py
similarity index 97%
rename from tempest/tests/lib/services/volume/v2/test_scheduler_stats_client.py
rename to tempest/tests/lib/services/volume/v3/test_scheduler_stats_client.py
index 8a5f25f..e0f5566 100644
--- a/tempest/tests/lib/services/volume/v2/test_scheduler_stats_client.py
+++ b/tempest/tests/lib/services/volume/v3/test_scheduler_stats_client.py
@@ -13,7 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
-from tempest.lib.services.volume.v2 import scheduler_stats_client
+from tempest.lib.services.volume.v3 import scheduler_stats_client
from tempest.tests.lib import fake_auth_provider
from tempest.tests.lib.services import base
diff --git a/tempest/tests/lib/services/volume/v2/test_snapshot_manage_client.py b/tempest/tests/lib/services/volume/v3/test_snapshot_manage_client.py
similarity index 94%
rename from tempest/tests/lib/services/volume/v2/test_snapshot_manage_client.py
rename to tempest/tests/lib/services/volume/v3/test_snapshot_manage_client.py
index e03a8eb..1b88020 100644
--- a/tempest/tests/lib/services/volume/v2/test_snapshot_manage_client.py
+++ b/tempest/tests/lib/services/volume/v3/test_snapshot_manage_client.py
@@ -17,9 +17,7 @@
from oslo_serialization import jsonutils as json
-from tempest.lib.services.volume.v2 import snapshot_manage_client
-from tempest.lib.services.volume.v3 import snapshot_manage_client \
- as snapshot_manage_clientv3
+from tempest.lib.services.volume.v3 import snapshot_manage_client
from tempest.tests.lib import fake_auth_provider
from tempest.tests.lib.services import base
@@ -65,7 +63,7 @@
# NOTE: Use sort_keys for json.dumps so that the expected and actual
# payloads are guaranteed to be identical for mock_args assert check.
- with mock.patch.object(snapshot_manage_clientv3.json,
+ with mock.patch.object(snapshot_manage_client.json,
'dumps') as mock_dumps:
mock_dumps.side_effect = lambda d: json_dumps(d, sort_keys=True)
diff --git a/tempest/tests/lib/services/volume/v2/test_snapshots_client.py b/tempest/tests/lib/services/volume/v3/test_snapshots_client.py
similarity index 99%
rename from tempest/tests/lib/services/volume/v2/test_snapshots_client.py
rename to tempest/tests/lib/services/volume/v3/test_snapshots_client.py
index c9f57a0..2efd2e6 100644
--- a/tempest/tests/lib/services/volume/v2/test_snapshots_client.py
+++ b/tempest/tests/lib/services/volume/v3/test_snapshots_client.py
@@ -12,7 +12,7 @@
# License for the specific language governing permissions and limitations
# under the License.
-from tempest.lib.services.volume.v2 import snapshots_client
+from tempest.lib.services.volume.v3 import snapshots_client
from tempest.tests.lib import fake_auth_provider
from tempest.tests.lib.services import base
diff --git a/tempest/tests/lib/services/volume/v2/test_transfers_client.py b/tempest/tests/lib/services/volume/v3/test_transfers_client.py
similarity index 95%
rename from tempest/tests/lib/services/volume/v2/test_transfers_client.py
rename to tempest/tests/lib/services/volume/v3/test_transfers_client.py
index 8e7c6f4..d631fe7 100644
--- a/tempest/tests/lib/services/volume/v2/test_transfers_client.py
+++ b/tempest/tests/lib/services/volume/v3/test_transfers_client.py
@@ -18,9 +18,7 @@
import mock
from oslo_serialization import jsonutils as json
-from tempest.lib.services.volume.v2 import transfers_client
-from tempest.lib.services.volume.v3 import transfers_client \
- as transfers_clientv3
+from tempest.lib.services.volume.v3 import transfers_client
from tempest.tests.lib import fake_auth_provider
from tempest.tests.lib.services import base
@@ -65,7 +63,7 @@
# NOTE: Use sort_keys for json.dumps so that the expected and actual
# payloads are guaranteed to be identical for mock_args assert check.
- with mock.patch.object(transfers_clientv3.json, 'dumps') as mock_dumps:
+ with mock.patch.object(transfers_client.json, 'dumps') as mock_dumps:
mock_dumps.side_effect = lambda d: json_dumps(d, sort_keys=True)
self.check_service_client_function(
@@ -86,7 +84,7 @@
# NOTE: Use sort_keys for json.dumps so that the expected and actual
# payloads are guaranteed to be identical for mock_args assert check.
- with mock.patch.object(transfers_clientv3.json, 'dumps') as mock_dumps:
+ with mock.patch.object(transfers_client.json, 'dumps') as mock_dumps:
mock_dumps.side_effect = lambda d: json_dumps(d, sort_keys=True)
self.check_service_client_function(
diff --git a/tempest/tests/lib/services/volume/v2/test_volume_manage_client.py b/tempest/tests/lib/services/volume/v3/test_volume_manage_client.py
similarity index 94%
rename from tempest/tests/lib/services/volume/v2/test_volume_manage_client.py
rename to tempest/tests/lib/services/volume/v3/test_volume_manage_client.py
index 0fb66bb..902f027 100644
--- a/tempest/tests/lib/services/volume/v2/test_volume_manage_client.py
+++ b/tempest/tests/lib/services/volume/v3/test_volume_manage_client.py
@@ -17,9 +17,7 @@
from oslo_serialization import jsonutils as json
-from tempest.lib.services.volume.v2 import volume_manage_client
-from tempest.lib.services.volume.v3 import volume_manage_client \
- as volume_manage_clientv3
+from tempest.lib.services.volume.v3 import volume_manage_client
from tempest.tests.lib import fake_auth_provider
from tempest.tests.lib.services import base
@@ -93,7 +91,7 @@
# NOTE: Use sort_keys for json.dumps so that the expected and actual
# payloads are guaranteed to be identical for mock_args assert check.
- with mock.patch.object(volume_manage_clientv3.json,
+ with mock.patch.object(volume_manage_client.json,
'dumps') as mock_dumps:
mock_dumps.side_effect = lambda d: json_dumps(d, sort_keys=True)
diff --git a/tempest/tests/lib/services/volume/v3/test_volumes_client.py b/tempest/tests/lib/services/volume/v3/test_volumes_client.py
index a515fd3..1250536 100644
--- a/tempest/tests/lib/services/volume/v3/test_volumes_client.py
+++ b/tempest/tests/lib/services/volume/v3/test_volumes_client.py
@@ -13,6 +13,8 @@
# License for the specific language governing permissions and limitations
# under the License.
+from oslo_serialization import jsonutils as json
+
from tempest.lib.services.volume.v3 import volumes_client
from tempest.tests.lib import fake_auth_provider
from tempest.tests.lib.services import base
@@ -27,6 +29,25 @@
}
}
+ FAKE_VOLUME_METADATA_ITEM = {
+ "meta": {
+ "key1": "value1"
+ }
+ }
+
+ FAKE_VOLUME_IMAGE_METADATA = {
+ "metadata": {
+ "container_format": "bare",
+ "min_ram": "0",
+ "disk_format": "raw",
+ "image_name": "xly-ubuntu16-server",
+ "image_id": "3e087b0c-10c5-4255-b147-6e8e9dbad6fc",
+ "checksum": "008f5d22fe3cb825d714da79607a90f9",
+ "min_disk": "0",
+ "size": "8589934592"
+ }
+ }
+
def setUp(self):
super(TestVolumesClient, self).setUp()
fake_auth = fake_auth_provider.FakeAuthProvider()
@@ -34,6 +55,60 @@
'volume',
'regionOne')
+ def _test_retype_volume(self, bytes_body=False):
+ kwargs = {
+ "new_type": "dedup-tier-replication",
+ "migration_policy": "never"
+ }
+
+ self.check_service_client_function(
+ self.client.retype_volume,
+ 'tempest.lib.common.rest_client.RestClient.post',
+ {},
+ to_utf=bytes_body,
+ status=202,
+ volume_id="a3be971b-8de5-4bdf-bdb8-3d8eb0fb69f8",
+ **kwargs
+ )
+
+ def _test_force_detach_volume(self, bytes_body=False):
+ kwargs = {
+ 'attachment_id': '6980e295-920f-412e-b189-05c50d605acd',
+ 'connector': {
+ 'initiator': 'iqn.2017-04.org.fake:01'
+ }
+ }
+
+ self.check_service_client_function(
+ self.client.force_detach_volume,
+ 'tempest.lib.common.rest_client.RestClient.post',
+ {},
+ to_utf=bytes_body,
+ status=202,
+ volume_id="a3be971b-8de5-4bdf-bdb8-3d8eb0fb69f8",
+ **kwargs
+ )
+
+ def _test_show_volume_metadata_item(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.show_volume_metadata_item,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_VOLUME_METADATA_ITEM,
+ to_utf=bytes_body,
+ volume_id="a3be971b-8de5-4bdf-bdb8-3d8eb0fb69f8",
+ id="key1")
+
+ def _test_show_volume_image_metadata(self, bytes_body=False):
+ fake_volume_id = "a3be971b-8de5-4bdf-bdb8-3d8eb0fb69f8"
+ self.check_service_client_function(
+ self.client.show_volume_image_metadata,
+ 'tempest.lib.common.rest_client.RestClient.post',
+ self.FAKE_VOLUME_IMAGE_METADATA,
+ to_utf=bytes_body,
+ mock_args=['volumes/%s/action' % fake_volume_id,
+ json.dumps({"os-show_image_metadata": {}})],
+ volume_id=fake_volume_id)
+
def _test_show_volume_summary(self, bytes_body=False):
self.check_service_client_function(
self.client.show_volume_summary,
@@ -41,6 +116,30 @@
self.FAKE_VOLUME_SUMMARY,
bytes_body)
+ def test_force_detach_volume_with_str_body(self):
+ self._test_force_detach_volume()
+
+ def test_force_detach_volume_with_bytes_body(self):
+ self._test_force_detach_volume(bytes_body=True)
+
+ def test_show_volume_metadata_item_with_str_body(self):
+ self._test_show_volume_metadata_item()
+
+ def test_show_volume_metadata_item_with_bytes_body(self):
+ self._test_show_volume_metadata_item(bytes_body=True)
+
+ def test_show_volume_image_metadata_with_str_body(self):
+ self._test_show_volume_image_metadata()
+
+ def test_show_volume_image_metadata_with_bytes_body(self):
+ self._test_show_volume_image_metadata(bytes_body=True)
+
+ def test_retype_volume_with_str_body(self):
+ self._test_retype_volume()
+
+ def test_retype_volume_with_bytes_body(self):
+ self._test_retype_volume(bytes_body=True)
+
def test_show_volume_summary_with_str_body(self):
self._test_show_volume_summary()
diff --git a/tempest/tests/lib/test_decorators.py b/tempest/tests/lib/test_decorators.py
index ed0eea3..0b1a599 100644
--- a/tempest/tests/lib/test_decorators.py
+++ b/tempest/tests/lib/test_decorators.py
@@ -19,6 +19,7 @@
from tempest.lib import base as test
from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
+from tempest.lib import exceptions as lib_exc
from tempest.tests import base
@@ -62,21 +63,40 @@
t = TestFoo('test_bar')
if expected_to_skip:
- self.assertRaises(testtools.TestCase.skipException, t.test_bar)
+ 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'Skipped until bug\: %s.*' % decorators._get_bug_url(
+ bug, bug_type)
+ )
else:
# assert that test_bar returned 0
self.assertEqual(TestFoo('test_bar').test_bar(), 0)
- def test_skip_because_bug(self):
+ def test_skip_because_launchpad_bug(self):
self._test_skip_because_helper(bug='12345')
- def test_skip_because_bug_and_condition_true(self):
+ def test_skip_because_launchpad_bug_and_condition_true(self):
self._test_skip_because_helper(bug='12348', condition=True)
- def test_skip_because_bug_and_condition_false(self):
+ 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)
+
+ 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)
+
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,
@@ -84,8 +104,8 @@
self._test_skip_because_helper(expected_to_skip=False)
def test_skip_because_invalid_bug_number(self):
- """Raise ValueError if with an invalid bug number"""
- self.assertRaises(ValueError, self._test_skip_because_helper,
+ """Raise InvalidParam if with an invalid bug number"""
+ self.assertRaises(lib_exc.InvalidParam, self._test_skip_because_helper,
bug='critical_bug')
@@ -126,6 +146,13 @@
class TestRelatedBugDecorator(base.TestCase):
+
+ def _get_my_exception(self):
+ class MyException(Exception):
+ def __init__(self, status_code):
+ self.status_code = status_code
+ return MyException
+
def test_relatedbug_when_no_exception(self):
f = mock.Mock()
sentinel = object()
@@ -137,10 +164,9 @@
test_foo(sentinel)
f.assert_called_once_with(sentinel)
- def test_relatedbug_when_exception(self):
- class MyException(Exception):
- def __init__(self, status_code):
- self.status_code = status_code
+ def test_relatedbug_when_exception_with_launchpad_bug_type(self):
+ """Validate related_bug decorator with bug_type == 'launchpad'"""
+ MyException = self._get_my_exception()
def f(self):
raise MyException(status_code=500)
@@ -152,4 +178,53 @@
with mock.patch.object(decorators.LOG, 'error') as m_error:
self.assertRaises(MyException, test_foo, object())
- m_error.assert_called_once_with(mock.ANY, '1234', '1234')
+ m_error.assert_called_once_with(
+ mock.ANY, '1234', 'https://launchpad.net/bugs/1234')
+
+ def test_relatedbug_when_exception_with_storyboard_bug_type(self):
+ """Validate related_bug decorator with bug_type == 'storyboard'"""
+ MyException = self._get_my_exception()
+
+ def f(self):
+ raise MyException(status_code=500)
+
+ @decorators.related_bug(bug="1234", status_code=500,
+ bug_type='storyboard')
+ def test_foo(self):
+ f(self)
+
+ with mock.patch.object(decorators.LOG, 'error') as m_error:
+ self.assertRaises(MyException, test_foo, object())
+
+ m_error.assert_called_once_with(
+ mock.ANY, '1234', 'https://storyboard.openstack.org/#!/story/1234')
+
+ def test_relatedbug_when_exception_invalid_bug_type(self):
+ """Check related_bug decorator raises exc when bug_type is not valid"""
+ MyException = self._get_my_exception()
+
+ def f(self):
+ raise MyException(status_code=500)
+
+ @decorators.related_bug(bug="1234", status_code=500,
+ bug_type=mock.sentinel.invalid)
+ def test_foo(self):
+ f(self)
+
+ with mock.patch.object(decorators.LOG, 'error'):
+ self.assertRaises(lib_exc.InvalidParam, test_foo, object())
+
+ def test_relatedbug_when_exception_invalid_bug_number(self):
+ """Check related_bug decorator raises exc when bug_number != digit"""
+ MyException = self._get_my_exception()
+
+ def f(self):
+ raise MyException(status_code=500)
+
+ @decorators.related_bug(bug="not a digit", status_code=500,
+ bug_type='launchpad')
+ def test_foo(self):
+ f(self)
+
+ with mock.patch.object(decorators.LOG, 'error'):
+ self.assertRaises(lib_exc.InvalidParam, test_foo, object())
diff --git a/tempest/tests/test_hacking.py b/tempest/tests/test_hacking.py
index bc3a753..9534ce8 100644
--- a/tempest/tests/test_hacking.py
+++ b/tempest/tests/test_hacking.py
@@ -193,3 +193,60 @@
"raise TestCase.failureException(exception.message)"))), 1)
self.assertEqual(len(list(checks.unsupported_exception_attribute_PY3(
"raise TestCase.failureException(ee.message)"))), 0)
+
+ def _test_no_negatve_test_attribute_applied_to_negative_test(
+ self, filename, with_other_decorators=False,
+ with_negative_decorator=True, expected_success=True):
+ check = checks.negative_test_attribute_always_applied_to_negative_tests
+ other_decorators = [
+ "@decorators.idempotent_id(123)",
+ "@utils.requires_ext(extension='ext', service='svc')"
+ ]
+
+ if with_other_decorators:
+ # Include multiple decorators to verify that this check works with
+ # arbitrarily many decorators. These insert decorators above the
+ # @decorators.attr(type=['negative']) decorator.
+ for decorator in other_decorators:
+ self.assertIsNone(check(" %s" % decorator, filename))
+ if with_negative_decorator:
+ self.assertIsNone(
+ check("@decorators.attr(type=['negative'])", filename))
+ if with_other_decorators:
+ # Include multiple decorators to verify that this check works with
+ # arbitrarily many decorators. These insert decorators between
+ # the test and the @decorators.attr(type=['negative']) decorator.
+ for decorator in other_decorators:
+ self.assertIsNone(check(" %s" % decorator, filename))
+ final_result = check(" def test_some_negative_case", filename)
+ if expected_success:
+ self.assertIsNone(final_result)
+ else:
+ self.assertIsInstance(final_result, tuple)
+ self.assertFalse(final_result[0])
+
+ def test_no_negatve_test_attribute_applied_to_negative_test(self):
+ # Check negative filename, negative decorator passes
+ self._test_no_negatve_test_attribute_applied_to_negative_test(
+ "./tempest/api/test_something_negative.py")
+ # Check negative filename, negative decorator, other decorators passes
+ self._test_no_negatve_test_attribute_applied_to_negative_test(
+ "./tempest/api/test_something_negative.py",
+ with_other_decorators=True)
+
+ # Check non-negative filename skips check, causing pass
+ self._test_no_negatve_test_attribute_applied_to_negative_test(
+ "./tempest/api/test_something.py")
+
+ # Check negative filename, no negative decorator fails
+ self._test_no_negatve_test_attribute_applied_to_negative_test(
+ "./tempest/api/test_something_negative.py",
+ with_negative_decorator=False,
+ expected_success=False)
+ # Check negative filename, no negative decorator, other decorators
+ # fails
+ self._test_no_negatve_test_attribute_applied_to_negative_test(
+ "./tempest/api/test_something_negative.py",
+ with_other_decorators=True,
+ with_negative_decorator=False,
+ expected_success=False)
diff --git a/tools/generate-tempest-plugins-list.sh b/tools/generate-tempest-plugins-list.sh
index 20c99b2..d2fd2aa 100755
--- a/tools/generate-tempest-plugins-list.sh
+++ b/tools/generate-tempest-plugins-list.sh
@@ -49,13 +49,37 @@
sorted_plugins=$(python tools/generate-tempest-plugins-list.py)
+name_col_len=$(echo "${sorted_plugins}" | wc -L)
+name_col_len=$(( name_col_len + 2 ))
+
+# Print the title underline for a RST table.
+function title_underline {
+ printf "== "
+ local len=$1
+ while [[ $len -gt 0 ]]; do
+ printf "="
+ len=$(( len - 1))
+ done
+ printf " ===\n"
+}
+
+printf "\n\n"
+title_underline ${name_col_len}
+printf "%-3s %-${name_col_len}s %s\n" "SR" "Plugin Name" "URL"
+title_underline ${name_col_len}
+
+i=0
for k in ${sorted_plugins}; do
+ i=$((i+1))
project=${k:0:28}
giturl="git://git.openstack.org/openstack/${k:0:26}"
- printf "|%-28s|%-73s|\n" "${project}" "${giturl}"
- printf "+----------------------------+-------------------------------------------------------------------------+\n"
+ printf "%-3s %-${name_col_len}s %s\n" "$i" "${project}" "${giturl}"
done
+title_underline ${name_col_len}
+
+printf "\n\n"
+
if [[ -r doc/source/data/tempest-plugins-registry.footer ]]; then
cat doc/source/data/tempest-plugins-registry.footer
fi
diff --git a/tox.ini b/tox.ini
index de4f1b7..befa991 100644
--- a/tox.ini
+++ b/tox.ini
@@ -135,6 +135,16 @@
find . -type f -name "*.pyc" -delete
tempest run --serial --regex '\[.*\bsmoke\b.*\]' {posargs}
+[testenv:slow-serial]
+envdir = .tox/tempest
+sitepackages = {[tempestenv]sitepackages}
+setenv = {[tempestenv]setenv}
+deps = {[tempestenv]deps}
+# The regex below is used to select the slow tagged tests to run serially:
+commands =
+ find . -type f -name "*.pyc" -delete
+ tempest run --serial --regex '\[.*\bslow\b.*\]' {posargs}
+
[testenv:venv]
deps =
-c{env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt}