Merge "Pass username on v3 token issue"
diff --git a/HACKING.rst b/HACKING.rst
index 7ab420b..a209b3f 100644
--- a/HACKING.rst
+++ b/HACKING.rst
@@ -240,29 +240,6 @@
can be used to perform this. See AggregatesAdminTest in
tempest.api.compute.admin for an example of using locking.
-Stress Tests in Tempest
------------------------
-Any tempest test case can be flagged as a stress test. With this flag it will
-be automatically discovery and used in the stress test runs. The stress test
-framework itself is a facility to spawn and control worker processes in order
-to find race conditions (see ``tempest/stress/`` for more information). Please
-note that these stress tests can't be used for benchmarking purposes since they
-don't measure any performance characteristics.
-
-Example::
-
- @stresstest(class_setup_per='process')
- def test_this_and_that(self):
- ...
-
-This will flag the test ``test_this_and_that`` as a stress test. The parameter
-``class_setup_per`` gives control when the setUpClass function should be called.
-
-Good candidates for stress tests are:
-
-- Scenario tests
-- API tests that have a wide focus
-
Sample Configuration File
-------------------------
The sample config file is autogenerated using a script. If any changes are made
diff --git a/README.rst b/README.rst
index fc4de5e..bc58cc0 100644
--- a/README.rst
+++ b/README.rst
@@ -1,3 +1,12 @@
+========================
+Team and repository tags
+========================
+
+.. image:: http://governance.openstack.org/badges/tempest.svg
+ :target: http://governance.openstack.org/reference/tags/index.html
+
+.. Change things from this point on
+
Tempest - The OpenStack Integration Test Suite
==============================================
diff --git a/REVIEWING.rst b/REVIEWING.rst
index cfe7f4c..9b272bb 100644
--- a/REVIEWING.rst
+++ b/REVIEWING.rst
@@ -93,6 +93,12 @@
.. _reno: http://docs.openstack.org/developer/reno/
+Deprecated Code
+---------------
+Sometimes we have some bugs in deprecated code. Basically, we leave it. Because
+we don't need to maintain it. However, if the bug is critical, we might need to
+fix it. When it will happen, we will deal with it on a case-by-case basis.
+
When to approve
---------------
* Every patch needs two +2s before being approved.
diff --git a/bindep.txt b/bindep.txt
new file mode 100644
index 0000000..6a28348
--- /dev/null
+++ b/bindep.txt
@@ -0,0 +1,11 @@
+# This file contains runtime (non-python) dependencies
+# More info at: http://docs.openstack.org/infra/bindep/readme.html
+
+libffi-dev [platform:dpkg]
+libffi-devel [platform:rpm]
+gcc [platform:rpm]
+gcc [platform:dpkg]
+python-dev [platform:dpkg]
+python-devel [platform:rpm]
+openssl-devel [platform:rpm]
+libssl-dev [platform:dpkg]
diff --git a/doc/source/field_guide/stress.rst b/doc/source/field_guide/stress.rst
deleted file mode 120000
index d39d0f8..0000000
--- a/doc/source/field_guide/stress.rst
+++ /dev/null
@@ -1 +0,0 @@
-../../../tempest/stress/README.rst
\ No newline at end of file
diff --git a/doc/source/index.rst b/doc/source/index.rst
index 6abe9dc..896cd98 100644
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -24,7 +24,6 @@
field_guide/index
field_guide/api
field_guide/scenario
- field_guide/stress
field_guide/unit_tests
=========
diff --git a/doc/source/library/api_microversion_testing.rst b/doc/source/library/api_microversion_testing.rst
index b7a4ba8..8be924d 100644
--- a/doc/source/library/api_microversion_testing.rst
+++ b/doc/source/library/api_microversion_testing.rst
@@ -1,6 +1,6 @@
.. _api_microversion_testing:
-API Microversion Testing Support in Temepst
+API Microversion Testing Support in Tempest
===========================================
---------------------------------------------
diff --git a/doc/source/microversion_testing.rst b/doc/source/microversion_testing.rst
index dc73ef2..6b87b4d 100644
--- a/doc/source/microversion_testing.rst
+++ b/doc/source/microversion_testing.rst
@@ -236,6 +236,10 @@
.. _2.25: http://docs.openstack.org/developer/nova/api_microversion_history.html#maximum-in-mitaka
+ * `2.32`_
+
+ .. _2.32: http://docs.openstack.org/developer/nova/api_microversion_history.html#id29
+
* `2.37`_
.. _2.37: http://docs.openstack.org/developer/nova/api_microversion_history.html#id34
diff --git a/etc/logging.conf.sample b/etc/logging.conf.sample
index 36cd324..c131b07 100644
--- a/etc/logging.conf.sample
+++ b/etc/logging.conf.sample
@@ -1,5 +1,5 @@
[loggers]
-keys=root,tempest_stress
+keys=root
[handlers]
keys=file,devel,syslog
@@ -11,11 +11,6 @@
level=DEBUG
handlers=file
-[logger_tempest_stress]
-level=DEBUG
-handlers=file,devel
-qualname=tempest.stress
-
[handler_file]
class=FileHandler
level=DEBUG
diff --git a/releasenotes/notes/12.2.0-clients_module-16f3025f515bf9ec.yaml b/releasenotes/notes/12.2.0-clients_module-16f3025f515bf9ec.yaml
index 53741da..484d543 100644
--- a/releasenotes/notes/12.2.0-clients_module-16f3025f515bf9ec.yaml
+++ b/releasenotes/notes/12.2.0-clients_module-16f3025f515bf9ec.yaml
@@ -8,7 +8,7 @@
access service clients defined in Tempest as well as service clients
defined in all loaded plugins.
The new ServiceClients class only exposes for now the service clients
- which are in tempest.lib, i.e. compute, network and image. The remaing
+ which are in tempest.lib, i.e. compute, network and image. The remaining
service clients (identity, volume and object-storage) will be added in
future updates.
deprecations:
diff --git a/releasenotes/notes/add-cred_client-to-tempest.lib-4d4af33f969c576f.yaml b/releasenotes/notes/add-cred_client-to-tempest.lib-4d4af33f969c576f.yaml
new file mode 100644
index 0000000..432a6b1
--- /dev/null
+++ b/releasenotes/notes/add-cred_client-to-tempest.lib-4d4af33f969c576f.yaml
@@ -0,0 +1,5 @@
+---
+features:
+ - The cred_client module was added to tempest.lib. This module provides a
+ wrapper to the keystone services client which provides a uniform
+ interface that abstracts out the differences between keystone api versions.
diff --git a/releasenotes/notes/add-image-clients-af94564fb34ddca6.yaml b/releasenotes/notes/add-image-clients-af94564fb34ddca6.yaml
new file mode 100644
index 0000000..7e40fd4
--- /dev/null
+++ b/releasenotes/notes/add-image-clients-af94564fb34ddca6.yaml
@@ -0,0 +1,9 @@
+---
+features:
+ - |
+ As in the [doc]:
+ http://developer.openstack.org/api-ref/image/v2/metadefs-index.html,
+ there are some apis are not included, add them.
+
+ * namespace_properties_client(v2)
+
diff --git a/releasenotes/notes/add-role-assignments-client-as-a-library-d34b4fdf376984ad.yaml b/releasenotes/notes/add-role-assignments-client-as-a-library-d34b4fdf376984ad.yaml
new file mode 100644
index 0000000..a1edcc5
--- /dev/null
+++ b/releasenotes/notes/add-role-assignments-client-as-a-library-d34b4fdf376984ad.yaml
@@ -0,0 +1,6 @@
+---
+features:
+ - Define the identity service role_assignments_client as a library.
+ Add role_assignments_client to the library interface so the other
+ projects can use this module as a stable library without any
+ maintenance changes.
diff --git a/releasenotes/notes/move-cinder-v3-to-lib-service-be3ba0c20753b594.yaml b/releasenotes/notes/move-cinder-v3-to-lib-service-be3ba0c20753b594.yaml
new file mode 100644
index 0000000..9223ba5
--- /dev/null
+++ b/releasenotes/notes/move-cinder-v3-to-lib-service-be3ba0c20753b594.yaml
@@ -0,0 +1,7 @@
+features:
+ - |
+ Define the Volume v3 service clients as library interfaces,
+ allowing other projects to use these modules as stable
+ libraries without maintenance changes.
+
+ * messages_client(v3)
diff --git a/releasenotes/notes/new-volume-limit-client-517c17d9090f4df4.yaml b/releasenotes/notes/new-volume-limit-client-517c17d9090f4df4.yaml
new file mode 100644
index 0000000..033e147
--- /dev/null
+++ b/releasenotes/notes/new-volume-limit-client-517c17d9090f4df4.yaml
@@ -0,0 +1,3 @@
+---
+features:
+ - The volume_limits client was added to tempest.lib.
diff --git a/releasenotes/notes/remo-stress-tests-81052b211ad95d2e.yaml b/releasenotes/notes/remo-stress-tests-81052b211ad95d2e.yaml
new file mode 100644
index 0000000..aa3a78e
--- /dev/null
+++ b/releasenotes/notes/remo-stress-tests-81052b211ad95d2e.yaml
@@ -0,0 +1,4 @@
+---
+upgrade:
+ - The Stress tests framework and all the stress tests have been removed.
+
diff --git a/releasenotes/notes/remove-bootable-option-024f8944c056a3e0.yaml b/releasenotes/notes/remove-bootable-option-024f8944c056a3e0.yaml
new file mode 100644
index 0000000..e6e53af
--- /dev/null
+++ b/releasenotes/notes/remove-bootable-option-024f8944c056a3e0.yaml
@@ -0,0 +1,6 @@
+---
+deprecations:
+ - The *bootable* config option in the *volume_feature_enabled* group is
+ removed because the corresponding feature os-set_bootable has been
+ implemented 2.5 years ago and all OpenStack versions which are supported
+ by Tempest should support the feature.
diff --git a/releasenotes/notes/remove-negative-test-generator-1653f4c0f86ccf75.yaml b/releasenotes/notes/remove-negative-test-generator-1653f4c0f86ccf75.yaml
new file mode 100644
index 0000000..a734d15
--- /dev/null
+++ b/releasenotes/notes/remove-negative-test-generator-1653f4c0f86ccf75.yaml
@@ -0,0 +1,4 @@
+---
+upgrade:
+ - The Negative Tests Generator has been removed (it was not used by any
+ Tempest tests).
diff --git a/releasenotes/source/unreleased.rst b/releasenotes/source/unreleased.rst
index 5860a46..875030f 100644
--- a/releasenotes/source/unreleased.rst
+++ b/releasenotes/source/unreleased.rst
@@ -1,5 +1,5 @@
-==========================
- Unreleased Release Notes
-==========================
+============================
+Current Series Release Notes
+============================
.. release-notes::
diff --git a/requirements.txt b/requirements.txt
index 9079a8d..d9a9ebb 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,18 +1,18 @@
# The order of packages is significant, because pip processes them in the order
# of appearance. Changing the order has an impact on the overall integration
# process, which may cause wedges in the gate later.
-pbr>=1.6 # Apache-2.0
-cliff>=2.2.0 # Apache-2.0
+pbr>=1.8 # Apache-2.0
+cliff>=2.3.0 # Apache-2.0
jsonschema!=2.5.0,<3.0.0,>=2.0.0 # MIT
testtools>=1.4.0 # MIT
paramiko>=2.0 # LGPLv2.1+
netaddr!=0.7.16,>=0.7.13 # BSD
testrepository>=0.0.18 # Apache-2.0/BSD
oslo.concurrency>=3.8.0 # Apache-2.0
-oslo.config>=3.14.0 # Apache-2.0
+oslo.config!=3.18.0,>=3.14.0 # Apache-2.0
oslo.log>=3.11.0 # Apache-2.0
oslo.serialization>=1.10.0 # Apache-2.0
-oslo.utils>=3.17.0 # Apache-2.0
+oslo.utils>=3.18.0 # Apache-2.0
six>=1.9.0 # MIT
fixtures>=3.0.0 # Apache-2.0/BSD
PyYAML>=3.10.0 # MIT
diff --git a/setup.cfg b/setup.cfg
index 28e17ef..96313fd 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -28,7 +28,6 @@
[entry_points]
console_scripts =
verify-tempest-config = tempest.cmd.verify_tempest_config:main
- run-tempest-stress = tempest.cmd.run_stress:main
tempest-account-generator = tempest.cmd.account_generator:main
tempest = tempest.cmd.main:main
skip-tracker = tempest.lib.cmd.skip_tracker:main
@@ -38,7 +37,6 @@
account-generator = tempest.cmd.account_generator:TempestAccountGenerator
init = tempest.cmd.init:TempestInit
cleanup = tempest.cmd.cleanup:TempestCleanup
- run-stress = tempest.cmd.run_stress:TempestRunStress
list-plugins = tempest.cmd.list_plugins:TempestListPlugins
verify-config = tempest.cmd.verify_tempest_config:TempestVerifyConfig
workspace = tempest.cmd.workspace:TempestWorkspace
diff --git a/tempest/README.rst b/tempest/README.rst
index c9a0491..0feec41 100644
--- a/tempest/README.rst
+++ b/tempest/README.rst
@@ -15,7 +15,6 @@
| tempest/
| api/ - API tests
| scenario/ - complex scenario tests
-| stress/ - stress tests
Each of these directories contains different types of tests. What
belongs in each directory, the rules and examples for good tests, are
@@ -46,14 +45,6 @@
but should instead use the tempest implementations of clients.
-:ref:`stress_field_guide`
--------------------------
-
-Stress tests are designed to stress an OpenStack environment by running a high
-workload against it and seeing what breaks. The stress test framework runs
-several test jobs in parallel and can run any existing test in Tempest as a
-stress job.
-
:ref:`unit_tests_field_guide`
-----------------------------
diff --git a/tempest/api/baremetal/admin/test_nodestates.py b/tempest/api/baremetal/admin/test_nodestates.py
index 1ffea25..e74dd04 100644
--- a/tempest/api/baremetal/admin/test_nodestates.py
+++ b/tempest/api/baremetal/admin/test_nodestates.py
@@ -15,7 +15,7 @@
from oslo_utils import timeutils
from tempest.api.baremetal.admin import base
-from tempest import exceptions
+from tempest.lib import exceptions
from tempest import test
diff --git a/tempest/api/compute/admin/test_agents.py b/tempest/api/compute/admin/test_agents.py
index 61359f1..40cb523 100644
--- a/tempest/api/compute/admin/test_agents.py
+++ b/tempest/api/compute/admin/test_agents.py
@@ -90,7 +90,8 @@
body = self.client.create_agent(**self.params_agent)['agent']
self.addCleanup(self.client.delete_agent, body['agent_id'])
agents = self.client.list_agents()['agents']
- self.assertTrue(len(agents) > 0, 'Cannot get any agents.(%s)' % agents)
+ self.assertGreater(len(agents), 0,
+ 'Cannot get any agents.(%s)' % agents)
self.assertIn(body['agent_id'], map(lambda x: x['agent_id'], agents))
@test.idempotent_id('eabadde4-3cd7-4ec4-a4b5-5a936d2d4408')
@@ -108,7 +109,8 @@
agent_id_xen = agent_xen['agent_id']
agents = (self.client.list_agents(hypervisor=agent_xen['hypervisor'])
['agents'])
- self.assertTrue(len(agents) > 0, 'Cannot get any agents.(%s)' % agents)
+ self.assertGreater(len(agents), 0,
+ 'Cannot get any agents.(%s)' % agents)
self.assertIn(agent_id_xen, map(lambda x: x['agent_id'], agents))
self.assertNotIn(body['agent_id'], map(lambda x: x['agent_id'],
agents))
diff --git a/tempest/api/compute/admin/test_aggregates.py b/tempest/api/compute/admin/test_aggregates.py
index 667d30b..1233a2b 100644
--- a/tempest/api/compute/admin/test_aggregates.py
+++ b/tempest/api/compute/admin/test_aggregates.py
@@ -18,9 +18,12 @@
from tempest.api.compute import base
from tempest.common import tempest_fixtures as fixtures
from tempest.common.utils import data_utils
+from tempest import config
from tempest.lib.common.utils import test_utils
from tempest import test
+CONF = config.CONF
+
class AggregatesAdminTestJSON(base.BaseV2ComputeAdminTest):
"""Tests Aggregates API that require admin privileges"""
@@ -39,14 +42,22 @@
cls.host = None
hypers = cls.os_adm.hypervisor_client.list_hypervisors(
detail=True)['hypervisors']
+
+ if CONF.compute.hypervisor_type:
+ hypers = [hyper for hyper in hypers
+ 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]
else:
- raise testtools.TestCase.failureException(
- "no available compute node found")
+ msg = "no available compute node found"
+ if CONF.compute.hypervisor_type:
+ msg += " for hypervisor_type %s" % CONF.compute.hypervisor_type
+ raise testtools.TestCase.failureException(msg)
@test.idempotent_id('0d148aa3-d54c-4317-aa8d-42040a475e20')
def test_aggregate_create_delete(self):
diff --git a/tempest/api/compute/admin/test_aggregates_negative.py b/tempest/api/compute/admin/test_aggregates_negative.py
index 3c4e313..85aa301 100644
--- a/tempest/api/compute/admin/test_aggregates_negative.py
+++ b/tempest/api/compute/admin/test_aggregates_negative.py
@@ -40,6 +40,13 @@
for host in hosts_all if host['service'] == 'compute'])
cls.host = hosts[0]
+ def _create_test_aggregate(self):
+ aggregate_name = data_utils.rand_name(self.aggregate_name_prefix)
+ aggregate = (self.client.create_aggregate(name=aggregate_name)
+ ['aggregate'])
+ self.addCleanup(self.client.delete_aggregate, aggregate['id'])
+ return aggregate
+
@test.attr(type=['negative'])
@test.idempotent_id('86a1cb14-da37-4a70-b056-903fd56dfe29')
def test_aggregate_create_as_user(self):
@@ -70,24 +77,16 @@
@test.idempotent_id('9c23a291-b0b1-487b-b464-132e061151b3')
def test_aggregate_create_with_existent_aggregate_name(self):
# creating an aggregate with existent aggregate name is forbidden
- aggregate_name = data_utils.rand_name(self.aggregate_name_prefix)
- aggregate = self.client.create_aggregate(name=aggregate_name)
- self.addCleanup(self.client.delete_aggregate,
- aggregate['aggregate']['id'])
-
+ aggregate = self._create_test_aggregate()
self.assertRaises(lib_exc.Conflict,
self.client.create_aggregate,
- name=aggregate_name)
+ name=aggregate['name'])
@test.attr(type=['negative'])
@test.idempotent_id('cd6de795-c15d-45f1-8d9e-813c6bb72a3d')
def test_aggregate_delete_as_user(self):
# Regular user is not allowed to delete an aggregate.
- aggregate_name = data_utils.rand_name(self.aggregate_name_prefix)
- aggregate = (self.client.create_aggregate(name=aggregate_name)
- ['aggregate'])
- self.addCleanup(self.client.delete_aggregate, aggregate['id'])
-
+ aggregate = self._create_test_aggregate()
self.assertRaises(lib_exc.Forbidden,
self.user_client.delete_aggregate,
aggregate['id'])
@@ -103,11 +102,7 @@
@test.idempotent_id('557cad12-34c9-4ff4-95f0-22f0dfbaf7dc')
def test_aggregate_get_details_as_user(self):
# Regular user is not allowed to get aggregate details.
- aggregate_name = data_utils.rand_name(self.aggregate_name_prefix)
- aggregate = (self.client.create_aggregate(name=aggregate_name)
- ['aggregate'])
- self.addCleanup(self.client.delete_aggregate, aggregate['id'])
-
+ aggregate = self._create_test_aggregate()
self.assertRaises(lib_exc.Forbidden,
self.user_client.show_aggregate,
aggregate['id'])
@@ -136,12 +131,7 @@
non_exist_host = data_utils.rand_name('nonexist_host')
if non_exist_host not in hosts:
break
-
- aggregate_name = data_utils.rand_name(self.aggregate_name_prefix)
- aggregate = (self.client.create_aggregate(name=aggregate_name)
- ['aggregate'])
- self.addCleanup(self.client.delete_aggregate, aggregate['id'])
-
+ aggregate = self._create_test_aggregate()
self.assertRaises(lib_exc.NotFound, self.client.add_host,
aggregate['id'], host=non_exist_host)
@@ -149,11 +139,7 @@
@test.idempotent_id('7324c334-bd13-4c93-8521-5877322c3d51')
def test_aggregate_add_host_as_user(self):
# Regular user is not allowed to add a host to an aggregate.
- aggregate_name = data_utils.rand_name(self.aggregate_name_prefix)
- aggregate = (self.client.create_aggregate(name=aggregate_name)
- ['aggregate'])
- self.addCleanup(self.client.delete_aggregate, aggregate['id'])
-
+ aggregate = self._create_test_aggregate()
self.assertRaises(lib_exc.Forbidden,
self.user_client.add_host,
aggregate['id'], host=self.host)
@@ -162,10 +148,7 @@
@test.idempotent_id('19dd44e1-c435-4ee1-a402-88c4f90b5950')
def test_aggregate_add_existent_host(self):
self.useFixture(fixtures.LockFixture('availability_zone'))
- aggregate_name = data_utils.rand_name(self.aggregate_name_prefix)
- aggregate = (self.client.create_aggregate(name=aggregate_name)
- ['aggregate'])
- self.addCleanup(self.client.delete_aggregate, aggregate['id'])
+ aggregate = self._create_test_aggregate()
self.client.add_host(aggregate['id'], host=self.host)
self.addCleanup(self.client.remove_host, aggregate['id'],
@@ -179,10 +162,8 @@
def test_aggregate_remove_host_as_user(self):
# Regular user is not allowed to remove a host from an aggregate.
self.useFixture(fixtures.LockFixture('availability_zone'))
- aggregate_name = data_utils.rand_name(self.aggregate_name_prefix)
- aggregate = (self.client.create_aggregate(name=aggregate_name)
- ['aggregate'])
- self.addCleanup(self.client.delete_aggregate, aggregate['id'])
+ aggregate = self._create_test_aggregate()
+
self.client.add_host(aggregate['id'], host=self.host)
self.addCleanup(self.client.remove_host, aggregate['id'],
host=self.host)
@@ -194,11 +175,7 @@
@test.attr(type=['negative'])
@test.idempotent_id('95d6a6fa-8da9-4426-84d0-eec0329f2e4d')
def test_aggregate_remove_nonexistent_host(self):
- non_exist_host = data_utils.rand_name('nonexist_host')
- aggregate_name = data_utils.rand_name(self.aggregate_name_prefix)
- aggregate = (self.client.create_aggregate(name=aggregate_name)
- ['aggregate'])
- self.addCleanup(self.client.delete_aggregate, aggregate['id'])
+ aggregate = self._create_test_aggregate()
self.assertRaises(lib_exc.NotFound, self.client.remove_host,
- aggregate['id'], host=non_exist_host)
+ aggregate['id'], host='nonexist_host')
diff --git a/tempest/api/compute/admin/test_auto_allocate_network.py b/tempest/api/compute/admin/test_auto_allocate_network.py
index ee8ed14..8e481fd 100644
--- a/tempest/api/compute/admin/test_auto_allocate_network.py
+++ b/tempest/api/compute/admin/test_auto_allocate_network.py
@@ -19,8 +19,8 @@
from tempest.common import credentials_factory as credentials
from tempest.common import waiters
from tempest import config
-from tempest import exceptions
from tempest.lib.common.utils import test_utils
+from tempest.lib import exceptions as lib_excs
from tempest import test
CONF = config.CONF
@@ -66,7 +66,6 @@
@classmethod
def setup_clients(cls):
super(AutoAllocateNetworkTest, cls).setup_clients()
- cls.servers_client = cls.servers_client
cls.networks_client = cls.os.networks_client
cls.routers_client = cls.os.routers_client
cls.subnets_client = cls.os.subnets_client
@@ -83,14 +82,14 @@
nets = cls.networks_client.list_networks(
**search_opts).get('networks', [])
if nets:
- raise exceptions.TempestException(
+ raise lib_excs.TempestException(
'Found tenant networks: %s' % nets)
# (2) Retrieve shared network list.
search_opts = {'shared': True}
nets = cls.networks_client.list_networks(
**search_opts).get('networks', [])
if nets:
- raise exceptions.TempestException(
+ raise lib_excs.TempestException(
'Found shared networks: %s' % nets)
@classmethod
diff --git a/tempest/api/compute/admin/test_availability_zone.py b/tempest/api/compute/admin/test_availability_zone.py
index 5befa53..3470602 100644
--- a/tempest/api/compute/admin/test_availability_zone.py
+++ b/tempest/api/compute/admin/test_availability_zone.py
@@ -29,10 +29,10 @@
def test_get_availability_zone_list(self):
# List of availability zone
availability_zone = self.client.list_availability_zones()
- self.assertTrue(len(availability_zone['availabilityZoneInfo']) > 0)
+ self.assertGreater(len(availability_zone['availabilityZoneInfo']), 0)
@test.idempotent_id('ef726c58-530f-44c2-968c-c7bed22d5b8c')
def test_get_availability_zone_list_detail(self):
# List of availability zones and available services
availability_zone = self.client.list_availability_zones(detail=True)
- self.assertTrue(len(availability_zone['availabilityZoneInfo']) > 0)
+ self.assertGreater(len(availability_zone['availabilityZoneInfo']), 0)
diff --git a/tempest/api/compute/admin/test_flavors_extra_specs_negative.py b/tempest/api/compute/admin/test_flavors_extra_specs_negative.py
index b0ab9be..7eb4bbf 100644
--- a/tempest/api/compute/admin/test_flavors_extra_specs_negative.py
+++ b/tempest/api/compute/admin/test_flavors_extra_specs_negative.py
@@ -102,11 +102,10 @@
@test.attr(type=['negative'])
@test.idempotent_id('440b9f3f-3c7f-4293-a106-0ceda350f8de')
def test_flavor_unset_nonexistent_key(self):
- nonexistent_key = data_utils.rand_name('flavor_key')
self.assertRaises(lib_exc.NotFound,
self.client.unset_flavor_extra_spec,
self.flavor['id'],
- nonexistent_key)
+ 'nonexistent_key')
@test.attr(type=['negative'])
@test.idempotent_id('329a7be3-54b2-48be-8052-bf2ce4afd898')
diff --git a/tempest/api/compute/admin/test_floating_ips_bulk.py b/tempest/api/compute/admin/test_floating_ips_bulk.py
index e207aed..a4695b0 100644
--- a/tempest/api/compute/admin/test_floating_ips_bulk.py
+++ b/tempest/api/compute/admin/test_floating_ips_bulk.py
@@ -17,6 +17,7 @@
from tempest.api.compute import base
from tempest import config
+from tempest.lib.common.utils import test_utils
from tempest.lib import exceptions
from tempest import test
@@ -54,12 +55,6 @@
raise exceptions.InvalidConfiguration(msg)
return
- def _delete_floating_ips_bulk(self, ip_range):
- try:
- self.client.delete_floating_ips_bulk(ip_range)
- except Exception:
- pass
-
@test.idempotent_id('2c8f145f-8012-4cb8-ac7e-95a587f0e4ab')
@test.services('network')
def test_create_list_delete_floating_ips_bulk(self):
@@ -73,7 +68,8 @@
pool,
interface)
['floating_ips_bulk_create'])
- self.addCleanup(self._delete_floating_ips_bulk, self.ip_range)
+ self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+ self.client.delete_floating_ips_bulk, self.ip_range)
self.assertEqual(self.ip_range, body['ip_range'])
ips_list = self.client.list_floating_ips_bulk()['floating_ip_info']
self.assertNotEqual(0, len(ips_list))
diff --git a/tempest/api/compute/admin/test_hosts.py b/tempest/api/compute/admin/test_hosts.py
index a9e9644..3797b29 100644
--- a/tempest/api/compute/admin/test_hosts.py
+++ b/tempest/api/compute/admin/test_hosts.py
@@ -28,7 +28,7 @@
@test.idempotent_id('9bfaf98d-e2cb-44b0-a07e-2558b2821e4f')
def test_list_hosts(self):
hosts = self.client.list_hosts()['hosts']
- self.assertTrue(len(hosts) >= 2, str(hosts))
+ self.assertGreaterEqual(len(hosts), 2, str(hosts))
@test.idempotent_id('5dc06f5b-d887-47a2-bb2a-67762ef3c6de')
def test_list_hosts_with_zone(self):
@@ -36,7 +36,7 @@
hosts = self.client.list_hosts()['hosts']
host = hosts[0]
hosts = self.client.list_hosts(zone=host['zone'])['hosts']
- self.assertTrue(len(hosts) >= 1)
+ self.assertGreaterEqual(len(hosts), 1)
self.assertIn(host, hosts)
@test.idempotent_id('9af3c171-fbf4-4150-a624-22109733c2a6')
@@ -58,12 +58,12 @@
hosts = self.client.list_hosts()['hosts']
hosts = [host for host in hosts if host['service'] == 'compute']
- self.assertTrue(len(hosts) >= 1)
+ self.assertGreaterEqual(len(hosts), 1)
for host in hosts:
hostname = host['host_name']
resources = self.client.show_host(hostname)['host']
- self.assertTrue(len(resources) >= 1)
+ self.assertGreaterEqual(len(resources), 1)
host_resource = resources[0]['resource']
self.assertIsNotNone(host_resource)
self.assertIsNotNone(host_resource['cpu'])
diff --git a/tempest/api/compute/admin/test_hosts_negative.py b/tempest/api/compute/admin/test_hosts_negative.py
index 8366945..3821b22 100644
--- a/tempest/api/compute/admin/test_hosts_negative.py
+++ b/tempest/api/compute/admin/test_hosts_negative.py
@@ -13,7 +13,6 @@
# under the License.
from tempest.api.compute import base
-from tempest.common.utils import data_utils
from tempest.lib import exceptions as lib_exc
from tempest import test
@@ -27,11 +26,13 @@
cls.client = cls.os_adm.hosts_client
cls.non_admin_client = cls.os.hosts_client
- def _get_host_name(self):
- hosts = self.client.list_hosts()['hosts']
- self.assertTrue(len(hosts) >= 1)
- hostname = hosts[0]['host_name']
- return hostname
+ @classmethod
+ def resource_setup(cls):
+ super(HostsAdminNegativeTestJSON, cls).resource_setup()
+ hosts = cls.client.list_hosts()['hosts']
+ if not hosts:
+ raise lib_exc.NotFound("no host found")
+ cls.hostname = hosts[0]['host_name']
@test.attr(type=['negative'])
@test.idempotent_id('dd032027-0210-4d9c-860e-69b1b8deed5f')
@@ -42,27 +43,22 @@
@test.attr(type=['negative'])
@test.idempotent_id('e75b0a1a-041f-47a1-8b4a-b72a6ff36d3f')
def test_show_host_detail_with_nonexistent_hostname(self):
- nonexitent_hostname = data_utils.rand_name('rand_hostname')
self.assertRaises(lib_exc.NotFound,
- self.client.show_host, nonexitent_hostname)
+ self.client.show_host, 'nonexistent_hostname')
@test.attr(type=['negative'])
@test.idempotent_id('19ebe09c-bfd4-4b7c-81a2-e2e0710f59cc')
def test_show_host_detail_with_non_admin_user(self):
- hostname = self._get_host_name()
-
self.assertRaises(lib_exc.Forbidden,
self.non_admin_client.show_host,
- hostname)
+ self.hostname)
@test.attr(type=['negative'])
@test.idempotent_id('e40c72b1-0239-4ed6-ba21-81a184df1f7c')
def test_update_host_with_non_admin_user(self):
- hostname = self._get_host_name()
-
self.assertRaises(lib_exc.Forbidden,
self.non_admin_client.update_host,
- hostname,
+ self.hostname,
status='enable',
maintenance_mode='enable')
@@ -70,11 +66,9 @@
@test.idempotent_id('fbe2bf3e-3246-4a95-a59f-94e4e298ec77')
def test_update_host_with_invalid_status(self):
# 'status' can only be 'enable' or 'disable'
- hostname = self._get_host_name()
-
self.assertRaises(lib_exc.BadRequest,
self.client.update_host,
- hostname,
+ self.hostname,
status='invalid',
maintenance_mode='enable')
@@ -82,11 +76,9 @@
@test.idempotent_id('ab1e230e-5e22-41a9-8699-82b9947915d4')
def test_update_host_with_invalid_maintenance_mode(self):
# 'maintenance_mode' can only be 'enable' or 'disable'
- hostname = self._get_host_name()
-
self.assertRaises(lib_exc.BadRequest,
self.client.update_host,
- hostname,
+ self.hostname,
status='enable',
maintenance_mode='invalid')
@@ -94,73 +86,57 @@
@test.idempotent_id('0cd85f75-6992-4a4a-b1bd-d11e37fd0eee')
def test_update_host_without_param(self):
# 'status' or 'maintenance_mode' needed for host update
- hostname = self._get_host_name()
-
self.assertRaises(lib_exc.BadRequest,
self.client.update_host,
- hostname)
+ self.hostname)
@test.attr(type=['negative'])
@test.idempotent_id('23c92146-2100-4d68-b2d6-c7ade970c9c1')
def test_update_nonexistent_host(self):
- nonexitent_hostname = data_utils.rand_name('rand_hostname')
-
self.assertRaises(lib_exc.NotFound,
self.client.update_host,
- nonexitent_hostname,
+ 'nonexistent_hostname',
status='enable',
maintenance_mode='enable')
@test.attr(type=['negative'])
@test.idempotent_id('0d981ac3-4320-4898-b674-82b61fbb60e4')
def test_startup_nonexistent_host(self):
- nonexitent_hostname = data_utils.rand_name('rand_hostname')
-
self.assertRaises(lib_exc.NotFound,
self.client.startup_host,
- nonexitent_hostname)
+ 'nonexistent_hostname')
@test.attr(type=['negative'])
@test.idempotent_id('9f4ebb7e-b2ae-4e5b-a38f-0fd1bb0ddfca')
def test_startup_host_with_non_admin_user(self):
- hostname = self._get_host_name()
-
self.assertRaises(lib_exc.Forbidden,
self.non_admin_client.startup_host,
- hostname)
+ self.hostname)
@test.attr(type=['negative'])
@test.idempotent_id('9e637444-29cf-4244-88c8-831ae82c31b6')
def test_shutdown_nonexistent_host(self):
- nonexitent_hostname = data_utils.rand_name('rand_hostname')
-
self.assertRaises(lib_exc.NotFound,
self.client.shutdown_host,
- nonexitent_hostname)
+ 'nonexistent_hostname')
@test.attr(type=['negative'])
@test.idempotent_id('a803529c-7e3f-4d3c-a7d6-8e1c203d27f6')
def test_shutdown_host_with_non_admin_user(self):
- hostname = self._get_host_name()
-
self.assertRaises(lib_exc.Forbidden,
self.non_admin_client.shutdown_host,
- hostname)
+ self.hostname)
@test.attr(type=['negative'])
@test.idempotent_id('f86bfd7b-0b13-4849-ae29-0322e83ee58b')
def test_reboot_nonexistent_host(self):
- nonexitent_hostname = data_utils.rand_name('rand_hostname')
-
self.assertRaises(lib_exc.NotFound,
self.client.reboot_host,
- nonexitent_hostname)
+ 'nonexistent_hostname')
@test.attr(type=['negative'])
@test.idempotent_id('02d79bb9-eb57-4612-abf6-2cb38897d2f8')
def test_reboot_host_with_non_admin_user(self):
- hostname = self._get_host_name()
-
self.assertRaises(lib_exc.Forbidden,
self.non_admin_client.reboot_host,
- hostname)
+ self.hostname)
diff --git a/tempest/api/compute/admin/test_hypervisor.py b/tempest/api/compute/admin/test_hypervisor.py
index 113ec40..1f043dc 100644
--- a/tempest/api/compute/admin/test_hypervisor.py
+++ b/tempest/api/compute/admin/test_hypervisor.py
@@ -31,7 +31,7 @@
return hypers
def assertHypervisors(self, hypers):
- self.assertTrue(len(hypers) > 0, "No hypervisors found: %s" % hypers)
+ self.assertGreater(len(hypers), 0, "No hypervisors found: %s" % hypers)
@test.idempotent_id('7f0ceacd-c64d-4e96-b8ee-d02943142cc5')
def test_get_hypervisor_list(self):
@@ -52,7 +52,7 @@
self.assertHypervisors(hypers)
details = self.client.show_hypervisor(hypers[0]['id'])['hypervisor']
- self.assertTrue(len(details) > 0)
+ self.assertGreater(len(details), 0)
self.assertEqual(details['hypervisor_hostname'],
hypers[0]['hypervisor_hostname'])
@@ -65,14 +65,14 @@
hostname = hypers[0]['hypervisor_hostname']
hypervisors = (self.client.list_servers_on_hypervisor(hostname)
['hypervisors'])
- self.assertTrue(len(hypervisors) > 0)
+ self.assertGreater(len(hypervisors), 0)
@test.idempotent_id('797e4f28-b6e0-454d-a548-80cc77c00816')
def test_get_hypervisor_stats(self):
# Verify the stats of the all hypervisor
stats = (self.client.show_hypervisor_statistics()
['hypervisor_statistics'])
- self.assertTrue(len(stats) > 0)
+ self.assertGreater(len(stats), 0)
@test.idempotent_id('91a50d7d-1c2b-4f24-b55a-a1fe20efca70')
def test_get_hypervisor_uptime(self):
@@ -87,7 +87,7 @@
ironic_only = True
hypers_without_ironic = []
for hyper in hypers:
- details = (self.client.show_hypervisor(hypers[0]['id'])
+ details = (self.client.show_hypervisor(hyper['id'])
['hypervisor'])
if details['hypervisor_type'] != 'ironic':
hypers_without_ironic.append(hyper)
diff --git a/tempest/api/compute/admin/test_hypervisor_negative.py b/tempest/api/compute/admin/test_hypervisor_negative.py
index 9c6df7f..0f35e14 100644
--- a/tempest/api/compute/admin/test_hypervisor_negative.py
+++ b/tempest/api/compute/admin/test_hypervisor_negative.py
@@ -47,7 +47,7 @@
@test.idempotent_id('51e663d0-6b89-4817-a465-20aca0667d03')
def test_show_hypervisor_with_non_admin_user(self):
hypers = self._list_hypervisors()
- self.assertTrue(len(hypers) > 0)
+ self.assertGreater(len(hypers), 0)
self.assertRaises(
lib_exc.Forbidden,
@@ -58,7 +58,7 @@
@test.idempotent_id('2a0a3938-832e-4859-95bf-1c57c236b924')
def test_show_servers_with_non_admin_user(self):
hypers = self._list_hypervisors()
- self.assertTrue(len(hypers) > 0)
+ self.assertGreater(len(hypers), 0)
self.assertRaises(
lib_exc.Forbidden,
@@ -96,7 +96,7 @@
@test.idempotent_id('6c3461f9-c04c-4e2a-bebb-71dc9cb47df2')
def test_get_hypervisor_uptime_with_non_admin_user(self):
hypers = self._list_hypervisors()
- self.assertTrue(len(hypers) > 0)
+ self.assertGreater(len(hypers), 0)
self.assertRaises(
lib_exc.Forbidden,
@@ -122,18 +122,16 @@
@test.attr(type=['negative'])
@test.idempotent_id('19a45cc1-1000-4055-b6d2-28e8b2ec4faa')
def test_search_nonexistent_hypervisor(self):
- nonexistent_hypervisor_name = data_utils.rand_name('test_hypervisor')
-
self.assertRaises(
lib_exc.NotFound,
self.client.search_hypervisor,
- nonexistent_hypervisor_name)
+ 'nonexistent_hypervisor_name')
@test.attr(type=['negative'])
@test.idempotent_id('5b6a6c79-5dc1-4fa5-9c58-9c8085948e74')
def test_search_hypervisor_with_non_admin_user(self):
hypers = self._list_hypervisors()
- self.assertTrue(len(hypers) > 0)
+ self.assertGreater(len(hypers), 0)
self.assertRaises(
lib_exc.Forbidden,
diff --git a/tempest/api/compute/admin/test_migrations.py b/tempest/api/compute/admin/test_migrations.py
index 4f075eb..c9ba730 100644
--- a/tempest/api/compute/admin/test_migrations.py
+++ b/tempest/api/compute/admin/test_migrations.py
@@ -47,12 +47,7 @@
server = self.create_test_server(wait_until="ACTIVE")
server_id = server['id']
- self.servers_client.resize_server(server_id, self.flavor_ref_alt)
- waiters.wait_for_server_status(self.servers_client,
- server_id, 'VERIFY_RESIZE')
- self.servers_client.confirm_resize_server(server_id)
- waiters.wait_for_server_status(self.servers_client,
- server_id, 'ACTIVE')
+ self.resize_server(server_id, self.flavor_ref_alt)
body = self.client.list_migrations()['migrations']
diff --git a/tempest/api/compute/admin/test_networks.py b/tempest/api/compute/admin/test_networks.py
index e5c8790..8504840 100644
--- a/tempest/api/compute/admin/test_networks.py
+++ b/tempest/api/compute/admin/test_networks.py
@@ -42,8 +42,12 @@
"{0} networks with label {1}".format(
len(configured_network),
CONF.compute.fixed_network_name))
+ elif CONF.network.public_network_id:
+ configured_network = [x for x in networks if x['id'] ==
+ CONF.network.public_network_id]
else:
- configured_network = networks
+ raise self.skipException(
+ "Environment has no known-for-sure existing network.")
configured_network = configured_network[0]
network = (self.client.show_network(configured_network['id'])
['network'])
diff --git a/tempest/api/compute/admin/test_quotas.py b/tempest/api/compute/admin/test_quotas.py
index 7d97ce2..ce0adb4 100644
--- a/tempest/api/compute/admin/test_quotas.py
+++ b/tempest/api/compute/admin/test_quotas.py
@@ -74,7 +74,8 @@
'ram': 10240, 'floating_ips': 20, 'fixed_ips': 10,
'key_pairs': 200, 'injected_file_path_bytes': 512,
'instances': 20, 'security_group_rules': 20,
- 'cores': 2, 'security_groups': 20}
+ 'cores': 2, 'security_groups': 20,
+ 'server_groups': 20, 'server_group_members': 20}
# Update limits for all quota resources
quota_set = self.adm_client.update_quota_set(
self.demo_tenant_id,
@@ -82,13 +83,6 @@
**new_quota_set)['quota_set']
default_quota_set.pop('id')
- # NOTE(PhilDay) The following is safe as we're not updating these
- # two quota values yet. Once the Nova change to add these is merged
- # and the client updated to support them this can be removed
- if 'server_groups' in default_quota_set:
- default_quota_set.pop('server_groups')
- if 'server_group_members' in default_quota_set:
- default_quota_set.pop('server_group_members')
self.addCleanup(self.adm_client.update_quota_set,
self.demo_tenant_id, **default_quota_set)
for quota in new_quota_set:
diff --git a/tempest/api/compute/admin/test_quotas_negative.py b/tempest/api/compute/admin/test_quotas_negative.py
index d6aba5b..015e14d 100644
--- a/tempest/api/compute/admin/test_quotas_negative.py
+++ b/tempest/api/compute/admin/test_quotas_negative.py
@@ -40,6 +40,17 @@
# tenant most of them should be skipped if we can't do that
cls.demo_tenant_id = cls.client.tenant_id
+ def _update_quota(self, quota_item, quota_value):
+ quota_set = (self.adm_client.show_quota_set(self.demo_tenant_id)
+ ['quota_set'])
+ default_quota_value = quota_set[quota_item]
+
+ self.adm_client.update_quota_set(self.demo_tenant_id,
+ force=True,
+ **{quota_item: quota_value})
+ self.addCleanup(self.adm_client.update_quota_set, self.demo_tenant_id,
+ **{quota_item: default_quota_value})
+
@test.attr(type=['negative'])
@test.idempotent_id('733abfe8-166e-47bb-8363-23dbd7ff3476')
def test_update_quota_normal_user(self):
@@ -54,17 +65,7 @@
@test.idempotent_id('91058876-9947-4807-9f22-f6eb17140d9b')
def test_create_server_when_cpu_quota_is_full(self):
# Disallow server creation when tenant's vcpu quota is full
- quota_set = (self.adm_client.show_quota_set(self.demo_tenant_id)
- ['quota_set'])
- default_vcpu_quota = quota_set['cores']
- vcpu_quota = 0 # Set the quota to zero to conserve resources
-
- self.adm_client.update_quota_set(self.demo_tenant_id,
- force=True,
- cores=vcpu_quota)
-
- self.addCleanup(self.adm_client.update_quota_set, self.demo_tenant_id,
- cores=default_vcpu_quota)
+ self._update_quota('cores', 0)
self.assertRaises((lib_exc.Forbidden, lib_exc.OverLimit),
self.create_test_server)
@@ -72,17 +73,7 @@
@test.idempotent_id('6fdd7012-584d-4327-a61c-49122e0d5864')
def test_create_server_when_memory_quota_is_full(self):
# Disallow server creation when tenant's memory quota is full
- quota_set = (self.adm_client.show_quota_set(self.demo_tenant_id)
- ['quota_set'])
- default_mem_quota = quota_set['ram']
- mem_quota = 0 # Set the quota to zero to conserve resources
-
- self.adm_client.update_quota_set(self.demo_tenant_id,
- force=True,
- ram=mem_quota)
-
- self.addCleanup(self.adm_client.update_quota_set, self.demo_tenant_id,
- ram=default_mem_quota)
+ self._update_quota('ram', 0)
self.assertRaises((lib_exc.Forbidden, lib_exc.OverLimit),
self.create_test_server)
@@ -90,16 +81,7 @@
@test.idempotent_id('7c6be468-0274-449a-81c3-ac1c32ee0161')
def test_create_server_when_instances_quota_is_full(self):
# Once instances quota limit is reached, disallow server creation
- quota_set = (self.adm_client.show_quota_set(self.demo_tenant_id)
- ['quota_set'])
- default_instances_quota = quota_set['instances']
- instances_quota = 0 # Set quota to zero to disallow server creation
-
- self.adm_client.update_quota_set(self.demo_tenant_id,
- force=True,
- instances=instances_quota)
- self.addCleanup(self.adm_client.update_quota_set, self.demo_tenant_id,
- instances=default_instances_quota)
+ self._update_quota('instances', 0)
self.assertRaises((lib_exc.Forbidden, lib_exc.OverLimit),
self.create_test_server)
@@ -109,22 +91,10 @@
@test.services('network')
def test_security_groups_exceed_limit(self):
# Negative test: Creation Security Groups over limit should FAIL
-
- quota_set = (self.adm_client.show_quota_set(self.demo_tenant_id)
- ['quota_set'])
- default_sg_quota = quota_set['security_groups']
-
# Set the quota to number of used security groups
sg_quota = self.limits_client.show_limits()['limits']['absolute'][
'totalSecurityGroupsUsed']
-
- self.adm_client.update_quota_set(self.demo_tenant_id,
- force=True,
- security_groups=sg_quota)
-
- self.addCleanup(self.adm_client.update_quota_set,
- self.demo_tenant_id,
- security_groups=default_sg_quota)
+ self._update_quota('security_groups', sg_quota)
# Check we cannot create anymore
# A 403 Forbidden or 413 Overlimit (old behaviour) exception
@@ -141,19 +111,7 @@
def test_security_groups_rules_exceed_limit(self):
# Negative test: Creation of Security Group Rules should FAIL
# when we reach limit maxSecurityGroupRules
-
- quota_set = (self.adm_client.show_quota_set(self.demo_tenant_id)
- ['quota_set'])
- default_sg_rules_quota = quota_set['security_group_rules']
- sg_rules_quota = 0 # Set the quota to zero to conserve resources
-
- self.adm_client.update_quota_set(self.demo_tenant_id,
- force=True,
- security_group_rules=sg_rules_quota)
-
- self.addCleanup(self.adm_client.update_quota_set,
- self.demo_tenant_id,
- security_group_rules=default_sg_rules_quota)
+ self._update_quota('security_group_rules', 0)
s_name = data_utils.rand_name('securitygroup')
s_description = data_utils.rand_name('description')
diff --git a/tempest/api/compute/admin/test_volumes_negative.py b/tempest/api/compute/admin/test_volumes_negative.py
new file mode 100644
index 0000000..b9dac6f
--- /dev/null
+++ b/tempest/api/compute/admin/test_volumes_negative.py
@@ -0,0 +1,61 @@
+# Copyright 2016 NEC 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.
+
+from tempest.api.compute import base
+from tempest.common.utils import data_utils
+from tempest import config
+from tempest.lib import exceptions as lib_exc
+from tempest import test
+
+CONF = config.CONF
+
+
+class VolumesAdminNegativeTest(base.BaseV2ComputeAdminTest):
+
+ @classmethod
+ def skip_checks(cls):
+ super(VolumesAdminNegativeTest, cls).skip_checks()
+ if not CONF.service_available.cinder:
+ skip_msg = ("%s skipped as Cinder is not available" % cls.__name__)
+ raise cls.skipException(skip_msg)
+
+ @classmethod
+ def setup_clients(cls):
+ super(VolumesAdminNegativeTest, cls).setup_clients()
+ cls.servers_admin_client = cls.os_adm.servers_client
+
+ @classmethod
+ def resource_setup(cls):
+ super(VolumesAdminNegativeTest, cls).resource_setup()
+ cls.server = cls.create_test_server(wait_until='ACTIVE')
+
+ @test.idempotent_id('309b5ecd-0585-4a7e-a36f-d2b2bf55259d')
+ def test_update_attached_volume_with_nonexistent_volume_in_uri(self):
+ volume = self.create_volume()
+ nonexistent_volume = data_utils.rand_uuid()
+ self.assertRaises(lib_exc.NotFound,
+ self.servers_admin_client.update_attached_volume,
+ self.server['id'], nonexistent_volume,
+ volumeId=volume['id'])
+
+ @test.idempotent_id('7dcac15a-b107-46d3-a5f6-cb863f4e454a')
+ def test_update_attached_volume_with_nonexistent_volume_in_body(self):
+ volume = self.create_volume()
+ self.attach_volume(self.server, volume)
+
+ nonexistent_volume = data_utils.rand_uuid()
+ self.assertRaises(lib_exc.BadRequest,
+ self.servers_admin_client.update_attached_volume,
+ self.server['id'], volume['id'],
+ volumeId=nonexistent_volume)
diff --git a/tempest/api/compute/base.py b/tempest/api/compute/base.py
index b738e82..173ee83 100644
--- a/tempest/api/compute/base.py
+++ b/tempest/api/compute/base.py
@@ -75,7 +75,6 @@
cls.os.compute_security_group_rules_client)
cls.security_groups_client = cls.os.compute_security_groups_client
cls.quotas_client = cls.os.quotas_client
- cls.quota_classes_client = cls.os.quota_classes_client
cls.compute_networks_client = cls.os.compute_networks_client
cls.limits_client = cls.os.limits_client
cls.volumes_extensions_client = cls.os.volumes_extensions_client
@@ -345,6 +344,15 @@
LOG.exception('Failed to delete server %s' % server_id)
@classmethod
+ def resize_server(cls, 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,
+ 'VERIFY_RESIZE')
+ cls.servers_client.confirm_resize_server(server_id)
+ waiters.wait_for_server_status(cls.servers_client, server_id, 'ACTIVE')
+
+ @classmethod
def delete_volume(cls, volume_id):
"""Deletes the given volume and waits for it to be gone."""
cls._delete_volume(cls.volumes_extensions_client, volume_id)
@@ -365,7 +373,7 @@
return address['addr']
raise exceptions.ServerUnreachable(server_id=server['id'])
else:
- raise exceptions.InvalidConfiguration()
+ raise lib_exc.InvalidConfiguration()
def setUp(self):
super(BaseV2ComputeTest, self).setUp()
@@ -373,14 +381,18 @@
self.request_microversion))
@classmethod
- def create_volume(cls):
+ def create_volume(cls, image_ref=None):
"""Create a volume and wait for it to become 'available'.
+ :param image_ref: Specify an image id to create a bootable volume.
:returns: The available volume.
"""
vol_name = data_utils.rand_name(cls.__name__ + '-volume')
- volume = cls.volumes_client.create_volume(
- size=CONF.volume.volume_size, display_name=vol_name)['volume']
+ create_params = dict(size=CONF.volume.volume_size,
+ display_name=vol_name)
+ if image_ref is not None:
+ create_params['imageRef'] = image_ref
+ volume = cls.volumes_client.create_volume(**create_params)['volume']
cls.volumes.append(volume)
waiters.wait_for_volume_status(cls.volumes_client,
volume['id'], 'available')
diff --git a/tempest/api/compute/floating_ips/test_floating_ips_actions.py b/tempest/api/compute/floating_ips/test_floating_ips_actions.py
index fdf1e93..dcb2d2c 100644
--- a/tempest/api/compute/floating_ips/test_floating_ips_actions.py
+++ b/tempest/api/compute/floating_ips/test_floating_ips_actions.py
@@ -14,7 +14,6 @@
# under the License.
from tempest.api.compute.floating_ips import base
-from tempest.common import waiters
from tempest import config
from tempest.lib.common.utils import test_utils
from tempest.lib import exceptions as lib_exc
@@ -111,9 +110,7 @@
# positive test:Association of an already associated floating IP
# to specific server should change the association of the Floating IP
# Create server so as to use for Multiple association
- body = self.create_test_server()
- waiters.wait_for_server_status(self.servers_client,
- body['id'], 'ACTIVE')
+ body = self.create_test_server(wait_until='ACTIVE')
self.new_server_id = body['id']
self.addCleanup(self.servers_client.delete_server, self.new_server_id)
diff --git a/tempest/api/compute/floating_ips/test_floating_ips_actions_negative.py b/tempest/api/compute/floating_ips/test_floating_ips_actions_negative.py
index f71f046..31cf39c 100644
--- a/tempest/api/compute/floating_ips/test_floating_ips_actions_negative.py
+++ b/tempest/api/compute/floating_ips/test_floating_ips_actions_negative.py
@@ -23,7 +23,6 @@
class FloatingIPsNegativeTestJSON(base.BaseFloatingIPsTest):
- server_id = None
@classmethod
def setup_clients(cls):
@@ -38,15 +37,14 @@
server = cls.create_test_server(wait_until='ACTIVE')
cls.server_id = server['id']
# Generating a nonexistent floatingIP id
- cls.floating_ip_ids = []
body = cls.client.list_floating_ips()['floating_ips']
- for i in range(len(body)):
- cls.floating_ip_ids.append(body[i]['id'])
+ floating_ip_ids = [floating_ip['id'] for floating_ip in body]
while True:
- cls.non_exist_id = data_utils.rand_int_id(start=999)
if CONF.service_available.neutron:
cls.non_exist_id = data_utils.rand_uuid()
- if cls.non_exist_id not in cls.floating_ip_ids:
+ else:
+ cls.non_exist_id = data_utils.rand_int_id(start=999)
+ if cls.non_exist_id not in floating_ip_ids:
break
@test.attr(type=['negative'])
diff --git a/tempest/api/compute/images/test_image_metadata.py b/tempest/api/compute/images/test_image_metadata.py
index 999233d..26d4efe 100644
--- a/tempest/api/compute/images/test_image_metadata.py
+++ b/tempest/api/compute/images/test_image_metadata.py
@@ -20,7 +20,7 @@
from tempest.common.utils import data_utils
from tempest.common import waiters
from tempest import config
-from tempest import exceptions
+from tempest.lib import exceptions
from tempest import test
CONF = config.CONF
diff --git a/tempest/api/compute/images/test_image_metadata_negative.py b/tempest/api/compute/images/test_image_metadata_negative.py
index 9cb9e03..489bfbc 100644
--- a/tempest/api/compute/images/test_image_metadata_negative.py
+++ b/tempest/api/compute/images/test_image_metadata_negative.py
@@ -19,11 +19,11 @@
from tempest import test
-class ImagesMetadataTestJSON(base.BaseV2ComputeTest):
+class ImagesMetadataNegativeTestJSON(base.BaseV2ComputeTest):
@classmethod
def setup_clients(cls):
- super(ImagesMetadataTestJSON, cls).setup_clients()
+ super(ImagesMetadataNegativeTestJSON, cls).setup_clients()
cls.client = cls.compute_images_client
@test.attr(type=['negative'])
diff --git a/tempest/api/compute/images/test_images.py b/tempest/api/compute/images/test_images.py
index 3754637..a06f4a7 100644
--- a/tempest/api/compute/images/test_images.py
+++ b/tempest/api/compute/images/test_images.py
@@ -38,15 +38,12 @@
def setup_clients(cls):
super(ImagesTestJSON, cls).setup_clients()
cls.client = cls.compute_images_client
- cls.servers_client = cls.servers_client
@test.idempotent_id('aa06b52b-2db5-4807-b218-9441f75d74e3')
def test_delete_saving_image(self):
- snapshot_name = data_utils.rand_name('test-snap')
server = self.create_test_server(wait_until='ACTIVE')
self.addCleanup(self.servers_client.delete_server, server['id'])
image = self.create_image_from_server(server['id'],
- name=snapshot_name,
wait_until='SAVING')
self.client.delete_image(image['id'])
msg = ('The image with ID {image_id} failed to be deleted'
diff --git a/tempest/api/compute/images/test_images_negative.py b/tempest/api/compute/images/test_images_negative.py
index e91944f..c48367f 100644
--- a/tempest/api/compute/images/test_images_negative.py
+++ b/tempest/api/compute/images/test_images_negative.py
@@ -39,7 +39,6 @@
def setup_clients(cls):
super(ImagesNegativeTestJSON, cls).setup_clients()
cls.client = cls.compute_images_client
- cls.servers_client = cls.servers_client
@test.attr(type=['negative'])
@test.idempotent_id('6cd5a89d-5b47-46a7-93bc-3916f0d84973')
@@ -64,8 +63,6 @@
# Create a new image with invalid server id
name = data_utils.rand_name('image')
meta = {'image_type': 'test'}
- resp = {}
- resp['status'] = None
self.assertRaises(lib_exc.NotFound, self.create_image_from_server,
'!@$^&*()', name=name, meta=meta)
@@ -99,7 +96,7 @@
def test_delete_non_existent_image(self):
# Return an error while trying to delete a non-existent image
- non_existent_image_id = '11a22b9-12a9-5555-cc11-00ab112223fa'
+ non_existent_image_id = data_utils.rand_uuid()
self.assertRaises(lib_exc.NotFound, self.client.delete_image,
non_existent_image_id)
@@ -113,9 +110,9 @@
@test.idempotent_id('924540c3-f1f1-444c-8f58-718958b6724e')
def test_delete_image_non_hex_string_id(self):
# Return an error while trying to delete an image with non hex id
- image_id = '11a22b9-120q-5555-cc11-00ab112223gj'
+ invalid_image_id = data_utils.rand_uuid()[:-1] + "j"
self.assertRaises(lib_exc.NotFound, self.client.delete_image,
- image_id)
+ invalid_image_id)
@test.attr(type=['negative'])
@test.idempotent_id('68e2c175-bd26-4407-ac0f-4ea9ce2139ea')
@@ -125,7 +122,8 @@
@test.attr(type=['negative'])
@test.idempotent_id('b340030d-82cd-4066-a314-c72fb7c59277')
- def test_delete_image_id_is_over_35_character_limit(self):
+ def test_delete_image_with_id_over_character_limit(self):
# Return an error while trying to delete image with id over limit
+ invalid_image_id = data_utils.rand_uuid() + "1"
self.assertRaises(lib_exc.NotFound, self.client.delete_image,
- '11a22b9-12a9-5555-cc11-00ab112223fa-3fac')
+ invalid_image_id)
diff --git a/tempest/api/compute/images/test_images_oneserver.py b/tempest/api/compute/images/test_images_oneserver.py
index 6c417f1..19e2880 100644
--- a/tempest/api/compute/images/test_images_oneserver.py
+++ b/tempest/api/compute/images/test_images_oneserver.py
@@ -45,35 +45,18 @@
super(ImagesOneServerTestJSON, cls).setup_clients()
cls.client = cls.compute_images_client
- @classmethod
- def resource_setup(cls):
- super(ImagesOneServerTestJSON, cls).resource_setup()
- server = cls.create_test_server(wait_until='ACTIVE')
- cls.server_id = server['id']
-
def _get_default_flavor_disk_size(self, flavor_id):
flavor = self.flavors_client.show_flavor(flavor_id)['flavor']
return flavor['disk']
- @classmethod
- def _rebuild_server_when_fails(cls, server_id):
- try:
- waiters.wait_for_server_status(cls.servers_client,
- server_id, 'ACTIVE')
- except Exception:
- LOG.exception('server %s timed out to become ACTIVE. rebuilding'
- % server_id)
- # Rebuild server if cannot reach the ACTIVE state
- # Usually it means the server had a serious accident
- cls.server_id = cls.rebuild_server(server_id)
-
@test.idempotent_id('3731d080-d4c5-4872-b41a-64d0d0021314')
def test_create_delete_image(self):
+ server_id = self.create_test_server(wait_until='ACTIVE')['id']
# Create a new image
name = data_utils.rand_name('image')
meta = {'image_type': 'test'}
- body = self.client.create_image(self.server_id, name=name,
+ body = self.client.create_image(server_id, name=name,
metadata=meta)
image_id = data_utils.parse_image_id(body.response['location'])
self.addCleanup(test_utils.call_and_ignore_notfound_exc,
@@ -98,10 +81,11 @@
# Verify the image was deleted correctly
self.client.delete_image(image_id)
self.client.wait_for_resource_deletion(image_id)
- self.addCleanup(self._rebuild_server_when_fails, self.server_id)
@test.idempotent_id('3b7c6fe4-dfe7-477c-9243-b06359db51e6')
def test_create_image_specify_multibyte_character_image_name(self):
+ server_id = self.create_test_server(wait_until='ACTIVE')['id']
+
# prefix character is:
# http://www.fileformat.info/info/unicode/char/1F4A9/index.htm
@@ -109,7 +93,6 @@
# #1370954 in glance which will 500 if mysql is used as the
# backend and it attempts to store a 4 byte utf-8 character
utf8_name = data_utils.rand_name('\xe2\x82\xa1')
- body = self.client.create_image(self.server_id, name=utf8_name)
+ body = self.client.create_image(server_id, name=utf8_name)
image_id = data_utils.parse_image_id(body.response['location'])
self.addCleanup(self.client.delete_image, image_id)
- self.addCleanup(self._rebuild_server_when_fails, self.server_id)
diff --git a/tempest/api/compute/images/test_images_oneserver_negative.py b/tempest/api/compute/images/test_images_oneserver_negative.py
index d9b80e1..039283a 100644
--- a/tempest/api/compute/images/test_images_oneserver_negative.py
+++ b/tempest/api/compute/images/test_images_oneserver_negative.py
@@ -69,11 +69,6 @@
raise cls.skipException(skip_msg)
@classmethod
- def setup_credentials(cls):
- cls.prepare_instance_network()
- super(ImagesOneServerNegativeTestJSON, cls).setup_credentials()
-
- @classmethod
def setup_clients(cls):
super(ImagesOneServerNegativeTestJSON, cls).setup_clients()
cls.client = cls.compute_images_client
diff --git a/tempest/api/compute/keypairs/test_keypairs.py b/tempest/api/compute/keypairs/test_keypairs.py
index be6f615..562a508 100644
--- a/tempest/api/compute/keypairs/test_keypairs.py
+++ b/tempest/api/compute/keypairs/test_keypairs.py
@@ -57,8 +57,8 @@
self.assertEqual(key_name, k_name,
"The created keypair name is not equal "
"to the requested name")
- self.assertTrue(private_key is not None,
- "Field private_key is empty or not found.")
+ self.assertIsNotNone(private_key,
+ "Field private_key is empty or not found.")
@test.idempotent_id('a4233d5d-52d8-47cc-9a25-e1864527e3df')
def test_get_keypair_detail(self):
@@ -72,8 +72,8 @@
"The created keypair name is not equal "
"to requested name")
public_key = keypair_detail['public_key']
- self.assertTrue(public_key is not None,
- "Field public_key is empty or not found.")
+ self.assertIsNotNone(public_key,
+ "Field public_key is empty or not found.")
@test.idempotent_id('39c90c6a-304a-49dd-95ec-2366129def05')
def test_keypair_create_with_pub_key(self):
@@ -89,7 +89,7 @@
"XcPojYN56tI0OlrGqojbediJYD0rUsJu4weZpbn8vilb3JuDY+jws"
"snSA8wzBx3A/8y9Pp1B nova@ubuntu")
keypair = self._create_keypair(k_name, pub_key)
- self.assertFalse('private_key' in keypair,
+ self.assertNotIn('private_key', keypair,
"Field private_key is not empty!")
key_name = keypair['name']
self.assertEqual(key_name, k_name,
diff --git a/tempest/api/compute/limits/test_absolute_limits.py b/tempest/api/compute/limits/test_absolute_limits.py
index 69811f4..6cc722c 100644
--- a/tempest/api/compute/limits/test_absolute_limits.py
+++ b/tempest/api/compute/limits/test_absolute_limits.py
@@ -35,9 +35,10 @@
'maxTotalFloatingIps', 'maxSecurityGroups',
'maxSecurityGroupRules', 'maxTotalInstances',
'maxTotalKeypairs', 'maxTotalRAMSize',
+ 'maxServerGroups', 'maxServerGroupMembers',
'totalCoresUsed', 'totalFloatingIpsUsed',
'totalSecurityGroupsUsed', 'totalInstancesUsed',
- 'totalRAMUsed']
+ 'totalRAMUsed', 'totalServerGroupsUsed']
# check whether all expected elements exist
missing_elements =\
[ele for ele in expected_elements if ele not in absolute_limits]
diff --git a/tempest/api/compute/security_groups/test_security_groups.py b/tempest/api/compute/security_groups/test_security_groups.py
index 755336f..4184afa 100644
--- a/tempest/api/compute/security_groups/test_security_groups.py
+++ b/tempest/api/compute/security_groups/test_security_groups.py
@@ -94,10 +94,8 @@
# Create server and add the security group created
# above to the server we just created
- server = self.create_test_server()
+ server = self.create_test_server(wait_until='ACTIVE')
server_id = server['id']
- waiters.wait_for_server_status(self.servers_client, server_id,
- 'ACTIVE')
self.servers_client.add_security_group(server_id, name=sg['name'])
# Check that we are not able to delete the security
diff --git a/tempest/api/compute/servers/test_attach_interfaces.py b/tempest/api/compute/servers/test_attach_interfaces.py
index b936b23..8342a3e 100644
--- a/tempest/api/compute/servers/test_attach_interfaces.py
+++ b/tempest/api/compute/servers/test_attach_interfaces.py
@@ -20,7 +20,6 @@
from tempest.common.utils import net_utils
from tempest.common import waiters
from tempest import config
-from tempest import exceptions
from tempest.lib import decorators
from tempest.lib import exceptions as lib_exc
from tempest import test
@@ -51,30 +50,6 @@
cls.subnets_client = cls.os.subnets_client
cls.ports_client = cls.os.ports_client
- def wait_for_interface_status(self, server, port_id, status):
- """Waits for an interface to reach a given status."""
- body = (self.interfaces_client.show_interface(server, port_id)
- ['interfaceAttachment'])
- interface_status = body['port_state']
- start = int(time.time())
-
- while(interface_status != status):
- time.sleep(self.build_interval)
- body = (self.interfaces_client.show_interface(server, port_id)
- ['interfaceAttachment'])
- interface_status = body['port_state']
-
- timed_out = int(time.time()) - start >= self.build_timeout
-
- if interface_status != status and timed_out:
- message = ('Interface %s failed to reach %s status '
- '(current %s) within the required time (%s s).' %
- (port_id, status, interface_status,
- self.build_timeout))
- raise exceptions.TimeoutException(message)
-
- return body
-
# TODO(mriedem): move this into a common waiters utility module
def wait_for_port_detach(self, port_id):
"""Waits for the port's device_id to be unset.
@@ -99,7 +74,7 @@
message = ('Port %s failed to detach (device_id %s) within '
'the required time (%s s).' %
(port_id, device_id, self.build_timeout))
- raise exceptions.TimeoutException(message)
+ raise lib_exc.TimeoutException(message)
return port
@@ -119,16 +94,16 @@
server = self.create_test_server(wait_until='ACTIVE')
ifs = (self.interfaces_client.list_interfaces(server['id'])
['interfaceAttachments'])
- body = self.wait_for_interface_status(
- server['id'], ifs[0]['port_id'], 'ACTIVE')
+ body = waiters.wait_for_interface_status(
+ self.interfaces_client, server['id'], ifs[0]['port_id'], 'ACTIVE')
ifs[0]['port_state'] = body['port_state']
return server, ifs
def _test_create_interface(self, server):
iface = (self.interfaces_client.create_interface(server['id'])
['interfaceAttachment'])
- iface = self.wait_for_interface_status(
- server['id'], iface['port_id'], 'ACTIVE')
+ iface = waiters.wait_for_interface_status(
+ self.interfaces_client, server['id'], iface['port_id'], 'ACTIVE')
self._check_interface(iface)
return iface
@@ -136,8 +111,8 @@
network_id = ifs[0]['net_id']
iface = self.interfaces_client.create_interface(
server['id'], net_id=network_id)['interfaceAttachment']
- iface = self.wait_for_interface_status(
- server['id'], iface['port_id'], 'ACTIVE')
+ iface = waiters.wait_for_interface_status(
+ self.interfaces_client, server['id'], iface['port_id'], 'ACTIVE')
self._check_interface(iface, network_id=network_id)
return iface
@@ -148,8 +123,8 @@
self.addCleanup(self.ports_client.delete_port, port_id)
iface = self.interfaces_client.create_interface(
server['id'], port_id=port_id)['interfaceAttachment']
- iface = self.wait_for_interface_status(
- server['id'], iface['port_id'], 'ACTIVE')
+ iface = waiters.wait_for_interface_status(
+ self.interfaces_client, server['id'], iface['port_id'], 'ACTIVE')
self._check_interface(iface, port_id=port_id)
return iface
@@ -167,8 +142,8 @@
server['id'], net_id=network_id,
fixed_ips=fixed_ips)['interfaceAttachment']
self.addCleanup(self.ports_client.delete_port, iface['port_id'])
- iface = self.wait_for_interface_status(
- server['id'], iface['port_id'], 'ACTIVE')
+ iface = waiters.wait_for_interface_status(
+ self.interfaces_client, server['id'], iface['port_id'], 'ACTIVE')
self._check_interface(iface, fixed_ip=ip_list[0])
return iface
@@ -197,7 +172,7 @@
if len(ifs) == len(_ifs) and timed_out:
message = ('Failed to delete interface within '
'the required time: %s sec.' % self.build_timeout)
- raise exceptions.TimeoutException(message)
+ raise lib_exc.TimeoutException(message)
self.assertNotIn(iface['port_id'], [i['port_id'] for i in _ifs])
return _ifs
@@ -215,7 +190,7 @@
def test_create_list_show_delete_interfaces(self):
server, ifs = self._create_server_get_interfaces()
interface_count = len(ifs)
- self.assertTrue(interface_count > 0)
+ self.assertGreater(interface_count, 0)
self._check_interface(ifs[0])
try:
@@ -253,7 +228,7 @@
# Add and Remove the fixed IP to server.
server, ifs = self._create_server_get_interfaces()
interface_count = len(ifs)
- self.assertTrue(interface_count > 0)
+ self.assertGreater(interface_count, 0)
self._check_interface(ifs[0])
network_id = ifs[0]['net_id']
self.servers_client.add_fixed_ip(server['id'], networkId=network_id)
diff --git a/tempest/api/compute/servers/test_create_server.py b/tempest/api/compute/servers/test_create_server.py
index 78f0db4..2f43157 100644
--- a/tempest/api/compute/servers/test_create_server.py
+++ b/tempest/api/compute/servers/test_create_server.py
@@ -142,16 +142,12 @@
self.assertEqual(self.name.lower(), hostname, msg)
@test.idempotent_id('ed20d3fb-9d1f-4329-b160-543fbd5d9811')
+ @testtools.skipUnless(
+ test.is_scheduler_filter_enabled("ServerGroupAffinityFilter"),
+ 'ServerGroupAffinityFilter is not available.')
def test_create_server_with_scheduler_hint_group(self):
# Create a server with the scheduler hint "group".
- name = data_utils.rand_name('server_group')
- policies = ['affinity']
- body = self.server_groups_client.create_server_group(
- name=name, policies=policies)['server_group']
- group_id = body['id']
- self.addCleanup(self.server_groups_client.delete_server_group,
- group_id)
-
+ group_id = self.create_test_server_group()['id']
hints = {'group': group_id}
server = self.create_test_server(scheduler_hints=hints,
wait_until='ACTIVE')
@@ -268,37 +264,25 @@
flavor_base = self.flavors_client.show_flavor(
self.flavor_ref)['flavor']
- def create_flavor_with_extra_specs():
- flavor_with_eph_disk_name = data_utils.rand_name('eph_flavor')
+ def create_flavor_with_ephemeral(ephem_disk):
flavor_with_eph_disk_id = data_utils.rand_int_id(start=1000)
ram = flavor_base['ram']
vcpus = flavor_base['vcpus']
disk = flavor_base['disk']
- # Create a flavor with extra specs
- flavor = (self.flavor_client.
- create_flavor(name=flavor_with_eph_disk_name,
- ram=ram, vcpus=vcpus, disk=disk,
- id=flavor_with_eph_disk_id,
- ephemeral=1))['flavor']
- self.addCleanup(flavor_clean_up, flavor['id'])
-
- return flavor['id']
-
- def create_flavor_without_extra_specs():
- flavor_no_eph_disk_name = data_utils.rand_name('no_eph_flavor')
- flavor_no_eph_disk_id = data_utils.rand_int_id(start=1000)
-
- ram = flavor_base['ram']
- vcpus = flavor_base['vcpus']
- disk = flavor_base['disk']
-
- # Create a flavor without extra specs
- flavor = (self.flavor_client.
- create_flavor(name=flavor_no_eph_disk_name,
- ram=ram, vcpus=vcpus, disk=disk,
- id=flavor_no_eph_disk_id))['flavor']
+ if ephem_disk > 0:
+ # Create a flavor with ephemeral disk
+ flavor_name = data_utils.rand_name('eph_flavor')
+ flavor = self.flavor_client.create_flavor(
+ name=flavor_name, ram=ram, vcpus=vcpus, disk=disk,
+ id=flavor_with_eph_disk_id, ephemeral=ephem_disk)['flavor']
+ else:
+ # Create a flavor without ephemeral disk
+ flavor_name = data_utils.rand_name('no_eph_flavor')
+ flavor = self.flavor_client.create_flavor(
+ name=flavor_name, ram=ram, vcpus=vcpus, disk=disk,
+ id=flavor_with_eph_disk_id)['flavor']
self.addCleanup(flavor_clean_up, flavor['id'])
return flavor['id']
@@ -307,8 +291,8 @@
self.flavor_client.delete_flavor(flavor_id)
self.flavor_client.wait_for_resource_deletion(flavor_id)
- flavor_with_eph_disk_id = create_flavor_with_extra_specs()
- flavor_no_eph_disk_id = create_flavor_without_extra_specs()
+ flavor_with_eph_disk_id = create_flavor_with_ephemeral(ephem_disk=1)
+ flavor_no_eph_disk_id = create_flavor_with_ephemeral(ephem_disk=0)
admin_pass = self.image_ssh_password
@@ -318,7 +302,7 @@
adminPass=admin_pass,
flavor=flavor_no_eph_disk_id)
- # Get partition number of server without extra specs.
+ # Get partition number of server without ephemeral disk.
server_no_eph_disk = self.client.show_server(
server_no_eph_disk['id'])['server']
linux_client = remote_client.RemoteClient(
diff --git a/tempest/api/compute/servers/test_device_tagging.py b/tempest/api/compute/servers/test_device_tagging.py
new file mode 100644
index 0000000..7252e1b
--- /dev/null
+++ b/tempest/api/compute/servers/test_device_tagging.py
@@ -0,0 +1,268 @@
+# Copyright (C) 2016, Red Hat, Inc.
+#
+# 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 json
+
+from oslo_log import log as logging
+
+from tempest.api.compute import base
+from tempest.common.utils import data_utils
+from tempest.common.utils.linux import remote_client
+from tempest.common import waiters
+from tempest import config
+from tempest.lib import exceptions
+from tempest import test
+
+
+CONF = config.CONF
+
+LOG = logging.getLogger(__name__)
+
+
+class DeviceTaggingTest(base.BaseV2ComputeTest):
+
+ min_microversion = '2.32'
+ max_microversion = 'latest'
+
+ @classmethod
+ def skip_checks(cls):
+ super(DeviceTaggingTest, cls).skip_checks()
+ if not CONF.service_available.neutron:
+ raise cls.skipException('Neutron is required')
+ if not CONF.validation.run_validation:
+ raise cls.skipException('Validation must be enabled')
+ if (not CONF.compute_feature_enabled.config_drive
+ and not CONF.compute_feature_enabled.metadata_service):
+ raise cls.skipException('One of metadata or config drive must be '
+ 'enabled')
+
+ @classmethod
+ def setup_clients(cls):
+ super(DeviceTaggingTest, cls).setup_clients()
+ cls.networks_client = cls.os.networks_client
+ cls.ports_client = cls.os.ports_client
+ cls.volumes_client = cls.os.volumes_client
+ cls.subnets_client = cls.os.subnets_client
+ cls.routers_client = cls.os.routers_client
+ cls.interfaces_client = cls.os.interfaces_client
+
+ @classmethod
+ def setup_credentials(cls):
+ cls.set_network_resources(network=True, subnet=True, router=True,
+ dhcp=True)
+ super(DeviceTaggingTest, cls).setup_credentials()
+
+ @classmethod
+ def resource_setup(cls):
+ cls.set_validation_resources()
+ super(DeviceTaggingTest, cls).resource_setup()
+
+ def verify_device_metadata(self, md_json):
+ md_dict = json.loads(md_json)
+ for d in md_dict['devices']:
+ if d['type'] == 'nic':
+ if d['mac'] == self.port1['mac_address']:
+ self.assertEqual(d['tags'], ['port-1'])
+ if d['mac'] == self.port2['mac_address']:
+ self.assertEqual(d['tags'], ['port-2'])
+ if d['mac'] == self.net_2_100_mac:
+ self.assertEqual(d['tags'], ['net-2-100'])
+ if d['mac'] == self.net_2_200_mac:
+ self.assertEqual(d['tags'], ['net-2-200'])
+
+ found_devices = [d['tags'][0] for d in md_dict['devices']]
+ self.assertItemsEqual(found_devices, ['port-1', 'port-2', 'net-1',
+ 'net-2-100', 'net-2-200',
+ 'boot', 'other'])
+
+ @test.idempotent_id('a2e65a6c-66f1-4442-aaa8-498c31778d96')
+ @test.services('network', 'volume', 'image')
+ def test_device_tagging(self):
+ # Create volumes
+ # The create_volume methods waits for the volumes to be available and
+ # the base class will clean them up on tearDown.
+ boot_volume = self.create_volume(CONF.compute.image_ref)
+ other_volume = self.create_volume()
+ untagged_volume = self.create_volume()
+
+ # Create networks
+ net1 = self.networks_client.create_network(
+ name=data_utils.rand_name('device-tagging-net1'))['network']
+ self.addCleanup(self.networks_client.delete_network, net1['id'])
+
+ net2 = self.networks_client.create_network(
+ name=data_utils.rand_name('device-tagging-net2'))['network']
+ self.addCleanup(self.networks_client.delete_network, net2['id'])
+
+ # Create subnets
+ subnet1 = self.subnets_client.create_subnet(
+ network_id=net1['id'],
+ cidr='10.1.1.0/24',
+ ip_version=4)['subnet']
+ self.addCleanup(self.subnets_client.delete_subnet, subnet1['id'])
+
+ subnet2 = self.subnets_client.create_subnet(
+ network_id=net2['id'],
+ cidr='10.2.2.0/24',
+ ip_version=4)['subnet']
+ self.addCleanup(self.subnets_client.delete_subnet, subnet2['id'])
+
+ # Create ports
+ self.port1 = self.ports_client.create_port(
+ network_id=net1['id'],
+ fixed_ips=[{'subnet_id': subnet1['id']}])['port']
+ self.addCleanup(self.ports_client.delete_port, self.port1['id'])
+
+ self.port2 = self.ports_client.create_port(
+ network_id=net1['id'],
+ fixed_ips=[{'subnet_id': subnet1['id']}])['port']
+ self.addCleanup(self.ports_client.delete_port, self.port2['id'])
+
+ # Create server
+ admin_pass = data_utils.rand_password()
+ config_drive_enabled = CONF.compute_feature_enabled.config_drive
+
+ server = self.create_test_server(
+ validatable=True,
+ config_drive=config_drive_enabled,
+ adminPass=admin_pass,
+ name=data_utils.rand_name('device-tagging-server'),
+ networks=[
+ # Validation network for ssh
+ {
+ 'uuid': self.get_tenant_network()['id']
+ },
+ # Different tags for different ports
+ {
+ 'port': self.port1['id'],
+ 'tag': 'port-1'
+ },
+ {
+ 'port': self.port2['id'],
+ 'tag': 'port-2'
+ },
+ # Two nics on same net, one tagged one not
+ {
+ 'uuid': net1['id'],
+ 'tag': 'net-1'
+ },
+ {
+ 'uuid': net1['id']
+ },
+ # Two nics on same net, different IP
+ {
+ 'uuid': net2['id'],
+ 'fixed_ip': '10.2.2.100',
+ 'tag': 'net-2-100'
+ },
+ {
+ 'uuid': net2['id'],
+ 'fixed_ip': '10.2.2.200',
+ 'tag': 'net-2-200'
+ }
+ ],
+ block_device_mapping_v2=[
+ # Boot volume
+ {
+ 'uuid': boot_volume['id'],
+ 'source_type': 'volume',
+ 'destination_type': 'volume',
+ 'boot_index': 0,
+ 'tag': 'boot'
+ },
+ # Other volume
+ {
+ 'uuid': other_volume['id'],
+ 'source_type': 'volume',
+ 'destination_type': 'volume',
+ 'boot_index': 1,
+ 'tag': 'other'
+ },
+ # Untagged volume
+ {
+ 'uuid': untagged_volume['id'],
+ 'source_type': 'volume',
+ 'destination_type': 'volume',
+ 'boot_index': 2
+ }
+ ])
+
+ self.addCleanup(waiters.wait_for_server_termination,
+ self.servers_client, server['id'])
+ self.addCleanup(self.servers_client.delete_server, server['id'])
+
+ self.ssh_client = remote_client.RemoteClient(
+ self.get_server_ip(server),
+ CONF.validation.image_ssh_user,
+ admin_pass,
+ self.validation_resources['keypair']['private_key'],
+ server=server,
+ servers_client=self.servers_client)
+
+ # Find the MAC addresses of our fixed IPs
+ self.net_2_100_mac = None
+ self.net_2_200_mac = None
+ ifaces = self.interfaces_client.list_interfaces(server['id'])
+ for iface in ifaces['interfaceAttachments']:
+ if 'fixed_ips' in iface:
+ for ip in iface['fixed_ips']:
+ if ip['ip_address'] == '10.2.2.100':
+ self.net_2_100_mac = iface['mac_addr']
+ if ip['ip_address'] == '10.2.2.200':
+ self.net_2_200_mac = iface['mac_addr']
+ # Make sure we have the MACs we need, there's no reason for some to be
+ # missing
+ self.assertTrue(self.net_2_100_mac)
+ self.assertTrue(self.net_2_200_mac)
+
+ # Verify metadata from metadata service
+ if CONF.compute_feature_enabled.metadata_service:
+ md_url = 'http://169.254.169.254/openstack/latest/meta_data.json'
+ LOG.info('Attempting to verify tagged devices in server %s via '
+ 'the metadata service: %s', server['id'], md_url)
+
+ def get_and_verify_metadata():
+ try:
+ self.ssh_client.exec_command('curl -V')
+ except exceptions.SSHExecCommandFailed:
+ if not CONF.compute_feature_enabled.config_drive:
+ raise self.skipException('curl not found in guest '
+ 'and config drive is '
+ 'disabled')
+ LOG.warning('curl was not found in the guest, device '
+ 'tagging metadata was not checked in the '
+ 'metadata API')
+ return True
+ cmd = 'curl %s' % md_url
+ md_json = self.ssh_client.exec_command(cmd)
+ self.verify_device_metadata(md_json)
+ return True
+
+ if not test.call_until_true(get_and_verify_metadata,
+ CONF.compute.build_timeout,
+ CONF.compute.build_interval):
+ raise exceptions.TimeoutException('Timeout while verifying '
+ 'metadata on server.')
+
+ # Verify metadata on config drive
+ if CONF.compute_feature_enabled.config_drive:
+ cmd_blkid = 'blkid -t LABEL=config-2 -o device'
+ LOG.info('Attempting to verify tagged devices in server %s via '
+ 'the config drive.', server['id'])
+ dev_name = self.ssh_client.exec_command(cmd_blkid)
+ dev_name = dev_name.rstrip()
+ self.ssh_client.exec_command('sudo mount %s /mnt' % dev_name)
+ cmd_md = 'sudo cat /mnt/openstack/latest/meta_data.json'
+ md_json = self.ssh_client.exec_command(cmd_md)
+ self.verify_device_metadata(md_json)
diff --git a/tempest/api/compute/servers/test_disk_config.py b/tempest/api/compute/servers/test_disk_config.py
index aba0240..ff8ea6e 100644
--- a/tempest/api/compute/servers/test_disk_config.py
+++ b/tempest/api/compute/servers/test_disk_config.py
@@ -94,12 +94,8 @@
self._update_server_with_disk_config(server['id'],
disk_config='MANUAL')
# Resize with auto option
- self.client.resize_server(server['id'], self.flavor_ref_alt,
- disk_config='AUTO')
- waiters.wait_for_server_status(self.client, server['id'],
- 'VERIFY_RESIZE')
- self.client.confirm_resize_server(server['id'])
- waiters.wait_for_server_status(self.client, server['id'], 'ACTIVE')
+ self.resize_server(server['id'], self.flavor_ref_alt,
+ disk_config='AUTO')
server = self.client.show_server(server['id'])['server']
self.assertEqual('AUTO', server['OS-DCF:diskConfig'])
@@ -114,12 +110,8 @@
self._update_server_with_disk_config(server['id'],
disk_config='AUTO')
# Resize with manual option
- self.client.resize_server(server['id'], self.flavor_ref_alt,
- disk_config='MANUAL')
- waiters.wait_for_server_status(self.client, server['id'],
- 'VERIFY_RESIZE')
- self.client.confirm_resize_server(server['id'])
- waiters.wait_for_server_status(self.client, server['id'], 'ACTIVE')
+ self.resize_server(server['id'], self.flavor_ref_alt,
+ disk_config='MANUAL')
server = self.client.show_server(server['id'])['server']
self.assertEqual('MANUAL', server['OS-DCF:diskConfig'])
diff --git a/tempest/api/compute/servers/test_instance_actions.py b/tempest/api/compute/servers/test_instance_actions.py
index a229df8..e50881f 100644
--- a/tempest/api/compute/servers/test_instance_actions.py
+++ b/tempest/api/compute/servers/test_instance_actions.py
@@ -28,17 +28,17 @@
@classmethod
def resource_setup(cls):
super(InstanceActionsTestJSON, cls).resource_setup()
- server = cls.create_test_server(wait_until='ACTIVE')
- cls.request_id = server.response['x-compute-request-id']
- cls.server_id = server['id']
+ cls.server = cls.create_test_server(wait_until='ACTIVE')
+ cls.request_id = cls.server.response['x-compute-request-id']
@test.idempotent_id('77ca5cc5-9990-45e0-ab98-1de8fead201a')
def test_list_instance_actions(self):
# List actions of the provided server
- self.client.reboot_server(self.server_id, type='HARD')
- waiters.wait_for_server_status(self.client, self.server_id, 'ACTIVE')
+ self.client.reboot_server(self.server['id'], type='HARD')
+ waiters.wait_for_server_status(self.client,
+ self.server['id'], 'ACTIVE')
- body = (self.client.list_instance_actions(self.server_id)
+ body = (self.client.list_instance_actions(self.server['id'])
['instanceActions'])
self.assertEqual(len(body), 2, str(body))
self.assertEqual(sorted([i['action'] for i in body]),
@@ -48,8 +48,8 @@
def test_get_instance_action(self):
# Get the action details of the provided server
body = self.client.show_instance_action(
- self.server_id, self.request_id)['instanceAction']
- self.assertEqual(self.server_id, body['instance_uuid'])
+ self.server['id'], self.request_id)['instanceAction']
+ self.assertEqual(self.server['id'], body['instance_uuid'])
self.assertEqual('create', body['action'])
diff --git a/tempest/api/compute/servers/test_instance_actions_negative.py b/tempest/api/compute/servers/test_instance_actions_negative.py
index 54ec6aa..33fed08 100644
--- a/tempest/api/compute/servers/test_instance_actions_negative.py
+++ b/tempest/api/compute/servers/test_instance_actions_negative.py
@@ -29,8 +29,7 @@
@classmethod
def resource_setup(cls):
super(InstanceActionsNegativeTestJSON, cls).resource_setup()
- server = cls.create_test_server(wait_until='ACTIVE')
- cls.server_id = server['id']
+ cls.server = cls.create_test_server(wait_until='ACTIVE')
@test.attr(type=['negative'])
@test.idempotent_id('67e1fce6-7ec2-45c6-92d4-0a8f1a632910')
@@ -46,4 +45,4 @@
def test_get_instance_action_invalid_request(self):
# Get the action details of the provided server with invalid request
self.assertRaises(lib_exc.NotFound, self.client.show_instance_action,
- self.server_id, '999')
+ self.server['id'], '999')
diff --git a/tempest/api/compute/servers/test_list_servers_negative.py b/tempest/api/compute/servers/test_list_servers_negative.py
index fcd5a24..5a35b4e 100644
--- a/tempest/api/compute/servers/test_list_servers_negative.py
+++ b/tempest/api/compute/servers/test_list_servers_negative.py
@@ -66,8 +66,7 @@
@test.idempotent_id('ff01387d-c7ad-47b4-ae9e-64fa214638fe')
def test_list_servers_by_non_existing_image(self):
# Listing servers for a non existing image returns empty list
- non_existing_image = '1234abcd-zzz0-aaa9-ppp3-0987654abcde'
- body = self.client.list_servers(image=non_existing_image)
+ body = self.client.list_servers(image='non_existing_image')
servers = body['servers']
self.assertEqual([], servers)
@@ -75,8 +74,7 @@
@test.idempotent_id('5913660b-223b-44d4-a651-a0fbfd44ca75')
def test_list_servers_by_non_existing_flavor(self):
# Listing servers by non existing flavor returns empty list
- non_existing_flavor = 1234
- body = self.client.list_servers(flavor=non_existing_flavor)
+ body = self.client.list_servers(flavor='non_existing_flavor')
servers = body['servers']
self.assertEqual([], servers)
@@ -84,8 +82,7 @@
@test.idempotent_id('e2c77c4a-000a-4af3-a0bd-629a328bde7c')
def test_list_servers_by_non_existing_server_name(self):
# Listing servers for a non existent server name returns empty list
- non_existing_name = 'junk_server_1234'
- body = self.client.list_servers(name=non_existing_name)
+ body = self.client.list_servers(name='non_existing_server_name')
servers = body['servers']
self.assertEqual([], servers)
@@ -93,8 +90,7 @@
@test.idempotent_id('fcdf192d-0f74-4d89-911f-1ec002b822c4')
def test_list_servers_status_non_existing(self):
# Return an empty list when invalid status is specified
- non_existing_status = 'BALONEY'
- body = self.client.list_servers(status=non_existing_status)
+ body = self.client.list_servers(status='non_existing_status')
servers = body['servers']
self.assertEqual([], servers)
diff --git a/tempest/api/compute/servers/test_multiple_create.py b/tempest/api/compute/servers/test_multiple_create.py
index 4dd26af..9fc30f9 100644
--- a/tempest/api/compute/servers/test_multiple_create.py
+++ b/tempest/api/compute/servers/test_multiple_create.py
@@ -14,26 +14,16 @@
# under the License.
from tempest.api.compute import base
-from tempest.common.utils import data_utils
from tempest import test
class MultipleCreateTestJSON(base.BaseV2ComputeTest):
- _name = 'multiple-create-test'
-
- def _create_multiple_servers(self, **kwargs):
- # This is the right way to create_multiple servers and manage to get
- # the created servers into the servers list to be cleaned up after all.
- kwargs['name'] = kwargs.get('name', data_utils.rand_name(self._name))
- body = self.create_test_server(**kwargs)
-
- return body
@test.idempotent_id('61e03386-89c3-449c-9bb1-a06f423fd9d1')
def test_multiple_create(self):
- body = self._create_multiple_servers(wait_until='ACTIVE',
- min_count=1,
- max_count=2)
+ body = self.create_test_server(wait_until='ACTIVE',
+ min_count=1,
+ max_count=2)
# NOTE(maurosr): do status response check and also make sure that
# reservation_id is not in the response body when the request send
# contains return_reservation_id=False
@@ -41,8 +31,8 @@
@test.idempotent_id('864777fb-2f1e-44e3-b5b9-3eb6fa84f2f7')
def test_multiple_create_with_reservation_return(self):
- body = self._create_multiple_servers(wait_until='ACTIVE',
- min_count=1,
- max_count=2,
- return_reservation_id=True)
+ body = self.create_test_server(wait_until='ACTIVE',
+ min_count=1,
+ max_count=2,
+ return_reservation_id=True)
self.assertIn('reservation_id', body)
diff --git a/tempest/api/compute/servers/test_multiple_create_negative.py b/tempest/api/compute/servers/test_multiple_create_negative.py
index c4dbe23..d9fb4ca 100644
--- a/tempest/api/compute/servers/test_multiple_create_negative.py
+++ b/tempest/api/compute/servers/test_multiple_create_negative.py
@@ -14,48 +14,38 @@
# under the License.
from tempest.api.compute import base
-from tempest.common.utils import data_utils
from tempest.lib import exceptions as lib_exc
from tempest import test
class MultipleCreateNegativeTestJSON(base.BaseV2ComputeTest):
- _name = 'multiple-create-test'
-
- def _create_multiple_servers(self, **kwargs):
- # This is the right way to create_multiple servers and manage to get
- # the created servers into the servers list to be cleaned up after all.
- kwargs['name'] = kwargs.get('name', data_utils.rand_name(self._name))
- body = self.create_test_server(**kwargs)
-
- return body
@test.attr(type=['negative'])
@test.idempotent_id('daf29d8d-e928-4a01-9a8c-b129603f3fc0')
def test_min_count_less_than_one(self):
invalid_min_count = 0
- self.assertRaises(lib_exc.BadRequest, self._create_multiple_servers,
+ self.assertRaises(lib_exc.BadRequest, self.create_test_server,
min_count=invalid_min_count)
@test.attr(type=['negative'])
@test.idempotent_id('999aa722-d624-4423-b813-0d1ac9884d7a')
def test_min_count_non_integer(self):
invalid_min_count = 2.5
- self.assertRaises(lib_exc.BadRequest, self._create_multiple_servers,
+ self.assertRaises(lib_exc.BadRequest, self.create_test_server,
min_count=invalid_min_count)
@test.attr(type=['negative'])
@test.idempotent_id('a6f9c2ab-e060-4b82-b23c-4532cb9390ff')
def test_max_count_less_than_one(self):
invalid_max_count = 0
- self.assertRaises(lib_exc.BadRequest, self._create_multiple_servers,
+ self.assertRaises(lib_exc.BadRequest, self.create_test_server,
max_count=invalid_max_count)
@test.attr(type=['negative'])
@test.idempotent_id('9c5698d1-d7af-4c80-b971-9d403135eea2')
def test_max_count_non_integer(self):
invalid_max_count = 2.5
- self.assertRaises(lib_exc.BadRequest, self._create_multiple_servers,
+ self.assertRaises(lib_exc.BadRequest, self.create_test_server,
max_count=invalid_max_count)
@test.attr(type=['negative'])
@@ -63,6 +53,6 @@
def test_max_count_less_than_min_count(self):
min_count = 3
max_count = 2
- self.assertRaises(lib_exc.BadRequest, self._create_multiple_servers,
+ self.assertRaises(lib_exc.BadRequest, self.create_test_server,
min_count=min_count,
max_count=max_count)
diff --git a/tempest/api/compute/servers/test_server_actions.py b/tempest/api/compute/servers/test_server_actions.py
index 9077801..0a94d5e 100644
--- a/tempest/api/compute/servers/test_server_actions.py
+++ b/tempest/api/compute/servers/test_server_actions.py
@@ -13,8 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
-import logging
-
+from oslo_log import log as logging
from six.moves.urllib import parse as urlparse
import testtools
@@ -131,8 +130,8 @@
server=server,
servers_client=self.client)
new_boot_time = linux_client.get_boot_time()
- self.assertTrue(new_boot_time > boot_time,
- '%s > %s' % (new_boot_time, boot_time))
+ self.assertGreater(new_boot_time, boot_time,
+ '%s > %s' % (new_boot_time, boot_time))
@test.attr(type='smoke')
@test.idempotent_id('2cb1baf6-ac8d-4429-bf0d-ba8a0ba53e32')
@@ -458,8 +457,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.assertTrue(lines > 10, "Cannot get enough console log length."
- " (lines: %s)" % lines)
+ self.assertGreater(lines, 10, "Cannot get enough console log "
+ "length. (lines: %s)" % lines)
self.wait_for(_check_full_length_console_log)
diff --git a/tempest/api/compute/servers/test_server_addresses.py b/tempest/api/compute/servers/test_server_addresses.py
index 864f38f..d31b6f8 100644
--- a/tempest/api/compute/servers/test_server_addresses.py
+++ b/tempest/api/compute/servers/test_server_addresses.py
@@ -49,9 +49,9 @@
# We do not know the exact network configuration, but an instance
# should at least have a single public or private address
- self.assertTrue(len(addresses) >= 1)
+ self.assertGreaterEqual(len(addresses), 1)
for network_name, network_addresses in six.iteritems(addresses):
- self.assertTrue(len(network_addresses) >= 1)
+ self.assertGreaterEqual(len(network_addresses), 1)
for address in network_addresses:
self.assertTrue(address['addr'])
self.assertTrue(address['version'])
diff --git a/tempest/api/compute/servers/test_server_metadata.py b/tempest/api/compute/servers/test_server_metadata.py
index 9c07677..847b7a1 100644
--- a/tempest/api/compute/servers/test_server_metadata.py
+++ b/tempest/api/compute/servers/test_server_metadata.py
@@ -23,23 +23,21 @@
def setup_clients(cls):
super(ServerMetadataTestJSON, cls).setup_clients()
cls.client = cls.servers_client
- cls.quotas = cls.quotas_client
@classmethod
def resource_setup(cls):
super(ServerMetadataTestJSON, cls).resource_setup()
- server = cls.create_test_server(metadata={}, wait_until='ACTIVE')
- cls.server_id = server['id']
+ cls.server = cls.create_test_server(metadata={}, wait_until='ACTIVE')
def setUp(self):
super(ServerMetadataTestJSON, self).setUp()
meta = {'key1': 'value1', 'key2': 'value2'}
- self.client.set_server_metadata(self.server_id, meta)['metadata']
+ self.client.set_server_metadata(self.server['id'], meta)['metadata']
@test.idempotent_id('479da087-92b3-4dcf-aeb3-fd293b2d14ce')
def test_list_server_metadata(self):
# All metadata key/value pairs for a server should be returned
- resp_metadata = (self.client.list_server_metadata(self.server_id)
+ resp_metadata = (self.client.list_server_metadata(self.server['id'])
['metadata'])
# Verify the expected metadata items are in the list
@@ -51,12 +49,12 @@
# The server's metadata should be replaced with the provided values
# Create a new set of metadata for the server
req_metadata = {'meta2': 'data2', 'meta3': 'data3'}
- self.client.set_server_metadata(self.server_id,
+ self.client.set_server_metadata(self.server['id'],
req_metadata)['metadata']
# Verify the expected values are correct, and that the
# previous values have been removed
- resp_metadata = (self.client.list_server_metadata(self.server_id)
+ resp_metadata = (self.client.list_server_metadata(self.server['id'])
['metadata'])
self.assertEqual(resp_metadata, req_metadata)
@@ -65,10 +63,10 @@
# The server's metadata values should be updated to the
# provided values
meta = {'key1': 'alt1', 'key3': 'value3'}
- self.client.update_server_metadata(self.server_id, meta)
+ self.client.update_server_metadata(self.server['id'], meta)
# Verify the values have been updated to the proper values
- resp_metadata = (self.client.list_server_metadata(self.server_id)
+ resp_metadata = (self.client.list_server_metadata(self.server['id'])
['metadata'])
expected = {'key1': 'alt1', 'key2': 'value2', 'key3': 'value3'}
self.assertEqual(expected, resp_metadata)
@@ -78,8 +76,8 @@
# The original metadata should not be lost if empty metadata body is
# passed
meta = {}
- self.client.update_server_metadata(self.server_id, meta)
- resp_metadata = (self.client.list_server_metadata(self.server_id)
+ self.client.update_server_metadata(self.server['id'], meta)
+ resp_metadata = (self.client.list_server_metadata(self.server['id'])
['metadata'])
expected = {'key1': 'value1', 'key2': 'value2'}
self.assertEqual(expected, resp_metadata)
@@ -87,7 +85,7 @@
@test.idempotent_id('3043c57d-7e0e-49a6-9a96-ad569c265e6a')
def test_get_server_metadata_item(self):
# The value for a specific metadata key should be returned
- meta = self.client.show_server_metadata_item(self.server_id,
+ meta = self.client.show_server_metadata_item(self.server['id'],
'key2')['meta']
self.assertEqual('value2', meta['key2'])
@@ -96,10 +94,10 @@
# The item's value should be updated to the provided value
# Update the metadata value
meta = {'nova': 'alt'}
- self.client.set_server_metadata_item(self.server_id, 'nova', meta)
+ self.client.set_server_metadata_item(self.server['id'], 'nova', meta)
# Verify the meta item's value has been updated
- resp_metadata = (self.client.list_server_metadata(self.server_id)
+ resp_metadata = (self.client.list_server_metadata(self.server['id'])
['metadata'])
expected = {'key1': 'value1', 'key2': 'value2', 'nova': 'alt'}
self.assertEqual(expected, resp_metadata)
@@ -107,10 +105,10 @@
@test.idempotent_id('127642d6-4c7b-4486-b7cd-07265a378658')
def test_delete_server_metadata_item(self):
# The metadata value/key pair should be deleted from the server
- self.client.delete_server_metadata_item(self.server_id, 'key1')
+ self.client.delete_server_metadata_item(self.server['id'], 'key1')
# Verify the metadata item has been removed
- resp_metadata = (self.client.list_server_metadata(self.server_id)
+ resp_metadata = (self.client.list_server_metadata(self.server['id'])
['metadata'])
expected = {'key2': 'value2'}
self.assertEqual(expected, resp_metadata)
diff --git a/tempest/api/compute/servers/test_server_metadata_negative.py b/tempest/api/compute/servers/test_server_metadata_negative.py
index cbe70e2..62b8962 100644
--- a/tempest/api/compute/servers/test_server_metadata_negative.py
+++ b/tempest/api/compute/servers/test_server_metadata_negative.py
@@ -25,15 +25,12 @@
def setup_clients(cls):
super(ServerMetadataNegativeTestJSON, cls).setup_clients()
cls.client = cls.servers_client
- cls.quotas = cls.quotas_client
@classmethod
def resource_setup(cls):
super(ServerMetadataNegativeTestJSON, cls).resource_setup()
cls.tenant_id = cls.client.tenant_id
- server = cls.create_test_server(metadata={}, wait_until='ACTIVE')
-
- cls.server_id = server['id']
+ cls.server = cls.create_test_server(metadata={}, wait_until='ACTIVE')
@test.attr(type=['negative'])
@test.idempotent_id('fe114a8f-3a57-4eff-9ee2-4e14628df049')
@@ -87,7 +84,7 @@
meta = {'testkey': 'testvalue'}
self.assertRaises(lib_exc.BadRequest,
self.client.set_server_metadata_item,
- self.server_id, 'key', meta)
+ self.server['id'], 'key', meta)
@test.attr(type=['negative'])
@test.idempotent_id('0df38c2a-3d4e-4db5-98d8-d4d9fa843a12')
@@ -118,7 +115,7 @@
meta = {'': 'data1'}
self.assertRaises(lib_exc.BadRequest,
self.client.update_server_metadata,
- self.server_id, meta=meta)
+ self.server['id'], meta=meta)
@test.attr(type=['negative'])
@test.idempotent_id('6bbd88e1-f8b3-424d-ba10-ae21c45ada8d')
@@ -136,7 +133,8 @@
# A 403 Forbidden or 413 Overlimit (old behaviour) exception
# will be raised while exceeding metadata items limit for
# tenant.
- quota_set = self.quotas.show_quota_set(self.tenant_id)['quota_set']
+ quota_set = self.quotas_client.show_quota_set(
+ self.tenant_id)['quota_set']
quota_metadata = quota_set['metadata_items']
if quota_metadata == -1:
raise self.skipException("No limit for metadata_items")
@@ -146,14 +144,14 @@
req_metadata['key' + str(num)] = 'val' + str(num)
self.assertRaises((lib_exc.OverLimit, lib_exc.Forbidden),
self.client.set_server_metadata,
- self.server_id, req_metadata)
+ self.server['id'], req_metadata)
# A 403 Forbidden or 413 Overlimit (old behaviour) exception
# will be raised while exceeding metadata items limit for
# tenant.
self.assertRaises((lib_exc.Forbidden, lib_exc.OverLimit),
self.client.update_server_metadata,
- self.server_id, req_metadata)
+ self.server['id'], req_metadata)
@test.attr(type=['negative'])
@test.idempotent_id('96100343-7fa9-40d8-80fa-d29ef588ce1c')
@@ -163,7 +161,7 @@
meta = {'': 'data1'}
self.assertRaises(lib_exc.BadRequest,
self.client.set_server_metadata,
- self.server_id, meta=meta)
+ self.server['id'], meta=meta)
@test.attr(type=['negative'])
@test.idempotent_id('64a91aee-9723-4863-be44-4c9d9f1e7d0e')
@@ -173,4 +171,4 @@
meta = {'meta1': 'data1'}
self.assertRaises(lib_exc.BadRequest,
self.client.set_server_metadata,
- self.server_id, meta=meta, no_metadata_field=True)
+ self.server['id'], meta=meta, no_metadata_field=True)
diff --git a/tempest/api/compute/servers/test_server_personality.py b/tempest/api/compute/servers/test_server_personality.py
index e5ad7b4..ab291b4 100644
--- a/tempest/api/compute/servers/test_server_personality.py
+++ b/tempest/api/compute/servers/test_server_personality.py
@@ -118,10 +118,12 @@
raise self.skipException("No limit for personality files")
person = []
for i in range(0, int(max_file_limit)):
- path = '/etc/test' + str(i) + '.txt'
+ # NOTE(andreaf) The cirros disk image is blank before boot
+ # so we can only inject safely to /
+ path = '/test' + str(i) + '.txt'
person.append({
'path': path,
- 'contents': base64.encode_as_text(file_contents),
+ 'contents': base64.encode_as_text(file_contents + str(i)),
})
password = data_utils.rand_password()
created_server = self.create_test_server(personality=person,
diff --git a/tempest/api/compute/servers/test_servers_negative.py b/tempest/api/compute/servers/test_servers_negative.py
index 89be3f3..1b1b339 100644
--- a/tempest/api/compute/servers/test_servers_negative.py
+++ b/tempest/api/compute/servers/test_servers_negative.py
@@ -490,6 +490,36 @@
self.client.unshelve_server,
self.server_id)
+ @test.attr(type=['negative'])
+ @test.idempotent_id('74085be3-a370-4ca2-bc51-2d0e10e0f573')
+ @test.services('volume', 'image')
+ def test_create_server_from_non_bootable_volume(self):
+ # Create a volume
+ volume = self.create_volume()
+
+ # Update volume bootable status to false
+ self.volumes_client.set_bootable_volume(volume['id'],
+ bootable=False)
+
+ # Verify bootable flag was updated
+ nonbootable_vol = self.volumes_client.show_volume(
+ volume['id'])['volume']
+ self.assertEqual('false', nonbootable_vol['bootable'])
+
+ # Block device mapping
+ bd_map = [{'boot_index': '0',
+ 'uuid': volume['id'],
+ 'source_type': 'volume',
+ 'destination_type': 'volume',
+ 'delete_on_termination': False}]
+
+ # Try creating a server from non-bootable volume
+ self.assertRaises(lib_exc.BadRequest,
+ self.create_test_server,
+ image_id='',
+ wait_until='ACTIVE',
+ block_device_mapping_v2=bd_map)
+
class ServersNegativeTestMultiTenantJSON(base.BaseV2ComputeTest):
diff --git a/tempest/api/compute/servers/test_virtual_interfaces.py b/tempest/api/compute/servers/test_virtual_interfaces.py
index e620e03..08c34d3 100644
--- a/tempest/api/compute/servers/test_virtual_interfaces.py
+++ b/tempest/api/compute/servers/test_virtual_interfaces.py
@@ -40,8 +40,7 @@
@classmethod
def resource_setup(cls):
super(VirtualInterfacesTestJSON, cls).resource_setup()
- server = cls.create_test_server(wait_until='ACTIVE')
- cls.server_id = server['id']
+ cls.server = cls.create_test_server(wait_until='ACTIVE')
@test.idempotent_id('96c4e2ef-5e4d-4d7f-87f5-fed6dca18016')
@test.services('network')
@@ -53,9 +52,9 @@
# TODO(mriedem): After a microversion implements the API for
# neutron, a 400 should be a failure for nova-network and neutron.
with testtools.ExpectedException(exceptions.BadRequest):
- self.client.list_virtual_interfaces(self.server_id)
+ self.client.list_virtual_interfaces(self.server['id'])
else:
- output = self.client.list_virtual_interfaces(self.server_id)
+ output = self.client.list_virtual_interfaces(self.server['id'])
self.assertIsNotNone(output)
virt_ifaces = output
self.assertNotEqual(0, len(virt_ifaces['virtual_interfaces']),
diff --git a/tempest/api/compute/test_live_block_migration_negative.py b/tempest/api/compute/test_live_block_migration_negative.py
index ffd274f..f072b81 100644
--- a/tempest/api/compute/test_live_block_migration_negative.py
+++ b/tempest/api/compute/test_live_block_migration_negative.py
@@ -33,7 +33,6 @@
@classmethod
def setup_clients(cls):
super(LiveBlockMigrationNegativeTestJSON, cls).setup_clients()
- cls.admin_hosts_client = cls.os_adm.hosts_client
cls.admin_servers_client = cls.os_adm.servers_client
def _migrate_server_to(self, server_id, dest_host):
@@ -49,9 +48,8 @@
# Migrating to an invalid host should not change the status
target_host = data_utils.rand_name('host')
server = self.create_test_server(wait_until="ACTIVE")
- server_id = server['id']
self.assertRaises(lib_exc.BadRequest, self._migrate_server_to,
- server_id, target_host)
- waiters.wait_for_server_status(self.servers_client, server_id,
+ server['id'], target_host)
+ waiters.wait_for_server_status(self.servers_client, server['id'],
'ACTIVE')
diff --git a/tempest/api/compute/test_quotas.py b/tempest/api/compute/test_quotas.py
index 122e7cc..b9e0c35 100644
--- a/tempest/api/compute/test_quotas.py
+++ b/tempest/api/compute/test_quotas.py
@@ -48,7 +48,8 @@
'fixed_ips', 'key_pairs',
'injected_file_path_bytes',
'instances', 'security_group_rules',
- 'cores', 'security_groups'))
+ 'cores', 'security_groups',
+ 'server_group_members', 'server_groups'))
@test.idempotent_id('f1ef0a97-dbbb-4cca-adc5-c9fbc4f76107')
def test_get_quotas(self):
diff --git a/tempest/api/compute/volumes/test_attach_volume.py b/tempest/api/compute/volumes/test_attach_volume.py
index d4831b1..56ec8c6 100644
--- a/tempest/api/compute/volumes/test_attach_volume.py
+++ b/tempest/api/compute/volumes/test_attach_volume.py
@@ -13,6 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
+import logging
import testtools
from tempest.api.compute import base
@@ -20,18 +21,17 @@
from tempest.common.utils.linux import remote_client
from tempest.common import waiters
from tempest import config
+from tempest.lib import exceptions as lib_exc
from tempest import test
CONF = config.CONF
+LOG = logging.getLogger(__name__)
+
class AttachVolumeTestJSON(base.BaseV2ComputeTest):
max_microversion = '2.19'
- def __init__(self, *args, **kwargs):
- super(AttachVolumeTestJSON, self).__init__(*args, **kwargs)
- self.attachment = None
-
@classmethod
def skip_checks(cls):
super(AttachVolumeTestJSON, cls).skip_checks()
@@ -47,50 +47,52 @@
@classmethod
def resource_setup(cls):
cls.set_validation_resources()
-
super(AttachVolumeTestJSON, cls).resource_setup()
cls.device = CONF.compute.volume_device_name
- def _detach(self, server_id, volume_id):
- if self.attachment:
- self.servers_client.detach_volume(server_id, volume_id)
- waiters.wait_for_volume_status(self.volumes_client,
- volume_id, 'available')
-
def _create_server(self):
# Start a server and wait for it to become ready
server = self.create_test_server(
validatable=True,
wait_until='ACTIVE',
adminPass=self.image_ssh_password)
-
# Record addresses so that we can ssh later
server['addresses'] = self.servers_client.list_addresses(
server['id'])['addresses']
return server
- def _create_and_attach_volume(self, server):
- # Create a volume and wait for it to become ready
- volume = self.create_volume()
- self.addCleanup(self.delete_volume, volume['id'])
+ def _detach_volume(self, server_id, volume_id):
+ try:
+ self.servers_client.detach_volume(server_id, volume_id)
+ waiters.wait_for_volume_status(self.volumes_client,
+ volume_id, 'available')
+ except lib_exc.NotFound:
+ LOG.warning("Unable to detach volume %s from server %s "
+ "possibly it was already detached" % (volume_id,
+ server_id))
+ def _attach_volume(self, server_id, volume_id, device=None):
# Attach the volume to the server
- self.attachment = self.servers_client.attach_volume(
- server['id'],
- volumeId=volume['id'],
- device='/dev/%s' % self.device)['volumeAttachment']
+ kwargs = {'volumeId': volume_id}
+ if device:
+ kwargs.update({'device': '/dev/%s' % device})
+ attachment = self.servers_client.attach_volume(
+ server_id, **kwargs)['volumeAttachment']
waiters.wait_for_volume_status(self.volumes_client,
- volume['id'], 'in-use')
+ volume_id, 'in-use')
+ self.addCleanup(self._detach_volume, server_id,
+ volume_id)
- self.addCleanup(self._detach, server['id'], volume['id'])
- return volume
+ return attachment
@test.idempotent_id('52e9045a-e90d-4c0d-9087-79d657faffff')
def test_attach_detach_volume(self):
# Stop and Start a server with an attached volume, ensuring that
# the volume remains attached.
server = self._create_server()
- volume = self._create_and_attach_volume(server)
+ volume = self.create_volume()
+ attachment = self._attach_volume(server['id'], volume['id'],
+ device=self.device)
self.servers_client.stop_server(server['id'])
waiters.wait_for_server_status(self.servers_client, server['id'],
@@ -113,8 +115,7 @@
device_name_to_match = ' ' + self.device + '\n'
self.assertIn(device_name_to_match, partitions)
- self._detach(server['id'], volume['id'])
- self.attachment = None
+ self._detach_volume(server['id'], attachment['volumeId'])
self.servers_client.stop_server(server['id'])
waiters.wait_for_server_status(self.servers_client, server['id'],
'SHUTOFF')
@@ -137,23 +138,53 @@
@test.idempotent_id('7fa563fe-f0f7-43eb-9e22-a1ece036b513')
def test_list_get_volume_attachments(self):
- # Create Server, Volume and attach that Volume to Server
+ # List volume attachment of the server
server = self._create_server()
- volume = self._create_and_attach_volume(server)
-
- # List Volume attachment of the server
+ volume = self.create_volume()
+ attachment = self._attach_volume(server['id'], volume['id'],
+ device=self.device)
body = self.servers_client.list_volume_attachments(
server['id'])['volumeAttachments']
self.assertEqual(1, len(body))
- self.assertIn(self.attachment, body)
+ self.assertIn(attachment, body)
- # Get Volume attachment of the server
+ # Get volume attachment of the server
body = self.servers_client.show_volume_attachment(
server['id'],
- self.attachment['id'])['volumeAttachment']
+ attachment['id'])['volumeAttachment']
self.assertEqual(server['id'], body['serverId'])
self.assertEqual(volume['id'], body['volumeId'])
- self.assertEqual(self.attachment['id'], body['id'])
+ self.assertEqual(attachment['id'], body['id'])
+
+ @test.idempotent_id('757d488b-a951-4bc7-b3cd-f417028da08a')
+ def test_list_get_two_volume_attachments(self):
+ # NOTE: This test is using the volume device auto-assignment
+ # without specifying the device ("/dev/sdb", etc). The feature
+ # is supported since Nova Liberty release or later. So this should
+ # be skipped on older clouds.
+ server = self._create_server()
+ volume_1st = self.create_volume()
+ volume_2nd = self.create_volume()
+ attachment_1st = self._attach_volume(server['id'], volume_1st['id'])
+ attachment_2nd = self._attach_volume(server['id'], volume_2nd['id'])
+
+ body = self.servers_client.list_volume_attachments(
+ server['id'])['volumeAttachments']
+ self.assertEqual(2, len(body))
+
+ body = self.servers_client.show_volume_attachment(
+ server['id'],
+ attachment_1st['id'])['volumeAttachment']
+ self.assertEqual(server['id'], body['serverId'])
+ self.assertEqual(attachment_1st['volumeId'], body['volumeId'])
+ self.assertEqual(attachment_1st['id'], body['id'])
+
+ body = self.servers_client.show_volume_attachment(
+ server['id'],
+ attachment_2nd['id'])['volumeAttachment']
+ self.assertEqual(server['id'], body['serverId'])
+ self.assertEqual(attachment_2nd['volumeId'], body['volumeId'])
+ self.assertEqual(attachment_2nd['id'], body['id'])
class AttachVolumeShelveTestJSON(AttachVolumeTestJSON):
@@ -219,19 +250,21 @@
# Create server, count number of volumes on it, shelve
# server and attach pre-created volume to shelved server
server = self._create_server()
+ volume = self.create_volume()
num_vol = self._count_volumes(server)
self._shelve_server(server)
- self._create_and_attach_volume(server)
+ attachment = self._attach_volume(server['id'], volume['id'],
+ device=self.device)
# Unshelve the instance and check that attached volume exists
self._unshelve_server_and_check_volumes(server, num_vol + 1)
- # Get Volume attachment of the server
+ # Get volume attachment of the server
volume_attachment = self.servers_client.show_volume_attachment(
server['id'],
- self.attachment['id'])['volumeAttachment']
+ attachment['id'])['volumeAttachment']
self.assertEqual(server['id'], volume_attachment['serverId'])
- self.assertEqual(self.attachment['id'], volume_attachment['id'])
+ self.assertEqual(attachment['id'], volume_attachment['id'])
# Check the mountpoint is not None after unshelve server even in
# case of shelved_offloaded.
self.assertIsNotNone(volume_attachment['device'])
@@ -240,16 +273,15 @@
@testtools.skipUnless(CONF.compute_feature_enabled.shelve,
'Shelve is not available.')
def test_detach_volume_shelved_or_offload_server(self):
- # Create server, count number of volumes on it, shelve
+ # Count number of volumes on instance, shelve
# server and attach pre-created volume to shelved server
server = self._create_server()
+ volume = self.create_volume()
num_vol = self._count_volumes(server)
self._shelve_server(server)
- volume = self._create_and_attach_volume(server)
-
+ self._attach_volume(server['id'], volume['id'], device=self.device)
# Detach the volume
- self._detach(server['id'], volume['id'])
- self.attachment = None
+ self._detach_volume(server['id'], volume['id'])
# Unshelve the instance and check that we have the expected number of
# volume(s)
diff --git a/tempest/api/compute/volumes/test_volume_snapshots.py b/tempest/api/compute/volumes/test_volume_snapshots.py
index 460c882..01718cc 100644
--- a/tempest/api/compute/volumes/test_volume_snapshots.py
+++ b/tempest/api/compute/volumes/test_volume_snapshots.py
@@ -13,6 +13,8 @@
# License for the specific language governing permissions and limitations
# under the License.
+import testtools
+
from tempest.api.compute import base
from tempest.common.utils import data_utils
from tempest.common import waiters
@@ -39,6 +41,8 @@
cls.snapshots_client = cls.snapshots_extensions_client
@test.idempotent_id('cd4ec87d-7825-450d-8040-6e2068f2da8f')
+ @testtools.skipUnless(CONF.volume_feature_enabled.snapshot,
+ 'Cinder volume snapshots are disabled')
def test_volume_snapshot_create_get_list_delete(self):
volume = self.create_volume()
self.addCleanup(self.delete_volume, volume['id'])
diff --git a/tempest/api/compute/volumes/test_volumes_get.py b/tempest/api/compute/volumes/test_volumes_get.py
index d599431..7549d4a 100644
--- a/tempest/api/compute/volumes/test_volumes_get.py
+++ b/tempest/api/compute/volumes/test_volumes_get.py
@@ -54,8 +54,8 @@
self.assertEqual(volume['displayName'], v_name,
"The created volume name is not equal "
"to the requested name")
- self.assertTrue(volume['id'] is not None,
- "Field volume id is empty or not found.")
+ self.assertIsNotNone(volume['id'],
+ "Field volume id is empty or not found.")
# Wait for Volume status to become ACTIVE
waiters.wait_for_volume_status(self.client, volume['id'], 'available')
# GET Volume
diff --git a/tempest/api/compute/volumes/test_volumes_list.py b/tempest/api/compute/volumes/test_volumes_list.py
index c60fcca..7d76731 100644
--- a/tempest/api/compute/volumes/test_volumes_list.py
+++ b/tempest/api/compute/volumes/test_volumes_list.py
@@ -13,6 +13,8 @@
# License for the specific language governing permissions and limitations
# under the License.
+from oslo_log import log as logging
+
from tempest.api.compute import base
from tempest.common.utils import data_utils
from tempest.common import waiters
@@ -20,6 +22,7 @@
from tempest import test
CONF = config.CONF
+LOG = logging.getLogger(__name__)
class VolumesTestJSON(base.BaseV2ComputeTest):
@@ -59,21 +62,16 @@
volume = cls.client.show_volume(volume['id'])['volume']
cls.volume_list.append(volume)
cls.volume_id_list.append(volume['id'])
- except Exception:
+ except Exception as exc:
+ LOG.exception(exc)
if cls.volume_list:
# We could not create all the volumes, though we were able
# to create *some* of the volumes. This is typically
# because the backing file size of the volume group is
- # too small. So, here, we clean up whatever we did manage
- # to create and raise a SkipTest
+ # too small.
for volume in cls.volume_list:
cls.delete_volume(volume['id'])
- msg = ("Failed to create ALL necessary volumes to run "
- "test. This typically means that the backing file "
- "size of the nova-volumes group is too small to "
- "create the 3 volumes needed by this test case")
- raise cls.skipException(msg)
- raise
+ raise exc
@classmethod
def resource_cleanup(cls):
diff --git a/tempest/api/compute/volumes/test_volumes_negative.py b/tempest/api/compute/volumes/test_volumes_negative.py
index 5fe4cb3..c4041cb 100644
--- a/tempest/api/compute/volumes/test_volumes_negative.py
+++ b/tempest/api/compute/volumes/test_volumes_negative.py
@@ -66,7 +66,7 @@
@test.attr(type=['negative'])
@test.idempotent_id('131cb3a1-75cc-4d40-b4c3-1317f64719b0')
- def test_create_volume_with_out_passing_size(self):
+ def test_create_volume_without_passing_size(self):
# Negative: Should not be able to create volume without passing size
# in request
v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
diff --git a/tempest/api/identity/admin/v2/test_services.py b/tempest/api/identity/admin/v2/test_services.py
index 3ed51f0..7973a03 100644
--- a/tempest/api/identity/admin/v2/test_services.py
+++ b/tempest/api/identity/admin/v2/test_services.py
@@ -38,7 +38,7 @@
service_data = self.services_client.create_service(
name=name, type=s_type,
description=description)['OS-KSADM:service']
- self.assertFalse(service_data['id'] is None)
+ self.assertIsNotNone(service_data['id'])
self.addCleanup(self._del_service, service_data['id'])
# Verifying response body of create service
self.assertIn('id', service_data)
diff --git a/tempest/api/identity/admin/v3/test_endpoints.py b/tempest/api/identity/admin/v3/test_endpoints.py
index 50cf258..655e4ef 100644
--- a/tempest/api/identity/admin/v3/test_endpoints.py
+++ b/tempest/api/identity/admin/v3/test_endpoints.py
@@ -23,7 +23,6 @@
@classmethod
def setup_clients(cls):
super(EndPointsTestJSON, cls).setup_clients()
- cls.identity_client = cls.client
cls.client = cls.endpoints_client
@classmethod
diff --git a/tempest/api/identity/admin/v3/test_endpoints_negative.py b/tempest/api/identity/admin/v3/test_endpoints_negative.py
index b16605e..f0f8707 100644
--- a/tempest/api/identity/admin/v3/test_endpoints_negative.py
+++ b/tempest/api/identity/admin/v3/test_endpoints_negative.py
@@ -25,7 +25,6 @@
@classmethod
def setup_clients(cls):
super(EndpointsNegativeTestJSON, cls).setup_clients()
- cls.identity_client = cls.client
cls.client = cls.endpoints_client
@classmethod
diff --git a/tempest/api/identity/admin/v3/test_inherits.py b/tempest/api/identity/admin/v3/test_inherits.py
index 955b6fb..c7e8411 100644
--- a/tempest/api/identity/admin/v3/test_inherits.py
+++ b/tempest/api/identity/admin/v3/test_inherits.py
@@ -168,15 +168,16 @@
self.domain['id'], self.user['id'], src_role['id'])
# List "effective" role assignments from user on the parent project
- assignments = (
- self.role_assignments.list_user_project_effective_assignments(
- self.project['id'], self.user['id']))['role_assignments']
+ params = {'scope.project.id': self.project['id'],
+ 'user.id': self.user['id']}
+ assignments = self.role_assignments.list_role_assignments(
+ effective=True, **params)['role_assignments']
self.assertNotEmpty(assignments)
# List "effective" role assignments from user on the leaf project
- assignments = (
- self.role_assignments.list_user_project_effective_assignments(
- leaf_project['id'], self.user['id']))['role_assignments']
+ params['scope.project.id'] = leaf_project['id']
+ assignments = self.role_assignments.list_role_assignments(
+ effective=True, **params)['role_assignments']
self.assertNotEmpty(assignments)
# Revoke role from domain
@@ -185,16 +186,16 @@
# List "effective" role assignments from user on the parent project
# should return an empty list
- assignments = (
- self.role_assignments.list_user_project_effective_assignments(
- self.project['id'], self.user['id']))['role_assignments']
+ params['scope.project.id'] = self.project['id']
+ assignments = self.role_assignments.list_role_assignments(
+ effective=True, **params)['role_assignments']
self.assertEmpty(assignments)
# List "effective" role assignments from user on the leaf project
# should return an empty list
- assignments = (
- self.role_assignments.list_user_project_effective_assignments(
- leaf_project['id'], self.user['id']))['role_assignments']
+ params['scope.project.id'] = leaf_project['id']
+ assignments = self.role_assignments.list_role_assignments(
+ effective=True, **params)['role_assignments']
self.assertEmpty(assignments)
@test.idempotent_id('9f02ccd9-9b57-46b4-8f77-dd5a736f3a06')
@@ -217,9 +218,10 @@
self.project['id'], self.user['id'], src_role['id'])
# List "effective" role assignments from user on the leaf project
- assignments = (
- self.role_assignments.list_user_project_effective_assignments(
- leaf_project['id'], self.user['id']))['role_assignments']
+ params = {'scope.project.id': leaf_project['id'],
+ 'user.id': self.user['id']}
+ assignments = self.role_assignments.list_role_assignments(
+ effective=True, **params)['role_assignments']
self.assertNotEmpty(assignments)
# Revoke role from parent project
@@ -228,7 +230,6 @@
# List "effective" role assignments from user on the leaf project
# should return an empty list
- assignments = (
- self.role_assignments.list_user_project_effective_assignments(
- leaf_project['id'], self.user['id']))['role_assignments']
+ assignments = self.role_assignments.list_role_assignments(
+ effective=True, **params)['role_assignments']
self.assertEmpty(assignments)
diff --git a/tempest/api/image/admin/v2/test_images.py b/tempest/api/image/admin/v2/test_images.py
index 9844a67..f22f321 100644
--- a/tempest/api/image/admin/v2/test_images.py
+++ b/tempest/api/image/admin/v2/test_images.py
@@ -34,11 +34,10 @@
def test_admin_deactivate_reactivate_image(self):
# Create image by non-admin tenant
image_name = data_utils.rand_name('image')
- image = self.client.create_image(name=image_name,
- container_format='bare',
- disk_format='raw',
- visibility='private')
- self.addCleanup(self.client.delete_image, image['id'])
+ image = self.create_image(name=image_name,
+ container_format='bare',
+ disk_format='raw',
+ visibility='private')
# upload an image file
content = data_utils.random_bytes()
image_file = six.BytesIO(content)
diff --git a/tempest/api/image/base.py b/tempest/api/image/base.py
index 1cc3fa2..812c436 100644
--- a/tempest/api/image/base.py
+++ b/tempest/api/image/base.py
@@ -141,6 +141,7 @@
cls.client = cls.os.image_client_v2
cls.namespaces_client = cls.os.namespaces_client
cls.resource_types_client = cls.os.resource_types_client
+ cls.namespace_properties_client = cls.os.namespace_properties_client
cls.schemas_client = cls.os.schemas_client
def create_namespace(cls, namespace_name=None, visibility='public',
diff --git a/tempest/api/image/v1/test_images.py b/tempest/api/image/v1/test_images.py
index 269e297..9fbdcd7 100644
--- a/tempest/api/image/v1/test_images.py
+++ b/tempest/api/image/v1/test_images.py
@@ -238,7 +238,7 @@
def test_index_max_size(self):
images_list = self.client.list_images(size_max=42)['images']
for image in images_list:
- self.assertTrue(image['size'] <= 42)
+ self.assertLessEqual(image['size'], 42)
result_set = set(map(lambda x: x['id'], images_list))
self.assertTrue(self.size42_set <= result_set)
self.assertFalse(self.created_set - self.size42_set <= result_set)
@@ -247,7 +247,7 @@
def test_index_min_size(self):
images_list = self.client.list_images(size_min=142)['images']
for image in images_list:
- self.assertTrue(image['size'] >= 142)
+ self.assertGreaterEqual(image['size'], 142)
result_set = set(map(lambda x: x['id'], images_list))
self.assertTrue(self.size142_set <= result_set)
self.assertFalse(self.size42_set <= result_set)
@@ -261,7 +261,7 @@
top_size = images_list[0]['size'] # We have non-zero sized images
for image in images_list:
size = image['size']
- self.assertTrue(size <= top_size)
+ self.assertLessEqual(size, top_size)
top_size = size
self.assertEqual(image['status'], 'active')
diff --git a/tempest/api/image/v2/test_images.py b/tempest/api/image/v2/test_images.py
index 6f8d239..7b9244b 100644
--- a/tempest/api/image/v2/test_images.py
+++ b/tempest/api/image/v2/test_images.py
@@ -83,10 +83,10 @@
image_name = data_utils.rand_name('image')
container_format = CONF.image.container_formats[0]
disk_format = CONF.image.disk_formats[0]
- image = self.client.create_image(name=image_name,
- container_format=container_format,
- disk_format=disk_format,
- visibility='private')
+ image = self.create_image(name=image_name,
+ container_format=container_format,
+ disk_format=disk_format,
+ visibility='private')
# Delete Image
self.client.delete_image(image['id'])
self.client.wait_for_resource_deletion(image['id'])
@@ -105,11 +105,10 @@
image_name = data_utils.rand_name('image')
container_format = CONF.image.container_formats[0]
disk_format = CONF.image.disk_formats[0]
- image = self.client.create_image(name=image_name,
- container_format=container_format,
- disk_format=disk_format,
- visibility='private')
- self.addCleanup(self.client.delete_image, image['id'])
+ image = self.create_image(name=image_name,
+ container_format=container_format,
+ disk_format=disk_format,
+ visibility='private')
self.assertEqual('queued', image['status'])
# Now try uploading an image file
@@ -129,7 +128,6 @@
class ListImagesTest(base.BaseV2ImageTest):
- """Here we test the listing of image information"""
@classmethod
def resource_setup(cls):
@@ -157,23 +155,49 @@
"""
size = random.randint(1024, 4096)
image_file = six.BytesIO(data_utils.random_bytes(size))
+ tags = [data_utils.rand_name('tag'), data_utils.rand_name('tag')]
image = cls.create_image(container_format=container_format,
disk_format=disk_format,
- visibility='private')
+ visibility='private',
+ tags=tags)
cls.client.store_image_file(image['id'], data=image_file)
+ # Keep the data of one test image so it can be used to filter lists
+ cls.test_data = image
+ cls.test_data['size'] = size
return image['id']
+
+class ListUserImagesTest(ListImagesTest):
+ """Here we test the listing of image information"""
+
def _list_by_param_value_and_assert(self, params):
"""Perform list action with given params and validates result."""
-
+ # Retrieve the list of images that meet the filter
images_list = self.client.list_images(params=params)['images']
# Validating params of fetched images
+ msg = 'No images were found that met the filter criteria.'
+ self.assertNotEmpty(images_list, msg)
for image in images_list:
for key in params:
msg = "Failed to list images by %s" % key
self.assertEqual(params[key], image[key], msg)
+ def _list_sorted_by_image_size_and_assert(self, params, desc=False):
+ """Validate an image list that has been sorted by size
+
+ Perform list action with given params and validates the results are
+ sorted by image size in either ascending or descending order.
+ """
+ # Retrieve the list of images that meet the filter
+ images_list = self.client.list_images(params=params)['images']
+ # Validate that the list was fetched sorted accordingly
+ msg = 'No images were found that met the filter criteria.'
+ self.assertNotEmpty(images_list, msg)
+ sorted_list = [image['size'] for image in images_list]
+ msg = 'The list of images was not sorted correctly.'
+ self.assertEqual(sorted(sorted_list, reverse=desc), sorted_list, msg)
+
@test.idempotent_id('1e341d7a-90a9-494c-b143-2cdf2aeb6aee')
def test_list_no_params(self):
# Simple test to see all fixture images returned
@@ -185,8 +209,8 @@
@test.idempotent_id('9959ca1d-1aa7-4b7a-a1ea-0fff0499b37e')
def test_list_images_param_container_format(self):
- # Test to get all images with container_format='bare'
- params = {"container_format": "bare"}
+ # Test to get all images with a specific container_format
+ params = {"container_format": self.test_data['container_format']}
self._list_by_param_value_and_assert(params)
@test.idempotent_id('4a4735a7-f22f-49b6-b0d9-66e1ef7453eb')
@@ -224,9 +248,10 @@
image_size_list = map(lambda x: x['size'], images_list)
for image_size in image_size_list:
- self.assertTrue(image_size >= params['size_min'] and
- image_size <= params['size_max'],
- "Failed to get images by size_min and size_max")
+ self.assertGreaterEqual(image_size, params['size_min'],
+ "Failed to get images by size_min")
+ self.assertLessEqual(image_size, params['size_max'],
+ "Failed to get images by size_max")
@test.idempotent_id('7fc9e369-0f58-4d05-9aa5-0969e2d59d15')
def test_list_images_param_status(self):
@@ -253,6 +278,37 @@
params = {"owner": image['owner']}
self._list_by_param_value_and_assert(params)
+ @test.idempotent_id('55c8f5f5-bfed-409d-a6d5-4caeda985d7b')
+ def test_list_images_param_name(self):
+ # Test to get images by name
+ params = {'name': self.test_data['name']}
+ self._list_by_param_value_and_assert(params)
+
+ @test.idempotent_id('aa8ac4df-cff9-418b-8d0f-dd9c67b072c9')
+ def test_list_images_param_tag(self):
+ # Test to get images matching a tag
+ params = {'tag': self.test_data['tags'][0]}
+ images_list = self.client.list_images(params=params)['images']
+ # Validating properties of fetched images
+ self.assertNotEmpty(images_list)
+ for image in images_list:
+ msg = ("The image {image_name} does not have the expected tag "
+ "{expected_tag} among its tags: {observerd_tags}."
+ .format(image_name=image['name'],
+ expected_tag=self.test_data['tags'][0],
+ observerd_tags=image['tags']))
+ self.assertIn(self.test_data['tags'][0], image['tags'], msg)
+
+ @test.idempotent_id('eeadce49-04e0-43b7-aec7-52535d903e7a')
+ def test_list_images_param_sort(self):
+ params = {'sort': 'size:desc'}
+ self._list_sorted_by_image_size_and_assert(params, desc=True)
+
+ @test.idempotent_id('9faaa0c2-c3a5-43e1-8f61-61c54b409a49')
+ def test_list_images_param_sort_key_dir(self):
+ params = {'sort_key': 'size', 'sort_dir': 'desc'}
+ self._list_sorted_by_image_size_and_assert(params, desc=True)
+
@test.idempotent_id('622b925c-479f-4736-860d-adeaf13bc371')
def test_get_image_schema(self):
# Test to get image schema
@@ -266,3 +322,32 @@
schema = "images"
body = self.schemas_client.show_schema(schema)
self.assertEqual("images", body['name'])
+
+
+class ListSharedImagesTest(ListImagesTest):
+ """Here we test the listing of a shared image information"""
+
+ credentials = ['primary', 'alt']
+
+ @classmethod
+ def setup_clients(cls):
+ super(ListSharedImagesTest, cls).setup_clients()
+ cls.image_member_client = cls.os.image_member_client_v2
+ cls.alt_img_client = cls.os_alt.image_client_v2
+
+ @test.idempotent_id('3fa50be4-8e38-4c02-a8db-7811bb780122')
+ def test_list_images_param_member_status(self):
+ # Share one of the images created with the alt user
+ self.image_member_client.create_image_member(
+ image_id=self.test_data['id'],
+ member=self.alt_img_client.tenant_id)
+ # Update the info on the test data so it remains accurate
+ self.test_data['updated_at'] = self.client.show_image(
+ self.test_data['id'])['updated_at']
+ # As an image consumer you need to provide the member_status parameter
+ # along with the visibility=shared parameter in order for it to show
+ # results
+ params = {'member_status': 'pending', 'visibility': 'shared'}
+ fetched_images = self.alt_img_client.list_images(params)['images']
+ self.assertEqual(1, len(fetched_images))
+ self.assertEqual(self.test_data['id'], fetched_images[0]['id'])
diff --git a/tempest/api/image/v2/test_images_member.py b/tempest/api/image/v2/test_images_member.py
index fe8dd65..8a4b334 100644
--- a/tempest/api/image/v2/test_images_member.py
+++ b/tempest/api/image/v2/test_images_member.py
@@ -21,6 +21,8 @@
image_id = self._create_image()
member = self.image_member_client.create_image_member(
image_id, member=self.alt_tenant_id)
+ self.addCleanup(self.image_member_client.delete_image_member,
+ image_id, self.alt_tenant_id)
self.assertEqual(member['member_id'], self.alt_tenant_id)
self.assertEqual(member['image_id'], image_id)
self.assertEqual(member['status'], 'pending')
@@ -42,6 +44,8 @@
image_id = self._create_image()
member = self.image_member_client.create_image_member(
image_id, member=self.alt_tenant_id)
+ self.addCleanup(self.image_member_client.delete_image_member,
+ image_id, self.alt_tenant_id)
self.assertEqual(member['member_id'], self.alt_tenant_id)
self.assertEqual(member['image_id'], image_id)
self.assertEqual(member['status'], 'pending')
@@ -56,6 +60,8 @@
image_id = self._create_image()
self.image_member_client.create_image_member(
image_id, member=self.alt_tenant_id)
+ self.addCleanup(self.image_member_client.delete_image_member,
+ image_id, self.alt_tenant_id)
self.alt_image_member_client.update_image_member(image_id,
self.alt_tenant_id,
status='accepted')
diff --git a/tempest/api/image/v2/test_images_metadefs_namespace_properties.py b/tempest/api/image/v2/test_images_metadefs_namespace_properties.py
new file mode 100644
index 0000000..7113db4
--- /dev/null
+++ b/tempest/api/image/v2/test_images_metadefs_namespace_properties.py
@@ -0,0 +1,57 @@
+# 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.api.image import base
+from tempest.common.utils import data_utils
+from tempest import test
+
+
+class MetadataNamespacePropertiesTest(base.BaseV2ImageTest):
+ """Test the Metadata definition namespace property basic functionality"""
+
+ @test.idempotent_id('b1a3765e-3a5d-4f6d-a3a7-3ca3476ae768')
+ def test_basic_meta_def_namespace_property(self):
+ # Get the available resource types and use one resource_type
+ body = self.resource_types_client.list_resource_types()
+ resource_name = body['resource_types'][0]['name']
+ enum = ["xen", "qemu", "kvm", "lxc", "uml", "vmware", "hyperv"]
+ # Create a namespace
+ namespace = self.create_namespace()
+ # Create resource type association
+ body = self.resource_types_client.create_resource_type_association(
+ namespace['namespace'], name=resource_name)
+ # Create a property
+ property_title = data_utils.rand_name('property')
+ body = self.namespace_properties_client.create_namespace_property(
+ namespace=namespace['namespace'], title=property_title,
+ name=resource_name, type="string", enum=enum)
+ self.assertEqual(property_title, body['title'])
+ # Show namespace property
+ body = self.namespace_properties_client.show_namespace_properties(
+ namespace['namespace'], resource_name)
+ self.assertEqual(resource_name, body['name'])
+ # Update namespace property
+ update_property_title = data_utils.rand_name('update-property')
+ body = self.namespace_properties_client.update_namespace_properties(
+ namespace['namespace'], resource_name,
+ title=update_property_title, type="string",
+ enum=enum, name=resource_name)
+ self.assertEqual(update_property_title, body['title'])
+ # Delete namespace property
+ self.namespace_properties_client.delete_namespace_property(
+ namespace['namespace'], resource_name)
+ # List namespace properties and validate deletion
+ namespace_property = [
+ namespace_property['title'] for namespace_property in
+ self.namespace_properties_client.list_namespace_properties(
+ namespace['namespace'])['properties']]
+ self.assertNotIn(update_property_title, namespace_property)
diff --git a/tempest/api/image/v2/test_images_metadefs_schema.py b/tempest/api/image/v2/test_images_metadefs_schema.py
new file mode 100644
index 0000000..7edf1af
--- /dev/null
+++ b/tempest/api/image/v2/test_images_metadefs_schema.py
@@ -0,0 +1,81 @@
+# Copyright 2016 EasyStack.
+# 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.api.image import base
+from tempest import test
+
+
+class MetadataSchemaTest(base.BaseV2ImageTest):
+ """Test to get metadata schema"""
+
+ @test.idempotent_id('e9e44891-3cb8-3b40-a532-e0a39fea3dab')
+ def test_get_metadata_namespace_schema(self):
+ # Test to get namespace schema
+ body = self.schemas_client.show_schema("metadefs/namespace")
+ self.assertEqual("namespace", body['name'])
+
+ @test.idempotent_id('ffe44891-678b-3ba0-a3e2-e0a3967b3aeb')
+ def test_get_metadata_namespaces_schema(self):
+ # Test to get namespaces schema
+ body = self.schemas_client.show_schema("metadefs/namespaces")
+ self.assertEqual("namespaces", body['name'])
+
+ @test.idempotent_id('fde34891-678b-3b40-ae32-e0a3e67b6beb')
+ def test_get_metadata_resource_type_schema(self):
+ # Test to get resource_type schema
+ body = self.schemas_client.show_schema("metadefs/resource_type")
+ self.assertEqual("resource_type_association", body['name'])
+
+ @test.idempotent_id('dfe4a891-b38b-3bf0-a3b2-e03ee67b3a3a')
+ def test_get_metadata_resources_types_schema(self):
+ # Test to get resource_types schema
+ body = self.schemas_client.show_schema("metadefs/resource_types")
+ self.assertEqual("resource_type_associations", body['name'])
+
+ @test.idempotent_id('dff4a891-b38b-3bf0-a3b2-e03ee67b3a3b')
+ def test_get_metadata_object_schema(self):
+ # Test to get object schema
+ body = self.schemas_client.show_schema("metadefs/object")
+ self.assertEqual("object", body['name'])
+
+ @test.idempotent_id('dee4a891-b38b-3bf0-a3b2-e03ee67b3a3c')
+ def test_get_metadata_objects_schema(self):
+ # Test to get objects schema
+ body = self.schemas_client.show_schema("metadefs/objects")
+ self.assertEqual("objects", body['name'])
+
+ @test.idempotent_id('dae4a891-b38b-3bf0-a3b2-e03ee67b3a3d')
+ def test_get_metadata_property_schema(self):
+ # Test to get property schema
+ body = self.schemas_client.show_schema("metadefs/property")
+ self.assertEqual("property", body['name'])
+
+ @test.idempotent_id('dce4a891-b38b-3bf0-a3b2-e03ee67b3a3e')
+ def test_get_metadata_properties_schema(self):
+ # Test to get properties schema
+ body = self.schemas_client.show_schema("metadefs/properties")
+ self.assertEqual("properties", body['name'])
+
+ @test.idempotent_id('dde4a891-b38b-3bf0-a3b2-e03ee67b3a3e')
+ def test_get_metadata_tag_schema(self):
+ # Test to get tag schema
+ body = self.schemas_client.show_schema("metadefs/tag")
+ self.assertEqual("tag", body['name'])
+
+ @test.idempotent_id('cde4a891-b38b-3bf0-a3b2-e03ee67b3a3a')
+ def test_get_metadata_tags_schema(self):
+ # Test to get tags schema
+ body = self.schemas_client.show_schema("metadefs/tags")
+ self.assertEqual("tags", body['name'])
diff --git a/tempest/api/network/admin/test_routers_dvr.py b/tempest/api/network/admin/test_routers_dvr.py
index 36cb15f..aaac921 100644
--- a/tempest/api/network/admin/test_routers_dvr.py
+++ b/tempest/api/network/admin/test_routers_dvr.py
@@ -13,6 +13,8 @@
# License for the specific language governing permissions and limitations
# under the License.
+import testtools
+
from tempest.api.network import base_routers as base
from tempest.common.utils import data_utils
from tempest import test
@@ -80,6 +82,8 @@
self.assertFalse(router['router']['distributed'])
@test.idempotent_id('acd43596-c1fb-439d-ada8-31ad48ae3c2e')
+ @testtools.skipUnless(test.is_extension_enabled('l3-ha', 'network'),
+ 'HA routers are not available.')
def test_centralized_router_update_to_dvr(self):
"""Test centralized router update
@@ -94,13 +98,22 @@
attribute will be set to True
"""
name = data_utils.rand_name('router')
+ tenant_id = self.routers_client.tenant_id
# router needs to be in admin state down in order to be upgraded to DVR
+ # l3ha routers are not upgradable to dvr, make it explicitly non ha
router = self.admin_routers_client.create_router(name=name,
distributed=False,
- admin_state_up=False)
+ admin_state_up=False,
+ ha=False,
+ tenant_id=tenant_id)
+ router_id = router['router']['id']
self.addCleanup(self.admin_routers_client.delete_router,
- router['router']['id'])
+ router_id)
self.assertFalse(router['router']['distributed'])
router = self.admin_routers_client.update_router(
- router['router']['id'], distributed=True)
+ router_id, distributed=True)
self.assertTrue(router['router']['distributed'])
+ show_body = self.admin_routers_client.show_router(router_id)
+ self.assertTrue(show_body['router']['distributed'])
+ show_body = self.routers_client.show_router(router_id)
+ self.assertNotIn('distributed', show_body['router'])
diff --git a/tempest/api/network/base.py b/tempest/api/network/base.py
index 629926d..deefbeb 100644
--- a/tempest/api/network/base.py
+++ b/tempest/api/network/base.py
@@ -135,12 +135,12 @@
super(BaseNetworkTest, cls).resource_cleanup()
@classmethod
- def create_network(cls, network_name=None):
+ def create_network(cls, network_name=None, **kwargs):
"""Wrapper utility that returns a test network."""
network_name = network_name or data_utils.rand_name(
- cls.__name__ + "-network")
+ cls.__name__ + '-test-network')
- body = cls.networks_client.create_network(name=network_name)
+ body = cls.networks_client.create_network(name=network_name, **kwargs)
network = body['network']
cls.networks.append(network)
return network
diff --git a/tempest/api/network/test_dhcp_ipv6.py b/tempest/api/network/test_dhcp_ipv6.py
index 4bc4262..84c48ec 100644
--- a/tempest/api/network/test_dhcp_ipv6.py
+++ b/tempest/api/network/test_dhcp_ipv6.py
@@ -348,9 +348,7 @@
def _create_subnet_router(self, kwargs):
subnet = self.create_subnet(self.network, **kwargs)
- router = self.create_router(
- router_name=data_utils.rand_name("routerv6-"),
- admin_state_up=True)
+ router = self.create_router(admin_state_up=True)
port = self.create_router_interface(router['id'],
subnet['id'])
body = self.ports_client.show_port(port['port_id'])
diff --git a/tempest/api/network/test_floating_ips_negative.py b/tempest/api/network/test_floating_ips_negative.py
index 963d99d..7ffc30f 100644
--- a/tempest/api/network/test_floating_ips_negative.py
+++ b/tempest/api/network/test_floating_ips_negative.py
@@ -15,7 +15,6 @@
# under the License.
from tempest.api.network import base
-from tempest.common.utils import data_utils
from tempest import config
from tempest.lib import exceptions as lib_exc
from tempest import test
@@ -45,7 +44,7 @@
# Create a network with a subnet connected to a router.
cls.network = cls.create_network()
cls.subnet = cls.create_subnet(cls.network)
- cls.router = cls.create_router(data_utils.rand_name('router'))
+ cls.router = cls.create_router()
cls.create_router_interface(cls.router['id'], cls.subnet['id'])
cls.port = cls.create_port(cls.network)
diff --git a/tempest/api/network/test_networks.py b/tempest/api/network/test_networks.py
index dadaaba..acac22b 100644
--- a/tempest/api/network/test_networks.py
+++ b/tempest/api/network/test_networks.py
@@ -27,39 +27,11 @@
CONF = config.CONF
-class NetworksTest(base.BaseNetworkTest):
- """Tests the following operations in the Neutron API:
-
- create a network for a project
- list project's networks
- show a project network details
- create a subnet for a project
- list project's subnets
- show a project subnet details
- network update
- subnet update
- delete a network also deletes its subnets
- list external networks
-
- All subnet tests are run once with ipv4 and once with ipv6.
-
- v2.0 of the Neutron API is assumed. It is also assumed that the following
- options are defined in the [network] section of etc/tempest.conf:
-
- project_network_cidr with a block of cidr's from which smaller blocks
- can be allocated for project ipv4 subnets
-
- project_network_v6_cidr is the equivalent for ipv6 subnets
-
- project_network_mask_bits with the mask bits to be used to partition
- the block defined by project_network_cidr
-
- project_network_v6_mask_bits is the equivalent for ipv6 subnets
- """
+class BaseNetworkTestResources(base.BaseNetworkTest):
@classmethod
def resource_setup(cls):
- super(NetworksTest, cls).resource_setup()
+ super(BaseNetworkTestResources, cls).resource_setup()
cls.network = cls.create_network()
cls.name = cls.network['name']
cls.subnet = cls._create_subnet_with_last_subnet_block(cls.network,
@@ -171,6 +143,37 @@
self.networks.pop()
self.subnets.pop()
+
+class NetworksTest(BaseNetworkTestResources):
+ """Tests the following operations in the Neutron API:
+
+ create a network for a project
+ list project's networks
+ show a project network details
+ create a subnet for a project
+ list project's subnets
+ show a project subnet details
+ network update
+ subnet update
+ delete a network also deletes its subnets
+ list external networks
+
+ All subnet tests are run once with ipv4 and once with ipv6.
+
+ v2.0 of the Neutron API is assumed. It is also assumed that the following
+ options are defined in the [network] section of etc/tempest.conf:
+
+ project_network_cidr with a block of cidr's from which smaller blocks
+ can be allocated for project ipv4 subnets
+
+ project_network_v6_cidr is the equivalent for ipv6 subnets
+
+ project_network_mask_bits with the mask bits to be used to partition
+ the block defined by project_network_cidr
+
+ project_network_v6_mask_bits is the equivalent for ipv6 subnets
+ """
+
@test.attr(type='smoke')
@test.idempotent_id('0e269138-0da6-4efc-a46d-578161e7b221')
def test_create_update_delete_network_subnet(self):
@@ -206,12 +209,16 @@
def test_show_network_fields(self):
# Verify specific fields of a network
fields = ['id', 'name']
+ if test.is_extension_enabled('net-mtu', 'network'):
+ fields.append('mtu')
body = self.networks_client.show_network(self.network['id'],
fields=fields)
network = body['network']
self.assertEqual(sorted(network.keys()), sorted(fields))
for field_name in fields:
self.assertEqual(network[field_name], self.network[field_name])
+ self.assertNotIn('tenant_id', network)
+ self.assertNotIn('project_id', network)
@test.attr(type='smoke')
@test.idempotent_id('f7ffdeda-e200-4a7a-bcbe-05716e86bf43')
@@ -226,6 +233,8 @@
def test_list_networks_fields(self):
# Verify specific fields of the networks
fields = ['id', 'name']
+ if test.is_extension_enabled('net-mtu', 'network'):
+ fields.append('mtu')
body = self.networks_client.list_networks(fields=fields)
networks = body['networks']
self.assertNotEmpty(networks, "Network list returned is empty")
@@ -276,29 +285,22 @@
@test.idempotent_id('f04f61a9-b7f3-4194-90b2-9bcf660d1bfe')
def test_delete_network_with_subnet(self):
# Creates a network
- name = data_utils.rand_name('network-')
- body = self.networks_client.create_network(name=name)
- network = body['network']
+ network = self.create_network()
net_id = network['id']
self.addCleanup(test_utils.call_and_ignore_notfound_exc,
- self.networks_client.delete_network, net_id)
+ self._delete_network, network)
# Find a cidr that is not in use yet and create a subnet with it
subnet = self.create_subnet(network)
subnet_id = subnet['id']
# Delete network while the subnet still exists
- body = self.networks_client.delete_network(net_id)
+ self.networks_client.delete_network(net_id)
# Verify that the subnet got automatically deleted.
self.assertRaises(lib_exc.NotFound, self.subnets_client.show_subnet,
subnet_id)
- # Since create_subnet adds the subnet to the delete list, and it is
- # actually deleted here - this will create and issue, hence remove
- # it from the list.
- self.subnets.pop()
-
@test.idempotent_id('d2d596e2-8e76-47a9-ac51-d4648009f4d3')
def test_create_delete_subnet_without_gateway(self):
self._create_verify_delete_subnet()
@@ -389,6 +391,21 @@
network_id=CONF.network.public_network_id)
self.assertEmpty(body['subnets'], "Public subnets visible")
+ @test.idempotent_id('c72c1c0c-2193-4aca-ccc4-b1442640bbbb')
+ @test.requires_ext(extension="standard-attr-description",
+ service="network")
+ def test_create_update_network_description(self):
+ body = self.create_network(description='d1')
+ self.assertEqual('d1', body['description'])
+ net_id = body['id']
+ body = self.networks_client.list_networks(id=net_id)['networks'][0]
+ self.assertEqual('d1', body['description'])
+ body = self.networks_client.update_network(body['id'],
+ description='d2')
+ self.assertEqual('d2', body['network']['description'])
+ body = self.networks_client.list_networks(id=net_id)['networks'][0]
+ self.assertEqual('d2', body['description'])
+
class BulkNetworkOpsTest(base.BaseNetworkTest):
"""Tests the following operations in the Neutron API:
@@ -566,7 +583,9 @@
'Subnet are not in the same network')
-class NetworksIpV6TestAttrs(NetworksIpV6Test):
+class NetworksIpV6TestAttrs(BaseNetworkTestResources):
+
+ _ip_version = 6
@classmethod
def skip_checks(cls):
diff --git a/tempest/api/network/test_ports.py b/tempest/api/network/test_ports.py
index 2979f7d..15d289d 100644
--- a/tempest/api/network/test_ports.py
+++ b/tempest/api/network/test_ports.py
@@ -199,7 +199,7 @@
self.addCleanup(self.networks_client.delete_network, network['id'])
subnet = self.create_subnet(network)
self.addCleanup(self.subnets_client.delete_subnet, subnet['id'])
- router = self.create_router(data_utils.rand_name('router-'))
+ router = self.create_router()
self.addCleanup(self.routers_client.delete_router, router['id'])
port = self.ports_client.create_port(network_id=network['id'])
# Add router interface to port created above
diff --git a/tempest/api/network/test_routers.py b/tempest/api/network/test_routers.py
index de2e71f..e989b69 100644
--- a/tempest/api/network/test_routers.py
+++ b/tempest/api/network/test_routers.py
@@ -49,41 +49,31 @@
@test.idempotent_id('f64403e2-8483-4b34-8ccd-b09a87bcc68c')
def test_create_show_list_update_delete_router(self):
# Create a router
- # NOTE(salv-orlando): Do not invoke self.create_router
- # as we need to check the response code
- name = data_utils.rand_name('router-')
- create_body = self.routers_client.create_router(
- name=name, external_gateway_info={
- "network_id": CONF.network.public_network_id},
- admin_state_up=False)
- self.addCleanup(self._delete_router, create_body['router']['id'])
- self.assertEqual(create_body['router']['name'], name)
+ router = self._create_router(
+ admin_state_up=False,
+ external_network_id=CONF.network.public_network_id)
+ self.assertEqual(router['admin_state_up'], False)
self.assertEqual(
- create_body['router']['external_gateway_info']['network_id'],
+ router['external_gateway_info']['network_id'],
CONF.network.public_network_id)
- self.assertEqual(create_body['router']['admin_state_up'], False)
# Show details of the created router
- show_body = self.routers_client.show_router(
- create_body['router']['id'])
- self.assertEqual(show_body['router']['name'], name)
+ router_show = self.routers_client.show_router(
+ router['id'])['router']
+ self.assertEqual(router_show['name'], router['name'])
self.assertEqual(
- show_body['router']['external_gateway_info']['network_id'],
+ router_show['external_gateway_info']['network_id'],
CONF.network.public_network_id)
- self.assertEqual(show_body['router']['admin_state_up'], False)
# List routers and verify if created router is there in response
- list_body = self.routers_client.list_routers()
- routers_list = list()
- for router in list_body['routers']:
- routers_list.append(router['id'])
- self.assertIn(create_body['router']['id'], routers_list)
+ routers = self.routers_client.list_routers()['routers']
+ self.assertIn(router['id'], map(lambda x: x['id'], routers))
# Update the name of router and verify if it is updated
- updated_name = 'updated ' + name
- update_body = self.routers_client.update_router(
- create_body['router']['id'], name=updated_name)
- self.assertEqual(update_body['router']['name'], updated_name)
- show_body = self.routers_client.show_router(
- create_body['router']['id'])
- self.assertEqual(show_body['router']['name'], updated_name)
+ updated_name = 'updated' + router['name']
+ router_update = self.routers_client.update_router(
+ router['id'], name=updated_name)['router']
+ self.assertEqual(router_update['name'], updated_name)
+ router_show = self.routers_client.show_router(
+ router['id'])['router']
+ self.assertEqual(router_show['name'], updated_name)
@test.idempotent_id('e54dd3a3-4352-4921-b09d-44369ae17397')
def test_create_router_setting_project_id(self):
@@ -390,35 +380,3 @@
class RoutersIpV6Test(RoutersTest):
_ip_version = 6
-
-
-class DvrRoutersTest(base.BaseRouterTest):
-
- @classmethod
- def skip_checks(cls):
- super(DvrRoutersTest, cls).skip_checks()
- if not test.is_extension_enabled('dvr', 'network'):
- msg = "DVR extension not enabled."
- raise cls.skipException(msg)
-
- @test.idempotent_id('141297aa-3424-455d-aa8d-f2d95731e00a')
- def test_create_distributed_router(self):
- name = data_utils.rand_name('router')
- create_body = self.admin_routers_client.create_router(
- name=name, distributed=True)
- self.addCleanup(self._delete_router,
- create_body['router']['id'],
- self.admin_routers_client)
- self.assertTrue(create_body['router']['distributed'])
-
- @test.idempotent_id('644d7a4a-01a1-4b68-bb8d-0c0042cb1729')
- def test_convert_centralized_router(self):
- router = self._create_router()
- self.assertNotIn('distributed', router)
- update_body = self.admin_routers_client.update_router(router['id'],
- distributed=True)
- self.assertTrue(update_body['router']['distributed'])
- show_body = self.admin_routers_client.show_router(router['id'])
- self.assertTrue(show_body['router']['distributed'])
- show_body = self.routers_client.show_router(router['id'])
- self.assertNotIn('distributed', show_body['router'])
diff --git a/tempest/api/network/test_routers_negative.py b/tempest/api/network/test_routers_negative.py
index 354e6d1..b3983de 100644
--- a/tempest/api/network/test_routers_negative.py
+++ b/tempest/api/network/test_routers_negative.py
@@ -134,7 +134,5 @@
@test.attr(type=['negative'])
@test.idempotent_id('4990b055-8fc7-48ab-bba7-aa28beaad0b9')
def test_router_create_tenant_distributed_returns_forbidden(self):
- self.assertRaises(lib_exc.Forbidden,
- self.create_router,
- data_utils.rand_name('router'),
+ self.assertRaises(lib_exc.Forbidden, self.create_router,
distributed=True)
diff --git a/tempest/api/object_storage/base.py b/tempest/api/object_storage/base.py
index fd973c6..52b0a9c 100644
--- a/tempest/api/object_storage/base.py
+++ b/tempest/api/object_storage/base.py
@@ -13,6 +13,8 @@
# License for the specific language governing permissions and limitations
# under the License.
+import time
+
from tempest.common import custom_matchers
from tempest import config
from tempest.lib.common.utils import data_utils
@@ -102,6 +104,10 @@
The containers should be visible from the container_client given.
Will not throw any error if the containers don't exist.
Will not check that object and container deletions succeed.
+ After delete all the objects from a container, it will wait 2
+ seconds before delete the container itself, in order to deployments
+ using HA proxy sync the deletion properly, otherwise, the container
+ might fail to be deleted because it's not empty.
:param container_client: if None, use cls.container_client, this means
that the default testing user will be used (see 'username' in
@@ -114,11 +120,16 @@
object_client = cls.object_client
for cont in cls.containers:
try:
- objlist = container_client.list_all_container_objects(cont)
+ params = {'limit': 9999, 'format': 'json'}
+ resp, objlist = container_client.list_container_contents(
+ cont, params)
# delete every object in the container
for obj in objlist:
test_utils.call_and_ignore_notfound_exc(
object_client.delete_object, cont, obj['name'])
+ # sleep 2 seconds to sync the deletion of the objects
+ # in HA deployment
+ time.sleep(2)
container_client.delete_container(cont)
except lib_exc.NotFound:
pass
diff --git a/tempest/api/object_storage/test_account_bulk.py b/tempest/api/object_storage/test_account_bulk.py
index 7292ee9..a75ed98 100644
--- a/tempest/api/object_storage/test_account_bulk.py
+++ b/tempest/api/object_storage/test_account_bulk.py
@@ -66,7 +66,7 @@
self.assertNotIn(container_name, body)
@test.idempotent_id('a407de51-1983-47cc-9f14-47c2b059413c')
- @test.requires_ext(extension='bulk', service='object')
+ @test.requires_ext(extension='bulk_upload', service='object')
def test_extract_archive(self):
# Test bulk operation of file upload with an archived file
filepath, container_name, object_name = self._create_archive()
@@ -102,7 +102,7 @@
self.assertIn(object_name, [c['name'] for c in contents_list])
@test.idempotent_id('c075e682-0d2a-43b2-808d-4116200d736d')
- @test.requires_ext(extension='bulk', service='object')
+ @test.requires_ext(extension='bulk_delete', service='object')
def test_bulk_delete(self):
# Test bulk operation of deleting multiple files
filepath, container_name, object_name = self._create_archive()
@@ -129,7 +129,7 @@
self._check_contents_deleted(container_name)
@test.idempotent_id('dbea2bcb-efbb-4674-ac8a-a5a0e33d1d79')
- @test.requires_ext(extension='bulk', service='object')
+ @test.requires_ext(extension='bulk_delete', service='object')
def test_bulk_delete_by_POST(self):
# Test bulk operation of deleting multiple files
filepath, container_name, object_name = self._create_archive()
diff --git a/tempest/api/object_storage/test_account_services.py b/tempest/api/object_storage/test_account_services.py
index 33e5852..eda4568 100644
--- a/tempest/api/object_storage/test_account_services.py
+++ b/tempest/api/object_storage/test_account_services.py
@@ -121,7 +121,7 @@
self.assertHeaders(resp, 'Account', 'GET')
self.assertIsNotNone(container_list)
self.assertEqual(container_list.tag, 'account')
- self.assertTrue('name' in container_list.keys())
+ self.assertIn('name', container_list.keys())
self.assertEqual(container_list.find(".//container").tag, 'container')
self.assertEqual(container_list.find(".//name").tag, 'name')
self.assertEqual(container_list.find(".//count").tag, 'count')
@@ -209,7 +209,8 @@
self.account_client.list_account_containers(params=params)
self.assertHeaders(resp, 'Account', 'GET')
- self.assertTrue(len(container_list) <= limit, str(container_list))
+ self.assertLessEqual(len(container_list), limit,
+ str(container_list))
@test.idempotent_id('888a3f0e-7214-4806-8e50-5e0c9a69bb5e')
def test_list_containers_with_limit_and_end_marker(self):
diff --git a/tempest/api/object_storage/test_container_services.py b/tempest/api/object_storage/test_container_services.py
index dbe8b4a..9ce1b18 100644
--- a/tempest/api/object_storage/test_container_services.py
+++ b/tempest/api/object_storage/test_container_services.py
@@ -205,7 +205,7 @@
self.assertIsNotNone(object_list)
self.assertEqual(object_list.tag, 'container')
- self.assertTrue('name' in object_list.keys())
+ self.assertIn('name', object_list.keys())
self.assertEqual(object_list.find(".//object").tag, 'object')
self.assertEqual(object_list.find(".//name").tag, 'name')
self.assertEqual(object_list.find(".//hash").tag, 'hash')
diff --git a/tempest/api/object_storage/test_container_services_negative.py b/tempest/api/object_storage/test_container_services_negative.py
index ed99eb2..df91325 100644
--- a/tempest/api/object_storage/test_container_services_negative.py
+++ b/tempest/api/object_storage/test_container_services_negative.py
@@ -13,11 +13,16 @@
# License for the specific language governing permissions and limitations
# under the License.
+import testtools
+
from tempest.api.object_storage import base
+from tempest import config
from tempest.lib.common.utils import data_utils
from tempest.lib import exceptions
from tempest import test
+CONF = config.CONF
+
class ContainerNegativeTest(base.BaseObjectTest):
@@ -25,12 +30,16 @@
def resource_setup(cls):
super(ContainerNegativeTest, cls).resource_setup()
- # use /info to get default constraints
- _, body = cls.account_client.list_extensions()
- cls.constraints = body['swift']
+ if CONF.object_storage_feature_enabled.discoverability:
+ # use /info to get default constraints
+ _, body = cls.account_client.list_extensions()
+ cls.constraints = body['swift']
@test.attr(type=["negative"])
@test.idempotent_id('30686921-4bed-4764-a038-40d741ed4e78')
+ @testtools.skipUnless(
+ CONF.object_storage_feature_enabled.discoverability,
+ 'Discoverability function is disabled')
def test_create_container_name_exceeds_max_length(self):
# Attempts to create a container name that is longer than max
max_length = self.constraints['max_container_name_length']
@@ -44,6 +53,9 @@
@test.attr(type=["negative"])
@test.idempotent_id('41e645bf-2e68-4f84-bf7b-c71aa5cd76ce')
+ @testtools.skipUnless(
+ CONF.object_storage_feature_enabled.discoverability,
+ 'Discoverability function is disabled')
def test_create_container_metadata_name_exceeds_max_length(self):
# Attempts to create container with metadata name
# that is longer than max.
@@ -58,6 +70,9 @@
@test.attr(type=["negative"])
@test.idempotent_id('81e36922-326b-4b7c-8155-3bbceecd7a82')
+ @testtools.skipUnless(
+ CONF.object_storage_feature_enabled.discoverability,
+ 'Discoverability function is disabled')
def test_create_container_metadata_value_exceeds_max_length(self):
# Attempts to create container with metadata value
# that is longer than max.
@@ -72,6 +87,9 @@
@test.attr(type=["negative"])
@test.idempotent_id('ac666539-d566-4f02-8ceb-58e968dfb732')
+ @testtools.skipUnless(
+ CONF.object_storage_feature_enabled.discoverability,
+ 'Discoverability function is disabled')
def test_create_container_metadata_exceeds_overall_metadata_count(self):
# Attempts to create container with metadata that exceeds the
# default count
@@ -92,47 +110,39 @@
def test_get_metadata_headers_with_invalid_container_name(self):
# Attempts to retrieve metadata headers with an invalid
# container name.
- invalid_name = data_utils.rand_name(name="TestInvalidContainer")
-
self.assertRaises(exceptions.NotFound,
self.container_client.list_container_metadata,
- invalid_name)
+ 'invalid_container_name')
@test.attr(type=["negative"])
@test.idempotent_id('125a24fa-90a7-4cfc-b604-44e49d788390')
def test_update_metadata_with_nonexistent_container_name(self):
# Attempts to update metadata using a nonexistent container name.
- nonexistent_name = data_utils.rand_name(
- name="TestNonexistentContainer")
metadata = {'animal': 'penguin'}
self.assertRaises(exceptions.NotFound,
self.container_client.update_container_metadata,
- nonexistent_name, metadata)
+ 'nonexistent_container_name', metadata)
@test.attr(type=["negative"])
@test.idempotent_id('65387dbf-a0e2-4aac-9ddc-16eb3f1f69ba')
def test_delete_with_nonexistent_container_name(self):
# Attempts to delete metadata using a nonexistent container name.
- nonexistent_name = data_utils.rand_name(
- name="TestNonexistentContainer")
metadata = {'animal': 'penguin'}
self.assertRaises(exceptions.NotFound,
self.container_client.delete_container_metadata,
- nonexistent_name, metadata)
+ 'nonexistent_container_name', metadata)
@test.attr(type=["negative"])
@test.idempotent_id('14331d21-1e81-420a-beea-19cb5e5207f5')
def test_list_all_container_objects_with_nonexistent_container(self):
# Attempts to get a listing of all objects on a container
# that doesn't exist.
- nonexistent_name = data_utils.rand_name(
- name="TestNonexistentContainer")
-
+ params = {'limit': 9999, 'format': 'json'}
self.assertRaises(exceptions.NotFound,
- self.container_client.list_all_container_objects,
- nonexistent_name)
+ self.container_client.list_container_contents,
+ 'nonexistent_container_name', params)
@test.attr(type=["negative"])
@test.idempotent_id('86b2ab08-92d5-493d-acd2-85f0c848819e')
@@ -143,10 +153,10 @@
# delete container
resp, _ = self.container_client.delete_container(container_name)
self.assertHeaders(resp, 'Container', 'DELETE')
-
+ params = {'limit': 9999, 'format': 'json'}
self.assertRaises(exceptions.NotFound,
- self.container_client.list_all_container_objects,
- container_name)
+ self.container_client.list_container_contents,
+ container_name, params)
@test.attr(type=["negative"])
@test.idempotent_id('42da116e-1e8c-4c96-9e06-2f13884ed2b1')
diff --git a/tempest/api/orchestration/stacks/test_neutron_resources.py b/tempest/api/orchestration/stacks/test_neutron_resources.py
index 3f45634..6d27502 100644
--- a/tempest/api/orchestration/stacks/test_neutron_resources.py
+++ b/tempest/api/orchestration/stacks/test_neutron_resources.py
@@ -10,15 +10,13 @@
# License for the specific language governing permissions and limitations
# under the License.
-
-import logging
-
import netaddr
+from oslo_log import log as logging
from tempest.api.orchestration import base
from tempest.common.utils import data_utils
from tempest import config
-from tempest import exceptions
+from tempest.lib import exceptions
from tempest import test
CONF = config.CONF
@@ -79,7 +77,7 @@
cls.client.wait_for_stack_status(cls.stack_id, 'CREATE_COMPLETE')
resources = (cls.client.list_resources(cls.stack_identifier)
['resources'])
- except exceptions.TimeoutException as e:
+ except exceptions.TimeoutException:
if CONF.compute_feature_enabled.console_output:
# attempt to log the server console to help with debugging
# the cause of the server not signalling the waitcondition
@@ -91,7 +89,7 @@
output = cls.servers_client.get_console_output(
server_id)['output']
LOG.debug(output)
- raise e
+ raise
cls.test_resources = {}
for resource in resources:
diff --git a/tempest/api/orchestration/stacks/test_nova_keypair_resources.py b/tempest/api/orchestration/stacks/test_nova_keypair_resources.py
index 8d12e75..16d8180 100644
--- a/tempest/api/orchestration/stacks/test_nova_keypair_resources.py
+++ b/tempest/api/orchestration/stacks/test_nova_keypair_resources.py
@@ -78,12 +78,12 @@
self.assertIn('-----BEGIN', output_map['KeyPair_PrivateKey'])
# Test that second key generated public key, and private key is not
# in the output due to save_private_key = false
- self.assertTrue('KeyPairDontSavePrivate_PublicKey' in output_map)
- self.assertTrue('Generated' in
- output_map['KeyPairDontSavePrivate_PublicKey'])
- self.assertTrue(u'KeyPairDontSavePrivate_PrivateKey' in output_map)
+ self.assertIn('KeyPairDontSavePrivate_PublicKey', output_map)
+ self.assertIn('Generated',
+ output_map['KeyPairDontSavePrivate_PublicKey'])
+ self.assertIn(u'KeyPairDontSavePrivate_PrivateKey', output_map)
private_key = output_map['KeyPairDontSavePrivate_PrivateKey']
- self.assertTrue(len(private_key) == 0)
+ self.assertEqual(0, len(private_key))
class NovaKeyPairResourcesAWSTest(NovaKeyPairResourcesYAMLTest):
diff --git a/tempest/api/orchestration/stacks/test_templates_negative.py b/tempest/api/orchestration/stacks/test_templates_negative.py
index 24e10dd..f8245c1 100644
--- a/tempest/api/orchestration/stacks/test_templates_negative.py
+++ b/tempest/api/orchestration/stacks/test_templates_negative.py
@@ -27,7 +27,7 @@
Type: AWS::IAM::User
"""
- invalid_template_url = 'http://www.example.com/template.yaml'
+ invalid_template_url = 'http:///template.yaml'
@classmethod
def resource_setup(cls):
@@ -57,4 +57,4 @@
}
"""
- invalid_template_url = 'http://www.example.com/template.template'
+ invalid_template_url = 'http:///template.template'
diff --git a/tempest/api/volume/admin/test_multi_backend.py b/tempest/api/volume/admin/test_multi_backend.py
index 120dbb1..5703313 100644
--- a/tempest/api/volume/admin/test_multi_backend.py
+++ b/tempest/api/volume/admin/test_multi_backend.py
@@ -129,7 +129,7 @@
volume1_host = volume['os-vol-host-attr:host']
msg = ("multi-backend reporting incorrect values for volume %s" %
volume_id)
- self.assertTrue(len(volume1_host.split("@")) > 1, msg)
+ self.assertGreater(len(volume1_host.split("@")), 1, msg)
def _test_backend_name_distinction(self, volume_id_list):
# this test checks that the volumes created at setUp don't
diff --git a/tempest/api/volume/admin/test_snapshots_actions.py b/tempest/api/volume/admin/test_snapshots_actions.py
index 1468e90..29a161b 100644
--- a/tempest/api/volume/admin/test_snapshots_actions.py
+++ b/tempest/api/volume/admin/test_snapshots_actions.py
@@ -14,7 +14,6 @@
# under the License.
from tempest.api.volume import base
-from tempest.common.utils import data_utils
from tempest import config
from tempest import test
@@ -29,25 +28,14 @@
raise cls.skipException("Cinder snapshot feature disabled")
@classmethod
- def setup_clients(cls):
- super(SnapshotsActionsV2Test, cls).setup_clients()
- cls.client = cls.snapshots_client
-
- @classmethod
def resource_setup(cls):
super(SnapshotsActionsV2Test, cls).resource_setup()
# Create a test shared volume for tests
- vol_name = data_utils.rand_name(cls.__name__ + '-Volume')
- cls.name_field = cls.special_fields['name_field']
- params = {cls.name_field: vol_name}
- cls.volume = cls.create_volume(**params)
+ cls.volume = cls.create_volume()
# Create a test shared snapshot for tests
- snap_name = data_utils.rand_name(cls.__name__ + '-Snapshot')
- params = {cls.name_field: snap_name}
- cls.snapshot = cls.create_snapshot(
- volume_id=cls.volume['id'], **params)
+ cls.snapshot = cls.create_snapshot(volume_id=cls.volume['id'])
def tearDown(self):
# Set snapshot's status to available after test
@@ -66,7 +54,7 @@
reset_snapshot_status(temp_snapshot['id'], status)
self.admin_snapshots_client.\
force_delete_snapshot(temp_snapshot['id'])
- self.client.wait_for_resource_deletion(temp_snapshot['id'])
+ self.snapshots_client.wait_for_resource_deletion(temp_snapshot['id'])
def _get_progress_alias(self):
return 'os-extended-snapshot-attributes:progress'
@@ -92,8 +80,9 @@
progress = '80%'
status = 'error'
progress_alias = self._get_progress_alias()
- self.client.update_snapshot_status(self.snapshot['id'],
- status=status, progress=progress)
+ self.snapshots_client.update_snapshot_status(self.snapshot['id'],
+ status=status,
+ progress=progress)
snapshot_get = self.admin_snapshots_client.show_snapshot(
self.snapshot['id'])['snapshot']
self.assertEqual(status, snapshot_get['status'])
diff --git a/tempest/api/volume/admin/test_volume_hosts.py b/tempest/api/volume/admin/test_volume_hosts.py
index b58c525..f6de9a6 100644
--- a/tempest/api/volume/admin/test_volume_hosts.py
+++ b/tempest/api/volume/admin/test_volume_hosts.py
@@ -22,8 +22,8 @@
@test.idempotent_id('d5f3efa2-6684-4190-9ced-1c2f526352ad')
def test_list_hosts(self):
hosts = self.admin_hosts_client.list_hosts()['hosts']
- self.assertTrue(len(hosts) >= 2, "No. of hosts are < 2,"
- "response of list hosts is: % s" % hosts)
+ self.assertGreaterEqual(len(hosts), 2, "No. of hosts are < 2,"
+ "response of list hosts is: % s" % hosts)
class VolumeHostsAdminV1TestsJSON(VolumeHostsAdminV2TestsJSON):
diff --git a/tempest/api/volume/admin/test_volume_types.py b/tempest/api/volume/admin/test_volume_types.py
index 99f0a6b..3098cab 100644
--- a/tempest/api/volume/admin/test_volume_types.py
+++ b/tempest/api/volume/admin/test_volume_types.py
@@ -51,8 +51,7 @@
'size': CONF.volume.volume_size}
# Create volume
- volume = self.volumes_client.create_volume(**params)['volume']
- self.addCleanup(self.delete_volume, self.volumes_client, volume['id'])
+ volume = self.create_volume(**params)
self.assertEqual(volume_types[0]['name'], volume["volume_type"])
self.assertEqual(volume[self.name_field], vol_name,
"The created volume name is not equal "
@@ -102,8 +101,8 @@
self.assertEqual(description, body['description'],
"The created volume_type_description name is "
"not equal to the requested name")
- self.assertTrue(body['id'] is not None,
- "Field volume_type id is empty or not found.")
+ self.assertIsNotNone(body['id'],
+ "Field volume_type id is empty or not found.")
fetched_volume_type = self.admin_volume_types_client.show_volume_type(
body['id'])['volume_type']
self.assertEqual(name, fetched_volume_type['name'],
diff --git a/tempest/api/volume/admin/test_volumes_actions.py b/tempest/api/volume/admin/test_volumes_actions.py
index e7a3f62..261e652 100644
--- a/tempest/api/volume/admin/test_volumes_actions.py
+++ b/tempest/api/volume/admin/test_volumes_actions.py
@@ -23,11 +23,6 @@
class VolumesActionsV2Test(base.BaseVolumeAdminTest):
@classmethod
- def setup_clients(cls):
- super(VolumesActionsV2Test, cls).setup_clients()
- cls.client = cls.volumes_client
-
- @classmethod
def resource_setup(cls):
super(VolumesActionsV2Test, cls).resource_setup()
@@ -47,7 +42,7 @@
self.admin_volume_client.reset_volume_status(
temp_volume['id'], status=status)
self.admin_volume_client.force_delete_volume(temp_volume['id'])
- self.client.wait_for_resource_deletion(temp_volume['id'])
+ self.volumes_client.wait_for_resource_deletion(temp_volume['id'])
@test.idempotent_id('d063f96e-a2e0-4f34-8b8a-395c42de1845')
def test_volume_reset_status(self):
diff --git a/tempest/api/volume/admin/test_volumes_backup.py b/tempest/api/volume/admin/test_volumes_backup.py
index 73f1f8f..61d4ba7 100644
--- a/tempest/api/volume/admin/test_volumes_backup.py
+++ b/tempest/api/volume/admin/test_volumes_backup.py
@@ -33,12 +33,6 @@
if not CONF.volume_feature_enabled.backup:
raise cls.skipException("Cinder backup feature disabled")
- @classmethod
- def resource_setup(cls):
- super(VolumesBackupsAdminV2Test, cls).resource_setup()
-
- cls.volume = cls.create_volume()
-
def _delete_backup(self, backup_id):
self.admin_backups_client.delete_backup(backup_id)
self.admin_backups_client.wait_for_resource_deletion(backup_id)
@@ -62,14 +56,13 @@
Cinder allows exporting DB backup information through its API so it can
be imported back in case of a DB loss.
"""
+ volume = self.create_volume()
# Create backup
backup_name = data_utils.rand_name(self.__class__.__name__ + '-Backup')
- backup = (self.admin_backups_client.create_backup(
- volume_id=self.volume['id'], name=backup_name)['backup'])
- self.addCleanup(self._delete_backup, backup['id'])
+ backup = (self.create_backup(backup_client=self.admin_backups_client,
+ volume_id=volume['id'],
+ name=backup_name))
self.assertEqual(backup_name, backup['name'])
- waiters.wait_for_backup_status(self.admin_backups_client,
- backup['id'], 'available')
# Export Backup
export_backup = (self.admin_backups_client.export_backup(backup['id'])
@@ -126,16 +119,15 @@
@test.idempotent_id('47a35425-a891-4e13-961c-c45deea21e94')
def test_volume_backup_reset_status(self):
+ # Create a volume
+ volume = self.create_volume()
# Create a backup
backup_name = data_utils.rand_name(
self.__class__.__name__ + '-Backup')
- backup = self.admin_backups_client.create_backup(
- volume_id=self.volume['id'], name=backup_name)['backup']
- self.addCleanup(self.admin_backups_client.delete_backup,
- backup['id'])
+ backup = self.create_backup(backup_client=self.admin_backups_client,
+ volume_id=volume['id'],
+ name=backup_name)
self.assertEqual(backup_name, backup['name'])
- waiters.wait_for_backup_status(self.admin_backups_client,
- backup['id'], 'available')
# Reset backup status to error
self.admin_backups_client.reset_backup_status(backup_id=backup['id'],
status="error")
diff --git a/tempest/api/volume/admin/v2/test_volume_type_access.py b/tempest/api/volume/admin/v2/test_volume_type_access.py
index 91ff5af..80dbf12 100644
--- a/tempest/api/volume/admin/v2/test_volume_type_access.py
+++ b/tempest/api/volume/admin/v2/test_volume_type_access.py
@@ -16,7 +16,6 @@
import operator
from tempest.api.volume import base
-from tempest.common import waiters
from tempest import config
from tempest.lib import exceptions as lib_exc
from tempest import test
@@ -52,13 +51,7 @@
project=self.volumes_client.tenant_id)
# Creating a volume from primary tenant
- volume = self.volumes_client.create_volume(
- volume_type=volume_type['id'],
- size=CONF.volume.volume_size)['volume']
- self.addCleanup(self.delete_volume, self.volumes_client, volume['id'])
- waiters.wait_for_volume_status(self.volumes_client, volume['id'],
- 'available')
-
+ volume = self.create_volume(volume_type=volume_type['id'])
# Validating the created volume is based on the volume type
self.assertEqual(volume_type['name'], volume['volume_type'])
diff --git a/tempest/api/volume/admin/v2/test_volumes_list.py b/tempest/api/volume/admin/v2/test_volumes_list.py
index 4437803..cdd9df9 100644
--- a/tempest/api/volume/admin/v2/test_volumes_list.py
+++ b/tempest/api/volume/admin/v2/test_volumes_list.py
@@ -29,7 +29,9 @@
def resource_setup(cls):
super(VolumesListAdminV2TestJSON, cls).resource_setup()
# Create 3 test volumes
- cls.volume_list = []
+ # NOTE(zhufl): When using pre-provisioned credentials, the project
+ # may have volumes other than those created below.
+ cls.volume_list = cls.volumes_client.list_volumes()['volumes']
for i in range(3):
volume = cls.create_volume()
# Fetch volume details
@@ -59,5 +61,6 @@
# primary tenant
fetched_tenant_id = [operator.itemgetter(
'os-vol-tenant-attr:tenant_id')(item) for item in fetched_list]
- expected_tenant_id = [self.volumes_client.tenant_id] * 3
+ expected_tenant_id = [self.volumes_client.tenant_id] * \
+ len(self.volume_list)
self.assertEqual(expected_tenant_id, fetched_tenant_id)
diff --git a/tempest/api/volume/admin/v3/test_user_messages.py b/tempest/api/volume/admin/v3/test_user_messages.py
index 39a5dfa..257a434 100755
--- a/tempest/api/volume/admin/v3/test_user_messages.py
+++ b/tempest/api/volume/admin/v3/test_user_messages.py
@@ -15,9 +15,7 @@
from tempest.api.volume.v3 import base
from tempest.common.utils import data_utils
-from tempest.common import waiters
from tempest import config
-from tempest import exceptions
from tempest import test
CONF = config.CONF
@@ -47,21 +45,11 @@
'vendor_name': bad_vendor}
vol_type_name = data_utils.rand_name(
self.__class__.__name__ + '-volume-type')
- bogus_type = self.admin_volume_types_client.create_volume_type(
- name=vol_type_name,
- extra_specs=extra_specs)['volume_type']
- self.addCleanup(self.admin_volume_types_client.delete_volume_type,
- bogus_type['id'])
+ bogus_type = self.create_volume_type(
+ name=vol_type_name, extra_specs=extra_specs)
params = {'volume_type': bogus_type['id'],
'size': CONF.volume.volume_size}
- volume = self.volumes_client.create_volume(**params)['volume']
- self.addCleanup(self.delete_volume, self.volumes_client, volume['id'])
- try:
- waiters.wait_for_volume_status(self.volumes_client, volume['id'],
- 'error')
- except exceptions.VolumeBuildErrorException:
- # Error state is expected and desired
- pass
+ volume = self.create_volume(wait_until="error", **params)
messages = self.messages_client.list_messages()['messages']
message_id = None
for message in messages:
diff --git a/tempest/api/volume/api_microversion_fixture.py b/tempest/api/volume/api_microversion_fixture.py
index 6817eaa..64bc537 100644
--- a/tempest/api/volume/api_microversion_fixture.py
+++ b/tempest/api/volume/api_microversion_fixture.py
@@ -13,7 +13,7 @@
import fixtures
-from tempest.services.volume.base import base_v3_client
+from tempest.lib.services.volume.v3 import base_client
class APIMicroversionFixture(fixtures.Fixture):
@@ -23,8 +23,8 @@
def _setUp(self):
super(APIMicroversionFixture, self)._setUp()
- base_v3_client.VOLUME_MICROVERSION = self.volume_microversion
+ base_client.VOLUME_MICROVERSION = self.volume_microversion
self.addCleanup(self._reset_volume_microversion)
def _reset_volume_microversion(self):
- base_v3_client.VOLUME_MICROVERSION = None
+ base_client.VOLUME_MICROVERSION = None
diff --git a/tempest/api/volume/base.py b/tempest/api/volume/base.py
index b788d54..01e2c82 100644
--- a/tempest/api/volume/base.py
+++ b/tempest/api/volume/base.py
@@ -73,6 +73,7 @@
cls.volumes_extension_client = cls.os.volumes_extension_client
cls.availability_zone_client = (
cls.os.volume_availability_zone_client)
+ cls.volume_limits_client = cls.os.volume_limits_client
else:
cls.snapshots_client = cls.os.snapshots_v2_client
cls.volumes_client = cls.os.volumes_v2_client
@@ -80,6 +81,7 @@
cls.volumes_extension_client = cls.os.volumes_v2_extension_client
cls.availability_zone_client = (
cls.os.volume_v2_availability_zone_client)
+ cls.volume_limits_client = cls.os.volume_v2_limits_client
@classmethod
def resource_setup(cls):
@@ -108,26 +110,39 @@
super(BaseVolumeTest, cls).resource_cleanup()
@classmethod
- def create_volume(cls, **kwargs):
- """Wrapper utility that returns a test volume."""
+ def create_volume(cls, wait_until='available', **kwargs):
+ """Wrapper utility that returns a test volume.
+
+ :param wait_until: wait till volume status.
+ """
if 'size' not in kwargs:
kwargs['size'] = CONF.volume.volume_size
- name = data_utils.rand_name(cls.__name__ + '-Volume')
+ if 'imageRef' in kwargs:
+ image = cls.compute_images_client.show_image(
+ kwargs['imageRef'])['image']
+ min_disk = image.get('minDisk')
+ kwargs['size'] = max(kwargs['size'], min_disk)
name_field = cls.special_fields['name_field']
+ if name_field not in kwargs:
+ name = data_utils.rand_name(cls.__name__ + '-Volume')
+ kwargs[name_field] = name
- kwargs[name_field] = name
volume = cls.volumes_client.create_volume(**kwargs)['volume']
-
cls.volumes.append(volume)
- waiters.wait_for_volume_status(cls.volumes_client,
- volume['id'], 'available')
+ waiters.wait_for_volume_status(cls.volumes_client, volume['id'],
+ wait_until)
return volume
@classmethod
def create_snapshot(cls, volume_id=1, **kwargs):
"""Wrapper utility that returns a test snapshot."""
+ name_field = cls.special_fields['name_field']
+ if name_field not in kwargs:
+ name = data_utils.rand_name(cls.__name__ + '-Snapshot')
+ kwargs[name_field] = name
+
snapshot = cls.snapshots_client.create_snapshot(
volume_id=volume_id, **kwargs)['snapshot']
cls.snapshots.append(snapshot)
@@ -135,6 +150,18 @@
snapshot['id'], 'available')
return snapshot
+ def create_backup(self, volume_id, backup_client=None, **kwargs):
+ """Wrapper utility that returns a test backup."""
+ if backup_client is None:
+ backup_client = self.backups_client
+
+ backup = backup_client.create_backup(
+ volume_id=volume_id, **kwargs)['backup']
+ self.addCleanup(backup_client.delete_backup, backup['id'])
+ waiters.wait_for_backup_status(backup_client, backup['id'],
+ 'available')
+ return backup
+
# NOTE(afazekas): these create_* and clean_* could be defined
# only in a single location in the source, and could be more general.
@@ -144,6 +171,18 @@
client.delete_volume(volume_id)
client.wait_for_resource_deletion(volume_id)
+ def attach_volume(self, server_id, volume_id):
+ """Attachs a volume to a server"""
+ self.servers_client.attach_volume(
+ server_id, volumeId=volume_id,
+ device='/dev/%s' % CONF.compute.volume_device_name)
+ waiters.wait_for_volume_status(self.volumes_client,
+ volume_id, 'in-use')
+ self.addCleanup(waiters.wait_for_volume_status, self.volumes_client,
+ volume_id, 'available')
+ self.addCleanup(self.servers_client.detach_volume, server_id,
+ self.volume_origin['id'])
+
@classmethod
def clear_volumes(cls):
for volume in cls.volumes:
@@ -173,7 +212,7 @@
pass
def create_server(self, **kwargs):
- name = kwargs.get(
+ name = kwargs.pop(
'name',
data_utils.rand_name(self.__class__.__name__ + '-instance'))
@@ -213,6 +252,7 @@
cls.admin_encryption_types_client = \
cls.os_adm.encryption_types_client
cls.admin_quotas_client = cls.os_adm.volume_quotas_client
+ cls.admin_volume_limits_client = cls.os_adm.volume_limits_client
elif cls._api_version == 2:
cls.admin_volume_qos_client = cls.os_adm.volume_qos_v2_client
cls.admin_volume_services_client = \
@@ -225,6 +265,7 @@
cls.admin_encryption_types_client = \
cls.os_adm.encryption_types_v2_client
cls.admin_quotas_client = cls.os_adm.volume_quotas_v2_client
+ cls.admin_volume_limits_client = cls.os_adm.volume_v2_limits_client
@classmethod
def resource_setup(cls):
diff --git a/tempest/api/volume/test_availability_zone.py b/tempest/api/volume/test_availability_zone.py
index fe51375..ae4b8f9 100644
--- a/tempest/api/volume/test_availability_zone.py
+++ b/tempest/api/volume/test_availability_zone.py
@@ -30,7 +30,7 @@
# List of availability zone
availability_zone = (self.client.list_availability_zones()
['availabilityZoneInfo'])
- self.assertTrue(len(availability_zone) > 0)
+ self.assertGreater(len(availability_zone), 0)
class AvailabilityZoneV1TestJSON(AvailabilityZoneV2TestJSON):
diff --git a/tempest/api/volume/test_snapshot_metadata.py b/tempest/api/volume/test_snapshot_metadata.py
index 688baf5..d0fa07e 100644
--- a/tempest/api/volume/test_snapshot_metadata.py
+++ b/tempest/api/volume/test_snapshot_metadata.py
@@ -30,70 +30,53 @@
raise cls.skipException("Cinder snapshot feature disabled")
@classmethod
- def setup_clients(cls):
- super(SnapshotV2MetadataTestJSON, cls).setup_clients()
- cls.client = cls.snapshots_client
-
- @classmethod
def resource_setup(cls):
super(SnapshotV2MetadataTestJSON, cls).resource_setup()
# Create a volume
cls.volume = cls.create_volume()
# Create a snapshot
cls.snapshot = cls.create_snapshot(volume_id=cls.volume['id'])
- cls.snapshot_id = cls.snapshot['id']
def tearDown(self):
# Update the metadata to {}
- self.client.update_snapshot_metadata(self.snapshot_id, metadata={})
+ self.snapshots_client.update_snapshot_metadata(
+ self.snapshot['id'], metadata={})
super(SnapshotV2MetadataTestJSON, self).tearDown()
@test.idempotent_id('a2f20f99-e363-4584-be97-bc33afb1a56c')
- def test_create_get_delete_snapshot_metadata(self):
+ def test_crud_snapshot_metadata(self):
# Create metadata for the snapshot
metadata = {"key1": "value1",
"key2": "value2",
"key3": "value3"}
- expected = {"key2": "value2",
- "key3": "value3"}
- body = self.client.create_snapshot_metadata(
- self.snapshot_id, metadata)['metadata']
- # Get the metadata of the snapshot
- body = self.client.show_snapshot_metadata(
- self.snapshot_id)['metadata']
- self.assertThat(body.items(), matchers.ContainsAll(metadata.items()))
-
- # Delete one item metadata of the snapshot
- self.client.delete_snapshot_metadata_item(
- self.snapshot_id, "key1")
- body = self.client.show_snapshot_metadata(
- self.snapshot_id)['metadata']
- self.assertThat(body.items(), matchers.ContainsAll(expected.items()))
- self.assertNotIn("key1", body)
-
- @test.idempotent_id('bd2363bc-de92-48a4-bc98-28943c6e4be1')
- def test_update_snapshot_metadata(self):
- # Update metadata for the snapshot
- metadata = {"key1": "value1",
- "key2": "value2",
- "key3": "value3"}
update = {"key3": "value3_update",
"key4": "value4"}
- # Create metadata for the snapshot
- body = self.client.create_snapshot_metadata(
- self.snapshot_id, metadata)['metadata']
- # Get the metadata of the snapshot
- body = self.client.show_snapshot_metadata(
- self.snapshot_id)['metadata']
- self.assertThat(body.items(), matchers.ContainsAll(metadata.items()))
+ expect = {"key4": "value4"}
+ # Create metadata
+ body = self.snapshots_client.create_snapshot_metadata(
+ self.snapshot['id'], metadata)['metadata']
- # Update metadata item
- body = self.client.update_snapshot_metadata(
- self.snapshot_id, metadata=update)['metadata']
# Get the metadata of the snapshot
- body = self.client.show_snapshot_metadata(
- self.snapshot_id)['metadata']
- self.assertEqual(update, body)
+ body = self.snapshots_client.show_snapshot_metadata(
+ self.snapshot['id'])['metadata']
+ self.assertThat(body.items(), matchers.ContainsAll(metadata.items()),
+ 'Create snapshot metadata failed')
+
+ # Update metadata
+ body = self.snapshots_client.update_snapshot_metadata(
+ self.snapshot['id'], metadata=update)['metadata']
+ body = self.snapshots_client.show_snapshot_metadata(
+ self.snapshot['id'])['metadata']
+ self.assertEqual(update, body, 'Update snapshot metadata failed')
+
+ # Delete one item metadata of the snapshot
+ self.snapshots_client.delete_snapshot_metadata_item(
+ self.snapshot['id'], "key3")
+ body = self.snapshots_client.show_snapshot_metadata(
+ self.snapshot['id'])['metadata']
+ self.assertThat(body.items(), matchers.ContainsAll(expect.items()),
+ 'Delete one item metadata of the snapshot failed')
+ self.assertNotIn("key3", body)
@test.idempotent_id('e8ff85c5-8f97-477f-806a-3ac364a949ed')
def test_update_snapshot_metadata_item(self):
@@ -106,18 +89,18 @@
"key2": "value2",
"key3": "value3_update"}
# Create metadata for the snapshot
- body = self.client.create_snapshot_metadata(
- self.snapshot_id, metadata)['metadata']
+ body = self.snapshots_client.create_snapshot_metadata(
+ self.snapshot['id'], metadata)['metadata']
# Get the metadata of the snapshot
- body = self.client.show_snapshot_metadata(
- self.snapshot_id)['metadata']
+ body = self.snapshots_client.show_snapshot_metadata(
+ self.snapshot['id'])['metadata']
self.assertThat(body.items(), matchers.ContainsAll(metadata.items()))
# Update metadata item
- body = self.client.update_snapshot_metadata_item(
- self.snapshot_id, "key3", meta=update_item)['meta']
+ body = self.snapshots_client.update_snapshot_metadata_item(
+ self.snapshot['id'], "key3", meta=update_item)['meta']
# Get the metadata of the snapshot
- body = self.client.show_snapshot_metadata(
- self.snapshot_id)['metadata']
+ body = self.snapshots_client.show_snapshot_metadata(
+ self.snapshot['id'])['metadata']
self.assertThat(body.items(), matchers.ContainsAll(expect.items()))
diff --git a/tempest/api/volume/test_volume_absolute_limits.py b/tempest/api/volume/test_volume_absolute_limits.py
new file mode 100644
index 0000000..bc7694a
--- /dev/null
+++ b/tempest/api/volume/test_volume_absolute_limits.py
@@ -0,0 +1,49 @@
+# Copyright 2016 OpenStack Foundation
+# 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.api.volume import base
+from tempest import config
+from tempest import test
+
+
+CONF = config.CONF
+
+
+class AbsoluteLimitsV2Tests(base.BaseVolumeTest):
+
+ @classmethod
+ def resource_setup(cls):
+ super(AbsoluteLimitsV2Tests, cls).resource_setup()
+ # Create a shared volume for tests
+ cls.volume = cls.create_volume()
+
+ @test.idempotent_id('8e943f53-e9d6-4272-b2e9-adcf2f7c29ad')
+ def test_get_volume_absolute_limits(self):
+ # get volume limit for a tenant
+ absolute_limits = \
+ self.volume_limits_client.show_limits(
+ )['limits']['absolute']
+
+ # verify volume limits and defaults per tenants
+ self.assertEqual(absolute_limits['totalGigabytesUsed'],
+ CONF.volume.volume_size)
+ self.assertEqual(absolute_limits['totalVolumesUsed'], 1)
+ self.assertEqual(absolute_limits['totalSnapshotsUsed'], 0)
+ self.assertEqual(absolute_limits['totalBackupsUsed'], 0)
+ self.assertEqual(absolute_limits['totalBackupGigabytesUsed'], 0)
+
+
+class AbsoluteLimitsV1Tests(AbsoluteLimitsV2Tests):
+ _api_version = 1
diff --git a/tempest/api/volume/test_volume_metadata.py b/tempest/api/volume/test_volume_metadata.py
index ee1744d..c125bb8 100644
--- a/tempest/api/volume/test_volume_metadata.py
+++ b/tempest/api/volume/test_volume_metadata.py
@@ -33,52 +33,39 @@
super(VolumesV2MetadataTest, self).tearDown()
@test.idempotent_id('6f5b125b-f664-44bf-910f-751591fe5769')
- def test_create_get_delete_volume_metadata(self):
+ def test_crud_volume_metadata(self):
# Create metadata for the volume
metadata = {"key1": "value1",
"key2": "value2",
"key3": "value3",
"key4": "<value&special_chars>"}
+ update = {"key4": "value4",
+ "key1": "value1_update"}
+ expected = {"key4": "value4"}
body = self.volumes_client.create_volume_metadata(self.volume['id'],
metadata)['metadata']
# Get the metadata of the volume
body = self.volumes_client.show_volume_metadata(
self.volume['id'])['metadata']
- self.assertThat(body.items(), matchers.ContainsAll(metadata.items()))
+ self.assertThat(body.items(), matchers.ContainsAll(metadata.items()),
+ 'Create metadata for the volume failed')
+
+ # Update metadata
+ body = self.volumes_client.update_volume_metadata(
+ self.volume['id'], update)['metadata']
+ body = self.volumes_client.show_volume_metadata(
+ self.volume['id'])['metadata']
+ self.assertEqual(update, body, 'Update metadata failed')
+
# Delete one item metadata of the volume
self.volumes_client.delete_volume_metadata_item(
self.volume['id'], "key1")
body = self.volumes_client.show_volume_metadata(
self.volume['id'])['metadata']
self.assertNotIn("key1", body)
- del metadata["key1"]
- self.assertThat(body.items(), matchers.ContainsAll(metadata.items()))
-
- @test.idempotent_id('774d2918-9beb-4f30-b3d1-2a4e8179ec0a')
- def test_update_volume_metadata(self):
- # Update metadata for the volume
- metadata = {"key1": "value1",
- "key2": "value2",
- "key3": "value3"}
-
- update = {"key4": "value4",
- "key1": "value1_update"}
-
- # Create metadata for the volume
- body = self.volumes_client.create_volume_metadata(
- self.volume['id'], metadata)['metadata']
- # Get the metadata of the volume
- body = self.volumes_client.show_volume_metadata(
- self.volume['id'])['metadata']
- self.assertThat(body.items(), matchers.ContainsAll(metadata.items()))
- # Update metadata
- body = self.volumes_client.update_volume_metadata(
- self.volume['id'], update)['metadata']
- # Get the metadata of the volume
- body = self.volumes_client.show_volume_metadata(
- self.volume['id'])['metadata']
- self.assertEqual(update, body)
+ self.assertThat(body.items(), matchers.ContainsAll(expected.items()),
+ 'Delete one item metadata of the volume failed')
@test.idempotent_id('862261c5-8df4-475a-8c21-946e50e36a20')
def test_update_volume_metadata_item(self):
@@ -93,7 +80,8 @@
# Create metadata for the volume
body = self.volumes_client.create_volume_metadata(
self.volume['id'], metadata)['metadata']
- self.assertThat(body.items(), matchers.ContainsAll(metadata.items()))
+ self.assertThat(body.items(),
+ matchers.ContainsAll(metadata.items()))
# Update metadata item
body = self.volumes_client.update_volume_metadata_item(
self.volume['id'], "key3", update_item)['meta']
diff --git a/tempest/api/volume/test_volumes_actions.py b/tempest/api/volume/test_volumes_actions.py
index 7fd8b60..d8d6b9a 100644
--- a/tempest/api/volume/test_volumes_actions.py
+++ b/tempest/api/volume/test_volumes_actions.py
@@ -12,7 +12,6 @@
# 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 testtools
from tempest.api.volume import base
from tempest.common.utils import data_utils
@@ -50,7 +49,6 @@
cls.volume = cls.create_volume()
@test.idempotent_id('fff42874-7db5-4487-a8e1-ddda5fb5288d')
- @test.stresstest(class_setup_per='process')
@test.attr(type='smoke')
@test.services('compute')
def test_attach_detach_volume_to_instance(self):
@@ -68,8 +66,6 @@
self.volume['id'], 'available')
@test.idempotent_id('63e21b4c-0a0c-41f6-bfc3-7c2816815599')
- @testtools.skipUnless(CONF.volume_feature_enabled.bootable,
- 'Update bootable status of a volume is not enabled.')
def test_volume_bootable(self):
# Verify that a volume bootable flag is retrieved
for bool_bootable in [True, False]:
@@ -78,11 +74,13 @@
fetched_volume = self.client.show_volume(
self.volume['id'])['volume']
# Get Volume information
- bool_flag = self._is_true(fetched_volume['bootable'])
- self.assertEqual(bool_bootable, bool_flag)
+ # NOTE(masayukig): 'bootable' is "true" or "false" in the current
+ # cinder implementation. So we need to cast boolean values to str
+ # and make it lower to compare here.
+ self.assertEqual(str(bool_bootable).lower(),
+ fetched_volume['bootable'])
@test.idempotent_id('9516a2c8-9135-488c-8dd6-5677a7e5f371')
- @test.stresstest(class_setup_per='process')
@test.services('compute')
def test_get_volume_attachment(self):
# Create a server
@@ -141,9 +139,6 @@
body = self.client.show_volume(self.volume['id'])['volume']
self.assertIn('available', body['status'])
- def _is_true(self, val):
- return val in ['true', 'True', True]
-
@test.idempotent_id('fff74e1e-5bd3-4b33-9ea9-24c103bc3f59')
def test_volume_readonly_update(self):
for readonly in [True, False]:
@@ -153,8 +148,11 @@
# Get Volume information
fetched_volume = self.client.show_volume(
self.volume['id'])['volume']
- bool_flag = self._is_true(fetched_volume['metadata']['readonly'])
- self.assertEqual(readonly, bool_flag)
+ # NOTE(masayukig): 'readonly' is "True" or "False" in the current
+ # cinder implementation. So we need to cast boolean values to str
+ # to compare here.
+ self.assertEqual(str(readonly),
+ fetched_volume['metadata']['readonly'])
class VolumesV1ActionsTest(VolumesV2ActionsTest):
diff --git a/tempest/api/volume/test_volumes_backup.py b/tempest/api/volume/test_volumes_backup.py
index 141336f..972dd58 100644
--- a/tempest/api/volume/test_volumes_backup.py
+++ b/tempest/api/volume/test_volumes_backup.py
@@ -38,16 +38,11 @@
volume['id'])
backup_name = data_utils.rand_name(
self.__class__.__name__ + '-Backup')
- create_backup = self.backups_client.create_backup
- backup = create_backup(volume_id=volume['id'],
- name=backup_name)['backup']
- self.addCleanup(self.backups_client.delete_backup,
- backup['id'])
+ backup = self.create_backup(volume_id=volume['id'],
+ name=backup_name)
self.assertEqual(backup_name, backup['name'])
waiters.wait_for_volume_status(self.volumes_client,
volume['id'], 'available')
- waiters.wait_for_backup_status(self.backups_client,
- backup['id'], 'available')
# Get a given backup
backup = self.backups_client.show_backup(backup['id'])['backup']
@@ -97,12 +92,8 @@
# Create backup using force flag
backup_name = data_utils.rand_name(
self.__class__.__name__ + '-Backup')
- backup = self.backups_client.create_backup(
- volume_id=volume['id'],
- name=backup_name, force=True)['backup']
- self.addCleanup(self.backups_client.delete_backup, backup['id'])
- waiters.wait_for_backup_status(self.backups_client,
- backup['id'], 'available')
+ backup = self.create_backup(volume_id=volume['id'],
+ name=backup_name, force=True)
self.assertEqual(backup_name, backup['name'])
diff --git a/tempest/api/volume/test_volumes_clone.py b/tempest/api/volume/test_volumes_clone.py
index 7529dc2..2cedb4e 100644
--- a/tempest/api/volume/test_volumes_clone.py
+++ b/tempest/api/volume/test_volumes_clone.py
@@ -21,11 +21,11 @@
CONF = config.CONF
-class VolumesCloneTest(base.BaseVolumeTest):
+class VolumesV2CloneTest(base.BaseVolumeTest):
@classmethod
def skip_checks(cls):
- super(VolumesCloneTest, cls).skip_checks()
+ super(VolumesV2CloneTest, cls).skip_checks()
if not CONF.volume_feature_enabled.clone:
raise cls.skipException("Cinder volume clones are disabled")
@@ -45,6 +45,22 @@
self.assertEqual(volume['source_volid'], src_vol['id'])
self.assertEqual(int(volume['size']), src_size + 1)
+ @test.idempotent_id('cbbcd7c6-5a6c-481a-97ac-ca55ab715d16')
+ def test_create_from_bootable_volume(self):
+ # Create volume from image
+ img_uuid = CONF.compute.image_ref
+ src_vol = self.create_volume(imageRef=img_uuid)
-class VolumesV1CloneTest(VolumesCloneTest):
+ # Create a volume from the bootable volume
+ cloned_vol = self.create_volume(source_volid=src_vol['id'])
+ cloned_vol_details = self.volumes_client.show_volume(
+ cloned_vol['id'])['volume']
+
+ # Verify cloned volume creation as expected
+ self.assertEqual('true', cloned_vol_details['bootable'])
+ self.assertEqual(src_vol['id'], cloned_vol_details['source_volid'])
+ self.assertEqual(src_vol['size'], cloned_vol_details['size'])
+
+
+class VolumesV1CloneTest(VolumesV2CloneTest):
_api_version = 1
diff --git a/tempest/api/volume/test_volumes_clone_negative.py b/tempest/api/volume/test_volumes_clone_negative.py
index d1bedb4..5c54e1e 100644
--- a/tempest/api/volume/test_volumes_clone_negative.py
+++ b/tempest/api/volume/test_volumes_clone_negative.py
@@ -22,11 +22,11 @@
CONF = config.CONF
-class VolumesCloneTest(base.BaseVolumeTest):
+class VolumesV2CloneNegativeTest(base.BaseVolumeTest):
@classmethod
def skip_checks(cls):
- super(VolumesCloneTest, cls).skip_checks()
+ super(VolumesV2CloneNegativeTest, cls).skip_checks()
if not CONF.volume_feature_enabled.clone:
raise cls.skipException("Cinder volume clones are disabled")
@@ -44,5 +44,5 @@
source_volid=src_vol['id'])
-class VolumesV1CloneTest(VolumesCloneTest):
+class VolumesV1CloneNegativeTest(VolumesV2CloneNegativeTest):
_api_version = 1
diff --git a/tempest/api/volume/test_volumes_extend.py b/tempest/api/volume/test_volumes_extend.py
index 7aea1c4..c3d6dbb 100644
--- a/tempest/api/volume/test_volumes_extend.py
+++ b/tempest/api/volume/test_volumes_extend.py
@@ -20,20 +20,16 @@
class VolumesV2ExtendTest(base.BaseVolumeTest):
- @classmethod
- def setup_clients(cls):
- super(VolumesV2ExtendTest, cls).setup_clients()
- cls.client = cls.volumes_client
-
@test.idempotent_id('9a36df71-a257-43a5-9555-dc7c88e66e0e')
def test_volume_extend(self):
# Extend Volume Test.
self.volume = self.create_volume()
extend_size = int(self.volume['size']) + 1
- self.client.extend_volume(self.volume['id'], new_size=extend_size)
- waiters.wait_for_volume_status(self.client,
+ self.volumes_client.extend_volume(self.volume['id'],
+ new_size=extend_size)
+ waiters.wait_for_volume_status(self.volumes_client,
self.volume['id'], 'available')
- volume = self.client.show_volume(self.volume['id'])['volume']
+ volume = self.volumes_client.show_volume(self.volume['id'])['volume']
self.assertEqual(int(volume['size']), extend_size)
diff --git a/tempest/api/volume/test_volumes_get.py b/tempest/api/volume/test_volumes_get.py
index 07f799b..65e461c 100644
--- a/tempest/api/volume/test_volumes_get.py
+++ b/tempest/api/volume/test_volumes_get.py
@@ -27,39 +27,31 @@
class VolumesV2GetTest(base.BaseVolumeTest):
- @classmethod
- def setup_clients(cls):
- super(VolumesV2GetTest, cls).setup_clients()
- cls.client = cls.volumes_client
-
- @classmethod
- def resource_setup(cls):
- super(VolumesV2GetTest, cls).resource_setup()
-
- cls.name_field = cls.special_fields['name_field']
- cls.descrip_field = cls.special_fields['descrip_field']
-
def _volume_create_get_update_delete(self, **kwargs):
+ name_field = self.special_fields['name_field']
+ descrip_field = self.special_fields['descrip_field']
+
# Create a volume, Get it's details and Delete the volume
v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
metadata = {'Type': 'Test'}
# Create a volume
- kwargs[self.name_field] = v_name
+ kwargs[name_field] = v_name
kwargs['metadata'] = metadata
- volume = self.client.create_volume(**kwargs)['volume']
+ volume = self.volumes_client.create_volume(**kwargs)['volume']
self.assertIn('id', volume)
- self.addCleanup(self.delete_volume, self.client, volume['id'])
- waiters.wait_for_volume_status(self.client, volume['id'], 'available')
- self.assertIn(self.name_field, volume)
- self.assertEqual(volume[self.name_field], v_name,
+ self.addCleanup(self.delete_volume, self.volumes_client, volume['id'])
+ waiters.wait_for_volume_status(self.volumes_client, volume['id'],
+ 'available')
+ self.assertIn(name_field, volume)
+ self.assertEqual(volume[name_field], v_name,
"The created volume name is not equal "
"to the requested name")
- self.assertTrue(volume['id'] is not None,
- "Field volume id is empty or not found.")
+
# Get Volume information
- fetched_volume = self.client.show_volume(volume['id'])['volume']
+ fetched_volume = self.volumes_client.show_volume(
+ volume['id'])['volume']
self.assertEqual(v_name,
- fetched_volume[self.name_field],
+ fetched_volume[name_field],
'The fetched Volume name is different '
'from the created Volume')
self.assertEqual(volume['id'],
@@ -73,53 +65,56 @@
if 'imageRef' in kwargs:
self.assertEqual('true', fetched_volume['bootable'])
- if 'imageRef' not in kwargs:
+ else:
self.assertEqual('false', fetched_volume['bootable'])
# Update Volume
# Test volume update when display_name is same with original value
- params = {self.name_field: v_name}
- self.client.update_volume(volume['id'], **params)
+ params = {name_field: v_name}
+ self.volumes_client.update_volume(volume['id'], **params)
# Test volume update when display_name is new
new_v_name = data_utils.rand_name(
self.__class__.__name__ + '-new-Volume')
new_desc = 'This is the new description of volume'
- params = {self.name_field: new_v_name,
- self.descrip_field: new_desc}
- update_volume = self.client.update_volume(
+ params = {name_field: new_v_name,
+ descrip_field: new_desc}
+ update_volume = self.volumes_client.update_volume(
volume['id'], **params)['volume']
# Assert response body for update_volume method
- self.assertEqual(new_v_name, update_volume[self.name_field])
- self.assertEqual(new_desc, update_volume[self.descrip_field])
+ self.assertEqual(new_v_name, update_volume[name_field])
+ self.assertEqual(new_desc, update_volume[descrip_field])
# Assert response body for show_volume method
- updated_volume = self.client.show_volume(volume['id'])['volume']
+ updated_volume = self.volumes_client.show_volume(
+ volume['id'])['volume']
self.assertEqual(volume['id'], updated_volume['id'])
- self.assertEqual(new_v_name, updated_volume[self.name_field])
- self.assertEqual(new_desc, updated_volume[self.descrip_field])
+ self.assertEqual(new_v_name, updated_volume[name_field])
+ self.assertEqual(new_desc, updated_volume[descrip_field])
self.assertThat(updated_volume['metadata'].items(),
matchers.ContainsAll(metadata.items()),
'The fetched Volume metadata misses data '
'from the created Volume')
+
# Test volume create when display_name is none and display_description
# contains specific characters,
# then test volume update if display_name is duplicated
new_v_desc = data_utils.rand_name('@#$%^* description')
- params = {self.descrip_field: new_v_desc,
+ params = {descrip_field: new_v_desc,
'availability_zone': volume['availability_zone'],
'size': CONF.volume.volume_size}
- new_volume = self.client.create_volume(**params)['volume']
+ new_volume = self.volumes_client.create_volume(**params)['volume']
self.assertIn('id', new_volume)
- self.addCleanup(self.delete_volume, self.client, new_volume['id'])
- waiters.wait_for_volume_status(self.client,
+ self.addCleanup(self.delete_volume, self.volumes_client,
+ new_volume['id'])
+ waiters.wait_for_volume_status(self.volumes_client,
new_volume['id'], 'available')
- params = {self.name_field: volume[self.name_field],
- self.descrip_field: volume[self.descrip_field]}
- self.client.update_volume(new_volume['id'], **params)
+ params = {name_field: volume[name_field],
+ descrip_field: volume[descrip_field]}
+ self.volumes_client.update_volume(new_volume['id'], **params)
if 'imageRef' in kwargs:
self.assertEqual('true', updated_volume['bootable'])
- if 'imageRef' not in kwargs:
+ else:
self.assertEqual('false', updated_volume['bootable'])
@test.attr(type='smoke')
diff --git a/tempest/api/volume/test_volumes_list.py b/tempest/api/volume/test_volumes_list.py
index 40793ec..030ea6c 100644
--- a/tempest/api/volume/test_volumes_list.py
+++ b/tempest/api/volume/test_volumes_list.py
@@ -55,11 +55,6 @@
[str_vol(v) for v in fetched_list]))
@classmethod
- def setup_clients(cls):
- super(VolumesV2ListTestJSON, cls).setup_clients()
- cls.client = cls.volumes_client
-
- @classmethod
def resource_setup(cls):
super(VolumesV2ListTestJSON, cls).resource_setup()
cls.name = cls.VOLUME_FIELDS[1]
@@ -68,16 +63,17 @@
cls.metadata = {'Type': 'work'}
for i in range(3):
volume = cls.create_volume(metadata=cls.metadata)
- volume = cls.client.show_volume(volume['id'])['volume']
+ volume = cls.volumes_client.show_volume(volume['id'])['volume']
cls.volume_list.append(volume)
def _list_by_param_value_and_assert(self, params, with_detail=False):
"""list or list_details with given params and validates result"""
if with_detail:
fetched_vol_list = \
- self.client.list_volumes(detail=True, params=params)['volumes']
+ self.volumes_client.list_volumes(detail=True,
+ params=params)['volumes']
else:
- fetched_vol_list = self.client.list_volumes(
+ fetched_vol_list = self.volumes_client.list_volumes(
params=params)['volumes']
# Validating params of fetched volumes
@@ -103,7 +99,7 @@
def test_volume_list(self):
# Get a list of Volumes
# Fetch all volumes
- fetched_list = self.client.list_volumes()['volumes']
+ fetched_list = self.volumes_client.list_volumes()['volumes']
self.assertVolumesIn(fetched_list, self.volume_list,
fields=self.VOLUME_FIELDS)
@@ -111,14 +107,15 @@
def test_volume_list_with_details(self):
# Get a list of Volumes with details
# Fetch all Volumes
- fetched_list = self.client.list_volumes(detail=True)['volumes']
+ fetched_list = self.volumes_client.list_volumes(detail=True)['volumes']
self.assertVolumesIn(fetched_list, self.volume_list)
@test.idempotent_id('a28e8da4-0b56-472f-87a8-0f4d3f819c02')
def test_volume_list_by_name(self):
volume = self.volume_list[data_utils.rand_int_id(0, 2)]
params = {self.name: volume[self.name]}
- fetched_vol = self.client.list_volumes(params=params)['volumes']
+ fetched_vol = self.volumes_client.list_volumes(
+ params=params)['volumes']
self.assertEqual(1, len(fetched_vol), str(fetched_vol))
self.assertEqual(fetched_vol[0][self.name],
volume[self.name])
@@ -127,7 +124,7 @@
def test_volume_list_details_by_name(self):
volume = self.volume_list[data_utils.rand_int_id(0, 2)]
params = {self.name: volume[self.name]}
- fetched_vol = self.client.list_volumes(
+ fetched_vol = self.volumes_client.list_volumes(
detail=True, params=params)['volumes']
self.assertEqual(1, len(fetched_vol), str(fetched_vol))
self.assertEqual(fetched_vol[0][self.name],
@@ -136,7 +133,8 @@
@test.idempotent_id('39654e13-734c-4dab-95ce-7613bf8407ce')
def test_volumes_list_by_status(self):
params = {'status': 'available'}
- fetched_list = self.client.list_volumes(params=params)['volumes']
+ fetched_list = self.volumes_client.list_volumes(
+ params=params)['volumes']
self._list_by_param_value_and_assert(params)
self.assertVolumesIn(fetched_list, self.volume_list,
fields=self.VOLUME_FIELDS)
@@ -144,7 +142,7 @@
@test.idempotent_id('2943f712-71ec-482a-bf49-d5ca06216b9f')
def test_volumes_list_details_by_status(self):
params = {'status': 'available'}
- fetched_list = self.client.list_volumes(
+ fetched_list = self.volumes_client.list_volumes(
detail=True, params=params)['volumes']
for volume in fetched_list:
self.assertEqual('available', volume['status'])
@@ -158,7 +156,8 @@
in volume_list are not a bootable volume.
"""
params = {'bootable': 'false'}
- fetched_list = self.client.list_volumes(params=params)['volumes']
+ fetched_list = self.volumes_client.list_volumes(
+ params=params)['volumes']
self._list_by_param_value_and_assert(params)
self.assertVolumesIn(fetched_list, self.volume_list,
fields=self.VOLUME_FIELDS)
@@ -166,7 +165,7 @@
@test.idempotent_id('2016a939-72ec-482a-bf49-d5ca06216b9f')
def test_volumes_list_details_by_bootable(self):
params = {'bootable': 'false'}
- fetched_list = self.client.list_volumes(
+ fetched_list = self.volumes_client.list_volumes(
detail=True, params=params)['volumes']
for volume in fetched_list:
self.assertEqual('false', volume['bootable'])
@@ -177,7 +176,8 @@
volume = self.volume_list[data_utils.rand_int_id(0, 2)]
zone = volume['availability_zone']
params = {'availability_zone': zone}
- fetched_list = self.client.list_volumes(params=params)['volumes']
+ fetched_list = self.volumes_client.list_volumes(
+ params=params)['volumes']
self._list_by_param_value_and_assert(params)
self.assertVolumesIn(fetched_list, self.volume_list,
fields=self.VOLUME_FIELDS)
@@ -187,7 +187,7 @@
volume = self.volume_list[data_utils.rand_int_id(0, 2)]
zone = volume['availability_zone']
params = {'availability_zone': zone}
- fetched_list = self.client.list_volumes(
+ fetched_list = self.volumes_client.list_volumes(
detail=True, params=params)['volumes']
for volume in fetched_list:
self.assertEqual(zone, volume['availability_zone'])
diff --git a/tempest/api/volume/test_volumes_negative.py b/tempest/api/volume/test_volumes_negative.py
index fda0dda..c45ace6 100644
--- a/tempest/api/volume/test_volumes_negative.py
+++ b/tempest/api/volume/test_volumes_negative.py
@@ -22,11 +22,6 @@
class VolumesV2NegativeTest(base.BaseVolumeTest):
@classmethod
- def setup_clients(cls):
- super(VolumesV2NegativeTest, cls).setup_clients()
- cls.client = cls.volumes_client
-
- @classmethod
def resource_setup(cls):
super(VolumesV2NegativeTest, cls).resource_setup()
@@ -40,14 +35,14 @@
@test.idempotent_id('f131c586-9448-44a4-a8b0-54ca838aa43e')
def test_volume_get_nonexistent_volume_id(self):
# Should not be able to get a non-existent volume
- self.assertRaises(lib_exc.NotFound, self.client.show_volume,
+ self.assertRaises(lib_exc.NotFound, self.volumes_client.show_volume,
data_utils.rand_uuid())
@test.attr(type=['negative'])
@test.idempotent_id('555efa6e-efcd-44ef-8a3b-4a7ca4837a29')
def test_volume_delete_nonexistent_volume_id(self):
# Should not be able to delete a non-existent Volume
- self.assertRaises(lib_exc.NotFound, self.client.delete_volume,
+ self.assertRaises(lib_exc.NotFound, self.volumes_client.delete_volume,
data_utils.rand_uuid())
@test.attr(type=['negative'])
@@ -57,17 +52,19 @@
# in request
v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
metadata = {'Type': 'work'}
- self.assertRaises(lib_exc.BadRequest, self.client.create_volume,
+ self.assertRaises(lib_exc.BadRequest,
+ self.volumes_client.create_volume,
size='#$%', display_name=v_name, metadata=metadata)
@test.attr(type=['negative'])
@test.idempotent_id('9387686f-334f-4d31-a439-33494b9e2683')
- def test_create_volume_with_out_passing_size(self):
+ def test_create_volume_without_passing_size(self):
# Should not be able to create volume without passing size
# in request
v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
metadata = {'Type': 'work'}
- self.assertRaises(lib_exc.BadRequest, self.client.create_volume,
+ self.assertRaises(lib_exc.BadRequest,
+ self.volumes_client.create_volume,
size='', display_name=v_name, metadata=metadata)
@test.attr(type=['negative'])
@@ -76,7 +73,8 @@
# Should not be able to create volume with size zero
v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
metadata = {'Type': 'work'}
- self.assertRaises(lib_exc.BadRequest, self.client.create_volume,
+ self.assertRaises(lib_exc.BadRequest,
+ self.volumes_client.create_volume,
size='0', display_name=v_name, metadata=metadata)
@test.attr(type=['negative'])
@@ -85,7 +83,8 @@
# Should not be able to create volume with size negative
v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
metadata = {'Type': 'work'}
- self.assertRaises(lib_exc.BadRequest, self.client.create_volume,
+ self.assertRaises(lib_exc.BadRequest,
+ self.volumes_client.create_volume,
size='-1', display_name=v_name, metadata=metadata)
@test.attr(type=['negative'])
@@ -94,7 +93,7 @@
# Should not be able to create volume with non-existent volume type
v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
metadata = {'Type': 'work'}
- self.assertRaises(lib_exc.NotFound, self.client.create_volume,
+ self.assertRaises(lib_exc.NotFound, self.volumes_client.create_volume,
size='1', volume_type=data_utils.rand_uuid(),
display_name=v_name, metadata=metadata)
@@ -104,7 +103,7 @@
# Should not be able to create volume with non-existent snapshot
v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
metadata = {'Type': 'work'}
- self.assertRaises(lib_exc.NotFound, self.client.create_volume,
+ self.assertRaises(lib_exc.NotFound, self.volumes_client.create_volume,
size='1', snapshot_id=data_utils.rand_uuid(),
display_name=v_name, metadata=metadata)
@@ -114,7 +113,7 @@
# Should not be able to create volume with non-existent source volume
v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
metadata = {'Type': 'work'}
- self.assertRaises(lib_exc.NotFound, self.client.create_volume,
+ self.assertRaises(lib_exc.NotFound, self.volumes_client.create_volume,
size='1', source_volid=data_utils.rand_uuid(),
display_name=v_name, metadata=metadata)
@@ -123,7 +122,7 @@
def test_update_volume_with_nonexistent_volume_id(self):
v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
metadata = {'Type': 'work'}
- self.assertRaises(lib_exc.NotFound, self.client.update_volume,
+ self.assertRaises(lib_exc.NotFound, self.volumes_client.update_volume,
volume_id=data_utils.rand_uuid(),
display_name=v_name,
metadata=metadata)
@@ -133,7 +132,7 @@
def test_update_volume_with_invalid_volume_id(self):
v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
metadata = {'Type': 'work'}
- self.assertRaises(lib_exc.NotFound, self.client.update_volume,
+ self.assertRaises(lib_exc.NotFound, self.volumes_client.update_volume,
volume_id='#$%%&^&^', display_name=v_name,
metadata=metadata)
@@ -142,7 +141,7 @@
def test_update_volume_with_empty_volume_id(self):
v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
metadata = {'Type': 'work'}
- self.assertRaises(lib_exc.NotFound, self.client.update_volume,
+ self.assertRaises(lib_exc.NotFound, self.volumes_client.update_volume,
volume_id='', display_name=v_name,
metadata=metadata)
@@ -150,27 +149,29 @@
@test.idempotent_id('30799cfd-7ee4-446c-b66c-45b383ed211b')
def test_get_invalid_volume_id(self):
# Should not be able to get volume with invalid id
- self.assertRaises(lib_exc.NotFound, self.client.show_volume,
+ self.assertRaises(lib_exc.NotFound, self.volumes_client.show_volume,
'#$%%&^&^')
@test.attr(type=['negative'])
@test.idempotent_id('c6c3db06-29ad-4e91-beb0-2ab195fe49e3')
def test_get_volume_without_passing_volume_id(self):
# Should not be able to get volume when empty ID is passed
- self.assertRaises(lib_exc.NotFound, self.client.show_volume, '')
+ self.assertRaises(lib_exc.NotFound,
+ self.volumes_client.show_volume, '')
@test.attr(type=['negative'])
@test.idempotent_id('1f035827-7c32-4019-9240-b4ec2dbd9dfd')
def test_delete_invalid_volume_id(self):
# Should not be able to delete volume when invalid ID is passed
- self.assertRaises(lib_exc.NotFound, self.client.delete_volume,
+ self.assertRaises(lib_exc.NotFound, self.volumes_client.delete_volume,
'!@#$%^&*()')
@test.attr(type=['negative'])
@test.idempotent_id('441a1550-5d44-4b30-af0f-a6d402f52026')
def test_delete_volume_without_passing_volume_id(self):
# Should not be able to delete volume when empty ID is passed
- self.assertRaises(lib_exc.NotFound, self.client.delete_volume, '')
+ self.assertRaises(lib_exc.NotFound,
+ self.volumes_client.delete_volume, '')
@test.attr(type=['negative'])
@test.idempotent_id('f5e56b0a-5d02-43c1-a2a7-c9b792c2e3f6')
@@ -179,7 +180,7 @@
server = self.create_server(wait_until='ACTIVE')
self.assertRaises(lib_exc.NotFound,
- self.client.attach_volume,
+ self.volumes_client.attach_volume,
data_utils.rand_uuid(),
instance_uuid=server['id'],
mountpoint=self.mountpoint)
@@ -188,7 +189,7 @@
@test.idempotent_id('9f9c24e4-011d-46b5-b992-952140ce237a')
def test_detach_volumes_with_invalid_volume_id(self):
self.assertRaises(lib_exc.NotFound,
- self.client.detach_volume,
+ self.volumes_client.detach_volume,
'xxx')
@test.attr(type=['negative'])
@@ -196,7 +197,8 @@
def test_volume_extend_with_size_smaller_than_original_size(self):
# Extend volume with smaller size than original size.
extend_size = 0
- self.assertRaises(lib_exc.BadRequest, self.client.extend_volume,
+ self.assertRaises(lib_exc.BadRequest,
+ self.volumes_client.extend_volume,
self.volume['id'], new_size=extend_size)
@test.attr(type=['negative'])
@@ -204,7 +206,8 @@
def test_volume_extend_with_non_number_size(self):
# Extend volume when size is non number.
extend_size = 'abc'
- self.assertRaises(lib_exc.BadRequest, self.client.extend_volume,
+ self.assertRaises(lib_exc.BadRequest,
+ self.volumes_client.extend_volume,
self.volume['id'], new_size=extend_size)
@test.attr(type=['negative'])
@@ -212,7 +215,8 @@
def test_volume_extend_with_None_size(self):
# Extend volume with None size.
extend_size = None
- self.assertRaises(lib_exc.BadRequest, self.client.extend_volume,
+ self.assertRaises(lib_exc.BadRequest,
+ self.volumes_client.extend_volume,
self.volume['id'], new_size=extend_size)
@test.attr(type=['negative'])
@@ -220,7 +224,7 @@
def test_volume_extend_with_nonexistent_volume_id(self):
# Extend volume size when volume is nonexistent.
extend_size = int(self.volume['size']) + 1
- self.assertRaises(lib_exc.NotFound, self.client.extend_volume,
+ self.assertRaises(lib_exc.NotFound, self.volumes_client.extend_volume,
data_utils.rand_uuid(), new_size=extend_size)
@test.attr(type=['negative'])
@@ -228,41 +232,42 @@
def test_volume_extend_without_passing_volume_id(self):
# Extend volume size when passing volume id is None.
extend_size = int(self.volume['size']) + 1
- self.assertRaises(lib_exc.NotFound, self.client.extend_volume,
+ self.assertRaises(lib_exc.NotFound, self.volumes_client.extend_volume,
None, new_size=extend_size)
@test.attr(type=['negative'])
@test.idempotent_id('ac6084c0-0546-45f9-b284-38a367e0e0e2')
def test_reserve_volume_with_nonexistent_volume_id(self):
self.assertRaises(lib_exc.NotFound,
- self.client.reserve_volume,
+ self.volumes_client.reserve_volume,
data_utils.rand_uuid())
@test.attr(type=['negative'])
@test.idempotent_id('eb467654-3dc1-4a72-9b46-47c29d22654c')
def test_unreserve_volume_with_nonexistent_volume_id(self):
self.assertRaises(lib_exc.NotFound,
- self.client.unreserve_volume,
+ self.volumes_client.unreserve_volume,
data_utils.rand_uuid())
@test.attr(type=['negative'])
@test.idempotent_id('449c4ed2-ecdd-47bb-98dc-072aeccf158c')
def test_reserve_volume_with_negative_volume_status(self):
# Mark volume as reserved.
- self.client.reserve_volume(self.volume['id'])
+ self.volumes_client.reserve_volume(self.volume['id'])
# Mark volume which is marked as reserved before
self.assertRaises(lib_exc.BadRequest,
- self.client.reserve_volume,
+ self.volumes_client.reserve_volume,
self.volume['id'])
# Unmark volume as reserved.
- self.client.unreserve_volume(self.volume['id'])
+ self.volumes_client.unreserve_volume(self.volume['id'])
@test.attr(type=['negative'])
@test.idempotent_id('0f4aa809-8c7b-418f-8fb3-84c7a5dfc52f')
def test_list_volumes_with_nonexistent_name(self):
v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
params = {self.name_field: v_name}
- fetched_volume = self.client.list_volumes(params=params)['volumes']
+ fetched_volume = self.volumes_client.list_volumes(
+ params=params)['volumes']
self.assertEqual(0, len(fetched_volume))
@test.attr(type=['negative'])
@@ -271,14 +276,16 @@
v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
params = {self.name_field: v_name}
fetched_volume = \
- self.client.list_volumes(detail=True, params=params)['volumes']
+ self.volumes_client.list_volumes(
+ detail=True, params=params)['volumes']
self.assertEqual(0, len(fetched_volume))
@test.attr(type=['negative'])
@test.idempotent_id('143b279b-7522-466b-81be-34a87d564a7c')
def test_list_volumes_with_invalid_status(self):
params = {'status': 'null'}
- fetched_volume = self.client.list_volumes(params=params)['volumes']
+ fetched_volume = self.volumes_client.list_volumes(
+ params=params)['volumes']
self.assertEqual(0, len(fetched_volume))
@test.attr(type=['negative'])
@@ -286,7 +293,8 @@
def test_list_volumes_detail_with_invalid_status(self):
params = {'status': 'null'}
fetched_volume = \
- self.client.list_volumes(detail=True, params=params)['volumes']
+ self.volumes_client.list_volumes(detail=True,
+ params=params)['volumes']
self.assertEqual(0, len(fetched_volume))
diff --git a/tempest/api/volume/test_volumes_snapshots.py b/tempest/api/volume/test_volumes_snapshots.py
index 7be31d1..3c7a2c8 100644
--- a/tempest/api/volume/test_volumes_snapshots.py
+++ b/tempest/api/volume/test_volumes_snapshots.py
@@ -12,7 +12,6 @@
from tempest.api.volume import base
from tempest.common.utils import data_utils
-from tempest.common import waiters
from tempest import config
from tempest import test
@@ -46,27 +45,57 @@
# Create a snapshot when volume status is in-use
# Create a test instance
server = self.create_server(wait_until='ACTIVE')
- self.servers_client.attach_volume(
- server['id'], volumeId=self.volume_origin['id'],
- device='/dev/%s' % CONF.compute.volume_device_name)
- waiters.wait_for_volume_status(self.volumes_client,
- self.volume_origin['id'], 'in-use')
- self.addCleanup(waiters.wait_for_volume_status, self.volumes_client,
- self.volume_origin['id'], 'available')
- self.addCleanup(self.servers_client.detach_volume, server['id'],
- self.volume_origin['id'])
+ self.attach_volume(server['id'], self.volume_origin['id'])
+
# Snapshot a volume even if it's attached to an instance
snapshot = self.create_snapshot(self.volume_origin['id'],
force=True)
# Delete the snapshot
self.cleanup_snapshot(snapshot)
+ @test.idempotent_id('8567b54c-4455-446d-a1cf-651ddeaa3ff2')
+ @test.services('compute')
+ def test_snapshot_delete_with_volume_in_use(self):
+ # Create a test instance
+ server = self.create_server(wait_until='ACTIVE')
+ self.attach_volume(server['id'], self.volume_origin['id'])
+
+ # Snapshot a volume attached to an instance
+ snapshot1 = self.create_snapshot(self.volume_origin['id'], force=True)
+ snapshot2 = self.create_snapshot(self.volume_origin['id'], force=True)
+ snapshot3 = self.create_snapshot(self.volume_origin['id'], force=True)
+
+ # Delete the snapshots. Some snapshot implementations can take
+ # different paths according to order they are deleted.
+ self.cleanup_snapshot(snapshot1)
+ self.cleanup_snapshot(snapshot3)
+ self.cleanup_snapshot(snapshot2)
+
+ @test.idempotent_id('5210a1de-85a0-11e6-bb21-641c676a5d61')
+ @test.services('compute')
+ def test_snapshot_create_offline_delete_online(self):
+
+ # Create a snapshot while it is not attached
+ snapshot1 = self.create_snapshot(self.volume_origin['id'])
+
+ # Create a server and attach it
+ server = self.create_server(wait_until='ACTIVE')
+ self.attach_volume(server['id'], self.volume_origin['id'])
+
+ # Now that the volume is attached, create another snapshots
+ snapshot2 = self.create_snapshot(self.volume_origin['id'], force=True)
+ snapshot3 = self.create_snapshot(self.volume_origin['id'], force=True)
+
+ # Delete the snapshots. Some snapshot implementations can take
+ # different paths according to order they are deleted.
+ self.cleanup_snapshot(snapshot3)
+ self.cleanup_snapshot(snapshot1)
+ self.cleanup_snapshot(snapshot2)
+
@test.idempotent_id('2a8abbe4-d871-46db-b049-c41f5af8216e')
def test_snapshot_create_get_list_update_delete(self):
# Create a snapshot
- s_name = data_utils.rand_name(self.__class__.__name__ + '-snap')
- params = {self.name_field: s_name}
- snapshot = self.create_snapshot(self.volume_origin['id'], **params)
+ snapshot = self.create_snapshot(self.volume_origin['id'])
# Get the snap and check for some of its details
snap_get = self.snapshots_client.show_snapshot(
diff --git a/tempest/api/volume/v2/test_volumes_list.py b/tempest/api/volume/v2/test_volumes_list.py
index 60a35b0..fb8c65d 100644
--- a/tempest/api/volume/v2/test_volumes_list.py
+++ b/tempest/api/volume/v2/test_volumes_list.py
@@ -33,11 +33,6 @@
"""
@classmethod
- def setup_clients(cls):
- super(VolumesV2ListTestJSON, cls).setup_clients()
- cls.client = cls.volumes_client
-
- @classmethod
def resource_setup(cls):
super(VolumesV2ListTestJSON, cls).resource_setup()
@@ -45,7 +40,7 @@
cls.metadata = {'Type': 'work'}
# NOTE(zhufl): When using pre-provisioned credentials, the project
# may have volumes other than those created below.
- existing_volumes = cls.client.list_volumes()['volumes']
+ existing_volumes = cls.volumes_client.list_volumes()['volumes']
cls.volume_id_list = [vol['id'] for vol in existing_volumes]
for i in range(3):
volume = cls.create_volume(metadata=cls.metadata)
@@ -63,7 +58,7 @@
'sort_dir': sort_dir,
'sort_key': sort_key
}
- fetched_volume = self.client.list_volumes(
+ fetched_volume = self.volumes_client.list_volumes(
detail=True, params=params)['volumes']
self.assertEqual(limit, len(fetched_volume),
"The count of volumes is %s, expected:%s " %
@@ -73,11 +68,13 @@
val0 = fetched_volume[0][sort_key]
val1 = fetched_volume[1][sort_key]
if sort_dir == 'asc':
- self.assertTrue(val0 < val1,
- "%s < %s" % (val0, val1))
+ self.assertLess(val0, val1,
+ "list is not in asc order with sort_key: %s."
+ " %s" % (sort_key, fetched_volume))
elif sort_dir == 'desc':
- self.assertTrue(val0 > val1,
- "%s > %s" % (val0, val1))
+ self.assertGreater(val0, val1,
+ "list is not in desc order with sort_key: "
+ "%s. %s" % (sort_key, fetched_volume))
_list_details_with_multiple_params()
_list_details_with_multiple_params(sort_dir='desc')
@@ -190,8 +187,8 @@
params = {'marker': random_volume}
# Running volume list using marker parameter
- vol_with_marker = self.client.list_volumes(detail=True,
- params=params)['volumes']
+ vol_with_marker = self.volumes_client.list_volumes(
+ detail=True, params=params)['volumes']
# Fetching the index of the random volume from volume_id_list
index_marker = self.volume_id_list.index(random_volume)
diff --git a/tempest/api/volume/v3/base.py b/tempest/api/volume/v3/base.py
index c31c83c..31fc1eb 100644
--- a/tempest/api/volume/v3/base.py
+++ b/tempest/api/volume/v3/base.py
@@ -44,7 +44,7 @@
@classmethod
def setup_clients(cls):
super(VolumesV3Test, cls).setup_clients()
- cls.messages_client = cls.os.volume_messages_client
+ cls.messages_client = cls.os.volume_v3_messages_client
def setUp(self):
super(VolumesV3Test, self).setUp()
@@ -52,7 +52,8 @@
self.request_microversion))
-class VolumesV3AdminTest(VolumesV3Test):
+class VolumesV3AdminTest(VolumesV3Test,
+ base.BaseVolumeAdminTest):
"""Base test case class for all v3 Volume Admin API tests."""
credentials = ['primary', 'admin']
@@ -60,5 +61,5 @@
@classmethod
def setup_clients(cls):
super(VolumesV3AdminTest, cls).setup_clients()
- cls.admin_messages_client = cls.os_adm.volume_messages_client
+ cls.admin_messages_client = cls.os_adm.volume_v3_messages_client
cls.admin_volume_types_client = cls.os_adm.volume_types_v2_client
diff --git a/tempest/clients.py b/tempest/clients.py
index be6bc02..b3290ac 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -17,9 +17,7 @@
from oslo_log import log as logging
-from tempest.common import negative_rest_client
from tempest import config
-from tempest import exceptions
from tempest.lib import auth
from tempest.lib import exceptions as lib_exc
from tempest.lib.services import clients
@@ -27,7 +25,6 @@
from tempest.services import identity
from tempest.services import object_storage
from tempest.services import orchestration
-from tempest.services import volume
CONF = config.CONF
LOG = logging.getLogger(__name__)
@@ -83,8 +80,6 @@
build_interval=CONF.orchestration.build_interval,
build_timeout=CONF.orchestration.build_timeout,
**self.default_params)
- self.negative_client = negative_rest_client.NegativeRestClient(
- self.auth_provider, service, **self.default_params)
def _prepare_configuration(self):
"""Map values from CONF into Manager parameters
@@ -142,6 +137,8 @@
self.namespaces_client = self.image_v2.NamespacesClient()
self.resource_types_client = self.image_v2.ResourceTypesClient()
self.schemas_client = self.image_v2.SchemasClient()
+ self.namespace_properties_client = \
+ self.image_v2.NamespacePropertiesClient()
def _set_compute_clients(self):
self.agents_client = self.compute.AgentsClient()
@@ -277,55 +274,35 @@
raise lib_exc.InvalidConfiguration(msg)
def _set_volume_clients(self):
- # Mandatory parameters (always defined)
- params = self.parameters['volume']
- self.volume_qos_client = volume.v1.QosSpecsClient(self.auth_provider,
- **params)
- self.volume_qos_v2_client = volume.v2.QosSpecsClient(
- self.auth_provider, **params)
- self.volume_services_client = volume.v1.ServicesClient(
- self.auth_provider, **params)
- self.volume_services_v2_client = volume.v2.ServicesClient(
- self.auth_provider, **params)
- self.backups_client = volume.v1.BackupsClient(self.auth_provider,
- **params)
- self.backups_v2_client = volume.v2.BackupsClient(self.auth_provider,
- **params)
- self.encryption_types_client = volume.v1.EncryptionTypesClient(
- self.auth_provider, **params)
- self.encryption_types_v2_client = volume.v2.EncryptionTypesClient(
- self.auth_provider, **params)
- self.snapshots_client = volume.v1.SnapshotsClient(self.auth_provider,
- **params)
- self.snapshots_v2_client = volume.v2.SnapshotsClient(
- self.auth_provider, **params)
- self.volumes_client = volume.v1.VolumesClient(self.auth_provider,
- **params)
- self.volumes_v2_client = volume.v2.VolumesClient(self.auth_provider,
- **params)
- self.volume_messages_client = volume.v3.MessagesClient(
- self.auth_provider, **params)
- self.volume_types_client = volume.v1.TypesClient(self.auth_provider,
- **params)
- self.volume_types_v2_client = volume.v2.TypesClient(self.auth_provider,
- **params)
- self.volume_hosts_client = volume.v1.HostsClient(self.auth_provider,
- **params)
- self.volume_hosts_v2_client = volume.v2.HostsClient(self.auth_provider,
- **params)
- self.volume_quotas_client = volume.v1.QuotasClient(self.auth_provider,
- **params)
- self.volume_quotas_v2_client = volume.v2.QuotasClient(
- self.auth_provider, **params)
- self.volumes_extension_client = volume.v1.ExtensionsClient(
- self.auth_provider, **params)
- self.volumes_v2_extension_client = volume.v2.ExtensionsClient(
- self.auth_provider, **params)
+ self.volume_qos_client = self.volume_v1.QosSpecsClient()
+ self.volume_qos_v2_client = self.volume_v2.QosSpecsClient()
+ self.volume_services_client = self.volume_v1.ServicesClient()
+ self.volume_services_v2_client = self.volume_v2.ServicesClient()
+ self.backups_client = self.volume_v1.BackupsClient()
+ self.backups_v2_client = self.volume_v2.BackupsClient()
+ self.encryption_types_client = self.volume_v1.EncryptionTypesClient()
+ self.encryption_types_v2_client = \
+ self.volume_v2.EncryptionTypesClient()
+ self.snapshots_client = self.volume_v1.SnapshotsClient()
+ self.snapshots_v2_client = self.volume_v2.SnapshotsClient()
+ self.volumes_client = self.volume_v1.VolumesClient()
+ self.volumes_v2_client = self.volume_v2.VolumesClient()
+ self.volume_v3_messages_client = self.volume_v3.MessagesClient()
+ self.volume_types_client = self.volume_v1.TypesClient()
+ self.volume_types_v2_client = self.volume_v2.TypesClient()
+ self.volume_hosts_client = self.volume_v1.HostsClient()
+ self.volume_hosts_v2_client = self.volume_v2.HostsClient()
+ self.volume_quotas_client = self.volume_v1.QuotasClient()
+ self.volume_quotas_v2_client = self.volume_v2.QuotasClient()
+ self.volumes_extension_client = self.volume_v1.ExtensionsClient()
+ self.volumes_v2_extension_client = self.volume_v2.ExtensionsClient()
self.volume_availability_zone_client = \
- volume.v1.AvailabilityZoneClient(self.auth_provider, **params)
+ self.volume_v1.AvailabilityZoneClient()
self.volume_v2_availability_zone_client = \
- volume.v2.AvailabilityZoneClient(self.auth_provider, **params)
+ self.volume_v2.AvailabilityZoneClient()
+ self.volume_limits_client = self.volume_v1.LimitsClient()
+ self.volume_v2_limits_client = self.volume_v2.LimitsClient()
def _set_object_storage_clients(self):
# Mandatory parameters (always defined)
@@ -350,7 +327,7 @@
# kwargs for auth provider match the common ones used by service clients
default_params = config.service_client_config()
if credentials is None:
- raise exceptions.InvalidCredentials(
+ raise lib_exc.InvalidCredentials(
'Credentials must be specified')
auth_provider_class, auth_url = get_auth_provider_class(
credentials)
diff --git a/tempest/cmd/account_generator.py b/tempest/cmd/account_generator.py
index 1779252..3d38e25 100755
--- a/tempest/cmd/account_generator.py
+++ b/tempest/cmd/account_generator.py
@@ -22,7 +22,7 @@
credentials for created users, so each user will be in separate tenant and
have the username, tenant_name, password and roles.
-**Usage:** ``tempest-account-generator [-h] [OPTIONS] accounts_file.yaml``.
+**Usage:** ``tempest account-generator [-h] [OPTIONS] accounts_file.yaml``.
Positional Arguments
--------------------
@@ -90,7 +90,7 @@
**-i VERSION**, **--identity-version VERSION** (Optional) Provisions accounts
using the specified version of the identity API. (default: '3').
-To see help on specific argument, please do: ``tempest-account-generator
+To see help on specific argument, please do: ``tempest account-generator
[OPTIONS] <accounts_file.yaml> -h``.
"""
import argparse
@@ -262,9 +262,9 @@
def get_options():
- usage_string = ('tempest-account-generator [-h] <ARG> ...\n\n'
+ usage_string = ('tempest account-generator [-h] <ARG> ...\n\n'
'To see help on specific argument, do:\n'
- 'tempest-account-generator <ARG> -h')
+ 'tempest account-generator <ARG> -h')
parser = argparse.ArgumentParser(
description=DESCRIPTION,
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
diff --git a/tempest/cmd/main.py b/tempest/cmd/main.py
index 641d11c..1090c41 100644
--- a/tempest/cmd/main.py
+++ b/tempest/cmd/main.py
@@ -11,11 +11,11 @@
# License for the specific language governing permissions and limitations
# under the License.
-import logging
import sys
from cliff import app
from cliff import commandmanager
+from oslo_log import log as logging
from pbr import version
diff --git a/tempest/cmd/run_stress.py b/tempest/cmd/run_stress.py
deleted file mode 100755
index 7502c23..0000000
--- a/tempest/cmd/run_stress.py
+++ /dev/null
@@ -1,172 +0,0 @@
-#!/usr/bin/env python
-
-# Copyright 2013 Quanta Research Cambridge, Inc.
-#
-# 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 argparse
-import inspect
-import sys
-try:
- from unittest import loader
-except ImportError:
- # unittest in python 2.6 does not contain loader, so uses unittest2
- from unittest2 import loader
-import traceback
-import warnings
-
-from cliff import command
-from oslo_log import log as logging
-from oslo_serialization import jsonutils as json
-from testtools import testsuite
-
-from tempest.stress import driver
-
-LOG = logging.getLogger(__name__)
-
-
-def discover_stress_tests(path="./", filter_attr=None, call_inherited=False):
- """Discovers all tempest tests and create action out of them"""
- LOG.info("Start test discovery")
- tests = []
- testloader = loader.TestLoader()
- list = testloader.discover(path)
- for func in (testsuite.iterate_tests(list)):
- attrs = []
- try:
- method_name = getattr(func, '_testMethodName')
- full_name = "%s.%s.%s" % (func.__module__,
- func.__class__.__name__,
- method_name)
- test_func = getattr(func, method_name)
- # NOTE(mkoderer): this contains a list of all type attributes
- attrs = getattr(test_func, "__testtools_attrs")
- except Exception:
- next
- if 'stress' in attrs:
- if filter_attr is not None and filter_attr not in attrs:
- continue
- class_setup_per = getattr(test_func, "st_class_setup_per")
-
- action = {'action':
- "tempest.stress.actions.unit_test.UnitTest",
- 'kwargs': {"test_method": full_name,
- "class_setup_per": class_setup_per
- }
- }
- if (not call_inherited and
- getattr(test_func, "st_allow_inheritance") is not True):
- class_structure = inspect.getmro(test_func.im_class)
- if test_func.__name__ not in class_structure[0].__dict__:
- continue
- tests.append(action)
- return tests
-
-
-class TempestRunStress(command.Command):
-
- @staticmethod
- def display_deprecation_warning():
- warnings.simplefilter('once', category=DeprecationWarning)
- warnings.warn(
- 'Stress tests are deprecated and will be removed from Tempest '
- 'in the Newton release.',
- DeprecationWarning)
- warnings.resetwarnings()
-
- def get_parser(self, prog_name):
- self.display_deprecation_warning()
- pa = super(TempestRunStress, self).get_parser(prog_name)
- pa = add_arguments(pa)
- return pa
-
- def take_action(self, pa):
- try:
- action(pa)
- except Exception:
- LOG.exception("Failure in the stress test framework")
- traceback.print_exc()
- raise
-
- def get_description(self):
- return 'Run tempest stress tests'
-
-
-def add_arguments(parser):
- parser.add_argument('-d', '--duration', default=300, type=int,
- help="Duration of test in secs")
- parser.add_argument('-s', '--serial', action='store_true',
- help="Trigger running tests serially")
- parser.add_argument('-S', '--stop', action='store_true',
- default=False, help="Stop on first error")
- parser.add_argument('-n', '--number', type=int,
- help="How often an action is executed for each "
- "process")
- group = parser.add_mutually_exclusive_group(required=True)
- group.add_argument('-a', '--all', action='store_true',
- help="Execute all stress tests")
- parser.add_argument('-T', '--type',
- help="Filters tests of a certain type (e.g. gate)")
- parser.add_argument('-i', '--call-inherited', action='store_true',
- default=False,
- help="Call also inherited function with stress "
- "attribute")
- group.add_argument('-t', "--tests", nargs='?',
- help="Name of the file with test description")
- return parser
-
-
-def action(ns):
- result = 0
- if not ns.all:
- tests = json.load(open(ns.tests, 'r'))
- else:
- tests = discover_stress_tests(filter_attr=ns.type,
- call_inherited=ns.call_inherited)
-
- if ns.serial:
- # Duration is total time
- duration = ns.duration / len(tests)
- for test in tests:
- step_result = driver.stress_openstack([test],
- duration,
- ns.number,
- ns.stop)
- # NOTE(mkoderer): we just save the last result code
- if (step_result != 0):
- result = step_result
- if ns.stop:
- return result
- else:
- result = driver.stress_openstack(tests,
- ns.duration,
- ns.number,
- ns.stop)
- return result
-
-
-def main():
- TempestRunStress.display_deprecation_warning()
- parser = argparse.ArgumentParser(description='Run stress tests')
- pa = add_arguments(parser)
- ns = pa.parse_args()
- return action(ns)
-
-
-if __name__ == "__main__":
- try:
- sys.exit(main())
- except Exception:
- LOG.exception("Failure in the stress test framework")
- traceback.print_exc()
- sys.exit(1)
diff --git a/tempest/common/compute.py b/tempest/common/compute.py
index 318eb10..64543fb 100644
--- a/tempest/common/compute.py
+++ b/tempest/common/compute.py
@@ -169,27 +169,28 @@
return body, servers
-def shelve_server(client, server_id, force_shelve_offload=False):
+def shelve_server(servers_client, server_id, force_shelve_offload=False):
"""Common wrapper utility to shelve server.
This method is a common wrapper to make server in 'SHELVED'
or 'SHELVED_OFFLOADED' state.
+ :param servers_clients: Compute servers client instance.
:param server_id: Server to make in shelve state
:param force_shelve_offload: Forcefully offload shelve server if it
is configured not to offload server
automatically after offload time.
"""
- client.shelve_server(server_id)
+ servers_client.shelve_server(server_id)
offload_time = CONF.compute.shelved_offload_time
if offload_time >= 0:
- waiters.wait_for_server_status(client, server_id,
+ waiters.wait_for_server_status(servers_client, server_id,
'SHELVED_OFFLOADED',
extra_timeout=offload_time)
else:
- waiters.wait_for_server_status(client, server_id, 'SHELVED')
+ waiters.wait_for_server_status(servers_client, server_id, 'SHELVED')
if force_shelve_offload:
- client.shelve_offload_server(server_id)
- waiters.wait_for_server_status(client, server_id,
+ servers_client.shelve_offload_server(server_id)
+ waiters.wait_for_server_status(servers_client, server_id,
'SHELVED_OFFLOADED')
diff --git a/tempest/common/dynamic_creds.py b/tempest/common/dynamic_creds.py
index 5c12fd8..2763d16 100644
--- a/tempest/common/dynamic_creds.py
+++ b/tempest/common/dynamic_creds.py
@@ -17,7 +17,7 @@
import six
from tempest import clients
-from tempest.common import cred_client
+from tempest.lib.common import cred_client
from tempest.lib.common import cred_provider
from tempest.lib.common.utils import data_utils
from tempest.lib import exceptions as lib_exc
diff --git a/tempest/common/generator/__init__.py b/tempest/common/generator/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/common/generator/__init__.py
+++ /dev/null
diff --git a/tempest/common/generator/base_generator.py b/tempest/common/generator/base_generator.py
deleted file mode 100644
index 0647edb..0000000
--- a/tempest/common/generator/base_generator.py
+++ /dev/null
@@ -1,176 +0,0 @@
-# Copyright 2014 Deutsche Telekom AG
-# 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
-import functools
-
-import jsonschema
-import six
-
-
-def _check_for_expected_result(name, schema):
- expected_result = None
- if "results" in schema:
- if name in schema["results"]:
- expected_result = schema["results"][name]
- return expected_result
-
-
-def generator_type(*args, **kwargs):
- def wrapper(func):
- func.types = args
- for key in kwargs:
- setattr(func, key, kwargs[key])
- return func
- return wrapper
-
-
-def simple_generator(fn):
- """Decorator for simple generators that return one value"""
- @functools.wraps(fn)
- def wrapped(self, schema):
- result = fn(self, schema)
- if result is not None:
- expected_result = _check_for_expected_result(fn.__name__, schema)
- return (fn.__name__, result, expected_result)
- return
- return wrapped
-
-
-class BasicGeneratorSet(object):
- _instance = None
-
- schema = {
- "type": "object",
- "properties": {
- "name": {"type": "string"},
- "http-method": {
- "enum": ["GET", "PUT", "HEAD",
- "POST", "PATCH", "DELETE", 'COPY']
- },
- "admin_client": {"type": "boolean"},
- "url": {"type": "string"},
- "default_result_code": {"type": "integer"},
- "json-schema": {},
- "resources": {
- "type": "array",
- "items": {
- "oneOf": [
- {"type": "string"},
- {
- "type": "object",
- "properties": {
- "name": {"type": "string"},
- "expected_result": {"type": "integer"}
- }
- }
- ]
- }
- },
- "results": {
- "type": "object",
- "properties": {}
- }
- },
- "required": ["name", "http-method", "url"],
- "additionalProperties": False,
- }
-
- def __init__(self):
- self.types_dict = {}
- for m in dir(self):
- if callable(getattr(self, m)) and not'__' in m:
- method = getattr(self, m)
- if hasattr(method, "types"):
- for type in method.types:
- if type not in self.types_dict:
- self.types_dict[type] = []
- self.types_dict[type].append(method)
-
- def validate_schema(self, schema):
- if "json-schema" in schema:
- jsonschema.Draft4Validator.check_schema(schema['json-schema'])
- jsonschema.validate(schema, self.schema)
-
- def generate_scenarios(self, schema, path=None):
- """Generate scenario (all possible test cases) out of the given schema
-
- :param schema: a dict style schema (see ``BasicGeneratorSet.schema``)
- :param path: the schema path if the given schema is a subschema
- """
- schema_type = schema['type']
- scenarios = []
-
- if schema_type == 'object':
- properties = schema["properties"]
- for attribute, definition in six.iteritems(properties):
- current_path = copy.copy(path)
- if path is not None:
- current_path.append(attribute)
- else:
- current_path = [attribute]
- scenarios.extend(
- self.generate_scenarios(definition, current_path))
- elif isinstance(schema_type, list):
- if "integer" in schema_type:
- schema_type = "integer"
- else:
- raise Exception("non-integer list types not supported")
- for generator in self.types_dict[schema_type]:
- if hasattr(generator, "needed_property"):
- prop = generator.needed_property
- if (prop not in schema or
- schema[prop] is None or
- schema[prop] is False):
- continue
-
- name = generator.__name__
- if ("exclude_tests" in schema and
- name in schema["exclude_tests"]):
- continue
- if path is not None:
- name = "%s_%s" % ("_".join(path), name)
- scenarios.append({
- "_negtest_name": name,
- "_negtest_generator": generator,
- "_negtest_schema": schema,
- "_negtest_path": path})
- return scenarios
-
- def generate_payload(self, test, schema):
- """Generates one jsonschema out of the given test.
-
- It's mandatory to use generate_scenarios before to register all needed
- variables to the test.
-
- :param test: A test object (scenario) with all _negtest variables on it
- :param schema: schema for the test
- """
- generator = test._negtest_generator
- ret = generator(test._negtest_schema)
- path = copy.copy(test._negtest_path)
- expected_result = None
-
- if ret is not None:
- generator_result = generator(test._negtest_schema)
- invalid_snippet = generator_result[1]
- expected_result = generator_result[2]
- element = path.pop()
- if len(path) > 0:
- schema_snip = six.moves.reduce(dict.get, path, schema)
- schema_snip[element] = invalid_snippet
- else:
- schema[element] = invalid_snippet
- return expected_result
diff --git a/tempest/common/generator/negative_generator.py b/tempest/common/generator/negative_generator.py
deleted file mode 100644
index 67ace54..0000000
--- a/tempest/common/generator/negative_generator.py
+++ /dev/null
@@ -1,75 +0,0 @@
-# Copyright 2014 Deutsche Telekom AG
-# 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
-
-import tempest.common.generator.base_generator as base
-import tempest.common.generator.valid_generator as valid
-
-
-class NegativeTestGenerator(base.BasicGeneratorSet):
- @base.generator_type("string")
- @base.simple_generator
- def gen_int(self, _):
- return 4
-
- @base.generator_type("integer")
- @base.simple_generator
- def gen_string(self, _):
- return "XXXXXX"
-
- @base.generator_type("integer", "string")
- def gen_none(self, schema):
- # Note(mkoderer): it's not using the decorator otherwise it'd be
- # filtered
- expected_result = base._check_for_expected_result('gen_none', schema)
- return ('gen_none', None, expected_result)
-
- @base.generator_type("string")
- @base.simple_generator
- def gen_str_min_length(self, schema):
- min_length = schema.get("minLength", 0)
- if min_length > 0:
- return "x" * (min_length - 1)
-
- @base.generator_type("string", needed_property="maxLength")
- @base.simple_generator
- def gen_str_max_length(self, schema):
- max_length = schema.get("maxLength", -1)
- return "x" * (max_length + 1)
-
- @base.generator_type("integer", needed_property="minimum")
- @base.simple_generator
- def gen_int_min(self, schema):
- minimum = schema["minimum"]
- if "exclusiveMinimum" not in schema:
- minimum -= 1
- return minimum
-
- @base.generator_type("integer", needed_property="maximum")
- @base.simple_generator
- def gen_int_max(self, schema):
- maximum = schema["maximum"]
- if "exclusiveMaximum" not in schema:
- maximum += 1
- return maximum
-
- @base.generator_type("object", needed_property="additionalProperties")
- @base.simple_generator
- def gen_obj_add_attr(self, schema):
- valid_schema = valid.ValidTestGenerator().generate_valid(schema)
- new_valid = copy.deepcopy(valid_schema)
- new_valid["$$$$$$$$$$"] = "xxx"
- return new_valid
diff --git a/tempest/common/generator/valid_generator.py b/tempest/common/generator/valid_generator.py
deleted file mode 100644
index 3070489..0000000
--- a/tempest/common/generator/valid_generator.py
+++ /dev/null
@@ -1,79 +0,0 @@
-# Copyright 2014 Deutsche Telekom AG
-# 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 six
-
-import tempest.common.generator.base_generator as base
-
-
-class ValidTestGenerator(base.BasicGeneratorSet):
- @base.generator_type("string")
- @base.simple_generator
- def generate_valid_string(self, schema):
- size = schema.get("minLength", 1)
- # TODO(dkr mko): handle format and pattern
- return "x" * size
-
- @base.generator_type("integer")
- @base.simple_generator
- def generate_valid_integer(self, schema):
- # TODO(dkr mko): handle multipleOf
- if "minimum" in schema:
- minimum = schema["minimum"]
- if "exclusiveMinimum" not in schema:
- return minimum
- else:
- return minimum + 1
- if "maximum" in schema:
- maximum = schema["maximum"]
- if "exclusiveMaximum" not in schema:
- return maximum
- else:
- return maximum - 1
- return 0
-
- @base.generator_type("object")
- @base.simple_generator
- def generate_valid_object(self, schema):
- obj = {}
- for k, v in six.iteritems(schema["properties"]):
- obj[k] = self.generate_valid(v)
- return obj
-
- def generate(self, schema):
- schema_type = schema["type"]
- if isinstance(schema_type, list):
- if "integer" in schema_type:
- schema_type = "integer"
- else:
- raise Exception("non-integer list types not supported")
- result = []
- if schema_type not in self.types_dict:
- raise TypeError("generator (%s) doesn't support type: %s"
- % (self.__class__.__name__, schema_type))
- for generator in self.types_dict[schema_type]:
- ret = generator(schema)
- if ret is not None:
- if isinstance(ret, list):
- result.extend(ret)
- elif isinstance(ret, tuple):
- result.append(ret)
- else:
- raise Exception("generator (%s) returns invalid result: %s"
- % (generator, ret))
- return result
-
- def generate_valid(self, schema):
- return self.generate(schema)[0][1]
diff --git a/tempest/common/negative_rest_client.py b/tempest/common/negative_rest_client.py
deleted file mode 100644
index 3495a24..0000000
--- a/tempest/common/negative_rest_client.py
+++ /dev/null
@@ -1,70 +0,0 @@
-# (c) 2014 Deutsche Telekom AG
-# Copyright 2014 Red Hat, Inc.
-# Copyright 2014 NEC 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.
-
-from tempest import config
-from tempest.lib.common import rest_client
-
-CONF = config.CONF
-
-
-class NegativeRestClient(rest_client.RestClient):
- """Version of RestClient that does not raise exceptions."""
- def __init__(self, auth_provider, service, **kwargs):
- region, endpoint_type = self._get_region_and_endpoint_type(service)
- super(NegativeRestClient, self).__init__(
- auth_provider, service, region, endpoint_type=endpoint_type,
- **kwargs)
-
- def _get_region_and_endpoint_type(self, service):
- """Returns the region for a specific service"""
- service_region = None
- service_endpoint_type = None
- for cfgname in dir(CONF._config):
- # Find all config.FOO.catalog_type and assume FOO is a service.
- cfg = getattr(CONF, cfgname)
- catalog_type = getattr(cfg, 'catalog_type', None)
- if catalog_type == service:
- service_region = getattr(cfg, 'region', None)
- service_endpoint_type = getattr(cfg, 'endpoint_type', None)
- if not service_region:
- service_region = CONF.identity.region
- return service_region, service_endpoint_type
-
- def _error_checker(self, method, url,
- headers, body, resp, resp_body):
- pass
-
- def send_request(self, method, url_template, resources, body=None):
- url = url_template % tuple(resources)
- if method == "GET":
- resp, body = self.get(url)
- elif method == "POST":
- resp, body = self.post(url, body)
- elif method == "PUT":
- resp, body = self.put(url, body)
- elif method == "PATCH":
- resp, body = self.patch(url, body)
- elif method == "HEAD":
- resp, body = self.head(url)
- elif method == "DELETE":
- resp, body = self.delete(url)
- elif method == "COPY":
- resp, body = self.copy(url)
- else:
- assert False
-
- return resp, body
diff --git a/tempest/common/utils/net_utils.py b/tempest/common/utils/net_utils.py
index f0d3da3..867b3dd 100644
--- a/tempest/common/utils/net_utils.py
+++ b/tempest/common/utils/net_utils.py
@@ -51,3 +51,23 @@
return addrs
msg = "Insufficient IP addresses available"
raise lib_exc.BadRequest(message=msg)
+
+
+def get_ping_payload_size(mtu, ip_version):
+ """Return the maximum size of ping payload that will fit into MTU."""
+ if not mtu:
+ return None
+ if ip_version == 4:
+ ip_header = 20
+ icmp_header = 8
+ else:
+ ip_header = 40
+ icmp_header = 4
+ res = mtu - ip_header - icmp_header
+ if res < 0:
+ raise lib_exc.BadRequest(
+ message='MTU = %(mtu)d is too low for IPv%(ip_version)d' % {
+ 'mtu': mtu,
+ 'ip_version': ip_version,
+ })
+ return res
diff --git a/tempest/common/waiters.py b/tempest/common/waiters.py
index c1942d6..981a922 100644
--- a/tempest/common/waiters.py
+++ b/tempest/common/waiters.py
@@ -92,7 +92,7 @@
caller = test_utils.find_test_caller()
if caller:
message = '(%s) %s' % (caller, message)
- raise exceptions.TimeoutException(message)
+ raise lib_exc.TimeoutException(message)
old_status = server_status
old_task_state = task_state
@@ -111,7 +111,7 @@
raise exceptions.BuildErrorException(server_id=server_id)
if int(time.time()) - start_time >= client.build_timeout:
- raise exceptions.TimeoutException
+ raise lib_exc.TimeoutException
time.sleep(client.build_interval)
@@ -163,7 +163,7 @@
caller = test_utils.find_test_caller()
if caller:
message = '(%s) %s' % (caller, message)
- raise exceptions.TimeoutException(message)
+ raise lib_exc.TimeoutException(message)
def wait_for_volume_status(client, volume_id, status):
@@ -176,7 +176,7 @@
time.sleep(client.build_interval)
body = client.show_volume(volume_id)['volume']
volume_status = body['status']
- if volume_status == 'error':
+ if volume_status == 'error' and status != 'error':
raise exceptions.VolumeBuildErrorException(volume_id=volume_id)
if volume_status == 'error_restoring':
raise exceptions.VolumeRestoreErrorException(volume_id=volume_id)
@@ -186,7 +186,7 @@
'within the required time (%s s).' %
(volume_id, status, volume_status,
client.build_timeout))
- raise exceptions.TimeoutException(message)
+ raise lib_exc.TimeoutException(message)
def wait_for_snapshot_status(client, snapshot_id, status):
@@ -207,7 +207,7 @@
'within the required time (%s s).' %
(snapshot_id, status, snapshot_status,
client.build_timeout))
- raise exceptions.TimeoutException(message)
+ raise lib_exc.TimeoutException(message)
def wait_for_backup_status(client, backup_id, status):
@@ -228,7 +228,7 @@
'(current %s) within the required time (%s s).' %
(backup_id, status, backup_status,
client.build_timeout))
- raise exceptions.TimeoutException(message)
+ raise lib_exc.TimeoutException(message)
def wait_for_bm_node_status(client, node_id, attr, status):
@@ -257,7 +257,7 @@
caller = test_utils.find_test_caller()
if caller:
message = '(%s) %s' % (caller, message)
- raise exceptions.TimeoutException(message)
+ raise lib_exc.TimeoutException(message)
def wait_for_qos_operations(client, qos_id, operation, args=None):
@@ -288,5 +288,30 @@
raise lib_exc.UnprocessableEntity(msg)
if int(time.time()) - start_time >= client.build_timeout:
- raise exceptions.TimeoutException
+ raise lib_exc.TimeoutException
time.sleep(client.build_interval)
+
+
+def wait_for_interface_status(client, server, port_id, status):
+ """Waits for an interface to reach a given status."""
+ body = (client.show_interface(server, port_id)
+ ['interfaceAttachment'])
+ interface_status = body['port_state']
+ start = int(time.time())
+
+ while(interface_status != status):
+ time.sleep(client.build_interval)
+ body = (client.show_interface(server, port_id)
+ ['interfaceAttachment'])
+ interface_status = body['port_state']
+
+ timed_out = int(time.time()) - start >= client.build_timeout
+
+ if interface_status != status and timed_out:
+ message = ('Interface %s failed to reach %s status '
+ '(current %s) within the required time (%s s).' %
+ (port_id, status, interface_status,
+ client.build_timeout))
+ raise lib_exc.TimeoutException(message)
+
+ return body
diff --git a/tempest/config.py b/tempest/config.py
index 8ce38f9..47eb427 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -278,6 +278,11 @@
'be utilized by some multinode specific tests to ensure '
'that requests match the expected size of the cluster '
'you are testing with.')),
+ cfg.StrOpt('hypervisor_type',
+ default=None,
+ help="Hypervisor type of the test target on heterogeneous "
+ "compute environment. The value can be 'QEMU', 'xen' or "
+ "something."),
cfg.StrOpt('min_microversion',
default=None,
help="Lower version of the test target microversion range. "
@@ -397,7 +402,10 @@
cfg.BoolOpt('nova_cert',
default=False,
help='Does the test environment have the nova cert running?',
- deprecated_for_removal=True),
+ deprecated_for_removal=True,
+ deprecated_reason="On Nova side, the nova-cert service is "
+ "deprecated and the service will be removed "
+ "as early as Ocata."),
cfg.BoolOpt('personality',
default=False,
help='Does the test environment support server personality'),
@@ -780,11 +788,6 @@
cfg.BoolOpt('api_v3',
default=False,
help="Is the v3 volume API enabled"),
- cfg.BoolOpt('bootable',
- default=True,
- help='Update bootable status of a volume '
- 'Not implemented on icehouse ',
- deprecated_for_removal=True),
# TODO(ynesenenko): Remove volume_services once liberty-eol happens.
cfg.BoolOpt('volume_services',
default=False,
@@ -900,44 +903,6 @@
]
-stress_group = cfg.OptGroup(name='stress', title='Stress Test Options')
-
-StressGroup = [
- cfg.StrOpt('nova_logdir',
- help='Directory containing log files on the compute nodes'),
- cfg.IntOpt('max_instances',
- default=16,
- help='Maximum number of instances to create during test.'),
- cfg.StrOpt('controller',
- help='Controller host.'),
- # new stress options
- cfg.StrOpt('target_controller',
- help='Controller host.'),
- cfg.StrOpt('target_ssh_user',
- help='ssh user.'),
- cfg.StrOpt('target_private_key_path',
- help='Path to private key.'),
- cfg.StrOpt('target_logfiles',
- help='regexp for list of log files.'),
- cfg.IntOpt('log_check_interval',
- default=60,
- help='time (in seconds) between log file error checks.'),
- cfg.IntOpt('default_thread_number_per_action',
- default=4,
- help='The number of threads created while stress test.'),
- cfg.BoolOpt('leave_dirty_stack',
- default=False,
- help='Prevent the cleaning (tearDownClass()) between'
- ' each stress test run if an exception occurs'
- ' during this run.'),
- cfg.BoolOpt('full_clean_stack',
- default=False,
- help='Allows a full cleaning process after a stress test.'
- ' Caution : this cleanup will remove every objects of'
- ' every project.')
-]
-
-
scenario_group = cfg.OptGroup(name='scenario', title='Scenario Test Options')
ScenarioGroup = [
@@ -1110,14 +1075,6 @@
"step in Node cleaning.")
]
-negative_group = cfg.OptGroup(name='negative', title="Negative Test Options")
-
-NegativeGroup = [
- cfg.StrOpt('test_generator',
- default='tempest.common.' +
- 'generator.negative_generator.NegativeTestGenerator',
- help="Test generator class for all negative tests"),
-]
DefaultGroup = [
cfg.StrOpt('resources_prefix',
@@ -1145,13 +1102,11 @@
(object_storage_group, ObjectStoreGroup),
(object_storage_feature_group, ObjectStoreFeaturesGroup),
(orchestration_group, OrchestrationGroup),
- (stress_group, StressGroup),
(scenario_group, ScenarioGroup),
(service_available_group, ServiceAvailableGroup),
(debug_group, DebugGroup),
(baremetal_group, BaremetalGroup),
(input_scenario_group, InputScenarioGroup),
- (negative_group, NegativeGroup),
(None, DefaultGroup)
]
@@ -1210,13 +1165,11 @@
self.object_storage_feature_enabled = _CONF[
'object-storage-feature-enabled']
self.orchestration = _CONF.orchestration
- self.stress = _CONF.stress
self.scenario = _CONF.scenario
self.service_available = _CONF.service_available
self.debug = _CONF.debug
self.baremetal = _CONF.baremetal
self.input_scenario = _CONF['input-scenario']
- self.negative = _CONF.negative
logging.tempest_set_log_file('tempest.log')
def __init__(self, parse_conf=True, config_path=None):
diff --git a/tempest/exceptions.py b/tempest/exceptions.py
index 727d54e..43f919a 100644
--- a/tempest/exceptions.py
+++ b/tempest/exceptions.py
@@ -21,10 +21,6 @@
message = "Invalid service tag"
-class TimeoutException(exceptions.TempestException):
- message = "Request timed out"
-
-
class BuildErrorException(exceptions.TempestException):
message = "Server %(server_id)s failed to build and is in ERROR status"
diff --git a/tempest/lib/api_schema/response/compute/v2_1/images.py b/tempest/lib/api_schema/response/compute/v2_1/images.py
index b0f1934..f65b9d8 100644
--- a/tempest/lib/api_schema/response/compute/v2_1/images.py
+++ b/tempest/lib/api_schema/response/compute/v2_1/images.py
@@ -19,11 +19,13 @@
image_links = copy.deepcopy(parameter_types.links)
image_links['items']['properties'].update({'type': {'type': 'string'}})
+image_status_enums = ['ACTIVE', 'SAVING', 'DELETED', 'ERROR', 'UNKNOWN']
+
common_image_schema = {
'type': 'object',
'properties': {
'id': {'type': 'string'},
- 'status': {'type': 'string'},
+ 'status': {'enum': image_status_enums},
'updated': {'type': 'string'},
'links': image_links,
'name': {'type': ['string', 'null']},
diff --git a/tempest/lib/api_schema/response/compute/v2_1/parameter_types.py b/tempest/lib/api_schema/response/compute/v2_1/parameter_types.py
index 07cc890..3cc5ca4 100644
--- a/tempest/lib/api_schema/response/compute/v2_1/parameter_types.py
+++ b/tempest/lib/api_schema/response/compute/v2_1/parameter_types.py
@@ -94,3 +94,14 @@
'format': 'data-time'
}
}
+
+power_state = {
+ 'type': 'integer',
+ # 0: NOSTATE
+ # 1: RUNNING
+ # 3: PAUSED
+ # 4: SHUTDOWN
+ # 6: CRASHED
+ # 7: SUSPENDED
+ 'enum': [0, 1, 3, 4, 6, 7]
+}
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 44497db..63e8467 100644
--- a/tempest/lib/api_schema/response/compute/v2_1/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_1/servers.py
@@ -170,7 +170,7 @@
# attributes.
'OS-EXT-STS:task_state': {'type': ['string', 'null']},
'OS-EXT-STS:vm_state': {'type': 'string'},
- 'OS-EXT-STS:power_state': {'type': 'integer'},
+ 'OS-EXT-STS:power_state': parameter_types.power_state,
'OS-EXT-SRV-ATTR:host': {'type': ['string', 'null']},
'OS-EXT-SRV-ATTR:instance_name': {'type': 'string'},
'OS-EXT-SRV-ATTR:hypervisor_hostname': {'type': ['string', 'null']},
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 6868110..3eb658f 100644
--- a/tempest/lib/api_schema/response/compute/v2_16/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_16/servers.py
@@ -77,7 +77,7 @@
'OS-EXT-AZ:availability_zone': {'type': 'string'},
'OS-EXT-STS:task_state': {'type': ['string', 'null']},
'OS-EXT-STS:vm_state': {'type': 'string'},
- 'OS-EXT-STS:power_state': {'type': 'integer'},
+ 'OS-EXT-STS:power_state': parameter_types.power_state,
'OS-EXT-SRV-ATTR:host': {'type': ['string', 'null']},
'OS-EXT-SRV-ATTR:instance_name': {'type': 'string'},
'OS-EXT-SRV-ATTR:hypervisor_hostname': {'type': ['string', 'null']},
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 ee16333..f24103e 100644
--- a/tempest/lib/api_schema/response/compute/v2_3/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_3/servers.py
@@ -85,7 +85,7 @@
'OS-EXT-AZ:availability_zone': {'type': 'string'},
'OS-EXT-STS:task_state': {'type': ['string', 'null']},
'OS-EXT-STS:vm_state': {'type': 'string'},
- 'OS-EXT-STS:power_state': {'type': 'integer'},
+ 'OS-EXT-STS:power_state': parameter_types.power_state,
'OS-EXT-SRV-ATTR:host': {'type': ['string', 'null']},
'OS-EXT-SRV-ATTR:instance_name': {'type': 'string'},
'OS-EXT-SRV-ATTR:hypervisor_hostname': {'type': ['string', 'null']},
diff --git a/tempest/lib/cli/base.py b/tempest/lib/cli/base.py
index a43d002..72a15b5 100644
--- a/tempest/lib/cli/base.py
+++ b/tempest/lib/cli/base.py
@@ -29,7 +29,7 @@
def execute(cmd, action, flags='', params='', fail_ok=False,
- merge_stderr=False, cli_dir='/usr/bin'):
+ merge_stderr=False, cli_dir='/usr/bin', prefix=''):
"""Executes specified command for the given action.
:param cmd: command to be executed
@@ -48,9 +48,12 @@
:type merge_stderr: boolean
:param cli_dir: The path where the cmd can be executed
:type cli_dir: string
+ :param prefix: prefix to insert before command
+ :type prefix: string
"""
- cmd = ' '.join([os.path.join(cli_dir, cmd),
+ cmd = ' '.join([prefix, os.path.join(cli_dir, cmd),
flags, action, params])
+ cmd = cmd.strip()
LOG.info("running: '%s'" % cmd)
if six.PY2:
cmd = cmd.encode('utf-8')
@@ -88,10 +91,12 @@
:type cli_dir: string
:param insecure: if True, --insecure is passed to python client binaries.
:type insecure: boolean
+ :param prefix: prefix to insert before commands
+ :type prefix: string
"""
def __init__(self, username='', password='', tenant_name='', uri='',
- cli_dir='', insecure=False, *args, **kwargs):
+ cli_dir='', insecure=False, prefix='', *args, **kwargs):
"""Initialize a new CLIClient object."""
super(CLIClient, self).__init__()
self.cli_dir = cli_dir if cli_dir else '/usr/bin'
@@ -100,6 +105,7 @@
self.password = password
self.uri = uri
self.insecure = insecure
+ self.prefix = prefix
def nova(self, action, flags='', params='', fail_ok=False,
endpoint_type='publicURL', merge_stderr=False):
@@ -365,7 +371,7 @@
else:
flags = creds + ' ' + flags
return execute(cmd, action, flags, params, fail_ok, merge_stderr,
- self.cli_dir)
+ self.cli_dir, prefix=self.prefix)
class ClientTestBase(base.BaseTestCase):
diff --git a/tempest/lib/cmd/check_uuid.py b/tempest/lib/cmd/check_uuid.py
index 1239ac5..88ce775 100755
--- a/tempest/lib/cmd/check_uuid.py
+++ b/tempest/lib/cmd/check_uuid.py
@@ -23,6 +23,7 @@
import unittest
import uuid
+from oslo_utils import uuidutils
import six.moves.urllib.parse as urlparse
DECORATOR_MODULE = 'test'
@@ -61,7 +62,7 @@
if filename not in self.source_files:
with open(filename) as f:
self.source_files[filename] = self._quote(f.read())
- patch_id = str(uuid.uuid4())
+ patch_id = uuidutils.generate_uuid()
if not patch.endswith('\n'):
patch += '\n'
self.patches[patch_id] = self._quote(patch)
diff --git a/tempest/common/cred_client.py b/tempest/lib/common/cred_client.py
similarity index 100%
rename from tempest/common/cred_client.py
rename to tempest/lib/common/cred_client.py
diff --git a/tempest/lib/common/utils/data_utils.py b/tempest/lib/common/utils/data_utils.py
index 4095c77..75c2e51 100644
--- a/tempest/lib/common/utils/data_utils.py
+++ b/tempest/lib/common/utils/data_utils.py
@@ -21,6 +21,7 @@
from debtcollector import removals
from oslo_utils import netutils
+from oslo_utils import uuidutils
import six.moves
@@ -30,7 +31,7 @@
:return: a random UUID (e.g. '1dc12c7d-60eb-4b61-a7a2-17cf210155b6')
:rtype: string
"""
- return str(uuid.uuid4())
+ return uuidutils.generate_uuid()
def rand_uuid_hex():
diff --git a/tempest/lib/exceptions.py b/tempest/lib/exceptions.py
index a6c01bb..108ba70 100644
--- a/tempest/lib/exceptions.py
+++ b/tempest/lib/exceptions.py
@@ -71,54 +71,70 @@
message = "The success code is different than the expected one"
-class NotFound(ClientRestClientException):
- message = "Object not found"
+class BadRequest(ClientRestClientException):
+ status_code = 400
+ message = "Bad request"
class Unauthorized(ClientRestClientException):
+ status_code = 401
message = 'Unauthorized'
class Forbidden(ClientRestClientException):
+ status_code = 403
message = "Forbidden"
-class TimeoutException(OtherRestClientException):
- message = "Request timed out"
-
-
-class BadRequest(ClientRestClientException):
- message = "Bad request"
-
-
-class UnprocessableEntity(ClientRestClientException):
- message = "Unprocessable entity"
-
-
-class RateLimitExceeded(ClientRestClientException):
- message = "Rate limit exceeded"
-
-
-class OverLimit(ClientRestClientException):
- message = "Request entity is too large"
-
-
-class ServerFault(ServerRestClientException):
- message = "Got server fault"
-
-
-class NotImplemented(ServerRestClientException):
- message = "Got NotImplemented error"
+class NotFound(ClientRestClientException):
+ status_code = 404
+ message = "Object not found"
class Conflict(ClientRestClientException):
+ status_code = 409
message = "An object with that identifier already exists"
class Gone(ClientRestClientException):
+ status_code = 410
message = "The requested resource is no longer available"
+class RateLimitExceeded(ClientRestClientException):
+ status_code = 413
+ message = "Rate limit exceeded"
+
+
+class OverLimit(ClientRestClientException):
+ status_code = 413
+ message = "Request entity is too large"
+
+
+class InvalidContentType(ClientRestClientException):
+ status_code = 415
+ message = "Invalid content type provided"
+
+
+class UnprocessableEntity(ClientRestClientException):
+ status_code = 422
+ message = "Unprocessable entity"
+
+
+class ServerFault(ServerRestClientException):
+ status_code = 500
+ message = "Got server fault"
+
+
+class NotImplemented(ServerRestClientException):
+ status_code = 501
+ message = "Got NotImplemented error"
+
+
+class TimeoutException(OtherRestClientException):
+ message = "Request timed out"
+
+
class ResponseWithNonEmptyBody(OtherRestClientException):
message = ("RFC Violation! Response with %(status)d HTTP Status Code "
"MUST NOT have a body")
@@ -137,10 +153,6 @@
message = "HTTP response header is invalid"
-class InvalidContentType(ClientRestClientException):
- message = "Invalid content type provided"
-
-
class UnexpectedContentType(OtherRestClientException):
message = "Unexpected content type provided"
diff --git a/tempest/lib/services/clients.py b/tempest/lib/services/clients.py
index adf666b..0e8e3c6 100644
--- a/tempest/lib/services/clients.py
+++ b/tempest/lib/services/clients.py
@@ -23,8 +23,10 @@
from tempest.lib.common.utils import misc
from tempest.lib import exceptions
from tempest.lib.services import compute
+from tempest.lib.services import identity
from tempest.lib.services import image
from tempest.lib.services import network
+from tempest.lib.services import volume
LOG = logging.getLogger(__name__)
@@ -38,9 +40,13 @@
"""
return {
'compute': compute,
+ 'identity.v2': identity.v2,
'image.v1': image.v1,
'image.v2': image.v2,
- 'network': network
+ 'network': network,
+ 'volume.v1': volume.v1,
+ 'volume.v2': volume.v2,
+ 'volume.v3': volume.v3
}
@@ -49,8 +55,7 @@
# NOTE(andreaf) This list will exists only as long the remain clients
# are migrated to tempest.lib, and it will then be deleted without
# deprecation or advance notice
- return set(['identity.v2', 'identity.v3', 'object-storage', 'volume.v1',
- 'volume.v2', 'volume.v3'])
+ return set(['identity.v3', 'object-storage'])
def available_modules():
diff --git a/tempest/lib/services/compute/agents_client.py b/tempest/lib/services/compute/agents_client.py
index 3f05d3b..169d978 100644
--- a/tempest/lib/services/compute/agents_client.py
+++ b/tempest/lib/services/compute/agents_client.py
@@ -26,8 +26,9 @@
def list_agents(self, **params):
"""List all agent builds.
- Available params: see http://developer.openstack.org/
- api-ref-compute-v2.1.html#listbuilds
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#listbuilds
"""
url = 'os-agents'
if params:
@@ -40,8 +41,9 @@
def create_agent(self, **kwargs):
"""Create an agent build.
- Available params: see http://developer.openstack.org/
- api-ref-compute-v2.1.html#agentbuild
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#agentbuild
"""
post_body = json.dumps({'agent': kwargs})
resp, body = self.post('os-agents', post_body)
@@ -52,8 +54,9 @@
def delete_agent(self, agent_id):
"""Delete an existing agent build.
- Available params: see http://developer.openstack.org/
- api-ref-compute-v2.1.html#deleteBuild
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#deleteBuild
"""
resp, body = self.delete("os-agents/%s" % agent_id)
self.validate_response(schema.delete_agent, resp, body)
@@ -62,8 +65,9 @@
def update_agent(self, agent_id, **kwargs):
"""Update an agent build.
- Available params: see http://developer.openstack.org/
- api-ref-compute-v2.1.html#updatebuild
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#updatebuild
"""
put_body = json.dumps({'para': kwargs})
resp, body = self.put('os-agents/%s' % agent_id, put_body)
diff --git a/tempest/lib/services/compute/aggregates_client.py b/tempest/lib/services/compute/aggregates_client.py
index 7ad14bc..c1a6c8c 100644
--- a/tempest/lib/services/compute/aggregates_client.py
+++ b/tempest/lib/services/compute/aggregates_client.py
@@ -40,8 +40,9 @@
def create_aggregate(self, **kwargs):
"""Create a new aggregate.
- Available params: see http://developer.openstack.org/
- api-ref-compute-v2.1.html#createAggregate
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#createAggregate
"""
post_body = json.dumps({'aggregate': kwargs})
resp, body = self.post('os-aggregates', post_body)
@@ -53,8 +54,9 @@
def update_aggregate(self, aggregate_id, **kwargs):
"""Update an aggregate.
- Available params: see http://developer.openstack.org/
- api-ref-compute-v2.1.html#updateAggregate
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#updateAggregate
"""
put_body = json.dumps({'aggregate': kwargs})
resp, body = self.put('os-aggregates/%s' % aggregate_id, put_body)
@@ -84,8 +86,9 @@
def add_host(self, aggregate_id, **kwargs):
"""Add a host to the given aggregate.
- Available params: see http://developer.openstack.org/
- api-ref-compute-v2.1.html#addHost
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#addHost
"""
post_body = json.dumps({'add_host': kwargs})
resp, body = self.post('os-aggregates/%s/action' % aggregate_id,
@@ -97,8 +100,9 @@
def remove_host(self, aggregate_id, **kwargs):
"""Remove a host from the given aggregate.
- Available params: see http://developer.openstack.org/
- api-ref-compute-v2.1.html#removeAggregateHost
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#removeAggregateHost
"""
post_body = json.dumps({'remove_host': kwargs})
resp, body = self.post('os-aggregates/%s/action' % aggregate_id,
@@ -110,8 +114,9 @@
def set_metadata(self, aggregate_id, **kwargs):
"""Replace the aggregate's existing metadata with new metadata.
- Available params: see http://developer.openstack.org/
- api-ref-compute-v2.1.html#addAggregateMetadata
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#addAggregateMetadata
"""
post_body = json.dumps({'set_metadata': kwargs})
resp, body = self.post('os-aggregates/%s/action' % aggregate_id,
diff --git a/tempest/lib/services/compute/fixed_ips_client.py b/tempest/lib/services/compute/fixed_ips_client.py
index c25ac2c..682ee86 100644
--- a/tempest/lib/services/compute/fixed_ips_client.py
+++ b/tempest/lib/services/compute/fixed_ips_client.py
@@ -32,8 +32,9 @@
def reserve_fixed_ip(self, fixed_ip, **kwargs):
"""Reserve/Unreserve a fixed IP.
- Available params: see http://developer.openstack.org/
- api-ref-compute-v2.1.html#reserveIP
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#reserveIP
"""
url = "os-fixed-ips/%s/action" % fixed_ip
resp, body = self.post(url, json.dumps(kwargs))
diff --git a/tempest/lib/services/compute/flavors_client.py b/tempest/lib/services/compute/flavors_client.py
index ae1700c..4d1044b 100644
--- a/tempest/lib/services/compute/flavors_client.py
+++ b/tempest/lib/services/compute/flavors_client.py
@@ -30,8 +30,9 @@
def list_flavors(self, detail=False, **params):
"""Lists flavors.
- Available params: see http://developer.openstack.org/
- api-ref-compute-v2.1.html#listFlavors
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#listFlavors
"""
url = 'flavors'
_schema = schema.list_flavors
@@ -50,8 +51,9 @@
def show_flavor(self, flavor_id):
"""Shows details for a flavor.
- Available params: see http://developer.openstack.org/
- api-ref-compute-v2.1.html#showFlavor
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#showFlavor
"""
resp, body = self.get("flavors/%s" % flavor_id)
body = json.loads(body)
@@ -61,8 +63,9 @@
def create_flavor(self, **kwargs):
"""Create a new flavor or instance type.
- Available params: see http://developer.openstack.org/
- api-ref-compute-v2.1.html#createFlavor
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#createFlavor
"""
if kwargs.get('ephemeral'):
kwargs['OS-FLV-EXT-DATA:ephemeral'] = kwargs.pop('ephemeral')
@@ -79,8 +82,9 @@
def delete_flavor(self, flavor_id):
"""Delete the given flavor.
- Available params: see http://developer.openstack.org/
- api-ref-compute-v2.1.html#deleteFlavor
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#deleteFlavor
"""
resp, body = self.delete("flavors/{0}".format(flavor_id))
self.validate_response(schema.delete_flavor, resp, body)
@@ -104,8 +108,9 @@
def set_flavor_extra_spec(self, flavor_id, **kwargs):
"""Set extra Specs to the mentioned flavor.
- Available params: see http://developer.openstack.org/
- api-ref-compute-v2.1.html#createFlavorExtraSpec
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#createFlavorExtraSpec
"""
post_body = json.dumps({'extra_specs': kwargs})
resp, body = self.post('flavors/%s/os-extra_specs' % flavor_id,
@@ -118,8 +123,9 @@
def list_flavor_extra_specs(self, flavor_id):
"""Get extra Specs details of the mentioned flavor.
- Available params: see http://developer.openstack.org/
- api-ref-compute-v2.1.html#listFlavorExtraSpecs
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#listFlavorExtraSpecs
"""
resp, body = self.get('flavors/%s/os-extra_specs' % flavor_id)
body = json.loads(body)
@@ -130,8 +136,9 @@
def show_flavor_extra_spec(self, flavor_id, key):
"""Get extra Specs key-value of the mentioned flavor and key.
- Available params: see http://developer.openstack.org/
- api-ref-compute-v2.1.html#showFlavorExtraSpec
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#showFlavorExtraSpec
"""
resp, body = self.get('flavors/%s/os-extra_specs/%s' % (flavor_id,
key))
@@ -144,8 +151,9 @@
def update_flavor_extra_spec(self, flavor_id, key, **kwargs):
"""Update specified extra Specs of the mentioned flavor and key.
- Available params: see http://developer.openstack.org/
- api-ref-compute-v2.1.html#updateFlavorExtraSpec
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#updateFlavorExtraSpec
"""
resp, body = self.put('flavors/%s/os-extra_specs/%s' %
(flavor_id, key), json.dumps(kwargs))
@@ -160,8 +168,9 @@
# to keep backwards compatibility.
"""Unset extra Specs from the mentioned flavor.
- Available params: see http://developer.openstack.org/
- api-ref-compute-v2.1.html#deleteFlavorExtraSpec
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#deleteFlavorExtraSpec
"""
resp, body = self.delete('flavors/%s/os-extra_specs/%s' %
(flavor_id, key))
@@ -171,8 +180,9 @@
def list_flavor_access(self, flavor_id):
"""Get flavor access information given the flavor id.
- Available params: see http://developer.openstack.org/
- api-ref-compute-v2.1.html#listFlavorAccess
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#listFlavorAccess
"""
resp, body = self.get('flavors/%s/os-flavor-access' % flavor_id)
body = json.loads(body)
@@ -183,8 +193,9 @@
def add_flavor_access(self, flavor_id, tenant_id):
"""Add flavor access for the specified tenant.
- Available params: see http://developer.openstack.org/
- api-ref-compute-v2.1.html#addFlavorAccess
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#addFlavorAccess
"""
post_body = {
'addTenantAccess': {
@@ -201,8 +212,9 @@
def remove_flavor_access(self, flavor_id, tenant_id):
"""Remove flavor access from the specified tenant.
- Available params: see http://developer.openstack.org/
- api-ref-compute-v2.1.html#removeFlavorAccess
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#removeFlavorAccess
"""
post_body = {
'removeTenantAccess': {
diff --git a/tempest/lib/services/compute/floating_ips_client.py b/tempest/lib/services/compute/floating_ips_client.py
index 6922c48..744e14c 100644
--- a/tempest/lib/services/compute/floating_ips_client.py
+++ b/tempest/lib/services/compute/floating_ips_client.py
@@ -27,8 +27,9 @@
def list_floating_ips(self, **params):
"""Returns a list of all floating IPs filtered by any parameters.
- Available params: see http://developer.openstack.org/
- api-ref-compute-v2.1.html#listfloatingipsObject
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#listfloatingipsObject
"""
url = 'os-floating-ips'
if params:
@@ -42,8 +43,9 @@
def show_floating_ip(self, floating_ip_id):
"""Get the details of a floating IP.
- Available params: see http://developer.openstack.org/
- api-ref-compute-v2.1.html#showFloatingIP
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#showFloatingIP
"""
url = "os-floating-ips/%s" % floating_ip_id
resp, body = self.get(url)
@@ -54,8 +56,9 @@
def create_floating_ip(self, **kwargs):
"""Allocate a floating IP to the project.
- Available params: see http://developer.openstack.org/
- api-ref-compute-v2.1.html#createFloatingIP
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#createFloatingIP
"""
url = 'os-floating-ips'
post_body = json.dumps(kwargs)
@@ -67,8 +70,9 @@
def delete_floating_ip(self, floating_ip_id):
"""Deletes the provided floating IP from the project.
- Available params: see http://developer.openstack.org/
- api-ref-compute-v2.1.html#deleteFloatingIP
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#deleteFloatingIP
"""
url = "os-floating-ips/%s" % floating_ip_id
resp, body = self.delete(url)
diff --git a/tempest/lib/services/compute/security_group_default_rules_client.py b/tempest/lib/services/compute/security_group_default_rules_client.py
index d57c8e0..e2d3c98 100644
--- a/tempest/lib/services/compute/security_group_default_rules_client.py
+++ b/tempest/lib/services/compute/security_group_default_rules_client.py
@@ -26,9 +26,9 @@
def create_security_default_group_rule(self, **kwargs):
"""Create security group default rule.
- Available params: see http://developer.openstack.org/
- api-ref-compute-v2.1.html
- #createSecGroupDefaultRule
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#createSecGroupDefaultRule
"""
post_body = json.dumps({'security_group_default_rule': kwargs})
url = 'os-security-group-default-rules'
diff --git a/tempest/lib/services/compute/security_group_rules_client.py b/tempest/lib/services/compute/security_group_rules_client.py
index c969b81..3121e24 100644
--- a/tempest/lib/services/compute/security_group_rules_client.py
+++ b/tempest/lib/services/compute/security_group_rules_client.py
@@ -26,8 +26,9 @@
def create_security_group_rule(self, **kwargs):
"""Create a new security group rule.
- Available params: see http://developer.openstack.org/
- api-ref-compute-v2.1.html#createSecGroupRule
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#createSecGroupRule
"""
post_body = json.dumps({'security_group_rule': kwargs})
url = 'os-security-group-rules'
diff --git a/tempest/lib/services/compute/security_groups_client.py b/tempest/lib/services/compute/security_groups_client.py
index 386c214..a247346 100644
--- a/tempest/lib/services/compute/security_groups_client.py
+++ b/tempest/lib/services/compute/security_groups_client.py
@@ -28,8 +28,9 @@
def list_security_groups(self, **params):
"""List all security groups for a user.
- Available params: see http://developer.openstack.org/
- api-ref-compute-v2.1.html#listSecGroups
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#listSecGroups
"""
url = 'os-security-groups'
@@ -44,8 +45,9 @@
def show_security_group(self, security_group_id):
"""Get the details of a Security Group.
- Available params: see http://developer.openstack.org/
- api-ref-compute-v2.1.html#showSecGroup
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#showSecGroup
"""
url = "os-security-groups/%s" % security_group_id
resp, body = self.get(url)
@@ -56,8 +58,9 @@
def create_security_group(self, **kwargs):
"""Create a new security group.
- Available params: see http://developer.openstack.org/
- api-ref-compute-v2.1.html#createSecGroup
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#createSecGroup
"""
post_body = json.dumps({'security_group': kwargs})
resp, body = self.post('os-security-groups', post_body)
@@ -68,8 +71,9 @@
def update_security_group(self, security_group_id, **kwargs):
"""Update a security group.
- Available params: see http://developer.openstack.org/
- api-ref-compute-v2.1.html#updateSecGroup
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#updateSecGroup
"""
post_body = json.dumps({'security_group': kwargs})
resp, body = self.put('os-security-groups/%s' % security_group_id,
@@ -81,8 +85,9 @@
def delete_security_group(self, security_group_id):
"""Delete the provided Security Group.
- Available params: see http://developer.openstack.org/
- api-ref-compute-v2.1.html#deleteSecGroup
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#deleteSecGroup
"""
resp, body = self.delete(
'os-security-groups/%s' % security_group_id)
diff --git a/tempest/lib/services/compute/server_groups_client.py b/tempest/lib/services/compute/server_groups_client.py
index e370457..9ba8d38 100644
--- a/tempest/lib/services/compute/server_groups_client.py
+++ b/tempest/lib/services/compute/server_groups_client.py
@@ -26,8 +26,9 @@
def create_server_group(self, **kwargs):
"""Create the server group.
- Available params: see http://developer.openstack.org/
- api-ref-compute-v2.1.html#createServerGroup
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#createServerGroup
"""
post_body = json.dumps({'server_group': kwargs})
resp, body = self.post('os-server-groups', post_body)
diff --git a/tempest/lib/services/compute/servers_client.py b/tempest/lib/services/compute/servers_client.py
index d5902e1..24557d8 100644
--- a/tempest/lib/services/compute/servers_client.py
+++ b/tempest/lib/services/compute/servers_client.py
@@ -92,8 +92,9 @@
def update_server(self, server_id, **kwargs):
"""Update server.
- Available params: see http://developer.openstack.org/
- api-ref-compute-v2.1.html#updateServer
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#updateServer
Most parameters except the following are passed to the API without
any changes.
@@ -112,8 +113,9 @@
def show_server(self, server_id):
"""Get server details.
- Available params: see http://developer.openstack.org/
- api-ref-compute-v2.1.html#showServer
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#showServer
"""
resp, body = self.get("servers/%s" % server_id)
body = json.loads(body)
@@ -124,8 +126,9 @@
def delete_server(self, server_id):
"""Delete server.
- Available params: see http://developer.openstack.org/
- api-ref-compute-v2.1.html#deleteServer
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#deleteServer
"""
resp, body = self.delete("servers/%s" % server_id)
self.validate_response(schema.delete_server, resp, body)
@@ -134,10 +137,10 @@
def list_servers(self, detail=False, **params):
"""List servers.
- Available params: see http://developer.openstack.org/
- api-ref-compute-v2.1.html#listServers
- and http://developer.openstack.org/
- api-ref-compute-v2.1.html#listDetailServers
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#listServers
+ http://developer.openstack.org/api-ref-compute-v2.1.html#listDetailServers
"""
url = 'servers'
@@ -158,8 +161,9 @@
def list_addresses(self, server_id):
"""Lists all addresses for a server.
- Available params: see http://developer.openstack.org/
- api-ref-compute-v2.1.html#list-ips
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#list-ips
"""
resp, body = self.get("servers/%s/ips" % server_id)
body = json.loads(body)
@@ -188,16 +192,18 @@
def create_backup(self, server_id, **kwargs):
"""Backup a server instance.
- Available params: see http://developer.openstack.org/
- api-ref-compute-v2.1.html#createBackup
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#createBackup
"""
return self.action(server_id, "createBackup", **kwargs)
def change_password(self, server_id, **kwargs):
"""Change the root password for the server.
- Available params: see http://developer.openstack.org/
- api-ref-compute-v2.1.html#changePassword
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#changePassword
"""
return self.action(server_id, 'changePassword', **kwargs)
@@ -223,16 +229,18 @@
def reboot_server(self, server_id, **kwargs):
"""Reboot a server.
- Available params: http://developer.openstack.org/
- api-ref-compute-v2.1.html#reboot
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#reboot
"""
return self.action(server_id, 'reboot', **kwargs)
def rebuild_server(self, server_id, image_ref, **kwargs):
"""Rebuild a server with a new image.
- Available params: http://developer.openstack.org/
- api-ref-compute-v2.1.html#rebuild
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#rebuild
Most parameters except the following are passed to the API without
any changes.
@@ -252,8 +260,9 @@
def resize_server(self, server_id, flavor_ref, **kwargs):
"""Change the flavor of a server.
- Available params: http://developer.openstack.org/
- api-ref-compute-v2.1.html#resize
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#resize
Most parameters except the following are passed to the API without
any changes.
@@ -267,8 +276,9 @@
def confirm_resize_server(self, server_id, **kwargs):
"""Confirm the flavor change for a server.
- Available params: see http://developer.openstack.org/
- api-ref-compute-v2.1.html#confirmResize
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#confirmResize
"""
return self.action(server_id, 'confirmResize',
schema.server_actions_confirm_resize,
@@ -277,16 +287,18 @@
def revert_resize_server(self, server_id, **kwargs):
"""Revert a server back to its original flavor.
- Available params: see http://developer.openstack.org/
- api-ref-compute-v2.1.html#revertResize
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#revertResize
"""
return self.action(server_id, 'revertResize', **kwargs)
def list_server_metadata(self, server_id):
"""Lists all metadata for a server.
- Available params: see http://developer.openstack.org/
- api-ref-compute-v2.1.html#listServerMetadata
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#listServerMetadata
"""
resp, body = self.get("servers/%s/metadata" % server_id)
body = json.loads(body)
@@ -296,8 +308,9 @@
def set_server_metadata(self, server_id, meta, no_metadata_field=False):
"""Sets one or more metadata items for a server.
- Available params: see http://developer.openstack.org/
- api-ref-compute-v2.1.html#createServerMetadata
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#createServerMetadata
"""
if no_metadata_field:
post_body = ""
@@ -312,8 +325,9 @@
def update_server_metadata(self, server_id, meta):
"""Updates one or more metadata items for a server.
- Available params: see http://developer.openstack.org/
- api-ref-compute-v2.1.html#updateServerMetadata
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#updateServerMetadata
"""
post_body = json.dumps({'metadata': meta})
resp, body = self.post('servers/%s/metadata' % server_id,
@@ -326,8 +340,9 @@
def show_server_metadata_item(self, server_id, key):
"""Shows details for a metadata item, by key, for a server.
- Available params: see http://developer.openstack.org/
- api-ref-compute-v2.1.html#showServerMetadataItem
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#showServerMetadataItem
"""
resp, body = self.get("servers/%s/metadata/%s" % (server_id, key))
body = json.loads(body)
@@ -338,8 +353,9 @@
def set_server_metadata_item(self, server_id, key, meta):
"""Sets a metadata item, by key, for a server.
- Available params: see http://developer.openstack.org/
- api-ref-compute-v2.1.html#setServerMetadataItem
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#setServerMetadataItem
"""
post_body = json.dumps({'meta': meta})
resp, body = self.put('servers/%s/metadata/%s' % (server_id, key),
@@ -352,8 +368,9 @@
def delete_server_metadata_item(self, server_id, key):
"""Deletes a metadata item, by key, from a server.
- Available params: see http://developer.openstack.org/
- api-ref-compute-v2.1.html#deleteServerMetadataItem
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#deleteServerMetadataItem
"""
resp, body = self.delete("servers/%s/metadata/%s" %
(server_id, key))
@@ -364,24 +381,27 @@
def stop_server(self, server_id, **kwargs):
"""Stops a running server and changes its status to SHUTOFF.
- Available params: see http://developer.openstack.org/
- api-ref-compute-v2.1.html#stop
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#stop
"""
return self.action(server_id, 'os-stop', **kwargs)
def start_server(self, server_id, **kwargs):
"""Starts a stopped server and changes its status to ACTIVE.
- Available params: see http://developer.openstack.org/
- api-ref-compute-v2.1.html#start
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#start
"""
return self.action(server_id, 'os-start', **kwargs)
def attach_volume(self, server_id, **kwargs):
"""Attaches a volume to a server instance.
- Available params: see http://developer.openstack.org/
- api-ref-compute-v2.1.html#attachVolume
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#attachVolume
"""
post_body = json.dumps({'volumeAttachment': kwargs})
resp, body = self.post('servers/%s/os-volume_attachments' % server_id,
@@ -402,8 +422,9 @@
def detach_volume(self, server_id, volume_id): # noqa
"""Detaches a volume from a server instance.
- Available params: see http://developer.openstack.org/
- api-ref-compute-v2.1.html#deleteVolumeAttachment
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#deleteVolumeAttachment
"""
resp, body = self.delete('servers/%s/os-volume_attachments/%s' %
(server_id, volume_id))
@@ -413,9 +434,9 @@
def show_volume_attachment(self, server_id, volume_id):
"""Return details about the given volume attachment.
- Available params: see http://developer.openstack.org/
- api-ref-compute-v2.1.html#
- getVolumeAttachmentDetails
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#getVolumeAttachmentDetails
"""
resp, body = self.get('servers/%s/os-volume_attachments/%s' % (
server_id, volume_id))
@@ -426,8 +447,9 @@
def list_volume_attachments(self, server_id):
"""Returns the list of volume attachments for a given instance.
- Available params: see http://developer.openstack.org/
- api-ref-compute-v2.1.html#listVolumeAttachments
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#listVolumeAttachments
"""
resp, body = self.get('servers/%s/os-volume_attachments' % (
server_id))
@@ -438,8 +460,9 @@
def add_security_group(self, server_id, **kwargs):
"""Add a security group to the server.
- Available params: http://developer.openstack.org/
- api-ref-compute-v2.1.html#addSecurityGroup
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#addSecurityGroup
"""
# TODO(oomichi): The api-site doesn't contain this API description.
# So the above should be changed to the api-site link after
@@ -450,8 +473,9 @@
def remove_security_group(self, server_id, **kwargs):
"""Remove a security group from the server.
- Available params: http://developer.openstack.org/
- api-ref-compute-v2.1.html#removeSecurityGroup
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#removeSecurityGroup
"""
# TODO(oomichi): The api-site doesn't contain this API description.
# So the above should be changed to the api-site link after
@@ -462,104 +486,117 @@
def live_migrate_server(self, server_id, **kwargs):
"""This should be called with administrator privileges.
- Available params: http://developer.openstack.org/
- api-ref-compute-v2.1.html#migrateLive
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#migrateLive
"""
return self.action(server_id, 'os-migrateLive', **kwargs)
def migrate_server(self, server_id, **kwargs):
"""Migrate a server to a new host.
- Available params: http://developer.openstack.org/
- api-ref-compute-v2.1.html#migrate
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#migrate
"""
return self.action(server_id, 'migrate', **kwargs)
def lock_server(self, server_id, **kwargs):
"""Lock the given server.
- Available params: http://developer.openstack.org/
- api-ref-compute-v2.1.html#lock
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#lock
"""
return self.action(server_id, 'lock', **kwargs)
def unlock_server(self, server_id, **kwargs):
"""UNlock the given server.
- Available params: http://developer.openstack.org/
- api-ref-compute-v2.1.html#unlock
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#unlock
"""
return self.action(server_id, 'unlock', **kwargs)
def suspend_server(self, server_id, **kwargs):
"""Suspend the provided server.
- Available params: http://developer.openstack.org/
- api-ref-compute-v2.1.html#suspend
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#suspend
"""
return self.action(server_id, 'suspend', **kwargs)
def resume_server(self, server_id, **kwargs):
"""Un-suspend the provided server.
- Available params: http://developer.openstack.org/
- api-ref-compute-v2.1.html#resume
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#resume
"""
return self.action(server_id, 'resume', **kwargs)
def pause_server(self, server_id, **kwargs):
"""Pause the provided server.
- Available params: http://developer.openstack.org/
- api-ref-compute-v2.1.html#pause
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#pause
"""
return self.action(server_id, 'pause', **kwargs)
def unpause_server(self, server_id, **kwargs):
"""Un-pause the provided server.
- Available params: http://developer.openstack.org/
- api-ref-compute-v2.1.html#unpause
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#unpause
"""
return self.action(server_id, 'unpause', **kwargs)
def reset_state(self, server_id, **kwargs):
"""Reset the state of a server to active/error.
- Available params: http://developer.openstack.org/
- api-ref-compute-v2.1.html#resetState
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#resetState
"""
return self.action(server_id, 'os-resetState', **kwargs)
def shelve_server(self, server_id, **kwargs):
"""Shelve the provided server.
- Available params: http://developer.openstack.org/
- api-ref-compute-v2.1.html#shelve
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#shelve
"""
return self.action(server_id, 'shelve', **kwargs)
def unshelve_server(self, server_id, **kwargs):
"""Un-shelve the provided server.
- Available params: http://developer.openstack.org/
- api-ref-compute-v2.1.html#unshelve
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#unshelve
"""
return self.action(server_id, 'unshelve', **kwargs)
def shelve_offload_server(self, server_id, **kwargs):
"""Shelve-offload the provided server.
- Available params: http://developer.openstack.org/
- api-ref-compute-v2.1.html#shelveOffload
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#shelveOffload
"""
return self.action(server_id, 'shelveOffload', **kwargs)
def get_console_output(self, server_id, **kwargs):
"""Get console output.
- Available params: http://developer.openstack.org/
- api-ref-compute-v2.1.html#getConsoleOutput
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#getConsoleOutput
"""
return self.action(server_id, 'os-getConsoleOutput',
schema.get_console_output, **kwargs)
@@ -575,16 +612,18 @@
def rescue_server(self, server_id, **kwargs):
"""Rescue the provided server.
- Available params: http://developer.openstack.org/
- api-ref-compute-v2.1.html#rescue
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#rescue
"""
return self.action(server_id, 'rescue', schema.rescue_server, **kwargs)
def unrescue_server(self, server_id):
"""Unrescue the provided server.
- Available params: http://developer.openstack.org/
- api-ref-compute-v2.1.html#unrescue
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#unrescue
"""
return self.action(server_id, 'unrescue')
@@ -612,40 +651,45 @@
def force_delete_server(self, server_id, **kwargs):
"""Force delete a server.
- Available params: http://developer.openstack.org/
- api-ref-compute-v2.1.html#forceDelete
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#forceDelete
"""
return self.action(server_id, 'forceDelete', **kwargs)
def restore_soft_deleted_server(self, server_id, **kwargs):
"""Restore a soft-deleted server.
- Available params: http://developer.openstack.org/
- api-ref-compute-v2.1.html#restore
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#restore
"""
return self.action(server_id, 'restore', **kwargs)
def reset_network(self, server_id, **kwargs):
"""Reset the Network of a server.
- Available params: http://developer.openstack.org/
- api-ref-compute-v2.1.html#resetNetwork
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#resetNetwork
"""
return self.action(server_id, 'resetNetwork', **kwargs)
def inject_network_info(self, server_id, **kwargs):
"""Inject the Network Info into server.
- Available params: http://developer.openstack.org/
- api-ref-compute-v2.1.html#injectNetworkInfo
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#injectNetworkInfo
"""
return self.action(server_id, 'injectNetworkInfo', **kwargs)
def get_vnc_console(self, server_id, **kwargs):
"""Get URL of VNC console.
- Available params: http://developer.openstack.org/
- api-ref-compute-v2.1.html#getVNCConsole
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#getVNCConsole
"""
return self.action(server_id, "os-getVNCConsole",
schema.get_vnc_console, **kwargs)
@@ -653,15 +697,17 @@
def add_fixed_ip(self, server_id, **kwargs):
"""Add a fixed IP to server instance.
- Available params: http://developer.openstack.org/
- api-ref-compute-v2.1.html#addFixedIp
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#addFixedIp
"""
return self.action(server_id, 'addFixedIp', **kwargs)
def remove_fixed_ip(self, server_id, **kwargs):
"""Remove input fixed IP from input server instance.
- Available params: http://developer.openstack.org/
- api-ref-compute-v2.1.html#removeFixedIp
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#removeFixedIp
"""
return self.action(server_id, 'removeFixedIp', **kwargs)
diff --git a/tempest/lib/services/compute/services_client.py b/tempest/lib/services/compute/services_client.py
index b6dbe28..0dbd1b2 100644
--- a/tempest/lib/services/compute/services_client.py
+++ b/tempest/lib/services/compute/services_client.py
@@ -27,8 +27,9 @@
def list_services(self, **params):
"""Lists all running Compute services for a tenant.
- Available params: see http://developer.openstack.org/
- api-ref-compute-v2.1.html#listServices
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#listServices
"""
url = 'os-services'
if params:
@@ -42,8 +43,9 @@
def enable_service(self, **kwargs):
"""Enable service on a host.
- Available params: see http://developer.openstack.org/
- api-ref-compute-v2.1.html#enableScheduling
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#enableScheduling
"""
post_body = json.dumps(kwargs)
resp, body = self.put('os-services/enable', post_body)
@@ -54,8 +56,9 @@
def disable_service(self, **kwargs):
"""Disable service on a host.
- Available params: see http://developer.openstack.org/
- api-ref-compute-v2.1.html#disableScheduling
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#disableScheduling
"""
post_body = json.dumps(kwargs)
resp, body = self.put('os-services/disable', post_body)
diff --git a/tempest/lib/services/compute/snapshots_client.py b/tempest/lib/services/compute/snapshots_client.py
index be41957..fde5288 100644
--- a/tempest/lib/services/compute/snapshots_client.py
+++ b/tempest/lib/services/compute/snapshots_client.py
@@ -27,8 +27,9 @@
def create_snapshot(self, volume_id, **kwargs):
"""Create a snapshot.
- Available params: see http://developer.openstack.org/
- api-ref-compute-v2.1.html#createSnapshot
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#createSnapshot
"""
post_body = {
'volume_id': volume_id
diff --git a/tempest/lib/services/compute/tenant_usages_client.py b/tempest/lib/services/compute/tenant_usages_client.py
index 5a748c7..d0eb1c9 100644
--- a/tempest/lib/services/compute/tenant_usages_client.py
+++ b/tempest/lib/services/compute/tenant_usages_client.py
@@ -24,6 +24,12 @@
class TenantUsagesClient(base_compute_client.BaseComputeClient):
def list_tenant_usages(self, **params):
+ """List Tenant Usage For All Tenants.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/compute/#list-tenant-usage-for-all-tenants
+ """
url = 'os-simple-tenant-usage'
if params:
url += '?%s' % urllib.urlencode(params)
@@ -34,6 +40,12 @@
return rest_client.ResponseBody(resp, body)
def show_tenant_usage(self, tenant_id, **params):
+ """Show Usage Details For Tenant.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/compute/#show-usage-details-for-tenant
+ """
url = 'os-simple-tenant-usage/%s' % tenant_id
if params:
url += '?%s' % urllib.urlencode(params)
diff --git a/tempest/lib/services/compute/volumes_client.py b/tempest/lib/services/compute/volumes_client.py
index 2787779..b75f22e 100644
--- a/tempest/lib/services/compute/volumes_client.py
+++ b/tempest/lib/services/compute/volumes_client.py
@@ -27,8 +27,9 @@
def list_volumes(self, detail=False, **params):
"""List all the volumes created.
- Available params: see http://developer.openstack.org/
- api-ref-compute-v2.1.html#listVolumes
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#listVolumes
"""
url = 'os-volumes'
@@ -45,8 +46,9 @@
def show_volume(self, volume_id):
"""Return the details of a single volume.
- Available params: see http://developer.openstack.org/
- api-ref-compute-v2.1.html#showVolume
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#showVolume
"""
url = "os-volumes/%s" % volume_id
resp, body = self.get(url)
@@ -57,8 +59,9 @@
def create_volume(self, **kwargs):
"""Create a new Volume.
- Available params: see http://developer.openstack.org/
- api-ref-compute-v2.1.html#createVolume
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#createVolume
"""
post_body = json.dumps({'volume': kwargs})
resp, body = self.post('os-volumes', post_body)
@@ -69,8 +72,9 @@
def delete_volume(self, volume_id):
"""Delete the Specified Volume.
- Available params: see http://developer.openstack.org/
- api-ref-compute-v2.1.html#deleteVolume
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html#deleteVolume
"""
resp, body = self.delete("os-volumes/%s" % volume_id)
self.validate_response(schema.delete_volume, resp, body)
diff --git a/tempest/lib/services/identity/v3/role_assignments_client.py b/tempest/lib/services/identity/v3/role_assignments_client.py
new file mode 100644
index 0000000..10de03f
--- /dev/null
+++ b/tempest/lib/services/identity/v3/role_assignments_client.py
@@ -0,0 +1,48 @@
+# Copyright 2016 Red Hat, Inc.
+#
+# 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 six.moves.urllib import parse as urllib
+
+from tempest.lib.common import rest_client
+
+
+class RoleAssignmentsClient(rest_client.RestClient):
+ api_version = "v3"
+
+ def list_role_assignments(self, effective=False, **kwargs):
+ """List role assignments.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/identity/v3/?expanded=list-effective-role-assignments-detail
+
+ :param effective: If True, returns the effective assignments, including
+ any assignments gained by virtue of group membership
+ or inherited roles.
+ """
+ url = 'role_assignments'
+ if kwargs:
+ # NOTE(rodrigods): "effective" is a key-only query parameter and
+ # is treated below.
+ if 'effective' in kwargs:
+ del kwargs['effective']
+ url += '?%s' % urllib.urlencode(kwargs)
+ if effective:
+ url += '&effective'
+
+ resp, body = self.get(url)
+ self.expected_success(200, resp.status)
+ body = json.loads(body)
+ return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/image/v2/__init__.py b/tempest/lib/services/image/v2/__init__.py
index 32bad8b..d359d4b 100644
--- a/tempest/lib/services/image/v2/__init__.py
+++ b/tempest/lib/services/image/v2/__init__.py
@@ -15,10 +15,12 @@
from tempest.lib.services.image.v2.image_members_client import \
ImageMembersClient
from tempest.lib.services.image.v2.images_client import ImagesClient
+from tempest.lib.services.image.v2.namespace_properties_client import \
+ NamespacePropertiesClient
from tempest.lib.services.image.v2.namespaces_client import NamespacesClient
from tempest.lib.services.image.v2.resource_types_client import \
ResourceTypesClient
from tempest.lib.services.image.v2.schemas_client import SchemasClient
-__all__ = ['ImageMembersClient', 'ImagesClient', 'NamespacesClient',
- 'ResourceTypesClient', 'SchemasClient']
+__all__ = ['ImageMembersClient', 'ImagesClient', 'NamespacePropertiesClient',
+ 'NamespacesClient', 'ResourceTypesClient', 'SchemasClient']
diff --git a/tempest/lib/services/image/v2/namespace_properties_client.py b/tempest/lib/services/image/v2/namespace_properties_client.py
new file mode 100644
index 0000000..1236b2b
--- /dev/null
+++ b/tempest/lib/services/image/v2/namespace_properties_client.py
@@ -0,0 +1,91 @@
+# Copyright 2016 EasyStack.
+# 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.common import rest_client
+
+
+class NamespacePropertiesClient(rest_client.RestClient):
+ api_version = "v2"
+
+ def list_namespace_properties(self, namespace):
+ """Lists property definitions in a namespace.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/image/v2/metadefs-index.html#list-properties
+ """
+ url = 'metadefs/namespaces/%s/properties' % namespace
+ resp, body = self.get(url)
+ self.expected_success(200, resp.status)
+ body = json.loads(body)
+ return rest_client.ResponseBody(resp, body)
+
+ def create_namespace_property(self, namespace, **kwargs):
+ """Creates a property definition in a namespace.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/image/v2/metadefs-index.html#create-property
+ """
+ url = 'metadefs/namespaces/%s/properties' % namespace
+ data = json.dumps(kwargs)
+ resp, body = self.post(url, data)
+ self.expected_success(201, resp.status)
+ body = json.loads(body)
+ return rest_client.ResponseBody(resp, body)
+
+ def show_namespace_properties(self, namespace, property_name):
+ """Shows the definition for a property.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/image/v2/metadefs-index.html#show-property-definition
+ """
+ url = 'metadefs/namespaces/%s/properties/%s' % (namespace,
+ property_name)
+ resp, body = self.get(url)
+ self.expected_success(200, resp.status)
+ body = json.loads(body)
+ return rest_client.ResponseBody(resp, body)
+
+ def update_namespace_properties(self, namespace, property_name, **kwargs):
+ """Updates a property definition.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/image/v2/metadefs-index.html#update-property-definition
+ """
+ url = 'metadefs/namespaces/%s/properties/%s' % (namespace,
+ property_name)
+ data = json.dumps(kwargs)
+ resp, body = self.put(url, data)
+ self.expected_success(200, resp.status)
+ body = json.loads(body)
+ return rest_client.ResponseBody(resp, body)
+
+ def delete_namespace_property(self, namespace, property_name):
+ """Removes a property definition from a namespace.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/image/v2/metadefs-index.html#remove-property-definition
+ """
+ url = 'metadefs/namespaces/%s/properties/%s' % (namespace,
+ property_name)
+ resp, _ = self.delete(url)
+ self.expected_success(204, resp.status)
+ return rest_client.ResponseBody(resp)
diff --git a/tempest/lib/services/volume/__init__.py b/tempest/lib/services/volume/__init__.py
index e69de29..6855d8e 100644
--- a/tempest/lib/services/volume/__init__.py
+++ b/tempest/lib/services/volume/__init__.py
@@ -0,0 +1,19 @@
+# Copyright (c) 2016 Hewlett-Packard Enterprise Development Company, L.P.
+#
+# 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 import v1
+from tempest.lib.services.volume import v2
+from tempest.lib.services.volume import v3
+
+__all__ = ['v1', 'v2', 'v3']
diff --git a/tempest/lib/services/volume/v1/__init__.py b/tempest/lib/services/volume/v1/__init__.py
index e69de29..7b5991f 100644
--- a/tempest/lib/services/volume/v1/__init__.py
+++ b/tempest/lib/services/volume/v1/__init__.py
@@ -0,0 +1,33 @@
+# Copyright (c) 2016 Hewlett-Packard Enterprise Development Company, L.P.
+#
+# 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.v1.availability_zone_client \
+ import AvailabilityZoneClient
+from tempest.lib.services.volume.v1.backups_client import BackupsClient
+from tempest.lib.services.volume.v1.encryption_types_client import \
+ EncryptionTypesClient
+from tempest.lib.services.volume.v1.extensions_client import ExtensionsClient
+from tempest.lib.services.volume.v1.hosts_client import HostsClient
+from tempest.lib.services.volume.v1.limits_client import LimitsClient
+from tempest.lib.services.volume.v1.qos_client import QosSpecsClient
+from tempest.lib.services.volume.v1.quotas_client import QuotasClient
+from tempest.lib.services.volume.v1.services_client import ServicesClient
+from tempest.lib.services.volume.v1.snapshots_client import SnapshotsClient
+from tempest.lib.services.volume.v1.types_client import TypesClient
+from tempest.lib.services.volume.v1.volumes_client import VolumesClient
+
+__all__ = ['AvailabilityZoneClient', 'BackupsClient', 'EncryptionTypesClient',
+ 'ExtensionsClient', 'HostsClient', 'QosSpecsClient', 'QuotasClient',
+ 'ServicesClient', 'SnapshotsClient', 'TypesClient', 'VolumesClient',
+ 'LimitsClient']
diff --git a/tempest/lib/services/volume/v1/backups_client.py b/tempest/lib/services/volume/v1/backups_client.py
index 2728c67..8677913 100644
--- a/tempest/lib/services/volume/v1/backups_client.py
+++ b/tempest/lib/services/volume/v1/backups_client.py
@@ -26,8 +26,9 @@
def create_backup(self, **kwargs):
"""Creates a backup of volume.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#createBackup
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v2/#create-backup
"""
post_body = json.dumps({'backup': kwargs})
resp, body = self.post('backups', post_body)
@@ -38,8 +39,9 @@
def restore_backup(self, backup_id, **kwargs):
"""Restore volume from backup.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#restoreBackup
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v2/#restore-backup
"""
post_body = json.dumps({'restore': kwargs})
resp, body = self.post('backups/%s/restore' % (backup_id), post_body)
diff --git a/tempest/lib/services/volume/v1/limits_client.py b/tempest/lib/services/volume/v1/limits_client.py
new file mode 100644
index 0000000..e14b2dc
--- /dev/null
+++ b/tempest/lib/services/volume/v1/limits_client.py
@@ -0,0 +1,32 @@
+# Copyright 2016 Red Hat, Inc.
+# 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.common import rest_client
+
+
+class LimitsClient(rest_client.RestClient):
+ """Volume V1 limits client."""
+
+ api_version = "v1"
+
+ def show_limits(self):
+ """Returns the details of a volume absolute limits."""
+ url = "limits"
+ resp, body = self.get(url)
+ body = json.loads(body)
+ self.expected_success(200, resp.status)
+ return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v1/qos_client.py b/tempest/lib/services/volume/v1/qos_client.py
index 65ae274..e247b7b 100644
--- a/tempest/lib/services/volume/v1/qos_client.py
+++ b/tempest/lib/services/volume/v1/qos_client.py
@@ -41,8 +41,9 @@
def create_qos(self, **kwargs):
"""Create a QoS Specification.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#createQoSSpec
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v2/#create-qos-specification
"""
post_body = json.dumps({'qos_specs': kwargs})
resp, body = self.post('qos-specs', post_body)
@@ -76,8 +77,9 @@
def set_qos_key(self, qos_id, **kwargs):
"""Set the specified keys/values of QoS specification.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#setQoSKey
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v2/#set-keys-in-qos-specification
"""
put_body = json.dumps({"qos_specs": kwargs})
resp, body = self.put('qos-specs/%s' % qos_id, put_body)
diff --git a/tempest/lib/services/volume/v1/snapshots_client.py b/tempest/lib/services/volume/v1/snapshots_client.py
index 1881078..3433e68 100644
--- a/tempest/lib/services/volume/v1/snapshots_client.py
+++ b/tempest/lib/services/volume/v1/snapshots_client.py
@@ -25,8 +25,9 @@
def list_snapshots(self, detail=False, **params):
"""List all the snapshot.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v1.html#listSnapshots
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v1/#list-snapshots-with-details-v1
"""
url = 'snapshots'
if detail:
@@ -42,8 +43,9 @@
def show_snapshot(self, snapshot_id):
"""Returns the details of a single snapshot.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v1.html#showSnapshot
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v1/#show-snapshot-details-v1
"""
url = "snapshots/%s" % snapshot_id
resp, body = self.get(url)
@@ -54,8 +56,9 @@
def create_snapshot(self, **kwargs):
"""Creates a new snapshot.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v1.html#createSnapshot
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v1/#create-snapshot-v1
"""
post_body = json.dumps({'snapshot': kwargs})
resp, body = self.post('snapshots', post_body)
@@ -66,8 +69,9 @@
def delete_snapshot(self, snapshot_id):
"""Delete Snapshot.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v1.html#deleteSnapshot
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v1/#delete-snapshot-v1
"""
resp, body = self.delete("snapshots/%s" % snapshot_id)
self.expected_success(202, resp.status)
@@ -117,9 +121,9 @@
def update_snapshot(self, snapshot_id, **kwargs):
"""Updates a snapshot.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v1.html#
- updateSnapshotMetadata
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v1/#update-snapshot-v1
"""
put_body = json.dumps({'snapshot': kwargs})
resp, body = self.put('snapshots/%s' % snapshot_id, put_body)
@@ -130,9 +134,9 @@
def show_snapshot_metadata(self, snapshot_id):
"""Get metadata of the snapshot.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v1.html#
- showSnapshotMetadata
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v1/#show-snapshot-metadata-v1
"""
url = "snapshots/%s/metadata" % snapshot_id
resp, body = self.get(url)
@@ -143,9 +147,9 @@
def update_snapshot_metadata(self, snapshot_id, **kwargs):
"""Update metadata for the snapshot.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v1.html#
- updateSnapshotMetadata
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v1/#update-snapshot-metadata-v1
"""
put_body = json.dumps(kwargs)
url = "snapshots/%s/metadata" % snapshot_id
diff --git a/tempest/lib/services/volume/v1/types_client.py b/tempest/lib/services/volume/v1/types_client.py
index dce728d..4ae9935 100644
--- a/tempest/lib/services/volume/v1/types_client.py
+++ b/tempest/lib/services/volume/v1/types_client.py
@@ -38,8 +38,9 @@
def list_volume_types(self, **params):
"""List all the volume_types created.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v1.html#listVolumeTypes
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v1/#list-volume-types-v1
"""
url = 'types'
if params:
@@ -53,8 +54,9 @@
def show_volume_type(self, volume_type_id):
"""Returns the details of a single volume_type.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v1.html#showVolumeType
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v1/#show-volume-type-v1
"""
url = "types/%s" % volume_type_id
resp, body = self.get(url)
@@ -65,8 +67,9 @@
def create_volume_type(self, **kwargs):
"""Create volume type.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v1.html#createVolumeType
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v1/#create-volume-type-v1
"""
post_body = json.dumps({'volume_type': kwargs})
resp, body = self.post('types', post_body)
@@ -77,8 +80,9 @@
def delete_volume_type(self, volume_type_id):
"""Deletes the Specified Volume_type.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v1.html#deleteVolumeType
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v1/#delete-volume-type-v1
"""
resp, body = self.delete("types/%s" % volume_type_id)
self.expected_success(202, resp.status)
@@ -131,8 +135,9 @@
def update_volume_type(self, volume_type_id, **kwargs):
"""Updates volume type name, description, and/or is_public.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#updateVolumeType
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v1/#update-volume-type-v1
"""
put_body = json.dumps({'volume_type': kwargs})
resp, body = self.put('types/%s' % volume_type_id, put_body)
@@ -148,9 +153,9 @@
extra_spec_name: Name of the extra spec to be updated.
extra_spec: A dictionary of with key as extra_spec_name and the
updated value.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#
- updateVolumeTypeExtraSpecs
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v1/#update-extra-specs-for-a-volume-type-v1
"""
url = "types/%s/extra_specs/%s" % (volume_type_id, extra_spec_name)
put_body = json.dumps(extra_specs)
diff --git a/tempest/lib/services/volume/v1/volumes_client.py b/tempest/lib/services/volume/v1/volumes_client.py
index 3df8da4..7a25697 100644
--- a/tempest/lib/services/volume/v1/volumes_client.py
+++ b/tempest/lib/services/volume/v1/volumes_client.py
@@ -22,7 +22,7 @@
class VolumesClient(rest_client.RestClient):
- """Base client class to send CRUD Volume API requests"""
+ """Client class to send CRUD Volume V1 API requests"""
def _prepare_params(self, params):
"""Prepares params for use in get or _ext_get methods.
@@ -61,8 +61,9 @@
def create_volume(self, **kwargs):
"""Creates a new Volume.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#createVolume
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v1/#create-volume
"""
post_body = json.dumps({'volume': kwargs})
resp, body = self.post('volumes', post_body)
@@ -73,8 +74,9 @@
def update_volume(self, volume_id, **kwargs):
"""Updates the Specified Volume.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#updateVolume
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v1/#update-volume
"""
put_body = json.dumps({'volume': kwargs})
resp, body = self.put('volumes/%s' % volume_id, put_body)
@@ -100,8 +102,9 @@
def attach_volume(self, volume_id, **kwargs):
"""Attaches a volume to a given instance on a given mountpoint.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#attachVolume
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v1/#attach-volume
"""
post_body = json.dumps({'os-attach': kwargs})
url = 'volumes/%s/action' % (volume_id)
@@ -156,8 +159,9 @@
def extend_volume(self, volume_id, **kwargs):
"""Extend a volume.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#extendVolume
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v1/#extend-volume
"""
post_body = json.dumps({'os-extend': kwargs})
url = 'volumes/%s/action' % (volume_id)
@@ -168,8 +172,9 @@
def reset_volume_status(self, volume_id, **kwargs):
"""Reset the Specified Volume's Status.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#resetVolume
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v1/#reset-volume-status
"""
post_body = json.dumps({'os-reset_status': kwargs})
resp, body = self.post('volumes/%s/action' % volume_id, post_body)
@@ -179,8 +184,9 @@
def create_volume_transfer(self, **kwargs):
"""Create a volume transfer.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#createVolumeTransfer
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v1/#create-volume-transfer
"""
post_body = json.dumps({'transfer': kwargs})
resp, body = self.post('os-volume-transfer', post_body)
@@ -199,8 +205,9 @@
def list_volume_transfers(self, **params):
"""List all the volume transfers created.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#listVolumeTransfer
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v1/#list-volume-transfers
"""
url = 'os-volume-transfer'
if params:
@@ -219,8 +226,9 @@
def accept_volume_transfer(self, transfer_id, **kwargs):
"""Accept a volume transfer.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#acceptVolumeTransfer
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v1/#accept-volume-transfer
"""
url = 'os-volume-transfer/%s/accept' % transfer_id
post_body = json.dumps({'accept': kwargs})
diff --git a/tempest/lib/services/volume/v2/__init__.py b/tempest/lib/services/volume/v2/__init__.py
index e69de29..eaaafa5 100644
--- a/tempest/lib/services/volume/v2/__init__.py
+++ b/tempest/lib/services/volume/v2/__init__.py
@@ -0,0 +1,33 @@
+# Copyright (c) 2016 Hewlett-Packard Enterprise Development Company, L.P.
+#
+# 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.availability_zone_client \
+ import AvailabilityZoneClient
+from tempest.lib.services.volume.v2.backups_client import BackupsClient
+from tempest.lib.services.volume.v2.encryption_types_client import \
+ EncryptionTypesClient
+from tempest.lib.services.volume.v2.extensions_client import ExtensionsClient
+from tempest.lib.services.volume.v2.hosts_client import HostsClient
+from tempest.lib.services.volume.v2.limits_client import LimitsClient
+from tempest.lib.services.volume.v2.qos_client import QosSpecsClient
+from tempest.lib.services.volume.v2.quotas_client import QuotasClient
+from tempest.lib.services.volume.v2.services_client import ServicesClient
+from tempest.lib.services.volume.v2.snapshots_client import SnapshotsClient
+from tempest.lib.services.volume.v2.types_client import TypesClient
+from tempest.lib.services.volume.v2.volumes_client import VolumesClient
+
+__all__ = ['AvailabilityZoneClient', 'BackupsClient', 'EncryptionTypesClient',
+ 'ExtensionsClient', 'HostsClient', 'QosSpecsClient', 'QuotasClient',
+ 'ServicesClient', 'SnapshotsClient', 'TypesClient', 'VolumesClient',
+ 'LimitsClient']
diff --git a/tempest/lib/services/volume/v2/backups_client.py b/tempest/lib/services/volume/v2/backups_client.py
index 61f865d..ab5eefd 100644
--- a/tempest/lib/services/volume/v2/backups_client.py
+++ b/tempest/lib/services/volume/v2/backups_client.py
@@ -26,8 +26,9 @@
def create_backup(self, **kwargs):
"""Creates a backup of volume.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#createBackup
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-blockstorage-v2.html#createBackup
"""
post_body = json.dumps({'backup': kwargs})
resp, body = self.post('backups', post_body)
@@ -38,8 +39,9 @@
def restore_backup(self, backup_id, **kwargs):
"""Restore volume from backup.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#restoreBackup
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-blockstorage-v2.html#restoreBackup
"""
post_body = json.dumps({'restore': kwargs})
resp, body = self.post('backups/%s/restore' % (backup_id), post_body)
diff --git a/tempest/lib/services/volume/v2/limits_client.py b/tempest/lib/services/volume/v2/limits_client.py
new file mode 100644
index 0000000..ce9fba9
--- /dev/null
+++ b/tempest/lib/services/volume/v2/limits_client.py
@@ -0,0 +1,32 @@
+# Copyright 2016 Red Hat, Inc.
+# 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.common import rest_client
+
+
+class LimitsClient(rest_client.RestClient):
+ """Volume V2 limits client."""
+
+ api_version = "v2"
+
+ def show_limits(self):
+ """Returns the details of a volume absolute limits."""
+ url = "limits"
+ resp, body = self.get(url)
+ body = json.loads(body)
+ self.expected_success(200, resp.status)
+ return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v2/snapshots_client.py b/tempest/lib/services/volume/v2/snapshots_client.py
index c84e557..6f51b51 100644
--- a/tempest/lib/services/volume/v2/snapshots_client.py
+++ b/tempest/lib/services/volume/v2/snapshots_client.py
@@ -25,8 +25,9 @@
def list_snapshots(self, detail=False, **params):
"""List all the snapshot.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#listSnapshots
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v2/#list-snapshots-v2
"""
url = 'snapshots'
if detail:
@@ -42,8 +43,9 @@
def show_snapshot(self, snapshot_id):
"""Returns the details of a single snapshot.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#showSnapshot
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v2/#show-snapshot-v2
"""
url = "snapshots/%s" % snapshot_id
resp, body = self.get(url)
@@ -54,8 +56,9 @@
def create_snapshot(self, **kwargs):
"""Creates a new snapshot.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#createSnapshot
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v2/#create-snapshot
"""
post_body = json.dumps({'snapshot': kwargs})
resp, body = self.post('snapshots', post_body)
@@ -66,8 +69,9 @@
def update_snapshot(self, snapshot_id, **kwargs):
"""Updates a snapshot.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#updateSnapshot
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v2/#update-snapshot-v2
"""
put_body = json.dumps({'snapshot': kwargs})
resp, body = self.put('snapshots/%s' % snapshot_id, put_body)
@@ -78,8 +82,9 @@
def delete_snapshot(self, snapshot_id):
"""Delete Snapshot.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#deleteSnapshot
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v2/#delete-snapshot-v2
"""
resp, body = self.delete("snapshots/%s" % snapshot_id)
self.expected_success(202, resp.status)
@@ -129,9 +134,9 @@
def show_snapshot_metadata(self, snapshot_id):
"""Get metadata of the snapshot.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#
- showSnapshotMetadata
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v2/#show-snapshot-metadata-v2
"""
url = "snapshots/%s/metadata" % snapshot_id
resp, body = self.get(url)
@@ -142,9 +147,9 @@
def update_snapshot_metadata(self, snapshot_id, **kwargs):
"""Update metadata for the snapshot.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#
- updateSnapshotMetadata
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v2/#update-snapshot-metadata-v2
"""
put_body = json.dumps(kwargs)
url = "snapshots/%s/metadata" % snapshot_id
diff --git a/tempest/lib/services/volume/v2/types_client.py b/tempest/lib/services/volume/v2/types_client.py
index d399e99..31597d7 100644
--- a/tempest/lib/services/volume/v2/types_client.py
+++ b/tempest/lib/services/volume/v2/types_client.py
@@ -39,8 +39,9 @@
def list_volume_types(self, **params):
"""List all the volume_types created.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#showVolumeTypes
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v2/#list-volume-types-v2
"""
url = 'types'
if params:
@@ -54,8 +55,9 @@
def show_volume_type(self, volume_type_id):
"""Returns the details of a single volume_type.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#showVolumeType
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v2/#show-volume-type-v2
"""
url = "types/%s" % volume_type_id
resp, body = self.get(url)
@@ -66,8 +68,9 @@
def create_volume_type(self, **kwargs):
"""Create volume type.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#createVolumeType
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v2/#create-volume-type-v2
"""
post_body = json.dumps({'volume_type': kwargs})
resp, body = self.post('types', post_body)
@@ -78,8 +81,9 @@
def delete_volume_type(self, volume_type_id):
"""Deletes the Specified Volume_type.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#deleteVolumeType
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v2/#delete-volume-type-v2
"""
resp, body = self.delete("types/%s" % volume_type_id)
self.expected_success(202, resp.status)
@@ -132,8 +136,9 @@
def update_volume_type(self, volume_type_id, **kwargs):
"""Updates volume type name, description, and/or is_public.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#updateVolumeType
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v2/#update-volume-type-v2
"""
put_body = json.dumps({'volume_type': kwargs})
resp, body = self.put('types/%s' % volume_type_id, put_body)
@@ -149,9 +154,9 @@
extra_spec_name: Name of the extra spec to be updated.
extra_spec: A dictionary of with key as extra_spec_name and the
updated value.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#
- updateVolumeTypeExtraSpecs
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v2/#update-volume-type-extra-specs-v2
"""
url = "types/%s/extra_specs/%s" % (volume_type_id, extra_spec_name)
put_body = json.dumps(extra_specs)
@@ -163,9 +168,9 @@
def add_type_access(self, volume_type_id, **kwargs):
"""Adds volume type access for the given project.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html
- #createVolumeTypeAccessExt
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v2/#add-type-access-v2
"""
post_body = json.dumps({'addProjectAccess': kwargs})
url = 'types/%s/action' % volume_type_id
@@ -176,9 +181,9 @@
def remove_type_access(self, volume_type_id, **kwargs):
"""Removes volume type access for the given project.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html
- #removeVolumeTypeAccessExt
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v2/#remove-type-access-v2
"""
post_body = json.dumps({'removeProjectAccess': kwargs})
url = 'types/%s/action' % volume_type_id
@@ -189,9 +194,9 @@
def list_type_access(self, volume_type_id):
"""Print access information about the given volume type.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#
- listVolumeTypeAccessExt
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v2/#list-type-access-v2
"""
url = 'types/%s/os-volume-type-access' % volume_type_id
resp, body = self.get(url)
diff --git a/tempest/lib/services/volume/v2/volumes_client.py b/tempest/lib/services/volume/v2/volumes_client.py
index b1930e1..ce97adb 100644
--- a/tempest/lib/services/volume/v2/volumes_client.py
+++ b/tempest/lib/services/volume/v2/volumes_client.py
@@ -62,8 +62,9 @@
def create_volume(self, **kwargs):
"""Creates a new Volume.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#createVolume
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v2/#create-volume-v2
"""
post_body = json.dumps({'volume': kwargs})
resp, body = self.post('volumes', post_body)
@@ -74,8 +75,9 @@
def update_volume(self, volume_id, **kwargs):
"""Updates the Specified Volume.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#updateVolume
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v2/#update-volume-v2
"""
put_body = json.dumps({'volume': kwargs})
resp, body = self.put('volumes/%s' % volume_id, put_body)
@@ -101,8 +103,9 @@
def attach_volume(self, volume_id, **kwargs):
"""Attaches a volume to a given instance on a given mountpoint.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#attachVolume
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v2/#attach-volume-v2
"""
post_body = json.dumps({'os-attach': kwargs})
url = 'volumes/%s/action' % (volume_id)
@@ -157,8 +160,9 @@
def extend_volume(self, volume_id, **kwargs):
"""Extend a volume.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#extendVolume
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v2/#extend-volume-v2
"""
post_body = json.dumps({'os-extend': kwargs})
url = 'volumes/%s/action' % (volume_id)
@@ -169,8 +173,9 @@
def reset_volume_status(self, volume_id, **kwargs):
"""Reset the Specified Volume's Status.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#resetVolume
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v2/#reset-volume-status-v2
"""
post_body = json.dumps({'os-reset_status': kwargs})
resp, body = self.post('volumes/%s/action' % volume_id, post_body)
@@ -180,8 +185,9 @@
def create_volume_transfer(self, **kwargs):
"""Create a volume transfer.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#createVolumeTransfer
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v2/#create-volume-transfer-v2
"""
post_body = json.dumps({'transfer': kwargs})
resp, body = self.post('os-volume-transfer', post_body)
@@ -200,8 +206,9 @@
def list_volume_transfers(self, **params):
"""List all the volume transfers created.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#listVolumeTransfer
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v2/#list-volume-transfers-v2
"""
url = 'os-volume-transfer'
if params:
@@ -220,8 +227,9 @@
def accept_volume_transfer(self, transfer_id, **kwargs):
"""Accept a volume transfer.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#acceptVolumeTransfer
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v2/#accept-volume-transfer-v2
"""
url = 'os-volume-transfer/%s/accept' % transfer_id
post_body = json.dumps({'accept': kwargs})
@@ -296,9 +304,9 @@
def update_volume_image_metadata(self, volume_id, **kwargs):
"""Update image metadata for the volume.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html
- #setVolumeimagemetadata
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v2/#update-volume-image-metadata-v2
"""
post_body = json.dumps({'os-set_image_metadata': {'metadata': kwargs}})
url = "volumes/%s/action" % (volume_id)
@@ -329,9 +337,9 @@
def show_backend_capabilities(self, host):
"""Shows capabilities for a storage back end.
- Output params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html
- #showBackendCapabilities
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v2/#show_backend_capabilities-v2
"""
url = 'capabilities/%s' % host
resp, body = self.get(url)
diff --git a/tempest/services/volume/v3/__init__.py b/tempest/lib/services/volume/v3/__init__.py
similarity index 77%
rename from tempest/services/volume/v3/__init__.py
rename to tempest/lib/services/volume/v3/__init__.py
index d50098c..a4600a8 100644
--- a/tempest/services/volume/v3/__init__.py
+++ b/tempest/lib/services/volume/v3/__init__.py
@@ -12,6 +12,7 @@
# License for the specific language governing permissions and limitations under
# the License.
-from tempest.services.volume.v3.json.messages_client import MessagesClient
+from tempest.lib.services.volume.v3.base_client import BaseClient
+from tempest.lib.services.volume.v3.messages_client import MessagesClient
-__all__ = ['MessagesClient']
+__all__ = ['MessagesClient', 'BaseClient']
diff --git a/tempest/services/volume/base/base_v3_client.py b/tempest/lib/services/volume/v3/base_client.py
similarity index 90%
rename from tempest/services/volume/base/base_v3_client.py
rename to tempest/lib/services/volume/v3/base_client.py
index ad6f760..958212a 100644
--- a/tempest/services/volume/base/base_v3_client.py
+++ b/tempest/lib/services/volume/v3/base_client.py
@@ -19,13 +19,13 @@
VOLUME_MICROVERSION = None
-class BaseV3Client(rest_client.RestClient):
+class BaseClient(rest_client.RestClient):
"""Base class to handle Cinder v3 client microversion support."""
api_version = 'v3'
api_microversion_header_name = 'Openstack-Api-Version'
def get_headers(self, accept_type=None, send_type=None):
- headers = super(BaseV3Client, self).get_headers(
+ headers = super(BaseClient, self).get_headers(
accept_type=accept_type, send_type=send_type)
if VOLUME_MICROVERSION:
headers[self.api_microversion_header_name] = ('volume %s' %
@@ -35,7 +35,7 @@
def request(self, method, url, extra_headers=False, headers=None,
body=None, chunked=False):
- resp, resp_body = super(BaseV3Client, self).request(
+ resp, resp_body = super(BaseClient, self).request(
method, url, extra_headers, headers, body, chunked)
if (VOLUME_MICROVERSION and
VOLUME_MICROVERSION != api_version_utils.LATEST_MICROVERSION):
diff --git a/tempest/services/volume/v3/json/messages_client.py b/tempest/lib/services/volume/v3/messages_client.py
similarity index 94%
rename from tempest/services/volume/v3/json/messages_client.py
rename to tempest/lib/services/volume/v3/messages_client.py
index 6be6d59..8a01864 100644
--- a/tempest/services/volume/v3/json/messages_client.py
+++ b/tempest/lib/services/volume/v3/messages_client.py
@@ -17,10 +17,10 @@
from tempest.lib.common import rest_client
from tempest.lib import exceptions as lib_exc
-from tempest.services.volume.base import base_v3_client
+from tempest.lib.services.volume.v3 import base_client
-class MessagesClient(base_v3_client.BaseV3Client):
+class MessagesClient(base_client.BaseClient):
"""Client class to send user messages API requests."""
def show_message(self, message_id):
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index 3a808ce..73544d9 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -26,6 +26,7 @@
from tempest.common import image as common_image
from tempest.common.utils import data_utils
from tempest.common.utils.linux import remote_client
+from tempest.common.utils import net_utils
from tempest.common import waiters
from tempest import config
from tempest import exceptions
@@ -140,6 +141,9 @@
if clients is None:
clients = self.manager
+ if name is None:
+ name = data_utils.rand_name(self.__class__.__name__ + "-server")
+
vnic_type = CONF.network.port_vnic_type
# If vnic_type is configured create port for
@@ -215,8 +219,12 @@
imageRef=None, volume_type=None):
if size is None:
size = CONF.volume.volume_size
+ if imageRef:
+ image = self.compute_images_client.show_image(imageRef)['image']
+ min_disk = image.get('minDisk')
+ size = max(size, min_disk)
if name is None:
- name = data_utils.rand_name(self.__class__.__name__)
+ name = data_utils.rand_name(self.__class__.__name__ + "-volume")
kwargs = {'display_name': name,
'snapshot_id': snapshot_id,
'imageRef': imageRef,
@@ -401,10 +409,14 @@
servers = self.servers_client.list_servers()
servers = servers['servers']
for server in servers:
- console_output = self.servers_client.get_console_output(
- server['id'])['output']
- LOG.debug('Console output for %s\nbody=\n%s',
- server['id'], console_output)
+ try:
+ console_output = self.servers_client.get_console_output(
+ server['id'])['output']
+ LOG.debug('Console output for %s\nbody=\n%s',
+ server['id'], console_output)
+ except lib_exc.NotFound:
+ LOG.debug("Server %s disappeared(deleted) while looking "
+ "for the console log", server['id'])
def _log_net_info(self, exc):
# network debug is called as part of ssh init
@@ -417,7 +429,7 @@
# Compute client
_images_client = self.compute_images_client
if name is None:
- name = data_utils.rand_name('scenario-snapshot')
+ name = data_utils.rand_name(self.__class__.__name__ + 'snapshot')
LOG.debug("Creating a snapshot image for server: %s", server['name'])
image = _images_client.create_image(server['id'], name=name)
image_id = image.response['location'].split('images/')[1]
@@ -495,9 +507,18 @@
server_id, 'ACTIVE')
def ping_ip_address(self, ip_address, should_succeed=True,
- ping_timeout=None):
+ ping_timeout=None, mtu=None):
timeout = ping_timeout or CONF.validation.ping_timeout
- cmd = ['ping', '-c1', '-w1', ip_address]
+ cmd = ['ping', '-c1', '-w1']
+
+ if mtu:
+ cmd += [
+ # don't fragment
+ '-M', 'do',
+ # ping receives just the size of ICMP payload
+ '-s', str(net_utils.get_ping_payload_size(mtu, 4))
+ ]
+ cmd.append(ip_address)
def ping():
proc = subprocess.Popen(cmd,
@@ -525,7 +546,8 @@
def check_vm_connectivity(self, ip_address,
username=None,
private_key=None,
- should_connect=True):
+ should_connect=True,
+ mtu=None):
"""Check server connectivity
:param ip_address: server to test against
@@ -534,6 +556,7 @@
:param should_connect: True/False indicates positive/negative test
positive - attempt ping and ssh
negative - attempt ping and fail if succeed
+ :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
@@ -543,7 +566,8 @@
else:
msg = "ip address %s is reachable" % ip_address
self.assertTrue(self.ping_ip_address(ip_address,
- should_succeed=should_connect),
+ should_succeed=should_connect,
+ mtu=mtu),
msg=msg)
if should_connect:
# no need to check ssh for negative connectivity
@@ -551,7 +575,7 @@
def check_public_network_connectivity(self, ip_address, username,
private_key, should_connect=True,
- msg=None, servers=None):
+ 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' %
@@ -560,7 +584,8 @@
self.check_vm_connectivity(ip_address,
username,
private_key,
- should_connect=should_connect)
+ should_connect=should_connect,
+ mtu=mtu)
except Exception:
ex_msg = 'Public network connectivity check failed'
if msg:
@@ -662,11 +687,6 @@
if not CONF.service_available.neutron:
raise cls.skipException('Neutron not available')
- @classmethod
- def resource_setup(cls):
- super(NetworkScenarioTest, cls).resource_setup()
- cls.tenant_id = cls.manager.identity_client.tenant_id
-
def _create_network(self, networks_client=None,
routers_client=None, tenant_id=None,
namestart='network-smoke-',
@@ -789,7 +809,7 @@
def _get_server_port_id_and_ip4(self, server, ip_addr=None):
ports = self._list_ports(device_id=server['id'], fixed_ip=ip_addr)
- # A port can have more then one IP address in some cases.
+ # 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
p_status = ['ACTIVE']
@@ -1005,7 +1025,7 @@
if sg['tenant_id'] == tenant_id and sg['name'] == 'default'
]
msg = "No default security group for tenant %s." % (tenant_id)
- self.assertTrue(len(sgs) > 0, msg)
+ self.assertGreater(len(sgs), 0, msg)
return sgs[0]
def _create_security_group_rule(self, secgroup=None,
@@ -1273,7 +1293,7 @@
check_state, timeout, interval):
msg = ("Timed out waiting for node %s to reach %s state(s) %s" %
(node_id, state_attr, target_states))
- raise exceptions.TimeoutException(msg)
+ raise lib_exc.TimeoutException(msg)
def wait_provisioning_state(self, node_id, state, timeout):
self._node_state_timeout(
@@ -1297,7 +1317,7 @@
_get_node, CONF.baremetal.association_timeout, 1):
msg = ('Timed out waiting to get Ironic node by instance id %s'
% instance_id)
- raise exceptions.TimeoutException(msg)
+ raise lib_exc.TimeoutException(msg)
def get_node(self, node_id=None, instance_id=None):
if node_id:
diff --git a/tempest/scenario/test_aggregates_basic_ops.py b/tempest/scenario/test_aggregates_basic_ops.py
index 43adfb1..8de3561 100644
--- a/tempest/scenario/test_aggregates_basic_ops.py
+++ b/tempest/scenario/test_aggregates_basic_ops.py
@@ -52,7 +52,7 @@
def _get_host_name(self):
hosts = self.hosts_client.list_hosts()['hosts']
- self.assertTrue(len(hosts) >= 1)
+ self.assertGreaterEqual(len(hosts), 1)
computes = [x for x in hosts if x['service'] == 'compute']
return computes[0]['host_name']
diff --git a/tempest/scenario/test_minimum_basic.py b/tempest/scenario/test_minimum_basic.py
index 3ac6759..f2f17d2 100644
--- a/tempest/scenario/test_minimum_basic.py
+++ b/tempest/scenario/test_minimum_basic.py
@@ -16,8 +16,8 @@
from tempest.common import custom_matchers
from tempest.common import waiters
from tempest import config
-from tempest import exceptions
from tempest.lib.common.utils import test_utils
+from tempest.lib import exceptions
from tempest.scenario import manager
from tempest import test
diff --git a/tempest/scenario/test_network_advanced_server_ops.py b/tempest/scenario/test_network_advanced_server_ops.py
index 3390aff..0605902 100644
--- a/tempest/scenario/test_network_advanced_server_ops.py
+++ b/tempest/scenario/test_network_advanced_server_ops.py
@@ -15,7 +15,6 @@
import testtools
-from tempest.common.utils import data_utils
from tempest.common import waiters
from tempest import config
from tempest.scenario import manager
@@ -36,6 +35,12 @@
"""
@classmethod
+ def setup_clients(cls):
+ super(TestNetworkAdvancedServerOps, cls).setup_clients()
+ cls.admin_servers_client = cls.os_adm.servers_client
+ cls.admin_hosts_client = cls.os_adm.hosts_client
+
+ @classmethod
def skip_checks(cls):
super(TestNetworkAdvancedServerOps, cls).skip_checks()
if not (CONF.network.project_networks_reachable
@@ -56,9 +61,7 @@
security_group = self._create_security_group()
security_groups = [{'name': security_group['name']}]
network, subnet, router = self.create_networks()
- server_name = data_utils.rand_name(self.__class__.__name__ + '-server')
server = self.create_server(
- name=server_name,
networks=[{'uuid': network['id']}],
key_name=keypair['name'],
security_groups=security_groups,
@@ -97,8 +100,11 @@
'ACTIVE')
self._check_network_connectivity(server, keypair, floating_ip)
+ def _get_host_for_server(self, server_id):
+ body = self.admin_servers_client.show_server(server_id)['server']
+ return body['OS-EXT-SRV-ATTR:host']
+
@test.idempotent_id('61f1aa9a-1573-410e-9054-afa557cab021')
- @test.stresstest(class_setup_per='process')
@test.services('compute', 'network')
def test_server_connectivity_stop_start(self):
keypair = self.create_keypair()
@@ -188,3 +194,29 @@
self.servers_client.confirm_resize_server(server['id'])
self._wait_server_status_and_check_network_connectivity(
server, keypair, floating_ip)
+
+ @test.idempotent_id('a4858f6c-401e-4155-9a49-d5cd053d1a2f')
+ @testtools.skipUnless(CONF.compute_feature_enabled.cold_migration,
+ 'Cold migration is not available.')
+ @test.services('compute', 'network')
+ def test_server_connectivity_cold_migration(self):
+ if CONF.compute.min_compute_nodes < 2:
+ msg = "Less than 2 compute nodes, skipping multinode tests."
+ raise self.skipException(msg)
+
+ keypair = self.create_keypair()
+ server = self._setup_server(keypair)
+ floating_ip = self._setup_network(server, keypair)
+ src_host = self._get_host_for_server(server['id'])
+ self._wait_server_status_and_check_network_connectivity(
+ server, keypair, floating_ip)
+
+ self.admin_servers_client.migrate_server(server['id'])
+ waiters.wait_for_server_status(self.servers_client, server['id'],
+ 'VERIFY_RESIZE')
+ self.servers_client.confirm_resize_server(server['id'])
+ self._wait_server_status_and_check_network_connectivity(
+ server, keypair, floating_ip)
+ dst_host = self._get_host_for_server(server['id'])
+
+ self.assertNotEqual(src_host, dst_host)
diff --git a/tempest/scenario/test_network_basic_ops.py b/tempest/scenario/test_network_basic_ops.py
index a295b6a..4a076e4 100644
--- a/tempest/scenario/test_network_basic_ops.py
+++ b/tempest/scenario/test_network_basic_ops.py
@@ -19,12 +19,11 @@
from oslo_log import log as logging
import testtools
-from tempest.common.utils import data_utils
from tempest.common import waiters
from tempest import config
-from tempest import exceptions
from tempest.lib.common.utils import test_utils
from tempest.lib import decorators
+from tempest.lib import exceptions
from tempest.scenario import manager
from tempest import test
@@ -105,8 +104,7 @@
def _setup_network_and_servers(self, **kwargs):
boot_with_port = kwargs.pop('boot_with_port', False)
- self.security_group = \
- self._create_security_group(tenant_id=self.tenant_id)
+ self.security_group = self._create_security_group()
self.network, self.subnet, self.router = self.create_networks(**kwargs)
self.check_networks()
@@ -117,8 +115,7 @@
self.port_id = self._create_port(self.network['id'])['id']
self.ports.append({'port': self.port_id})
- name = data_utils.rand_name('server-smoke')
- server = self._create_server(name, self.network, self.port_id)
+ server = self._create_server(self.network, self.port_id)
self._check_tenant_network_connectivity()
floating_ip = self.create_floating_ip(server)
@@ -152,7 +149,7 @@
self.assertIn(self.router['id'],
seen_router_ids)
- def _create_server(self, name, network, port_id=None):
+ def _create_server(self, network, port_id=None):
keypair = self.create_keypair()
self.keypairs[keypair['name']] = keypair
security_groups = [{'name': self.security_group['name']}]
@@ -161,7 +158,6 @@
network['port'] = port_id
server = self.create_server(
- name=name,
networks=[network],
key_name=keypair['name'],
security_groups=security_groups,
@@ -183,7 +179,7 @@
def check_public_network_connectivity(
self, should_connect=True, msg=None,
- should_check_floating_ip_status=True):
+ should_check_floating_ip_status=True, mtu=None):
"""Verifies connectivty to a VM via public network and floating IP
and verifies floating IP has resource status is correct.
@@ -195,6 +191,7 @@
to indicate the context of the failure
:param should_check_floating_ip_status: bool. should status of
floating_ip be checked or not
+ :param mtu: int. MTU network to use for connectivity validation
"""
ssh_login = CONF.validation.image_ssh_user
floating_ip, server = self.floating_ip_tuple
@@ -210,7 +207,7 @@
# 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)
+ self.servers, mtu=mtu)
def _disassociate_floating_ips(self):
floating_ip, server = self.floating_ip_tuple
@@ -220,15 +217,14 @@
def _reassociate_floating_ips(self):
floating_ip, server = self.floating_ip_tuple
- name = data_utils.rand_name('new_server-smoke')
# create a new server for the floating ip
- server = self._create_server(name, self.network)
+ server = self._create_server(self.network)
self._associate_floating_ip(floating_ip, server)
self.floating_ip_tuple = Floating_IP_tuple(
floating_ip, server)
def _create_new_network(self, create_gateway=False):
- self.new_net = self._create_network(tenant_id=self.tenant_id)
+ self.new_net = self._create_network()
if create_gateway:
self.new_subnet = self._create_subnet(
network=self.new_net)
@@ -410,6 +406,16 @@
msg="after re-associate "
"floating ip")
+ @test.idempotent_id('b158ea55-472e-4086-8fa9-c64ac0c6c1d0')
+ @testtools.skipUnless(test.is_extension_enabled('net-mtu', 'network'),
+ 'No way to calculate MTU for networks')
+ @test.services('compute', 'network')
+ def test_mtu_sized_frames(self):
+ """Validate that network MTU sized frames fit through."""
+ self._setup_network_and_servers()
+ self.check_public_network_connectivity(
+ should_connect=True, mtu=self.network['mtu'])
+
@test.idempotent_id('1546850e-fbaa-42f5-8b5f-03d8a6a95f15')
@testtools.skipIf(CONF.baremetal.driver_enabled,
'Baremetal relies on a shared physical network.')
@@ -449,8 +455,7 @@
self._check_network_internal_connectivity(network=self.network)
self._check_network_external_connectivity()
self._create_new_network(create_gateway=True)
- name = data_utils.rand_name('server-smoke')
- self._create_server(name, self.new_net)
+ self._create_server(self.new_net)
self._check_network_internal_connectivity(network=self.new_net,
should_connect=False)
router_id = self.router['id']
@@ -678,8 +683,7 @@
# Boot another server with the same port to make sure nothing was
# left around that could cause issues.
- name = data_utils.rand_name('reuse-port')
- server = self._create_server(name, self.network, port['id'])
+ server = self._create_server(self.network, port['id'])
port_list = self._list_ports(device_id=server['id'],
network_id=self.network['id'])
self.assertEqual(1, len(port_list),
@@ -744,7 +748,6 @@
self.check_public_network_connectivity(
should_connect=False,
msg='after router unscheduling',
- should_check_floating_ip_status=False
)
# schedule resource to new agent
@@ -803,8 +806,7 @@
ssh_client = self.get_remote_client(fip['floating_ip_address'],
private_key=private_key)
spoof_nic = ssh_client.get_nic_name_by_mac(spoof_port["mac_address"])
- name = data_utils.rand_name('peer')
- peer = self._create_server(name, self.new_net)
+ peer = self._create_server(self.new_net)
peer_address = peer['addresses'][self.new_net['name']][0]['addr']
self._check_remote_connectivity(ssh_client, dest=peer_address,
nic=spoof_nic, should_succeed=True)
diff --git a/tempest/scenario/test_network_v6.py b/tempest/scenario/test_network_v6.py
index dd86d90..6700236 100644
--- a/tempest/scenario/test_network_v6.py
+++ b/tempest/scenario/test_network_v6.py
@@ -18,7 +18,6 @@
from tempest import config
from tempest.lib.common.utils import test_utils
-from tempest.lib import decorators
from tempest.scenario import manager
from tempest import test
@@ -64,7 +63,7 @@
def setUp(self):
super(TestGettingAddress, self).setUp()
self.keypair = self.create_keypair()
- self.sec_grp = self._create_security_group(tenant_id=self.tenant_id)
+ self.sec_grp = self._create_security_group()
def prepare_network(self, address6_mode, n_subnets6=1, dualnet=False):
"""Prepare network
@@ -75,15 +74,15 @@
if dualnet - create IPv6 subnets on a different network
:return: list of created networks
"""
- self.network = self._create_network(tenant_id=self.tenant_id)
+ self.network = self._create_network()
if dualnet:
- self.network_v6 = self._create_network(tenant_id=self.tenant_id)
+ self.network_v6 = self._create_network()
sub4 = self._create_subnet(network=self.network,
namestart='sub4',
ip_version=4)
- router = self._get_router(tenant_id=self.tenant_id)
+ router = self._get_router()
self.routers_client.add_router_interface(router['id'],
subnet_id=sub4['id'])
@@ -255,7 +254,6 @@
self._prepare_and_test(address6_mode='dhcpv6-stateless', n_subnets6=2,
dualnet=True)
- @decorators.skip_because(bug="1540983")
@test.idempotent_id('9178ad42-10e4-47e9-8987-e02b170cc5cd')
@test.services('compute', 'network')
def test_dualnet_multi_prefix_slaac(self):
diff --git a/tempest/scenario/test_object_storage_basic_ops.py b/tempest/scenario/test_object_storage_basic_ops.py
index 9ac1e30..1d2b2b6 100644
--- a/tempest/scenario/test_object_storage_basic_ops.py
+++ b/tempest/scenario/test_object_storage_basic_ops.py
@@ -18,23 +18,21 @@
class TestObjectStorageBasicOps(manager.ObjectStorageScenarioTest):
- """Test swift basic ops.
-
- * get swift stat.
- * create container.
- * upload a file to the created container.
- * list container's objects and assure that the uploaded file is present.
- * download the object and check the content
- * delete object from container.
- * list container's objects and assure that the deleted file is gone.
- * delete a container.
- * list containers and assure that the deleted container is gone.
- * change ACL of the container and make sure it works successfully
- """
-
@test.idempotent_id('b920faf1-7b8a-4657-b9fe-9c4512bfb381')
@test.services('object_storage')
def test_swift_basic_ops(self):
+ """Test swift basic ops.
+
+ * get swift stat.
+ * create container.
+ * upload a file to the created container.
+ * list container's objects and assure that the uploaded file is
+ present.
+ * download the object and check the content
+ * delete object from container.
+ * list container's objects and assure that the deleted file is gone.
+ * delete a container.
+ """
self.get_swift_stat()
container_name = self.create_container()
obj_name, obj_data = self.upload_object_to_container(container_name)
diff --git a/tempest/scenario/test_server_basic_ops.py b/tempest/scenario/test_server_basic_ops.py
index e031ff7..2d2f7df 100644
--- a/tempest/scenario/test_server_basic_ops.py
+++ b/tempest/scenario/test_server_basic_ops.py
@@ -16,9 +16,10 @@
import json
import re
+from tempest.common import waiters
from tempest import config
-from tempest import exceptions
from tempest.lib.common.utils import test_utils
+from tempest.lib import exceptions
from tempest.scenario import manager
from tempest import test
@@ -119,14 +120,13 @@
@test.services('compute', 'network')
def test_server_basic_ops(self):
keypair = self.create_keypair()
- self.security_group = self._create_security_group()
- security_groups = [{'name': self.security_group['name']}]
+ security_group = self._create_security_group()
self.md = {'meta1': 'data1', 'meta2': 'data2', 'metaN': 'dataN'}
self.instance = self.create_server(
image_id=self.image_ref,
flavor=self.flavor_ref,
key_name=keypair['name'],
- security_groups=security_groups,
+ security_groups=[{'name': security_group['name']}],
config_drive=CONF.compute_feature_enabled.config_drive,
metadata=self.md,
wait_until='ACTIVE')
@@ -135,3 +135,5 @@
self.verify_metadata_on_config_drive()
self.verify_networkdata_on_config_drive()
self.servers_client.delete_server(self.instance['id'])
+ waiters.wait_for_server_termination(
+ self.servers_client, self.instance['id'], ignore_error=False)
diff --git a/tempest/scenario/test_server_multinode.py b/tempest/scenario/test_server_multinode.py
index 333079c..170d220 100644
--- a/tempest/scenario/test_server_multinode.py
+++ b/tempest/scenario/test_server_multinode.py
@@ -47,9 +47,17 @@
@test.attr(type='smoke')
@test.services('compute', 'network')
def test_schedule_to_all_nodes(self):
- host_client = self.manager.hosts_client
- hosts = host_client.list_hosts()['hosts']
- hosts = [x for x in hosts if x['service'] == 'compute']
+ available_zone = \
+ self.os_adm.availability_zone_client.list_availability_zones(
+ detail=True)['availabilityZoneInfo']
+ hosts = []
+ for zone in available_zone:
+ if zone['zoneState']['available']:
+ for host in zone['hosts']:
+ if 'nova-compute' in zone['hosts'][host] and \
+ zone['hosts'][host]['nova-compute']['available']:
+ hosts.append({'zone': zone['zoneName'],
+ 'host_name': host})
# ensure we have at least as many compute hosts as we expect
if len(hosts) < CONF.compute.min_compute_nodes:
diff --git a/tempest/scenario/test_stamp_pattern.py b/tempest/scenario/test_stamp_pattern.py
index 5fd934c..0f2c78c 100644
--- a/tempest/scenario/test_stamp_pattern.py
+++ b/tempest/scenario/test_stamp_pattern.py
@@ -21,7 +21,6 @@
from tempest.common.utils import data_utils
from tempest.common import waiters
from tempest import config
-from tempest import exceptions
from tempest.lib.common.utils import test_utils
from tempest.lib import decorators
from tempest.lib import exceptions as lib_exc
@@ -93,7 +92,7 @@
if not test_utils.call_until_true(_func,
CONF.compute.build_timeout,
CONF.compute.build_interval):
- raise exceptions.TimeoutException
+ raise lib_exc.TimeoutException
@decorators.skip_because(bug="1205344")
@test.idempotent_id('10fd234a-515c-41e5-b092-8323060598c5')
diff --git a/tempest/scenario/test_volume_boot_pattern.py b/tempest/scenario/test_volume_boot_pattern.py
index 44ad136..46aebfe 100644
--- a/tempest/scenario/test_volume_boot_pattern.py
+++ b/tempest/scenario/test_volume_boot_pattern.py
@@ -88,11 +88,6 @@
return snap
- def _create_volume_from_snapshot(self, snap_id):
- vol_name = data_utils.rand_name(
- self.__class__.__name__ + '-volume')
- return self.create_volume(name=vol_name, snapshot_id=snap_id)
-
def _delete_server(self, server):
self.servers_client.delete_server(server['id'])
waiters.wait_for_server_termination(self.servers_client, server['id'])
@@ -153,10 +148,13 @@
# create a 3rd instance from snapshot
LOG.info("Creating third instance from snapshot: %s" % snapshot['id'])
- volume = self._create_volume_from_snapshot(snapshot['id'])
+ volume = self.create_volume(snapshot_id=snapshot['id'],
+ size=snapshot['size'])
+ LOG.info("Booting third instance from snapshot")
server_from_snapshot = (
self._boot_instance_from_volume(volume['id'],
keypair, security_group))
+ LOG.info("Booted third instance %s", server_from_snapshot)
# check the content of written file
LOG.info("Logging into third instance to get timestamp: %s" %
@@ -174,8 +172,7 @@
instance = self._boot_instance_from_volume(volume_origin['id'],
delete_on_termination=True)
# create EBS image
- name = data_utils.rand_name(self.__class__.__name__ + '-image')
- image = self.create_server_snapshot(instance, name=name)
+ image = self.create_server_snapshot(instance)
# delete instance
self._delete_server(instance)
diff --git a/tempest/services/identity/v3/__init__.py b/tempest/services/identity/v3/__init__.py
index 9b40b77..6e64a7d 100644
--- a/tempest/services/identity/v3/__init__.py
+++ b/tempest/services/identity/v3/__init__.py
@@ -22,14 +22,14 @@
from tempest.lib.services.identity.v3.policies_client import PoliciesClient
from tempest.lib.services.identity.v3.projects_client import ProjectsClient
from tempest.lib.services.identity.v3.regions_client import RegionsClient
+from tempest.lib.services.identity.v3.role_assignments_client import \
+ RoleAssignmentsClient
from tempest.lib.services.identity.v3.roles_client import RolesClient
from tempest.lib.services.identity.v3.services_client import ServicesClient
from tempest.lib.services.identity.v3.token_client import V3TokenClient
from tempest.lib.services.identity.v3.trusts_client import TrustsClient
from tempest.lib.services.identity.v3.users_client import UsersClient
from tempest.services.identity.v3.json.domains_client import DomainsClient
-from tempest.services.identity.v3.json.role_assignments_client import \
- RoleAssignmentsClient
__all__ = ['CredentialsClient', 'EndPointsClient', 'GroupsClient',
'IdentityClient', 'InheritedRolesClient', 'PoliciesClient',
diff --git a/tempest/services/identity/v3/json/role_assignments_client.py b/tempest/services/identity/v3/json/role_assignments_client.py
deleted file mode 100644
index 9fd7736..0000000
--- a/tempest/services/identity/v3/json/role_assignments_client.py
+++ /dev/null
@@ -1,31 +0,0 @@
-# Copyright 2016 Red Hat, Inc.
-#
-# 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.common import rest_client
-
-
-class RoleAssignmentsClient(rest_client.RestClient):
- api_version = "v3"
-
- def list_user_project_effective_assignments(
- self, project_id, user_id):
- """List the effective role assignments for a user in a project."""
- resp, body = self.get(
- "role_assignments?scope.project.id=%s&user.id=%s&effective" %
- (project_id, user_id))
- self.expected_success(200, resp.status)
- body = json.loads(body)
- return rest_client.ResponseBody(resp, body)
diff --git a/tempest/services/object_storage/container_client.py b/tempest/services/object_storage/container_client.py
index 5a26bfc..2509156 100644
--- a/tempest/services/object_storage/container_client.py
+++ b/tempest/services/object_storage/container_client.py
@@ -96,28 +96,6 @@
self.expected_success(204, resp.status)
return resp, body
- def list_all_container_objects(self, container, params=None):
- """Returns complete list of all objects in the container
-
- even if item count is beyond 10,000 item listing limit.
- Does not require any parameters aside from container name.
- """
- # TODO(dwalleck): Rewrite using json format to avoid newlines at end of
- # obj names. Set limit to API limit - 1 (max returned items = 9999)
- limit = 9999
- if params is not None:
- if 'limit' in params:
- limit = params['limit']
-
- if 'marker' in params:
- limit = params['marker']
-
- resp, objlist = self.list_container_contents(
- container,
- params={'limit': limit, 'format': 'json'})
- self.expected_success(200, resp.status)
- return objlist
-
def list_container_contents(self, container, params=None):
"""List the objects in a container, given the container name
diff --git a/tempest/services/orchestration/json/orchestration_client.py b/tempest/services/orchestration/json/orchestration_client.py
index ea5dbe5..9fec548 100644
--- a/tempest/services/orchestration/json/orchestration_client.py
+++ b/tempest/services/orchestration/json/orchestration_client.py
@@ -178,7 +178,7 @@
'within the required time (%s s).' %
(stack_name, status, stack_status,
self.build_timeout))
- raise exceptions.TimeoutException(message)
+ raise lib_exc.TimeoutException(message)
time.sleep(self.build_interval)
def show_resource_metadata(self, stack_identifier, resource_name):
diff --git a/tempest/services/volume/__init__.py b/tempest/services/volume/__init__.py
deleted file mode 100644
index 4d29cdd..0000000
--- a/tempest/services/volume/__init__.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# Copyright (c) 2016 Hewlett-Packard Enterprise Development Company, L.P.
-#
-# 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.services.volume import v1
-from tempest.services.volume import v2
-from tempest.services.volume import v3
-
-__all__ = ['v1', 'v2', 'v3']
diff --git a/tempest/services/volume/base/__init__.py b/tempest/services/volume/base/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/services/volume/base/__init__.py
+++ /dev/null
diff --git a/tempest/services/volume/v1/__init__.py b/tempest/services/volume/v1/__init__.py
deleted file mode 100644
index 7fb3ed3..0000000
--- a/tempest/services/volume/v1/__init__.py
+++ /dev/null
@@ -1,33 +0,0 @@
-# Copyright (c) 2016 Hewlett-Packard Enterprise Development Company, L.P.
-#
-# 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.v1.availability_zone_client import \
- AvailabilityZoneClient
-from tempest.lib.services.volume.v1.backups_client import BackupsClient
-from tempest.lib.services.volume.v1.encryption_types_client import \
- EncryptionTypesClient
-from tempest.lib.services.volume.v1.extensions_client import ExtensionsClient
-from tempest.lib.services.volume.v1.hosts_client import HostsClient
-from tempest.lib.services.volume.v1.qos_client import QosSpecsClient
-from tempest.lib.services.volume.v1.quotas_client import QuotasClient
-from tempest.lib.services.volume.v1.services_client import ServicesClient
-from tempest.lib.services.volume.v1.snapshots_client import SnapshotsClient
-from tempest.lib.services.volume.v1.types_client import TypesClient
-from tempest.lib.services.volume.v1.volumes_client import VolumesClient
-
-__all__ = ['AvailabilityZoneClient', 'EncryptionTypesClient',
- 'ExtensionsClient', 'HostsClient', 'QuotasClient',
- 'QosSpecsClient', 'ServicesClient',
- 'SnapshotsClient', 'TypesClient', 'BackupsClient',
- 'VolumesClient', ]
diff --git a/tempest/services/volume/v1/json/__init__.py b/tempest/services/volume/v1/json/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/services/volume/v1/json/__init__.py
+++ /dev/null
diff --git a/tempest/services/volume/v2/__init__.py b/tempest/services/volume/v2/__init__.py
deleted file mode 100644
index 8edaf2a..0000000
--- a/tempest/services/volume/v2/__init__.py
+++ /dev/null
@@ -1,32 +0,0 @@
-# Copyright (c) 2016 Hewlett-Packard Enterprise Development Company, L.P.
-#
-# 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.availability_zone_client import \
- AvailabilityZoneClient
-from tempest.lib.services.volume.v2.backups_client import BackupsClient
-from tempest.lib.services.volume.v2.encryption_types_client import \
- EncryptionTypesClient
-from tempest.lib.services.volume.v2.extensions_client import ExtensionsClient
-from tempest.lib.services.volume.v2.hosts_client import HostsClient
-from tempest.lib.services.volume.v2.qos_client import QosSpecsClient
-from tempest.lib.services.volume.v2.quotas_client import QuotasClient
-from tempest.lib.services.volume.v2.services_client import ServicesClient
-from tempest.lib.services.volume.v2.snapshots_client import SnapshotsClient
-from tempest.lib.services.volume.v2.types_client import TypesClient
-from tempest.lib.services.volume.v2.volumes_client import VolumesClient
-
-__all__ = ['AvailabilityZoneClient', 'BackupsClient', 'EncryptionTypesClient',
- 'ExtensionsClient', 'HostsClient', 'QosSpecsClient', 'QuotasClient',
- 'ServicesClient', 'SnapshotsClient', 'TypesClient',
- 'VolumesClient', ]
diff --git a/tempest/services/volume/v2/json/__init__.py b/tempest/services/volume/v2/json/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/services/volume/v2/json/__init__.py
+++ /dev/null
diff --git a/tempest/services/volume/v3/json/__init__.py b/tempest/services/volume/v3/json/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/services/volume/v3/json/__init__.py
+++ /dev/null
diff --git a/tempest/stress/README.rst b/tempest/stress/README.rst
deleted file mode 100644
index f22c9ce..0000000
--- a/tempest/stress/README.rst
+++ /dev/null
@@ -1,62 +0,0 @@
-.. _stress_field_guide:
-
-Tempest Field Guide to Stress Tests
-===================================
-
-OpenStack is a distributed, asynchronous system that is prone to race condition
-bugs. These bugs will not be easily found during
-functional testing but will be encountered by users in large deployments in a
-way that is hard to debug. The stress test tries to cause these bugs to happen
-in a more controlled environment.
-
-
-Environment
------------
-This particular framework assumes your working Nova cluster understands Nova
-API 2.0. The stress tests can read the logs from the cluster. To enable this
-you have to provide the hostname to call 'nova-manage' and
-the private key and user name for ssh to the cluster in the
-[stress] section of tempest.conf. You also need to provide the
-location of the log files:
-
- .. code-block:: ini
-
- target_logfiles = "regexp to all log files to be checked for errors"
- target_private_key_path = "private ssh key for controller and log file nodes"
- target_ssh_user = "username for controller and log file nodes"
- target_controller = "hostname or ip of controller node (for nova-manage)
- log_check_interval = "time between checking logs for errors (default 60s)"
-
-To activate logging on your console please make sure that you activate `use_stderr`
-in tempest.conf or use the default `logging.conf.sample` file.
-
-Running default stress test set
--------------------------------
-
-The stress test framework can automatically discover test inside the tempest
-test suite. All test flag with the `@stresstest` decorator will be executed.
-In order to use this discovery you have to install tempest CLI, be in the
-tempest root directory and execute the following:
-
- tempest run-stress -a -d 30
-
-Running the sample test
------------------------
-
-To test installation, do the following:
-
- tempest run-stress -t tempest/stress/etc/server-create-destroy-test.json -d 30
-
-This sample test tries to create a few VMs and kill a few VMs.
-
-
-Additional Tools
-----------------
-
-Sometimes the tests don't finish, or there are failures. In these
-cases, you may want to clean out the nova cluster. We have provided
-some scripts to do this in the ``tools`` subdirectory.
-You can use the following script to destroy any keypairs,
-floating ips, and servers:
-
-tempest/stress/tools/cleanup.py
diff --git a/tempest/stress/__init__.py b/tempest/stress/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/stress/__init__.py
+++ /dev/null
diff --git a/tempest/stress/actions/__init__.py b/tempest/stress/actions/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/stress/actions/__init__.py
+++ /dev/null
diff --git a/tempest/stress/actions/server_create_destroy.py b/tempest/stress/actions/server_create_destroy.py
deleted file mode 100644
index 183bc6c..0000000
--- a/tempest/stress/actions/server_create_destroy.py
+++ /dev/null
@@ -1,42 +0,0 @@
-# Copyright 2013 Quanta Research Cambridge, Inc.
-#
-# 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.common.utils import data_utils
-from tempest.common import waiters
-from tempest import config
-import tempest.stress.stressaction as stressaction
-
-CONF = config.CONF
-
-
-class ServerCreateDestroyTest(stressaction.StressAction):
-
- def setUp(self, **kwargs):
- self.image = CONF.compute.image_ref
- self.flavor = CONF.compute.flavor_ref
-
- def run(self):
- name = data_utils.rand_name(self.__class__.__name__ + "-instance")
- self.logger.info("creating %s" % name)
- server = self.manager.servers_client.create_server(
- name=name, imageRef=self.image, flavorRef=self.flavor)['server']
- server_id = server['id']
- waiters.wait_for_server_status(self.manager.servers_client, server_id,
- 'ACTIVE')
- self.logger.info("created %s" % server_id)
- self.logger.info("deleting %s" % name)
- self.manager.servers_client.delete_server(server_id)
- waiters.wait_for_server_termination(self.manager.servers_client,
- server_id)
- self.logger.info("deleted %s" % server_id)
diff --git a/tempest/stress/actions/ssh_floating.py b/tempest/stress/actions/ssh_floating.py
deleted file mode 100644
index 845b4a7..0000000
--- a/tempest/stress/actions/ssh_floating.py
+++ /dev/null
@@ -1,200 +0,0 @@
-# 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 socket
-import subprocess
-
-from tempest.common.utils import data_utils
-from tempest.common import waiters
-from tempest import config
-from tempest.lib.common.utils import test_utils
-import tempest.stress.stressaction as stressaction
-
-CONF = config.CONF
-
-
-class FloatingStress(stressaction.StressAction):
-
- # from the scenario manager
- def ping_ip_address(self, ip_address):
- cmd = ['ping', '-c1', '-w1', ip_address]
-
- proc = subprocess.Popen(cmd,
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE)
- proc.communicate()
- success = proc.returncode == 0
- return success
-
- def tcp_connect_scan(self, addr, port):
- # like tcp
- s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- try:
- s.connect((addr, port))
- except socket.error as exc:
- self.logger.info("%s(%s): %s", self.server_id, self.floating['ip'],
- str(exc))
- return False
- self.logger.info("%s(%s): Connected :)", self.server_id,
- self.floating['ip'])
- s.close()
- return True
-
- def check_port_ssh(self):
- def func():
- return self.tcp_connect_scan(self.floating['ip'], 22)
- if not test_utils.call_until_true(func, self.check_timeout,
- self.check_interval):
- raise RuntimeError("Cannot connect to the ssh port.")
-
- def check_icmp_echo(self):
- self.logger.info("%s(%s): Pinging..",
- self.server_id, self.floating['ip'])
-
- def func():
- return self.ping_ip_address(self.floating['ip'])
- if not test_utils.call_until_true(func, self.check_timeout,
- self.check_interval):
- raise RuntimeError("%s(%s): Cannot ping the machine.",
- self.server_id, self.floating['ip'])
- self.logger.info("%s(%s): pong :)",
- self.server_id, self.floating['ip'])
-
- def _create_vm(self):
- self.name = name = data_utils.rand_name(
- self.__class__.__name__ + "-instance")
- servers_client = self.manager.servers_client
- self.logger.info("creating %s" % name)
- vm_args = self.vm_extra_args.copy()
- vm_args['security_groups'] = [self.sec_grp]
- server = servers_client.create_server(name=name, imageRef=self.image,
- flavorRef=self.flavor,
- **vm_args)['server']
- self.server_id = server['id']
- if self.wait_after_vm_create:
- waiters.wait_for_server_status(self.manager.servers_client,
- self.server_id, 'ACTIVE')
-
- def _destroy_vm(self):
- self.logger.info("deleting %s" % self.server_id)
- self.manager.servers_client.delete_server(self.server_id)
- waiters.wait_for_server_termination(self.manager.servers_client,
- self.server_id)
- self.logger.info("deleted %s" % self.server_id)
-
- def _create_sec_group(self):
- sec_grp_cli = self.manager.compute_security_groups_client
- s_name = data_utils.rand_name(self.__class__.__name__ + '-sec_grp')
- s_description = data_utils.rand_name('desc')
- self.sec_grp = sec_grp_cli.create_security_group(
- name=s_name, description=s_description)['security_group']
- create_rule = sec_grp_cli.create_security_group_rule
- create_rule(parent_group_id=self.sec_grp['id'], ip_protocol='tcp',
- from_port=22, to_port=22)
- create_rule(parent_group_id=self.sec_grp['id'], ip_protocol='icmp',
- from_port=-1, to_port=-1)
-
- def _destroy_sec_grp(self):
- sec_grp_cli = self.manager.compute_security_groups_client
- sec_grp_cli.delete_security_group(self.sec_grp['id'])
-
- def _create_floating_ip(self):
- floating_cli = self.manager.compute_floating_ips_client
- self.floating = (floating_cli.create_floating_ip(self.floating_pool)
- ['floating_ip'])
-
- def _destroy_floating_ip(self):
- cli = self.manager.compute_floating_ips_client
- cli.delete_floating_ip(self.floating['id'])
- cli.wait_for_resource_deletion(self.floating['id'])
- self.logger.info("Deleted Floating IP %s", str(self.floating['ip']))
-
- def setUp(self, **kwargs):
- self.image = CONF.compute.image_ref
- self.flavor = CONF.compute.flavor_ref
- self.vm_extra_args = kwargs.get('vm_extra_args', {})
- self.wait_after_vm_create = kwargs.get('wait_after_vm_create',
- True)
- self.new_vm = kwargs.get('new_vm', False)
- self.new_sec_grp = kwargs.get('new_sec_group', False)
- self.new_floating = kwargs.get('new_floating', False)
- self.reboot = kwargs.get('reboot', False)
- self.floating_pool = kwargs.get('floating_pool', None)
- self.verify = kwargs.get('verify', ('check_port_ssh',
- 'check_icmp_echo'))
- self.check_timeout = kwargs.get('check_timeout', 120)
- self.check_interval = kwargs.get('check_interval', 1)
- self.wait_for_disassociate = kwargs.get('wait_for_disassociate',
- True)
-
- # allocate floating
- if not self.new_floating:
- self._create_floating_ip()
- # add security group
- if not self.new_sec_grp:
- self._create_sec_group()
- # create vm
- if not self.new_vm:
- self._create_vm()
-
- def wait_disassociate(self):
- cli = self.manager.compute_floating_ips_client
-
- def func():
- floating = (cli.show_floating_ip(self.floating['id'])
- ['floating_ip'])
- return floating['instance_id'] is None
-
- if not test_utils.call_until_true(func, self.check_timeout,
- self.check_interval):
- raise RuntimeError("IP disassociate timeout!")
-
- def run_core(self):
- cli = self.manager.compute_floating_ips_client
- cli.associate_floating_ip_to_server(self.floating['ip'],
- self.server_id)
- for method in self.verify:
- m = getattr(self, method)
- m()
- cli.disassociate_floating_ip_from_server(self.floating['ip'],
- self.server_id)
- if self.wait_for_disassociate:
- self.wait_disassociate()
-
- def run(self):
- if self.new_sec_grp:
- self._create_sec_group()
- if self.new_floating:
- self._create_floating_ip()
- if self.new_vm:
- self._create_vm()
- if self.reboot:
- self.manager.servers_client.reboot(self.server_id, 'HARD')
- waiters.wait_for_server_status(self.manager.servers_client,
- self.server_id, 'ACTIVE')
-
- self.run_core()
-
- if self.new_vm:
- self._destroy_vm()
- if self.new_floating:
- self._destroy_floating_ip()
- if self.new_sec_grp:
- self._destroy_sec_grp()
-
- def tearDown(self):
- if not self.new_vm:
- self._destroy_vm()
- if not self.new_floating:
- self._destroy_floating_ip()
- if not self.new_sec_grp:
- self._destroy_sec_grp()
diff --git a/tempest/stress/actions/unit_test.py b/tempest/stress/actions/unit_test.py
deleted file mode 100644
index e016c61..0000000
--- a/tempest/stress/actions/unit_test.py
+++ /dev/null
@@ -1,92 +0,0 @@
-# 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_log import log as logging
-from oslo_utils import importutils
-
-from tempest import config
-import tempest.stress.stressaction as stressaction
-
-CONF = config.CONF
-
-
-class SetUpClassRunTime(object):
-
- process = 'process'
- action = 'action'
- application = 'application'
-
- allowed = set((process, action, application))
-
- @classmethod
- def validate(cls, name):
- if name not in cls.allowed:
- raise KeyError("\'%s\' not a valid option" % name)
-
-
-class UnitTest(stressaction.StressAction):
- """This is a special action for running existing unittests as stress test.
-
- You need to pass ``test_method`` and ``class_setup_per``
- using ``kwargs`` in the JSON descriptor;
- ``test_method`` should be the fully qualified name of a unittest,
- ``class_setup_per`` should be one from:
- ``application``: once in the stress job lifetime
- ``process``: once in the worker process lifetime
- ``action``: on each action
- Not all combination working in every case.
- """
-
- def setUp(self, **kwargs):
- method = kwargs['test_method'].split('.')
- self.test_method = method.pop()
- self.klass = importutils.import_class('.'.join(method))
- self.logger = logging.getLogger('.'.join(method))
- # valid options are 'process', 'application' , 'action'
- self.class_setup_per = kwargs.get('class_setup_per',
- SetUpClassRunTime.process)
- SetUpClassRunTime.validate(self.class_setup_per)
-
- if self.class_setup_per == SetUpClassRunTime.application:
- self.klass.setUpClass()
- self.setupclass_called = False
-
- @property
- def action(self):
- if self.test_method:
- return self.test_method
- return super(UnitTest, self).action
-
- def run_core(self):
- res = self.klass(self.test_method).run()
- if res.errors:
- raise RuntimeError(res.errors)
-
- def run(self):
- if self.class_setup_per != SetUpClassRunTime.application:
- if (self.class_setup_per == SetUpClassRunTime.action
- or self.setupclass_called is False):
- self.klass.setUpClass()
- self.setupclass_called = True
-
- try:
- self.run_core()
- finally:
- if (CONF.stress.leave_dirty_stack is False
- and self.class_setup_per == SetUpClassRunTime.action):
- self.klass.tearDownClass()
- else:
- self.run_core()
-
- def tearDown(self):
- if self.class_setup_per != SetUpClassRunTime.action:
- self.klass.tearDownClass()
diff --git a/tempest/stress/actions/volume_attach_delete.py b/tempest/stress/actions/volume_attach_delete.py
deleted file mode 100644
index 5fc006e..0000000
--- a/tempest/stress/actions/volume_attach_delete.py
+++ /dev/null
@@ -1,70 +0,0 @@
-# (c) 2013 Deutsche Telekom AG
-# 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.common.utils import data_utils
-from tempest.common import waiters
-from tempest import config
-import tempest.stress.stressaction as stressaction
-
-CONF = config.CONF
-
-
-class VolumeAttachDeleteTest(stressaction.StressAction):
-
- def setUp(self, **kwargs):
- self.image = CONF.compute.image_ref
- self.flavor = CONF.compute.flavor_ref
-
- def run(self):
- # Step 1: create volume
- name = data_utils.rand_name(self.__class__.__name__ + "-volume")
- self.logger.info("creating volume: %s" % name)
- volume = self.manager.volumes_client.create_volume(
- display_name=name, size=CONF.volume.volume_size)['volume']
- self.manager.volumes_client.wait_for_volume_status(volume['id'],
- 'available')
- self.logger.info("created volume: %s" % volume['id'])
-
- # Step 2: create vm instance
- vm_name = data_utils.rand_name(self.__class__.__name__ + "-instance")
- self.logger.info("creating vm: %s" % vm_name)
- server = self.manager.servers_client.create_server(
- name=vm_name, imageRef=self.image, flavorRef=self.flavor)['server']
- server_id = server['id']
- waiters.wait_for_server_status(self.manager.servers_client, server_id,
- 'ACTIVE')
- self.logger.info("created vm %s" % server_id)
-
- # Step 3: attach volume to vm
- self.logger.info("attach volume (%s) to vm %s" %
- (volume['id'], server_id))
- self.manager.servers_client.attach_volume(server_id,
- volumeId=volume['id'],
- device='/dev/vdc')
- self.manager.volumes_client.wait_for_volume_status(volume['id'],
- 'in-use')
- self.logger.info("volume (%s) attached to vm %s" %
- (volume['id'], server_id))
-
- # Step 4: delete vm
- self.logger.info("deleting vm: %s" % vm_name)
- self.manager.servers_client.delete_server(server_id)
- waiters.wait_for_server_termination(self.manager.servers_client,
- server_id)
- self.logger.info("deleted vm: %s" % server_id)
-
- # Step 5: delete volume
- self.logger.info("deleting volume: %s" % volume['id'])
- self.manager.volumes_client.delete_volume(volume['id'])
- self.manager.volumes_client.wait_for_resource_deletion(volume['id'])
- self.logger.info("deleted volume: %s" % volume['id'])
diff --git a/tempest/stress/actions/volume_attach_verify.py b/tempest/stress/actions/volume_attach_verify.py
deleted file mode 100644
index 4fbb851..0000000
--- a/tempest/stress/actions/volume_attach_verify.py
+++ /dev/null
@@ -1,233 +0,0 @@
-# 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 re
-
-from tempest.common.utils import data_utils
-from tempest.common.utils.linux import remote_client
-from tempest.common import waiters
-from tempest import config
-from tempest.lib.common.utils import test_utils
-import tempest.stress.stressaction as stressaction
-
-CONF = config.CONF
-
-
-class VolumeVerifyStress(stressaction.StressAction):
-
- def _create_keypair(self):
- keyname = data_utils.rand_name("key")
- self.key = (self.manager.keypairs_client.create_keypair(name=keyname)
- ['keypair'])
-
- def _delete_keypair(self):
- self.manager.keypairs_client.delete_keypair(self.key['name'])
-
- def _create_vm(self):
- self.name = name = data_utils.rand_name(
- self.__class__.__name__ + "-instance")
- servers_client = self.manager.servers_client
- self.logger.info("creating %s" % name)
- vm_args = self.vm_extra_args.copy()
- vm_args['security_groups'] = [self.sec_grp]
- vm_args['key_name'] = self.key['name']
- server = servers_client.create_server(name=name, imageRef=self.image,
- flavorRef=self.flavor,
- **vm_args)['server']
- self.server_id = server['id']
- waiters.wait_for_server_status(self.manager.servers_client,
- self.server_id, 'ACTIVE')
-
- def _destroy_vm(self):
- self.logger.info("deleting server: %s" % self.server_id)
- self.manager.servers_client.delete_server(self.server_id)
- waiters.wait_for_server_termination(self.manager.servers_client,
- self.server_id)
- self.logger.info("deleted server: %s" % self.server_id)
-
- def _create_sec_group(self):
- sec_grp_cli = self.manager.compute_security_groups_client
- s_name = data_utils.rand_name(self.__class__.__name__ + '-sec_grp')
- s_description = data_utils.rand_name('desc')
- self.sec_grp = sec_grp_cli.create_security_group(
- name=s_name, description=s_description)['security_group']
- create_rule = sec_grp_cli.create_security_group_rule
- create_rule(parent_group_id=self.sec_grp['id'], ip_protocol='tcp',
- from_port=22, to_port=22)
- create_rule(parent_group_id=self.sec_grp['id'], ip_protocol='icmp',
- from_port=-1, to_port=-1)
-
- def _destroy_sec_grp(self):
- sec_grp_cli = self.manager.compute_security_groups_client
- sec_grp_cli.delete_security_group(self.sec_grp['id'])
-
- def _create_floating_ip(self):
- floating_cli = self.manager.compute_floating_ips_client
- self.floating = (floating_cli.create_floating_ip(self.floating_pool)
- ['floating_ip'])
-
- def _destroy_floating_ip(self):
- cli = self.manager.compute_floating_ips_client
- cli.delete_floating_ip(self.floating['id'])
- cli.wait_for_resource_deletion(self.floating['id'])
- self.logger.info("Deleted Floating IP %s", str(self.floating['ip']))
-
- def _create_volume(self):
- name = data_utils.rand_name(self.__class__.__name__ + "-volume")
- self.logger.info("creating volume: %s" % name)
- volumes_client = self.manager.volumes_client
- self.volume = volumes_client.create_volume(
- display_name=name, size=CONF.volume.volume_size)['volume']
- volumes_client.wait_for_volume_status(self.volume['id'],
- 'available')
- self.logger.info("created volume: %s" % self.volume['id'])
-
- def _delete_volume(self):
- self.logger.info("deleting volume: %s" % self.volume['id'])
- volumes_client = self.manager.volumes_client
- volumes_client.delete_volume(self.volume['id'])
- volumes_client.wait_for_resource_deletion(self.volume['id'])
- self.logger.info("deleted volume: %s" % self.volume['id'])
-
- def _wait_disassociate(self):
- cli = self.manager.compute_floating_ips_client
-
- def func():
- floating = (cli.show_floating_ip(self.floating['id'])
- ['floating_ip'])
- return floating['instance_id'] is None
-
- if not test_utils.call_until_true(func, CONF.compute.build_timeout,
- CONF.compute.build_interval):
- raise RuntimeError("IP disassociate timeout!")
-
- def new_server_ops(self):
- self._create_vm()
- cli = self.manager.compute_floating_ips_client
- cli.associate_floating_ip_to_server(self.floating['ip'],
- self.server_id)
- if self.ssh_test_before_attach and self.enable_ssh_verify:
- self.logger.info("Scanning for block devices via ssh on %s"
- % self.server_id)
- self.part_wait(self.detach_match_count)
-
- def setUp(self, **kwargs):
- """Note able configuration combinations:
-
- Closest options to the test_stamp_pattern:
- new_server = True
- new_volume = True
- enable_ssh_verify = True
- ssh_test_before_attach = False
- Just attaching:
- new_server = False
- new_volume = False
- enable_ssh_verify = True
- ssh_test_before_attach = True
- Mostly API load by repeated attachment:
- new_server = False
- new_volume = False
- enable_ssh_verify = False
- ssh_test_before_attach = False
- Minimal Nova load, but cinder load not decreased:
- new_server = False
- new_volume = True
- enable_ssh_verify = True
- ssh_test_before_attach = True
- """
- self.image = CONF.compute.image_ref
- self.flavor = CONF.compute.flavor_ref
- self.vm_extra_args = kwargs.get('vm_extra_args', {})
- self.floating_pool = kwargs.get('floating_pool', None)
- self.new_volume = kwargs.get('new_volume', True)
- self.new_server = kwargs.get('new_server', False)
- self.enable_ssh_verify = kwargs.get('enable_ssh_verify', True)
- self.ssh_test_before_attach = kwargs.get('ssh_test_before_attach',
- False)
- self.part_line_re = re.compile(kwargs.get('part_line_re', '.*vd.*'))
- self.detach_match_count = kwargs.get('detach_match_count', 1)
- self.attach_match_count = kwargs.get('attach_match_count', 2)
- self.part_name = kwargs.get('part_name', '/dev/vdc')
-
- self._create_floating_ip()
- self._create_sec_group()
- self._create_keypair()
- private_key = self.key['private_key']
- username = CONF.validation.image_ssh_user
- self.remote_client = remote_client.RemoteClient(self.floating['ip'],
- username,
- pkey=private_key)
- if not self.new_volume:
- self._create_volume()
- if not self.new_server:
- self.new_server_ops()
-
- # now we just test that the number of partitions has increased or decreased
- def part_wait(self, num_match):
- def _part_state():
- self.partitions = self.remote_client.get_partitions().split('\n')
- matching = 0
- for part_line in self.partitions[1:]:
- if self.part_line_re.match(part_line):
- matching += 1
- return matching == num_match
- if test_utils.call_until_true(_part_state,
- CONF.compute.build_timeout,
- CONF.compute.build_interval):
- return
- else:
- raise RuntimeError("Unexpected partitions: %s",
- str(self.partitions))
-
- def run(self):
- if self.new_server:
- self.new_server_ops()
- if self.new_volume:
- self._create_volume()
- servers_client = self.manager.servers_client
- self.logger.info("attach volume (%s) to vm %s" %
- (self.volume['id'], self.server_id))
- servers_client.attach_volume(self.server_id,
- volumeId=self.volume['id'],
- device=self.part_name)
- self.manager.volumes_client.wait_for_volume_status(self.volume['id'],
- 'in-use')
- if self.enable_ssh_verify:
- self.logger.info("Scanning for new block device on %s"
- % self.server_id)
- self.part_wait(self.attach_match_count)
-
- servers_client.detach_volume(self.server_id,
- self.volume['id'])
- self.manager.volumes_client.wait_for_volume_status(self.volume['id'],
- 'available')
- if self.enable_ssh_verify:
- self.logger.info("Scanning for block device disappearance on %s"
- % self.server_id)
- self.part_wait(self.detach_match_count)
- if self.new_volume:
- self._delete_volume()
- if self.new_server:
- self._destroy_vm()
-
- def tearDown(self):
- cli = self.manager.compute_floating_ips_client
- cli.disassociate_floating_ip_from_server(self.floating['ip'],
- self.server_id)
- self._wait_disassociate()
- if not self.new_server:
- self._destroy_vm()
- self._delete_keypair()
- self._destroy_floating_ip()
- self._destroy_sec_grp()
- if not self.new_volume:
- self._delete_volume()
diff --git a/tempest/stress/actions/volume_create_delete.py b/tempest/stress/actions/volume_create_delete.py
deleted file mode 100644
index 66971ea..0000000
--- a/tempest/stress/actions/volume_create_delete.py
+++ /dev/null
@@ -1,34 +0,0 @@
-# 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.common.utils import data_utils
-from tempest import config
-import tempest.stress.stressaction as stressaction
-
-CONF = config.CONF
-
-
-class VolumeCreateDeleteTest(stressaction.StressAction):
-
- def run(self):
- name = data_utils.rand_name("volume")
- self.logger.info("creating %s" % name)
- volumes_client = self.manager.volumes_client
- volume = volumes_client.create_volume(
- display_name=name, size=CONF.volume.volume_size)['volume']
- vol_id = volume['id']
- volumes_client.wait_for_volume_status(vol_id, 'available')
- self.logger.info("created %s" % volume['id'])
- self.logger.info("deleting %s" % name)
- volumes_client.delete_volume(vol_id)
- volumes_client.wait_for_resource_deletion(vol_id)
- self.logger.info("deleted %s" % vol_id)
diff --git a/tempest/stress/cleanup.py b/tempest/stress/cleanup.py
deleted file mode 100644
index 3b0a937..0000000
--- a/tempest/stress/cleanup.py
+++ /dev/null
@@ -1,118 +0,0 @@
-#!/usr/bin/env python
-
-# Copyright 2013 Quanta Research Cambridge, Inc.
-#
-# 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_log import log as logging
-
-from tempest.common import credentials_factory as credentials
-from tempest.common import waiters
-
-LOG = logging.getLogger(__name__)
-
-
-def cleanup():
- admin_manager = credentials.AdminManager()
-
- body = admin_manager.servers_client.list_servers(all_tenants=True)
- LOG.info("Cleanup::remove %s servers" % len(body['servers']))
- for s in body['servers']:
- try:
- admin_manager.servers_client.delete_server(s['id'])
- except Exception:
- pass
-
- for s in body['servers']:
- try:
- waiters.wait_for_server_termination(admin_manager.servers_client,
- s['id'])
- except Exception:
- pass
-
- keypairs = admin_manager.keypairs_client.list_keypairs()['keypairs']
- LOG.info("Cleanup::remove %s keypairs" % len(keypairs))
- for k in keypairs:
- try:
- admin_manager.keypairs_client.delete_keypair(k['name'])
- except Exception:
- pass
-
- secgrp_client = admin_manager.compute_security_groups_client
- secgrp = (secgrp_client.list_security_groups(all_tenants=True)
- ['security_groups'])
- secgrp_del = [grp for grp in secgrp if grp['name'] != 'default']
- LOG.info("Cleanup::remove %s Security Group" % len(secgrp_del))
- for g in secgrp_del:
- try:
- secgrp_client.delete_security_group(g['id'])
- except Exception:
- pass
-
- admin_floating_ips_client = admin_manager.compute_floating_ips_client
- floating_ips = (admin_floating_ips_client.list_floating_ips()
- ['floating_ips'])
- LOG.info("Cleanup::remove %s floating ips" % len(floating_ips))
- for f in floating_ips:
- try:
- admin_floating_ips_client.delete_floating_ip(f['id'])
- except Exception:
- pass
-
- users = admin_manager.users_client.list_users()['users']
- LOG.info("Cleanup::remove %s users" % len(users))
- for user in users:
- if user['name'].startswith("stress_user"):
- admin_manager.users_client.delete_user(user['id'])
- tenants = admin_manager.tenants_client.list_tenants()['tenants']
- LOG.info("Cleanup::remove %s tenants" % len(tenants))
- for tenant in tenants:
- if tenant['name'].startswith("stress_tenant"):
- admin_manager.tenants_client.delete_tenant(tenant['id'])
-
- # We have to delete snapshots first or
- # volume deletion may block
-
- _, snaps = admin_manager.snapshots_client.list_snapshots(
- all_tenants=True)['snapshots']
- LOG.info("Cleanup::remove %s snapshots" % len(snaps))
- for v in snaps:
- try:
- waiters.wait_for_snapshot_status(
- admin_manager.snapshots_client, v['id'], 'available')
- admin_manager.snapshots_client.delete_snapshot(v['id'])
- except Exception:
- pass
-
- for v in snaps:
- try:
- admin_manager.snapshots_client.wait_for_resource_deletion(v['id'])
- except Exception:
- pass
-
- vols = admin_manager.volumes_client.list_volumes(
- params={"all_tenants": True})
- LOG.info("Cleanup::remove %s volumes" % len(vols))
- for v in vols:
- try:
- waiters.wait_for_volume_status(
- admin_manager.volumes_client, v['id'], 'available')
- admin_manager.volumes_client.delete_volume(v['id'])
- except Exception:
- pass
-
- for v in vols:
- try:
- admin_manager.volumes_client.wait_for_resource_deletion(v['id'])
- except Exception:
- pass
diff --git a/tempest/stress/driver.py b/tempest/stress/driver.py
deleted file mode 100644
index 1e33e88..0000000
--- a/tempest/stress/driver.py
+++ /dev/null
@@ -1,264 +0,0 @@
-# Copyright 2013 Quanta Research Cambridge, Inc.
-#
-# 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 multiprocessing
-import os
-import signal
-import time
-
-from oslo_log import log as logging
-from oslo_utils import importutils
-import six
-
-from tempest import clients
-from tempest.common import cred_client
-from tempest.common import credentials_factory as credentials
-from tempest.common.utils import data_utils
-from tempest import config
-from tempest import exceptions
-from tempest.lib.common import ssh
-from tempest.stress import cleanup
-
-CONF = config.CONF
-
-LOG = logging.getLogger(__name__)
-processes = []
-
-
-def do_ssh(command, host, ssh_user, ssh_key=None):
- ssh_client = ssh.Client(host, ssh_user, key_filename=ssh_key)
- try:
- return ssh_client.exec_command(command)
- except exceptions.SSHExecCommandFailed:
- LOG.error('do_ssh raise exception. command:%s, host:%s.'
- % (command, host))
- return None
-
-
-def _get_compute_nodes(controller, ssh_user, ssh_key=None):
- """Returns a list of active compute nodes.
-
- List is generated by running nova-manage on the controller.
- """
- nodes = []
- cmd = "nova-manage service list | grep ^nova-compute"
- output = do_ssh(cmd, controller, ssh_user, ssh_key)
- if not output:
- return nodes
- # For example: nova-compute xg11eth0 nova enabled :-) 2011-10-31 18:57:46
- # This is fragile but there is, at present, no other way to get this info.
- for line in output.split('\n'):
- words = line.split()
- if len(words) > 0 and words[4] == ":-)":
- nodes.append(words[1])
- return nodes
-
-
-def _has_error_in_logs(logfiles, nodes, ssh_user, ssh_key=None,
- stop_on_error=False):
- """Detect errors in nova log files on the controller and compute nodes."""
- grep = 'egrep "ERROR|TRACE" %s' % logfiles
- ret = False
- for node in nodes:
- errors = do_ssh(grep, node, ssh_user, ssh_key)
- if len(errors) > 0:
- LOG.error('%s: %s' % (node, errors))
- ret = True
- if stop_on_error:
- break
- return ret
-
-
-def sigchld_handler(signalnum, frame):
- """Signal handler (only active if stop_on_error is True)."""
- for process in processes:
- if (not process['process'].is_alive() and
- process['process'].exitcode != 0):
- signal.signal(signalnum, signal.SIG_DFL)
- terminate_all_processes()
- break
-
-
-def terminate_all_processes(check_interval=20):
- """Goes through the process list and terminates all child processes."""
- LOG.info("Stopping all processes.")
- for process in processes:
- if process['process'].is_alive():
- try:
- process['process'].terminate()
- except Exception:
- pass
- time.sleep(check_interval)
- for process in processes:
- if process['process'].is_alive():
- try:
- pid = process['process'].pid
- LOG.warning("Process %d hangs. Send SIGKILL." % pid)
- os.kill(pid, signal.SIGKILL)
- except Exception:
- pass
- process['process'].join()
-
-
-def stress_openstack(tests, duration, max_runs=None, stop_on_error=False):
- """Workload driver. Executes an action function against a nova-cluster."""
- admin_manager = credentials.AdminManager()
-
- ssh_user = CONF.stress.target_ssh_user
- ssh_key = CONF.stress.target_private_key_path
- logfiles = CONF.stress.target_logfiles
- log_check_interval = int(CONF.stress.log_check_interval)
- default_thread_num = int(CONF.stress.default_thread_number_per_action)
- if logfiles:
- controller = CONF.stress.target_controller
- computes = _get_compute_nodes(controller, ssh_user, ssh_key)
- for node in computes:
- do_ssh("rm -f %s" % logfiles, node, ssh_user, ssh_key)
- skip = False
- for test in tests:
- for service in test.get('required_services', []):
- if not CONF.service_available.get(service):
- skip = True
- break
- if skip:
- break
- # TODO(andreaf) This has to be reworked to use the credential
- # provider interface. For now only tests marked as 'use_admin' will
- # work.
- if test.get('use_admin', False):
- manager = admin_manager
- else:
- raise NotImplemented('Non admin tests are not supported')
- for p_number in range(test.get('threads', default_thread_num)):
- if test.get('use_isolated_tenants', False):
- username = data_utils.rand_name("stress_user")
- tenant_name = data_utils.rand_name("stress_tenant")
- password = "pass"
- if CONF.identity.auth_version == 'v2':
- identity_client = admin_manager.identity_client
- projects_client = admin_manager.tenants_client
- roles_client = admin_manager.roles_client
- users_client = admin_manager.users_client
- domains_client = None
- else:
- identity_client = admin_manager.identity_v3_client
- projects_client = admin_manager.projects_client
- roles_client = admin_manager.roles_v3_client
- users_client = admin_manager.users_v3_client
- domains_client = admin_manager.domains_client
- domain = (identity_client.auth_provider.credentials.
- get('project_domain_name', 'Default'))
- credentials_client = cred_client.get_creds_client(
- identity_client, projects_client, users_client,
- roles_client, domains_client, project_domain_name=domain)
- project = credentials_client.create_project(
- name=tenant_name, description=tenant_name)
- user = credentials_client.create_user(username, password,
- project, "email")
- # Add roles specified in config file
- for conf_role in CONF.auth.tempest_roles:
- credentials_client.assign_user_role(user, project,
- conf_role)
- creds = credentials_client.get_credentials(user, project,
- password)
- manager = clients.Manager(credentials=creds)
-
- test_obj = importutils.import_class(test['action'])
- test_run = test_obj(manager, max_runs, stop_on_error)
-
- kwargs = test.get('kwargs', {})
- test_run.setUp(**dict(six.iteritems(kwargs)))
-
- LOG.debug("calling Target Object %s" %
- test_run.__class__.__name__)
-
- mp_manager = multiprocessing.Manager()
- shared_statistic = mp_manager.dict()
- shared_statistic['runs'] = 0
- shared_statistic['fails'] = 0
-
- p = multiprocessing.Process(target=test_run.execute,
- args=(shared_statistic,))
-
- process = {'process': p,
- 'p_number': p_number,
- 'action': test_run.action,
- 'statistic': shared_statistic}
-
- processes.append(process)
- p.start()
- if stop_on_error:
- # NOTE(mkoderer): only the parent should register the handler
- signal.signal(signal.SIGCHLD, sigchld_handler)
- end_time = time.time() + duration
- had_errors = False
- try:
- while True:
- if max_runs is None:
- remaining = end_time - time.time()
- if remaining <= 0:
- break
- else:
- remaining = log_check_interval
- all_proc_term = True
- for process in processes:
- if process['process'].is_alive():
- all_proc_term = False
- break
- if all_proc_term:
- break
-
- time.sleep(min(remaining, log_check_interval))
- if stop_on_error:
- if any([True for proc in processes
- if proc['statistic']['fails'] > 0]):
- break
-
- if not logfiles:
- continue
- if _has_error_in_logs(logfiles, computes, ssh_user, ssh_key,
- stop_on_error):
- had_errors = True
- break
- except KeyboardInterrupt:
- LOG.warning("Interrupted, going to print statistics and exit ...")
-
- if stop_on_error:
- signal.signal(signal.SIGCHLD, signal.SIG_DFL)
- terminate_all_processes()
-
- sum_fails = 0
- sum_runs = 0
-
- LOG.info("Statistics (per process):")
- for process in processes:
- if process['statistic']['fails'] > 0:
- had_errors = True
- sum_runs += process['statistic']['runs']
- sum_fails += process['statistic']['fails']
- print("Process %d (%s): Run %d actions (%d failed)" % (
- process['p_number'],
- process['action'],
- process['statistic']['runs'],
- process['statistic']['fails']))
- print("Summary:")
- print("Run %d actions (%d failed)" % (sum_runs, sum_fails))
-
- if not had_errors and CONF.stress.full_clean_stack:
- LOG.info("cleaning up")
- cleanup.cleanup()
- if had_errors:
- return 1
- else:
- return 0
diff --git a/tempest/stress/etc/sample-unit-test.json b/tempest/stress/etc/sample-unit-test.json
deleted file mode 100644
index 54433d5..0000000
--- a/tempest/stress/etc/sample-unit-test.json
+++ /dev/null
@@ -1,8 +0,0 @@
-[{"action": "tempest.stress.actions.unit_test.UnitTest",
- "threads": 8,
- "use_admin": true,
- "use_isolated_tenants": true,
- "kwargs": {"test_method": "tempest.cli.simple_read_only.test_glance.SimpleReadOnlyGlanceClientTest.test_glance_fake_action",
- "class_setup_per": "process"}
- }
-]
diff --git a/tempest/stress/etc/server-create-destroy-test.json b/tempest/stress/etc/server-create-destroy-test.json
deleted file mode 100644
index bbb5352..0000000
--- a/tempest/stress/etc/server-create-destroy-test.json
+++ /dev/null
@@ -1,7 +0,0 @@
-[{"action": "tempest.stress.actions.server_create_destroy.ServerCreateDestroyTest",
- "threads": 8,
- "use_admin": true,
- "use_isolated_tenants": true,
- "kwargs": {}
- }
-]
diff --git a/tempest/stress/etc/ssh_floating.json b/tempest/stress/etc/ssh_floating.json
deleted file mode 100644
index c502e96..0000000
--- a/tempest/stress/etc/ssh_floating.json
+++ /dev/null
@@ -1,16 +0,0 @@
-[{"action": "tempest.stress.actions.ssh_floating.FloatingStress",
- "threads": 8,
- "use_admin": true,
- "use_isolated_tenants": true,
- "kwargs": {"vm_extra_args": {},
- "new_vm": true,
- "new_sec_group": true,
- "new_floating": true,
- "verify": ["check_icmp_echo", "check_port_ssh"],
- "check_timeout": 120,
- "check_interval": 1,
- "wait_after_vm_create": true,
- "wait_for_disassociate": true,
- "reboot": false}
-}
-]
diff --git a/tempest/stress/etc/stress-tox-job.json b/tempest/stress/etc/stress-tox-job.json
deleted file mode 100644
index bfa448d..0000000
--- a/tempest/stress/etc/stress-tox-job.json
+++ /dev/null
@@ -1,28 +0,0 @@
-[{"action": "tempest.stress.actions.server_create_destroy.ServerCreateDestroyTest",
- "threads": 8,
- "use_admin": true,
- "use_isolated_tenants": true,
- "kwargs": {}
- },
- {"action": "tempest.stress.actions.volume_create_delete.VolumeCreateDeleteTest",
- "threads": 4,
- "use_admin": true,
- "use_isolated_tenants": true,
- "kwargs": {}
- },
- {"action": "tempest.stress.actions.volume_attach_delete.VolumeAttachDeleteTest",
- "threads": 2,
- "use_admin": true,
- "use_isolated_tenants": true,
- "kwargs": {}
- },
- {"action": "tempest.stress.actions.unit_test.UnitTest",
- "threads": 4,
- "use_admin": true,
- "use_isolated_tenants": true,
- "required_services": ["neutron"],
- "kwargs": {"test_method": "tempest.scenario.test_network_advanced_server_ops.TestNetworkAdvancedServerOps.test_server_connectivity_stop_start",
- "class_setup_per": "process"}
- }
-]
-
diff --git a/tempest/stress/etc/volume-attach-delete-test.json b/tempest/stress/etc/volume-attach-delete-test.json
deleted file mode 100644
index d468967..0000000
--- a/tempest/stress/etc/volume-attach-delete-test.json
+++ /dev/null
@@ -1,7 +0,0 @@
-[{"action": "tempest.stress.actions.volume_attach_delete.VolumeAttachDeleteTest",
- "threads": 4,
- "use_admin": true,
- "use_isolated_tenants": true,
- "kwargs": {}
- }
-]
diff --git a/tempest/stress/etc/volume-attach-verify.json b/tempest/stress/etc/volume-attach-verify.json
deleted file mode 100644
index d8c96fd..0000000
--- a/tempest/stress/etc/volume-attach-verify.json
+++ /dev/null
@@ -1,11 +0,0 @@
-[{"action": "tempest.stress.actions.volume_attach_verify.VolumeVerifyStress",
- "threads": 1,
- "use_admin": true,
- "use_isolated_tenants": true,
- "kwargs": {"vm_extra_args": {},
- "new_volume": true,
- "new_server": false,
- "ssh_test_before_attach": false,
- "enable_ssh_verify": true}
-}
-]
diff --git a/tempest/stress/etc/volume-create-delete-test.json b/tempest/stress/etc/volume-create-delete-test.json
deleted file mode 100644
index a60cde6..0000000
--- a/tempest/stress/etc/volume-create-delete-test.json
+++ /dev/null
@@ -1,7 +0,0 @@
-[{"action": "tempest.stress.actions.volume_create_delete.VolumeCreateDeleteTest",
- "threads": 4,
- "use_admin": true,
- "use_isolated_tenants": true,
- "kwargs": {}
- }
-]
diff --git a/tempest/stress/stressaction.py b/tempest/stress/stressaction.py
deleted file mode 100644
index cf0a08a..0000000
--- a/tempest/stress/stressaction.py
+++ /dev/null
@@ -1,96 +0,0 @@
-# (c) Copyright 2013 Hewlett-Packard Development Company, L.P.
-#
-# 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 abc
-import signal
-import sys
-
-import six
-
-from oslo_log import log as logging
-
-
-@six.add_metaclass(abc.ABCMeta)
-class StressAction(object):
-
- def __init__(self, manager, max_runs=None, stop_on_error=False):
- full_cname = self.__module__ + "." + self.__class__.__name__
- self.logger = logging.getLogger(full_cname)
- self.manager = manager
- self.max_runs = max_runs
- self.stop_on_error = stop_on_error
-
- def _shutdown_handler(self, signal, frame):
- try:
- self.tearDown()
- except Exception:
- self.logger.exception("Error while tearDown")
- sys.exit(0)
-
- @property
- def action(self):
- """This methods returns the action.
-
- Overload this if you create a stress test wrapper.
- """
- return self.__class__.__name__
-
- def setUp(self, **kwargs):
- """Initialize test structures/resources
-
- This method is called before "run" method to help the test
- initialize any structures. kwargs contains arguments passed
- in from the configuration json file.
-
- setUp doesn't count against the time duration.
- """
- self.logger.debug("setUp")
-
- def tearDown(self):
- """Cleanup test structures/resources
-
- This method is called to do any cleanup after the test is complete.
- """
- self.logger.debug("tearDown")
-
- def execute(self, shared_statistic):
- """This is the main execution entry point called by the driver.
-
- We register a signal handler to allow us to tearDown gracefully,
- and then exit. We also keep track of how many runs we do.
- """
- signal.signal(signal.SIGHUP, self._shutdown_handler)
- signal.signal(signal.SIGTERM, self._shutdown_handler)
-
- while self.max_runs is None or (shared_statistic['runs'] <
- self.max_runs):
- self.logger.debug("Trigger new run (run %d)" %
- shared_statistic['runs'])
- try:
- self.run()
- except Exception:
- shared_statistic['fails'] += 1
- self.logger.exception("Failure in run")
- finally:
- shared_statistic['runs'] += 1
- if self.stop_on_error and (shared_statistic['fails'] > 1):
- self.logger.warning("Stop process due to"
- "\"stop-on-error\" argument")
- self.tearDown()
- sys.exit(1)
-
- @abc.abstractmethod
- def run(self):
- """This method is where the stress test code runs."""
- return
diff --git a/tempest/stress/tools/cleanup.py b/tempest/stress/tools/cleanup.py
deleted file mode 100755
index 3885ba0..0000000
--- a/tempest/stress/tools/cleanup.py
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/usr/bin/env python
-
-# Copyright 2013 Quanta Research Cambridge, Inc.
-#
-# 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.stress import cleanup
-
-cleanup.cleanup()
diff --git a/tempest/test.py b/tempest/test.py
index 6dc065c..93fbed3 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -25,12 +25,12 @@
import testtools
from tempest import clients
-from tempest.common import cred_client
from tempest.common import credentials_factory as credentials
from tempest.common import fixed_network
import tempest.common.validation_resources as vresources
from tempest import config
from tempest import exceptions
+from tempest.lib.common import cred_client
from tempest.lib.common.utils import test_utils
from tempest.lib import decorators
from tempest.lib import exceptions as lib_exc
@@ -102,32 +102,6 @@
return decorator
-def stresstest(**kwargs):
- """Add stress test decorator
-
- For all functions with this decorator a attr stress will be
- set automatically.
-
- @param class_setup_per: allowed values are application, process, action
- ``application``: once in the stress job lifetime
- ``process``: once in the worker process lifetime
- ``action``: on each action
- @param allow_inheritance: allows inheritance of this attribute
- """
- def decorator(f):
- if 'class_setup_per' in kwargs:
- setattr(f, "st_class_setup_per", kwargs['class_setup_per'])
- else:
- setattr(f, "st_class_setup_per", 'process')
- if 'allow_inheritance' in kwargs:
- setattr(f, "st_allow_inheritance", kwargs['allow_inheritance'])
- else:
- setattr(f, "st_allow_inheritance", False)
- attr(type='stress')(f)
- return f
- return decorator
-
-
def requires_ext(**kwargs):
"""A decorator to skip tests if an extension is not enabled
@@ -636,10 +610,10 @@
cred_provider, networks_client, CONF.compute.fixed_network_name)
def assertEmpty(self, list, msg=None):
- self.assertTrue(len(list) == 0, msg)
+ self.assertEqual(0, len(list), msg)
def assertNotEmpty(self, list, msg=None):
- self.assertTrue(len(list) > 0, msg)
+ self.assertGreater(len(list), 0, msg)
call_until_true = debtcollector.moves.moved_function(
diff --git a/tempest/tests/cmd/test_account_generator.py b/tempest/tests/cmd/test_account_generator.py
index b3931d1..b08954f 100644
--- a/tempest/tests/cmd/test_account_generator.py
+++ b/tempest/tests/cmd/test_account_generator.py
@@ -146,7 +146,7 @@
identity_version = 2
identity_response = fake_identity._fake_v2_response
- cred_client = 'tempest.common.cred_client.V2CredsClient'
+ cred_client = 'tempest.lib.common.cred_client.V2CredsClient'
dynamic_creds = 'tempest.common.dynamic_creds.DynamicCredentialProvider'
def setUp(self):
@@ -245,7 +245,7 @@
identity_version = 3
identity_response = fake_identity._fake_v3_response
- cred_client = 'tempest.common.cred_client.V3CredsClient'
+ cred_client = 'tempest.lib.common.cred_client.V3CredsClient'
def setUp(self):
self.mock_domains()
@@ -256,7 +256,7 @@
identity_version = 2
identity_response = fake_identity._fake_v2_response
- cred_client = 'tempest.common.cred_client.V2CredsClient'
+ cred_client = 'tempest.lib.common.cred_client.V2CredsClient'
dynamic_creds = 'tempest.common.dynamic_creds.DynamicCredentialProvider'
domain_is_in = False
@@ -338,7 +338,7 @@
identity_version = 3
identity_response = fake_identity._fake_v3_response
- cred_client = 'tempest.common.cred_client.V3CredsClient'
+ cred_client = 'tempest.lib.common.cred_client.V3CredsClient'
domain_is_in = True
def setUp(self):
diff --git a/tempest/tests/common/test_custom_matchers.py b/tempest/tests/common/test_custom_matchers.py
index 07867fc..1053d86 100644
--- a/tempest/tests/common/test_custom_matchers.py
+++ b/tempest/tests/common/test_custom_matchers.py
@@ -25,11 +25,11 @@
matches = self.matches_matches
mismatches = self.matches_mismatches
for candidate in matches:
- self.assertEqual(None, matcher.match(candidate))
+ self.assertIsNone(matcher.match(candidate))
for candidate in mismatches:
mismatch = matcher.match(candidate)
- self.assertNotEqual(None, mismatch)
- self.assertNotEqual(None, getattr(mismatch, 'describe', None))
+ self.assertIsNotNone(mismatch)
+ self.assertIsNotNone(getattr(mismatch, 'describe', None))
def test__str__(self):
# [(expected, object to __str__)].
diff --git a/tempest/tests/common/test_waiters.py b/tempest/tests/common/test_waiters.py
index a826337..46f9526 100644
--- a/tempest/tests/common/test_waiters.py
+++ b/tempest/tests/common/test_waiters.py
@@ -18,6 +18,7 @@
from tempest.common import waiters
from tempest import exceptions
+from tempest.lib import exceptions as lib_exc
from tempest.lib.services.volume.v2 import volumes_client
from tempest.tests import base
import tempest.tests.utils as utils
@@ -36,14 +37,14 @@
waiters.wait_for_image_status(self.client, 'fake_image_id', 'active')
end_time = int(time.time())
# Ensure waiter returns before build_timeout
- self.assertTrue((end_time - start_time) < 10)
+ self.assertLess((end_time - start_time), 10)
def test_wait_for_image_status_timeout(self):
time_mock = self.patch('time.time')
time_mock.side_effect = utils.generate_timeout_series(1)
self.client.show_image.return_value = ({'status': 'saving'})
- self.assertRaises(exceptions.TimeoutException,
+ self.assertRaises(lib_exc.TimeoutException,
waiters.wait_for_image_status,
self.client, 'fake_image_id', 'active')
diff --git a/tempest/tests/common/utils/test_net_utils.py b/tempest/tests/common/utils/test_net_utils.py
new file mode 100644
index 0000000..83c6bcc
--- /dev/null
+++ b/tempest/tests/common/utils/test_net_utils.py
@@ -0,0 +1,33 @@
+# 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 mock
+
+from tempest.common.utils import net_utils
+from tempest.lib import exceptions as lib_exc
+from tempest.tests import base
+
+
+class TestGetPingPayloadSize(base.TestCase):
+
+ def test_ipv4(self):
+ self.assertEqual(1422, net_utils.get_ping_payload_size(1450, 4))
+
+ def test_ipv6(self):
+ self.assertEqual(1406, net_utils.get_ping_payload_size(1450, 6))
+
+ def test_too_low_mtu(self):
+ self.assertRaises(
+ lib_exc.BadRequest, net_utils.get_ping_payload_size, 10, 4)
+
+ def test_None(self):
+ self.assertIsNone(net_utils.get_ping_payload_size(None, mock.Mock()))
diff --git a/tempest/tests/lib/cli/test_execute.py b/tempest/tests/lib/cli/test_execute.py
index cc9c94c..aaeb6f4 100644
--- a/tempest/tests/lib/cli/test_execute.py
+++ b/tempest/tests/lib/cli/test_execute.py
@@ -11,7 +11,6 @@
# License for the specific language governing permissions and limitations
# under the License.
-
import mock
import subprocess
@@ -74,3 +73,20 @@
self.assertRaises(exceptions.CommandFailed, cli_base.execute,
"/bin/ls", action="tempest", flags="--foobar",
merge_stderr=True)
+
+ def test_execute_with_prefix(self):
+ result = cli_base.execute("env", action="",
+ prefix="env NEW_VAR=1")
+ self.assertIsInstance(result, str)
+ self.assertIn("NEW_VAR=1", result)
+
+
+class TestCLIClient(base.TestCase):
+
+ @mock.patch.object(cli_base, 'execute')
+ def test_execute_with_prefix(self, mock_execute):
+ cli = cli_base.CLIClient(prefix='env LAC_ALL=C')
+ cli.glance('action')
+ self.assertEqual(mock_execute.call_count, 1)
+ self.assertEqual(mock_execute.call_args[1],
+ {'prefix': 'env LAC_ALL=C'})
diff --git a/tempest/tests/lib/common/test_cred_client.py b/tempest/tests/lib/common/test_cred_client.py
new file mode 100644
index 0000000..1cb3103
--- /dev/null
+++ b/tempest/tests/lib/common/test_cred_client.py
@@ -0,0 +1,78 @@
+# Copyright 2016 Hewlett Packard Enterprise Development LP
+# 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 mock
+
+from tempest.lib.common import cred_client
+from tempest.tests import base
+
+
+class TestCredClientV2(base.TestCase):
+ def setUp(self):
+ super(TestCredClientV2, self).setUp()
+ self.identity_client = mock.MagicMock()
+ self.projects_client = mock.MagicMock()
+ self.users_client = mock.MagicMock()
+ self.roles_client = mock.MagicMock()
+ self.creds_client = cred_client.V2CredsClient(self.identity_client,
+ self.projects_client,
+ self.users_client,
+ self.roles_client)
+
+ def test_create_project(self):
+ self.projects_client.create_tenant.return_value = {
+ 'tenant': 'a_tenant'
+ }
+ res = self.creds_client.create_project('fake_name', 'desc')
+ self.assertEqual('a_tenant', res)
+ self.projects_client.create_tenant.assert_called_once_with(
+ name='fake_name', description='desc')
+
+ def test_delete_project(self):
+ self.creds_client.delete_project('fake_id')
+ self.projects_client.delete_tenant.assert_called_once_with(
+ 'fake_id')
+
+
+class TestCredClientV3(base.TestCase):
+ def setUp(self):
+ super(TestCredClientV3, self).setUp()
+ self.identity_client = mock.MagicMock()
+ self.projects_client = mock.MagicMock()
+ self.users_client = mock.MagicMock()
+ self.roles_client = mock.MagicMock()
+ self.domains_client = mock.MagicMock()
+ self.domains_client.list_domains.return_value = {
+ 'domains': [{'id': 'fake_domain_id'}]
+ }
+ self.creds_client = cred_client.V3CredsClient(self.identity_client,
+ self.projects_client,
+ self.users_client,
+ self.roles_client,
+ self.domains_client,
+ 'fake_domain')
+
+ def test_create_project(self):
+ self.projects_client.create_project.return_value = {
+ 'project': 'a_tenant'
+ }
+ res = self.creds_client.create_project('fake_name', 'desc')
+ self.assertEqual('a_tenant', res)
+ self.projects_client.create_project.assert_called_once_with(
+ name='fake_name', description='desc', domain_id='fake_domain_id')
+
+ def test_delete_project(self):
+ self.creds_client.delete_project('fake_id')
+ print(self.projects_client.calls)
+ self.projects_client.delete_project.assert_called_once_with(
+ 'fake_id')
diff --git a/tempest/tests/lib/services/identity/v3/test_role_assignments_client.py b/tempest/tests/lib/services/identity/v3/test_role_assignments_client.py
new file mode 100644
index 0000000..7d304c1
--- /dev/null
+++ b/tempest/tests/lib/services/identity/v3/test_role_assignments_client.py
@@ -0,0 +1,206 @@
+# Copyright 2016 Red Hat, Inc.
+#
+# 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.identity.v3 import role_assignments_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestRoleAssignmentsClient(base.BaseServiceTest):
+
+ FAKE_USER_ID = "313234"
+ FAKE_GROUP_ID = "101112"
+
+ FAKE_ROLE1_ID = "123456"
+ FAKE_ROLE2_ID = "123457"
+
+ FAKE_PROJECT_ID = "456789"
+ FAKE_DOMAIN_ID = "102030"
+
+ FAKE_USER_PROJECT_ASSIGNMENT = {
+ "links": {
+ "assignment": "http://example.com/identity/v3/projects/"
+ "%s/users/%s/roles/%s" % (FAKE_PROJECT_ID,
+ FAKE_USER_ID,
+ FAKE_ROLE2_ID)
+ },
+ "role": {
+ "id": FAKE_ROLE2_ID
+ },
+ "scope": {
+ "project": {
+ "id": FAKE_PROJECT_ID
+ }
+ },
+ "user": {
+ "id": FAKE_USER_ID
+ }
+ }
+
+ FAKE_GROUP_PROJECT_ASSIGNMENT = {
+ "links": {
+ "assignment": "http://example.com/identity/v3/projects/"
+ "%s/groups/%s/roles/%s" % (FAKE_PROJECT_ID,
+ FAKE_GROUP_ID,
+ FAKE_ROLE1_ID)
+ },
+ "role": {
+ "id": FAKE_ROLE1_ID
+ },
+ "scope": {
+ "project": {
+ "id": FAKE_PROJECT_ID
+ }
+ },
+ "group": {
+ "id": FAKE_GROUP_ID
+ }
+ }
+
+ FAKE_USER_PROJECT_EFFECTIVE_ASSIGNMENT = {
+ "links": {
+ "assignment": "http://example.com/identity/v3/projects/"
+ "%s/groups/%s/roles/%s" % (FAKE_PROJECT_ID,
+ FAKE_GROUP_ID,
+ FAKE_ROLE1_ID),
+ "membership": "http://example.com/identity/v3/groups/"
+ "%s/users/%s" % (FAKE_GROUP_ID, FAKE_USER_ID)
+ },
+ "role": {
+ "id": FAKE_ROLE1_ID
+ },
+ "scope": {
+ "project": {
+ "id": FAKE_PROJECT_ID
+ }
+ },
+ "user": {
+ "id": FAKE_USER_ID
+ }
+ }
+
+ FAKE_USER_DOMAIN_ASSIGNMENT = {
+ "links": {
+ "assignment": "http://example.com/identity/v3/domains/"
+ "%s/users/%s/roles/%s" % (FAKE_DOMAIN_ID,
+ FAKE_USER_ID,
+ FAKE_ROLE1_ID)
+ },
+ "role": {
+ "id": FAKE_ROLE1_ID
+ },
+ "scope": {
+ "domain": {
+ "id": FAKE_DOMAIN_ID
+ }
+ },
+ "user": {
+ "id": FAKE_USER_ID
+ }
+ }
+
+ FAKE_GROUP_PROJECT_ASSIGNMENTS = {
+ "role_assignments": [
+ FAKE_GROUP_PROJECT_ASSIGNMENT
+ ],
+ "links": {
+ "self": "http://example.com/identity/v3/role_assignments?"
+ "scope.project.id=%s&group.id=%s&effective" % (
+ FAKE_PROJECT_ID, FAKE_GROUP_ID),
+ "previous": None,
+ "next": None
+ }
+ }
+
+ FAKE_USER_PROJECT_EFFECTIVE_ASSIGNMENTS = {
+ "role_assignments": [
+ FAKE_USER_PROJECT_ASSIGNMENT,
+ FAKE_USER_PROJECT_EFFECTIVE_ASSIGNMENT
+ ],
+ "links": {
+ "self": "http://example.com/identity/v3/role_assignments?"
+ "scope.project.id=%s&user.id=%s&effective" % (
+ FAKE_PROJECT_ID, FAKE_USER_ID),
+ "previous": None,
+ "next": None
+ }
+ }
+
+ FAKE_USER_DOMAIN_ASSIGNMENTS = {
+ "role_assignments": [
+ FAKE_USER_DOMAIN_ASSIGNMENT
+ ],
+ "links": {
+ "self": "http://example.com/identity/v3/role_assignments?"
+ "scope.domain.id=%s&user.id=%s&effective" % (
+ FAKE_DOMAIN_ID, FAKE_USER_ID),
+ "previous": None,
+ "next": None
+ }
+ }
+
+ def setUp(self):
+ super(TestRoleAssignmentsClient, self).setUp()
+ fake_auth = fake_auth_provider.FakeAuthProvider()
+ self.client = role_assignments_client.RoleAssignmentsClient(
+ fake_auth, 'identity', 'regionOne')
+
+ def _test_list_user_project_effective_assignments(self, bytes_body=False):
+ params = {'scope.project.id': self.FAKE_PROJECT_ID,
+ 'user.id': self.FAKE_USER_ID}
+ self.check_service_client_function(
+ self.client.list_role_assignments,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_USER_PROJECT_EFFECTIVE_ASSIGNMENTS,
+ bytes_body,
+ effective=True,
+ **params)
+
+ def test_list_user_project_effective_assignments_with_str_body(self):
+ self._test_list_user_project_effective_assignments()
+
+ def test_list_user_project_effective_assignments_with_bytes_body(self):
+ self._test_list_user_project_effective_assignments(bytes_body=True)
+
+ def _test_list_group_project_assignments(self, bytes_body=False):
+ params = {'scope.project.id': self.FAKE_PROJECT_ID,
+ 'group.id': self.FAKE_GROUP_ID}
+ self.check_service_client_function(
+ self.client.list_role_assignments,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_GROUP_PROJECT_ASSIGNMENTS,
+ bytes_body,
+ **params)
+
+ def test_list_group_project_assignments_with_str_body(self):
+ self._test_list_group_project_assignments()
+
+ def test_list_group_project_assignments_with_bytes_body(self):
+ self._test_list_group_project_assignments(bytes_body=True)
+
+ def _test_list_user_domain_assignments(self, bytes_body=False):
+ params = {'scope.domain.id': self.FAKE_DOMAIN_ID,
+ 'user.id': self.FAKE_USER_ID}
+ self.check_service_client_function(
+ self.client.list_role_assignments,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_GROUP_PROJECT_ASSIGNMENTS,
+ bytes_body,
+ **params)
+
+ def test_list_user_domain_assignments_with_str_body(self):
+ self._test_list_user_domain_assignments()
+
+ def test_list_user_domain_assignments_with_bytes_body(self):
+ self._test_list_user_domain_assignments(bytes_body=True)
diff --git a/tempest/tests/lib/services/network/test_versions_client.py b/tempest/tests/lib/services/network/test_versions_client.py
index ae52c8a..026dc6d 100644
--- a/tempest/tests/lib/services/network/test_versions_client.py
+++ b/tempest/tests/lib/services/network/test_versions_client.py
@@ -35,10 +35,7 @@
"type": "text/html"
}
],
- "status": "CURRENT",
- "updated": "2013-07-23T11:33:21Z",
- "version": "2.0",
- "min_version": "2.0"
+ "status": "CURRENT"
}
}
diff --git a/tempest/tests/lib/services/volume/v1/test_quotas_client.py b/tempest/tests/lib/services/volume/v1/test_quotas_client.py
new file mode 100644
index 0000000..f9e76af
--- /dev/null
+++ b/tempest/tests/lib/services/volume/v1/test_quotas_client.py
@@ -0,0 +1,96 @@
+# Copyright 2016 NEC 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.
+
+from tempest.lib.services.volume.v1 import quotas_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestQuotasClient(base.BaseServiceTest):
+ FAKE_QUOTAS = {
+ "quota_set": {
+ "cores": 20,
+ "fixed_ips": -1,
+ "floating_ips": 10,
+ "id": "fake_tenant",
+ "injected_file_content_bytes": 10240,
+ "injected_file_path_bytes": 255,
+ "injected_files": 5,
+ "instances": 10,
+ "key_pairs": 100,
+ "metadata_items": 128,
+ "ram": 51200,
+ "security_group_rules": 20,
+ "security_groups": 10
+ }
+ }
+
+ FAKE_UPDATE_QUOTAS_REQUEST = {
+ "quota_set": {
+ "security_groups": 45
+ }
+ }
+
+ def setUp(self):
+ super(TestQuotasClient, self).setUp()
+ fake_auth = fake_auth_provider.FakeAuthProvider()
+ self.client = quotas_client.QuotasClient(fake_auth,
+ 'volume',
+ 'regionOne')
+
+ def _test_show_default_quota_set(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.show_default_quota_set,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_QUOTAS,
+ bytes_body, tenant_id="fake_tenant")
+
+ def _test_show_quota_set(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.show_quota_set,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_QUOTAS,
+ bytes_body, tenant_id="fake_tenant")
+
+ def _test_update_quota_set(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.update_quota_set,
+ 'tempest.lib.common.rest_client.RestClient.put',
+ self.FAKE_UPDATE_QUOTAS_REQUEST,
+ bytes_body, tenant_id="fake_tenant")
+
+ def test_show_default_quota_set_with_str_body(self):
+ self._test_show_default_quota_set()
+
+ def test_show_default_quota_set_with_bytes_body(self):
+ self._test_show_default_quota_set(bytes_body=True)
+
+ def test_show_quota_set_with_str_body(self):
+ self._test_show_quota_set()
+
+ def test_show_quota_set_with_bytes_body(self):
+ self._test_show_quota_set(bytes_body=True)
+
+ def test_update_quota_set_with_str_body(self):
+ self._test_update_quota_set()
+
+ def test_update_quota_set_with_bytes_body(self):
+ self._test_update_quota_set(bytes_body=True)
+
+ def test_delete_quota_set(self):
+ self.check_service_client_function(
+ self.client.delete_quota_set,
+ 'tempest.lib.common.rest_client.RestClient.delete',
+ {},
+ tenant_id="fake_tenant")
diff --git a/tempest/tests/lib/services/volume/v1/test_snapshots_client.py b/tempest/tests/lib/services/volume/v1/test_snapshots_client.py
new file mode 100644
index 0000000..49191e3
--- /dev/null
+++ b/tempest/tests/lib/services/volume/v1/test_snapshots_client.py
@@ -0,0 +1,200 @@
+# Copyright 2016 NEC 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.
+
+from tempest.lib.services.volume.v1 import snapshots_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestSnapshotsClient(base.BaseServiceTest):
+ FAKE_CREATE_SNAPSHOT = {
+ "snapshot": {
+ "display_name": "snap-001",
+ "display_description": "Daily backup",
+ "volume_id": "521752a6-acf6-4b2d-bc7a-119f9148cd8c",
+ "force": True
+ }
+ }
+
+ FAKE_UPDATE_SNAPSHOT_REQUEST = {
+ "metadata": {
+ "key": "v1"
+ }
+ }
+
+ FAKE_INFO_SNAPSHOT = {
+ "snapshot": {
+ "id": "3fbbcccf-d058-4502-8844-6feeffdf4cb5",
+ "display_name": "snap-001",
+ "display_description": "Daily backup",
+ "volume_id": "521752a6-acf6-4b2d-bc7a-119f9148cd8c",
+ "status": "available",
+ "size": 30,
+ "created_at": "2012-02-29T03:50:07Z"
+ }
+ }
+
+ FAKE_LIST_SNAPSHOTS = {
+ "snapshots": [
+ {
+ "id": "3fbbcccf-d058-4502-8844-6feeffdf4cb5",
+ "display_name": "snap-001",
+ "display_description": "Daily backup",
+ "volume_id": "521752a6-acf6-4b2d-bc7a-119f9148cd8c",
+ "status": "available",
+ "size": 30,
+ "created_at": "2012-02-29T03:50:07Z",
+ "metadata": {
+ "contents": "junk"
+ }
+ },
+ {
+ "id": "e479997c-650b-40a4-9dfe-77655818b0d2",
+ "display_name": "snap-002",
+ "display_description": "Weekly backup",
+ "volume_id": "76b8950a-8594-4e5b-8dce-0dfa9c696358",
+ "status": "available",
+ "size": 25,
+ "created_at": "2012-03-19T01:52:47Z",
+ "metadata": {}
+ }
+ ]
+ }
+
+ def setUp(self):
+ super(TestSnapshotsClient, self).setUp()
+ fake_auth = fake_auth_provider.FakeAuthProvider()
+ self.client = snapshots_client.SnapshotsClient(fake_auth,
+ 'volume',
+ 'regionOne')
+
+ def _test_create_snapshot(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.create_snapshot,
+ 'tempest.lib.common.rest_client.RestClient.post',
+ self.FAKE_CREATE_SNAPSHOT,
+ bytes_body)
+
+ def _test_show_snapshot(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.show_snapshot,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_INFO_SNAPSHOT,
+ bytes_body,
+ snapshot_id="3fbbcccf-d058-4502-8844-6feeffdf4cb5")
+
+ def _test_list_snapshots(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.list_snapshots,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_LIST_SNAPSHOTS,
+ bytes_body,
+ detail=True)
+
+ def _test_create_snapshot_metadata(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.create_snapshot_metadata,
+ 'tempest.lib.common.rest_client.RestClient.post',
+ self.FAKE_INFO_SNAPSHOT,
+ bytes_body,
+ snapshot_id="3fbbcccf-d058-4502-8844-6feeffdf4cb5",
+ metadata={"key": "v1"})
+
+ def _test_update_snapshot(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.update_snapshot,
+ 'tempest.lib.common.rest_client.RestClient.put',
+ self.FAKE_UPDATE_SNAPSHOT_REQUEST,
+ bytes_body,
+ snapshot_id="3fbbcccf-d058-4502-8844-6feeffdf4cb5")
+
+ def _test_show_snapshot_metadata(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.show_snapshot_metadata,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_UPDATE_SNAPSHOT_REQUEST,
+ bytes_body,
+ snapshot_id="3fbbcccf-d058-4502-8844-6feeffdf4cb5")
+
+ def _test_update_snapshot_metadata(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.update_snapshot_metadata,
+ 'tempest.lib.common.rest_client.RestClient.put',
+ self.FAKE_UPDATE_SNAPSHOT_REQUEST,
+ bytes_body, snapshot_id="cbc36478b0bd8e67e89")
+
+ def _test_update_snapshot_metadata_item(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.update_snapshot_metadata_item,
+ 'tempest.lib.common.rest_client.RestClient.put',
+ self.FAKE_INFO_SNAPSHOT,
+ bytes_body, volume_type_id="cbc36478b0bd8e67e89")
+
+ def test_create_snapshot_with_str_body(self):
+ self._test_create_snapshot()
+
+ def test_create_snapshot_with_bytes_body(self):
+ self._test_create_snapshot(bytes_body=True)
+
+ def test_show_snapshot_with_str_body(self):
+ self._test_show_snapshot()
+
+ def test_show_snapshot_with_bytes_body(self):
+ self._test_show_snapshot(bytes_body=True)
+
+ def test_list_snapshots_with_str_body(self):
+ self._test_list_snapshots()
+
+ def test_list_snapshots_with_bytes_body(self):
+ self._test_list_snapshots(bytes_body=True)
+
+ def test_create_snapshot_metadata_with_str_body(self):
+ self._test_create_snapshot_metadata()
+
+ def test_create_snapshot_metadata_with_bytes_body(self):
+ self._test_create_snapshot_metadata(bytes_body=True)
+
+ def test_update_snapshot_with_str_body(self):
+ self._test_update_snapshot()
+
+ def test_update_snapshot_with_bytes_body(self):
+ self._test_update_snapshot(bytes_body=True)
+
+ def test_show_snapshot_metadata_with_str_body(self):
+ self._test_show_snapshot_metadata()
+
+ def test_show_snapshot_metadata_with_bytes_body(self):
+ self._test_show_snapshot_metadata(bytes_body=True)
+
+ def test_update_snapshot_metadata_with_str_body(self):
+ self._test_update_snapshot_metadata()
+
+ def test_update_snapshot_metadata_with_bytes_body(self):
+ self._test_update_snapshot_metadata(bytes_body=True)
+
+ def test_force_delete_snapshot(self):
+ self.check_service_client_function(
+ self.client.force_delete_snapshot,
+ 'tempest.lib.common.rest_client.RestClient.post',
+ {},
+ snapshot_id="521752a6-acf6-4b2d-bc7a-119f9148cd8c",
+ status=202)
+
+ def test_delete_snapshot(self):
+ self.check_service_client_function(
+ self.client.delete_snapshot,
+ 'tempest.lib.common.rest_client.RestClient.delete',
+ {},
+ snapshot_id="521752a6-acf6-4b2d-bc7a-119f9148cd8c",
+ status=202)
diff --git a/tempest/tests/lib/services/volume/v2/test_quotas_client.py b/tempest/tests/lib/services/volume/v2/test_quotas_client.py
new file mode 100644
index 0000000..6384350
--- /dev/null
+++ b/tempest/tests/lib/services/volume/v2/test_quotas_client.py
@@ -0,0 +1,86 @@
+# Copyright 2016 NEC 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.
+
+from tempest.lib.services.volume.v2 import quotas_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestQuotasClient(base.BaseServiceTest):
+ FAKE_QUOTAS = {
+ "quota_set": {
+ "gigabytes": 5,
+ "snapshots": 10,
+ "volumes": 20
+ }
+ }
+
+ FAKE_UPDATE_QUOTAS_REQUEST = {
+ "quota_set": {
+ "security_groups": 45
+ }
+ }
+
+ def setUp(self):
+ super(TestQuotasClient, self).setUp()
+ fake_auth = fake_auth_provider.FakeAuthProvider()
+ self.client = quotas_client.QuotasClient(fake_auth,
+ 'volume',
+ 'regionOne')
+
+ def _test_show_default_quota_set(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.show_default_quota_set,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_QUOTAS,
+ bytes_body, tenant_id="fake_tenant")
+
+ def _test_show_quota_set(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.show_quota_set,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_QUOTAS,
+ bytes_body, tenant_id="fake_tenant")
+
+ def _test_update_quota_set(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.update_quota_set,
+ 'tempest.lib.common.rest_client.RestClient.put',
+ self.FAKE_UPDATE_QUOTAS_REQUEST,
+ bytes_body, tenant_id="fake_tenant")
+
+ def test_show_default_quota_set_with_str_body(self):
+ self._test_show_default_quota_set()
+
+ def test_show_default_quota_set_with_bytes_body(self):
+ self._test_show_default_quota_set(bytes_body=True)
+
+ def test_show_quota_set_with_str_body(self):
+ self._test_show_quota_set()
+
+ def test_show_quota_set_with_bytes_body(self):
+ self._test_show_quota_set(bytes_body=True)
+
+ def test_update_quota_set_with_str_body(self):
+ self._test_update_quota_set()
+
+ def test_update_quota_set_with_bytes_body(self):
+ self._test_update_quota_set(bytes_body=True)
+
+ def test_delete_quota_set(self):
+ self.check_service_client_function(
+ self.client.delete_quota_set,
+ 'tempest.lib.common.rest_client.RestClient.delete',
+ {},
+ tenant_id="fake_tenant")
diff --git a/tempest/tests/lib/services/volume/v2/test_snapshots_client.py b/tempest/tests/lib/services/volume/v2/test_snapshots_client.py
new file mode 100644
index 0000000..7d656f1
--- /dev/null
+++ b/tempest/tests/lib/services/volume/v2/test_snapshots_client.py
@@ -0,0 +1,201 @@
+# Copyright 2016 NEC 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.
+
+from tempest.lib.services.volume.v2 import snapshots_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestSnapshotsClient(base.BaseServiceTest):
+ FAKE_CREATE_SNAPSHOT = {
+ "snapshot": {
+ "display_name": "snap-001",
+ "display_description": "Daily backup",
+ "volume_id": "521752a6-acf6-4b2d-bc7a-119f9148cd8c",
+ "force": True
+ }
+ }
+
+ FAKE_UPDATE_SNAPSHOT_REQUEST = {
+ "metadata": {
+ "key": "v1"
+ }
+ }
+
+ FAKE_INFO_SNAPSHOT = {
+ "snapshot": {
+ "id": "3fbbcccf-d058-4502-8844-6feeffdf4cb5",
+ "display_name": "snap-001",
+ "display_description": "Daily backup",
+ "volume_id": "521752a6-acf6-4b2d-bc7a-119f9148cd8c",
+ "status": "available",
+ "size": 30,
+ "created_at": "2012-02-29T03:50:07Z"
+ }
+ }
+
+ FAKE_LIST_SNAPSHOTS = {
+ "snapshots": [
+ {
+ "id": "3fbbcccf-d058-4502-8844-6feeffdf4cb5",
+ "display_name": "snap-001",
+ "display_description": "Daily backup",
+ "volume_id": "521752a6-acf6-4b2d-bc7a-119f9148cd8c",
+ "status": "available",
+ "size": 30,
+ "created_at": "2012-02-29T03:50:07Z",
+ "metadata": {
+ "contents": "junk"
+ }
+ },
+ {
+ "id": "e479997c-650b-40a4-9dfe-77655818b0d2",
+ "display_name": "snap-002",
+ "display_description": "Weekly backup",
+ "volume_id": "76b8950a-8594-4e5b-8dce-0dfa9c696358",
+ "status": "available",
+ "size": 25,
+ "created_at": "2012-03-19T01:52:47Z",
+ "metadata": {}
+ }
+ ]
+ }
+
+ def setUp(self):
+ super(TestSnapshotsClient, self).setUp()
+ fake_auth = fake_auth_provider.FakeAuthProvider()
+ self.client = snapshots_client.SnapshotsClient(fake_auth,
+ 'volume',
+ 'regionOne')
+
+ def _test_create_snapshot(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.create_snapshot,
+ 'tempest.lib.common.rest_client.RestClient.post',
+ self.FAKE_CREATE_SNAPSHOT,
+ bytes_body,
+ status=202)
+
+ def _test_show_snapshot(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.show_snapshot,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_INFO_SNAPSHOT,
+ bytes_body,
+ snapshot_id="3fbbcccf-d058-4502-8844-6feeffdf4cb5")
+
+ def _test_list_snapshots(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.list_snapshots,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_LIST_SNAPSHOTS,
+ bytes_body,
+ detail=True)
+
+ def _test_create_snapshot_metadata(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.create_snapshot_metadata,
+ 'tempest.lib.common.rest_client.RestClient.post',
+ self.FAKE_INFO_SNAPSHOT,
+ bytes_body,
+ snapshot_id="3fbbcccf-d058-4502-8844-6feeffdf4cb5",
+ metadata={"key": "v1"})
+
+ def _test_update_snapshot(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.update_snapshot,
+ 'tempest.lib.common.rest_client.RestClient.put',
+ self.FAKE_UPDATE_SNAPSHOT_REQUEST,
+ bytes_body,
+ snapshot_id="3fbbcccf-d058-4502-8844-6feeffdf4cb5")
+
+ def _test_show_snapshot_metadata(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.show_snapshot_metadata,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_UPDATE_SNAPSHOT_REQUEST,
+ bytes_body,
+ snapshot_id="3fbbcccf-d058-4502-8844-6feeffdf4cb5")
+
+ def _test_update_snapshot_metadata(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.update_snapshot_metadata,
+ 'tempest.lib.common.rest_client.RestClient.put',
+ self.FAKE_UPDATE_SNAPSHOT_REQUEST,
+ bytes_body, snapshot_id="cbc36478b0bd8e67e89")
+
+ def _test_update_snapshot_metadata_item(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.update_snapshot_metadata_item,
+ 'tempest.lib.common.rest_client.RestClient.put',
+ self.FAKE_INFO_SNAPSHOT,
+ bytes_body, volume_type_id="cbc36478b0bd8e67e89")
+
+ def test_create_snapshot_with_str_body(self):
+ self._test_create_snapshot()
+
+ def test_create_snapshot_with_bytes_body(self):
+ self._test_create_snapshot(bytes_body=True)
+
+ def test_show_snapshot_with_str_body(self):
+ self._test_show_snapshot()
+
+ def test_show_snapshot_with_bytes_body(self):
+ self._test_show_snapshot(bytes_body=True)
+
+ def test_list_snapshots_with_str_body(self):
+ self._test_list_snapshots()
+
+ def test_list_snapshots_with_bytes_body(self):
+ self._test_list_snapshots(bytes_body=True)
+
+ def test_create_snapshot_metadata_with_str_body(self):
+ self._test_create_snapshot_metadata()
+
+ def test_create_snapshot_metadata_with_bytes_body(self):
+ self._test_create_snapshot_metadata(bytes_body=True)
+
+ def test_update_snapshot_with_str_body(self):
+ self._test_update_snapshot()
+
+ def test_update_snapshot_with_bytes_body(self):
+ self._test_update_snapshot(bytes_body=True)
+
+ def test_show_snapshot_metadata_with_str_body(self):
+ self._test_show_snapshot_metadata()
+
+ def test_show_snapshot_metadata_with_bytes_body(self):
+ self._test_show_snapshot_metadata(bytes_body=True)
+
+ def test_update_snapshot_metadata_with_str_body(self):
+ self._test_update_snapshot_metadata()
+
+ def test_update_snapshot_metadata_with_bytes_body(self):
+ self._test_update_snapshot_metadata(bytes_body=True)
+
+ def test_force_delete_snapshot(self):
+ self.check_service_client_function(
+ self.client.force_delete_snapshot,
+ 'tempest.lib.common.rest_client.RestClient.post',
+ {},
+ snapshot_id="521752a6-acf6-4b2d-bc7a-119f9148cd8c",
+ status=202)
+
+ def test_delete_snapshot(self):
+ self.check_service_client_function(
+ self.client.delete_snapshot,
+ 'tempest.lib.common.rest_client.RestClient.delete',
+ {},
+ snapshot_id="521752a6-acf6-4b2d-bc7a-119f9148cd8c",
+ status=202)
diff --git a/tempest/tests/stress/__init__.py b/tempest/tests/lib/services/volume/v3/__init__.py
similarity index 100%
rename from tempest/tests/stress/__init__.py
rename to tempest/tests/lib/services/volume/v3/__init__.py
diff --git a/tempest/tests/lib/services/volume/v3/test_user_messages_client.py b/tempest/tests/lib/services/volume/v3/test_user_messages_client.py
new file mode 100644
index 0000000..4aeed5f
--- /dev/null
+++ b/tempest/tests/lib/services/volume/v3/test_user_messages_client.py
@@ -0,0 +1,92 @@
+# Copyright 2016 Red Hat. 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.v3 import messages_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestUserMessagesClient(base.BaseServiceTest):
+ USER_MESSAGE_INFO = {
+ "created_at": "2016-11-21T06:16:34.000000",
+ "guaranteed_until": "2016-12-21T06:16:34.000000",
+ "user_message": "No storage could be allocated for this volume "
+ "request. You may be able to try another size or"
+ " volume type.",
+ "resource_uuid": "c570b406-bf0b-4067-9398-f0bb09a7d9d7",
+ "request_id": "req-8f68681e-9b6b-4009-b94c-ac0811595451",
+ "message_level": "ERROR",
+ "id": "9a7dafbd-a156-4540-8996-50e71b5dcadf",
+ "resource_type": "VOLUME",
+ "links": [
+ {"href": "http://192.168.100.230:8776/v3/"
+ "a678cb65f701462ea2257245cd640829/messages/"
+ "9a7dafbd-a156-4540-8996-50e71b5dcadf",
+ "rel": "self"},
+ {"href": "http://192.168.100.230:8776/"
+ "a678cb65f701462ea2257245cd640829/messages/"
+ "9a7dafbd-a156-4540-8996-50e71b5dcadf",
+ "rel": "bookmark"}]
+ }
+ FAKE_SHOW_USER_MESSAGE = {
+ "message": dict(event_id="000002", **USER_MESSAGE_INFO)}
+
+ FAKE_LIST_USER_MESSAGES = {
+ "messages": [
+ dict(event_id="000003", **USER_MESSAGE_INFO),
+ dict(event_id="000004", **USER_MESSAGE_INFO)
+ ]
+ }
+
+ def setUp(self):
+ super(TestUserMessagesClient, self).setUp()
+ fake_auth = fake_auth_provider.FakeAuthProvider()
+ self.client = messages_client.MessagesClient(fake_auth,
+ 'volume',
+ 'regionOne')
+
+ def _test_show_user_message(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.show_message,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_SHOW_USER_MESSAGE,
+ bytes_body,
+ message_id="9a7dafbd-a156-4540-8996-50e71b5dcadf")
+
+ def _test_list_user_message(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.list_messages,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_LIST_USER_MESSAGES,
+ bytes_body)
+
+ def test_list_user_message_with_str_body(self):
+ self._test_list_user_message()
+
+ def test_list_user_message_with_bytes_body(self):
+ self._test_list_user_message(bytes_body=True)
+
+ def test_show_user_message_with_str_body(self):
+ self._test_show_user_message()
+
+ def test_show_user_message_with_bytes_body(self):
+ self._test_show_user_message(bytes_body=True)
+
+ def test_delete_user_message(self):
+ self.check_service_client_function(
+ self.client.delete_message,
+ 'tempest.lib.common.rest_client.RestClient.delete',
+ {},
+ message_id="9a7dafbd-a156-4540-8996-50e71b5dcadf",
+ status=204)
diff --git a/tempest/tests/negative/test_negative_generators.py b/tempest/tests/negative/test_negative_generators.py
deleted file mode 100644
index 7e1ee2c..0000000
--- a/tempest/tests/negative/test_negative_generators.py
+++ /dev/null
@@ -1,150 +0,0 @@
-# Copyright 2014 Deutsche Telekom AG
-# 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
-
-import jsonschema
-import mock
-import six
-
-from tempest.common.generator import base_generator
-from tempest.common.generator import negative_generator
-from tempest.common.generator import valid_generator
-from tempest.tests import base
-
-
-class TestNegativeBasicGenerator(base.TestCase):
- valid_desc = {
- "name": "list-flavors-with-detail",
- "http-method": "GET",
- "url": "flavors/detail",
- "json-schema": {
- "type": "object",
- "properties": {
- "minRam": {"type": "integer"},
- "minDisk": {"type": "integer"}
- }
- },
- "resources": ["flavor", "volume", "image"]
- }
-
- minimal_desc = {
- "name": "list-flavors-with-detail",
- "http-method": "GET",
- "url": "flavors/detail",
- }
-
- add_prop_desc = {
- "name": "list-flavors-with-detail",
- "http-method": "GET",
- "url": "flavors/detail",
- "unknown_field": [12]
- }
-
- invalid_json_schema_desc = {
- "name": "list-flavors-with-detail",
- "http-method": "GET",
- "url": "flavors/detail",
- "json-schema": {"type": "NotExistingType"}
- }
-
- def setUp(self):
- super(TestNegativeBasicGenerator, self).setUp()
- self.generator = base_generator.BasicGeneratorSet()
-
- def _assert_valid_jsonschema_call(self, jsonschema_mock, desc):
- self.assertEqual(jsonschema_mock.call_count, 1)
- jsonschema_mock.assert_called_with(desc, self.generator.schema)
-
- @mock.patch('jsonschema.validate', wraps=jsonschema.validate)
- def test_validate_schema_with_valid_input(self, jsonschema_mock):
- self.generator.validate_schema(self.valid_desc)
- self._assert_valid_jsonschema_call(jsonschema_mock, self.valid_desc)
-
- @mock.patch('jsonschema.validate', wraps=jsonschema.validate)
- def test_validate_schema_with_minimal_input(self, jsonschema_mock):
- self.generator.validate_schema(self.minimal_desc)
- self._assert_valid_jsonschema_call(jsonschema_mock, self.minimal_desc)
-
- def test_validate_schema_with_invalid_input(self):
- self.assertRaises(jsonschema.ValidationError,
- self.generator.validate_schema, self.add_prop_desc)
- self.assertRaises(jsonschema.SchemaError,
- self.generator.validate_schema,
- self.invalid_json_schema_desc)
-
-
-class BaseNegativeGenerator(object):
- types = ['string', 'integer', 'object']
-
- fake_input_obj = {"type": "object",
- "properties": {"minRam": {"type": "integer"},
- "diskName": {"type": "string"},
- "maxRam": {"type": "integer", }
- }
- }
-
- unknown_type_schema = {
- "type": "not_defined"
- }
-
- class fake_test_class(object):
- def __init__(self, scenario):
- for k, v in six.iteritems(scenario):
- setattr(self, k, v)
-
- def _validate_result(self, valid_schema, invalid_schema):
- for k, v in six.iteritems(valid_schema):
- self.assertIn(k, invalid_schema)
-
- def test_generator_mandatory_functions(self):
- for data_type in self.types:
- self.assertIn(data_type, self.generator.types_dict)
-
- def test_generate_with_unknown_type(self):
- self.assertRaises(TypeError, self.generator.generate_payload,
- self.unknown_type_schema)
-
-
-class TestNegativeValidGenerator(base.TestCase, BaseNegativeGenerator):
- def setUp(self):
- super(TestNegativeValidGenerator, self).setUp()
- self.generator = valid_generator.ValidTestGenerator()
-
- def test_generate_valid(self):
- result = self.generator.generate_valid(self.fake_input_obj)
- self.assertIn("minRam", result)
- self.assertIsInstance(result["minRam"], int)
- self.assertIn("diskName", result)
- self.assertIsInstance(result["diskName"], str)
-
-
-class TestNegativeNegativeGenerator(base.TestCase, BaseNegativeGenerator):
- def setUp(self):
- super(TestNegativeNegativeGenerator, self).setUp()
- self.generator = negative_generator.NegativeTestGenerator()
-
- def test_generate_obj(self):
- schema = self.fake_input_obj
- scenarios = self.generator.generate_scenarios(schema)
- for scenario in scenarios:
- test = self.fake_test_class(scenario)
- valid_schema = \
- valid_generator.ValidTestGenerator().generate_valid(schema)
- schema_under_test = copy.copy(valid_schema)
- expected_result = \
- self.generator.generate_payload(test, schema_under_test)
- self.assertIsNone(expected_result)
- self._validate_result(valid_schema, schema_under_test)
diff --git a/tempest/tests/stress/test_stress.py b/tempest/tests/stress/test_stress.py
deleted file mode 100644
index dfe0291..0000000
--- a/tempest/tests/stress/test_stress.py
+++ /dev/null
@@ -1,54 +0,0 @@
-# Copyright 2013 Deutsche Telekom AG
-# 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 shlex
-import subprocess
-
-from oslo_log import log as logging
-from tempest.lib import exceptions
-from tempest.tests import base
-
-LOG = logging.getLogger(__name__)
-
-
-class StressFrameworkTest(base.TestCase):
- """Basic test for the stress test framework."""
-
- def _cmd(self, cmd, param):
- """Executes specified command."""
- cmd = ' '.join([cmd, param])
- LOG.info("running: '%s'" % cmd)
- cmd_str = cmd
- cmd = shlex.split(cmd)
- result = ''
- result_err = ''
- try:
- stdout = subprocess.PIPE
- stderr = subprocess.PIPE
- proc = subprocess.Popen(
- cmd, stdout=stdout, stderr=stderr)
- result, result_err = proc.communicate()
- if proc.returncode != 0:
- LOG.debug('error of %s:\n%s' % (cmd_str, result_err))
- raise exceptions.CommandFailed(proc.returncode,
- cmd,
- result)
- finally:
- LOG.debug('output of %s:\n%s' % (cmd_str, result))
- return proc.returncode
-
- def test_help_function(self):
- result = self._cmd("python", "-m tempest.cmd.run_stress -h")
- self.assertEqual(0, result)
diff --git a/tempest/tests/stress/test_stressaction.py b/tempest/tests/stress/test_stressaction.py
deleted file mode 100644
index 1a1bb67..0000000
--- a/tempest/tests/stress/test_stressaction.py
+++ /dev/null
@@ -1,63 +0,0 @@
-# Copyright 2013 Deutsche Telekom AG
-# 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 tempest.stress.stressaction as stressaction
-import tempest.test
-
-
-class FakeStressAction(stressaction.StressAction):
- def __init__(self, manager, max_runs=None, stop_on_error=False):
- super(self.__class__, self).__init__(manager, max_runs, stop_on_error)
- self._run_called = False
-
- def run(self):
- self._run_called = True
-
- @property
- def run_called(self):
- return self._run_called
-
-
-class FakeStressActionFailing(stressaction.StressAction):
- def run(self):
- raise Exception('FakeStressActionFailing raise exception')
-
-
-class TestStressAction(tempest.test.BaseTestCase):
- def _bulid_stats_dict(self, runs=0, fails=0):
- return {'runs': runs, 'fails': fails}
-
- def testStressTestRun(self):
- stressAction = FakeStressAction(manager=None, max_runs=1)
- stats = self._bulid_stats_dict()
- stressAction.execute(stats)
- self.assertTrue(stressAction.run_called)
- self.assertEqual(stats['runs'], 1)
- self.assertEqual(stats['fails'], 0)
-
- def testStressMaxTestRuns(self):
- stressAction = FakeStressAction(manager=None, max_runs=500)
- stats = self._bulid_stats_dict(runs=499)
- stressAction.execute(stats)
- self.assertTrue(stressAction.run_called)
- self.assertEqual(stats['runs'], 500)
- self.assertEqual(stats['fails'], 0)
-
- def testStressTestRunWithException(self):
- stressAction = FakeStressActionFailing(manager=None, max_runs=1)
- stats = self._bulid_stats_dict()
- stressAction.execute(stats)
- self.assertEqual(stats['runs'], 1)
- self.assertEqual(stats['fails'], 1)
diff --git a/tempest/tests/test_decorators.py b/tempest/tests/test_decorators.py
index 17dbea0..ae2f2a3 100644
--- a/tempest/tests/test_decorators.py
+++ b/tempest/tests/test_decorators.py
@@ -153,36 +153,6 @@
continue
-class TestStressDecorator(BaseDecoratorsTest):
- def _test_stresstest_helper(self, expected_frequency='process',
- expected_inheritance=False,
- **decorator_args):
- @test.stresstest(**decorator_args)
- def foo():
- pass
- self.assertEqual(getattr(foo, 'st_class_setup_per'),
- expected_frequency)
- self.assertEqual(getattr(foo, 'st_allow_inheritance'),
- expected_inheritance)
- self.assertEqual(set(['stress']), getattr(foo, '__testtools_attrs'))
-
- def test_stresstest_decorator_default(self):
- self._test_stresstest_helper()
-
- def test_stresstest_decorator_class_setup_frequency(self):
- self._test_stresstest_helper('process', class_setup_per='process')
-
- def test_stresstest_decorator_class_setup_frequency_non_default(self):
- self._test_stresstest_helper(expected_frequency='application',
- class_setup_per='application')
-
- def test_stresstest_decorator_set_frequency_and_inheritance(self):
- self._test_stresstest_helper(expected_frequency='application',
- expected_inheritance=True,
- class_setup_per='application',
- allow_inheritance=True)
-
-
class TestRequiresExtDecorator(BaseDecoratorsTest):
def setUp(self):
super(TestRequiresExtDecorator, self).setUp()
diff --git a/tempest/tests/test_negative_rest_client.py b/tempest/tests/test_negative_rest_client.py
deleted file mode 100644
index 05f9f3e..0000000
--- a/tempest/tests/test_negative_rest_client.py
+++ /dev/null
@@ -1,101 +0,0 @@
-# (c) 2015 Deutsche Telekom AG
-# Copyright 2015 Red Hat, Inc.
-# Copyright 2015 NEC 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 mock
-from oslotest import mockpatch
-
-from tempest.common import negative_rest_client
-from tempest import config
-from tempest.tests import base
-from tempest.tests import fake_config
-from tempest.tests.lib import fake_auth_provider
-
-
-class TestNegativeRestClient(base.TestCase):
-
- url = 'fake_endpoint'
-
- def setUp(self):
- super(TestNegativeRestClient, self).setUp()
- self.useFixture(fake_config.ConfigFixture())
- self.patchobject(config, 'TempestConfigPrivate',
- fake_config.FakePrivate)
- self.negative_rest_client = negative_rest_client.NegativeRestClient(
- fake_auth_provider.FakeAuthProvider(), None)
- self.useFixture(mockpatch.PatchObject(self.negative_rest_client,
- '_log_request'))
-
- @mock.patch('tempest.lib.common.rest_client.RestClient.post',
- return_value=(mock.Mock(), mock.Mock()))
- def test_post(self, mock_post):
- __, return_dict = self.negative_rest_client.send_request('POST',
- self.url,
- [], {})
- mock_post.assert_called_once_with(self.url, {})
-
- @mock.patch('tempest.lib.common.rest_client.RestClient.get',
- return_value=(mock.Mock(), mock.Mock()))
- def test_get(self, mock_get):
- __, return_dict = self.negative_rest_client.send_request('GET',
- self.url,
- [])
- mock_get.assert_called_once_with(self.url)
-
- @mock.patch('tempest.lib.common.rest_client.RestClient.delete',
- return_value=(mock.Mock(), mock.Mock()))
- def test_delete(self, mock_delete):
- __, return_dict = self.negative_rest_client.send_request('DELETE',
- self.url,
- [])
- mock_delete.assert_called_once_with(self.url)
-
- @mock.patch('tempest.lib.common.rest_client.RestClient.patch',
- return_value=(mock.Mock(), mock.Mock()))
- def test_patch(self, mock_patch):
- __, return_dict = self.negative_rest_client.send_request('PATCH',
- self.url,
- [], {})
- mock_patch.assert_called_once_with(self.url, {})
-
- @mock.patch('tempest.lib.common.rest_client.RestClient.put',
- return_value=(mock.Mock(), mock.Mock()))
- def test_put(self, mock_put):
- __, return_dict = self.negative_rest_client.send_request('PUT',
- self.url,
- [], {})
- mock_put.assert_called_once_with(self.url, {})
-
- @mock.patch('tempest.lib.common.rest_client.RestClient.head',
- return_value=(mock.Mock(), mock.Mock()))
- def test_head(self, mock_head):
- __, return_dict = self.negative_rest_client.send_request('HEAD',
- self.url,
- [])
- mock_head.assert_called_once_with(self.url)
-
- @mock.patch('tempest.lib.common.rest_client.RestClient.copy',
- return_value=(mock.Mock(), mock.Mock()))
- def test_copy(self, mock_copy):
- __, return_dict = self.negative_rest_client.send_request('COPY',
- self.url,
- [])
- mock_copy.assert_called_once_with(self.url)
-
- def test_other(self):
- self.assertRaises(AssertionError,
- self.negative_rest_client.send_request,
- 'OTHER', self.url, [])
diff --git a/test-requirements.txt b/test-requirements.txt
index 53efa46..475fb16 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -1,11 +1,11 @@
# The order of packages is significant, because pip processes them in the order
# of appearance. Changing the order has an impact on the overall integration
# process, which may cause wedges in the gate later.
-hacking<0.12,>=0.11.0 # Apache-2.0
+hacking<0.13,>=0.12.0 # Apache-2.0
# needed for doc build
sphinx!=1.3b1,<1.4,>=1.2.1 # BSD
oslosphinx>=4.7.0 # Apache-2.0
-reno>=1.8.0 # Apache2
+reno>=1.8.0 # Apache-2.0
mock>=2.0 # BSD
-coverage>=3.6 # Apache-2.0
+coverage>=4.0 # Apache-2.0
oslotest>=1.10.0 # Apache-2.0
diff --git a/tox.ini b/tox.ini
index 02eef78..7a36e84 100644
--- a/tox.ini
+++ b/tox.ini
@@ -112,14 +112,6 @@
find . -type f -name "*.pyc" -delete
tempest run --serial --regex '\[.*\bsmoke\b.*\]' {posargs}
-[testenv:stress]
-envdir = .tox/tempest
-sitepackages = {[tempestenv]sitepackages}
-setenv = {[tempestenv]setenv}
-deps = {[tempestenv]deps}
-commands =
- run-tempest-stress {posargs}
-
[testenv:venv]
commands = {posargs}
@@ -151,10 +143,10 @@
# E125 is a won't fix until https://github.com/jcrocholl/pep8/issues/126 is resolved. For further detail see https://review.openstack.org/#/c/36788/
# E123 skipped because it is ignored by default in the default pep8
# E129 skipped because it is too limiting when combined with other rules
-# Skipped because of new hacking 0.9: H405
ignore = E125,E123,E129
show-source = True
exclude = .git,.venv,.tox,dist,doc,*egg
+enable-extensions = H106,H203
[testenv:releasenotes]
commands = sphinx-build -a -E -W -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html
@@ -169,3 +161,12 @@
commands=
pip-extra-reqs -d --ignore-file=tempest/tests/* tempest
pip-missing-reqs -d --ignore-file=tempest/tests/* tempest
+
+
+[testenv:bindep]
+# Do not install any requirements. We want this to be fast and work even if
+# system dependencies are missing, since it's used to tell you what system
+# dependencies are missing! This also means that bindep must be installed
+# separately, outside of the requirements files.
+deps = bindep
+commands = bindep test