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 0000000000..b572a347e8 --- /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/lib/services/volume/v2/availability_zone_client.py b/tempest/lib/services/volume/v2/availability_zone_client.py index 147e4c6560..bdb2304d41 100644 --- a/tempest/lib/services/volume/v2/availability_zone_client.py +++ b/tempest/lib/services/volume/v2/availability_zone_client.py @@ -13,15 +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): - - 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 3dbb30c9db..80b3631de7 100644 --- a/tempest/lib/services/volume/v2/backups_client.py +++ b/tempest/lib/services/volume/v2/backups_client.py @@ -12,107 +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 Backups client""" - - 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 7ebcd69bf9..d8cf806918 100644 --- a/tempest/lib/services/volume/v2/capabilities_client.py +++ b/tempest/lib/services/volume/v2/capabilities_client.py @@ -13,22 +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): - - 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 5a7ea12b2f..875e59e8ad 100644 --- a/tempest/lib/services/volume/v2/encryption_types_client.py +++ b/tempest/lib/services/volume/v2/encryption_types_client.py @@ -13,78 +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): - - 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 45b7a56bb6..6316ef5837 100644 --- a/tempest/lib/services/volume/v2/extensions_client.py +++ b/tempest/lib/services/volume/v2/extensions_client.py @@ -12,18 +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 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) +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 c395325dc6..38f1b38d83 100644 --- a/tempest/lib/services/volume/v2/hosts_client.py +++ b/tempest/lib/services/volume/v2/hosts_client.py @@ -12,36 +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 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/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 95002547e9..a6b8c5abf2 100644 --- a/tempest/lib/services/volume/v2/limits_client.py +++ b/tempest/lib/services/volume/v2/limits_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 limits_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) +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 f8b8c3cddb..b81384e6bf 100644 --- a/tempest/lib/services/volume/v2/qos_client.py +++ b/tempest/lib/services/volume/v2/qos_client.py @@ -11,121 +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 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: - 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 eeeb268c0d..24aab896dc 100644 --- a/tempest/lib/services/volume/v2/quota_classes_client.py +++ b/tempest/lib/services/volume/v2/quota_classes_client.py @@ -12,38 +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 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/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 8906294d49..6f9f61cb2e 100644 --- a/tempest/lib/services/volume/v2/quotas_client.py +++ b/tempest/lib/services/volume/v2/quotas_client.py @@ -12,52 +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 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/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 bd5fa6d258..a5adb34014 100644 --- a/tempest/lib/services/volume/v2/scheduler_stats_client.py +++ b/tempest/lib/services/volume/v2/scheduler_stats_client.py @@ -12,25 +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): - - 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 09036a4833..a4491d334a 100644 --- a/tempest/lib/services/volume/v2/services_client.py +++ b/tempest/lib/services/volume/v2/services_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 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 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) +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 43fd328ac8..132209f5d8 100644 --- a/tempest/lib/services/volume/v2/snapshot_manage_client.py +++ b/tempest/lib/services/volume/v2/snapshot_manage_client.py @@ -12,20 +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 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) +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 1f051801d1..3a72cc1bfb 100644 --- a/tempest/lib/services/volume/v2/snapshots_client.py +++ b/tempest/lib/services/volume/v2/snapshots_client.py @@ -9,199 +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 API requests.""" - 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 291aadf83f..701d0ae7d5 100644 --- a/tempest/lib/services/volume/v2/transfers_client.py +++ b/tempest/lib/services/volume/v2/transfers_client.py @@ -12,71 +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 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/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 ef5e434a08..8457f91ead 100644 --- a/tempest/lib/services/volume/v2/types_client.py +++ b/tempest/lib/services/volume/v2/types_client.py @@ -12,193 +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 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/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 ddae1278c6..06693269f4 100644 --- a/tempest/lib/services/volume/v2/volume_manage_client.py +++ b/tempest/lib/services/volume/v2/volume_manage_client.py @@ -12,24 +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 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/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 dd019f6809..f5f9e6e5d7 100644 --- a/tempest/lib/services/volume/v2/volumes_client.py +++ b/tempest/lib/services/volume/v2/volumes_client.py @@ -12,340 +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 API requests""" - - 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 2d85553b46..a1b7de31f8 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 0000000000..147e4c6560 --- /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 e742e3901b..10538b0d94 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 @@ class BackupsClient(backups_client.BackupsClient): 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 0000000000..7ebcd69bf9 --- /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 0000000000..7443a873dc --- /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 0000000000..45b7a56bb6 --- /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 0000000000..8b658050ab --- /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 0000000000..95002547e9 --- /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 0000000000..8f4d37ffc4 --- /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 0000000000..a8eb53615d --- /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 0000000000..538a915f7a --- /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 0000000000..9b80851cf4 --- /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 0000000000..09036a4833 --- /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 0000000000..43fd328ac8 --- /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 88c094f924..298925a077 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 0000000000..97c559756a --- /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 0000000000..6d9d03aa95 --- /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 0000000000..349e11dd3d --- /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 5f4b278e80..a1185c41b4 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 @@ class VolumesClient(volumes_client.VolumesClient): 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/tests/lib/services/volume/v2/test_snapshot_manage_client.py b/tempest/tests/lib/services/volume/v2/test_snapshot_manage_client.py index 3fe8970a89..e03a8eb916 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 @@ import mock 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 @@ class TestSnapshotManageClient(base.BaseServiceTest): # 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 84f4992834..8e7c6f4ad4 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 @@ import mock from oslo_serialization import jsonutils as json from tempest.lib.services.volume.v2 import transfers_client +from tempest.lib.services.volume.v3 import transfers_client \ + as transfers_clientv3 from tempest.tests.lib import fake_auth_provider from tempest.tests.lib.services import base @@ -63,7 +65,7 @@ class TestTransfersClient(base.BaseServiceTest): # 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 @@ class TestTransfersClient(base.BaseServiceTest): # 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 ea4a9f9967..0fb66bb261 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 @@ import mock 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 @@ class TestVolumeManageClient(base.BaseServiceTest): # 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)