Merge "Volume force-delete when volume in maintenance state"
diff --git a/doc/source/configuration.rst b/doc/source/configuration.rst
index 18269bf..2314222 100644
--- a/doc/source/configuration.rst
+++ b/doc/source/configuration.rst
@@ -9,6 +9,8 @@
config file which explains the purpose of each individual option. You can see
the sample config file here: :ref:`tempest-sampleconf`
+.. _tempest_cred_provider_conf:
+
Test Credentials
----------------
@@ -232,6 +234,9 @@
Enabling Remote Access to Created Servers
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. _tempest_conf_network_allocation:
+
Network Creation/Usage for Servers
""""""""""""""""""""""""""""""""""
When Tempest creates servers for testing, some tests require being able to
diff --git a/doc/source/index.rst b/doc/source/index.rst
index 896cd98..1264ecc 100644
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -67,7 +67,8 @@
HACKING
REVIEWING
microversion_testing
- test-removal
+ test_removal
+ write_tests
-------
Plugins
diff --git a/doc/source/plugin.rst b/doc/source/plugin.rst
index 6b30825..b3af92f 100644
--- a/doc/source/plugin.rst
+++ b/doc/source/plugin.rst
@@ -1,3 +1,5 @@
+.. _tempest_plugin:
+
=============================
Tempest Test Plugin Interface
=============================
diff --git a/doc/source/test-removal.rst b/doc/source/test_removal.rst
similarity index 100%
rename from doc/source/test-removal.rst
rename to doc/source/test_removal.rst
diff --git a/doc/source/write_tests.rst b/doc/source/write_tests.rst
new file mode 100644
index 0000000..768bf0f
--- /dev/null
+++ b/doc/source/write_tests.rst
@@ -0,0 +1,243 @@
+.. _tempest_test_writing:
+
+Tempest Test Writing Guide
+==========================
+
+This guide serves as a starting point for developers working on writing new
+Tempest tests. At a high level tests in Tempest are just tests that conform to
+the standard python `unit test`_ framework. But there are several aspects of
+that are unique to tempest and it's role as an integration test suite running
+against a real cloud.
+
+.. _unit test: https://docs.python.org/3.6/library/unittest.html
+
+.. note:: This guide is for writing tests in the tempest repository. While many
+ parts of this guide are also applicable to tempest plugins, not all
+ the APIs mentioned are considered stable or recommended for use in
+ plugins. Please refer to :ref:`tempest_plugin` for details about
+ writing plugins
+
+
+Adding a New TestCase
+=====================
+
+The base unit of testing in Tempest is the `TestCase`_ (also called the test
+class). Each TestCase contains test methods which are the individual tests that
+will be executed by the test runner. But, the TestCase is the smallest self
+contained unit for tests from the tempest perspective. It's also the level at
+which tempest is parallel safe. In other words, multiple TestCases can be
+executed in parallel, but individual test methods in the same TestCase can not.
+Also, all test methods within a TestCase are assumed to be executed serially. As
+such you can use the test case to store variables that are shared between
+methods.
+
+.. _TestCase: https://docs.python.org/3.6/library/unittest.html#unittest.TestCase
+
+In standard unittest the lifecycle of a TestCase can be described in the
+following phases:
+
+ #. setUpClass
+ #. setUp
+ #. Test Execution
+ #. tearDown
+ #. doCleanups
+ #. tearDownClass
+
+setUpClass
+----------
+
+The setUpClass phase is the first phase executed by the test runner and is used
+to perform any setup required for all the test methods to be executed. In
+Tempest this is a very important step and will automatically do the necessary
+setup for interacting with the configured cloud.
+
+To accomplish this you do **not** define a setUpClass function, instead there
+are a number of predefined phases to setUpClass that are used. The phases are:
+
+ * skip_checks
+ * setup_credentials
+ * setup_clients
+ * resource_setup
+
+which is executed in that order. An example of a TestCase which defines all
+of these would be::
+
+ from tempest import config
+ from tempest import test
+
+ CONF = config.CONF
+
+
+ class TestExampleCase(test.BaseTestCase):
+
+ @classmethod
+ def skip_checks(cls):
+ """This section is used to evaluate config early and skip all test
+ methods based on these checks
+ """
+ super(TestExampleCase, cls).skip_checks()
+ if not CONF.section.foo
+ cls.skip('A helpful message')
+
+ @classmethod
+ def setup_credentials(cls):
+ """This section is used to do any manual credential allocation and also
+ in the case of dynamic credentials to override the default network
+ resource creation/auto allocation
+ """
+ # This call is used to tell the credential allocator to not create any
+ # network resources for this test case. It also enables selective
+ # creation of other neutron resources. NOTE: it must go before the
+ # super call
+ cls.set_network_resources()
+ super(TestExampleCase, cls).setup_credentials()
+
+ @classmethod
+ def setup_clients(cls):
+ """This section is used to setup client aliases from the manager object
+ or to initialize any additional clients. Except in a few very
+ specific situations you should not need to use this.
+ """
+ super(TestExampleCase, cls).setup_clients()
+ cls.servers_client = cls.os.servers_client
+
+ @classmethod
+ def resource_setup(cls):
+ """This section is used to create any resources or objects which are
+ going to be used and shared by **all** test methods in the
+ TestCase. Note then anything created in this section must also be
+ destroyed in the corresponding resource_cleanup() method (which will
+ be run during tearDownClass())
+ """
+ super(TestExampleCase, cls).resource_setup()
+ cls.shared_server = cls.servers_client.create_server(...)
+
+
+Allocating Credentials
+''''''''''''''''''''''
+
+Since Tempest tests are all about testing a running cloud, every test will need
+credentials to be able to make API requests against the cloud. Since this is
+critical to operation and, when running in parallel, easy to make a mistake,
+the base TestCase class will automatically allocate a regular user for each
+TestCase during the setup_credentials() phase. During this process it will also
+initialize a client manager object using those credentials, which will be your
+entry point into interacting with the cloud. For more details on how credentials
+are allocated the :ref:`tempest_cred_provider_conf` section of the Tempest
+Configuration Guide provides more details on the operation of this.
+
+There are some cases when you need more than a single set of credentials, or
+credentials with a more specialized set of roles. To accomplish this you have
+to set a class variable ``credentials`` on the TestCase directly. For example::
+
+ from tempest import test
+
+ class TestExampleAdmin(test.BaseTestCase):
+
+ credentials = ['primary', 'admin']
+
+ @classmethod
+ def skip_checks(cls):
+ ...
+
+In this example the ``TestExampleAdmin`` TestCase will allocate 2 sets of
+credentials, one regular user and one admin user. The corresponding manager
+objects will be set as class variables cls.os and cls.os_adm respectively. You
+can also allocate a second user by putting **'alt'** in the list too. A set of
+alt credentials are the same as primary but can be used for tests cases that
+need a second user/project.
+
+You can also specify credentials with specific roles assigned. This is useful
+for cases where there are specific RBAC requirements hard coded into an API.
+The canonical example of this are swift tests which often want to test swift's
+concepts of operator and reseller_admin. An actual example from tempest on how
+to do this is::
+
+ class PublicObjectTest(base.BaseObjectTest):
+
+ credentials = [['operator', CONF.object_storage.operator_role],
+ ['operator_alt', CONF.object_storage.operator_role]]
+
+ @classmethod
+ def setup_credentials(cls):
+ super(PublicObjectTest, cls).setup_credentials()
+ ...
+
+In this case the manager objects will be set to ``cls.os_roles_operator`` and
+``cls.os_roles_operator_alt`` respectively.
+
+
+There is no limit to how many credentials you can allocate in this manner,
+however in almost every case you should **not** need more than 3 sets of
+credentials per test case.
+
+To figure out the mapping of manager objects set on the TestCase and the
+requested credentials you can reference:
+
++-------------------+---------------------+
+| Credentials Entry | Manager Variable |
++===================+=====================+
+| primary | cls.os |
++-------------------+---------------------+
+| admin | cls.os_adm |
++-------------------+---------------------+
+| alt | cls.os_alt |
++-------------------+---------------------+
+| [$label, $role] | cls.os_roles_$label |
++-------------------+---------------------+
+
+By default cls.os is available since it is allocated in the base tempest test
+class. (located in tempest/test.py) If your TestCase inherits from a different
+direct parent class (it'll still inherit from the BaseTestCase, just not
+directly) be sure to check if that class overrides allocated credentials.
+
+Dealing with Network Allocation
+'''''''''''''''''''''''''''''''
+
+When neutron is enabled and a testing requires networking this isn't normally
+automatically setup when a tenant is created. Since tempest needs isolated
+tenants to function properly it also needs to handle network allocation. By
+default the base test class will allocate a network, subnet, and router
+automatically. (this depends on the configured credential provider, for more
+details see: :ref:`tempest_conf_network_allocation`) However, there are
+situations where you do no need all of these resources allocated. (or your
+TestCase inherits from a class that overrides the default in tempest/test.py)
+There is a class level mechanism to override this allocation and specify which
+resources you need. To do this you need to call `cls.set_network_resources()`
+in the `setup_credentials()` method before the `super()`. For example::
+
+ from tempest import test
+
+
+ class TestExampleCase(test.BaseTestCase):
+
+ @classmethod
+ def setup_credentials(cls):
+ cls.set_network_resources(network=True, subnet=True, router=False)
+ super(TestExampleCase, cls).setup_credentials()
+
+There are 2 quirks with the usage here. First for the set_network_resources
+function to work properly it **must be called before super()**. This is so
+that children classes' settings are always used instead of a parent classes'.
+The other quirk here is that if you do not want to allocate any network
+resources for your test class simply call `set_network_resources()` without
+any arguments. For example::
+
+ from tempest import test
+
+
+ class TestExampleCase(test.BaseTestCase):
+
+ @classmethod
+ def setup_credentials(cls):
+ cls.set_network_resources()
+ super(TestExampleCase, cls).setup_credentials()
+
+This will not allocate any networking resources. This is because by default all
+the arguments default to False.
+
+It's also worth pointing out that it is common for base test classes for
+different services (and scenario tests) to override this setting. When
+inheriting from classes other than the base TestCase in tempest/test.py it is
+worth checking the immediate parent for what is set to determine if your
+class needs to override that setting.
diff --git a/releasenotes/notes/add-OAUTH-Consumer-Client-tempest-tests-db1df7aae4a9fd4e.yaml b/releasenotes/notes/add-OAUTH-Consumer-Client-tempest-tests-db1df7aae4a9fd4e.yaml
new file mode 100644
index 0000000..6b45666
--- /dev/null
+++ b/releasenotes/notes/add-OAUTH-Consumer-Client-tempest-tests-db1df7aae4a9fd4e.yaml
@@ -0,0 +1,3 @@
+---
+features:
+ - Add a new client to handle the OAUTH consumers feature from the identity API.
diff --git a/releasenotes/notes/add-additional-methods-to-roles-client-library-178d4a6000dec72d.yaml b/releasenotes/notes/add-additional-methods-to-roles-client-library-178d4a6000dec72d.yaml
new file mode 100644
index 0000000..01136c6
--- /dev/null
+++ b/releasenotes/notes/add-additional-methods-to-roles-client-library-178d4a6000dec72d.yaml
@@ -0,0 +1,7 @@
+---
+features:
+ - |
+ Add missing API call, list all role inference rules,
+ to the roles_client library. This feature enables the
+ possibility of listing all role inference rules in the
+ system.
diff --git a/releasenotes/notes/add-compute-server-evaculate-client-as-a-library-ed76baf25f02c3ca.yaml b/releasenotes/notes/add-compute-server-evaculate-client-as-a-library-ed76baf25f02c3ca.yaml
new file mode 100644
index 0000000..848a21b
--- /dev/null
+++ b/releasenotes/notes/add-compute-server-evaculate-client-as-a-library-ed76baf25f02c3ca.yaml
@@ -0,0 +1,3 @@
+---
+features:
+ - Define the compute server evacuate client method in the servers_client library.
diff --git a/releasenotes/notes/add-update-encryption-type-to-encryption-types-client-f3093532a0bcf9a1.yaml b/releasenotes/notes/add-update-encryption-type-to-encryption-types-client-f3093532a0bcf9a1.yaml
new file mode 100644
index 0000000..c95e77c
--- /dev/null
+++ b/releasenotes/notes/add-update-encryption-type-to-encryption-types-client-f3093532a0bcf9a1.yaml
@@ -0,0 +1,6 @@
+---
+features:
+ - |
+ Add update encryption type API to the v2 encryption_types_client library.
+ This feature enables the possibility to update an encryption type for an
+ existing volume type.
diff --git a/releasenotes/notes/add-volume-manage-client-as-library-78ab198a1dc1bd41.yaml b/releasenotes/notes/add-volume-manage-client-as-library-78ab198a1dc1bd41.yaml
new file mode 100644
index 0000000..3453050
--- /dev/null
+++ b/releasenotes/notes/add-volume-manage-client-as-library-78ab198a1dc1bd41.yaml
@@ -0,0 +1,9 @@
+---
+features:
+ - |
+ Add the unmanage volume API service method in v2 volumes_client library.
+ Define v2 volume_manage_client client for the volume service as library
+ interfaces, allowing other projects to use this module as stable libraries
+ without maintenance changes.
+
+ * volume_manage_client(v2)
diff --git a/releasenotes/notes/deprecate-dvr_extra_resources-config-8c319d6dab7f7e5c.yaml b/releasenotes/notes/deprecate-dvr_extra_resources-config-8c319d6dab7f7e5c.yaml
new file mode 100644
index 0000000..c3e43ee
--- /dev/null
+++ b/releasenotes/notes/deprecate-dvr_extra_resources-config-8c319d6dab7f7e5c.yaml
@@ -0,0 +1,7 @@
+---
+deprecations:
+ - |
+ The ``dvr_extra_resources`` configuration switch from the ``config`` module
+ is deprecated. It was added to support the Liberty Release which we don't
+ support anymore.
+
diff --git a/releasenotes/notes/remove-deprecated-compute-validation-config-options-part-2-5cd17b6e0e6cb8a3.yaml b/releasenotes/notes/remove-deprecated-compute-validation-config-options-part-2-5cd17b6e0e6cb8a3.yaml
new file mode 100644
index 0000000..b4e4dd1
--- /dev/null
+++ b/releasenotes/notes/remove-deprecated-compute-validation-config-options-part-2-5cd17b6e0e6cb8a3.yaml
@@ -0,0 +1,11 @@
+---
+upgrade:
+ - |
+ Below deprecated config options from compute group have been removed.
+ Corresponding config options already been available in validation group.
+
+ - ``compute.image_ssh_user`` (available as ``validation.image_ssh_user``)
+ - ``compute.ssh_user`` (available as ``validation.image_ssh_user``)
+ - ``scenario.ssh_user`` (available as ``validation.image_ssh_user``)
+ - ``compute.network_for_ssh`` (available as ``validation.network_for_ssh``)
+ - ``compute.ping_timeout `` (available as ``validation.ping_timeout``)
diff --git a/requirements.txt b/requirements.txt
index 92825a7..7c934c2 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -10,7 +10,7 @@
testrepository>=0.0.18 # Apache-2.0/BSD
oslo.concurrency>=3.8.0 # Apache-2.0
oslo.config>=3.22.0 # Apache-2.0
-oslo.log>=3.11.0 # Apache-2.0
+oslo.log>=3.22.0 # Apache-2.0
oslo.serialization>=1.10.0 # Apache-2.0
oslo.utils>=3.20.0 # Apache-2.0
six>=1.9.0 # MIT
diff --git a/tempest/api/compute/admin/test_volume_swap.py b/tempest/api/compute/admin/test_volume_swap.py
index 984f1a9..22a5bc4 100644
--- a/tempest/api/compute/admin/test_volume_swap.py
+++ b/tempest/api/compute/admin/test_volume_swap.py
@@ -10,10 +10,13 @@
# License for the specific language governing permissions and limitations
# under the License.
+import time
+
from tempest.api.compute import base
from tempest.common import waiters
from tempest import config
from tempest.lib import decorators
+from tempest.lib import exceptions as lib_exc
from tempest import test
CONF = config.CONF
@@ -41,6 +44,41 @@
if not CONF.compute_feature_enabled.swap_volume:
raise cls.skipException("Swapping volumes is not supported.")
+ def _wait_for_server_volume_swap(self, server_id, old_volume_id,
+ new_volume_id):
+ """Waits for a server to swap the old volume to a new one."""
+ volume_attachments = self.servers_client.list_volume_attachments(
+ server_id)['volumeAttachments']
+ attached_volume_ids = [attachment['volumeId']
+ for attachment in volume_attachments]
+ start = int(time.time())
+
+ while (old_volume_id in attached_volume_ids) \
+ or (new_volume_id not in attached_volume_ids):
+ time.sleep(self.servers_client.build_interval)
+ volume_attachments = self.servers_client.list_volume_attachments(
+ server_id)['volumeAttachments']
+ attached_volume_ids = [attachment['volumeId']
+ for attachment in volume_attachments]
+
+ if int(time.time()) - start >= self.servers_client.build_timeout:
+ old_vol_bdm_status = 'in BDM' \
+ if old_volume_id in attached_volume_ids else 'not in BDM'
+ new_vol_bdm_status = 'in BDM' \
+ if new_volume_id in attached_volume_ids else 'not in BDM'
+ message = ('Failed to swap old volume %(old_volume_id)s '
+ '(current %(old_vol_bdm_status)s) to new volume '
+ '%(new_volume_id)s (current %(new_vol_bdm_status)s)'
+ ' on server %(server_id)s within the required time '
+ '(%(timeout)s s)' %
+ {'old_volume_id': old_volume_id,
+ 'old_vol_bdm_status': old_vol_bdm_status,
+ 'new_volume_id': new_volume_id,
+ 'new_vol_bdm_status': new_vol_bdm_status,
+ 'server_id': server_id,
+ 'timeout': self.servers_client.build_timeout})
+ raise lib_exc.TimeoutException(message)
+
@decorators.idempotent_id('1769f00d-a693-4d67-a631-6a3496773813')
@test.services('volume')
def test_volume_swap(self):
@@ -61,6 +99,8 @@
volume1['id'], 'available')
waiters.wait_for_volume_resource_status(self.volumes_client,
volume2['id'], 'in-use')
+ self._wait_for_server_volume_swap(server['id'], volume1['id'],
+ volume2['id'])
# Verify "volume2" is attached to the server
vol_attachments = self.servers_client.list_volume_attachments(
server['id'])['volumeAttachments']
@@ -74,6 +114,8 @@
volume2['id'], 'available')
waiters.wait_for_volume_resource_status(self.volumes_client,
volume1['id'], 'in-use')
+ self._wait_for_server_volume_swap(server['id'], volume2['id'],
+ volume1['id'])
# Verify "volume1" is attached to the server
vol_attachments = self.servers_client.list_volume_attachments(
server['id'])['volumeAttachments']
diff --git a/tempest/api/compute/base.py b/tempest/api/compute/base.py
index ef13eef..1736463 100644
--- a/tempest/api/compute/base.py
+++ b/tempest/api/compute/base.py
@@ -94,10 +94,7 @@
cls.os.security_group_default_rules_client)
cls.versions_client = cls.os.compute_versions_client
- if CONF.volume_feature_enabled.api_v1:
- cls.volumes_client = cls.os.volumes_client
- else:
- cls.volumes_client = cls.os.volumes_v2_client
+ cls.volumes_client = cls.os.volumes_v2_client
@classmethod
def resource_setup(cls):
diff --git a/tempest/api/compute/security_groups/test_security_group_rules.py b/tempest/api/compute/security_groups/test_security_group_rules.py
index b82fa3b..a50933b 100644
--- a/tempest/api/compute/security_groups/test_security_group_rules.py
+++ b/tempest/api/compute/security_groups/test_security_group_rules.py
@@ -14,12 +14,9 @@
# under the License.
from tempest.api.compute.security_groups import base
-from tempest import config
from tempest.lib import decorators
from tempest import test
-CONF = config.CONF
-
class SecurityGroupRulesTestJSON(base.BaseSecurityGroupsTest):
diff --git a/tempest/api/compute/servers/test_list_server_filters.py b/tempest/api/compute/servers/test_list_server_filters.py
index 12eb5e1..1ad153a 100644
--- a/tempest/api/compute/servers/test_list_server_filters.py
+++ b/tempest/api/compute/servers/test_list_server_filters.py
@@ -48,23 +48,25 @@
cls.fixed_network_name = None
network_kwargs = fixed_network.set_networks_kwarg(network)
cls.s1_name = data_utils.rand_name(cls.__name__ + '-instance')
- cls.s1 = cls.create_test_server(name=cls.s1_name,
- wait_until='ACTIVE',
- **network_kwargs)
+ cls.s1 = cls.create_test_server(name=cls.s1_name, **network_kwargs)
cls.s2_name = data_utils.rand_name(cls.__name__ + '-instance')
# If image_ref_alt is "" or None then we still want to boot a server
# but we rely on `testtools.skipUnless` decorator to actually skip
# the irrelevant tests.
cls.s2 = cls.create_test_server(
- name=cls.s2_name, image_id=cls.image_ref_alt or cls.image_ref,
- wait_until='ACTIVE')
+ name=cls.s2_name, image_id=cls.image_ref_alt or cls.image_ref)
cls.s3_name = data_utils.rand_name(cls.__name__ + '-instance')
cls.s3 = cls.create_test_server(name=cls.s3_name,
flavor=cls.flavor_ref_alt,
wait_until='ACTIVE')
+ waiters.wait_for_server_status(cls.client, cls.s1['id'],
+ 'ACTIVE')
+ waiters.wait_for_server_status(cls.client, cls.s2['id'],
+ 'ACTIVE')
+
@decorators.idempotent_id('05e8a8e7-9659-459a-989d-92c2f501f4ba')
@testtools.skipUnless(CONF.compute.image_ref != CONF.compute.image_ref_alt,
"Need distinct images to run this test")
diff --git a/tempest/api/compute/servers/test_novnc.py b/tempest/api/compute/servers/test_novnc.py
index a72df5e..6354c57 100644
--- a/tempest/api/compute/servers/test_novnc.py
+++ b/tempest/api/compute/servers/test_novnc.py
@@ -13,14 +13,13 @@
# License for the specific language governing permissions and limitations
# under the License.
-import socket
import struct
import six
-from six.moves.urllib import parse as urlparse
import urllib3
from tempest.api.compute import base
+from tempest.common import compute
from tempest import config
from tempest.lib import decorators
@@ -158,14 +157,6 @@
self._websocket.response.find(b'Server: WebSockify') > 0,
'Did not get the expected WebSocket HTTP Response.')
- def _create_websocket(self, url):
- url = urlparse.urlparse(url)
- client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- client_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
- client_socket.connect((url.hostname, url.port))
- # Turn the Socket into a WebSocket to do the communication
- return _WebSocket(client_socket, url)
-
@decorators.idempotent_id('c640fdff-8ab4-45a4-a5d8-7e6146cbd0dc')
def test_novnc(self):
body = self.client.get_vnc_console(self.server['id'],
@@ -174,7 +165,7 @@
# Do the initial HTTP Request to novncproxy to get the NoVNC JavaScript
self._validate_novnc_html(body['url'])
# Do the WebSockify HTTP Request to novncproxy to do the RFB connection
- self._websocket = self._create_websocket(body['url'])
+ self._websocket = compute.create_websocket(body['url'])
# Validate that we succesfully connected and upgraded to Web Sockets
self._validate_websocket_upgrade()
# Validate the RFB Negotiation to determine if a valid VNC session
@@ -187,84 +178,9 @@
self.assertEqual('novnc', body['type'])
# Do the WebSockify HTTP Request to novncproxy with a bad token
url = body['url'].replace('token=', 'token=bad')
- self._websocket = self._create_websocket(url)
+ self._websocket = compute.create_websocket(url)
# Make sure the novncproxy rejected the connection and closed it
data = self._websocket.receive_frame()
self.assertTrue(data is None or len(data) == 0,
"The novnc proxy actually sent us some data, but we "
"expected it to close the connection.")
-
-
-class _WebSocket(object):
- def __init__(self, client_socket, url):
- """Contructor for the WebSocket wrapper to the socket."""
- self._socket = client_socket
- # Upgrade the HTTP connection to a WebSocket
- self._upgrade(url)
-
- def receive_frame(self):
- """Wrapper for receiving data to parse the WebSocket frame format"""
- # We need to loop until we either get some bytes back in the frame
- # or no data was received (meaning the socket was closed). This is
- # done to handle the case where we get back some empty frames
- while True:
- header = self._socket.recv(2)
- # If we didn't receive any data, just return None
- if len(header) == 0:
- return None
- # We will make the assumption that we are only dealing with
- # frames less than 125 bytes here (for the negotiation) and
- # that only the 2nd byte contains the length, and since the
- # server doesn't do masking, we can just read the data length
- if ord_func(header[1]) & 127 > 0:
- return self._socket.recv(ord_func(header[1]) & 127)
-
- def send_frame(self, data):
- """Wrapper for sending data to add in the WebSocket frame format."""
- frame_bytes = list()
- # For the first byte, want to say we are sending binary data (130)
- frame_bytes.append(130)
- # Only sending negotiation data so don't need to worry about > 125
- # We do need to add the bit that says we are masking the data
- frame_bytes.append(len(data) | 128)
- # We don't really care about providing a random mask for security
- # So we will just hard-code a value since a test program
- mask = [7, 2, 1, 9]
- for i in range(len(mask)):
- frame_bytes.append(mask[i])
- # Mask each of the actual data bytes that we are going to send
- for i in range(len(data)):
- frame_bytes.append(ord_func(data[i]) ^ mask[i % 4])
- # Convert our integer list to a binary array of bytes
- frame_bytes = struct.pack('!%iB' % len(frame_bytes), * frame_bytes)
- self._socket.sendall(frame_bytes)
-
- def close(self):
- """Helper method to close the connection."""
- # Close down the real socket connection and exit the test program
- if self._socket is not None:
- self._socket.shutdown(1)
- self._socket.close()
- self._socket = None
-
- def _upgrade(self, url):
- """Upgrade the HTTP connection to a WebSocket and verify."""
- # The real request goes to the /websockify URI always
- reqdata = 'GET /websockify HTTP/1.1\r\n'
- reqdata += 'Host: %s:%s\r\n' % (url.hostname, url.port)
- # Tell the HTTP Server to Upgrade the connection to a WebSocket
- reqdata += 'Upgrade: websocket\r\nConnection: Upgrade\r\n'
- # The token=xxx is sent as a Cookie not in the URI
- reqdata += 'Cookie: %s\r\n' % url.query
- # Use a hard-coded WebSocket key since a test program
- reqdata += 'Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\r\n'
- reqdata += 'Sec-WebSocket-Version: 13\r\n'
- # We are choosing to use binary even though browser may do Base64
- reqdata += 'Sec-WebSocket-Protocol: binary\r\n\r\n'
- # Send the HTTP GET request and get the response back
- self._socket.sendall(reqdata.encode('utf8'))
- self.response = data = self._socket.recv(4096)
- # Loop through & concatenate all of the data in the response body
- while len(data) > 0 and self.response.find(b'\r\n\r\n') < 0:
- data = self._socket.recv(4096)
- self.response += data
diff --git a/tempest/api/compute/volumes/test_attach_volume.py b/tempest/api/compute/volumes/test_attach_volume.py
index 73c7614..b0a6622 100644
--- a/tempest/api/compute/volumes/test_attach_volume.py
+++ b/tempest/api/compute/volumes/test_attach_volume.py
@@ -13,7 +13,6 @@
# License for the specific language governing permissions and limitations
# under the License.
-from oslo_log import log as logging
import testtools
from tempest.api.compute import base
@@ -25,8 +24,6 @@
CONF = config.CONF
-LOG = logging.getLogger(__name__)
-
class AttachVolumeTestJSON(base.BaseV2ComputeTest):
max_microversion = '2.19'
@@ -65,6 +62,20 @@
# Stop and Start a server with an attached volume, ensuring that
# the volume remains attached.
server = self._create_server()
+
+ # NOTE(andreaf) Create one remote client used throughout the test.
+ if CONF.validation.run_validation:
+ linux_client = remote_client.RemoteClient(
+ self.get_server_ip(server),
+ self.image_ssh_user,
+ self.image_ssh_password,
+ self.validation_resources['keypair']['private_key'],
+ server=server,
+ servers_client=self.servers_client)
+ # NOTE(andreaf) We need to ensure the ssh key has been
+ # injected in the guest before we power cycle
+ linux_client.validate_authentication()
+
volume = self.create_volume()
attachment = self.attach_volume(server, volume,
device=('/dev/%s' % self.device))
@@ -78,14 +89,6 @@
'ACTIVE')
if CONF.validation.run_validation:
- linux_client = remote_client.RemoteClient(
- self.get_server_ip(server),
- self.image_ssh_user,
- self.image_ssh_password,
- self.validation_resources['keypair']['private_key'],
- server=server,
- servers_client=self.servers_client)
-
disks = linux_client.get_disks()
device_name_to_match = '\n' + self.device + ' '
self.assertIn(device_name_to_match, disks)
@@ -103,14 +106,6 @@
'ACTIVE')
if CONF.validation.run_validation:
- linux_client = remote_client.RemoteClient(
- self.get_server_ip(server),
- self.image_ssh_user,
- self.image_ssh_password,
- self.validation_resources['keypair']['private_key'],
- server=server,
- servers_client=self.servers_client)
-
disks = linux_client.get_disks()
self.assertNotIn(device_name_to_match, disks)
diff --git a/tempest/api/identity/admin/v3/test_oauth_consumers.py b/tempest/api/identity/admin/v3/test_oauth_consumers.py
new file mode 100644
index 0000000..f06fb8f
--- /dev/null
+++ b/tempest/api/identity/admin/v3/test_oauth_consumers.py
@@ -0,0 +1,91 @@
+# Copyright 2017 AT&T 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.identity import base
+from tempest.common.utils import data_utils
+from tempest.lib.common.utils import test_utils
+from tempest.lib import decorators
+from tempest.lib import exceptions as exceptions
+
+
+class OAUTHConsumersV3Test(base.BaseIdentityV3AdminTest):
+
+ def _create_consumer(self):
+ """Creates a consumer with a random description."""
+ description = data_utils.rand_name('test_create_consumer')
+ consumer = self.oauth_consumers_client.create_consumer(
+ description)['consumer']
+ # cleans up created consumers after tests
+ self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+ self.oauth_consumers_client.delete_consumer,
+ consumer['id'])
+ return consumer
+
+ @decorators.idempotent_id('c8307ea6-a86c-47fd-ae7b-5b3b2caca76d')
+ def test_create_and_show_consumer(self):
+ """Tests to make sure that a consumer with parameters is made"""
+ consumer = self._create_consumer()
+ # fetch created consumer from client
+ fetched_consumer = self.oauth_consumers_client.show_consumer(
+ consumer['id'])['consumer']
+ # assert that the fetched consumer matches the created one and
+ # has all parameters
+ for key in ['description', 'id', 'links']:
+ self.assertEqual(consumer[key], fetched_consumer[key])
+
+ @decorators.idempotent_id('fdfa1b7f-2a31-4354-b2c7-f6ae20554f93')
+ def test_delete_consumer(self):
+ """Tests the delete function."""
+ consumer = self._create_consumer()
+ # fetch consumer from client to confirm it exists
+ fetched_consumer = self.oauth_consumers_client.show_consumer(
+ consumer['id'])['consumer']
+ self.assertEqual(consumer['id'], fetched_consumer['id'])
+ # delete existing consumer
+ self.oauth_consumers_client.delete_consumer(consumer['id'])
+ # check that consumer no longer exists
+ self.assertRaises(exceptions.NotFound,
+ self.oauth_consumers_client.show_consumer,
+ consumer['id'])
+
+ @decorators.idempotent_id('080a9b1a-c009-47c0-9979-5305bf72e3dc')
+ def test_update_consumer(self):
+ """Tests the update functionality"""
+ # create a new consumer to update
+ consumer = self._create_consumer()
+ # create new description
+ new_description = data_utils.rand_name('test_update_consumer')
+ # update consumer
+ self.oauth_consumers_client.update_consumer(consumer['id'],
+ new_description)
+ # check that the same consumer now has the new description
+ updated_consumer = self.oauth_consumers_client.show_consumer(
+ consumer['id'])['consumer']
+ self.assertEqual(new_description, updated_consumer['description'])
+
+ @decorators.idempotent_id('09ca50de-78f2-4ffb-ac71-f2254036b2b8')
+ def test_list_consumers(self):
+ """Test for listing consumers"""
+ # create two consumers to populate list
+ new_consumer_one = self._create_consumer()
+ new_consumer_two = self._create_consumer()
+ # fetch the list of consumers
+ consumer_list = self.oauth_consumers_client \
+ .list_consumers()['consumers']
+ # add fetched consumer ids to a list
+ id_list = [consumer['id'] for consumer in consumer_list]
+ # check if created consumers are in the list
+ self.assertIn(new_consumer_one['id'], id_list)
+ self.assertIn(new_consumer_two['id'], id_list)
diff --git a/tempest/api/identity/admin/v3/test_roles.py b/tempest/api/identity/admin/v3/test_roles.py
index e07d525..04be00b 100644
--- a/tempest/api/identity/admin/v3/test_roles.py
+++ b/tempest/api/identity/admin/v3/test_roles.py
@@ -381,3 +381,48 @@
role_assignments = self.role_assignments.list_role_assignments(
effective=True, **params)['role_assignments']
self.assertEmpty(role_assignments)
+
+ @decorators.idempotent_id('3748c316-c18f-4b08-997b-c60567bc6235')
+ def test_list_all_implied_roles(self):
+ # Create inference rule from "roles[0]" to "roles[1]"
+ self._create_implied_role(
+ self.roles[0]['id'], self.roles[1]['id'])
+
+ # Create inference rule from "roles[0]" to "roles[2]"
+ self._create_implied_role(
+ self.roles[0]['id'], self.roles[2]['id'])
+
+ # Create inference rule from "roles[2]" to "role"
+ self._create_implied_role(
+ self.roles[2]['id'], self.role['id'])
+
+ rules = self.roles_client.list_all_role_inference_rules()[
+ 'role_inferences']
+ # Sort the rules by the number of inferences, since there should be 1
+ # inference between "roles[2]" and "role" and 2 inferences for
+ # "roles[0]": between "roles[1]" and "roles[2]".
+ sorted_rules = sorted(rules, key=lambda r: len(r['implies']))
+
+ # Check that 2 sets of rules are returned.
+ self.assertEqual(2, len(sorted_rules))
+ # Check that only 1 inference rule exists between "roles[2]" and "role"
+ self.assertEqual(1, len(sorted_rules[0]['implies']))
+ # Check that 2 inference rules exist for "roles[0]": one between
+ # "roles[1]" and one between "roles[2]".
+ self.assertEqual(2, len(sorted_rules[1]['implies']))
+
+ # Check that "roles[2]" is the "prior_role" and that "role" is the
+ # "implies" role.
+ self.assertEqual(self.roles[2]['id'],
+ sorted_rules[0]['prior_role']['id'])
+ self.assertEqual(self.role['id'],
+ sorted_rules[0]['implies'][0]['id'])
+
+ # Check that "roles[0]" is the "prior_role" and that "roles[1]" and
+ # "roles[2]" are the "implies" roles.
+ self.assertEqual(self.roles[0]['id'],
+ sorted_rules[1]['prior_role']['id'])
+
+ implies_ids = [r['id'] for r in sorted_rules[1]['implies']]
+ self.assertIn(self.roles[1]['id'], implies_ids)
+ self.assertIn(self.roles[2]['id'], implies_ids)
diff --git a/tempest/api/identity/admin/v3/test_trusts.py b/tempest/api/identity/admin/v3/test_trusts.py
index 7a569b7..ec64e0c 100644
--- a/tempest/api/identity/admin/v3/test_trusts.py
+++ b/tempest/api/identity/admin/v3/test_trusts.py
@@ -46,21 +46,21 @@
def create_trustor_and_roles(self):
# create a project that trusts will be granted on
- self.trustor_project_name = data_utils.rand_name(name='project')
+ trustor_project_name = data_utils.rand_name(name='project')
project = self.projects_client.create_project(
- self.trustor_project_name, domain_id='default')['project']
+ trustor_project_name, domain_id='default')['project']
self.trustor_project_id = project['id']
self.assertIsNotNone(self.trustor_project_id)
# Create a trustor User
- self.trustor_username = data_utils.rand_name('user')
- u_desc = self.trustor_username + 'description'
- u_email = self.trustor_username + '@testmail.xx'
- self.trustor_password = data_utils.rand_password()
+ trustor_username = data_utils.rand_name('user')
+ u_desc = trustor_username + 'description'
+ u_email = trustor_username + '@testmail.xx'
+ trustor_password = data_utils.rand_password()
user = self.users_client.create_user(
- name=self.trustor_username,
+ name=trustor_username,
description=u_desc,
- password=self.trustor_password,
+ password=trustor_password,
email=u_email,
project_id=self.trustor_project_id,
domain_id='default')['user']
@@ -95,10 +95,10 @@
# Initialize a new client with the trustor credentials
creds = common_creds.get_credentials(
identity_version='v3',
- username=self.trustor_username,
- password=self.trustor_password,
+ username=trustor_username,
+ password=trustor_password,
user_domain_id='default',
- tenant_name=self.trustor_project_name,
+ tenant_name=trustor_project_name,
project_domain_id='default',
domain_id='default')
os = clients.Manager(credentials=creds)
diff --git a/tempest/api/identity/base.py b/tempest/api/identity/base.py
index 9339d3c..8317535 100644
--- a/tempest/api/identity/base.py
+++ b/tempest/api/identity/base.py
@@ -212,6 +212,7 @@
cls.groups_client = cls.os_adm.groups_client
cls.projects_client = cls.os_adm.projects_client
cls.role_assignments = cls.os_admin.role_assignments_client
+ cls.oauth_consumers_client = cls.os_adm.oauth_consumers_client
if CONF.identity.admin_domain_scope:
# NOTE(andreaf) When keystone policy requires it, the identity
# admin clients for these tests shall use 'domain' scoped tokens.
diff --git a/tempest/api/identity/v3/test_users.py b/tempest/api/identity/v3/test_users.py
index f263258..e7998ee 100644
--- a/tempest/api/identity/v3/test_users.py
+++ b/tempest/api/identity/v3/test_users.py
@@ -34,8 +34,6 @@
super(IdentityV3UsersTest, cls).resource_setup()
cls.creds = cls.os.credentials
cls.user_id = cls.creds.user_id
- cls.username = cls.creds.username
- cls.password = cls.creds.password
def _update_password(self, original_password, password):
self.non_admin_users_client.update_user_password(
diff --git a/tempest/api/image/v2/test_images_negative.py b/tempest/api/image/v2/test_images_negative.py
index 04c752a..6e5e726 100644
--- a/tempest/api/image/v2/test_images_negative.py
+++ b/tempest/api/image/v2/test_images_negative.py
@@ -98,3 +98,16 @@
self.assertRaises(lib_exc.BadRequest, self.client.create_image,
name='test', container_format='bare',
disk_format='wrong')
+
+ @test.attr(type=['negative'])
+ @decorators.idempotent_id('ab980a34-8410-40eb-872b-f264752f46e5')
+ def test_delete_protected_image(self):
+ # Create a protected image
+ image = self.create_image(protected=True)
+ self.addCleanup(self.client.update_image, image['id'],
+ [dict(replace="/protected", value=False)])
+
+ # Try deleting the protected image
+ self.assertRaises(lib_exc.Forbidden,
+ self.client.delete_image,
+ image['id'])
diff --git a/tempest/api/network/test_routers.py b/tempest/api/network/test_routers.py
index 742fe59..8df5248 100644
--- a/tempest/api/network/test_routers.py
+++ b/tempest/api/network/test_routers.py
@@ -243,6 +243,7 @@
@test.requires_ext(extension='ext-gw-mode', service='network')
@testtools.skipUnless(CONF.network.public_network_id,
'The public_network_id option must be specified.')
+ @decorators.skip_because(bug='1676207')
def test_create_router_set_gateway_with_fixed_ip(self):
# Don't know public_network_address, so at first create address
# from public_network and delete
diff --git a/tempest/api/orchestration/base.py b/tempest/api/orchestration/base.py
index 4e17c3d..8d3d344 100644
--- a/tempest/api/orchestration/base.py
+++ b/tempest/api/orchestration/base.py
@@ -47,13 +47,8 @@
cls.servers_client = cls.os.servers_client
cls.keypairs_client = cls.os.keypairs_client
cls.networks_client = cls.os.networks_client
- cls.volumes_client = cls.os.volumes_client
cls.images_v2_client = cls.os.image_client_v2
-
- if CONF.volume_feature_enabled.api_v2:
- cls.volumes_client = cls.os.volumes_v2_client
- else:
- cls.volumes_client = cls.os.volumes_client
+ cls.volumes_client = cls.os.volumes_v2_client
@classmethod
def resource_setup(cls):
diff --git a/tempest/api/orchestration/stacks/test_volumes.py b/tempest/api/orchestration/stacks/test_volumes.py
index 86323e1..7ddf7af 100644
--- a/tempest/api/orchestration/stacks/test_volumes.py
+++ b/tempest/api/orchestration/stacks/test_volumes.py
@@ -35,18 +35,10 @@
self.assertEqual('available', volume.get('status'))
self.assertEqual(CONF.volume.volume_size, volume.get('size'))
- # Some volume properties have been renamed with Cinder v2
- if CONF.volume_feature_enabled.api_v2:
- description_field = 'description'
- name_field = 'name'
- else:
- description_field = 'display_description'
- name_field = 'display_name'
-
self.assertEqual(template['resources']['volume']['properties'][
- 'description'], volume.get(description_field))
+ 'description'], volume.get('description'))
self.assertEqual(template['resources']['volume']['properties'][
- 'name'], volume.get(name_field))
+ 'name'], volume.get('name'))
def _outputs_verify(self, stack_identifier, template):
self.assertEqual('available',
diff --git a/tempest/api/volume/admin/v2/test_backends_capabilities.py b/tempest/api/volume/admin/test_backends_capabilities.py
similarity index 94%
rename from tempest/api/volume/admin/v2/test_backends_capabilities.py
rename to tempest/api/volume/admin/test_backends_capabilities.py
index 1060f2b..607fc43 100644
--- a/tempest/api/volume/admin/v2/test_backends_capabilities.py
+++ b/tempest/api/volume/admin/test_backends_capabilities.py
@@ -19,7 +19,7 @@
from tempest.lib import decorators
-class BackendsCapabilitiesAdminV2TestsJSON(base.BaseVolumeAdminTest):
+class BackendsCapabilitiesAdminTestsJSON(base.BaseVolumeAdminTest):
CAPABILITIES = ('namespace',
'vendor_name',
@@ -34,7 +34,7 @@
@classmethod
def resource_setup(cls):
- super(BackendsCapabilitiesAdminV2TestsJSON, cls).resource_setup()
+ super(BackendsCapabilitiesAdminTestsJSON, cls).resource_setup()
# Get host list, formation: host@backend-name
cls.hosts = [
pool['name'] for pool in
diff --git a/tempest/api/volume/admin/test_multi_backend.py b/tempest/api/volume/admin/test_multi_backend.py
index eea918a..2db8010 100644
--- a/tempest/api/volume/admin/test_multi_backend.py
+++ b/tempest/api/volume/admin/test_multi_backend.py
@@ -20,18 +20,18 @@
CONF = config.CONF
-class VolumeMultiBackendV2Test(base.BaseVolumeAdminTest):
+class VolumeMultiBackendTest(base.BaseVolumeAdminTest):
@classmethod
def skip_checks(cls):
- super(VolumeMultiBackendV2Test, cls).skip_checks()
+ super(VolumeMultiBackendTest, cls).skip_checks()
if not CONF.volume_feature_enabled.multi_backend:
raise cls.skipException("Cinder multi-backend feature disabled")
@classmethod
def resource_setup(cls):
- super(VolumeMultiBackendV2Test, cls).resource_setup()
+ super(VolumeMultiBackendTest, cls).resource_setup()
# read backend name from a list .
backend_names = set(CONF.volume.backend_names)
@@ -89,7 +89,7 @@
cls.admin_volume_client.delete_volume(volume_id)
cls.admin_volume_client.wait_for_resource_deletion(volume_id)
- super(VolumeMultiBackendV2Test, cls).resource_cleanup()
+ super(VolumeMultiBackendTest, cls).resource_cleanup()
@decorators.idempotent_id('c1a41f3f-9dad-493e-9f09-3ff197d477cc')
def test_backend_name_reporting(self):
diff --git a/tempest/api/volume/admin/test_qos.py b/tempest/api/volume/admin/test_qos.py
index aa6aa98..e31c0ef 100644
--- a/tempest/api/volume/admin/test_qos.py
+++ b/tempest/api/volume/admin/test_qos.py
@@ -18,16 +18,16 @@
from tempest.lib import decorators
-class QosSpecsV2TestJSON(base.BaseVolumeAdminTest):
+class QosSpecsTestJSON(base.BaseVolumeAdminTest):
"""Test the Cinder QoS-specs.
Tests for create, list, delete, show, associate,
- disassociate, set/unset key V2 APIs.
+ disassociate, set/unset key APIs.
"""
@classmethod
def resource_setup(cls):
- super(QosSpecsV2TestJSON, cls).resource_setup()
+ super(QosSpecsTestJSON, cls).resource_setup()
# Create admin qos client
# Create a test shared qos-specs for tests
cls.qos_name = utils.rand_name(cls.__name__ + '-QoS')
diff --git a/tempest/api/volume/admin/v2/test_snapshot_manage.py b/tempest/api/volume/admin/test_snapshot_manage.py
similarity index 97%
rename from tempest/api/volume/admin/v2/test_snapshot_manage.py
rename to tempest/api/volume/admin/test_snapshot_manage.py
index e8bd477..a2d5fb1 100644
--- a/tempest/api/volume/admin/v2/test_snapshot_manage.py
+++ b/tempest/api/volume/admin/test_snapshot_manage.py
@@ -23,7 +23,7 @@
CONF = config.CONF
-class SnapshotManageAdminV2Test(base.BaseVolumeAdminTest):
+class SnapshotManageAdminTest(base.BaseVolumeAdminTest):
"""Unmanage & manage snapshots
This feature provides the ability to import/export volume snapshot
diff --git a/tempest/api/volume/admin/test_snapshots_actions.py b/tempest/api/volume/admin/test_snapshots_actions.py
index 4d8e5bf..471f39a 100644
--- a/tempest/api/volume/admin/test_snapshots_actions.py
+++ b/tempest/api/volume/admin/test_snapshots_actions.py
@@ -20,16 +20,16 @@
CONF = config.CONF
-class SnapshotsActionsV2Test(base.BaseVolumeAdminTest):
+class SnapshotsActionsTest(base.BaseVolumeAdminTest):
@classmethod
def skip_checks(cls):
- super(SnapshotsActionsV2Test, cls).skip_checks()
+ super(SnapshotsActionsTest, cls).skip_checks()
if not CONF.volume_feature_enabled.snapshot:
raise cls.skipException("Cinder snapshot feature disabled")
@classmethod
def resource_setup(cls):
- super(SnapshotsActionsV2Test, cls).resource_setup()
+ super(SnapshotsActionsTest, cls).resource_setup()
# Create a test shared volume for tests
cls.volume = cls.create_volume()
@@ -43,7 +43,7 @@
snapshot_id = self.snapshot['id']
self.admin_snapshots_client.reset_snapshot_status(snapshot_id,
status)
- super(SnapshotsActionsV2Test, self).tearDown()
+ super(SnapshotsActionsTest, self).tearDown()
def _create_reset_and_force_delete_temp_snapshot(self, status=None):
# Create snapshot, reset snapshot status,
diff --git a/tempest/api/volume/admin/v3/test_user_messages.py b/tempest/api/volume/admin/test_user_messages.py
similarity index 96%
rename from tempest/api/volume/admin/v3/test_user_messages.py
rename to tempest/api/volume/admin/test_user_messages.py
index 991397a..20c3538 100755
--- a/tempest/api/volume/admin/v3/test_user_messages.py
+++ b/tempest/api/volume/admin/test_user_messages.py
@@ -13,7 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
-from tempest.api.volume.v3 import base
+from tempest.api.volume import base
from tempest import config
from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
@@ -33,7 +33,8 @@
'links']
-class UserMessagesTest(base.VolumesV3AdminTest):
+class UserMessagesTest(base.BaseVolumeAdminTest):
+ _api_version = 3
min_microversion = '3.3'
max_microversion = 'latest'
diff --git a/tempest/api/volume/admin/test_volume_hosts.py b/tempest/api/volume/admin/test_volume_hosts.py
index 1686582..04d6cf9 100644
--- a/tempest/api/volume/admin/test_volume_hosts.py
+++ b/tempest/api/volume/admin/test_volume_hosts.py
@@ -17,7 +17,7 @@
from tempest.lib import decorators
-class VolumeHostsAdminV2TestsJSON(base.BaseVolumeAdminTest):
+class VolumeHostsAdminTestsJSON(base.BaseVolumeAdminTest):
@decorators.idempotent_id('d5f3efa2-6684-4190-9ced-1c2f526352ad')
def test_list_hosts(self):
diff --git a/tempest/api/volume/admin/test_volume_manage.py b/tempest/api/volume/admin/test_volume_manage.py
new file mode 100644
index 0000000..a039085
--- /dev/null
+++ b/tempest/api/volume/admin/test_volume_manage.py
@@ -0,0 +1,81 @@
+# Copyright 2017 FiberHome Telecommunication Technologies CO.,LTD
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from tempest.api.volume import base
+from tempest.common import waiters
+from tempest import config
+from tempest.lib.common.utils import data_utils
+from tempest.lib import decorators
+
+CONF = config.CONF
+
+
+class VolumeManageAdminTest(base.BaseVolumeAdminTest):
+
+ @classmethod
+ def skip_checks(cls):
+ super(VolumeManageAdminTest, cls).skip_checks()
+
+ if not CONF.volume_feature_enabled.manage_volume:
+ raise cls.skipException("Manage volume tests are disabled")
+
+ if len(CONF.volume.manage_volume_ref) != 2:
+ raise cls.skipException("Manage volume ref is not correctly "
+ "configured")
+
+ @decorators.idempotent_id('70076c71-0ce1-4208-a8ff-36a66e65cc1e')
+ def test_unmanage_manage_volume(self):
+ # Create original volume
+ org_vol_id = self.create_volume()['id']
+ org_vol_info = self.admin_volume_client.show_volume(
+ org_vol_id)['volume']
+
+ # Unmanage the original volume
+ self.admin_volume_client.unmanage_volume(org_vol_id)
+ self.admin_volume_client.wait_for_resource_deletion(org_vol_id)
+
+ # Verify the original volume does not exist in volume list
+ params = {'all_tenants': 1}
+ all_tenants_volumes = self.admin_volume_client.list_volumes(
+ detail=True, params=params)['volumes']
+ self.assertNotIn(org_vol_id, [v['id'] for v in all_tenants_volumes])
+
+ # Manage volume
+ new_vol_name = data_utils.rand_name(
+ self.__class__.__name__ + '-volume')
+ new_vol_ref = {
+ 'name': new_vol_name,
+ 'host': org_vol_info['os-vol-host-attr:host'],
+ 'ref': {CONF.volume.manage_volume_ref[0]:
+ CONF.volume.manage_volume_ref[1] % org_vol_id},
+ 'volume_type': org_vol_info['volume_type'],
+ 'availability_zone': org_vol_info['availability_zone']}
+ new_vol_id = self.admin_volume_manage_client.manage_volume(
+ **new_vol_ref)['volume']['id']
+ self.addCleanup(self.delete_volume,
+ self.admin_volume_client, new_vol_id)
+ waiters.wait_for_volume_resource_status(self.admin_volume_client,
+ new_vol_id, 'available')
+
+ # Compare the managed volume with the original
+ new_vol_info = self.admin_volume_client.show_volume(
+ new_vol_id)['volume']
+ self.assertNotIn(new_vol_id, [org_vol_id])
+ self.assertEqual(new_vol_info['name'], new_vol_name)
+ for key in ['size',
+ 'volume_type',
+ 'availability_zone',
+ 'os-vol-host-attr:host']:
+ self.assertEqual(new_vol_info[key], org_vol_info[key])
diff --git a/tempest/api/volume/admin/v2/test_volume_pools.py b/tempest/api/volume/admin/test_volume_pools.py
similarity index 61%
rename from tempest/api/volume/admin/v2/test_volume_pools.py
rename to tempest/api/volume/admin/test_volume_pools.py
index 91d092d..60a3bda 100644
--- a/tempest/api/volume/admin/v2/test_volume_pools.py
+++ b/tempest/api/volume/admin/test_volume_pools.py
@@ -14,29 +14,26 @@
# under the License.
from tempest.api.volume import base
+from tempest import config
from tempest.lib import decorators
+CONF = config.CONF
-class VolumePoolsAdminV2TestsJSON(base.BaseVolumeAdminTest):
- @classmethod
- def resource_setup(cls):
- super(VolumePoolsAdminV2TestsJSON, cls).resource_setup()
- # Create a test shared volume for tests
- cls.volume = cls.create_volume()
-
- def _assert_host_volume_in_pools(self, with_detail=False):
- volume_info = self.admin_volume_client.show_volume(
- self.volume['id'])['volume']
+class VolumePoolsAdminTestsJSON(base.BaseVolumeAdminTest):
+ def _assert_pools(self, with_detail=False):
cinder_pools = self.admin_volume_client.show_pools(
detail=with_detail)['pools']
- self.assertIn(volume_info['os-vol-host-attr:host'],
- [pool['name'] for pool in cinder_pools])
+ self.assertIn('name', cinder_pools[0])
+ if with_detail:
+ self.assertIn(CONF.volume.vendor_name,
+ [pool['capabilities']['vendor_name']
+ for pool in cinder_pools])
@decorators.idempotent_id('0248a46c-e226-4933-be10-ad6fca8227e7')
def test_get_pools_without_details(self):
- self._assert_host_volume_in_pools()
+ self._assert_pools()
@decorators.idempotent_id('d4bb61f7-762d-4437-b8a4-5785759a0ced')
def test_get_pools_with_details(self):
- self._assert_host_volume_in_pools(with_detail=True)
+ self._assert_pools(with_detail=True)
diff --git a/tempest/api/volume/admin/test_volume_quotas.py b/tempest/api/volume/admin/test_volume_quotas.py
index 97f61c1..8bf416a 100644
--- a/tempest/api/volume/admin/test_volume_quotas.py
+++ b/tempest/api/volume/admin/test_volume_quotas.py
@@ -21,14 +21,14 @@
QUOTA_USAGE_KEYS = ['reserved', 'limit', 'in_use']
-class BaseVolumeQuotasAdminV2TestJSON(base.BaseVolumeAdminTest):
+class BaseVolumeQuotasAdminTestJSON(base.BaseVolumeAdminTest):
force_tenant_isolation = True
credentials = ['primary', 'alt', 'admin']
@classmethod
def setup_credentials(cls):
- super(BaseVolumeQuotasAdminV2TestJSON, cls).setup_credentials()
+ super(BaseVolumeQuotasAdminTestJSON, cls).setup_credentials()
cls.demo_tenant_id = cls.os.credentials.tenant_id
cls.alt_client = cls.os_alt.volumes_client
diff --git a/tempest/api/volume/admin/test_volume_quotas_negative.py b/tempest/api/volume/admin/test_volume_quotas_negative.py
index 1051445..10cf890 100644
--- a/tempest/api/volume/admin/test_volume_quotas_negative.py
+++ b/tempest/api/volume/admin/test_volume_quotas_negative.py
@@ -22,17 +22,17 @@
CONF = config.CONF
-class BaseVolumeQuotasNegativeV2TestJSON(base.BaseVolumeAdminTest):
+class BaseVolumeQuotasNegativeTestJSON(base.BaseVolumeAdminTest):
force_tenant_isolation = True
@classmethod
def setup_credentials(cls):
- super(BaseVolumeQuotasNegativeV2TestJSON, cls).setup_credentials()
+ super(BaseVolumeQuotasNegativeTestJSON, cls).setup_credentials()
cls.demo_tenant_id = cls.os.credentials.tenant_id
@classmethod
def resource_setup(cls):
- super(BaseVolumeQuotasNegativeV2TestJSON, cls).resource_setup()
+ super(BaseVolumeQuotasNegativeTestJSON, cls).resource_setup()
cls.shared_quota_set = {'gigabytes': 2 * CONF.volume.volume_size,
'volumes': 1}
diff --git a/tempest/api/volume/admin/test_volume_retype_with_migration.py b/tempest/api/volume/admin/test_volume_retype_with_migration.py
index 4d32fdd..94d5299 100644
--- a/tempest/api/volume/admin/test_volume_retype_with_migration.py
+++ b/tempest/api/volume/admin/test_volume_retype_with_migration.py
@@ -23,11 +23,11 @@
LOG = logging.getLogger(__name__)
-class VolumeRetypeWithMigrationV2Test(base.BaseVolumeAdminTest):
+class VolumeRetypeWithMigrationTest(base.BaseVolumeAdminTest):
@classmethod
def skip_checks(cls):
- super(VolumeRetypeWithMigrationV2Test, cls).skip_checks()
+ super(VolumeRetypeWithMigrationTest, cls).skip_checks()
if not CONF.volume_feature_enabled.multi_backend:
raise cls.skipException("Cinder multi-backend feature disabled.")
@@ -38,7 +38,7 @@
@classmethod
def resource_setup(cls):
- super(VolumeRetypeWithMigrationV2Test, cls).resource_setup()
+ super(VolumeRetypeWithMigrationTest, cls).resource_setup()
# read backend name from a list.
backend_src = CONF.volume.backend_names[0]
backend_dst = CONF.volume.backend_names[1]
@@ -73,7 +73,7 @@
fetched_vol['id'])
break
- super(VolumeRetypeWithMigrationV2Test, cls).resource_cleanup()
+ super(VolumeRetypeWithMigrationTest, cls).resource_cleanup()
@decorators.idempotent_id('a1a41f3f-9dad-493e-9f09-3ff197d477cd')
def test_available_volume_retype_with_migration(self):
diff --git a/tempest/api/volume/admin/test_volume_services.py b/tempest/api/volume/admin/test_volume_services.py
index aa5145d..4aab9c1 100644
--- a/tempest/api/volume/admin/test_volume_services.py
+++ b/tempest/api/volume/admin/test_volume_services.py
@@ -13,18 +13,14 @@
# License for the specific language governing permissions and limitations
# under the License.
from tempest.api.volume import base
-from tempest import config
from tempest.lib import decorators
-CONF = config.CONF
-
-
def _get_host(host):
return host.split('@')[0]
-class VolumesServicesV2TestJSON(base.BaseVolumeAdminTest):
+class VolumesServicesTestJSON(base.BaseVolumeAdminTest):
"""Tests Volume Services API.
volume service list requires admin privileges.
@@ -32,7 +28,7 @@
@classmethod
def resource_setup(cls):
- super(VolumesServicesV2TestJSON, cls).resource_setup()
+ super(VolumesServicesTestJSON, cls).resource_setup()
cls.services = (cls.admin_volume_services_client.list_services()
['services'])
# NOTE: Cinder service-list API returns the list contains
@@ -71,6 +67,20 @@
# on order.
self.assertEqual(sorted(s1), sorted(s2))
+ @decorators.idempotent_id('67ec6902-f91d-4dec-91fa-338523208bbc')
+ def test_get_service_by_volume_host_name(self):
+ volume_id = self.create_volume()['id']
+ volume = self.admin_volume_client.show_volume(volume_id)['volume']
+ hostname = _get_host(volume['os-vol-host-attr:host'])
+
+ services = (self.admin_volume_services_client.list_services(
+ host=hostname, binary='cinder-volume')['services'])
+
+ self.assertNotEqual(0, len(services),
+ 'cinder-volume not found on host %s' % hostname)
+ self.assertEqual(hostname, _get_host(services[0]['host']))
+ self.assertEqual('cinder-volume', services[0]['binary'])
+
@decorators.idempotent_id('ffa6167c-4497-4944-a464-226bbdb53908')
def test_get_service_by_service_and_host_name(self):
diff --git a/tempest/api/volume/admin/test_volume_snapshot_quotas_negative.py b/tempest/api/volume/admin/test_volume_snapshot_quotas_negative.py
index 6320fb1..e5c78cb 100644
--- a/tempest/api/volume/admin/test_volume_snapshot_quotas_negative.py
+++ b/tempest/api/volume/admin/test_volume_snapshot_quotas_negative.py
@@ -22,23 +22,23 @@
CONF = config.CONF
-class VolumeSnapshotQuotasNegativeV2TestJSON(base.BaseVolumeAdminTest):
+class VolumeSnapshotQuotasNegativeTestJSON(base.BaseVolumeAdminTest):
force_tenant_isolation = True
@classmethod
def skip_checks(cls):
- super(VolumeSnapshotQuotasNegativeV2TestJSON, cls).skip_checks()
+ super(VolumeSnapshotQuotasNegativeTestJSON, cls).skip_checks()
if not CONF.volume_feature_enabled.snapshot:
raise cls.skipException('Cinder volume snapshots are disabled')
@classmethod
def setup_credentials(cls):
- super(VolumeSnapshotQuotasNegativeV2TestJSON, cls).setup_credentials()
+ super(VolumeSnapshotQuotasNegativeTestJSON, cls).setup_credentials()
cls.demo_tenant_id = cls.os.credentials.tenant_id
@classmethod
def resource_setup(cls):
- super(VolumeSnapshotQuotasNegativeV2TestJSON, cls).resource_setup()
+ super(VolumeSnapshotQuotasNegativeTestJSON, cls).resource_setup()
cls.default_volume_size = CONF.volume.volume_size
cls.shared_quota_set = {'gigabytes': 3 * cls.default_volume_size,
'volumes': 1, 'snapshots': 1}
diff --git a/tempest/api/volume/admin/v2/test_volume_type_access.py b/tempest/api/volume/admin/test_volume_type_access.py
similarity index 96%
rename from tempest/api/volume/admin/v2/test_volume_type_access.py
rename to tempest/api/volume/admin/test_volume_type_access.py
index b60b33b..297ab6e 100644
--- a/tempest/api/volume/admin/v2/test_volume_type_access.py
+++ b/tempest/api/volume/admin/test_volume_type_access.py
@@ -23,13 +23,13 @@
CONF = config.CONF
-class VolumeTypesAccessV2Test(base.BaseVolumeAdminTest):
+class VolumeTypesAccessTest(base.BaseVolumeAdminTest):
credentials = ['primary', 'alt', 'admin']
@classmethod
def setup_clients(cls):
- super(VolumeTypesAccessV2Test, cls).setup_clients()
+ super(VolumeTypesAccessTest, cls).setup_clients()
cls.alt_client = cls.os_alt.volumes_client
@decorators.idempotent_id('d4dd0027-835f-4554-a6e5-50903fb79184')
diff --git a/tempest/api/volume/admin/test_volume_types.py b/tempest/api/volume/admin/test_volume_types.py
index fd333eb..ac717f8 100644
--- a/tempest/api/volume/admin/test_volume_types.py
+++ b/tempest/api/volume/admin/test_volume_types.py
@@ -22,7 +22,7 @@
CONF = config.CONF
-class VolumeTypesV2Test(base.BaseVolumeAdminTest):
+class VolumeTypesTest(base.BaseVolumeAdminTest):
@decorators.idempotent_id('9d9b28e3-1b2e-4483-a2cc-24aa0ea1de54')
def test_volume_type_list(self):
@@ -122,43 +122,55 @@
fetched_volume_type['os-volume-type-access:is_public'])
@decorators.idempotent_id('7830abd0-ff99-4793-a265-405684a54d46')
- def test_volume_type_encryption_create_get_delete(self):
- # Create/get/delete encryption type.
- provider = "LuksEncryptor"
- control_location = "front-end"
- body = self.create_volume_type()
+ def test_volume_type_encryption_create_get_update_delete(self):
+ # Create/get/update/delete encryption type.
+ create_kwargs = {'provider': 'LuksEncryptor',
+ 'control_location': 'front-end'}
+ volume_type_id = self.create_volume_type()['id']
+
# Create encryption type
encryption_type = \
self.admin_encryption_types_client.create_encryption_type(
- body['id'], provider=provider,
- control_location=control_location)['encryption']
+ volume_type_id, **create_kwargs)['encryption']
self.assertIn('volume_type_id', encryption_type)
- self.assertEqual(provider, encryption_type['provider'],
- "The created encryption_type provider is not equal "
- "to the requested provider")
- self.assertEqual(control_location, encryption_type['control_location'],
- "The created encryption_type control_location is not "
- "equal to the requested control_location")
+ for key in create_kwargs:
+ self.assertEqual(create_kwargs[key], encryption_type[key],
+ 'The created encryption_type %s is different '
+ 'from the requested encryption_type' % key)
# Get encryption type
+ encrypt_type_id = encryption_type['volume_type_id']
fetched_encryption_type = (
self.admin_encryption_types_client.show_encryption_type(
- encryption_type['volume_type_id']))
- self.assertEqual(provider,
- fetched_encryption_type['provider'],
- 'The fetched encryption_type provider is different '
- 'from the created encryption_type')
- self.assertEqual(control_location,
- fetched_encryption_type['control_location'],
- 'The fetched encryption_type control_location is '
- 'different from the created encryption_type')
+ encrypt_type_id))
+ for key in create_kwargs:
+ self.assertEqual(create_kwargs[key], fetched_encryption_type[key],
+ 'The fetched encryption_type %s is different '
+ 'from the created encryption_type' % key)
+
+ # Update encryption type
+ update_kwargs = {'key_size': 128,
+ 'provider': 'SomeProvider',
+ 'cipher': 'aes-xts-plain64',
+ 'control_location': 'back-end'}
+ self.admin_encryption_types_client.update_encryption_type(
+ encrypt_type_id, **update_kwargs)
+ updated_encryption_type = (
+ self.admin_encryption_types_client.show_encryption_type(
+ encrypt_type_id))
+ for key in update_kwargs:
+ self.assertEqual(update_kwargs[key], updated_encryption_type[key],
+ 'The fetched encryption_type %s is different '
+ 'from the updated encryption_type' % key)
# Delete encryption type
- type_id = encryption_type['volume_type_id']
- self.admin_encryption_types_client.delete_encryption_type(type_id)
- self.admin_encryption_types_client.wait_for_resource_deletion(type_id)
+ self.admin_encryption_types_client.delete_encryption_type(
+ encrypt_type_id)
+ self.admin_encryption_types_client.wait_for_resource_deletion(
+ encrypt_type_id)
deleted_encryption_type = (
- self.admin_encryption_types_client.show_encryption_type(type_id))
+ self.admin_encryption_types_client.show_encryption_type(
+ encrypt_type_id))
self.assertEmpty(deleted_encryption_type)
@decorators.idempotent_id('cf9f07c6-db9e-4462-a243-5933ad65e9c8')
diff --git a/tempest/api/volume/admin/test_volume_types_extra_specs.py b/tempest/api/volume/admin/test_volume_types_extra_specs.py
index 6a7ee3b..b5a2fb7 100644
--- a/tempest/api/volume/admin/test_volume_types_extra_specs.py
+++ b/tempest/api/volume/admin/test_volume_types_extra_specs.py
@@ -18,11 +18,11 @@
from tempest.lib import exceptions as lib_exc
-class VolumeTypesExtraSpecsV2Test(base.BaseVolumeAdminTest):
+class VolumeTypesExtraSpecsTest(base.BaseVolumeAdminTest):
@classmethod
def resource_setup(cls):
- super(VolumeTypesExtraSpecsV2Test, cls).resource_setup()
+ super(VolumeTypesExtraSpecsTest, cls).resource_setup()
cls.volume_type = cls.create_volume_type()
@decorators.idempotent_id('b42923e9-0452-4945-be5b-d362ae533e60')
diff --git a/tempest/api/volume/admin/test_volume_types_extra_specs_negative.py b/tempest/api/volume/admin/test_volume_types_extra_specs_negative.py
index 37422ca..4efc44b 100644
--- a/tempest/api/volume/admin/test_volume_types_extra_specs_negative.py
+++ b/tempest/api/volume/admin/test_volume_types_extra_specs_negative.py
@@ -20,11 +20,11 @@
from tempest import test
-class ExtraSpecsNegativeV2Test(base.BaseVolumeAdminTest):
+class ExtraSpecsNegativeTest(base.BaseVolumeAdminTest):
@classmethod
def resource_setup(cls):
- super(ExtraSpecsNegativeV2Test, cls).resource_setup()
+ super(ExtraSpecsNegativeTest, cls).resource_setup()
extra_specs = {"spec1": "val1"}
cls.volume_type = cls.create_volume_type(extra_specs=extra_specs)
diff --git a/tempest/api/volume/admin/test_volume_types_negative.py b/tempest/api/volume/admin/test_volume_types_negative.py
index 4017de6..bac2ea3 100644
--- a/tempest/api/volume/admin/test_volume_types_negative.py
+++ b/tempest/api/volume/admin/test_volume_types_negative.py
@@ -20,7 +20,7 @@
from tempest import test
-class VolumeTypesNegativeV2Test(base.BaseVolumeAdminTest):
+class VolumeTypesNegativeTest(base.BaseVolumeAdminTest):
@test.attr(type=['negative'])
@decorators.idempotent_id('b48c98f2-e662-4885-9b71-032256906314')
diff --git a/tempest/api/volume/admin/test_volumes_actions.py b/tempest/api/volume/admin/test_volumes_actions.py
index ba8032f..7f291e9 100644
--- a/tempest/api/volume/admin/test_volumes_actions.py
+++ b/tempest/api/volume/admin/test_volumes_actions.py
@@ -17,7 +17,7 @@
from tempest.lib import decorators
-class VolumesActionsV2Test(base.BaseVolumeAdminTest):
+class VolumesActionsTest(base.BaseVolumeAdminTest):
def _create_reset_and_force_delete_temp_volume(self, status=None):
# Create volume, reset volume status, and force delete temp volume
@@ -32,7 +32,9 @@
def test_volume_reset_status(self):
# test volume reset status : available->error->available
volume = self.create_volume()
- for status in ['error', 'available']:
+ self.addCleanup(self.admin_volume_client.reset_volume_status,
+ volume['id'], status='available')
+ for status in ['error', 'available', 'maintenance']:
self.admin_volume_client.reset_volume_status(
volume['id'], status=status)
volume_get = self.admin_volume_client.show_volume(
diff --git a/tempest/api/volume/admin/test_volumes_backup.py b/tempest/api/volume/admin/test_volumes_backup.py
index 29c79f9..afc3281 100644
--- a/tempest/api/volume/admin/test_volumes_backup.py
+++ b/tempest/api/volume/admin/test_volumes_backup.py
@@ -25,11 +25,11 @@
CONF = config.CONF
-class VolumesBackupsAdminV2Test(base.BaseVolumeAdminTest):
+class VolumesBackupsAdminTest(base.BaseVolumeAdminTest):
@classmethod
def skip_checks(cls):
- super(VolumesBackupsAdminV2Test, cls).skip_checks()
+ super(VolumesBackupsAdminTest, cls).skip_checks()
if not CONF.volume_feature_enabled.backup:
raise cls.skipException("Cinder backup feature disabled")
diff --git a/tempest/api/volume/admin/v2/test_volumes_list.py b/tempest/api/volume/admin/test_volumes_list.py
similarity index 95%
rename from tempest/api/volume/admin/v2/test_volumes_list.py
rename to tempest/api/volume/admin/test_volumes_list.py
index 6bab373..9d98b7a 100644
--- a/tempest/api/volume/admin/v2/test_volumes_list.py
+++ b/tempest/api/volume/admin/test_volumes_list.py
@@ -23,11 +23,11 @@
CONF = config.CONF
-class VolumesListAdminV2TestJSON(base.BaseVolumeAdminTest):
+class VolumesListAdminTestJSON(base.BaseVolumeAdminTest):
@classmethod
def resource_setup(cls):
- super(VolumesListAdminV2TestJSON, cls).resource_setup()
+ super(VolumesListAdminTestJSON, cls).resource_setup()
# Create 3 test volumes
# NOTE(zhufl): When using pre-provisioned credentials, the project
# may have volumes other than those created below.
diff --git a/tempest/api/volume/admin/v2/__init__.py b/tempest/api/volume/admin/v2/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/api/volume/admin/v2/__init__.py
+++ /dev/null
diff --git a/tempest/api/volume/admin/v3/__init__.py b/tempest/api/volume/admin/v3/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/api/volume/admin/v3/__init__.py
+++ /dev/null
diff --git a/tempest/api/volume/base.py b/tempest/api/volume/base.py
index 5e4fada..0cd5ec7 100644
--- a/tempest/api/volume/base.py
+++ b/tempest/api/volume/base.py
@@ -13,9 +13,11 @@
# License for the specific language governing permissions and limitations
# under the License.
+from tempest.api.volume import api_microversion_fixture
from tempest.common import compute
from tempest.common import waiters
from tempest import config
+from tempest.lib.common import api_version_utils
from tempest.lib.common.utils import data_utils
from tempest.lib.common.utils import test_utils
from tempest.lib import exceptions
@@ -24,7 +26,8 @@
CONF = config.CONF
-class BaseVolumeTest(tempest.test.BaseTestCase):
+class BaseVolumeTest(api_version_utils.BaseMicroversionTest,
+ tempest.test.BaseTestCase):
"""Base test case class for all Cinder API tests."""
_api_version = 2
@@ -49,6 +52,10 @@
msg = ("Invalid Cinder API version (%s)" % cls._api_version)
raise exceptions.InvalidConfiguration(msg)
+ api_version_utils.check_skip_with_microversion(
+ cls.min_microversion, cls.max_microversion,
+ CONF.volume.min_microversion, CONF.volume.max_microversion)
+
@classmethod
def setup_credentials(cls):
cls.set_network_resources()
@@ -68,10 +75,21 @@
cls.availability_zone_client = (
cls.os.volume_v2_availability_zone_client)
cls.volume_limits_client = cls.os.volume_v2_limits_client
+ cls.messages_client = cls.os.volume_v3_messages_client
+ cls.versions_client = cls.os.volume_v3_versions_client
+
+ def setUp(self):
+ super(BaseVolumeTest, self).setUp()
+ self.useFixture(api_microversion_fixture.APIMicroversionFixture(
+ self.request_microversion))
@classmethod
def resource_setup(cls):
super(BaseVolumeTest, cls).resource_setup()
+ cls.request_microversion = (
+ api_version_utils.select_request_microversion(
+ cls.min_microversion,
+ CONF.volume.min_microversion))
cls.snapshots = []
cls.volumes = []
@@ -225,6 +243,7 @@
cls.admin_volume_services_client = \
cls.os_adm.volume_services_v2_client
cls.admin_volume_types_client = cls.os_adm.volume_types_v2_client
+ cls.admin_volume_manage_client = cls.os_adm.volume_manage_v2_client
cls.admin_volume_client = cls.os_adm.volumes_v2_client
cls.admin_hosts_client = cls.os_adm.volume_hosts_v2_client
cls.admin_snapshot_manage_client = \
@@ -239,6 +258,8 @@
cls.os_adm.volume_capabilities_v2_client
cls.admin_scheduler_stats_client = \
cls.os_adm.volume_scheduler_stats_v2_client
+ cls.admin_messages_client = cls.os_adm.volume_v3_messages_client
+ cls.admin_volume_types_client = cls.os_adm.volume_types_v2_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 f0d653f..666efdf 100644
--- a/tempest/api/volume/test_availability_zone.py
+++ b/tempest/api/volume/test_availability_zone.py
@@ -17,12 +17,12 @@
from tempest.lib import decorators
-class AvailabilityZoneV2TestJSON(base.BaseVolumeTest):
- """Tests Availability Zone V2 API List"""
+class AvailabilityZoneTestJSON(base.BaseVolumeTest):
+ """Tests Availability Zone API List"""
@classmethod
def setup_clients(cls):
- super(AvailabilityZoneV2TestJSON, cls).setup_clients()
+ super(AvailabilityZoneTestJSON, cls).setup_clients()
cls.client = cls.availability_zone_client
@decorators.idempotent_id('01f1ae88-eba9-4c6b-a011-6f7ace06b725')
diff --git a/tempest/api/volume/test_extensions.py b/tempest/api/volume/test_extensions.py
index ac3742c..91bfa33 100644
--- a/tempest/api/volume/test_extensions.py
+++ b/tempest/api/volume/test_extensions.py
@@ -25,7 +25,7 @@
LOG = logging.getLogger(__name__)
-class ExtensionsV2TestJSON(base.BaseVolumeTest):
+class ExtensionsTestJSON(base.BaseVolumeTest):
@decorators.idempotent_id('94607eb0-43a5-47ca-82aa-736b41bd2e2c')
def test_list_extensions(self):
diff --git a/tempest/api/volume/v2/test_image_metadata.py b/tempest/api/volume/test_image_metadata.py
similarity index 87%
rename from tempest/api/volume/v2/test_image_metadata.py
rename to tempest/api/volume/test_image_metadata.py
index 9c082b3..77baf18 100644
--- a/tempest/api/volume/v2/test_image_metadata.py
+++ b/tempest/api/volume/test_image_metadata.py
@@ -23,11 +23,18 @@
CONF = config.CONF
-class VolumesV2ImageMetadata(base.BaseVolumeTest):
+class VolumesImageMetadata(base.BaseVolumeTest):
+
+ @classmethod
+ def skip_checks(cls):
+ super(VolumesImageMetadata, cls).skip_checks()
+ if not CONF.service_available.glance:
+ skip_msg = ("%s skipped as Glance is not available" % cls.__name__)
+ raise cls.skipException(skip_msg)
@classmethod
def resource_setup(cls):
- super(VolumesV2ImageMetadata, cls).resource_setup()
+ super(VolumesImageMetadata, cls).resource_setup()
# Create a volume from image ID
cls.volume = cls.create_volume(imageRef=CONF.compute.image_ref)
diff --git a/tempest/api/volume/test_snapshot_metadata.py b/tempest/api/volume/test_snapshot_metadata.py
index c1d4e05..164ed37 100644
--- a/tempest/api/volume/test_snapshot_metadata.py
+++ b/tempest/api/volume/test_snapshot_metadata.py
@@ -22,16 +22,16 @@
CONF = config.CONF
-class SnapshotV2MetadataTestJSON(base.BaseVolumeTest):
+class SnapshotMetadataTestJSON(base.BaseVolumeTest):
@classmethod
def skip_checks(cls):
- super(SnapshotV2MetadataTestJSON, cls).skip_checks()
+ super(SnapshotMetadataTestJSON, cls).skip_checks()
if not CONF.volume_feature_enabled.snapshot:
raise cls.skipException("Cinder snapshot feature disabled")
@classmethod
def resource_setup(cls):
- super(SnapshotV2MetadataTestJSON, cls).resource_setup()
+ super(SnapshotMetadataTestJSON, cls).resource_setup()
# Create a volume
cls.volume = cls.create_volume()
# Create a snapshot
@@ -41,7 +41,7 @@
# Update the metadata to {}
self.snapshots_client.update_snapshot_metadata(
self.snapshot['id'], metadata={})
- super(SnapshotV2MetadataTestJSON, self).tearDown()
+ super(SnapshotMetadataTestJSON, self).tearDown()
@decorators.idempotent_id('a2f20f99-e363-4584-be97-bc33afb1a56c')
def test_crud_snapshot_metadata(self):
diff --git a/tempest/api/volume/v3/test_versions.py b/tempest/api/volume/test_versions.py
similarity index 90%
rename from tempest/api/volume/v3/test_versions.py
rename to tempest/api/volume/test_versions.py
index 20f1657..76f2a99 100644
--- a/tempest/api/volume/v3/test_versions.py
+++ b/tempest/api/volume/test_versions.py
@@ -12,12 +12,14 @@
# License for the specific language governing permissions and limitations
# under the License.
-from tempest.api.volume.v3 import base
+from tempest.api.volume import base
from tempest.lib import decorators
from tempest import test
-class VersionsTest(base.VolumesV3Test):
+class VersionsTest(base.BaseVolumeTest):
+
+ _api_version = 3
@decorators.idempotent_id('77838fc4-b49b-4c64-9533-166762517369')
@test.attr(type='smoke')
diff --git a/tempest/api/volume/test_volume_absolute_limits.py b/tempest/api/volume/test_volume_absolute_limits.py
index 8e3c343..836e489 100644
--- a/tempest/api/volume/test_volume_absolute_limits.py
+++ b/tempest/api/volume/test_volume_absolute_limits.py
@@ -21,14 +21,14 @@
CONF = config.CONF
-class AbsoluteLimitsV2Tests(base.BaseVolumeTest):
+class AbsoluteLimitsTests(base.BaseVolumeTest):
# avoid existing volumes of pre-defined tenant
force_tenant_isolation = True
@classmethod
def resource_setup(cls):
- super(AbsoluteLimitsV2Tests, cls).resource_setup()
+ super(AbsoluteLimitsTests, cls).resource_setup()
# Create a shared volume for tests
cls.volume = cls.create_volume()
diff --git a/tempest/api/volume/test_volume_metadata.py b/tempest/api/volume/test_volume_metadata.py
index bbe1765..b4d22fe 100644
--- a/tempest/api/volume/test_volume_metadata.py
+++ b/tempest/api/volume/test_volume_metadata.py
@@ -19,18 +19,18 @@
from tempest.lib import decorators
-class VolumesV2MetadataTest(base.BaseVolumeTest):
+class VolumesMetadataTest(base.BaseVolumeTest):
@classmethod
def resource_setup(cls):
- super(VolumesV2MetadataTest, cls).resource_setup()
+ super(VolumesMetadataTest, cls).resource_setup()
# Create a volume
cls.volume = cls.create_volume()
def tearDown(self):
# Update the metadata to {}
self.volumes_client.update_volume_metadata(self.volume['id'], {})
- super(VolumesV2MetadataTest, self).tearDown()
+ super(VolumesMetadataTest, self).tearDown()
@decorators.idempotent_id('6f5b125b-f664-44bf-910f-751591fe5769')
def test_crud_volume_metadata(self):
diff --git a/tempest/api/volume/test_volume_transfers.py b/tempest/api/volume/test_volume_transfers.py
index 22cbd85..bfb42c6 100644
--- a/tempest/api/volume/test_volume_transfers.py
+++ b/tempest/api/volume/test_volume_transfers.py
@@ -20,13 +20,13 @@
from tempest.lib import decorators
-class VolumesV2TransfersTest(base.BaseVolumeTest):
+class VolumesTransfersTest(base.BaseVolumeTest):
credentials = ['primary', 'alt', 'admin']
@classmethod
def setup_clients(cls):
- super(VolumesV2TransfersTest, cls).setup_clients()
+ super(VolumesTransfersTest, cls).setup_clients()
cls.client = cls.volumes_client
cls.alt_client = cls.os_alt.volumes_client
diff --git a/tempest/api/volume/test_volumes_actions.py b/tempest/api/volume/test_volumes_actions.py
index 4fc1a69..a2a3d27 100644
--- a/tempest/api/volume/test_volumes_actions.py
+++ b/tempest/api/volume/test_volumes_actions.py
@@ -25,12 +25,11 @@
CONF = config.CONF
-class VolumesV2ActionsTest(base.BaseVolumeTest):
+class VolumesActionsTest(base.BaseVolumeTest):
@classmethod
def setup_clients(cls):
- super(VolumesV2ActionsTest, cls).setup_clients()
- cls.client = cls.volumes_client
+ super(VolumesActionsTest, cls).setup_clients()
if CONF.service_available.glance:
# Check if glance v1 is available to determine which client to use.
if CONF.image_feature_enabled.api_v1:
@@ -44,7 +43,7 @@
@classmethod
def resource_setup(cls):
- super(VolumesV2ActionsTest, cls).resource_setup()
+ super(VolumesActionsTest, cls).resource_setup()
# Create a test shared volume for attach/detach tests
cls.volume = cls.create_volume()
@@ -56,23 +55,23 @@
# Create a server
server = self.create_server(wait_until='ACTIVE')
# Volume is attached and detached successfully from an instance
- self.client.attach_volume(self.volume['id'],
- instance_uuid=server['id'],
- mountpoint='/dev/%s' %
- CONF.compute.volume_device_name)
- waiters.wait_for_volume_resource_status(self.client,
+ self.volumes_client.attach_volume(self.volume['id'],
+ instance_uuid=server['id'],
+ mountpoint='/dev/%s' %
+ CONF.compute.volume_device_name)
+ waiters.wait_for_volume_resource_status(self.volumes_client,
self.volume['id'], 'in-use')
- self.client.detach_volume(self.volume['id'])
- waiters.wait_for_volume_resource_status(self.client,
+ self.volumes_client.detach_volume(self.volume['id'])
+ waiters.wait_for_volume_resource_status(self.volumes_client,
self.volume['id'], 'available')
@decorators.idempotent_id('63e21b4c-0a0c-41f6-bfc3-7c2816815599')
def test_volume_bootable(self):
# Verify that a volume bootable flag is retrieved
for bool_bootable in [True, False]:
- self.client.set_bootable_volume(self.volume['id'],
- bootable=bool_bootable)
- fetched_volume = self.client.show_volume(
+ self.volumes_client.set_bootable_volume(self.volume['id'],
+ bootable=bool_bootable)
+ fetched_volume = self.volumes_client.show_volume(
self.volume['id'])['volume']
# Get Volume information
# NOTE(masayukig): 'bootable' is "true" or "false" in the current
@@ -87,16 +86,18 @@
# Create a server
server = self.create_server(wait_until='ACTIVE')
# Verify that a volume's attachment information is retrieved
- self.client.attach_volume(self.volume['id'],
- instance_uuid=server['id'],
- mountpoint='/dev/%s' %
- CONF.compute.volume_device_name)
- waiters.wait_for_volume_resource_status(self.client, self.volume['id'],
+ self.volumes_client.attach_volume(self.volume['id'],
+ instance_uuid=server['id'],
+ mountpoint='/dev/%s' %
+ CONF.compute.volume_device_name)
+ waiters.wait_for_volume_resource_status(self.volumes_client,
+ self.volume['id'],
'in-use')
- self.addCleanup(waiters.wait_for_volume_resource_status, self.client,
+ self.addCleanup(waiters.wait_for_volume_resource_status,
+ self.volumes_client,
self.volume['id'], 'available')
- self.addCleanup(self.client.detach_volume, self.volume['id'])
- volume = self.client.show_volume(self.volume['id'])['volume']
+ self.addCleanup(self.volumes_client.detach_volume, self.volume['id'])
+ volume = self.volumes_client.show_volume(self.volume['id'])['volume']
self.assertIn('attachments', volume)
attachment = volume['attachments'][0]
@@ -115,7 +116,7 @@
# there is no way to delete it from Cinder, so we delete it from Glance
# using the Glance image_client and from Cinder via tearDownClass.
image_name = data_utils.rand_name(self.__class__.__name__ + '-Image')
- body = self.client.upload_volume(
+ body = self.volumes_client.upload_volume(
self.volume['id'], image_name=image_name,
disk_format=CONF.volume.disk_format)['os-volume_upload_image']
image_id = body["image_id"]
@@ -123,30 +124,30 @@
self.image_client.delete_image,
image_id)
waiters.wait_for_image_status(self.image_client, image_id, 'active')
- waiters.wait_for_volume_resource_status(self.client,
+ waiters.wait_for_volume_resource_status(self.volumes_client,
self.volume['id'], 'available')
@decorators.idempotent_id('92c4ef64-51b2-40c0-9f7e-4749fbaaba33')
def test_reserve_unreserve_volume(self):
# Mark volume as reserved.
- body = self.client.reserve_volume(self.volume['id'])
+ body = self.volumes_client.reserve_volume(self.volume['id'])
# To get the volume info
- body = self.client.show_volume(self.volume['id'])['volume']
+ body = self.volumes_client.show_volume(self.volume['id'])['volume']
self.assertIn('attaching', body['status'])
# Unmark volume as reserved.
- body = self.client.unreserve_volume(self.volume['id'])
+ body = self.volumes_client.unreserve_volume(self.volume['id'])
# To get the volume info
- body = self.client.show_volume(self.volume['id'])['volume']
+ body = self.volumes_client.show_volume(self.volume['id'])['volume']
self.assertIn('available', body['status'])
@decorators.idempotent_id('fff74e1e-5bd3-4b33-9ea9-24c103bc3f59')
def test_volume_readonly_update(self):
for readonly in [True, False]:
# Update volume readonly
- self.client.update_volume_readonly(self.volume['id'],
- readonly=readonly)
+ self.volumes_client.update_volume_readonly(self.volume['id'],
+ readonly=readonly)
# Get Volume information
- fetched_volume = self.client.show_volume(
+ fetched_volume = self.volumes_client.show_volume(
self.volume['id'])['volume']
# NOTE(masayukig): 'readonly' is "True" or "False" in the current
# cinder implementation. So we need to cast boolean values to str
diff --git a/tempest/api/volume/test_volumes_backup.py b/tempest/api/volume/test_volumes_backup.py
index e0b54b5..925beee 100644
--- a/tempest/api/volume/test_volumes_backup.py
+++ b/tempest/api/volume/test_volumes_backup.py
@@ -13,6 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
+import testtools
from testtools import matchers
from tempest.api.volume import base
@@ -25,11 +26,11 @@
CONF = config.CONF
-class VolumesBackupsV2Test(base.BaseVolumeTest):
+class VolumesBackupsTest(base.BaseVolumeTest):
@classmethod
def skip_checks(cls):
- super(VolumesBackupsV2Test, cls).skip_checks()
+ super(VolumesBackupsTest, cls).skip_checks()
if not CONF.volume_feature_enabled.backup:
raise cls.skipException("Cinder backup feature disabled")
@@ -65,7 +66,8 @@
description = data_utils.rand_name("volume-backup-description")
backup = self.create_backup(volume_id=volume['id'],
name=backup_name,
- description=description)
+ description=description,
+ container='container')
self.assertEqual(backup_name, backup['name'])
waiters.wait_for_volume_resource_status(self.volumes_client,
volume['id'], 'available')
@@ -74,6 +76,7 @@
backup = self.backups_client.show_backup(backup['id'])['backup']
self.assertEqual(backup_name, backup['name'])
self.assertEqual(description, backup['description'])
+ self.assertEqual('container', backup['container'])
# Get all backups with detail
backups = self.backups_client.list_backups(
@@ -113,6 +116,8 @@
name=backup_name, force=True)
self.assertEqual(backup_name, backup['name'])
+ @testtools.skipUnless(CONF.service_available.glance,
+ "Glance is not available")
@decorators.idempotent_id('2a8ba340-dff2-4511-9db7-646f07156b15')
def test_bootable_volume_backup_and_restore(self):
# Create volume from image
diff --git a/tempest/api/volume/test_volumes_clone.py b/tempest/api/volume/test_volumes_clone.py
index 620f3d4..a6bbb0a 100644
--- a/tempest/api/volume/test_volumes_clone.py
+++ b/tempest/api/volume/test_volumes_clone.py
@@ -13,6 +13,8 @@
# License for the specific language governing permissions and limitations
# under the License.
+import testtools
+
from tempest.api.volume import base
from tempest import config
from tempest.lib import decorators
@@ -21,11 +23,11 @@
CONF = config.CONF
-class VolumesV2CloneTest(base.BaseVolumeTest):
+class VolumesCloneTest(base.BaseVolumeTest):
@classmethod
def skip_checks(cls):
- super(VolumesV2CloneTest, cls).skip_checks()
+ super(VolumesCloneTest, cls).skip_checks()
if not CONF.volume_feature_enabled.clone:
raise cls.skipException("Cinder volume clones are disabled")
@@ -45,6 +47,8 @@
self.assertEqual(volume['source_volid'], src_vol['id'])
self.assertEqual(volume['size'], src_size + 1)
+ @testtools.skipUnless(CONF.service_available.glance,
+ "Glance is not available")
@decorators.idempotent_id('cbbcd7c6-5a6c-481a-97ac-ca55ab715d16')
def test_create_from_bootable_volume(self):
# Create volume from image
diff --git a/tempest/api/volume/test_volumes_clone_negative.py b/tempest/api/volume/test_volumes_clone_negative.py
index 28327f4..9169c19 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 VolumesV2CloneNegativeTest(base.BaseVolumeTest):
+class VolumesCloneNegativeTest(base.BaseVolumeTest):
@classmethod
def skip_checks(cls):
- super(VolumesV2CloneNegativeTest, cls).skip_checks()
+ super(VolumesCloneNegativeTest, cls).skip_checks()
if not CONF.volume_feature_enabled.clone:
raise cls.skipException("Cinder volume clones are disabled")
diff --git a/tempest/api/volume/test_volumes_extend.py b/tempest/api/volume/test_volumes_extend.py
index 3b09b60..84ecb22 100644
--- a/tempest/api/volume/test_volumes_extend.py
+++ b/tempest/api/volume/test_volumes_extend.py
@@ -18,7 +18,7 @@
from tempest.lib import decorators
-class VolumesV2ExtendTest(base.BaseVolumeTest):
+class VolumesExtendTest(base.BaseVolumeTest):
@decorators.idempotent_id('9a36df71-a257-43a5-9555-dc7c88e66e0e')
def test_volume_extend(self):
diff --git a/tempest/api/volume/test_volumes_get.py b/tempest/api/volume/test_volumes_get.py
index 9eff97a..65027bf 100644
--- a/tempest/api/volume/test_volumes_get.py
+++ b/tempest/api/volume/test_volumes_get.py
@@ -26,7 +26,7 @@
CONF = config.CONF
-class VolumesV2GetTest(base.BaseVolumeTest):
+class VolumesGetTest(base.BaseVolumeTest):
def _volume_create_get_update_delete(self, **kwargs):
# Create a volume, Get it's details and Delete the volume
diff --git a/tempest/api/volume/test_volumes_list.py b/tempest/api/volume/test_volumes_list.py
index a852cea..0570797 100644
--- a/tempest/api/volume/test_volumes_list.py
+++ b/tempest/api/volume/test_volumes_list.py
@@ -23,7 +23,7 @@
from tempest import test
-class VolumesV2ListTestJSON(base.BaseVolumeTest):
+class VolumesListTestJSON(base.BaseVolumeTest):
# NOTE: This test creates a number of 1G volumes. To run successfully,
# ensure that the backing file for the volume group that Nova uses
# has space for at least 3 1G volumes!
@@ -57,7 +57,7 @@
@classmethod
def resource_setup(cls):
- super(VolumesV2ListTestJSON, cls).resource_setup()
+ super(VolumesListTestJSON, cls).resource_setup()
cls.name = cls.VOLUME_FIELDS[1]
# Create 3 test volumes
cls.volume_list = []
@@ -78,11 +78,7 @@
params=params)['volumes']
# Validating params of fetched volumes
- # In v2, only list detail view includes items in params.
- # In v1, list view and list detail view are same. So the
- # following check should be run when 'with_detail' is True
- # or v1 tests.
- if with_detail or self._api_version == 1:
+ if with_detail:
for volume in fetched_vol_list:
for key in params:
msg = "Failed to list volumes %s by %s" % \
diff --git a/tempest/api/volume/test_volumes_negative.py b/tempest/api/volume/test_volumes_negative.py
index 8990a15..609a031 100644
--- a/tempest/api/volume/test_volumes_negative.py
+++ b/tempest/api/volume/test_volumes_negative.py
@@ -20,11 +20,11 @@
from tempest import test
-class VolumesV2NegativeTest(base.BaseVolumeTest):
+class VolumesNegativeTest(base.BaseVolumeTest):
@classmethod
def resource_setup(cls):
- super(VolumesV2NegativeTest, cls).resource_setup()
+ super(VolumesNegativeTest, cls).resource_setup()
# Create a test shared instance and volume for attach/detach tests
cls.volume = cls.create_volume()
@@ -47,99 +47,70 @@
@test.attr(type=['negative'])
@decorators.idempotent_id('1ed83a8a-682d-4dfb-a30e-ee63ffd6c049')
def test_create_volume_with_invalid_size(self):
- # Should not be able to create volume with invalid size
- # in request
- v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
- params = {'name': v_name}
+ # Should not be able to create volume with invalid size in request
self.assertRaises(lib_exc.BadRequest,
- self.volumes_client.create_volume,
- size='#$%', params=params)
+ self.volumes_client.create_volume, size='#$%')
@test.attr(type=['negative'])
@decorators.idempotent_id('9387686f-334f-4d31-a439-33494b9e2683')
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')
- params = {'name': v_name}
self.assertRaises(lib_exc.BadRequest,
- self.volumes_client.create_volume,
- size='', params=params)
+ self.volumes_client.create_volume, size='')
@test.attr(type=['negative'])
@decorators.idempotent_id('41331caa-eaf4-4001-869d-bc18c1869360')
def test_create_volume_with_size_zero(self):
# Should not be able to create volume with size zero
- v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
- params = {'name': v_name}
self.assertRaises(lib_exc.BadRequest,
- self.volumes_client.create_volume,
- size='0', params=params)
+ self.volumes_client.create_volume, size='0')
@test.attr(type=['negative'])
@decorators.idempotent_id('8b472729-9eba-446e-a83b-916bdb34bef7')
def test_create_volume_with_size_negative(self):
# Should not be able to create volume with size negative
- v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
- params = {'name': v_name}
self.assertRaises(lib_exc.BadRequest,
- self.volumes_client.create_volume,
- size='-1', params=params)
+ self.volumes_client.create_volume, size='-1')
@test.attr(type=['negative'])
@decorators.idempotent_id('10254ed8-3849-454e-862e-3ab8e6aa01d2')
def test_create_volume_with_nonexistent_volume_type(self):
# Should not be able to create volume with non-existent volume type
- v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
- params = {'name': v_name}
self.assertRaises(lib_exc.NotFound, self.volumes_client.create_volume,
- size='1', volume_type=data_utils.rand_uuid(),
- params=params)
+ size='1', volume_type=data_utils.rand_uuid())
@test.attr(type=['negative'])
@decorators.idempotent_id('0c36f6ae-4604-4017-b0a9-34fdc63096f9')
def test_create_volume_with_nonexistent_snapshot_id(self):
# Should not be able to create volume with non-existent snapshot
- v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
- params = {'name': v_name}
self.assertRaises(lib_exc.NotFound, self.volumes_client.create_volume,
- size='1', snapshot_id=data_utils.rand_uuid(),
- params=params)
+ size='1', snapshot_id=data_utils.rand_uuid())
@test.attr(type=['negative'])
@decorators.idempotent_id('47c73e08-4be8-45bb-bfdf-0c4e79b88344')
def test_create_volume_with_nonexistent_source_volid(self):
# Should not be able to create volume with non-existent source volume
- v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
- params = {'name': v_name}
self.assertRaises(lib_exc.NotFound, self.volumes_client.create_volume,
- size='1', source_volid=data_utils.rand_uuid(),
- params=params)
+ size='1', source_volid=data_utils.rand_uuid())
@test.attr(type=['negative'])
@decorators.idempotent_id('0186422c-999a-480e-a026-6a665744c30c')
def test_update_volume_with_nonexistent_volume_id(self):
- v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
- params = {'name': v_name}
self.assertRaises(lib_exc.NotFound, self.volumes_client.update_volume,
- volume_id=data_utils.rand_uuid(), params=params)
+ volume_id=data_utils.rand_uuid())
@test.attr(type=['negative'])
@decorators.idempotent_id('e66e40d6-65e6-4e75-bdc7-636792fa152d')
def test_update_volume_with_invalid_volume_id(self):
- v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
- params = {'name': v_name}
self.assertRaises(lib_exc.NotFound, self.volumes_client.update_volume,
- volume_id=data_utils.rand_name('invalid'),
- params=params)
+ volume_id=data_utils.rand_name('invalid'))
@test.attr(type=['negative'])
@decorators.idempotent_id('72aeca85-57a5-4c1f-9057-f320f9ea575b')
def test_update_volume_with_empty_volume_id(self):
- v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
- params = {'name': v_name}
self.assertRaises(lib_exc.NotFound, self.volumes_client.update_volume,
- volume_id='', params=params)
+ volume_id='')
@test.attr(type=['negative'])
@decorators.idempotent_id('30799cfd-7ee4-446c-b66c-45b383ed211b')
diff --git a/tempest/api/volume/test_volumes_snapshots.py b/tempest/api/volume/test_volumes_snapshots.py
index 82f456b..504875b 100644
--- a/tempest/api/volume/test_volumes_snapshots.py
+++ b/tempest/api/volume/test_volumes_snapshots.py
@@ -21,17 +21,17 @@
CONF = config.CONF
-class VolumesV2SnapshotTestJSON(base.BaseVolumeTest):
+class VolumesSnapshotTestJSON(base.BaseVolumeTest):
@classmethod
def skip_checks(cls):
- super(VolumesV2SnapshotTestJSON, cls).skip_checks()
+ super(VolumesSnapshotTestJSON, cls).skip_checks()
if not CONF.volume_feature_enabled.snapshot:
raise cls.skipException("Cinder volume snapshots are disabled")
@classmethod
def resource_setup(cls):
- super(VolumesV2SnapshotTestJSON, cls).resource_setup()
+ super(VolumesSnapshotTestJSON, cls).resource_setup()
cls.volume_origin = cls.create_volume()
@decorators.idempotent_id('b467b54c-07a4-446d-a1cf-651dedcc3ff1')
diff --git a/tempest/api/volume/test_volumes_snapshots_list.py b/tempest/api/volume/test_volumes_snapshots_list.py
index 0ea8ec7..68eb181 100644
--- a/tempest/api/volume/test_volumes_snapshots_list.py
+++ b/tempest/api/volume/test_volumes_snapshots_list.py
@@ -17,17 +17,17 @@
CONF = config.CONF
-class VolumesV2SnapshotListTestJSON(base.BaseVolumeTest):
+class VolumesSnapshotListTestJSON(base.BaseVolumeTest):
@classmethod
def skip_checks(cls):
- super(VolumesV2SnapshotListTestJSON, cls).skip_checks()
+ super(VolumesSnapshotListTestJSON, cls).skip_checks()
if not CONF.volume_feature_enabled.snapshot:
raise cls.skipException("Cinder volume snapshots are disabled")
@classmethod
def resource_setup(cls):
- super(VolumesV2SnapshotListTestJSON, cls).resource_setup()
+ super(VolumesSnapshotListTestJSON, cls).resource_setup()
volume_origin = cls.create_volume()
# Create snapshots with params
for _ in range(2):
@@ -97,7 +97,6 @@
self._list_snapshots_by_param_limit(limit=100000,
expected_elements=len(snap_list))
- @decorators.skip_because(bug='1540893')
@decorators.idempotent_id('e3b44b7f-ae87-45b5-8a8c-66110eb24d0a')
def test_snapshot_list_param_limit_equals_zero(self):
# List returns zero elements
diff --git a/tempest/api/volume/test_volumes_snapshots_negative.py b/tempest/api/volume/test_volumes_snapshots_negative.py
index 285d3f5..2e30d04 100644
--- a/tempest/api/volume/test_volumes_snapshots_negative.py
+++ b/tempest/api/volume/test_volumes_snapshots_negative.py
@@ -20,11 +20,11 @@
CONF = config.CONF
-class VolumesV2SnapshotNegativeTestJSON(base.BaseVolumeTest):
+class VolumesSnapshotNegativeTestJSON(base.BaseVolumeTest):
@classmethod
def skip_checks(cls):
- super(VolumesV2SnapshotNegativeTestJSON, cls).skip_checks()
+ super(VolumesSnapshotNegativeTestJSON, cls).skip_checks()
if not CONF.volume_feature_enabled.snapshot:
raise cls.skipException("Cinder volume snapshots are disabled")
@@ -68,3 +68,17 @@
self.assertRaises(lib_exc.BadRequest,
self.snapshots_client.list_snapshots,
limit='invalid')
+
+ @test.attr(type=['negative'])
+ @decorators.idempotent_id('27b5f37f-bf69-4e8c-986e-c44f3d6819b8')
+ def test_list_snapshots_invalid_param_sort(self):
+ self.assertRaises(lib_exc.BadRequest,
+ self.snapshots_client.list_snapshots,
+ sort_key='invalid')
+
+ @test.attr(type=['negative'])
+ @decorators.idempotent_id('b68deeda-ca79-4a32-81af-5c51179e553a')
+ def test_list_snapshots_invalid_param_marker(self):
+ self.assertRaises(lib_exc.NotFound,
+ self.snapshots_client.list_snapshots,
+ marker=data_utils.rand_uuid())
diff --git a/tempest/api/volume/v2/test_volumes_list.py b/tempest/api/volume/v2/test_volumes_list.py
index d2328c8..e7adcd6 100644
--- a/tempest/api/volume/v2/test_volumes_list.py
+++ b/tempest/api/volume/v2/test_volumes_list.py
@@ -22,8 +22,8 @@
from tempest.lib import decorators
-class VolumesV2ListTestJSON(base.BaseVolumeTest):
- """volumes v2 specific tests.
+class VolumesListTestJSON(base.BaseVolumeTest):
+ """volumes tests.
This test creates a number of 1G volumes. To run successfully,
ensure that the backing file for the volume group that Nova uses
@@ -34,7 +34,7 @@
@classmethod
def resource_setup(cls):
- super(VolumesV2ListTestJSON, cls).resource_setup()
+ super(VolumesListTestJSON, cls).resource_setup()
# Create 3 test volumes
# NOTE(zhufl): When using pre-provisioned credentials, the project
diff --git a/tempest/api/volume/v2/test_volumes_snapshots_list.py b/tempest/api/volume/v2/test_volumes_snapshots_list.py
index d385f65..bfed67b 100644
--- a/tempest/api/volume/v2/test_volumes_snapshots_list.py
+++ b/tempest/api/volume/v2/test_volumes_snapshots_list.py
@@ -20,17 +20,17 @@
CONF = config.CONF
-class VolumesV2SnapshotListTestJSON(base.BaseVolumeTest):
+class VolumesSnapshotListTestJSON(base.BaseVolumeTest):
@classmethod
def skip_checks(cls):
- super(VolumesV2SnapshotListTestJSON, cls).skip_checks()
+ super(VolumesSnapshotListTestJSON, cls).skip_checks()
if not CONF.volume_feature_enabled.snapshot:
raise cls.skipException("Cinder volume snapshots are disabled")
@classmethod
def resource_setup(cls):
- super(VolumesV2SnapshotListTestJSON, cls).resource_setup()
+ super(VolumesSnapshotListTestJSON, cls).resource_setup()
cls.snapshot_id_list = []
# Create a volume
volume_origin = cls.create_volume()
@@ -46,7 +46,7 @@
self.assertNotEmpty(snap_list)
if sort_key is 'display_name':
sort_key = 'name'
- # Note: On Cinder V2 API, 'display_name' works as a sort key
+ # Note: On Cinder API, 'display_name' works as a sort key
# on a request, a volume name appears as 'name' on the response.
# So Tempest needs to change the key name here for this inconsistent
# API behavior.
diff --git a/tempest/api/volume/v2/test_volumes_snapshots_negative.py b/tempest/api/volume/v2/test_volumes_snapshots_negative.py
deleted file mode 100644
index 4608b07..0000000
--- a/tempest/api/volume/v2/test_volumes_snapshots_negative.py
+++ /dev/null
@@ -1,46 +0,0 @@
-# Copyright 2017 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 tempest.api.volume import base
-from tempest import config
-from tempest.lib.common.utils import data_utils
-from tempest.lib import decorators
-from tempest.lib import exceptions as lib_exc
-from tempest import test
-
-CONF = config.CONF
-
-
-class VolumesV2SnapshotNegativeTest(base.BaseVolumeTest):
-
- @classmethod
- def skip_checks(cls):
- super(VolumesV2SnapshotNegativeTest, cls).skip_checks()
- if not CONF.volume_feature_enabled.snapshot:
- raise cls.skipException("Cinder volume snapshots are disabled")
-
- @test.attr(type=['negative'])
- @decorators.idempotent_id('27b5f37f-bf69-4e8c-986e-c44f3d6819b8')
- def test_list_snapshots_invalid_param_sort(self):
- self.assertRaises(lib_exc.BadRequest,
- self.snapshots_client.list_snapshots,
- sort_key='invalid')
-
- @test.attr(type=['negative'])
- @decorators.idempotent_id('b68deeda-ca79-4a32-81af-5c51179e553a')
- def test_list_snapshots_invalid_param_marker(self):
- self.assertRaises(lib_exc.NotFound,
- self.snapshots_client.list_snapshots,
- marker=data_utils.rand_uuid())
diff --git a/tempest/api/volume/v3/__init__.py b/tempest/api/volume/v3/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/api/volume/v3/__init__.py
+++ /dev/null
diff --git a/tempest/api/volume/v3/base.py b/tempest/api/volume/v3/base.py
deleted file mode 100644
index 7f76e6f..0000000
--- a/tempest/api/volume/v3/base.py
+++ /dev/null
@@ -1,66 +0,0 @@
-# Copyright 2016 Andrew Kerr
-# 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 api_microversion_fixture
-from tempest.api.volume import base
-from tempest import config
-from tempest.lib.common import api_version_utils
-
-CONF = config.CONF
-
-
-class VolumesV3Test(api_version_utils.BaseMicroversionTest,
- base.BaseVolumeTest):
- """Base test case class for all v3 Cinder API tests."""
-
- _api_version = 3
-
- @classmethod
- def skip_checks(cls):
- super(VolumesV3Test, cls).skip_checks()
- api_version_utils.check_skip_with_microversion(
- cls.min_microversion, cls.max_microversion,
- CONF.volume.min_microversion, CONF.volume.max_microversion)
-
- @classmethod
- def resource_setup(cls):
- super(VolumesV3Test, cls).resource_setup()
- cls.request_microversion = (
- api_version_utils.select_request_microversion(
- cls.min_microversion,
- CONF.volume.min_microversion))
-
- @classmethod
- def setup_clients(cls):
- super(VolumesV3Test, cls).setup_clients()
- cls.messages_client = cls.os.volume_v3_messages_client
- cls.versions_client = cls.os.volume_v3_versions_client
-
- def setUp(self):
- super(VolumesV3Test, self).setUp()
- self.useFixture(api_microversion_fixture.APIMicroversionFixture(
- self.request_microversion))
-
-
-class VolumesV3AdminTest(VolumesV3Test,
- base.BaseVolumeAdminTest):
- """Base test case class for all v3 Volume Admin API tests."""
-
- credentials = ['primary', 'admin']
-
- @classmethod
- def setup_clients(cls):
- super(VolumesV3AdminTest, cls).setup_clients()
- 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 0654110..71c3d41 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -229,6 +229,8 @@
self.groups_client = self.identity_v3.GroupsClient(**params_v3)
self.identity_versions_v3_client = self.identity_v3.VersionsClient(
**params_v3)
+ self.oauth_consumers_client = self.identity_v3.OAUTHConsumerClient(
+ **params_v3)
# Token clients do not use the catalog. They only need default_params.
# They read auth_url, so they should only be set if the corresponding
@@ -262,6 +264,7 @@
self.snapshot_manage_v2_client = self.volume_v2.SnapshotManageClient()
self.snapshots_client = self.volume_v1.SnapshotsClient()
self.snapshots_v2_client = self.volume_v2.SnapshotsClient()
+ self.volume_manage_v2_client = self.volume_v2.VolumeManageClient()
self.volumes_client = self.volume_v1.VolumesClient()
self.volumes_v2_client = self.volume_v2.VolumesClient()
self.volume_v3_messages_client = self.volume_v3.MessagesClient()
diff --git a/tempest/common/compute.py b/tempest/common/compute.py
index b2667e5..38daffe 100644
--- a/tempest/common/compute.py
+++ b/tempest/common/compute.py
@@ -14,8 +14,13 @@
# limitations under the License.
import base64
+import socket
+import struct
import textwrap
+import six
+from six.moves.urllib import parse as urlparse
+
from oslo_log import log as logging
from oslo_utils import excutils
@@ -25,6 +30,11 @@
from tempest.lib.common import rest_client
from tempest.lib.common.utils import data_utils
+if six.PY2:
+ ord_func = ord
+else:
+ ord_func = int
+
CONF = config.CONF
LOG = logging.getLogger(__name__)
@@ -132,11 +142,7 @@
if volume_backed:
volume_name = data_utils.rand_name(__name__ + '-volume')
volumes_client = clients.volumes_v2_client
- name_field = 'name'
- if not CONF.volume_feature_enabled.api_v2:
- volumes_client = clients.volumes_client
- name_field = 'display_name'
- params = {name_field: volume_name,
+ params = {'name': volume_name,
'imageRef': image_id,
'size': CONF.volume.volume_size}
volume = volumes_client.create_volume(**params)
@@ -226,3 +232,87 @@
servers_client.shelve_offload_server(server_id)
waiters.wait_for_server_status(servers_client, server_id,
'SHELVED_OFFLOADED')
+
+
+def create_websocket(url):
+ url = urlparse.urlparse(url)
+ client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ client_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ client_socket.connect((url.hostname, url.port))
+ # Turn the Socket into a WebSocket to do the communication
+ return _WebSocket(client_socket, url)
+
+
+class _WebSocket(object):
+ def __init__(self, client_socket, url):
+ """Contructor for the WebSocket wrapper to the socket."""
+ self._socket = client_socket
+ # Upgrade the HTTP connection to a WebSocket
+ self._upgrade(url)
+
+ def receive_frame(self):
+ """Wrapper for receiving data to parse the WebSocket frame format"""
+ # We need to loop until we either get some bytes back in the frame
+ # or no data was received (meaning the socket was closed). This is
+ # done to handle the case where we get back some empty frames
+ while True:
+ header = self._socket.recv(2)
+ # If we didn't receive any data, just return None
+ if len(header) == 0:
+ return None
+ # We will make the assumption that we are only dealing with
+ # frames less than 125 bytes here (for the negotiation) and
+ # that only the 2nd byte contains the length, and since the
+ # server doesn't do masking, we can just read the data length
+ if ord_func(header[1]) & 127 > 0:
+ return self._socket.recv(ord_func(header[1]) & 127)
+
+ def send_frame(self, data):
+ """Wrapper for sending data to add in the WebSocket frame format."""
+ frame_bytes = list()
+ # For the first byte, want to say we are sending binary data (130)
+ frame_bytes.append(130)
+ # Only sending negotiation data so don't need to worry about > 125
+ # We do need to add the bit that says we are masking the data
+ frame_bytes.append(len(data) | 128)
+ # We don't really care about providing a random mask for security
+ # So we will just hard-code a value since a test program
+ mask = [7, 2, 1, 9]
+ for i in range(len(mask)):
+ frame_bytes.append(mask[i])
+ # Mask each of the actual data bytes that we are going to send
+ for i in range(len(data)):
+ frame_bytes.append(ord_func(data[i]) ^ mask[i % 4])
+ # Convert our integer list to a binary array of bytes
+ frame_bytes = struct.pack('!%iB' % len(frame_bytes), * frame_bytes)
+ self._socket.sendall(frame_bytes)
+
+ def close(self):
+ """Helper method to close the connection."""
+ # Close down the real socket connection and exit the test program
+ if self._socket is not None:
+ self._socket.shutdown(1)
+ self._socket.close()
+ self._socket = None
+
+ def _upgrade(self, url):
+ """Upgrade the HTTP connection to a WebSocket and verify."""
+ # The real request goes to the /websockify URI always
+ reqdata = 'GET /websockify HTTP/1.1\r\n'
+ reqdata += 'Host: %s:%s\r\n' % (url.hostname, url.port)
+ # Tell the HTTP Server to Upgrade the connection to a WebSocket
+ reqdata += 'Upgrade: websocket\r\nConnection: Upgrade\r\n'
+ # The token=xxx is sent as a Cookie not in the URI
+ reqdata += 'Cookie: %s\r\n' % url.query
+ # Use a hard-coded WebSocket key since a test program
+ reqdata += 'Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\r\n'
+ reqdata += 'Sec-WebSocket-Version: 13\r\n'
+ # We are choosing to use binary even though browser may do Base64
+ reqdata += 'Sec-WebSocket-Protocol: binary\r\n\r\n'
+ # Send the HTTP GET request and get the response back
+ self._socket.sendall(reqdata.encode('utf8'))
+ self.response = data = self._socket.recv(4096)
+ # Loop through & concatenate all of the data in the response body
+ while len(data) > 0 and self.response.find(b'\r\n\r\n') < 0:
+ data = self._socket.recv(4096)
+ self.response += data
diff --git a/tempest/common/utils/linux/remote_client.py b/tempest/common/utils/linux/remote_client.py
index 6dfc579..9319d2a 100644
--- a/tempest/common/utils/linux/remote_client.py
+++ b/tempest/common/utils/linux/remote_client.py
@@ -50,6 +50,8 @@
ping_count=CONF.validation.ping_count,
ping_size=CONF.validation.ping_size)
+ # Note that this method will not work on SLES11 guests, as they do
+ # not support the TYPE column on lsblk
def get_disks(self):
# Select root disk devices as shown by lsblk
command = 'lsblk -lb --nodeps'
@@ -79,12 +81,6 @@
cmd = 'sudo sh -c "echo \\"%s\\" >/dev/console"' % message
return self.exec_command(cmd)
- def set_mac_address(self, nic, address):
- self.set_nic_state(nic=nic, state="down")
- cmd = "sudo ip link set dev {0} address {1}".format(nic, address)
- self.exec_command(cmd)
- self.set_nic_state(nic=nic, state="up")
-
def get_mac_address(self, nic=""):
show_nic = "show {nic} ".format(nic=nic) if nic else ""
cmd = "ip addr %s| awk '/ether/ {print $2}'" % show_nic
@@ -100,15 +96,6 @@
nic = self.exec_command(cmd)
return nic.strip().strip(":").lower()
- def assign_static_ip(self, nic, addr, network_mask_bits=28):
- cmd = "sudo ip addr add {ip}/{mask} dev {nic}".format(
- ip=addr, mask=network_mask_bits, nic=nic)
- return self.exec_command(cmd)
-
- def set_nic_state(self, nic, state="up"):
- cmd = "sudo ip link set {nic} {state}".format(nic=nic, state=state)
- return self.exec_command(cmd)
-
def get_dns_servers(self):
cmd = 'cat /etc/resolv.conf'
resolve_file = self.exec_command(cmd).strip().split('\n')
diff --git a/tempest/common/waiters.py b/tempest/common/waiters.py
index 3e5600c..9c83c99 100644
--- a/tempest/common/waiters.py
+++ b/tempest/common/waiters.py
@@ -261,16 +261,16 @@
time.sleep(client.build_interval)
-def wait_for_interface_status(client, server, port_id, status):
+def wait_for_interface_status(client, server_id, port_id, status):
"""Waits for an interface to reach a given status."""
- body = (client.show_interface(server, port_id)
+ body = (client.show_interface(server_id, 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)
+ body = (client.show_interface(server_id, port_id)
['interfaceAttachment'])
interface_status = body['port_state']
diff --git a/tempest/config.py b/tempest/config.py
index 93b8ab2..35eb187 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -291,7 +291,9 @@
cfg.StrOpt('volume_device_name',
default='vdb',
help="Expected device name when a volume is attached to "
- "an instance"),
+ "an instance. Not all hypervisors guarantee that they "
+ "will respect the user defined device name, tests may "
+ "fail if inappropriate device name is set."),
cfg.IntOpt('shelved_offload_time',
default=0,
help='Time in seconds before a shelved instance is eligible '
@@ -503,7 +505,7 @@
"users can specify."),
cfg.ListOpt('disk_formats',
default=['ami', 'ari', 'aki', 'vhd', 'vmdk', 'raw', 'qcow2',
- 'vdi', 'iso'],
+ 'vdi', 'iso', 'vhdx'],
help="A list of image's disk formats "
"users can specify.")
]
@@ -619,7 +621,10 @@
"in L3 agent scheduler test. Extra resources need to be "
"provisioned in order to bind router to L3 agent in the "
"Liberty release or older, and are not required since "
- "the Mitaka release.")
+ "the Mitaka release.",
+ deprecated_for_removal=True,
+ deprecated_reason='This config switch was added for Liberty '
+ 'which is not supported anymore.')
]
network_feature_group = cfg.OptGroup(name='network-feature-enabled',
@@ -681,9 +686,7 @@
help='Default IP version for ssh connections.'),
cfg.IntOpt('ping_timeout',
default=120,
- help='Timeout in seconds to wait for ping to succeed.',
- deprecated_opts=[cfg.DeprecatedOpt('ping_timeout',
- group='compute')]),
+ help='Timeout in seconds to wait for ping to succeed.'),
cfg.IntOpt('connect_timeout',
default=60,
help='Timeout in seconds to wait for the TCP connection to be '
@@ -693,13 +696,7 @@
help='Timeout in seconds to wait for the ssh banner.'),
cfg.StrOpt('image_ssh_user',
default="root",
- help="User name used to authenticate to an instance.",
- deprecated_opts=[cfg.DeprecatedOpt('image_ssh_user',
- group='compute'),
- cfg.DeprecatedOpt('ssh_user',
- group='compute'),
- cfg.DeprecatedOpt('ssh_user',
- group='scenario')]),
+ help="User name used to authenticate to an instance."),
cfg.StrOpt('image_ssh_password',
default="password",
help="Password used to authenticate to an instance."),
@@ -724,9 +721,7 @@
cfg.StrOpt('network_for_ssh',
default='public',
help="Network used for SSH connections. Ignored if "
- "connect_method=floating.",
- deprecated_opts=[cfg.DeprecatedOpt('network_for_ssh',
- group='compute')]),
+ "connect_method=floating."),
]
volume_group = cfg.OptGroup(name='volume',
@@ -770,6 +765,12 @@
cfg.IntOpt('volume_size',
default=1,
help='Default size in GB for volumes created by volumes tests'),
+ cfg.ListOpt('manage_volume_ref',
+ default=['source-name', 'volume-%s'],
+ help="A reference to existing volume for volume manage. "
+ "It contains two elements, the first is ref type "
+ "(like 'source-name', 'source-id', etc), the second is "
+ "volume name template used in storage backend"),
cfg.StrOpt('min_microversion',
default=None,
help="Lower version of the test target microversion range. "
@@ -809,6 +810,9 @@
cfg.BoolOpt('manage_snapshot',
default=False,
help='Runs Cinder manage snapshot tests'),
+ cfg.BoolOpt('manage_volume',
+ default=False,
+ help='Runs Cinder manage volume tests'),
cfg.ListOpt('api_extensions',
default=['all'],
help='A list of enabled volume extensions with a special '
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 4ccca6f..33a7757 100644
--- a/tempest/lib/api_schema/response/compute/v2_1/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_1/servers.py
@@ -100,8 +100,10 @@
'id': {'type': 'string'},
'links': parameter_types.links
},
- 'additionalProperties': False,
- 'required': ['id', 'links']
+ # NOTE(gmann): This will be empty object if there is no
+ # flavor info present in DB. This can happen when flavor info is
+ # deleted after server creation.
+ 'additionalProperties': False
},
'fault': {
'type': 'object',
@@ -564,3 +566,19 @@
update_attached_volume = {
'status_code': [202]
}
+
+evacuate_server = {
+ 'status_code': [200]
+}
+
+evacuate_server_with_admin_pass = {
+ 'status_code': [200],
+ 'response_body': {
+ 'type': 'object',
+ 'properties': {
+ 'adminPass': {'type': 'string'}
+ },
+ 'additionalProperties': False,
+ 'required': ['adminPass']
+ }
+}
diff --git a/tempest/lib/cmd/check_uuid.py b/tempest/lib/cmd/check_uuid.py
index eafde44..e911776 100755
--- a/tempest/lib/cmd/check_uuid.py
+++ b/tempest/lib/cmd/check_uuid.py
@@ -29,7 +29,7 @@
DECORATOR_MODULE = 'decorators'
DECORATOR_NAME = 'idempotent_id'
DECORATOR_IMPORT = 'tempest.%s' % DECORATOR_MODULE
-IMPORT_LINE = 'from tempest import %s' % DECORATOR_MODULE
+IMPORT_LINE = 'from tempest.lib import %s' % DECORATOR_MODULE
DECORATOR_TEMPLATE = "@%s.%s('%%s')" % (DECORATOR_MODULE,
DECORATOR_NAME)
UNIT_TESTS_EXCLUDE = 'tempest.tests'
diff --git a/tempest/lib/services/compute/servers_client.py b/tempest/lib/services/compute/servers_client.py
index b37afb3..0d355a1 100644
--- a/tempest/lib/services/compute/servers_client.py
+++ b/tempest/lib/services/compute/servers_client.py
@@ -814,3 +814,19 @@
schema = self.get_schema(self.schema_versions_info)
self.validate_response(schema.delete_tag, resp, body)
return rest_client.ResponseBody(resp, body)
+
+ def evacuate_server(self, server_id, **kwargs):
+ """Evacuate the given server.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/compute/#evacuate-server-evacuate-action
+ """
+ if self.enable_instance_password:
+ evacuate_schema = schema.evacuate_server_with_admin_pass
+ else:
+ evacuate_schema = schema.evacuate_server
+
+ return self.action(server_id, 'evacuate',
+ evacuate_schema,
+ **kwargs)
diff --git a/tempest/lib/services/identity/v3/__init__.py b/tempest/lib/services/identity/v3/__init__.py
index 88801e7..1489b50 100644
--- a/tempest/lib/services/identity/v3/__init__.py
+++ b/tempest/lib/services/identity/v3/__init__.py
@@ -20,6 +20,8 @@
from tempest.lib.services.identity.v3.identity_client import IdentityClient
from tempest.lib.services.identity.v3.inherited_roles_client import \
InheritedRolesClient
+from tempest.lib.services.identity.v3.oauth_consumers_client import \
+ OAUTHConsumerClient
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
@@ -36,4 +38,5 @@
'GroupsClient', 'IdentityClient', 'InheritedRolesClient',
'PoliciesClient', 'ProjectsClient', 'RegionsClient',
'RoleAssignmentsClient', 'RolesClient', 'ServicesClient',
- 'V3TokenClient', 'TrustsClient', 'UsersClient', 'VersionsClient']
+ 'V3TokenClient', 'TrustsClient', 'UsersClient', 'VersionsClient',
+ 'OAUTHConsumerClient']
diff --git a/tempest/lib/services/identity/v3/oauth_consumers_client.py b/tempest/lib/services/identity/v3/oauth_consumers_client.py
new file mode 100644
index 0000000..582c9e0
--- /dev/null
+++ b/tempest/lib/services/identity/v3/oauth_consumers_client.py
@@ -0,0 +1,95 @@
+# Copyright 2017 AT&T 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 oslo_serialization import jsonutils as json
+
+from tempest.lib.common import rest_client
+
+
+class OAUTHConsumerClient(rest_client.RestClient):
+ api_version = "v3"
+
+ def create_consumer(self, description=None):
+ """Creates a consumer.
+
+ :param str description: Optional field to add notes about the consumer
+
+ For more information, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/identity/v3-ext/#create-consumer
+ """
+ post_body = {"description": description}
+ post_body = json.dumps({'consumer': post_body})
+ resp, body = self.post('OS-OAUTH1/consumers', post_body)
+ self.expected_success(201, resp.status)
+ body = json.loads(body)
+ return rest_client.ResponseBody(resp, body)
+
+ def delete_consumer(self, consumer_id):
+ """Deletes a consumer.
+
+ :param str consumer_id: The ID of the consumer that will be deleted
+
+ For more information, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/identity/v3-ext/#delete-consumer
+ """
+ resp, body = self.delete('OS-OAUTH1/consumers/%s' % consumer_id)
+ self.expected_success(204, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def update_consumer(self, consumer_id, description=None):
+ """Updates a consumer.
+
+ :param str consumer_id: The ID of the consumer that will be updated
+ :param str description: Optional field to add notes about the consumer
+
+ For more information, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/identity/v3-ext/#update-consumers
+ """
+ post_body = {"description": description}
+ post_body = json.dumps({'consumer': post_body})
+ resp, body = self.patch('OS-OAUTH1/consumers/%s' % consumer_id,
+ post_body)
+ self.expected_success(200, resp.status)
+ body = json.loads(body)
+ return rest_client.ResponseBody(resp, body)
+
+ def show_consumer(self, consumer_id):
+ """Show consumer details.
+
+ :param str consumer_id: The ID of the consumer that will be shown
+
+ For more information, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/identity/v3-ext/#show-consumer-details
+ """
+ resp, body = self.get('OS-OAUTH1/consumers/%s' % consumer_id)
+ self.expected_success(200, resp.status)
+ body = json.loads(body)
+ return rest_client.ResponseBody(resp, body)
+
+ def list_consumers(self):
+ """List all consumers.
+
+ For more information, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/identity/v3-ext/#list-consumers
+ """
+ resp, body = self.get('OS-OAUTH1/consumers')
+ self.expected_success(200, resp.status)
+ body = json.loads(body)
+ return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/identity/v3/roles_client.py b/tempest/lib/services/identity/v3/roles_client.py
index 0df23ce..43e3c01 100644
--- a/tempest/lib/services/identity/v3/roles_client.py
+++ b/tempest/lib/services/identity/v3/roles_client.py
@@ -214,6 +214,18 @@
body = json.loads(body)
return rest_client.ResponseBody(resp, body)
+ def list_all_role_inference_rules(self):
+ """Lists all role inference rules.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/identity/v3/index.html#list-all-role-inference-rules
+ """
+ resp, body = self.get('role_inferences')
+ self.expected_success(200, resp.status)
+ body = json.loads(body)
+ return rest_client.ResponseBody(resp, body)
+
def check_role_inference_rule(self, prior_role, implies_role):
"""Check a role inference rule."""
resp, body = self.head('roles/%s/implies/%s' %
diff --git a/tempest/lib/services/volume/v2/__init__.py b/tempest/lib/services/volume/v2/__init__.py
index 8acad0f..b4eb771 100644
--- a/tempest/lib/services/volume/v2/__init__.py
+++ b/tempest/lib/services/volume/v2/__init__.py
@@ -31,10 +31,12 @@
SnapshotManageClient
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.volume_manage_client import \
+ VolumeManageClient
from tempest.lib.services.volume.v2.volumes_client import VolumesClient
__all__ = ['AvailabilityZoneClient', 'BackupsClient', 'EncryptionTypesClient',
'ExtensionsClient', 'HostsClient', 'QosSpecsClient', 'QuotasClient',
'ServicesClient', 'SnapshotsClient', 'TypesClient', 'VolumesClient',
'LimitsClient', 'CapabilitiesClient', 'SchedulerStatsClient',
- 'SnapshotManageClient']
+ 'SnapshotManageClient', 'VolumeManageClient']
diff --git a/tempest/lib/services/volume/v2/encryption_types_client.py b/tempest/lib/services/volume/v2/encryption_types_client.py
index 8b01f11..eeff537 100755
--- a/tempest/lib/services/volume/v2/encryption_types_client.py
+++ b/tempest/lib/services/volume/v2/encryption_types_client.py
@@ -67,3 +67,17 @@
"/types/%s/encryption/provider" % volume_type_id)
self.expected_success(202, resp.status)
return rest_client.ResponseBody(resp, body)
+
+ def update_encryption_type(self, volume_type_id, **kwargs):
+ """Update an encryption type for an existing volume type.
+
+ TODO: Current api-site doesn't contain this API description.
+ After fixing the api-site, we need to fix here also for putting
+ the link to api-site.
+ """
+ url = "/types/%s/encryption/provider" % volume_type_id
+ put_body = json.dumps({'encryption': kwargs})
+ resp, body = self.put(url, put_body)
+ 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 2bdf1b1..983ed89 100644
--- a/tempest/lib/services/volume/v2/snapshots_client.py
+++ b/tempest/lib/services/volume/v2/snapshots_client.py
@@ -27,7 +27,8 @@
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
+ http://developer.openstack.org/api-ref/block-storage/v2/#list-snapshots-with-details
+ http://developer.openstack.org/api-ref/block-storage/v2/#list-snapshots
"""
url = 'snapshots'
if detail:
@@ -45,7 +46,7 @@
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
+ http://developer.openstack.org/api-ref/block-storage/v2/#show-snapshot-details
"""
url = "snapshots/%s" % snapshot_id
resp, body = self.get(url)
@@ -71,7 +72,7 @@
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
+ http://developer.openstack.org/api-ref/block-storage/v2/#update-snapshot
"""
put_body = json.dumps({'snapshot': kwargs})
resp, body = self.put('snapshots/%s' % snapshot_id, put_body)
@@ -84,7 +85,7 @@
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
+ http://developer.openstack.org/api-ref/block-storage/v2/#delete-snapshot
"""
resp, body = self.delete("snapshots/%s" % snapshot_id)
self.expected_success(202, resp.status)
@@ -136,7 +137,7 @@
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
+ http://developer.openstack.org/api-ref/block-storage/v2/#show-snapshot-metadata
"""
url = "snapshots/%s/metadata" % snapshot_id
resp, body = self.get(url)
@@ -149,7 +150,7 @@
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
+ http://developer.openstack.org/api-ref/block-storage/v2/#update-snapshot-metadata
"""
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 31597d7..5d30615 100644
--- a/tempest/lib/services/volume/v2/types_client.py
+++ b/tempest/lib/services/volume/v2/types_client.py
@@ -41,7 +41,7 @@
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
+ http://developer.openstack.org/api-ref/block-storage/v2/#list-volume-types
"""
url = 'types'
if params:
@@ -57,7 +57,7 @@
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
+ http://developer.openstack.org/api-ref/block-storage/v2/#show-volume-type-details
"""
url = "types/%s" % volume_type_id
resp, body = self.get(url)
@@ -70,7 +70,7 @@
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
+ http://developer.openstack.org/api-ref/block-storage/v2/#create-volume-type
"""
post_body = json.dumps({'volume_type': kwargs})
resp, body = self.post('types', post_body)
@@ -83,7 +83,7 @@
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
+ http://developer.openstack.org/api-ref/block-storage/v2/#delete-volume-type
"""
resp, body = self.delete("types/%s" % volume_type_id)
self.expected_success(202, resp.status)
@@ -138,7 +138,7 @@
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
+ http://developer.openstack.org/api-ref/block-storage/v2/#update-volume-type
"""
put_body = json.dumps({'volume_type': kwargs})
resp, body = self.put('types/%s' % volume_type_id, put_body)
@@ -156,7 +156,7 @@
updated value.
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
+ http://developer.openstack.org/api-ref/block-storage/v2/#update-extra-specs-for-a-volume-type
"""
url = "types/%s/extra_specs/%s" % (volume_type_id, extra_spec_name)
put_body = json.dumps(extra_specs)
@@ -170,7 +170,7 @@
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
+ http://developer.openstack.org/api-ref/block-storage/v2/#add-private-volume-type-access
"""
post_body = json.dumps({'addProjectAccess': kwargs})
url = 'types/%s/action' % volume_type_id
@@ -183,7 +183,7 @@
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
+ http://developer.openstack.org/api-ref/block-storage/v2/#remove-private-volume-type-access
"""
post_body = json.dumps({'removeProjectAccess': kwargs})
url = 'types/%s/action' % volume_type_id
@@ -196,7 +196,7 @@
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
+ http://developer.openstack.org/api-ref/block-storage/v2/#list-private-volume-type-access-details
"""
url = 'types/%s/os-volume-type-access' % volume_type_id
resp, body = self.get(url)
diff --git a/tempest/lib/services/volume/v2/volume_manage_client.py b/tempest/lib/services/volume/v2/volume_manage_client.py
new file mode 100644
index 0000000..12f4240
--- /dev/null
+++ b/tempest/lib/services/volume/v2/volume_manage_client.py
@@ -0,0 +1,37 @@
+# Copyright 2017 FiberHome Telecommunication Technologies CO.,LTD
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from oslo_serialization import jsonutils as json
+
+from tempest.lib.common import rest_client
+
+
+class VolumeManageClient(rest_client.RestClient):
+ """Volume manage V2 client."""
+
+ api_version = "v2"
+
+ def manage_volume(self, **kwargs):
+ """Manage existing volume.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/block-storage/v2/#manage-existing-volume
+ """
+ post_body = json.dumps({'volume': kwargs})
+ resp, body = self.post('os-volume-manage', post_body)
+ self.expected_success(202, resp.status)
+ body = json.loads(body)
+ return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v2/volumes_client.py b/tempest/lib/services/volume/v2/volumes_client.py
index 8b8e249..72823c0 100644
--- a/tempest/lib/services/volume/v2/volumes_client.py
+++ b/tempest/lib/services/volume/v2/volumes_client.py
@@ -65,7 +65,7 @@
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
+ http://developer.openstack.org/api-ref/block-storage/v2/#create-volume
"""
post_body = json.dumps({'volume': kwargs})
resp, body = self.post('volumes', post_body)
@@ -78,7 +78,7 @@
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
+ http://developer.openstack.org/api-ref/block-storage/v2/#update-volume
"""
put_body = json.dumps({'volume': kwargs})
resp, body = self.put('volumes/%s' % volume_id, put_body)
@@ -106,7 +106,7 @@
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
+ http://developer.openstack.org/api-ref/block-storage/v2/#attach-volume-to-server
"""
post_body = json.dumps({'os-attach': kwargs})
url = 'volumes/%s/action' % (volume_id)
@@ -163,7 +163,7 @@
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
+ http://developer.openstack.org/api-ref/block-storage/v2/#extend-volume-size
"""
post_body = json.dumps({'os-extend': kwargs})
url = 'volumes/%s/action' % (volume_id)
@@ -176,7 +176,7 @@
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
+ http://developer.openstack.org/api-ref/block-storage/v2/#reset-volume-statuses
"""
post_body = json.dumps({'os-reset_status': kwargs})
resp, body = self.post('volumes/%s/action' % volume_id, post_body)
@@ -188,7 +188,7 @@
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
+ http://developer.openstack.org/api-ref/block-storage/v2/#create-volume-transfer
"""
post_body = json.dumps({'transfer': kwargs})
resp, body = self.post('os-volume-transfer', post_body)
@@ -209,7 +209,7 @@
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
+ http://developer.openstack.org/api-ref/block-storage/v2/#list-volume-transfers
"""
url = 'os-volume-transfer'
if params:
@@ -230,7 +230,7 @@
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
+ http://developer.openstack.org/api-ref/block-storage/v2/#accept-volume-transfer
"""
url = 'os-volume-transfer/%s/accept' % transfer_id
post_body = json.dumps({'accept': kwargs})
@@ -307,7 +307,7 @@
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
+ http://developer.openstack.org/api-ref/block-storage/v2/#set-image-metadata-for-volume
"""
post_body = json.dumps({'os-set_image_metadata': {'metadata': kwargs}})
url = "volumes/%s/action" % (volume_id)
@@ -344,10 +344,22 @@
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
+ http://developer.openstack.org/api-ref/block-storage/v2/#show-back-end-capabilities
"""
url = 'capabilities/%s' % host
resp, body = self.get(url)
body = json.loads(body)
self.expected_success(200, resp.status)
return rest_client.ResponseBody(resp, body)
+
+ def unmanage_volume(self, volume_id):
+ """Unmanage volume.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/block-storage/v2/#unmanage-volume
+ """
+ post_body = json.dumps({'os-unmanage': {}})
+ resp, body = self.post('volumes/%s/action' % volume_id, post_body)
+ self.expected_success(202, resp.status)
+ return rest_client.ResponseBody(resp, body)
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index 727afd6..c1270c7 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -79,13 +79,8 @@
cls.security_groups_client = cls.manager.security_groups_client
cls.security_group_rules_client = (
cls.manager.security_group_rules_client)
-
- if CONF.volume_feature_enabled.api_v2:
- cls.volumes_client = cls.manager.volumes_v2_client
- cls.snapshots_client = cls.manager.snapshots_v2_client
- else:
- cls.volumes_client = cls.manager.volumes_client
- cls.snapshots_client = cls.manager.snapshots_client
+ cls.volumes_client = cls.manager.volumes_v2_client
+ cls.snapshots_client = cls.manager.snapshots_v2_client
# ## Test functions library
#
@@ -235,12 +230,7 @@
volume['id'])
self.addCleanup(test_utils.call_and_ignore_notfound_exc,
self.volumes_client.delete_volume, volume['id'])
-
- # NOTE(e0ne): Cinder API v2 uses name instead of display_name
- if 'display_name' in volume:
- self.assertEqual(name, volume['display_name'])
- else:
- self.assertEqual(name, volume['name'])
+ self.assertEqual(name, volume['name'])
waiters.wait_for_volume_resource_status(self.volumes_client,
volume['id'], 'available')
# The volume retrieved on creation has a non-up-to-date status.
@@ -888,16 +878,16 @@
show_floatingip(floatingip_id)['floatingip'])
return status == result['status']
- test_utils.call_until_true(refresh,
- CONF.network.build_timeout,
- CONF.network.build_interval)
- floating_ip = self.floating_ips_client.show_floatingip(
- floatingip_id)['floatingip']
- self.assertEqual(status, floating_ip['status'],
- message="FloatingIP: {fp} is at status: {cst}. "
- "failed to reach status: {st}"
- .format(fp=floating_ip, cst=floating_ip['status'],
- st=status))
+ if not test_utils.call_until_true(refresh,
+ CONF.network.build_timeout,
+ CONF.network.build_interval):
+ floating_ip = self.floating_ips_client.show_floatingip(
+ floatingip_id)['floatingip']
+ self.assertEqual(status, floating_ip['status'],
+ message="FloatingIP: {fp} is at status: {cst}. "
+ "failed to reach status: {st}"
+ .format(fp=floating_ip, cst=floating_ip['status'],
+ st=status))
LOG.info("FloatingIP: {fp} is at status: {st}"
.format(fp=floating_ip, st=status))
@@ -1251,14 +1241,9 @@
@classmethod
def setup_clients(cls):
super(EncryptionScenarioTest, cls).setup_clients()
- if CONF.volume_feature_enabled.api_v2:
- cls.admin_volume_types_client = cls.os_adm.volume_types_v2_client
- cls.admin_encryption_types_client =\
- cls.os_adm.encryption_types_v2_client
- else:
- cls.admin_volume_types_client = cls.os_adm.volume_types_client
- cls.admin_encryption_types_client =\
- cls.os_adm.encryption_types_client
+ cls.admin_volume_types_client = cls.os_adm.volume_types_v2_client
+ cls.admin_encryption_types_client =\
+ cls.os_adm.encryption_types_v2_client
def create_encryption_type(self, client=None, type_id=None, provider=None,
key_size=None, cipher=None,
diff --git a/tempest/scenario/test_network_basic_ops.py b/tempest/scenario/test_network_basic_ops.py
index 15a0a70..dec0ad0 100644
--- a/tempest/scenario/test_network_basic_ops.py
+++ b/tempest/scenario/test_network_basic_ops.py
@@ -286,10 +286,11 @@
% CONF.network.build_timeout)
num, new_nic = self.diff_list[0]
- ssh_client.assign_static_ip(
- nic=new_nic, addr=new_port['fixed_ips'][0]['ip_address'],
- network_mask_bits=CONF.network.project_network_mask_bits)
- ssh_client.set_nic_state(nic=new_nic)
+ ssh_client.exec_command("sudo ip addr add %s/%s dev %s" % (
+ new_port['fixed_ips'][0]['ip_address'],
+ CONF.network.project_network_mask_bits,
+ new_nic))
+ ssh_client.exec_command("sudo ip link set %s up" % new_nic)
def _get_server_nics(self, ssh_client):
reg = re.compile(r'(?P<num>\d+): (?P<nic_name>\w+):')
@@ -833,7 +834,13 @@
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)
- ssh_client.set_mac_address(spoof_nic, spoof_mac)
+ # Set a mac address by making nic down temporary
+ cmd = ("sudo ip link set {nic} down;"
+ "sudo ip link set dev {nic} address {mac};"
+ "sudo ip link set {nic} up").format(nic=spoof_nic,
+ mac=spoof_mac)
+ ssh_client.exec_command(cmd)
+
new_mac = ssh_client.get_mac_address(nic=spoof_nic)
self.assertEqual(spoof_mac, new_mac)
self.check_remote_connectivity(ssh_client, dest=peer_address,
diff --git a/tempest/scenario/test_network_v6.py b/tempest/scenario/test_network_v6.py
index cfd83d0..4e8b004 100644
--- a/tempest/scenario/test_network_v6.py
+++ b/tempest/scenario/test_network_v6.py
@@ -153,7 +153,8 @@
"ports: %s")
% (network_id, ports))
mac6 = ports[0]
- ssh.set_nic_state(ssh.get_nic_name_by_mac(mac6))
+ nic = ssh.get_nic_name_by_mac(mac6)
+ ssh.exec_command("sudo ip link set %s up" % nic)
def _prepare_and_test(self, address6_mode, n_subnets6=1, dualnet=False):
net_list = self.prepare_network(address6_mode=address6_mode,
diff --git a/tempest/scenario/test_server_advanced_ops.py b/tempest/scenario/test_server_advanced_ops.py
index 1960e9a..cc3687f 100644
--- a/tempest/scenario/test_server_advanced_ops.py
+++ b/tempest/scenario/test_server_advanced_ops.py
@@ -37,13 +37,6 @@
"""
@classmethod
- def skip_checks(cls):
- super(TestServerAdvancedOps, cls).skip_checks()
- if CONF.compute.flavor_ref_alt == CONF.compute.flavor_ref:
- msg = "Skipping test - flavor_ref and flavor_ref_alt are identical"
- raise cls.skipException(msg)
-
- @classmethod
def setup_credentials(cls):
cls.set_network_resources()
super(TestServerAdvancedOps, cls).setup_credentials()
@@ -52,6 +45,11 @@
@decorators.idempotent_id('e6c28180-7454-4b59-b188-0257af08a63b')
@testtools.skipUnless(CONF.compute_feature_enabled.resize,
'Resize is not available.')
+ @testtools.skipUnless(CONF.compute.flavor_ref !=
+ CONF.compute.flavor_ref_alt
+ and CONF.compute.flavor_ref_alt != "",
+ 'The flavor_ref_alt option should not be empty and '
+ 'should not be identical with flavor_ref')
@test.services('compute', 'volume')
def test_resize_volume_backed_server_confirm(self):
# We create an instance for use in this test
diff --git a/tempest/scenario/test_stamp_pattern.py b/tempest/scenario/test_stamp_pattern.py
index 96b423d..5f5d701 100644
--- a/tempest/scenario/test_stamp_pattern.py
+++ b/tempest/scenario/test_stamp_pattern.py
@@ -68,10 +68,7 @@
volume['id'], 'available')
waiters.wait_for_volume_resource_status(self.snapshots_client,
snapshot['id'], 'available')
- if 'display_name' in snapshot:
- self.assertEqual(snapshot_name, snapshot['display_name'])
- else:
- self.assertEqual(snapshot_name, snapshot['name'])
+ self.assertEqual(snapshot_name, snapshot['name'])
return snapshot
def _wait_for_volume_available_on_the_system(self, ip_address,
diff --git a/tempest/scenario/test_volume_boot_pattern.py b/tempest/scenario/test_volume_boot_pattern.py
index ae0230e..888bff2 100644
--- a/tempest/scenario/test_volume_boot_pattern.py
+++ b/tempest/scenario/test_volume_boot_pattern.py
@@ -81,13 +81,7 @@
self.addCleanup(self.snapshots_client.delete_snapshot, snap['id'])
waiters.wait_for_volume_resource_status(self.snapshots_client,
snap['id'], 'available')
-
- # NOTE(e0ne): Cinder API v2 uses name instead of display_name
- if 'display_name' in snap:
- self.assertEqual(snap_name, snap['display_name'])
- else:
- self.assertEqual(snap_name, snap['name'])
-
+ self.assertEqual(snap_name, snap['name'])
return snap
def _delete_server(self, server):
diff --git a/tempest/scenario/test_volume_migrate_attached.py b/tempest/scenario/test_volume_migrate_attached.py
index f580ea6..f04947c 100644
--- a/tempest/scenario/test_volume_migrate_attached.py
+++ b/tempest/scenario/test_volume_migrate_attached.py
@@ -40,10 +40,7 @@
@classmethod
def setup_clients(cls):
super(TestVolumeMigrateRetypeAttached, cls).setup_clients()
- if CONF.volume_feature_enabled.api_v1:
- cls.admin_volume_types_client = cls.os_adm.volume_types_client
- else:
- cls.admin_volume_types_client = cls.os_adm.volume_types_v2_client
+ cls.admin_volume_types_client = cls.os_adm.volume_types_v2_client
@classmethod
def skip_checks(cls):
diff --git a/tempest/tests/common/utils/linux/test_remote_client.py b/tempest/tests/common/utils/linux/test_remote_client.py
index 48cb86b..ecb8e64 100644
--- a/tempest/tests/common/utils/linux/test_remote_client.py
+++ b/tempest/tests/common/utils/linux/test_remote_client.py
@@ -139,23 +139,6 @@
self._assert_exec_called_with(
"ip addr | awk '/ether/ {print $2}'")
- def test_assign_static_ip(self):
- self.ssh_mock.mock.exec_command.return_value = ''
- ip = '10.0.0.2'
- nic = 'eth0'
- self.assertEqual(self.conn.assign_static_ip(nic, ip), '')
- self._assert_exec_called_with(
- "sudo ip addr add %s/%s dev %s" % (ip, '28', nic))
-
- def test_set_nic_state(self):
- nic = 'eth0'
- self.conn.set_nic_state(nic)
- self._assert_exec_called_with(
- 'sudo ip link set %s up' % nic)
- self.conn.set_nic_state(nic, "down")
- self._assert_exec_called_with(
- 'sudo ip link set %s down' % nic)
-
class TestRemoteClientWithServer(base.TestCase):
diff --git a/tempest/tests/lib/services/compute/test_servers_client.py b/tempest/tests/lib/services/compute/test_servers_client.py
index 8d391c1..a277dfe 100644
--- a/tempest/tests/lib/services/compute/test_servers_client.py
+++ b/tempest/tests/lib/services/compute/test_servers_client.py
@@ -168,6 +168,10 @@
"url": "http://os.co/v2/616fb98f-46ca-475e-917e-2563e5a8cd19"
}
+ FAKE_SERVER_PASSWORD = {
+ "adminPass": "fake-password",
+ }
+
FAKE_INSTANCE_ACTION_EVENTS = {
"event": "fake-event",
"start_time": "2016-10-02T10:00:00-05:00",
@@ -322,6 +326,21 @@
name='fake-name'
)
+ def test_evacuate_server_with_str_body(self):
+ self._test_evacuate_server()
+
+ def test_evacuate_server_with_bytes_body(self):
+ self._test_evacuate_server(bytes_body=True)
+
+ def _test_evacuate_server(self, bytes_body=False):
+ kwargs = {'server_id': self.server_id,
+ 'host': 'fake-target-host'}
+ self.check_service_client_function(
+ self.client.evacuate_server,
+ 'tempest.lib.common.rest_client.RestClient.post',
+ self.FAKE_SERVER_PASSWORD,
+ **kwargs)
+
def test_change_password_with_str_body(self):
self._test_change_password()
diff --git a/tempest/tests/lib/services/identity/v3/test_oauth_consumers_client.py b/tempest/tests/lib/services/identity/v3/test_oauth_consumers_client.py
new file mode 100644
index 0000000..8d53792
--- /dev/null
+++ b/tempest/tests/lib/services/identity/v3/test_oauth_consumers_client.py
@@ -0,0 +1,160 @@
+# Copyright 2017 AT&T 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.identity.v3 import oauth_consumers_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestOAUTHConsumerClient(base.BaseServiceTest):
+ FAKE_CREATE_CONSUMER = {
+ "consumer": {
+ 'description': 'A fake description 1'
+ }
+
+ }
+
+ FAKE_CONSUMER_INFO = {
+ "consumer": {
+ 'id': '6392c7d3b7a2062e09a07aa377',
+ 'links': {
+ 'self': 'http://example.com/identity/v3/' +
+ 'OS-OAUTH1/consumers/g6f2l9'
+ },
+ 'description': 'A description that is fake'
+ }
+
+ }
+
+ FAKE_LIST_CONSUMERS = {
+ 'links': {
+ 'self': 'http://example.com/identity/v3/OS-OAUTH1/consumers/',
+ 'next': None,
+ 'previous': None
+ },
+ 'consumers': [
+ {
+ 'id': '6392c7d3b7a2062e09a07aa377',
+ 'links': {
+ 'self': 'http://example.com/identity/v3/' +
+ 'OS-OAUTH1/consumers/6b9f2g5'
+ },
+ 'description': 'A description that is fake'
+ },
+ {
+ 'id': '677a855c9e3eb3a3954b36aca6',
+ 'links': {
+ 'self': 'http://example.com/identity/v3/' +
+ 'OS-OAUTH1/consumers/6a9f2366'
+ },
+ 'description': 'A very fake description 2'
+ },
+ {
+ 'id': '9d3ac57b08d65e07826b5e506',
+ 'links': {
+ 'self': 'http://example.com/identity/v3/' +
+ 'OS-OAUTH1/consumers/626b5e506'
+ },
+ 'description': 'A very fake description 3'
+ },
+ {
+ 'id': 'b522d163b1a18e928aca9y426',
+ 'links': {
+ 'self': 'http://example.com/identity/v3/' +
+ 'OS-OAUTH1/consumers/g7ca9426'
+ },
+ 'description': 'A very fake description 4'
+ },
+ {
+ 'id': 'b7e47321b5ef9051f93c2049e',
+ 'links': {
+ 'self': 'http://example.com/identity/v3/' +
+ 'OS-OAUTH1/consumers/23d82049e'
+ },
+ 'description': 'A very fake description 5'
+ }
+ ]
+ }
+
+ def setUp(self):
+ super(TestOAUTHConsumerClient, self).setUp()
+ fake_auth = fake_auth_provider.FakeAuthProvider()
+ self.client = oauth_consumers_client.OAUTHConsumerClient(fake_auth,
+ 'identity',
+ 'regionOne')
+
+ def _test_create_consumer(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.create_consumer,
+ 'tempest.lib.common.rest_client.RestClient.post',
+ self.FAKE_CREATE_CONSUMER,
+ bytes_body,
+ description=self.FAKE_CREATE_CONSUMER["consumer"]["description"],
+ status=201)
+
+ def _test_show_consumer(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.show_consumer,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_CONSUMER_INFO,
+ bytes_body,
+ consumer_id="6392c7d3b7a2062e09a07aa377")
+
+ def _test_list_consumers(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.list_consumers,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_LIST_CONSUMERS,
+ bytes_body)
+
+ def _test_update_consumer(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.update_consumer,
+ 'tempest.lib.common.rest_client.RestClient.patch',
+ self.FAKE_CONSUMER_INFO,
+ bytes_body,
+ consumer_id="6392c7d3b7a2062e09a07aa377")
+
+ def test_create_consumer_with_str_body(self):
+ self._test_create_consumer()
+
+ def test_create_consumer_with_bytes_body(self):
+ self._test_create_consumer(bytes_body=True)
+
+ def test_show_consumer_with_str_body(self):
+ self._test_show_consumer()
+
+ def test_show_consumer_with_bytes_body(self):
+ self._test_show_consumer(bytes_body=True)
+
+ def test_list_consumers_with_str_body(self):
+ self._test_list_consumers()
+
+ def test_list_consumers_with_bytes_body(self):
+ self._test_list_consumers(bytes_body=True)
+
+ def test_update_consumer_with_str_body(self):
+ self._test_update_consumer()
+
+ def test_update_consumer_with_bytes_body(self):
+ self._test_update_consumer(bytes_body=True)
+
+ def test_delete_consumer(self):
+ self.check_service_client_function(
+ self.client.delete_consumer,
+ 'tempest.lib.common.rest_client.RestClient.delete',
+ {},
+ consumer_id="6392c7d3b7a2062e09a07aa377",
+ status=204)
diff --git a/tempest/tests/lib/services/identity/v3/test_roles_client.py b/tempest/tests/lib/services/identity/v3/test_roles_client.py
index 41cea85..8d6bb42 100644
--- a/tempest/tests/lib/services/identity/v3/test_roles_client.py
+++ b/tempest/tests/lib/services/identity/v3/test_roles_client.py
@@ -26,6 +26,18 @@
FAKE_ROLE_ID_2 = "2"
FAKE_ROLE_NAME_2 = "test2"
+ FAKE_ROLE_ID_3 = "3"
+ FAKE_ROLE_NAME_3 = "test3"
+
+ FAKE_ROLE_ID_4 = "4"
+ FAKE_ROLE_NAME_4 = "test4"
+
+ FAKE_ROLE_ID_5 = "5"
+ FAKE_ROLE_NAME_5 = "test5"
+
+ FAKE_ROLE_ID_6 = "6"
+ FAKE_ROLE_NAME_6 = "test6"
+
FAKE_ROLE_INFO = {
"role": {
"domain_id": FAKE_DOMAIN_ID,
@@ -77,8 +89,8 @@
}
}
- FAKE_LIST_ROLE_INFERENCES_RULES = {
- "role_inference": {
+ COMMON_FAKE_LIST_ROLE_INFERENCE_RULES = [
+ {
"prior_role": {
"id": FAKE_ROLE_ID,
"name": FAKE_ROLE_NAME,
@@ -97,20 +109,60 @@
}
},
{
- "id": "3",
- "name": "test3",
+ "id": FAKE_ROLE_ID_3,
+ "name": FAKE_ROLE_NAME_3,
"links": {
- "self": "http://example.com/identity/v3/roles/3"
+ "self": "http://example.com/identity/v3/roles/%s" % (
+ FAKE_ROLE_ID_3)
}
}
]
},
+ {
+ "prior_role": {
+ "id": FAKE_ROLE_ID_4,
+ "name": FAKE_ROLE_NAME_4,
+ "links": {
+ "self": "http://example.com/identity/v3/roles/%s" % (
+ FAKE_ROLE_ID_4)
+ }
+ },
+ "implies": [
+ {
+ "id": FAKE_ROLE_ID_5,
+ "name": FAKE_ROLE_NAME_5,
+ "links": {
+ "self": "http://example.com/identity/v3/roles/%s" % (
+ FAKE_ROLE_ID_5)
+ }
+ },
+ {
+ "id": FAKE_ROLE_ID_6,
+ "name": FAKE_ROLE_NAME_6,
+ "links": {
+ "self": "http://example.com/identity/v3/roles/%s" % (
+ FAKE_ROLE_ID_6)
+ }
+ }
+ ]
+ }
+ ]
+
+ FAKE_LIST_ROLE_INFERENCE_RULES = {
+ "role_inference": COMMON_FAKE_LIST_ROLE_INFERENCE_RULES[0],
"links": {
"self": "http://example.com/identity/v3/roles/"
"%s/implies" % FAKE_ROLE_ID
}
}
+ FAKE_LIST_ALL_ROLE_INFERENCE_RULES = {
+ "role_inferences": COMMON_FAKE_LIST_ROLE_INFERENCE_RULES,
+ "links": {
+ "self": "http://example.com/identity/v3/role_inferences"
+ }
+ }
+
def setUp(self):
super(TestRolesClient, self).setUp()
fake_auth = fake_auth_provider.FakeAuthProvider()
@@ -254,10 +306,17 @@
self.check_service_client_function(
self.client.list_role_inferences_rules,
'tempest.lib.common.rest_client.RestClient.get',
- self.FAKE_LIST_ROLE_INFERENCES_RULES,
+ self.FAKE_LIST_ROLE_INFERENCE_RULES,
bytes_body,
prior_role=self.FAKE_ROLE_ID)
+ def _test_list_all_role_inference_rules(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.list_all_role_inference_rules,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_LIST_ALL_ROLE_INFERENCE_RULES,
+ bytes_body)
+
def test_create_role_with_str_body(self):
self._test_create_role()
@@ -441,3 +500,9 @@
status=204,
prior_role=self.FAKE_ROLE_ID,
implies_role=self.FAKE_ROLE_ID_2)
+
+ def test_list_all_role_inference_rules_with_str_body(self):
+ self._test_list_all_role_inference_rules()
+
+ def test_list_all_role_inference_rules_with_bytes_body(self):
+ self._test_list_all_role_inference_rules(bytes_body=True)
diff --git a/tox.ini b/tox.ini
index e2a9b3f..892f834 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,5 +1,5 @@
[tox]
-envlist = pep8,py35,py34,py27,pip-check-reqs
+envlist = pep8,py35,py27,pip-check-reqs
minversion = 2.3.1
skipsdist = True
@@ -36,7 +36,6 @@
commands = oslo-config-generator --config-file tempest/cmd/config-generator.tempest.conf
[testenv:cover]
-setenv = OS_TEST_PATH=./tempest/tests
commands = python setup.py testr --coverage --testr-arg='tempest\.tests {posargs}'
[testenv:all]