Merge "Add tests for wait_for_interface_status"
diff --git a/releasenotes/notes/volume-service-testing-default-to-v3-endpoints-20b86895a590925d.yaml b/releasenotes/notes/volume-service-testing-default-to-v3-endpoints-20b86895a590925d.yaml
new file mode 100644
index 0000000..ea69293
--- /dev/null
+++ b/releasenotes/notes/volume-service-testing-default-to-v3-endpoints-20b86895a590925d.yaml
@@ -0,0 +1,8 @@
+---
+upgrade:
+ - |
+ The volume config option ``catalog_type`` default is changed to
+ ``volumev3`` which is v3 API endpoint configured in devstack.
+ With this change Tempest will be testing v3 API as default.
+ User who want to test v2 API can still test by configuring the
+ ``catalog_type`` to v2 endpoint.
diff --git a/releasenotes/notes/volume-v3-service-clients-a863a6336af56cca.yaml b/releasenotes/notes/volume-v3-service-clients-a863a6336af56cca.yaml
new file mode 100644
index 0000000..b572a34
--- /dev/null
+++ b/releasenotes/notes/volume-v3-service-clients-a863a6336af56cca.yaml
@@ -0,0 +1,12 @@
+---
+features:
+ - |
+ Adds volume service clients for v3 APIs. As v3 base API should be
+ identical to v2 APIs, we just copy all existing v2 service client
+ for v3 API.
+deprecations:
+ - |
+ Deprecates the volume service clients for v2 APIs. Volume v2 APIs
+ are deprecated in all supported stable branches, so it's time
+ to deprecate the tempest service clients for v2 APIs and remove in future
+ release.
diff --git a/tempest/README.rst b/tempest/README.rst
index 62821de..a5f4a92 100644
--- a/tempest/README.rst
+++ b/tempest/README.rst
@@ -12,12 +12,12 @@
and guidelines. Below is the overview of the Tempest respository structure
to make this clear.
- .. code-block:: console
+.. code-block:: console
- tempest/
- api/ - API tests
- scenario/ - complex scenario tests
- tests/ - unit tests for Tempest internals
+ tempest/
+ api/ - API tests
+ scenario/ - complex scenario tests
+ tests/ - unit tests for Tempest internals
Each of these directories contains different types of tests. What
belongs in each directory, the rules and examples for good tests, are
diff --git a/tempest/api/compute/volumes/test_attach_volume_negative.py b/tempest/api/compute/volumes/test_attach_volume_negative.py
index 7a74869..8618148 100644
--- a/tempest/api/compute/volumes/test_attach_volume_negative.py
+++ b/tempest/api/compute/volumes/test_attach_volume_negative.py
@@ -56,3 +56,16 @@
self.assertRaises(lib_exc.BadRequest,
self.attach_volume, server, volume)
+
+ @decorators.attr(type=['negative'])
+ @decorators.idempotent_id('ee37a796-2afb-11e7-bc0f-fa163e65f5ce')
+ def test_attach_attached_volume_to_different_server(self):
+ server1 = self.create_test_server(wait_until='ACTIVE')
+ volume = self.create_volume()
+
+ self.attach_volume(server1, volume)
+
+ # Create server2 and attach in-use volume
+ server2 = self.create_test_server(wait_until='ACTIVE')
+ self.assertRaises(lib_exc.BadRequest,
+ self.attach_volume, server2, volume)
diff --git a/tempest/config.py b/tempest/config.py
index 008c9f6..1fb5c8e 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -752,7 +752,7 @@
help='Timeout in seconds to wait for a volume to become '
'available.'),
cfg.StrOpt('catalog_type',
- default='volume',
+ default='volumev3',
help="Catalog type of the Volume Service"),
cfg.StrOpt('region',
default='',
diff --git a/tempest/lib/services/volume/v2/availability_zone_client.py b/tempest/lib/services/volume/v2/availability_zone_client.py
index bb4a357..bdb2304 100644
--- a/tempest/lib/services/volume/v2/availability_zone_client.py
+++ b/tempest/lib/services/volume/v2/availability_zone_client.py
@@ -13,16 +13,11 @@
# License for the specific language governing permissions and limitations
# under the License.
-from oslo_serialization import jsonutils as json
+from debtcollector import moves
-from tempest.lib.common import rest_client
+from tempest.lib.services.volume.v3 import availability_zone_client
-class AvailabilityZoneClient(rest_client.RestClient):
- api_version = "v2"
-
- def list_availability_zones(self):
- resp, body = self.get('os-availability-zone')
- body = json.loads(body)
- self.expected_success(200, resp.status)
- return rest_client.ResponseBody(resp, body)
+AvailabilityZoneClient = moves.moved_class(
+ availability_zone_client.AvailabilityZoneClient, 'AvailabilityZoneClient',
+ __name__, version="Rocky", removal_version='?')
diff --git a/tempest/lib/services/volume/v2/backups_client.py b/tempest/lib/services/volume/v2/backups_client.py
index adfa6a6..80b3631 100644
--- a/tempest/lib/services/volume/v2/backups_client.py
+++ b/tempest/lib/services/volume/v2/backups_client.py
@@ -12,108 +12,11 @@
# 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 debtcollector import moves
-from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
-
-from tempest.lib.common import rest_client
-from tempest.lib import exceptions as lib_exc
-from tempest.lib.services.volume import base_client
+from tempest.lib.services.volume.v3 import backups_client
-class BackupsClient(base_client.BaseClient):
- """Volume V2 Backups client"""
- api_version = "v2"
-
- def create_backup(self, **kwargs):
- """Creates a backup of volume.
-
- For a full list of available parameters, please refer to the official
- API reference:
- https://developer.openstack.org/api-ref/block-storage/v2/index.html#create-backup
- """
- post_body = json.dumps({'backup': kwargs})
- resp, body = self.post('backups', post_body)
- body = json.loads(body)
- self.expected_success(202, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def restore_backup(self, backup_id, **kwargs):
- """Restore volume from backup.
-
- For a full list of available parameters, please refer to the official
- API reference:
- https://developer.openstack.org/api-ref/block-storage/v2/index.html#restore-backup
- """
- post_body = json.dumps({'restore': kwargs})
- resp, body = self.post('backups/%s/restore' % (backup_id), post_body)
- body = json.loads(body)
- self.expected_success(202, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def delete_backup(self, backup_id):
- """Delete a backup of volume."""
- resp, body = self.delete('backups/%s' % backup_id)
- self.expected_success(202, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def show_backup(self, backup_id):
- """Returns the details of a single backup."""
- url = "backups/%s" % backup_id
- resp, body = self.get(url)
- body = json.loads(body)
- self.expected_success(200, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def list_backups(self, detail=False, **params):
- """List all the tenant's backups.
-
- For a full list of available parameters, please refer to the official
- API reference:
- http://developer.openstack.org/api-ref/block-storage/v2/#list-backups
- http://developer.openstack.org/api-ref/block-storage/v2/#list-backups-with-details
- """
- url = "backups"
- if detail:
- url += "/detail"
- if params:
- url += '?%s' % urllib.urlencode(params)
- resp, body = self.get(url)
- body = json.loads(body)
- self.expected_success(200, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def export_backup(self, backup_id):
- """Export backup metadata record."""
- url = "backups/%s/export_record" % backup_id
- resp, body = self.get(url)
- body = json.loads(body)
- self.expected_success(200, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def import_backup(self, **kwargs):
- """Import backup metadata record."""
- post_body = json.dumps({'backup-record': kwargs})
- resp, body = self.post("backups/import_record", post_body)
- body = json.loads(body)
- self.expected_success(201, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def reset_backup_status(self, backup_id, status):
- """Reset the specified backup's status."""
- post_body = json.dumps({'os-reset_status': {"status": status}})
- resp, body = self.post('backups/%s/action' % backup_id, post_body)
- self.expected_success(202, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def is_resource_deleted(self, id):
- try:
- self.show_backup(id)
- except lib_exc.NotFound:
- return True
- return False
-
- @property
- def resource_type(self):
- """Returns the primary type of resource this client works with."""
- return 'backup'
+BackupsClient = moves.moved_class(
+ backups_client.BackupsClient, 'BackupsClient',
+ __name__, version="Rocky", removal_version='?')
diff --git a/tempest/lib/services/volume/v2/capabilities_client.py b/tempest/lib/services/volume/v2/capabilities_client.py
index 240be13..d8cf806 100644
--- a/tempest/lib/services/volume/v2/capabilities_client.py
+++ b/tempest/lib/services/volume/v2/capabilities_client.py
@@ -13,23 +13,11 @@
# License for the specific language governing permissions and limitations
# under the License.
-from oslo_serialization import jsonutils as json
+from debtcollector import moves
-from tempest.lib.common import rest_client
+from tempest.lib.services.volume.v3 import capabilities_client
-class CapabilitiesClient(rest_client.RestClient):
- api_version = "v2"
-
- def show_backend_capabilities(self, host):
- """Shows capabilities for a storage back end.
-
- For a full list of available parameters, please refer to the official
- API reference:
- https://developer.openstack.org/api-ref/block-storage/v2/index.html#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)
+CapabilitiesClient = moves.moved_class(
+ capabilities_client.CapabilitiesClient, 'CapabilitiesClient',
+ __name__, version="Queens", removal_version='?')
diff --git a/tempest/lib/services/volume/v2/encryption_types_client.py b/tempest/lib/services/volume/v2/encryption_types_client.py
index b99d1fe..875e59e 100644
--- a/tempest/lib/services/volume/v2/encryption_types_client.py
+++ b/tempest/lib/services/volume/v2/encryption_types_client.py
@@ -13,79 +13,11 @@
# License for the specific language governing permissions and limitations
# under the License.
-from oslo_serialization import jsonutils as json
+from debtcollector import moves
-from tempest.lib.common import rest_client
-from tempest.lib import exceptions as lib_exc
+from tempest.lib.services.volume.v3 import encryption_types_client
-class EncryptionTypesClient(rest_client.RestClient):
- api_version = "v2"
-
- def is_resource_deleted(self, id):
- try:
- body = self.show_encryption_type(id)
- if not body:
- return True
- except lib_exc.NotFound:
- return True
- return False
-
- @property
- def resource_type(self):
- """Returns the primary type of resource this client works with."""
- return 'encryption-type'
-
- def show_encryption_type(self, volume_type_id):
- """Get the volume encryption type for the specified volume type.
-
- volume_type_id: Id of volume_type.
- """
- url = "/types/%s/encryption" % volume_type_id
- resp, body = self.get(url)
- body = json.loads(body)
- self.expected_success(200, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def show_encryption_specs_item(self, volume_type_id, key):
- """Get the encryption specs item for the specified volume type."""
- url = "/types/%s/encryption/%s" % (volume_type_id, key)
- resp, body = self.get(url)
- body = json.loads(body)
- self.expected_success(200, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def create_encryption_type(self, volume_type_id, **kwargs):
- """Create encryption type.
-
- For a full list of available parameters, please refer to the official
- API reference:
- https://developer.openstack.org/api-ref/block-storage/v2/#create-an-encryption-type-for-v2
- """
- url = "/types/%s/encryption" % volume_type_id
- post_body = json.dumps({'encryption': kwargs})
- resp, body = self.post(url, post_body)
- body = json.loads(body)
- self.expected_success(200, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def delete_encryption_type(self, volume_type_id):
- """Delete the encryption type for the specified volume-type."""
- resp, body = self.delete(
- "/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.
-
- For a full list of available parameters, please refer to the official
- API reference:
- https://developer.openstack.org/api-ref/block-storage/v2/#update-an-encryption-type-for-v2
- """
- 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)
+EncryptionTypesClient = moves.moved_class(
+ encryption_types_client.EncryptionTypesClient, 'EncryptionTypesClient',
+ __name__, version="Rocky", removal_version='?')
diff --git a/tempest/lib/services/volume/v2/extensions_client.py b/tempest/lib/services/volume/v2/extensions_client.py
index 09279d5..6316ef5 100644
--- a/tempest/lib/services/volume/v2/extensions_client.py
+++ b/tempest/lib/services/volume/v2/extensions_client.py
@@ -12,19 +12,11 @@
# 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 debtcollector import moves
-from oslo_serialization import jsonutils as json
-
-from tempest.lib.common import rest_client
+from tempest.lib.services.volume.v3 import extensions_client
-class ExtensionsClient(rest_client.RestClient):
- """Volume V2 extensions client."""
- api_version = "v2"
-
- def list_extensions(self):
- url = 'extensions'
- resp, body = self.get(url)
- body = json.loads(body)
- self.expected_success(200, resp.status)
- return rest_client.ResponseBody(resp, body)
+ExtensionsClient = moves.moved_class(
+ extensions_client.ExtensionsClient, 'ExtensionsClient',
+ __name__, version="Rocky", removal_version='?')
diff --git a/tempest/lib/services/volume/v2/hosts_client.py b/tempest/lib/services/volume/v2/hosts_client.py
index f44bda3..38f1b38 100644
--- a/tempest/lib/services/volume/v2/hosts_client.py
+++ b/tempest/lib/services/volume/v2/hosts_client.py
@@ -12,37 +12,11 @@
# 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 debtcollector import moves
-from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
-
-from tempest.lib.common import rest_client
+from tempest.lib.services.volume.v3 import hosts_client
-class HostsClient(rest_client.RestClient):
- """Client class to send CRUD Volume V2 API requests"""
- api_version = "v2"
-
- def list_hosts(self, **params):
- """Lists all hosts.
-
- For a full list of available parameters, please refer to the official
- API reference:
- https://developer.openstack.org/api-ref/block-storage/v2/#list-all-hosts
- """
- url = 'os-hosts'
- if params:
- url += '?%s' % urllib.urlencode(params)
-
- resp, body = self.get(url)
- body = json.loads(body)
- self.expected_success(200, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def show_host(self, host_name):
- """Show host details."""
- url = 'os-hosts/%s' % host_name
- resp, body = self.get(url)
- self.expected_success(200, resp.status)
- body = json.loads(body)
- return rest_client.ResponseBody(resp, body)
+HostsClient = moves.moved_class(
+ hosts_client.HostsClient, 'HostsClient',
+ __name__, version="Rocky", removal_version='?')
diff --git a/tempest/lib/services/volume/v2/limits_client.py b/tempest/lib/services/volume/v2/limits_client.py
index ce9fba9..a6b8c5a 100644
--- a/tempest/lib/services/volume/v2/limits_client.py
+++ b/tempest/lib/services/volume/v2/limits_client.py
@@ -12,21 +12,11 @@
# 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 debtcollector import moves
-from oslo_serialization import jsonutils as json
-
-from tempest.lib.common import rest_client
+from tempest.lib.services.volume.v3 import limits_client
-class LimitsClient(rest_client.RestClient):
- """Volume V2 limits client."""
-
- api_version = "v2"
-
- def show_limits(self):
- """Returns the details of a volume absolute limits."""
- url = "limits"
- resp, body = self.get(url)
- body = json.loads(body)
- self.expected_success(200, resp.status)
- return rest_client.ResponseBody(resp, body)
+LimitsClient = moves.moved_class(
+ limits_client.LimitsClient, 'LimitsClient',
+ __name__, version="Rocky", removal_version='?')
diff --git a/tempest/lib/services/volume/v2/qos_client.py b/tempest/lib/services/volume/v2/qos_client.py
index 47d3914..b81384e 100644
--- a/tempest/lib/services/volume/v2/qos_client.py
+++ b/tempest/lib/services/volume/v2/qos_client.py
@@ -11,123 +11,11 @@
# 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 debtcollector import moves
-from oslo_serialization import jsonutils as json
-
-from tempest.lib.common import rest_client
-from tempest.lib import exceptions as lib_exc
+from tempest.lib.services.volume.v3 import qos_client
-class QosSpecsClient(rest_client.RestClient):
- """Volume V2 QoS client.
-
- Client class to send CRUD QoS API requests
- """
-
- api_version = "v2"
-
- def is_resource_deleted(self, qos_id):
- try:
- self.show_qos(qos_id)
- except lib_exc.NotFound:
- return True
- return False
-
- @property
- def resource_type(self):
- """Returns the primary type of resource this client works with."""
- return 'qos'
-
- def create_qos(self, **kwargs):
- """Create a QoS Specification.
-
- For a full list of available parameters, please refer to the official
- API reference:
- http://developer.openstack.org/api-ref/block-storage/v2/#create-qos-specification
- """
- post_body = json.dumps({'qos_specs': kwargs})
- resp, body = self.post('qos-specs', post_body)
- self.expected_success(200, resp.status)
- body = json.loads(body)
- return rest_client.ResponseBody(resp, body)
-
- def delete_qos(self, qos_id, force=False):
- """Delete the specified QoS specification."""
- resp, body = self.delete(
- "qos-specs/%s?force=%s" % (qos_id, force))
- self.expected_success(202, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def list_qos(self):
- """List all the QoS specifications created."""
- url = 'qos-specs'
- resp, body = self.get(url)
- body = json.loads(body)
- self.expected_success(200, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def show_qos(self, qos_id):
- """Get the specified QoS specification."""
- url = "qos-specs/%s" % qos_id
- resp, body = self.get(url)
- body = json.loads(body)
- self.expected_success(200, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def set_qos_key(self, qos_id, **kwargs):
- """Set the specified keys/values of QoS specification.
-
- For a full list of available parameters, please refer to the official
- API reference:
- http://developer.openstack.org/api-ref/block-storage/v2/#set-keys-in-qos-specification
- """
- put_body = json.dumps({"qos_specs": kwargs})
- resp, body = self.put('qos-specs/%s' % qos_id, put_body)
- body = json.loads(body)
- self.expected_success(200, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def unset_qos_key(self, qos_id, keys):
- """Unset the specified keys of QoS specification.
-
- :param keys: keys to delete from the QoS specification.
-
- For a full list of available parameters, please refer to the official
- API reference:
- http://developer.openstack.org/api-ref/block-storage/v2/#unset-keys-in-qos-specification
- """
- put_body = json.dumps({'keys': keys})
- resp, body = self.put('qos-specs/%s/delete_keys' % qos_id, put_body)
- self.expected_success(202, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def associate_qos(self, qos_id, vol_type_id):
- """Associate the specified QoS with specified volume-type."""
- url = "qos-specs/%s/associate" % qos_id
- url += "?vol_type_id=%s" % vol_type_id
- resp, body = self.get(url)
- self.expected_success(202, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def show_association_qos(self, qos_id):
- """Get the association of the specified QoS specification."""
- url = "qos-specs/%s/associations" % qos_id
- resp, body = self.get(url)
- body = json.loads(body)
- self.expected_success(200, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def disassociate_qos(self, qos_id, vol_type_id):
- """Disassociate the specified QoS with specified volume-type."""
- url = "qos-specs/%s/disassociate" % qos_id
- url += "?vol_type_id=%s" % vol_type_id
- resp, body = self.get(url)
- self.expected_success(202, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def disassociate_all_qos(self, qos_id):
- """Disassociate the specified QoS with all associations."""
- url = "qos-specs/%s/disassociate_all" % qos_id
- resp, body = self.get(url)
- self.expected_success(202, resp.status)
- return rest_client.ResponseBody(resp, body)
+QosSpecsClient = moves.moved_class(
+ qos_client.QosSpecsClient, 'QosSpecsClient',
+ __name__, version="Rocky", removal_version='?')
diff --git a/tempest/lib/services/volume/v2/quota_classes_client.py b/tempest/lib/services/volume/v2/quota_classes_client.py
index 733b1ac..24aab89 100644
--- a/tempest/lib/services/volume/v2/quota_classes_client.py
+++ b/tempest/lib/services/volume/v2/quota_classes_client.py
@@ -12,40 +12,11 @@
# 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 debtcollector import moves
-from oslo_serialization import jsonutils as json
-
-from tempest.lib.common import rest_client
+from tempest.lib.services.volume.v3 import quota_classes_client
-class QuotaClassesClient(rest_client.RestClient):
- """Volume quota class V2 client."""
-
- api_version = "v2"
-
- def show_quota_class_set(self, quota_class_id):
- """List quotas for a quota class.
-
- For a full list of available parameters, please refer to the official
- API reference:
- https://developer.openstack.org/api-ref/block-storage/v2/index.html#show-quota-classes
- """
- url = 'os-quota-class-sets/%s' % quota_class_id
- resp, body = self.get(url)
- self.expected_success(200, resp.status)
- body = json.loads(body)
- return rest_client.ResponseBody(resp, body)
-
- def update_quota_class_set(self, quota_class_id, **kwargs):
- """Update quotas for a quota class.
-
- For a full list of available parameters, please refer to the official
- API reference:
- https://developer.openstack.org/api-ref/block-storage/v2/index.html#update-quota-classes
- """
- url = 'os-quota-class-sets/%s' % quota_class_id
- put_body = json.dumps({'quota_class_set': kwargs})
- resp, body = self.put(url, put_body)
- self.expected_success(200, resp.status)
- body = json.loads(body)
- return rest_client.ResponseBody(resp, body)
+QuotaClassesClient = moves.moved_class(
+ quota_classes_client.QuotaClassesClient, 'QuotaClassesClient',
+ __name__, version="Rocky", removal_version='?')
diff --git a/tempest/lib/services/volume/v2/quotas_client.py b/tempest/lib/services/volume/v2/quotas_client.py
index e4b2895..6f9f61c 100644
--- a/tempest/lib/services/volume/v2/quotas_client.py
+++ b/tempest/lib/services/volume/v2/quotas_client.py
@@ -12,53 +12,11 @@
# 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 debtcollector import moves
-from oslo_serialization import jsonutils
-from six.moves.urllib import parse as urllib
-
-from tempest.lib.common import rest_client
+from tempest.lib.services.volume.v3 import quotas_client
-class QuotasClient(rest_client.RestClient):
- """Client class to send CRUD Volume Quotas API V2 requests"""
- api_version = "v2"
-
- def show_default_quota_set(self, tenant_id):
- """List the default volume quota set for a tenant."""
-
- url = 'os-quota-sets/%s/defaults' % tenant_id
- resp, body = self.get(url)
- self.expected_success(200, resp.status)
- body = jsonutils.loads(body)
- return rest_client.ResponseBody(resp, body)
-
- def show_quota_set(self, tenant_id, params=None):
- """List the quota set for a tenant."""
-
- url = 'os-quota-sets/%s' % tenant_id
- if params:
- url += '?%s' % urllib.urlencode(params)
-
- resp, body = self.get(url)
- self.expected_success(200, resp.status)
- body = jsonutils.loads(body)
- return rest_client.ResponseBody(resp, body)
-
- def update_quota_set(self, tenant_id, **kwargs):
- """Updates quota set
-
- For a full list of available parameters, please refer to the official
- API reference:
- https://developer.openstack.org/api-ref/block-storage/v2/index.html#update-quotas
- """
- put_body = jsonutils.dumps({'quota_set': kwargs})
- resp, body = self.put('os-quota-sets/%s' % tenant_id, put_body)
- self.expected_success(200, resp.status)
- body = jsonutils.loads(body)
- return rest_client.ResponseBody(resp, body)
-
- def delete_quota_set(self, tenant_id):
- """Delete the tenant's quota set."""
- resp, body = self.delete('os-quota-sets/%s' % tenant_id)
- self.expected_success(200, resp.status)
- return rest_client.ResponseBody(resp, body)
+QuotasClient = moves.moved_class(
+ quotas_client.QuotasClient, 'QuotasClient',
+ __name__, version="Rocky", removal_version='?')
diff --git a/tempest/lib/services/volume/v2/scheduler_stats_client.py b/tempest/lib/services/volume/v2/scheduler_stats_client.py
index 0d04f85..a5adb34 100644
--- a/tempest/lib/services/volume/v2/scheduler_stats_client.py
+++ b/tempest/lib/services/volume/v2/scheduler_stats_client.py
@@ -12,26 +12,11 @@
# 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 debtcollector import moves
-from oslo_serialization import jsonutils as json
-
-from tempest.lib.common import rest_client
+from tempest.lib.services.volume.v3 import scheduler_stats_client
-class SchedulerStatsClient(rest_client.RestClient):
- api_version = "v2"
-
- def list_pools(self, detail=False):
- """List all the volumes pools (hosts).
-
- For a full list of available parameters, please refer to the official
- API reference:
- https://developer.openstack.org/api-ref/block-storage/v2/index.html#list-back-end-storage-pools
- """
- url = 'scheduler-stats/get_pools'
- if detail:
- url += '?detail=True'
- resp, body = self.get(url)
- body = json.loads(body)
- self.expected_success(200, resp.status)
- return rest_client.ResponseBody(resp, body)
+SchedulerStatsClient = moves.moved_class(
+ scheduler_stats_client.SchedulerStatsClient, 'SchedulerStatsClient',
+ __name__, version="Rocky", removal_version='?')
diff --git a/tempest/lib/services/volume/v2/services_client.py b/tempest/lib/services/volume/v2/services_client.py
index bc55469..a4491d3 100644
--- a/tempest/lib/services/volume/v2/services_client.py
+++ b/tempest/lib/services/volume/v2/services_client.py
@@ -12,23 +12,11 @@
# 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 debtcollector import moves
-from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
-
-from tempest.lib.common import rest_client
+from tempest.lib.services.volume.v3 import services_client
-class ServicesClient(rest_client.RestClient):
- """Client class to send CRUD Volume V2 API requests"""
- api_version = "v2"
-
- def list_services(self, **params):
- url = 'os-services'
- if params:
- url += '?%s' % urllib.urlencode(params)
-
- resp, body = self.get(url)
- body = json.loads(body)
- self.expected_success(200, resp.status)
- return rest_client.ResponseBody(resp, body)
+ServicesClient = moves.moved_class(
+ services_client.ServicesClient, 'ServicesClient',
+ __name__, version="Rocky", removal_version='?')
diff --git a/tempest/lib/services/volume/v2/snapshot_manage_client.py b/tempest/lib/services/volume/v2/snapshot_manage_client.py
index aecd30b..132209f 100644
--- a/tempest/lib/services/volume/v2/snapshot_manage_client.py
+++ b/tempest/lib/services/volume/v2/snapshot_manage_client.py
@@ -12,22 +12,11 @@
# 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 debtcollector import moves
-from oslo_serialization import jsonutils as json
-
-from tempest.lib.common import rest_client
+from tempest.lib.services.volume.v3 import snapshot_manage_client
-class SnapshotManageClient(rest_client.RestClient):
- """Snapshot manage V2 client."""
-
- api_version = "v2"
-
- def manage_snapshot(self, **kwargs):
- """Manage a snapshot."""
- post_body = json.dumps({'snapshot': kwargs})
- url = 'os-snapshot-manage'
- resp, body = self.post(url, post_body)
- self.expected_success(202, resp.status)
- body = json.loads(body)
- return rest_client.ResponseBody(resp, body)
+SnapshotManageClient = moves.moved_class(
+ snapshot_manage_client.SnapshotManageClient, 'SnapshotManageClient',
+ __name__, version="Rocky", removal_version='?')
diff --git a/tempest/lib/services/volume/v2/snapshots_client.py b/tempest/lib/services/volume/v2/snapshots_client.py
index 4bc2842..3a72cc1 100644
--- a/tempest/lib/services/volume/v2/snapshots_client.py
+++ b/tempest/lib/services/volume/v2/snapshots_client.py
@@ -9,200 +9,11 @@
# 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 debtcollector import moves
-from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
-
-from tempest.lib.common import rest_client
-from tempest.lib import exceptions as lib_exc
+from tempest.lib.services.volume.v3 import snapshots_client
-class SnapshotsClient(rest_client.RestClient):
- """Client class to send CRUD Volume V2 API requests."""
- api_version = "v2"
- create_resp = 202
-
- def list_snapshots(self, detail=False, **params):
- """List all the snapshot.
-
- 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-with-details
- http://developer.openstack.org/api-ref/block-storage/v2/#list-snapshots
- """
- url = 'snapshots'
- if detail:
- url += '/detail'
- if params:
- url += '?%s' % urllib.urlencode(params)
-
- resp, body = self.get(url)
- body = json.loads(body)
- self.expected_success(200, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def show_snapshot(self, snapshot_id):
- """Returns the details of a single snapshot.
-
- 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-details
- """
- url = "snapshots/%s" % snapshot_id
- resp, body = self.get(url)
- body = json.loads(body)
- self.expected_success(200, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def create_snapshot(self, **kwargs):
- """Creates a new snapshot.
-
- For a full list of available parameters, please refer to the official
- API reference:
- http://developer.openstack.org/api-ref/block-storage/v2/#create-snapshot
- """
- post_body = json.dumps({'snapshot': kwargs})
- resp, body = self.post('snapshots', post_body)
- body = json.loads(body)
- self.expected_success(self.create_resp, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def update_snapshot(self, snapshot_id, **kwargs):
- """Updates a snapshot.
-
- 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
- """
- put_body = json.dumps({'snapshot': kwargs})
- resp, body = self.put('snapshots/%s' % snapshot_id, put_body)
- body = json.loads(body)
- self.expected_success(200, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def delete_snapshot(self, snapshot_id):
- """Delete Snapshot.
-
- 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
- """
- resp, body = self.delete("snapshots/%s" % snapshot_id)
- self.expected_success(202, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def is_resource_deleted(self, id):
- try:
- self.show_snapshot(id)
- except lib_exc.NotFound:
- return True
- return False
-
- @property
- def resource_type(self):
- """Returns the primary type of resource this client works with."""
- return 'volume-snapshot'
-
- def reset_snapshot_status(self, snapshot_id, status):
- """Reset the specified snapshot's status."""
- post_body = json.dumps({'os-reset_status': {"status": status}})
- resp, body = self.post('snapshots/%s/action' % snapshot_id, post_body)
- self.expected_success(202, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def update_snapshot_status(self, snapshot_id, **kwargs):
- """Update the specified snapshot's status."""
- # TODO(gmann): api-site doesn't contain doc ref
- # for this API. After fixing the api-site, we need to
- # add the link here.
- # Bug https://bugs.launchpad.net/openstack-api-site/+bug/1532645
-
- post_body = json.dumps({'os-update_snapshot_status': kwargs})
- url = 'snapshots/%s/action' % snapshot_id
- resp, body = self.post(url, post_body)
- self.expected_success(202, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def create_snapshot_metadata(self, snapshot_id, metadata):
- """Create metadata for the snapshot.
-
- For a full list of available parameters, please refer to the official
- API reference:
- http://developer.openstack.org/api-ref/block-storage/v2/#create-snapshot-metadata
- """
- put_body = json.dumps({'metadata': metadata})
- url = "snapshots/%s/metadata" % snapshot_id
- resp, body = self.post(url, put_body)
- body = json.loads(body)
- self.expected_success(200, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def show_snapshot_metadata(self, snapshot_id):
- """Get metadata of the snapshot.
-
- 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
- """
- url = "snapshots/%s/metadata" % snapshot_id
- resp, body = self.get(url)
- body = json.loads(body)
- self.expected_success(200, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def update_snapshot_metadata(self, snapshot_id, **kwargs):
- """Update metadata for the snapshot.
-
- 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
- """
- put_body = json.dumps(kwargs)
- url = "snapshots/%s/metadata" % snapshot_id
- resp, body = self.put(url, put_body)
- body = json.loads(body)
- self.expected_success(200, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def show_snapshot_metadata_item(self, snapshot_id, id):
- """Show metadata item for the snapshot."""
- url = "snapshots/%s/metadata/%s" % (snapshot_id, id)
- resp, body = self.get(url)
- body = json.loads(body)
- self.expected_success(200, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def update_snapshot_metadata_item(self, snapshot_id, id, **kwargs):
- """Update metadata item for the snapshot."""
- # TODO(piyush): 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.
- # LP: https://bugs.launchpad.net/openstack-api-site/+bug/1529064
- put_body = json.dumps(kwargs)
- url = "snapshots/%s/metadata/%s" % (snapshot_id, id)
- resp, body = self.put(url, put_body)
- body = json.loads(body)
- self.expected_success(200, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def delete_snapshot_metadata_item(self, snapshot_id, id):
- """Delete metadata item for the snapshot."""
- url = "snapshots/%s/metadata/%s" % (snapshot_id, id)
- resp, body = self.delete(url)
- self.expected_success(200, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def force_delete_snapshot(self, snapshot_id):
- """Force Delete Snapshot."""
- post_body = json.dumps({'os-force_delete': {}})
- resp, body = self.post('snapshots/%s/action' % snapshot_id, post_body)
- self.expected_success(202, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def unmanage_snapshot(self, snapshot_id):
- """Unmanage a snapshot."""
- post_body = json.dumps({'os-unmanage': {}})
- url = 'snapshots/%s/action' % (snapshot_id)
- resp, body = self.post(url, post_body)
- self.expected_success(202, resp.status)
- return rest_client.ResponseBody(resp, body)
+SnapshotsClient = moves.moved_class(
+ snapshots_client.SnapshotsClient, 'SnapshotsClient',
+ __name__, version="Rocky", removal_version='?')
diff --git a/tempest/lib/services/volume/v2/transfers_client.py b/tempest/lib/services/volume/v2/transfers_client.py
index 2dfbe7b..701d0ae 100644
--- a/tempest/lib/services/volume/v2/transfers_client.py
+++ b/tempest/lib/services/volume/v2/transfers_client.py
@@ -12,72 +12,11 @@
# 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 debtcollector import moves
-from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
-
-from tempest.lib.common import rest_client
+from tempest.lib.services.volume.v3 import transfers_client
-class TransfersClient(rest_client.RestClient):
- """Client class to send CRUD Volume Transfer V2 API requests"""
- api_version = "v2"
-
- def create_volume_transfer(self, **kwargs):
- """Create a volume transfer.
-
- For a full list of available parameters, please refer to the official
- API reference:
- https://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)
- body = json.loads(body)
- self.expected_success(202, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def show_volume_transfer(self, transfer_id):
- """Returns the details of a volume transfer."""
- url = "os-volume-transfer/%s" % transfer_id
- resp, body = self.get(url)
- body = json.loads(body)
- self.expected_success(200, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def list_volume_transfers(self, detail=False, **params):
- """List all the volume transfers created.
-
- For a full list of available parameters, please refer to the official
- API reference:
- https://developer.openstack.org/api-ref/block-storage/v2/#list-volume-transfers
- https://developer.openstack.org/api-ref/block-storage/v2/#list-volume-transfers-with-details
- """
- url = 'os-volume-transfer'
- if detail:
- url += '/detail'
- if params:
- url += '?%s' % urllib.urlencode(params)
- resp, body = self.get(url)
- body = json.loads(body)
- self.expected_success(200, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def delete_volume_transfer(self, transfer_id):
- """Delete a volume transfer."""
- resp, body = self.delete("os-volume-transfer/%s" % transfer_id)
- self.expected_success(202, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def accept_volume_transfer(self, transfer_id, **kwargs):
- """Accept a volume transfer.
-
- For a full list of available parameters, please refer to the official
- API reference:
- https://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})
- resp, body = self.post(url, post_body)
- body = json.loads(body)
- self.expected_success(202, resp.status)
- return rest_client.ResponseBody(resp, body)
+TransfersClient = moves.moved_class(
+ transfers_client.TransfersClient, 'TransfersClient',
+ __name__, version="Rocky", removal_version='?')
diff --git a/tempest/lib/services/volume/v2/types_client.py b/tempest/lib/services/volume/v2/types_client.py
index af4fd8c..8457f91 100644
--- a/tempest/lib/services/volume/v2/types_client.py
+++ b/tempest/lib/services/volume/v2/types_client.py
@@ -12,194 +12,11 @@
# 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 debtcollector import moves
-from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
-
-from tempest.lib.common import rest_client
-from tempest.lib import exceptions as lib_exc
+from tempest.lib.services.volume.v3 import types_client
-class TypesClient(rest_client.RestClient):
- """Client class to send CRUD Volume V2 API requests"""
- api_version = "v2"
-
- def is_resource_deleted(self, id):
- try:
- self.show_volume_type(id)
- except lib_exc.NotFound:
- return True
- return False
-
- @property
- def resource_type(self):
- """Returns the primary type of resource this client works with."""
- return 'volume-type'
-
- def list_volume_types(self, **params):
- """List all the volume_types created.
-
- For a full list of available parameters, please refer to the official
- API reference:
- https://developer.openstack.org/api-ref/block-storage/v2/#list-all-volume-types-for-v2
- """
- url = 'types'
- if params:
- url += '?%s' % urllib.urlencode(params)
-
- resp, body = self.get(url)
- body = json.loads(body)
- self.expected_success(200, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def show_volume_type(self, volume_type_id):
- """Returns the details of a single volume_type.
-
- For a full list of available parameters, please refer to the official
- API reference:
- https://developer.openstack.org/api-ref/block-storage/v2/#show-volume-type-details-for-v2
- """
- url = "types/%s" % volume_type_id
- resp, body = self.get(url)
- body = json.loads(body)
- self.expected_success(200, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def create_volume_type(self, **kwargs):
- """Create volume type.
-
- For a full list of available parameters, please refer to the official
- API reference:
- https://developer.openstack.org/api-ref/block-storage/v2/#create-volume-type-for-v2
- """
- post_body = json.dumps({'volume_type': kwargs})
- resp, body = self.post('types', post_body)
- body = json.loads(body)
- self.expected_success(200, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def delete_volume_type(self, volume_type_id):
- """Deletes the Specified Volume_type.
-
- 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
- """
- resp, body = self.delete("types/%s" % volume_type_id)
- self.expected_success(202, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def list_volume_types_extra_specs(self, volume_type_id, **params):
- """List all the volume_types extra specs created.
-
- 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/extra_specs' % volume_type_id
- if params:
- url += '?%s' % urllib.urlencode(params)
-
- resp, body = self.get(url)
- body = json.loads(body)
- self.expected_success(200, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def show_volume_type_extra_specs(self, volume_type_id, extra_specs_name):
- """Returns the details of a single volume_type extra spec."""
- url = "types/%s/extra_specs/%s" % (volume_type_id, extra_specs_name)
- resp, body = self.get(url)
- body = json.loads(body)
- self.expected_success(200, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def create_volume_type_extra_specs(self, volume_type_id, extra_specs):
- """Creates a new Volume_type extra spec.
-
- volume_type_id: Id of volume_type.
- extra_specs: A dictionary of values to be used as extra_specs.
- """
- url = "types/%s/extra_specs" % volume_type_id
- post_body = json.dumps({'extra_specs': extra_specs})
- resp, body = self.post(url, post_body)
- body = json.loads(body)
- self.expected_success(200, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def delete_volume_type_extra_specs(self, volume_type_id, extra_spec_name):
- """Deletes the Specified Volume_type extra spec."""
- resp, body = self.delete("types/%s/extra_specs/%s" % (
- volume_type_id, extra_spec_name))
- self.expected_success(202, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def update_volume_type(self, volume_type_id, **kwargs):
- """Updates volume type name, description, and/or is_public.
-
- 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
- """
- put_body = json.dumps({'volume_type': kwargs})
- resp, body = self.put('types/%s' % volume_type_id, put_body)
- body = json.loads(body)
- self.expected_success(200, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def update_volume_type_extra_specs(self, volume_type_id, extra_spec_name,
- extra_specs):
- """Update a volume_type extra spec.
-
- volume_type_id: Id of volume_type.
- extra_spec_name: Name of the extra spec to be updated.
- extra_spec: A dictionary of with key as extra_spec_name and the
- updated value.
- For a full list of available parameters, please refer to the official
- API reference:
- 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)
- resp, body = self.put(url, put_body)
- body = json.loads(body)
- self.expected_success(200, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def add_type_access(self, volume_type_id, **kwargs):
- """Adds volume type access for the given project.
-
- For a full list of available parameters, please refer to the official
- API reference:
- 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
- resp, body = self.post(url, post_body)
- self.expected_success(202, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def remove_type_access(self, volume_type_id, **kwargs):
- """Removes volume type access for the given project.
-
- For a full list of available parameters, please refer to the official
- API reference:
- 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
- resp, body = self.post(url, post_body)
- self.expected_success(202, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def list_type_access(self, volume_type_id):
- """Print access information about the given volume type.
-
- For a full list of available parameters, please refer to the official
- API reference:
- 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)
- body = json.loads(body)
- self.expected_success(200, resp.status)
- return rest_client.ResponseBody(resp, body)
+TypesClient = moves.moved_class(
+ types_client.TypesClient, 'TypesClient',
+ __name__, version="Rocky", removal_version='?')
diff --git a/tempest/lib/services/volume/v2/volume_manage_client.py b/tempest/lib/services/volume/v2/volume_manage_client.py
index 12f4240..0669326 100644
--- a/tempest/lib/services/volume/v2/volume_manage_client.py
+++ b/tempest/lib/services/volume/v2/volume_manage_client.py
@@ -12,26 +12,11 @@
# 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 debtcollector import moves
-from oslo_serialization import jsonutils as json
-
-from tempest.lib.common import rest_client
+from tempest.lib.services.volume.v3 import volume_manage_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)
+VolumeManageClient = moves.moved_class(
+ volume_manage_client.VolumeManageClient, 'VolumeManageClient',
+ __name__, version="Rocky", removal_version='?')
diff --git a/tempest/lib/services/volume/v2/volumes_client.py b/tempest/lib/services/volume/v2/volumes_client.py
index da3f2b5..f5f9e6e 100644
--- a/tempest/lib/services/volume/v2/volumes_client.py
+++ b/tempest/lib/services/volume/v2/volumes_client.py
@@ -12,341 +12,11 @@
# 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 debtcollector import moves
-from oslo_serialization import jsonutils as json
-import six
-from six.moves.urllib import parse as urllib
-
-from tempest.lib.common import rest_client
-from tempest.lib import exceptions as lib_exc
-from tempest.lib.services.volume import base_client
+from tempest.lib.services.volume.v3 import volumes_client
-class VolumesClient(base_client.BaseClient):
- """Client class to send CRUD Volume V2 API requests"""
- api_version = "v2"
-
- def _prepare_params(self, params):
- """Prepares params for use in get or _ext_get methods.
-
- If params is a string it will be left as it is, but if it's not it will
- be urlencoded.
- """
- if isinstance(params, six.string_types):
- return params
- return urllib.urlencode(params)
-
- def list_volumes(self, detail=False, params=None):
- """List all the volumes created.
-
- Params can be a string (must be urlencoded) or a dictionary.
- For a full list of available parameters, please refer to the official
- API reference:
- http://developer.openstack.org/api-ref/block-storage/v2/#list-volumes-with-details
- http://developer.openstack.org/api-ref/block-storage/v2/#list-volumes
- """
- url = 'volumes'
- if detail:
- url += '/detail'
- if params:
- url += '?%s' % self._prepare_params(params)
-
- resp, body = self.get(url)
- body = json.loads(body)
- self.expected_success(200, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def show_volume(self, volume_id):
- """Returns the details of a single volume."""
- url = "volumes/%s" % volume_id
- resp, body = self.get(url)
- body = json.loads(body)
- self.expected_success(200, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def create_volume(self, **kwargs):
- """Creates a new Volume.
-
- 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
- """
- post_body = json.dumps({'volume': kwargs})
- resp, body = self.post('volumes', post_body)
- body = json.loads(body)
- self.expected_success(202, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def update_volume(self, volume_id, **kwargs):
- """Updates the Specified Volume.
-
- 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
- """
- put_body = json.dumps({'volume': kwargs})
- resp, body = self.put('volumes/%s' % volume_id, put_body)
- body = json.loads(body)
- self.expected_success(200, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def delete_volume(self, volume_id, **params):
- """Deletes the Specified Volume.
-
- For a full list of available parameters, please refer to the official
- API reference:
- https://developer.openstack.org/api-ref/block-storage/v2/#delete-volume
- """
- url = 'volumes/%s' % volume_id
- if params:
- url += '?%s' % urllib.urlencode(params)
- resp, body = self.delete(url)
- self.expected_success(202, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def upload_volume(self, volume_id, **kwargs):
- """Uploads a volume in Glance."""
- post_body = json.dumps({'os-volume_upload_image': kwargs})
- url = 'volumes/%s/action' % (volume_id)
- resp, body = self.post(url, post_body)
- body = json.loads(body)
- self.expected_success(202, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def attach_volume(self, volume_id, **kwargs):
- """Attaches a volume to a given instance on a given mountpoint.
-
- 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-to-server
- """
- post_body = json.dumps({'os-attach': kwargs})
- url = 'volumes/%s/action' % (volume_id)
- resp, body = self.post(url, post_body)
- self.expected_success(202, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def set_bootable_volume(self, volume_id, **kwargs):
- """Set a bootable flag for a volume - true or false.
-
- 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-bootable-status
- """
- post_body = json.dumps({'os-set_bootable': kwargs})
- url = 'volumes/%s/action' % (volume_id)
- resp, body = self.post(url, post_body)
- self.expected_success(200, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def detach_volume(self, volume_id):
- """Detaches a volume from an instance."""
- post_body = json.dumps({'os-detach': {}})
- url = 'volumes/%s/action' % (volume_id)
- resp, body = self.post(url, post_body)
- self.expected_success(202, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def reserve_volume(self, volume_id):
- """Reserves a volume."""
- post_body = json.dumps({'os-reserve': {}})
- url = 'volumes/%s/action' % (volume_id)
- resp, body = self.post(url, post_body)
- self.expected_success(202, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def unreserve_volume(self, volume_id):
- """Restore a reserved volume ."""
- post_body = json.dumps({'os-unreserve': {}})
- url = 'volumes/%s/action' % (volume_id)
- resp, body = self.post(url, post_body)
- self.expected_success(202, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def is_resource_deleted(self, id):
- """Check the specified resource is deleted or not.
-
- :param id: A checked resource id
- :raises lib_exc.DeleteErrorException: If the specified resource is on
- the status the delete was failed.
- """
- try:
- volume = self.show_volume(id)
- except lib_exc.NotFound:
- return True
- if volume["volume"]["status"] == "error_deleting":
- raise lib_exc.DeleteErrorException(resource_id=id)
- return False
-
- @property
- def resource_type(self):
- """Returns the primary type of resource this client works with."""
- return 'volume'
-
- def extend_volume(self, volume_id, **kwargs):
- """Extend a volume.
-
- 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-size
- """
- post_body = json.dumps({'os-extend': kwargs})
- url = 'volumes/%s/action' % (volume_id)
- resp, body = self.post(url, post_body)
- self.expected_success(202, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def reset_volume_status(self, volume_id, **kwargs):
- """Reset the Specified Volume's Status.
-
- 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-statuses
- """
- post_body = json.dumps({'os-reset_status': kwargs})
- resp, body = self.post('volumes/%s/action' % volume_id, post_body)
- self.expected_success(202, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def update_volume_readonly(self, volume_id, **kwargs):
- """Update the Specified Volume readonly."""
- post_body = json.dumps({'os-update_readonly_flag': kwargs})
- url = 'volumes/%s/action' % (volume_id)
- resp, body = self.post(url, post_body)
- self.expected_success(202, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def force_delete_volume(self, volume_id):
- """Force Delete Volume."""
- post_body = json.dumps({'os-force_delete': {}})
- resp, body = self.post('volumes/%s/action' % volume_id, post_body)
- self.expected_success(202, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def create_volume_metadata(self, volume_id, metadata):
- """Create metadata for the volume.
-
- 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-metadata
- """
- put_body = json.dumps({'metadata': metadata})
- url = "volumes/%s/metadata" % volume_id
- resp, body = self.post(url, put_body)
- body = json.loads(body)
- self.expected_success(200, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def show_volume_metadata(self, volume_id):
- """Get metadata of the volume."""
- url = "volumes/%s/metadata" % volume_id
- resp, body = self.get(url)
- body = json.loads(body)
- self.expected_success(200, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def update_volume_metadata(self, volume_id, metadata):
- """Update metadata for the volume.
-
- 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-metadata
- """
- put_body = json.dumps({'metadata': metadata})
- url = "volumes/%s/metadata" % volume_id
- resp, body = self.put(url, put_body)
- body = json.loads(body)
- self.expected_success(200, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def show_volume_metadata_item(self, volume_id, id):
- """Show metadata item for the volume."""
- url = "volumes/%s/metadata/%s" % (volume_id, id)
- resp, body = self.get(url)
- body = json.loads(body)
- self.expected_success(200, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def update_volume_metadata_item(self, volume_id, id, meta_item):
- """Update metadata item for the volume."""
- put_body = json.dumps({'meta': meta_item})
- url = "volumes/%s/metadata/%s" % (volume_id, id)
- resp, body = self.put(url, put_body)
- body = json.loads(body)
- self.expected_success(200, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def delete_volume_metadata_item(self, volume_id, id):
- """Delete metadata item for the volume."""
- url = "volumes/%s/metadata/%s" % (volume_id, id)
- resp, body = self.delete(url)
- self.expected_success(200, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def retype_volume(self, volume_id, **kwargs):
- """Updates volume with new volume type.
-
- For a full list of available parameters, please refer to the official
- API reference:
- https://developer.openstack.org/api-ref/block-storage/v2/#retype-volume
- """
- post_body = json.dumps({'os-retype': kwargs})
- resp, body = self.post('volumes/%s/action' % volume_id, post_body)
- self.expected_success(202, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def force_detach_volume(self, volume_id, **kwargs):
- """Force detach a volume.
-
- For a full list of available parameters, please refer to the official
- API reference:
- https://developer.openstack.org/api-ref/block-storage/v2/#force-detach-volume
- """
- post_body = json.dumps({'os-force_detach': kwargs})
- url = 'volumes/%s/action' % volume_id
- resp, body = self.post(url, post_body)
- self.expected_success(202, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def update_volume_image_metadata(self, volume_id, **kwargs):
- """Update image metadata for the volume.
-
- For a full list of available parameters, please refer to the official
- API reference:
- 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)
- resp, body = self.post(url, post_body)
- body = json.loads(body)
- self.expected_success(200, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def delete_volume_image_metadata(self, volume_id, key_name):
- """Delete image metadata item for the volume."""
- post_body = json.dumps({'os-unset_image_metadata': {'key': key_name}})
- url = "volumes/%s/action" % (volume_id)
- resp, body = self.post(url, post_body)
- self.expected_success(200, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def show_volume_image_metadata(self, volume_id):
- """Show image metadata for the volume."""
- post_body = json.dumps({'os-show_image_metadata': {}})
- url = "volumes/%s/action" % volume_id
- resp, body = self.post(url, post_body)
- 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)
+VolumesClient = moves.moved_class(
+ volumes_client.VolumesClient, 'VolumesClient',
+ __name__, version="Rocky", removal_version='?')
diff --git a/tempest/lib/services/volume/v3/__init__.py b/tempest/lib/services/volume/v3/__init__.py
index 2d85553..a1b7de3 100644
--- a/tempest/lib/services/volume/v3/__init__.py
+++ b/tempest/lib/services/volume/v3/__init__.py
@@ -11,19 +11,44 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.
-
+from tempest.lib.services.volume.v3.availability_zone_client \
+ import AvailabilityZoneClient
from tempest.lib.services.volume.v3.backups_client import BackupsClient
from tempest.lib.services.volume.v3.base_client import BaseClient
+from tempest.lib.services.volume.v3.capabilities_client import \
+ CapabilitiesClient
+from tempest.lib.services.volume.v3.encryption_types_client import \
+ EncryptionTypesClient
+from tempest.lib.services.volume.v3.extensions_client import ExtensionsClient
from tempest.lib.services.volume.v3.group_snapshots_client import \
GroupSnapshotsClient
from tempest.lib.services.volume.v3.group_types_client import GroupTypesClient
from tempest.lib.services.volume.v3.groups_client import GroupsClient
+from tempest.lib.services.volume.v3.hosts_client import HostsClient
+from tempest.lib.services.volume.v3.limits_client import LimitsClient
from tempest.lib.services.volume.v3.messages_client import MessagesClient
+from tempest.lib.services.volume.v3.qos_client import QosSpecsClient
+from tempest.lib.services.volume.v3.quota_classes_client import \
+ QuotaClassesClient
+from tempest.lib.services.volume.v3.quotas_client import QuotasClient
+from tempest.lib.services.volume.v3.scheduler_stats_client import \
+ SchedulerStatsClient
+from tempest.lib.services.volume.v3.services_client import ServicesClient
+from tempest.lib.services.volume.v3.snapshot_manage_client import \
+ SnapshotManageClient
from tempest.lib.services.volume.v3.snapshots_client import SnapshotsClient
+from tempest.lib.services.volume.v3.transfers_client import TransfersClient
+from tempest.lib.services.volume.v3.types_client import TypesClient
from tempest.lib.services.volume.v3.versions_client import VersionsClient
+from tempest.lib.services.volume.v3.volume_manage_client import \
+ VolumeManageClient
from tempest.lib.services.volume.v3.volumes_client import VolumesClient
-__all__ = ['BackupsClient', 'BaseClient', 'GroupsClient',
- 'GroupSnapshotsClient', 'GroupTypesClient',
- 'MessagesClient', 'SnapshotsClient', 'VersionsClient',
- 'VolumesClient']
+__all__ = ['AvailabilityZoneClient', 'BackupsClient', 'BaseClient',
+ 'CapabilitiesClient', 'EncryptionTypesClient', 'ExtensionsClient',
+ 'GroupSnapshotsClient', 'GroupTypesClient', 'GroupsClient',
+ 'HostsClient', 'LimitsClient', 'MessagesClient', 'QosSpecsClient',
+ 'QuotaClassesClient', 'QuotasClient', 'SchedulerStatsClient',
+ 'ServicesClient', 'SnapshotManageClient', 'SnapshotsClient',
+ 'TransfersClient', 'TypesClient', 'VersionsClient',
+ 'VolumeManageClient', 'VolumesClient']
diff --git a/tempest/lib/services/volume/v3/availability_zone_client.py b/tempest/lib/services/volume/v3/availability_zone_client.py
new file mode 100644
index 0000000..147e4c6
--- /dev/null
+++ b/tempest/lib/services/volume/v3/availability_zone_client.py
@@ -0,0 +1,27 @@
+# Copyright 2014 IBM Corp.
+# 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 AvailabilityZoneClient(rest_client.RestClient):
+
+ def list_availability_zones(self):
+ resp, body = self.get('os-availability-zone')
+ body = json.loads(body)
+ self.expected_success(200, resp.status)
+ return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v3/backups_client.py b/tempest/lib/services/volume/v3/backups_client.py
index e742e39..10538b0 100644
--- a/tempest/lib/services/volume/v3/backups_client.py
+++ b/tempest/lib/services/volume/v3/backups_client.py
@@ -14,15 +14,30 @@
# under the License.
from oslo_serialization import jsonutils as json
+from six.moves.urllib import parse as urllib
from tempest.lib.common import rest_client
-from tempest.lib.services.volume.v2 import backups_client
+from tempest.lib import exceptions as lib_exc
+from tempest.lib.services.volume import base_client
-class BackupsClient(backups_client.BackupsClient):
+class BackupsClient(base_client.BaseClient):
"""Volume V3 Backups client"""
api_version = "v3"
+ def create_backup(self, **kwargs):
+ """Creates a backup of volume.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/block-storage/v3/index.html#create-a-backup
+ """
+ post_body = json.dumps({'backup': kwargs})
+ resp, body = self.post('backups', post_body)
+ body = json.loads(body)
+ self.expected_success(202, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
def update_backup(self, backup_id, **kwargs):
"""Updates the specified volume backup.
@@ -35,3 +50,83 @@
body = json.loads(body)
self.expected_success(200, resp.status)
return rest_client.ResponseBody(resp, body)
+
+ def restore_backup(self, backup_id, **kwargs):
+ """Restore volume from backup.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/block-storage/v3/index.html#restore-a-backup
+ """
+ post_body = json.dumps({'restore': kwargs})
+ resp, body = self.post('backups/%s/restore' % (backup_id), post_body)
+ body = json.loads(body)
+ self.expected_success(202, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def delete_backup(self, backup_id):
+ """Delete a backup of volume."""
+ resp, body = self.delete('backups/%s' % backup_id)
+ self.expected_success(202, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def show_backup(self, backup_id):
+ """Returns the details of a single backup."""
+ url = "backups/%s" % backup_id
+ resp, body = self.get(url)
+ body = json.loads(body)
+ self.expected_success(200, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def list_backups(self, detail=False, **params):
+ """List all the tenant's backups.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/block-storage/v3/index.html#list-backups-for-project
+ https://developer.openstack.org/api-ref/block-storage/v3/index.html#list-backups-with-detail
+ """
+ url = "backups"
+ if detail:
+ url += "/detail"
+ if params:
+ url += '?%s' % urllib.urlencode(params)
+ resp, body = self.get(url)
+ body = json.loads(body)
+ self.expected_success(200, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def export_backup(self, backup_id):
+ """Export backup metadata record."""
+ url = "backups/%s/export_record" % backup_id
+ resp, body = self.get(url)
+ body = json.loads(body)
+ self.expected_success(200, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def import_backup(self, **kwargs):
+ """Import backup metadata record."""
+ post_body = json.dumps({'backup-record': kwargs})
+ resp, body = self.post("backups/import_record", post_body)
+ body = json.loads(body)
+ self.expected_success(201, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def reset_backup_status(self, backup_id, status):
+ """Reset the specified backup's status."""
+ post_body = json.dumps({'os-reset_status': {"status": status}})
+ resp, body = self.post('backups/%s/action' % backup_id, post_body)
+ self.expected_success(202, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def is_resource_deleted(self, id):
+ try:
+ self.show_backup(id)
+ except lib_exc.NotFound:
+ return True
+ return False
+
+ @property
+ def resource_type(self):
+ """Returns the primary type of resource this client works with."""
+ return 'backup'
diff --git a/tempest/lib/services/volume/v3/capabilities_client.py b/tempest/lib/services/volume/v3/capabilities_client.py
new file mode 100644
index 0000000..7ebcd69
--- /dev/null
+++ b/tempest/lib/services/volume/v3/capabilities_client.py
@@ -0,0 +1,34 @@
+# Copyright 2016 Red Hat, Inc.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from oslo_serialization import jsonutils as json
+
+from tempest.lib.common import rest_client
+
+
+class CapabilitiesClient(rest_client.RestClient):
+
+ def show_backend_capabilities(self, host):
+ """Shows capabilities for a storage back end.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/block-storage/v2/index.html#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)
diff --git a/tempest/lib/services/volume/v3/encryption_types_client.py b/tempest/lib/services/volume/v3/encryption_types_client.py
new file mode 100644
index 0000000..7443a87
--- /dev/null
+++ b/tempest/lib/services/volume/v3/encryption_types_client.py
@@ -0,0 +1,90 @@
+# Copyright 2012 OpenStack Foundation
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from oslo_serialization import jsonutils as json
+
+from tempest.lib.common import rest_client
+from tempest.lib import exceptions as lib_exc
+
+
+class EncryptionTypesClient(rest_client.RestClient):
+
+ def is_resource_deleted(self, id):
+ try:
+ body = self.show_encryption_type(id)
+ if not body:
+ return True
+ except lib_exc.NotFound:
+ return True
+ return False
+
+ @property
+ def resource_type(self):
+ """Returns the primary type of resource this client works with."""
+ return 'encryption-type'
+
+ def show_encryption_type(self, volume_type_id):
+ """Get the volume encryption type for the specified volume type.
+
+ volume_type_id: Id of volume_type.
+ """
+ url = "/types/%s/encryption" % volume_type_id
+ resp, body = self.get(url)
+ body = json.loads(body)
+ self.expected_success(200, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def show_encryption_specs_item(self, volume_type_id, key):
+ """Get the encryption specs item for the specified volume type."""
+ url = "/types/%s/encryption/%s" % (volume_type_id, key)
+ resp, body = self.get(url)
+ body = json.loads(body)
+ self.expected_success(200, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def create_encryption_type(self, volume_type_id, **kwargs):
+ """Create encryption type.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/block-storage/v3/index.html#create-an-encryption-type
+ """
+ url = "/types/%s/encryption" % volume_type_id
+ post_body = json.dumps({'encryption': kwargs})
+ resp, body = self.post(url, post_body)
+ body = json.loads(body)
+ self.expected_success(200, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def delete_encryption_type(self, volume_type_id):
+ """Delete the encryption type for the specified volume-type."""
+ resp, body = self.delete(
+ "/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.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/block-storage/v3/index.html#update-an-encryption-type
+ """
+ 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/v3/extensions_client.py b/tempest/lib/services/volume/v3/extensions_client.py
new file mode 100644
index 0000000..45b7a56
--- /dev/null
+++ b/tempest/lib/services/volume/v3/extensions_client.py
@@ -0,0 +1,29 @@
+# Copyright 2014 IBM Corp.
+# 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 ExtensionsClient(rest_client.RestClient):
+ """Volume extensions client."""
+
+ def list_extensions(self):
+ url = 'extensions'
+ resp, body = self.get(url)
+ body = json.loads(body)
+ self.expected_success(200, resp.status)
+ return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v3/hosts_client.py b/tempest/lib/services/volume/v3/hosts_client.py
new file mode 100644
index 0000000..8b65805
--- /dev/null
+++ b/tempest/lib/services/volume/v3/hosts_client.py
@@ -0,0 +1,47 @@
+# Copyright 2014 OpenStack Foundation
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from oslo_serialization import jsonutils as json
+from six.moves.urllib import parse as urllib
+
+from tempest.lib.common import rest_client
+
+
+class HostsClient(rest_client.RestClient):
+ """Client class to send CRUD Volume API requests"""
+
+ def list_hosts(self, **params):
+ """Lists all hosts.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/block-storage/v3/index.html#list-all-hosts-for-a-project
+ """
+ url = 'os-hosts'
+ if params:
+ url += '?%s' % urllib.urlencode(params)
+
+ resp, body = self.get(url)
+ body = json.loads(body)
+ self.expected_success(200, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def show_host(self, host_name):
+ """Show host details."""
+ url = 'os-hosts/%s' % host_name
+ resp, body = self.get(url)
+ self.expected_success(200, resp.status)
+ body = json.loads(body)
+ return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v3/limits_client.py b/tempest/lib/services/volume/v3/limits_client.py
new file mode 100644
index 0000000..9500254
--- /dev/null
+++ b/tempest/lib/services/volume/v3/limits_client.py
@@ -0,0 +1,30 @@
+# Copyright 2016 Red Hat, Inc.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from oslo_serialization import jsonutils as json
+
+from tempest.lib.common import rest_client
+
+
+class LimitsClient(rest_client.RestClient):
+ """Volume limits client."""
+
+ def show_limits(self):
+ """Returns the details of a volume absolute limits."""
+ url = "limits"
+ resp, body = self.get(url)
+ body = json.loads(body)
+ self.expected_success(200, resp.status)
+ return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v3/qos_client.py b/tempest/lib/services/volume/v3/qos_client.py
new file mode 100644
index 0000000..8f4d37f
--- /dev/null
+++ b/tempest/lib/services/volume/v3/qos_client.py
@@ -0,0 +1,131 @@
+# 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
+from tempest.lib import exceptions as lib_exc
+
+
+class QosSpecsClient(rest_client.RestClient):
+ """Volume QoS client.
+
+ Client class to send CRUD QoS API requests
+ """
+
+ def is_resource_deleted(self, qos_id):
+ try:
+ self.show_qos(qos_id)
+ except lib_exc.NotFound:
+ return True
+ return False
+
+ @property
+ def resource_type(self):
+ """Returns the primary type of resource this client works with."""
+ return 'qos'
+
+ def create_qos(self, **kwargs):
+ """Create a QoS Specification.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/block-storage/v3/index.html#create-a-qos-specification
+ """
+ post_body = json.dumps({'qos_specs': kwargs})
+ resp, body = self.post('qos-specs', post_body)
+ self.expected_success(200, resp.status)
+ body = json.loads(body)
+ return rest_client.ResponseBody(resp, body)
+
+ def delete_qos(self, qos_id, force=False):
+ """Delete the specified QoS specification."""
+ resp, body = self.delete(
+ "qos-specs/%s?force=%s" % (qos_id, force))
+ self.expected_success(202, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def list_qos(self):
+ """List all the QoS specifications created."""
+ url = 'qos-specs'
+ resp, body = self.get(url)
+ body = json.loads(body)
+ self.expected_success(200, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def show_qos(self, qos_id):
+ """Get the specified QoS specification."""
+ url = "qos-specs/%s" % qos_id
+ resp, body = self.get(url)
+ body = json.loads(body)
+ self.expected_success(200, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def set_qos_key(self, qos_id, **kwargs):
+ """Set the specified keys/values of QoS specification.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/block-storage/v3/index.html#set-keys-in-a-qos-specification
+ """
+ put_body = json.dumps({"qos_specs": kwargs})
+ resp, body = self.put('qos-specs/%s' % qos_id, put_body)
+ body = json.loads(body)
+ self.expected_success(200, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def unset_qos_key(self, qos_id, keys):
+ """Unset the specified keys of QoS specification.
+
+ :param keys: keys to delete from the QoS specification.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/block-storage/v3/index.html#unset-keys-in-a-qos-specification
+ """
+ put_body = json.dumps({'keys': keys})
+ resp, body = self.put('qos-specs/%s/delete_keys' % qos_id, put_body)
+ self.expected_success(202, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def associate_qos(self, qos_id, vol_type_id):
+ """Associate the specified QoS with specified volume-type."""
+ url = "qos-specs/%s/associate" % qos_id
+ url += "?vol_type_id=%s" % vol_type_id
+ resp, body = self.get(url)
+ self.expected_success(202, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def show_association_qos(self, qos_id):
+ """Get the association of the specified QoS specification."""
+ url = "qos-specs/%s/associations" % qos_id
+ resp, body = self.get(url)
+ body = json.loads(body)
+ self.expected_success(200, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def disassociate_qos(self, qos_id, vol_type_id):
+ """Disassociate the specified QoS with specified volume-type."""
+ url = "qos-specs/%s/disassociate" % qos_id
+ url += "?vol_type_id=%s" % vol_type_id
+ resp, body = self.get(url)
+ self.expected_success(202, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def disassociate_all_qos(self, qos_id):
+ """Disassociate the specified QoS with all associations."""
+ url = "qos-specs/%s/disassociate_all" % qos_id
+ resp, body = self.get(url)
+ self.expected_success(202, resp.status)
+ return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v3/quota_classes_client.py b/tempest/lib/services/volume/v3/quota_classes_client.py
new file mode 100644
index 0000000..a8eb536
--- /dev/null
+++ b/tempest/lib/services/volume/v3/quota_classes_client.py
@@ -0,0 +1,49 @@
+# 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 QuotaClassesClient(rest_client.RestClient):
+ """Volume quota class client."""
+
+ def show_quota_class_set(self, quota_class_id):
+ """List quotas for a quota class.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/block-storage/v3/index.html#show-quota-classes-for-a-project
+ """
+ url = 'os-quota-class-sets/%s' % quota_class_id
+ resp, body = self.get(url)
+ self.expected_success(200, resp.status)
+ body = json.loads(body)
+ return rest_client.ResponseBody(resp, body)
+
+ def update_quota_class_set(self, quota_class_id, **kwargs):
+ """Update quotas for a quota class.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/block-storage/v3/index.html#update-quota-classes-for-a-project
+ """
+ url = 'os-quota-class-sets/%s' % quota_class_id
+ put_body = json.dumps({'quota_class_set': kwargs})
+ resp, body = self.put(url, put_body)
+ self.expected_success(200, resp.status)
+ body = json.loads(body)
+ return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v3/quotas_client.py b/tempest/lib/services/volume/v3/quotas_client.py
new file mode 100644
index 0000000..538a915
--- /dev/null
+++ b/tempest/lib/services/volume/v3/quotas_client.py
@@ -0,0 +1,63 @@
+# Copyright 2014 OpenStack Foundation
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from oslo_serialization import jsonutils
+from six.moves.urllib import parse as urllib
+
+from tempest.lib.common import rest_client
+
+
+class QuotasClient(rest_client.RestClient):
+ """Client class to send CRUD Volume Quotas API requests"""
+
+ def show_default_quota_set(self, tenant_id):
+ """List the default volume quota set for a tenant."""
+
+ url = 'os-quota-sets/%s/defaults' % tenant_id
+ resp, body = self.get(url)
+ self.expected_success(200, resp.status)
+ body = jsonutils.loads(body)
+ return rest_client.ResponseBody(resp, body)
+
+ def show_quota_set(self, tenant_id, params=None):
+ """List the quota set for a tenant."""
+
+ url = 'os-quota-sets/%s' % tenant_id
+ if params:
+ url += '?%s' % urllib.urlencode(params)
+
+ resp, body = self.get(url)
+ self.expected_success(200, resp.status)
+ body = jsonutils.loads(body)
+ return rest_client.ResponseBody(resp, body)
+
+ def update_quota_set(self, tenant_id, **kwargs):
+ """Updates quota set
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/block-storage/v3/index.html#update-quotas-for-a-project
+ """
+ put_body = jsonutils.dumps({'quota_set': kwargs})
+ resp, body = self.put('os-quota-sets/%s' % tenant_id, put_body)
+ self.expected_success(200, resp.status)
+ body = jsonutils.loads(body)
+ return rest_client.ResponseBody(resp, body)
+
+ def delete_quota_set(self, tenant_id):
+ """Delete the tenant's quota set."""
+ resp, body = self.delete('os-quota-sets/%s' % tenant_id)
+ self.expected_success(200, resp.status)
+ return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v3/scheduler_stats_client.py b/tempest/lib/services/volume/v3/scheduler_stats_client.py
new file mode 100644
index 0000000..9b80851
--- /dev/null
+++ b/tempest/lib/services/volume/v3/scheduler_stats_client.py
@@ -0,0 +1,36 @@
+# Copyright 2016 Red Hat, Inc.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from oslo_serialization import jsonutils as json
+
+from tempest.lib.common import rest_client
+
+
+class SchedulerStatsClient(rest_client.RestClient):
+
+ def list_pools(self, detail=False):
+ """List all the volumes pools (hosts).
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/block-storage/v3/index.html#list-all-back-end-storage-pools
+ """
+ url = 'scheduler-stats/get_pools'
+ if detail:
+ url += '?detail=True'
+ resp, body = self.get(url)
+ body = json.loads(body)
+ self.expected_success(200, resp.status)
+ return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v3/services_client.py b/tempest/lib/services/volume/v3/services_client.py
new file mode 100644
index 0000000..09036a4
--- /dev/null
+++ b/tempest/lib/services/volume/v3/services_client.py
@@ -0,0 +1,33 @@
+# Copyright 2014 OpenStack Foundation
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from oslo_serialization import jsonutils as json
+from six.moves.urllib import parse as urllib
+
+from tempest.lib.common import rest_client
+
+
+class ServicesClient(rest_client.RestClient):
+ """Client class to send CRUD Volume API requests"""
+
+ def list_services(self, **params):
+ url = 'os-services'
+ if params:
+ url += '?%s' % urllib.urlencode(params)
+
+ resp, body = self.get(url)
+ body = json.loads(body)
+ self.expected_success(200, resp.status)
+ return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v3/snapshot_manage_client.py b/tempest/lib/services/volume/v3/snapshot_manage_client.py
new file mode 100644
index 0000000..43fd328
--- /dev/null
+++ b/tempest/lib/services/volume/v3/snapshot_manage_client.py
@@ -0,0 +1,31 @@
+# Copyright 2016 Red Hat, Inc.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from oslo_serialization import jsonutils as json
+
+from tempest.lib.common import rest_client
+
+
+class SnapshotManageClient(rest_client.RestClient):
+ """Snapshot manage client."""
+
+ def manage_snapshot(self, **kwargs):
+ """Manage a snapshot."""
+ post_body = json.dumps({'snapshot': kwargs})
+ url = 'os-snapshot-manage'
+ resp, body = self.post(url, 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/v3/snapshots_client.py b/tempest/lib/services/volume/v3/snapshots_client.py
index 88c094f..298925a 100644
--- a/tempest/lib/services/volume/v3/snapshots_client.py
+++ b/tempest/lib/services/volume/v3/snapshots_client.py
@@ -13,9 +13,199 @@
# License for the specific language governing permissions and limitations
# under the License.
-from tempest.lib.services.volume.v2 import snapshots_client
+from oslo_serialization import jsonutils as json
+from six.moves.urllib import parse as urllib
+
+from tempest.lib.common import rest_client
+from tempest.lib import exceptions as lib_exc
-class SnapshotsClient(snapshots_client.SnapshotsClient):
+class SnapshotsClient(rest_client.RestClient):
"""Client class to send CRUD Volume Snapshot V3 API requests."""
api_version = "v3"
+ create_resp = 202
+
+ def list_snapshots(self, detail=False, **params):
+ """List all the snapshot.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/block-storage/v3/index.html#list-accessible-snapshots
+ https://developer.openstack.org/api-ref/block-storage/v3/index.html#list-snapshots-and-details
+ """
+ url = 'snapshots'
+ if detail:
+ url += '/detail'
+ if params:
+ url += '?%s' % urllib.urlencode(params)
+
+ resp, body = self.get(url)
+ body = json.loads(body)
+ self.expected_success(200, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def show_snapshot(self, snapshot_id):
+ """Returns the details of a single snapshot.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/block-storage/v3/index.html#show-a-snapshot-s-details
+ """
+ url = "snapshots/%s" % snapshot_id
+ resp, body = self.get(url)
+ body = json.loads(body)
+ self.expected_success(200, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def create_snapshot(self, **kwargs):
+ """Creates a new snapshot.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/block-storage/v3/index.html#create-a-snapshot
+ """
+ post_body = json.dumps({'snapshot': kwargs})
+ resp, body = self.post('snapshots', post_body)
+ body = json.loads(body)
+ self.expected_success(self.create_resp, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def update_snapshot(self, snapshot_id, **kwargs):
+ """Updates a snapshot.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/block-storage/v3/index.html#update-a-snapshot
+ """
+ put_body = json.dumps({'snapshot': kwargs})
+ resp, body = self.put('snapshots/%s' % snapshot_id, put_body)
+ body = json.loads(body)
+ self.expected_success(200, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def delete_snapshot(self, snapshot_id):
+ """Delete Snapshot.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/block-storage/v3/index.html#delete-a-snapshot
+ """
+ resp, body = self.delete("snapshots/%s" % snapshot_id)
+ self.expected_success(202, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def is_resource_deleted(self, id):
+ try:
+ self.show_snapshot(id)
+ except lib_exc.NotFound:
+ return True
+ return False
+
+ @property
+ def resource_type(self):
+ """Returns the primary type of resource this client works with."""
+ return 'volume-snapshot'
+
+ def reset_snapshot_status(self, snapshot_id, status):
+ """Reset the specified snapshot's status."""
+ post_body = json.dumps({'os-reset_status': {"status": status}})
+ resp, body = self.post('snapshots/%s/action' % snapshot_id, post_body)
+ self.expected_success(202, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def update_snapshot_status(self, snapshot_id, **kwargs):
+ """Update the specified snapshot's status."""
+ # TODO(gmann): api-site doesn't contain doc ref
+ # for this API. After fixing the api-site, we need to
+ # add the link here.
+ # Bug https://bugs.launchpad.net/openstack-api-site/+bug/1532645
+
+ post_body = json.dumps({'os-update_snapshot_status': kwargs})
+ url = 'snapshots/%s/action' % snapshot_id
+ resp, body = self.post(url, post_body)
+ self.expected_success(202, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def create_snapshot_metadata(self, snapshot_id, metadata):
+ """Create metadata for the snapshot.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/block-storage/v3/index.html#create-a-snapshot-s-metadata
+ """
+ put_body = json.dumps({'metadata': metadata})
+ url = "snapshots/%s/metadata" % snapshot_id
+ resp, body = self.post(url, put_body)
+ body = json.loads(body)
+ self.expected_success(200, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def show_snapshot_metadata(self, snapshot_id):
+ """Get metadata of the snapshot.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/block-storage/v3/index.html#show-a-snapshot-s-metadata
+ """
+ url = "snapshots/%s/metadata" % snapshot_id
+ resp, body = self.get(url)
+ body = json.loads(body)
+ self.expected_success(200, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def update_snapshot_metadata(self, snapshot_id, **kwargs):
+ """Update metadata for the snapshot.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/block-storage/v3/index.html#update-a-snapshot-s-metadata
+ """
+ put_body = json.dumps(kwargs)
+ url = "snapshots/%s/metadata" % snapshot_id
+ resp, body = self.put(url, put_body)
+ body = json.loads(body)
+ self.expected_success(200, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def show_snapshot_metadata_item(self, snapshot_id, id):
+ """Show metadata item for the snapshot."""
+ url = "snapshots/%s/metadata/%s" % (snapshot_id, id)
+ resp, body = self.get(url)
+ body = json.loads(body)
+ self.expected_success(200, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def update_snapshot_metadata_item(self, snapshot_id, id, **kwargs):
+ """Update metadata item for the snapshot."""
+ # TODO(piyush): 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.
+ # LP: https://bugs.launchpad.net/openstack-api-site/+bug/1529064
+ put_body = json.dumps(kwargs)
+ url = "snapshots/%s/metadata/%s" % (snapshot_id, id)
+ resp, body = self.put(url, put_body)
+ body = json.loads(body)
+ self.expected_success(200, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def delete_snapshot_metadata_item(self, snapshot_id, id):
+ """Delete metadata item for the snapshot."""
+ url = "snapshots/%s/metadata/%s" % (snapshot_id, id)
+ resp, body = self.delete(url)
+ self.expected_success(200, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def force_delete_snapshot(self, snapshot_id):
+ """Force Delete Snapshot."""
+ post_body = json.dumps({'os-force_delete': {}})
+ resp, body = self.post('snapshots/%s/action' % snapshot_id, post_body)
+ self.expected_success(202, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def unmanage_snapshot(self, snapshot_id):
+ """Unmanage a snapshot."""
+ post_body = json.dumps({'os-unmanage': {}})
+ url = 'snapshots/%s/action' % (snapshot_id)
+ resp, body = self.post(url, post_body)
+ self.expected_success(202, resp.status)
+ return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v3/transfers_client.py b/tempest/lib/services/volume/v3/transfers_client.py
new file mode 100644
index 0000000..97c5597
--- /dev/null
+++ b/tempest/lib/services/volume/v3/transfers_client.py
@@ -0,0 +1,82 @@
+# Copyright 2012 OpenStack Foundation
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from oslo_serialization import jsonutils as json
+from six.moves.urllib import parse as urllib
+
+from tempest.lib.common import rest_client
+
+
+class TransfersClient(rest_client.RestClient):
+ """Client class to send CRUD Volume Transfer API requests"""
+
+ def create_volume_transfer(self, **kwargs):
+ """Create a volume transfer.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/block-storage/v3/index.html#create-a-volume-transfer
+ """
+ post_body = json.dumps({'transfer': kwargs})
+ resp, body = self.post('os-volume-transfer', post_body)
+ body = json.loads(body)
+ self.expected_success(202, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def show_volume_transfer(self, transfer_id):
+ """Returns the details of a volume transfer."""
+ url = "os-volume-transfer/%s" % transfer_id
+ resp, body = self.get(url)
+ body = json.loads(body)
+ self.expected_success(200, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def list_volume_transfers(self, detail=False, **params):
+ """List all the volume transfers created.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/block-storage/v3/index.html#list-volume-transfers-for-a-project
+ https://developer.openstack.org/api-ref/block-storage/v3/index.html#list-volume-transfers-and-details
+ """
+ url = 'os-volume-transfer'
+ if detail:
+ url += '/detail'
+ if params:
+ url += '?%s' % urllib.urlencode(params)
+ resp, body = self.get(url)
+ body = json.loads(body)
+ self.expected_success(200, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def delete_volume_transfer(self, transfer_id):
+ """Delete a volume transfer."""
+ resp, body = self.delete("os-volume-transfer/%s" % transfer_id)
+ self.expected_success(202, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def accept_volume_transfer(self, transfer_id, **kwargs):
+ """Accept a volume transfer.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/block-storage/v3/index.html#accept-a-volume-transfer
+ """
+ url = 'os-volume-transfer/%s/accept' % transfer_id
+ post_body = json.dumps({'accept': kwargs})
+ resp, body = self.post(url, post_body)
+ body = json.loads(body)
+ self.expected_success(202, resp.status)
+ return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v3/types_client.py b/tempest/lib/services/volume/v3/types_client.py
new file mode 100644
index 0000000..6d9d03a
--- /dev/null
+++ b/tempest/lib/services/volume/v3/types_client.py
@@ -0,0 +1,204 @@
+# Copyright 2012 OpenStack Foundation
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from oslo_serialization import jsonutils as json
+from six.moves.urllib import parse as urllib
+
+from tempest.lib.common import rest_client
+from tempest.lib import exceptions as lib_exc
+
+
+class TypesClient(rest_client.RestClient):
+ """Client class to send CRUD Volume API requests"""
+
+ def is_resource_deleted(self, id):
+ try:
+ self.show_volume_type(id)
+ except lib_exc.NotFound:
+ return True
+ return False
+
+ @property
+ def resource_type(self):
+ """Returns the primary type of resource this client works with."""
+ return 'volume-type'
+
+ def list_volume_types(self, **params):
+ """List all the volume_types created.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/block-storage/v3/index.html#list-all-volume-types
+ """
+ url = 'types'
+ if params:
+ url += '?%s' % urllib.urlencode(params)
+
+ resp, body = self.get(url)
+ body = json.loads(body)
+ self.expected_success(200, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def show_volume_type(self, volume_type_id):
+ """Returns the details of a single volume_type.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/block-storage/v3/index.html#show-volume-type-detail
+ """
+ url = "types/%s" % volume_type_id
+ resp, body = self.get(url)
+ body = json.loads(body)
+ self.expected_success(200, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def create_volume_type(self, **kwargs):
+ """Create volume type.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/block-storage/v3/index.html#create-a-volume-type
+ """
+ post_body = json.dumps({'volume_type': kwargs})
+ resp, body = self.post('types', post_body)
+ body = json.loads(body)
+ self.expected_success(200, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def delete_volume_type(self, volume_type_id):
+ """Deletes the Specified Volume_type.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/block-storage/v3/index.html#delete-a-volume-type
+ """
+ resp, body = self.delete("types/%s" % volume_type_id)
+ self.expected_success(202, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def list_volume_types_extra_specs(self, volume_type_id, **params):
+ """List all the volume_types extra specs created.
+
+ 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/extra_specs' % volume_type_id
+ if params:
+ url += '?%s' % urllib.urlencode(params)
+
+ resp, body = self.get(url)
+ body = json.loads(body)
+ self.expected_success(200, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def show_volume_type_extra_specs(self, volume_type_id, extra_specs_name):
+ """Returns the details of a single volume_type extra spec."""
+ url = "types/%s/extra_specs/%s" % (volume_type_id, extra_specs_name)
+ resp, body = self.get(url)
+ body = json.loads(body)
+ self.expected_success(200, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def create_volume_type_extra_specs(self, volume_type_id, extra_specs):
+ """Creates a new Volume_type extra spec.
+
+ volume_type_id: Id of volume_type.
+ extra_specs: A dictionary of values to be used as extra_specs.
+ """
+ url = "types/%s/extra_specs" % volume_type_id
+ post_body = json.dumps({'extra_specs': extra_specs})
+ resp, body = self.post(url, post_body)
+ body = json.loads(body)
+ self.expected_success(200, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def delete_volume_type_extra_specs(self, volume_type_id, extra_spec_name):
+ """Deletes the Specified Volume_type extra spec."""
+ resp, body = self.delete("types/%s/extra_specs/%s" % (
+ volume_type_id, extra_spec_name))
+ self.expected_success(202, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def update_volume_type(self, volume_type_id, **kwargs):
+ """Updates volume type name, description, and/or is_public.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/block-storage/v3/index.html#update-a-volume-type
+ """
+ put_body = json.dumps({'volume_type': kwargs})
+ resp, body = self.put('types/%s' % volume_type_id, put_body)
+ body = json.loads(body)
+ self.expected_success(200, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def update_volume_type_extra_specs(self, volume_type_id, extra_spec_name,
+ extra_specs):
+ """Update a volume_type extra spec.
+
+ volume_type_id: Id of volume_type.
+ extra_spec_name: Name of the extra spec to be updated.
+ extra_spec: A dictionary of with key as extra_spec_name and the
+ updated value.
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/block-storage/v3/index.html#update-extra-specification-for-volume-type
+ """
+ url = "types/%s/extra_specs/%s" % (volume_type_id, extra_spec_name)
+ put_body = json.dumps(extra_specs)
+ resp, body = self.put(url, put_body)
+ body = json.loads(body)
+ self.expected_success(200, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def add_type_access(self, volume_type_id, **kwargs):
+ """Adds volume type access for the given project.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/block-storage/v3/index.html#add-private-volume-type-access-to-project
+ """
+ post_body = json.dumps({'addProjectAccess': kwargs})
+ url = 'types/%s/action' % volume_type_id
+ resp, body = self.post(url, post_body)
+ self.expected_success(202, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def remove_type_access(self, volume_type_id, **kwargs):
+ """Removes volume type access for the given project.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/block-storage/v3/index.html#remove-private-volume-type-access-from-project
+ """
+ post_body = json.dumps({'removeProjectAccess': kwargs})
+ url = 'types/%s/action' % volume_type_id
+ resp, body = self.post(url, post_body)
+ self.expected_success(202, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def list_type_access(self, volume_type_id):
+ """Print access information about the given volume type.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/block-storage/v3/index.html#list-private-volume-type-access-detail
+ """
+ url = 'types/%s/os-volume-type-access' % volume_type_id
+ resp, body = self.get(url)
+ body = json.loads(body)
+ self.expected_success(200, resp.status)
+ return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v3/volume_manage_client.py b/tempest/lib/services/volume/v3/volume_manage_client.py
new file mode 100644
index 0000000..349e11d
--- /dev/null
+++ b/tempest/lib/services/volume/v3/volume_manage_client.py
@@ -0,0 +1,35 @@
+# 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 client."""
+
+ 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/v3/index.html#manage-an-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/v3/volumes_client.py b/tempest/lib/services/volume/v3/volumes_client.py
index 5f4b278..a1185c4 100644
--- a/tempest/lib/services/volume/v3/volumes_client.py
+++ b/tempest/lib/services/volume/v3/volumes_client.py
@@ -14,16 +14,96 @@
# under the License.
from oslo_serialization import jsonutils as json
+import six
from six.moves.urllib import parse as urllib
from tempest.lib.common import rest_client
-from tempest.lib.services.volume.v2 import volumes_client
+from tempest.lib import exceptions as lib_exc
+from tempest.lib.services.volume import base_client
-class VolumesClient(volumes_client.VolumesClient):
+class VolumesClient(base_client.BaseClient):
"""Client class to send CRUD Volume V3 API requests"""
api_version = "v3"
+ def _prepare_params(self, params):
+ """Prepares params for use in get or _ext_get methods.
+
+ If params is a string it will be left as it is, but if it's not it will
+ be urlencoded.
+ """
+ if isinstance(params, six.string_types):
+ return params
+ return urllib.urlencode(params)
+
+ def list_volumes(self, detail=False, params=None):
+ """List all the volumes created.
+
+ Params can be a string (must be urlencoded) or a dictionary.
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/block-storage/v3/index.html#list-accessible-volumes-with-details
+ https://developer.openstack.org/api-ref/block-storage/v3/index.html#list-accessible-volumes
+ """
+ url = 'volumes'
+ if detail:
+ url += '/detail'
+ if params:
+ url += '?%s' % self._prepare_params(params)
+
+ resp, body = self.get(url)
+ body = json.loads(body)
+ self.expected_success(200, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def show_volume(self, volume_id):
+ """Returns the details of a single volume."""
+ url = "volumes/%s" % volume_id
+ resp, body = self.get(url)
+ body = json.loads(body)
+ self.expected_success(200, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def create_volume(self, **kwargs):
+ """Creates a new Volume.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/block-storage/v3/index.html#create-a-volume
+ """
+ post_body = json.dumps({'volume': kwargs})
+ resp, body = self.post('volumes', post_body)
+ body = json.loads(body)
+ self.expected_success(202, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def update_volume(self, volume_id, **kwargs):
+ """Updates the Specified Volume.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/block-storage/v3/index.html#update-a-volume
+ """
+ put_body = json.dumps({'volume': kwargs})
+ resp, body = self.put('volumes/%s' % volume_id, put_body)
+ body = json.loads(body)
+ self.expected_success(200, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def delete_volume(self, volume_id, **params):
+ """Deletes the Specified Volume.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/block-storage/v3/index.html#delete-a-volume
+ """
+ url = 'volumes/%s' % volume_id
+ if params:
+ url += '?%s' % urllib.urlencode(params)
+ resp, body = self.delete(url)
+ self.expected_success(202, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
def show_volume_summary(self, **params):
"""Get volumes summary.
@@ -38,3 +118,250 @@
body = json.loads(body)
self.expected_success(200, resp.status)
return rest_client.ResponseBody(resp, body)
+
+ def upload_volume(self, volume_id, **kwargs):
+ """Uploads a volume in Glance."""
+ post_body = json.dumps({'os-volume_upload_image': kwargs})
+ url = 'volumes/%s/action' % (volume_id)
+ resp, body = self.post(url, post_body)
+ body = json.loads(body)
+ self.expected_success(202, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def attach_volume(self, volume_id, **kwargs):
+ """Attaches a volume to a given instance on a given mountpoint.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/block-storage/v3/index.html#attach-volume-to-a-server
+ """
+ post_body = json.dumps({'os-attach': kwargs})
+ url = 'volumes/%s/action' % (volume_id)
+ resp, body = self.post(url, post_body)
+ self.expected_success(202, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def set_bootable_volume(self, volume_id, **kwargs):
+ """Set a bootable flag for a volume - true or false.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/block-storage/v3/index.html#update-a-volume-s-bootable-status
+ """
+ post_body = json.dumps({'os-set_bootable': kwargs})
+ url = 'volumes/%s/action' % (volume_id)
+ resp, body = self.post(url, post_body)
+ self.expected_success(200, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def detach_volume(self, volume_id):
+ """Detaches a volume from an instance."""
+ post_body = json.dumps({'os-detach': {}})
+ url = 'volumes/%s/action' % (volume_id)
+ resp, body = self.post(url, post_body)
+ self.expected_success(202, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def reserve_volume(self, volume_id):
+ """Reserves a volume."""
+ post_body = json.dumps({'os-reserve': {}})
+ url = 'volumes/%s/action' % (volume_id)
+ resp, body = self.post(url, post_body)
+ self.expected_success(202, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def unreserve_volume(self, volume_id):
+ """Restore a reserved volume ."""
+ post_body = json.dumps({'os-unreserve': {}})
+ url = 'volumes/%s/action' % (volume_id)
+ resp, body = self.post(url, post_body)
+ self.expected_success(202, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def is_resource_deleted(self, id):
+ """Check the specified resource is deleted or not.
+
+ :param id: A checked resource id
+ :raises lib_exc.DeleteErrorException: If the specified resource is on
+ the status the delete was failed.
+ """
+ try:
+ volume = self.show_volume(id)
+ except lib_exc.NotFound:
+ return True
+ if volume["volume"]["status"] == "error_deleting":
+ raise lib_exc.DeleteErrorException(resource_id=id)
+ return False
+
+ @property
+ def resource_type(self):
+ """Returns the primary type of resource this client works with."""
+ return 'volume'
+
+ def extend_volume(self, volume_id, **kwargs):
+ """Extend a volume.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/block-storage/v3/index.html#extend-a-volume-size
+ """
+ post_body = json.dumps({'os-extend': kwargs})
+ url = 'volumes/%s/action' % (volume_id)
+ resp, body = self.post(url, post_body)
+ self.expected_success(202, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def reset_volume_status(self, volume_id, **kwargs):
+ """Reset the Specified Volume's Status.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/block-storage/v3/index.html#reset-a-volume-s-statuses
+ """
+ post_body = json.dumps({'os-reset_status': kwargs})
+ resp, body = self.post('volumes/%s/action' % volume_id, post_body)
+ self.expected_success(202, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def update_volume_readonly(self, volume_id, **kwargs):
+ """Update the Specified Volume readonly."""
+ post_body = json.dumps({'os-update_readonly_flag': kwargs})
+ url = 'volumes/%s/action' % (volume_id)
+ resp, body = self.post(url, post_body)
+ self.expected_success(202, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def force_delete_volume(self, volume_id):
+ """Force Delete Volume."""
+ post_body = json.dumps({'os-force_delete': {}})
+ resp, body = self.post('volumes/%s/action' % volume_id, post_body)
+ self.expected_success(202, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def create_volume_metadata(self, volume_id, metadata):
+ """Create metadata for the volume.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/block-storage/v3/index.html#create-metadata-for-volume
+ """
+ put_body = json.dumps({'metadata': metadata})
+ url = "volumes/%s/metadata" % volume_id
+ resp, body = self.post(url, put_body)
+ body = json.loads(body)
+ self.expected_success(200, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def show_volume_metadata(self, volume_id):
+ """Get metadata of the volume."""
+ url = "volumes/%s/metadata" % volume_id
+ resp, body = self.get(url)
+ body = json.loads(body)
+ self.expected_success(200, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def update_volume_metadata(self, volume_id, metadata):
+ """Update metadata for the volume.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/block-storage/v3/index.html#update-a-volume-s-metadata
+ """
+ put_body = json.dumps({'metadata': metadata})
+ url = "volumes/%s/metadata" % volume_id
+ resp, body = self.put(url, put_body)
+ body = json.loads(body)
+ self.expected_success(200, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def show_volume_metadata_item(self, volume_id, id):
+ """Show metadata item for the volume."""
+ url = "volumes/%s/metadata/%s" % (volume_id, id)
+ resp, body = self.get(url)
+ body = json.loads(body)
+ self.expected_success(200, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def update_volume_metadata_item(self, volume_id, id, meta_item):
+ """Update metadata item for the volume."""
+ put_body = json.dumps({'meta': meta_item})
+ url = "volumes/%s/metadata/%s" % (volume_id, id)
+ resp, body = self.put(url, put_body)
+ body = json.loads(body)
+ self.expected_success(200, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def delete_volume_metadata_item(self, volume_id, id):
+ """Delete metadata item for the volume."""
+ url = "volumes/%s/metadata/%s" % (volume_id, id)
+ resp, body = self.delete(url)
+ self.expected_success(200, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def retype_volume(self, volume_id, **kwargs):
+ """Updates volume with new volume type.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/block-storage/v3/index.html#retype-a-volume
+ """
+ post_body = json.dumps({'os-retype': kwargs})
+ resp, body = self.post('volumes/%s/action' % volume_id, post_body)
+ self.expected_success(202, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def force_detach_volume(self, volume_id, **kwargs):
+ """Force detach a volume.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/block-storage/v3/index.html#force-delete-a-volume
+ """
+ post_body = json.dumps({'os-force_detach': kwargs})
+ url = 'volumes/%s/action' % volume_id
+ resp, body = self.post(url, post_body)
+ self.expected_success(202, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def update_volume_image_metadata(self, volume_id, **kwargs):
+ """Update image metadata for the volume.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/block-storage/v3/index.html#set-image-metadata-for-a-volume
+ """
+ post_body = json.dumps({'os-set_image_metadata': {'metadata': kwargs}})
+ url = "volumes/%s/action" % (volume_id)
+ resp, body = self.post(url, post_body)
+ body = json.loads(body)
+ self.expected_success(200, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def delete_volume_image_metadata(self, volume_id, key_name):
+ """Delete image metadata item for the volume."""
+ post_body = json.dumps({'os-unset_image_metadata': {'key': key_name}})
+ url = "volumes/%s/action" % (volume_id)
+ resp, body = self.post(url, post_body)
+ self.expected_success(200, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def show_volume_image_metadata(self, volume_id):
+ """Show image metadata for the volume."""
+ post_body = json.dumps({'os-show_image_metadata': {}})
+ url = "volumes/%s/action" % volume_id
+ resp, body = self.post(url, post_body)
+ 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/v3/index.html#unmanage-a-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 6809f4d..9965fe5 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -298,10 +298,8 @@
def create_volume_type(self, client=None, name=None, backend_name=None):
if not client:
client = self.os_admin.volume_types_v2_client
- if not name:
- class_name = self.__class__.__name__
- name = data_utils.rand_name(class_name + '-volume-type')
- randomized_name = data_utils.rand_name('scenario-type-' + name)
+ randomized_name = name or data_utils.rand_name(
+ 'volume-type-' + self.__class__.__name__)
LOG.debug("Creating a volume type: %s on backend %s",
randomized_name, backend_name)
diff --git a/tempest/scenario/test_encrypted_cinder_volumes.py b/tempest/scenario/test_encrypted_cinder_volumes.py
index b5220e9..8c210d5 100644
--- a/tempest/scenario/test_encrypted_cinder_volumes.py
+++ b/tempest/scenario/test_encrypted_cinder_volumes.py
@@ -57,8 +57,7 @@
@utils.services('compute', 'volume', 'image')
def test_encrypted_cinder_volumes_luks(self):
server = self.launch_instance()
- volume = self.create_encrypted_volume('nova.volume.encryptors.'
- 'luks.LuksEncryptor',
+ volume = self.create_encrypted_volume('luks',
volume_type='luks')
self.attach_detach_volume(server, volume)
@@ -67,7 +66,6 @@
@utils.services('compute', 'volume', 'image')
def test_encrypted_cinder_volumes_cryptsetup(self):
server = self.launch_instance()
- volume = self.create_encrypted_volume('nova.volume.encryptors.'
- 'cryptsetup.CryptsetupEncryptor',
+ volume = self.create_encrypted_volume('plain',
volume_type='cryptsetup')
self.attach_detach_volume(server, volume)
diff --git a/tempest/scenario/test_volume_boot_pattern.py b/tempest/scenario/test_volume_boot_pattern.py
index 1fc57e7..2d024e9 100644
--- a/tempest/scenario/test_volume_boot_pattern.py
+++ b/tempest/scenario/test_volume_boot_pattern.py
@@ -236,16 +236,11 @@
# Delete the second server which should also delete the second volume
# created from the volume snapshot.
- # TODO(mriedem): Currently, the compute service fails to delete the
- # volume it created because the volume still has the snapshot
- # associated with it, and the cleanups for the volume snapshot which
- # were added in create_server_snapshot above, don't run until after
- # this is called. So we need to delete the volume snapshot and wait for
- # it to be gone here first before deleting the server, and then we can
- # also assert that the underlying volume is deleted when the server is
- # deleted.
self._delete_server(instance)
+ # Assert that the underlying volume is gone.
+ self.volumes_client.wait_for_resource_deletion(created_volume['id'])
+
@decorators.idempotent_id('cb78919a-e553-4bab-b73b-10cf4d2eb125')
@testtools.skipUnless(CONF.compute_feature_enabled.attach_encrypted_volume,
'Encrypted volume attach is not supported')
diff --git a/tempest/tests/lib/services/volume/v2/test_snapshot_manage_client.py b/tempest/tests/lib/services/volume/v2/test_snapshot_manage_client.py
index 3fe8970..e03a8eb 100644
--- a/tempest/tests/lib/services/volume/v2/test_snapshot_manage_client.py
+++ b/tempest/tests/lib/services/volume/v2/test_snapshot_manage_client.py
@@ -18,6 +18,8 @@
from oslo_serialization import jsonutils as json
from tempest.lib.services.volume.v2 import snapshot_manage_client
+from tempest.lib.services.volume.v3 import snapshot_manage_client \
+ as snapshot_manage_clientv3
from tempest.tests.lib import fake_auth_provider
from tempest.tests.lib.services import base
@@ -63,7 +65,7 @@
# NOTE: Use sort_keys for json.dumps so that the expected and actual
# payloads are guaranteed to be identical for mock_args assert check.
- with mock.patch.object(snapshot_manage_client.json,
+ with mock.patch.object(snapshot_manage_clientv3.json,
'dumps') as mock_dumps:
mock_dumps.side_effect = lambda d: json_dumps(d, sort_keys=True)
diff --git a/tempest/tests/lib/services/volume/v2/test_transfers_client.py b/tempest/tests/lib/services/volume/v2/test_transfers_client.py
index 84f4992..8e7c6f4 100644
--- a/tempest/tests/lib/services/volume/v2/test_transfers_client.py
+++ b/tempest/tests/lib/services/volume/v2/test_transfers_client.py
@@ -19,6 +19,8 @@
from oslo_serialization import jsonutils as json
from tempest.lib.services.volume.v2 import transfers_client
+from tempest.lib.services.volume.v3 import transfers_client \
+ as transfers_clientv3
from tempest.tests.lib import fake_auth_provider
from tempest.tests.lib.services import base
@@ -63,7 +65,7 @@
# NOTE: Use sort_keys for json.dumps so that the expected and actual
# payloads are guaranteed to be identical for mock_args assert check.
- with mock.patch.object(transfers_client.json, 'dumps') as mock_dumps:
+ with mock.patch.object(transfers_clientv3.json, 'dumps') as mock_dumps:
mock_dumps.side_effect = lambda d: json_dumps(d, sort_keys=True)
self.check_service_client_function(
@@ -84,7 +86,7 @@
# NOTE: Use sort_keys for json.dumps so that the expected and actual
# payloads are guaranteed to be identical for mock_args assert check.
- with mock.patch.object(transfers_client.json, 'dumps') as mock_dumps:
+ with mock.patch.object(transfers_clientv3.json, 'dumps') as mock_dumps:
mock_dumps.side_effect = lambda d: json_dumps(d, sort_keys=True)
self.check_service_client_function(
diff --git a/tempest/tests/lib/services/volume/v2/test_volume_manage_client.py b/tempest/tests/lib/services/volume/v2/test_volume_manage_client.py
index ea4a9f9..0fb66bb 100644
--- a/tempest/tests/lib/services/volume/v2/test_volume_manage_client.py
+++ b/tempest/tests/lib/services/volume/v2/test_volume_manage_client.py
@@ -18,6 +18,8 @@
from oslo_serialization import jsonutils as json
from tempest.lib.services.volume.v2 import volume_manage_client
+from tempest.lib.services.volume.v3 import volume_manage_client \
+ as volume_manage_clientv3
from tempest.tests.lib import fake_auth_provider
from tempest.tests.lib.services import base
@@ -91,7 +93,7 @@
# NOTE: Use sort_keys for json.dumps so that the expected and actual
# payloads are guaranteed to be identical for mock_args assert check.
- with mock.patch.object(volume_manage_client.json,
+ with mock.patch.object(volume_manage_clientv3.json,
'dumps') as mock_dumps:
mock_dumps.side_effect = lambda d: json_dumps(d, sort_keys=True)