From cb5235250cc10957b4392764b1dc3a6757784da5 Mon Sep 17 00:00:00 2001 From: Brian Rosmaita Date: Mon, 24 May 2021 14:55:34 -0400 Subject: [PATCH] Remove v2 classes Remove all cinderclient.v2 classes, mostly incorporating them into their v3 counterparts and updating the tests and test fixtures. Depends-on: https://review.opendev.org/c/openstack/horizon/+/800814 Change-Id: I335db5c1799edb2273bf8bfc9e1bc9de404a4ba5 --- .../tests/unit/fixture_data/client.py | 16 -- cinderclient/tests/unit/test_base.py | 4 +- cinderclient/tests/unit/test_client.py | 1 - cinderclient/tests/unit/test_utils.py | 4 +- .../tests/unit/v2/contrib/__init__.py | 0 cinderclient/tests/unit/v2/test_quotas.py | 83 ---------- .../tests/unit/v2/test_volume_transfers.py | 58 ------- .../tests/unit/{v2 => v3/contrib}/__init__.py | 0 .../contrib/test_list_extensions.py | 4 +- cinderclient/tests/unit/v3/fakes.py | 38 +++-- .../unit/{v2/fakes.py => v3/fakes_base.py} | 14 -- .../tests/unit/{v2 => v3}/test_auth.py | 2 +- .../unit/{v2 => v3}/test_capabilities.py | 7 +- .../tests/unit/{v2 => v3}/test_cgsnapshots.py | 5 +- .../unit/{v2 => v3}/test_consistencygroups.py | 5 +- .../tests/unit/{v2 => v3}/test_limits.py | 2 +- .../tests/unit/{v2 => v3}/test_pools.py | 8 +- .../tests/unit/{v2 => v3}/test_qos.py | 5 +- .../unit/{v2 => v3}/test_quota_classes.py | 5 +- cinderclient/tests/unit/v3/test_quotas.py | 63 ++++++- .../test_services_base.py} | 8 +- cinderclient/tests/unit/v3/test_shell.py | 2 +- .../unit/{v2 => v3}/test_snapshot_actions.py | 2 +- .../tests/unit/{v2 => v3}/test_type_access.py | 4 +- .../tests/unit/{v2 => v3}/test_types.py | 4 +- .../tests/unit/v3/test_volume_backups.py | 21 +++ .../test_volume_backups_30.py} | 21 +-- .../test_volume_encryption_types.py | 4 +- cinderclient/tests/unit/v3/test_volumes.py | 19 +++ .../test_volumes_base.py} | 35 ++-- cinderclient/v2/__init__.py | 17 -- cinderclient/v2/availability_zones.py | 41 ----- cinderclient/v2/capabilities.py | 38 ----- cinderclient/v2/cgsnapshots.py | 112 ------------- cinderclient/v2/client.py | 141 ---------------- cinderclient/v2/consistencygroups.py | 149 ----------------- cinderclient/v2/contrib/__init__.py | 0 cinderclient/v2/contrib/list_extensions.py | 44 ----- cinderclient/v2/limits.py | 99 ----------- cinderclient/v2/pools.py | 60 ------- cinderclient/v2/qos_specs.py | 155 ------------------ cinderclient/v2/quota_classes.py | 47 ------ cinderclient/v2/quotas.py | 56 ------- cinderclient/v2/services.py | 80 --------- cinderclient/v2/volume_backups.py | 137 ---------------- cinderclient/v2/volume_backups_restore.py | 44 ----- cinderclient/v2/volume_encryption_types.py | 104 ------------ cinderclient/v2/volume_snapshots.py | 39 ----- cinderclient/v2/volume_transfers.py | 88 ---------- cinderclient/v2/volume_type_access.py | 53 ------ cinderclient/v2/volume_types.py | 153 ----------------- cinderclient/v3/capabilities.py | 22 ++- cinderclient/v3/cgsnapshots.py | 96 ++++++++++- cinderclient/v3/consistencygroups.py | 133 ++++++++++++++- cinderclient/v3/contrib/list_extensions.py | 30 +++- cinderclient/v3/limits.py | 84 +++++++++- cinderclient/v3/pools.py | 44 ++++- cinderclient/v3/qos_specs.py | 136 ++++++++++++++- cinderclient/v3/quota_classes.py | 33 +++- cinderclient/v3/quotas.py | 31 +++- cinderclient/v3/services.py | 63 ++++++- cinderclient/v3/shell_base.py | 2 +- cinderclient/v3/volume_backups.py | 91 +++++++++- cinderclient/v3/volume_backups_restore.py | 25 ++- cinderclient/v3/volume_encryption_types.py | 86 +++++++++- cinderclient/v3/volume_snapshots.py | 12 +- cinderclient/v3/volume_transfers.py | 17 +- cinderclient/v3/volume_type_access.py | 38 ++++- cinderclient/v3/volumes.py | 20 ++- .../{v2/volumes.py => v3/volumes_base.py} | 88 +--------- doc/source/contributor/unit_tests.rst | 4 +- .../drop-v2-support-e578ca21c7c6b532.yaml | 5 + 72 files changed, 1122 insertions(+), 2039 deletions(-) delete mode 100644 cinderclient/tests/unit/v2/contrib/__init__.py delete mode 100644 cinderclient/tests/unit/v2/test_quotas.py delete mode 100644 cinderclient/tests/unit/v2/test_volume_transfers.py rename cinderclient/tests/unit/{v2 => v3/contrib}/__init__.py (100%) rename cinderclient/tests/unit/{v2 => v3}/contrib/test_list_extensions.py (92%) rename cinderclient/tests/unit/{v2/fakes.py => v3/fakes_base.py} (98%) rename cinderclient/tests/unit/{v2 => v3}/test_auth.py (99%) rename cinderclient/tests/unit/{v2 => v3}/test_capabilities.py (90%) rename cinderclient/tests/unit/{v2 => v3}/test_cgsnapshots.py (96%) rename cinderclient/tests/unit/{v2 => v3}/test_consistencygroups.py (98%) rename cinderclient/tests/unit/{v2 => v3}/test_limits.py (99%) rename cinderclient/tests/unit/{v2 => v3}/test_pools.py (90%) rename cinderclient/tests/unit/{v2 => v3}/test_qos.py (96%) rename cinderclient/tests/unit/{v2 => v3}/test_quota_classes.py (95%) rename cinderclient/tests/unit/{v2/test_services.py => v3/test_services_base.py} (94%) rename cinderclient/tests/unit/{v2 => v3}/test_snapshot_actions.py (98%) rename cinderclient/tests/unit/{v2 => v3}/test_type_access.py (94%) rename cinderclient/tests/unit/{v2 => v3}/test_types.py (98%) rename cinderclient/tests/unit/{v2/test_volume_backups.py => v3/test_volume_backups_30.py} (86%) rename cinderclient/tests/unit/{v2 => v3}/test_volume_encryption_types.py (98%) rename cinderclient/tests/unit/{v2/test_volumes.py => v3/test_volumes_base.py} (93%) delete mode 100644 cinderclient/v2/__init__.py delete mode 100644 cinderclient/v2/availability_zones.py delete mode 100644 cinderclient/v2/capabilities.py delete mode 100644 cinderclient/v2/cgsnapshots.py delete mode 100644 cinderclient/v2/client.py delete mode 100644 cinderclient/v2/consistencygroups.py delete mode 100644 cinderclient/v2/contrib/__init__.py delete mode 100644 cinderclient/v2/contrib/list_extensions.py delete mode 100644 cinderclient/v2/limits.py delete mode 100644 cinderclient/v2/pools.py delete mode 100644 cinderclient/v2/qos_specs.py delete mode 100644 cinderclient/v2/quota_classes.py delete mode 100644 cinderclient/v2/quotas.py delete mode 100644 cinderclient/v2/services.py delete mode 100644 cinderclient/v2/volume_backups.py delete mode 100644 cinderclient/v2/volume_backups_restore.py delete mode 100644 cinderclient/v2/volume_encryption_types.py delete mode 100644 cinderclient/v2/volume_snapshots.py delete mode 100644 cinderclient/v2/volume_transfers.py delete mode 100644 cinderclient/v2/volume_type_access.py delete mode 100644 cinderclient/v2/volume_types.py rename cinderclient/{v2/volumes.py => v3/volumes_base.py} (82%) create mode 100644 releasenotes/notes/drop-v2-support-e578ca21c7c6b532.yaml diff --git a/cinderclient/tests/unit/fixture_data/client.py b/cinderclient/tests/unit/fixture_data/client.py index 2beeb906d..9fe975666 100644 --- a/cinderclient/tests/unit/fixture_data/client.py +++ b/cinderclient/tests/unit/fixture_data/client.py @@ -13,7 +13,6 @@ from keystoneauth1 import fixture from cinderclient.tests.unit.fixture_data import base -from cinderclient.v2 import client as v2client from cinderclient.v3 import client as v3client @@ -34,21 +33,6 @@ class Base(base.Fixture): headers=self.json_headers) -class V2(Base): - - def __init__(self, *args, **kwargs): - super(V2, self).__init__(*args, **kwargs) - - svc = self.token.add_service('volumev2') - svc.add_endpoint(self.volume_url) - - def new_client(self): - return v2client.Client(username='xx', - api_key='xx', - project_id='xx', - auth_url=self.identity_url) - - class V3(Base): def __init__(self, *args, **kwargs): diff --git a/cinderclient/tests/unit/test_base.py b/cinderclient/tests/unit/test_base.py index 6ec2ca6f4..36503e152 100644 --- a/cinderclient/tests/unit/test_base.py +++ b/cinderclient/tests/unit/test_base.py @@ -22,13 +22,13 @@ from cinderclient import base from cinderclient import exceptions from cinderclient.tests.unit import test_utils from cinderclient.tests.unit import utils -from cinderclient.tests.unit.v2 import fakes +from cinderclient.tests.unit.v3 import fakes from cinderclient.v3 import client from cinderclient.v3 import volumes cs = fakes.FakeClient() -REQUEST_ID = 'req-test-request-id' +REQUEST_ID = test_utils.REQUEST_ID def create_response_obj_with_header(): diff --git a/cinderclient/tests/unit/test_client.py b/cinderclient/tests/unit/test_client.py index 1501d6f32..b7cd3c63a 100644 --- a/cinderclient/tests/unit/test_client.py +++ b/cinderclient/tests/unit/test_client.py @@ -26,7 +26,6 @@ import cinderclient.client from cinderclient import exceptions from cinderclient.tests.unit import utils from cinderclient.tests.unit.v3 import fakes -import cinderclient.v2.client @ddt.ddt diff --git a/cinderclient/tests/unit/test_utils.py b/cinderclient/tests/unit/test_utils.py index 1fb9433b4..cce4498b4 100644 --- a/cinderclient/tests/unit/test_utils.py +++ b/cinderclient/tests/unit/test_utils.py @@ -24,9 +24,9 @@ from cinderclient import base from cinderclient import exceptions from cinderclient import shell_utils from cinderclient.tests.unit import utils as test_utils -from cinderclient.tests.unit.v2 import fakes from cinderclient import utils +REQUEST_ID = 'req-test-request-id' UUID = '8e8ec658-c7b0-4243-bdf8-6f7f2952c0d0' @@ -61,7 +61,7 @@ class FakeManager(base.ManagerWithFind): raise exceptions.NotFound(resource_id) def list(self, search_opts, **kwargs): - return common_base.ListWithMeta(self.resources, fakes.REQUEST_ID) + return common_base.ListWithMeta(self.resources, REQUEST_ID) class FakeManagerWithApi(base.Manager): diff --git a/cinderclient/tests/unit/v2/contrib/__init__.py b/cinderclient/tests/unit/v2/contrib/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/cinderclient/tests/unit/v2/test_quotas.py b/cinderclient/tests/unit/v2/test_quotas.py deleted file mode 100644 index bb29e4d8e..000000000 --- a/cinderclient/tests/unit/v2/test_quotas.py +++ /dev/null @@ -1,83 +0,0 @@ -# Copyright (c) 2013 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 cinderclient.tests.unit import utils -from cinderclient.tests.unit.v2 import fakes - - -cs = fakes.FakeClient() - - -class QuotaSetsTest(utils.TestCase): - - def test_tenant_quotas_get(self): - tenant_id = 'test' - quota = cs.quotas.get(tenant_id) - cs.assert_called('GET', '/os-quota-sets/%s?usage=False' % tenant_id) - self._assert_request_id(quota) - - def test_tenant_quotas_defaults(self): - tenant_id = 'test' - quota = cs.quotas.defaults(tenant_id) - cs.assert_called('GET', '/os-quota-sets/%s/defaults' % tenant_id) - self._assert_request_id(quota) - - def test_update_quota(self): - q = cs.quotas.get('test') - q.update(volumes=2) - q.update(snapshots=2) - q.update(gigabytes=2000) - q.update(backups=2) - q.update(backup_gigabytes=2000) - q.update(per_volume_gigabytes=100) - cs.assert_called('PUT', '/os-quota-sets/test') - self._assert_request_id(q) - - def test_refresh_quota(self): - q = cs.quotas.get('test') - q2 = cs.quotas.get('test') - self.assertEqual(q.volumes, q2.volumes) - self.assertEqual(q.snapshots, q2.snapshots) - self.assertEqual(q.gigabytes, q2.gigabytes) - self.assertEqual(q.backups, q2.backups) - self.assertEqual(q.backup_gigabytes, q2.backup_gigabytes) - self.assertEqual(q.per_volume_gigabytes, q2.per_volume_gigabytes) - q2.volumes = 0 - self.assertNotEqual(q.volumes, q2.volumes) - q2.snapshots = 0 - self.assertNotEqual(q.snapshots, q2.snapshots) - q2.gigabytes = 0 - self.assertNotEqual(q.gigabytes, q2.gigabytes) - q2.backups = 0 - self.assertNotEqual(q.backups, q2.backups) - q2.backup_gigabytes = 0 - self.assertNotEqual(q.backup_gigabytes, q2.backup_gigabytes) - q2.per_volume_gigabytes = 0 - self.assertNotEqual(q.per_volume_gigabytes, q2.per_volume_gigabytes) - q2.get() - self.assertEqual(q.volumes, q2.volumes) - self.assertEqual(q.snapshots, q2.snapshots) - self.assertEqual(q.gigabytes, q2.gigabytes) - self.assertEqual(q.backups, q2.backups) - self.assertEqual(q.backup_gigabytes, q2.backup_gigabytes) - self.assertEqual(q.per_volume_gigabytes, q2.per_volume_gigabytes) - self._assert_request_id(q) - self._assert_request_id(q2) - - def test_delete_quota(self): - tenant_id = 'test' - quota = cs.quotas.delete(tenant_id) - cs.assert_called('DELETE', '/os-quota-sets/test') - self._assert_request_id(quota) diff --git a/cinderclient/tests/unit/v2/test_volume_transfers.py b/cinderclient/tests/unit/v2/test_volume_transfers.py deleted file mode 100644 index a265cf534..000000000 --- a/cinderclient/tests/unit/v2/test_volume_transfers.py +++ /dev/null @@ -1,58 +0,0 @@ -# Copyright (C) 2013 Hewlett-Packard Development Company, L.P. -# 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 cinderclient.tests.unit import utils -from cinderclient.tests.unit.v2 import fakes - - -cs = fakes.FakeClient() - - -class VolumeTransfersTest(utils.TestCase): - - def test_create(self): - vol = cs.transfers.create('1234') - cs.assert_called('POST', '/os-volume-transfer') - self._assert_request_id(vol) - - def test_get(self): - transfer_id = '5678' - vol = cs.transfers.get(transfer_id) - cs.assert_called('GET', '/os-volume-transfer/%s' % transfer_id) - self._assert_request_id(vol) - - def test_list(self): - lst = cs.transfers.list() - cs.assert_called('GET', '/os-volume-transfer/detail') - self._assert_request_id(lst) - - def test_delete(self): - b = cs.transfers.list()[0] - vol = b.delete() - cs.assert_called('DELETE', '/os-volume-transfer/5678') - self._assert_request_id(vol) - vol = cs.transfers.delete('5678') - self._assert_request_id(vol) - cs.assert_called('DELETE', '/os-volume-transfer/5678') - vol = cs.transfers.delete(b) - cs.assert_called('DELETE', '/os-volume-transfer/5678') - self._assert_request_id(vol) - - def test_accept(self): - transfer_id = '5678' - auth_key = '12345' - vol = cs.transfers.accept(transfer_id, auth_key) - cs.assert_called('POST', '/os-volume-transfer/%s/accept' % transfer_id) - self._assert_request_id(vol) diff --git a/cinderclient/tests/unit/v2/__init__.py b/cinderclient/tests/unit/v3/contrib/__init__.py similarity index 100% rename from cinderclient/tests/unit/v2/__init__.py rename to cinderclient/tests/unit/v3/contrib/__init__.py diff --git a/cinderclient/tests/unit/v2/contrib/test_list_extensions.py b/cinderclient/tests/unit/v3/contrib/test_list_extensions.py similarity index 92% rename from cinderclient/tests/unit/v2/contrib/test_list_extensions.py rename to cinderclient/tests/unit/v3/contrib/test_list_extensions.py index 4b6100f7a..1d7eb357d 100644 --- a/cinderclient/tests/unit/v2/contrib/test_list_extensions.py +++ b/cinderclient/tests/unit/v3/contrib/test_list_extensions.py @@ -16,8 +16,8 @@ from cinderclient import extension from cinderclient.tests.unit import utils -from cinderclient.tests.unit.v2 import fakes -from cinderclient.v2.contrib import list_extensions +from cinderclient.tests.unit.v3 import fakes +from cinderclient.v3.contrib import list_extensions extensions = [ extension.Extension(list_extensions.__name__.split(".")[-1], diff --git a/cinderclient/tests/unit/v3/fakes.py b/cinderclient/tests/unit/v3/fakes.py index 7647fb39f..7429af255 100644 --- a/cinderclient/tests/unit/v3/fakes.py +++ b/cinderclient/tests/unit/v3/fakes.py @@ -15,7 +15,7 @@ from datetime import datetime from cinderclient.tests.unit import fakes -from cinderclient.tests.unit.v2 import fakes as fake_v2 +from cinderclient.tests.unit.v3 import fakes_base from cinderclient.v3 import client @@ -133,7 +133,7 @@ class FakeClient(fakes.FakeClient, client.Client): return self.client.get_volume_api_version_from_endpoint() -class FakeHTTPClient(fake_v2.FakeHTTPClient): +class FakeHTTPClient(fakes_base.FakeHTTPClient): def __init__(self, **kwargs): super(FakeHTTPClient, self).__init__() @@ -194,6 +194,19 @@ class FakeHTTPClient(fake_v2.FakeHTTPClient): del svc['backend_state'] return (200, {}, {'services': services}) + def put_os_services_enable(self, body, **kw): + return (200, {}, {'host': body['host'], 'binary': body['binary'], + 'status': 'enabled'}) + + def put_os_services_disable(self, body, **kw): + return (200, {}, {'host': body['host'], 'binary': body['binary'], + 'status': 'disabled'}) + + def put_os_services_disable_log_reason(self, body, **kw): + return (200, {}, {'host': body['host'], 'binary': body['binary'], + 'status': 'disabled', + 'disabled_reason': body['disabled_reason']}) + # # Clusters # @@ -285,7 +298,7 @@ class FakeHTTPClient(fake_v2.FakeHTTPClient): # Backups # def put_backups_1234(self, **kw): - backup = fake_v2._stub_backup( + backup = fakes_base._stub_backup( id='1234', base_uri='http://localhost:8776', tenant_id='0fa851f6668144cf9cd8c8419c1646c1') @@ -640,10 +653,10 @@ class FakeHTTPClient(fake_v2.FakeHTTPClient): transfer2 = 'f625ec3e-13dd-4498-a22a-50afd534cc41' return (200, {}, {'transfers': [ - fake_v2._stub_transfer_full(transfer1, base_uri, - tenant_id), - fake_v2._stub_transfer_full(transfer2, base_uri, - tenant_id)]}) + fakes_base._stub_transfer_full(transfer1, base_uri, + tenant_id), + fakes_base._stub_transfer_full(transfer2, base_uri, + tenant_id)]}) def get_volume_transfers_5678(self, **kw): base_uri = 'http://localhost:8776' @@ -651,7 +664,8 @@ class FakeHTTPClient(fake_v2.FakeHTTPClient): transfer1 = '5678' return (200, {}, {'transfer': - fake_v2._stub_transfer_full(transfer1, base_uri, tenant_id)}) + fakes_base._stub_transfer_full(transfer1, base_uri, + tenant_id)}) def delete_volume_transfers_5678(self, **kw): return (202, {}, None) @@ -661,16 +675,16 @@ class FakeHTTPClient(fake_v2.FakeHTTPClient): tenant_id = '0fa851f6668144cf9cd8c8419c1646c1' transfer1 = '5678' return (202, {}, - {'transfer': fake_v2._stub_transfer(transfer1, base_uri, - tenant_id)}) + {'transfer': fakes_base._stub_transfer(transfer1, base_uri, + tenant_id)}) def post_volume_transfers_5678_accept(self, **kw): base_uri = 'http://localhost:8776' tenant_id = '0fa851f6668144cf9cd8c8419c1646c1' transfer1 = '5678' return (200, {}, - {'transfer': fake_v2._stub_transfer(transfer1, base_uri, - tenant_id)}) + {'transfer': fakes_base._stub_transfer(transfer1, base_uri, + tenant_id)}) def fake_request_get(): diff --git a/cinderclient/tests/unit/v2/fakes.py b/cinderclient/tests/unit/v3/fakes_base.py similarity index 98% rename from cinderclient/tests/unit/v2/fakes.py rename to cinderclient/tests/unit/v3/fakes_base.py index 700bf1e2f..ec75ff079 100644 --- a/cinderclient/tests/unit/v2/fakes.py +++ b/cinderclient/tests/unit/v3/fakes_base.py @@ -18,7 +18,6 @@ from urllib import parse as urlparse from cinderclient import client as base_client from cinderclient.tests.unit import fakes import cinderclient.tests.unit.utils as utils -from cinderclient.v2 import client REQUEST_ID = 'req-test-request-id' @@ -332,19 +331,6 @@ def stub_default_types(): } -class FakeClient(fakes.FakeClient, client.Client): - - def __init__(self, api_version=None, *args, **kwargs): - client.Client.__init__(self, 'username', 'password', - 'project_id', 'auth_url', - extensions=kwargs.get('extensions')) - self.api_version = api_version - self.client = FakeHTTPClient(**kwargs) - - def get_volume_api_version_from_endpoint(self): - return self.client.get_volume_api_version_from_endpoint() - - class FakeHTTPClient(base_client.HTTPClient): def __init__(self, version_header=None, **kwargs): diff --git a/cinderclient/tests/unit/v2/test_auth.py b/cinderclient/tests/unit/v3/test_auth.py similarity index 99% rename from cinderclient/tests/unit/v2/test_auth.py rename to cinderclient/tests/unit/v3/test_auth.py index 5d7a4bc36..3e5e70890 100644 --- a/cinderclient/tests/unit/v2/test_auth.py +++ b/cinderclient/tests/unit/v3/test_auth.py @@ -21,7 +21,7 @@ import requests from cinderclient import exceptions from cinderclient.tests.unit import utils -from cinderclient.v2 import client +from cinderclient.v3 import client class AuthenticateAgainstKeystoneTests(utils.TestCase): diff --git a/cinderclient/tests/unit/v2/test_capabilities.py b/cinderclient/tests/unit/v3/test_capabilities.py similarity index 90% rename from cinderclient/tests/unit/v2/test_capabilities.py rename to cinderclient/tests/unit/v3/test_capabilities.py index 98d8d71fc..9f8c4c66f 100644 --- a/cinderclient/tests/unit/v2/test_capabilities.py +++ b/cinderclient/tests/unit/v3/test_capabilities.py @@ -13,11 +13,12 @@ # License for the specific language governing permissions and limitations # under the License. +from cinderclient import api_versions from cinderclient.tests.unit import utils -from cinderclient.tests.unit.v2 import fakes -from cinderclient.v2.capabilities import Capabilities +from cinderclient.tests.unit.v3 import fakes +from cinderclient.v3.capabilities import Capabilities -cs = fakes.FakeClient() +cs = fakes.FakeClient(api_versions.APIVersion('3.0')) FAKE_CAPABILITY = { 'namespace': 'OS::Storage::Capabilities::fake', diff --git a/cinderclient/tests/unit/v2/test_cgsnapshots.py b/cinderclient/tests/unit/v3/test_cgsnapshots.py similarity index 96% rename from cinderclient/tests/unit/v2/test_cgsnapshots.py rename to cinderclient/tests/unit/v3/test_cgsnapshots.py index 3ebef9e4f..5f0cec76c 100644 --- a/cinderclient/tests/unit/v2/test_cgsnapshots.py +++ b/cinderclient/tests/unit/v3/test_cgsnapshots.py @@ -14,11 +14,12 @@ # License for the specific language governing permissions and limitations # under the License. +from cinderclient import api_versions from cinderclient.tests.unit import utils -from cinderclient.tests.unit.v2 import fakes +from cinderclient.tests.unit.v3 import fakes -cs = fakes.FakeClient() +cs = fakes.FakeClient(api_versions.APIVersion('3.0')) class cgsnapshotsTest(utils.TestCase): diff --git a/cinderclient/tests/unit/v2/test_consistencygroups.py b/cinderclient/tests/unit/v3/test_consistencygroups.py similarity index 98% rename from cinderclient/tests/unit/v2/test_consistencygroups.py rename to cinderclient/tests/unit/v3/test_consistencygroups.py index a695fe9d9..d265aabd3 100644 --- a/cinderclient/tests/unit/v2/test_consistencygroups.py +++ b/cinderclient/tests/unit/v3/test_consistencygroups.py @@ -14,10 +14,11 @@ # License for the specific language governing permissions and limitations # under the License. +from cinderclient import api_versions from cinderclient.tests.unit import utils -from cinderclient.tests.unit.v2 import fakes +from cinderclient.tests.unit.v3 import fakes -cs = fakes.FakeClient() +cs = fakes.FakeClient(api_versions.APIVersion('3.0')) class ConsistencygroupsTest(utils.TestCase): diff --git a/cinderclient/tests/unit/v2/test_limits.py b/cinderclient/tests/unit/v3/test_limits.py similarity index 99% rename from cinderclient/tests/unit/v2/test_limits.py rename to cinderclient/tests/unit/v3/test_limits.py index d8fbdfe59..a42f770fe 100644 --- a/cinderclient/tests/unit/v2/test_limits.py +++ b/cinderclient/tests/unit/v3/test_limits.py @@ -18,7 +18,7 @@ from unittest import mock import ddt from cinderclient.tests.unit import utils -from cinderclient.v2 import limits +from cinderclient.v3 import limits REQUEST_ID = 'req-test-request-id' diff --git a/cinderclient/tests/unit/v2/test_pools.py b/cinderclient/tests/unit/v3/test_pools.py similarity index 90% rename from cinderclient/tests/unit/v2/test_pools.py rename to cinderclient/tests/unit/v3/test_pools.py index e909871ae..6af90578b 100644 --- a/cinderclient/tests/unit/v2/test_pools.py +++ b/cinderclient/tests/unit/v3/test_pools.py @@ -13,11 +13,13 @@ # License for the specific language governing permissions and limitations # under the License. +from cinderclient import api_versions from cinderclient.tests.unit import utils -from cinderclient.tests.unit.v2 import fakes -from cinderclient.v2.pools import Pool +from cinderclient.tests.unit.v3 import fakes +from cinderclient.v3.pools import Pool -cs = fakes.FakeClient() + +cs = fakes.FakeClient(api_versions.APIVersion('3.0')) class PoolsTest(utils.TestCase): diff --git a/cinderclient/tests/unit/v2/test_qos.py b/cinderclient/tests/unit/v3/test_qos.py similarity index 96% rename from cinderclient/tests/unit/v2/test_qos.py rename to cinderclient/tests/unit/v3/test_qos.py index 809a71947..f6133900a 100644 --- a/cinderclient/tests/unit/v2/test_qos.py +++ b/cinderclient/tests/unit/v3/test_qos.py @@ -13,11 +13,12 @@ # License for the specific language governing permissions and limitations # under the License. +from cinderclient import api_versions from cinderclient.tests.unit import utils -from cinderclient.tests.unit.v2 import fakes +from cinderclient.tests.unit.v3 import fakes -cs = fakes.FakeClient() +cs = fakes.FakeClient(api_versions.APIVersion('3.0')) class QoSSpecsTest(utils.TestCase): diff --git a/cinderclient/tests/unit/v2/test_quota_classes.py b/cinderclient/tests/unit/v3/test_quota_classes.py similarity index 95% rename from cinderclient/tests/unit/v2/test_quota_classes.py rename to cinderclient/tests/unit/v3/test_quota_classes.py index 4182fdf3a..29f4d0c23 100644 --- a/cinderclient/tests/unit/v2/test_quota_classes.py +++ b/cinderclient/tests/unit/v3/test_quota_classes.py @@ -13,11 +13,12 @@ # License for the specific language governing permissions and limitations # under the License. +from cinderclient import api_versions from cinderclient.tests.unit import utils -from cinderclient.tests.unit.v2 import fakes +from cinderclient.tests.unit.v3 import fakes -cs = fakes.FakeClient() +cs = fakes.FakeClient(api_versions.APIVersion('3.0')) class QuotaClassSetsTest(utils.TestCase): diff --git a/cinderclient/tests/unit/v3/test_quotas.py b/cinderclient/tests/unit/v3/test_quotas.py index fbabb47f7..e67c47764 100644 --- a/cinderclient/tests/unit/v3/test_quotas.py +++ b/cinderclient/tests/unit/v3/test_quotas.py @@ -13,17 +13,78 @@ # License for the specific language governing permissions and limitations # under the License. +from cinderclient import api_versions from cinderclient.tests.unit import utils from cinderclient.tests.unit.v3 import fakes -cs = fakes.FakeClient() +cs = fakes.FakeClient(api_versions.APIVersion('3.0')) class QuotaSetsTest(utils.TestCase): + def test_tenant_quotas_get(self): + tenant_id = 'test' + quota = cs.quotas.get(tenant_id) + cs.assert_called('GET', '/os-quota-sets/%s?usage=False' % tenant_id) + self._assert_request_id(quota) + + def test_tenant_quotas_defaults(self): + tenant_id = 'test' + quota = cs.quotas.defaults(tenant_id) + cs.assert_called('GET', '/os-quota-sets/%s/defaults' % tenant_id) + self._assert_request_id(quota) + + def test_update_quota(self): + q = cs.quotas.get('test') + q.update(volumes=2) + q.update(snapshots=2) + q.update(gigabytes=2000) + q.update(backups=2) + q.update(backup_gigabytes=2000) + q.update(per_volume_gigabytes=100) + cs.assert_called('PUT', '/os-quota-sets/test') + self._assert_request_id(q) + def test_update_quota_with_skip_(self): q = cs.quotas.get('test') q.update(skip_validation=False) cs.assert_called('PUT', '/os-quota-sets/test?skip_validation=False') self._assert_request_id(q) + + def test_refresh_quota(self): + q = cs.quotas.get('test') + q2 = cs.quotas.get('test') + self.assertEqual(q.volumes, q2.volumes) + self.assertEqual(q.snapshots, q2.snapshots) + self.assertEqual(q.gigabytes, q2.gigabytes) + self.assertEqual(q.backups, q2.backups) + self.assertEqual(q.backup_gigabytes, q2.backup_gigabytes) + self.assertEqual(q.per_volume_gigabytes, q2.per_volume_gigabytes) + q2.volumes = 0 + self.assertNotEqual(q.volumes, q2.volumes) + q2.snapshots = 0 + self.assertNotEqual(q.snapshots, q2.snapshots) + q2.gigabytes = 0 + self.assertNotEqual(q.gigabytes, q2.gigabytes) + q2.backups = 0 + self.assertNotEqual(q.backups, q2.backups) + q2.backup_gigabytes = 0 + self.assertNotEqual(q.backup_gigabytes, q2.backup_gigabytes) + q2.per_volume_gigabytes = 0 + self.assertNotEqual(q.per_volume_gigabytes, q2.per_volume_gigabytes) + q2.get() + self.assertEqual(q.volumes, q2.volumes) + self.assertEqual(q.snapshots, q2.snapshots) + self.assertEqual(q.gigabytes, q2.gigabytes) + self.assertEqual(q.backups, q2.backups) + self.assertEqual(q.backup_gigabytes, q2.backup_gigabytes) + self.assertEqual(q.per_volume_gigabytes, q2.per_volume_gigabytes) + self._assert_request_id(q) + self._assert_request_id(q2) + + def test_delete_quota(self): + tenant_id = 'test' + quota = cs.quotas.delete(tenant_id) + cs.assert_called('DELETE', '/os-quota-sets/test') + self._assert_request_id(quota) diff --git a/cinderclient/tests/unit/v2/test_services.py b/cinderclient/tests/unit/v3/test_services_base.py similarity index 94% rename from cinderclient/tests/unit/v2/test_services.py rename to cinderclient/tests/unit/v3/test_services_base.py index d355d7fb1..711a5361d 100644 --- a/cinderclient/tests/unit/v2/test_services.py +++ b/cinderclient/tests/unit/v3/test_services_base.py @@ -13,15 +13,17 @@ # License for the specific language governing permissions and limitations # under the License. +from cinderclient import api_versions from cinderclient.tests.unit import utils -from cinderclient.tests.unit.v2 import fakes -from cinderclient.v2 import services +from cinderclient.tests.unit.v3 import fakes +from cinderclient.v3 import services -cs = fakes.FakeClient() +cs = fakes.FakeClient(api_version=api_versions.APIVersion('3.0')) class ServicesTest(utils.TestCase): + """Tests for v3.0 behavior""" def test_list_services(self): svs = cs.services.list() diff --git a/cinderclient/tests/unit/v3/test_shell.py b/cinderclient/tests/unit/v3/test_shell.py index 756f51201..a5b76fe96 100644 --- a/cinderclient/tests/unit/v3/test_shell.py +++ b/cinderclient/tests/unit/v3/test_shell.py @@ -1745,7 +1745,7 @@ class ShellTest(utils.TestCase): ) @ddt.unpack @mock.patch('cinderclient.utils.print_dict') - @mock.patch('cinderclient.tests.unit.v2.fakes._stub_restore') + @mock.patch('cinderclient.tests.unit.v3.fakes_base._stub_restore') def test_do_backup_restore(self, mock_stub_restore, mock_print_dict, diff --git a/cinderclient/tests/unit/v2/test_snapshot_actions.py b/cinderclient/tests/unit/v3/test_snapshot_actions.py similarity index 98% rename from cinderclient/tests/unit/v2/test_snapshot_actions.py rename to cinderclient/tests/unit/v3/test_snapshot_actions.py index 61fd0d06d..8d2b23a0d 100644 --- a/cinderclient/tests/unit/v2/test_snapshot_actions.py +++ b/cinderclient/tests/unit/v3/test_snapshot_actions.py @@ -20,7 +20,7 @@ from cinderclient.tests.unit import utils class SnapshotActionsTest(utils.FixturedTestCase): - client_fixture_class = client.V2 + client_fixture_class = client.V3 data_fixture_class = snapshots.Fixture def test_update_snapshot_status(self): diff --git a/cinderclient/tests/unit/v2/test_type_access.py b/cinderclient/tests/unit/v3/test_type_access.py similarity index 94% rename from cinderclient/tests/unit/v2/test_type_access.py rename to cinderclient/tests/unit/v3/test_type_access.py index d904c1d3e..5b2dbb3e4 100644 --- a/cinderclient/tests/unit/v2/test_type_access.py +++ b/cinderclient/tests/unit/v3/test_type_access.py @@ -15,8 +15,8 @@ # under the License. from cinderclient.tests.unit import utils -from cinderclient.tests.unit.v2 import fakes -from cinderclient.v2 import volume_type_access +from cinderclient.tests.unit.v3 import fakes +from cinderclient.v3 import volume_type_access cs = fakes.FakeClient() diff --git a/cinderclient/tests/unit/v2/test_types.py b/cinderclient/tests/unit/v3/test_types.py similarity index 98% rename from cinderclient/tests/unit/v2/test_types.py rename to cinderclient/tests/unit/v3/test_types.py index cf13723f1..fdbd32317 100644 --- a/cinderclient/tests/unit/v2/test_types.py +++ b/cinderclient/tests/unit/v3/test_types.py @@ -15,8 +15,8 @@ # under the License. from cinderclient.tests.unit import utils -from cinderclient.tests.unit.v2 import fakes -from cinderclient.v2 import volume_types +from cinderclient.tests.unit.v3 import fakes +from cinderclient.v3 import volume_types cs = fakes.FakeClient() diff --git a/cinderclient/tests/unit/v3/test_volume_backups.py b/cinderclient/tests/unit/v3/test_volume_backups.py index faa0185c2..3be6328fb 100644 --- a/cinderclient/tests/unit/v3/test_volume_backups.py +++ b/cinderclient/tests/unit/v3/test_volume_backups.py @@ -17,6 +17,7 @@ from cinderclient import api_versions from cinderclient import exceptions as exc from cinderclient.tests.unit import utils from cinderclient.tests.unit.v3 import fakes +from cinderclient.v3 import volume_backups_restore class VolumesTest(utils.TestCase): @@ -35,3 +36,23 @@ class VolumesTest(utils.TestCase): b = cs.backups.get('1234') self.assertRaises(exc.VersionNotFoundForAPIMethod, b.update, name='new-name') + + def test_restore(self): + cs = fakes.FakeClient(api_version=api_versions.APIVersion('3.0')) + backup_id = '76a17945-3c6f-435c-975b-b5685db10b62' + info = cs.restores.restore(backup_id) + cs.assert_called('POST', '/backups/%s/restore' % backup_id) + self.assertIsInstance(info, + volume_backups_restore.VolumeBackupsRestore) + self._assert_request_id(info) + + def test_restore_with_name(self): + cs = fakes.FakeClient(api_version=api_versions.APIVersion('3.0')) + backup_id = '76a17945-3c6f-435c-975b-b5685db10b62' + name = 'restore_vol' + info = cs.restores.restore(backup_id, name=name) + expected_body = {'restore': {'volume_id': None, 'name': name}} + cs.assert_called('POST', '/backups/%s/restore' % backup_id, + body=expected_body) + self.assertIsInstance(info, + volume_backups_restore.VolumeBackupsRestore) diff --git a/cinderclient/tests/unit/v2/test_volume_backups.py b/cinderclient/tests/unit/v3/test_volume_backups_30.py similarity index 86% rename from cinderclient/tests/unit/v2/test_volume_backups.py rename to cinderclient/tests/unit/v3/test_volume_backups_30.py index 700c44086..daf517c9f 100644 --- a/cinderclient/tests/unit/v2/test_volume_backups.py +++ b/cinderclient/tests/unit/v3/test_volume_backups_30.py @@ -14,8 +14,7 @@ # under the License. from cinderclient.tests.unit import utils -from cinderclient.tests.unit.v2 import fakes -from cinderclient.v2 import volume_backups_restore +from cinderclient.tests.unit.v3 import fakes cs = fakes.FakeClient() @@ -118,24 +117,6 @@ class VolumeBackupsTest(utils.TestCase): '/backups/76a17945-3c6f-435c-975b-b5685db10b62') self._assert_request_id(del_back) - def test_restore(self): - backup_id = '76a17945-3c6f-435c-975b-b5685db10b62' - info = cs.restores.restore(backup_id) - cs.assert_called('POST', '/backups/%s/restore' % backup_id) - self.assertIsInstance(info, - volume_backups_restore.VolumeBackupsRestore) - self._assert_request_id(info) - - def test_restore_with_name(self): - backup_id = '76a17945-3c6f-435c-975b-b5685db10b62' - name = 'restore_vol' - info = cs.restores.restore(backup_id, name=name) - expected_body = {'restore': {'volume_id': None, 'name': name}} - cs.assert_called('POST', '/backups/%s/restore' % backup_id, - body=expected_body) - self.assertIsInstance(info, - volume_backups_restore.VolumeBackupsRestore) - def test_reset_state(self): b = cs.backups.list()[0] api = '/backups/76a17945-3c6f-435c-975b-b5685db10b62/action' diff --git a/cinderclient/tests/unit/v2/test_volume_encryption_types.py b/cinderclient/tests/unit/v3/test_volume_encryption_types.py similarity index 98% rename from cinderclient/tests/unit/v2/test_volume_encryption_types.py rename to cinderclient/tests/unit/v3/test_volume_encryption_types.py index 1bbf537a1..9e38d5165 100644 --- a/cinderclient/tests/unit/v2/test_volume_encryption_types.py +++ b/cinderclient/tests/unit/v3/test_volume_encryption_types.py @@ -14,8 +14,8 @@ # under the License. from cinderclient.tests.unit import utils -from cinderclient.tests.unit.v2 import fakes -from cinderclient.v2.volume_encryption_types import VolumeEncryptionType +from cinderclient.tests.unit.v3 import fakes +from cinderclient.v3.volume_encryption_types import VolumeEncryptionType cs = fakes.FakeClient() diff --git a/cinderclient/tests/unit/v3/test_volumes.py b/cinderclient/tests/unit/v3/test_volumes.py index e5b3f2183..c733a0917 100644 --- a/cinderclient/tests/unit/v3/test_volumes.py +++ b/cinderclient/tests/unit/v3/test_volumes.py @@ -87,6 +87,25 @@ class VolumesTest(utils.TestCase): cs.assert_called('POST', '/volumes', body=expected) self._assert_request_id(vol) + def test_create_volume_with_hint(self): + cs = fakes.FakeClient(api_versions.APIVersion('3.0')) + vol = cs.volumes.create(1, scheduler_hints='uuid') + expected = {'volume': {'description': None, + 'availability_zone': None, + 'source_volid': None, + 'snapshot_id': None, + 'size': 1, + 'name': None, + 'imageRef': None, + 'volume_type': None, + 'metadata': {}, + 'consistencygroup_id': None, + 'backup_id': None, + }, + 'OS-SCH-HNT:scheduler_hints': 'uuid'} + cs.assert_called('POST', '/volumes', body=expected) + self._assert_request_id(vol) + @ddt.data((False, '/volumes/summary'), (True, '/volumes/summary?all_tenants=True')) def test_volume_summary(self, all_tenants_input): diff --git a/cinderclient/tests/unit/v2/test_volumes.py b/cinderclient/tests/unit/v3/test_volumes_base.py similarity index 93% rename from cinderclient/tests/unit/v2/test_volumes.py rename to cinderclient/tests/unit/v3/test_volumes_base.py index e4f9e323e..cca808aac 100644 --- a/cinderclient/tests/unit/v2/test_volumes.py +++ b/cinderclient/tests/unit/v3/test_volumes_base.py @@ -15,14 +15,16 @@ # License for the specific language governing permissions and limitations # under the License. +from cinderclient import api_versions from cinderclient.tests.unit import utils -from cinderclient.tests.unit.v2 import fakes -from cinderclient.v2.volumes import Volume +from cinderclient.tests.unit.v3 import fakes +from cinderclient.v3.volumes import Volume -cs = fakes.FakeClient() +cs = fakes.FakeClient(api_version=api_versions.APIVersion('3.0')) class VolumesTest(utils.TestCase): + """Block Storage API v3.0""" def test_list_volumes_with_marker_limit(self): lst = cs.volumes.list(marker=1234, limit=2) @@ -58,6 +60,11 @@ class VolumesTest(utils.TestCase): self._assert_request_id(volumes) cs.client.osapi_max_limit = 1000 + def test_create_volume(self): + vol = cs.volumes.create(1) + cs.assert_called('POST', '/volumes') + self._assert_request_id(vol) + def test_delete_volume(self): v = cs.volumes.list()[0] del_v = v.delete() @@ -70,28 +77,6 @@ class VolumesTest(utils.TestCase): cs.assert_called('DELETE', '/volumes/1234') self._assert_request_id(del_v) - def test_create_volume(self): - vol = cs.volumes.create(1) - cs.assert_called('POST', '/volumes') - self._assert_request_id(vol) - - def test_create_volume_with_hint(self): - vol = cs.volumes.create(1, scheduler_hints='uuid') - expected = {'volume': {'description': None, - 'availability_zone': None, - 'source_volid': None, - 'snapshot_id': None, - 'size': 1, - 'name': None, - 'imageRef': None, - 'volume_type': None, - 'metadata': {}, - 'consistencygroup_id': None, - }, - 'OS-SCH-HNT:scheduler_hints': 'uuid'} - cs.assert_called('POST', '/volumes', body=expected) - self._assert_request_id(vol) - def test_attach(self): v = cs.volumes.get('1234') self._assert_request_id(v) diff --git a/cinderclient/v2/__init__.py b/cinderclient/v2/__init__.py deleted file mode 100644 index 325460e38..000000000 --- a/cinderclient/v2/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright (c) 2013 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 cinderclient.v2.client import Client # noqa diff --git a/cinderclient/v2/availability_zones.py b/cinderclient/v2/availability_zones.py deleted file mode 100644 index 6503c12ae..000000000 --- a/cinderclient/v2/availability_zones.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright 2011-2013 OpenStack Foundation -# Copyright 2013 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. -"""Availability Zone interface (v2 extension)""" - -from cinderclient import base - - -class AvailabilityZone(base.Resource): - NAME_ATTR = 'display_name' - - def __repr__(self): - return "" % self.zoneName - - -class AvailabilityZoneManager(base.ManagerWithFind): - """Manage :class:`AvailabilityZone` resources.""" - resource_class = AvailabilityZone - - def list(self, detailed=False): - """Lists all availability zones. - - :rtype: list of :class:`AvailabilityZone` - """ - if detailed is True: - return self._list("/os-availability-zone/detail", - "availabilityZoneInfo") - else: - return self._list("/os-availability-zone", "availabilityZoneInfo") diff --git a/cinderclient/v2/capabilities.py b/cinderclient/v2/capabilities.py deleted file mode 100644 index 2045f02be..000000000 --- a/cinderclient/v2/capabilities.py +++ /dev/null @@ -1,38 +0,0 @@ -# Copyright (c) 2015 Hitachi Data Systems, 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. - -"""Capabilities interface (v2 extension)""" - -from cinderclient import base - - -class Capabilities(base.Resource): - NAME_ATTR = 'name' - - def __repr__(self): - return "" % self._info.get('namespace') - - -class CapabilitiesManager(base.Manager): - """Manage :class:`Capabilities` resources.""" - resource_class = Capabilities - - def get(self, host): - """Show backend volume stats and properties. - - :param host: Specified backend to obtain volume stats and properties. - :rtype: :class:`Capabilities` - """ - return self._get('/capabilities/%s' % host, None) diff --git a/cinderclient/v2/cgsnapshots.py b/cinderclient/v2/cgsnapshots.py deleted file mode 100644 index 04444e2b3..000000000 --- a/cinderclient/v2/cgsnapshots.py +++ /dev/null @@ -1,112 +0,0 @@ -# Copyright (C) 2012 - 2014 EMC Corporation. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -"""cgsnapshot interface (v2 extension).""" - -from cinderclient.apiclient import base as common_base -from cinderclient import base -from cinderclient import utils - - -class Cgsnapshot(base.Resource): - """A cgsnapshot is snapshot of a consistency group.""" - def __repr__(self): - return "" % self.id - - def delete(self): - """Delete this cgsnapshot.""" - return self.manager.delete(self) - - def update(self, **kwargs): - """Update the name or description for this cgsnapshot.""" - return self.manager.update(self, **kwargs) - - -class CgsnapshotManager(base.ManagerWithFind): - """Manage :class:`Cgsnapshot` resources.""" - resource_class = Cgsnapshot - - def create(self, consistencygroup_id, name=None, description=None, - user_id=None, - project_id=None): - """Creates a cgsnapshot. - - :param consistencygroup: Name or uuid of a consistency group - :param name: Name of the cgsnapshot - :param description: Description of the cgsnapshot - :param user_id: User id derived from context - :param project_id: Project id derived from context - :rtype: :class:`Cgsnapshot` - """ - - body = {'cgsnapshot': {'consistencygroup_id': consistencygroup_id, - 'name': name, - 'description': description, - 'user_id': user_id, - 'project_id': project_id, - 'status': "creating", - }} - - return self._create('/cgsnapshots', body, 'cgsnapshot') - - def get(self, cgsnapshot_id): - """Get a cgsnapshot. - - :param cgsnapshot_id: The ID of the cgsnapshot to get. - :rtype: :class:`Cgsnapshot` - """ - return self._get("/cgsnapshots/%s" % cgsnapshot_id, "cgsnapshot") - - def list(self, detailed=True, search_opts=None): - """Lists all cgsnapshots. - - :rtype: list of :class:`Cgsnapshot` - """ - query_string = utils.build_query_param(search_opts) - - detail = "" - if detailed: - detail = "/detail" - - return self._list("/cgsnapshots%s%s" % (detail, query_string), - "cgsnapshots") - - def delete(self, cgsnapshot): - """Delete a cgsnapshot. - - :param cgsnapshot: The :class:`Cgsnapshot` to delete. - """ - return self._delete("/cgsnapshots/%s" % base.getid(cgsnapshot)) - - def update(self, cgsnapshot, **kwargs): - """Update the name or description for a cgsnapshot. - - :param cgsnapshot: The :class:`Cgsnapshot` to update. - """ - if not kwargs: - return - - body = {"cgsnapshot": kwargs} - - return self._update("/cgsnapshots/%s" % base.getid(cgsnapshot), body) - - def _action(self, action, cgsnapshot, info=None, **kwargs): - """Perform a cgsnapshot "action." - """ - body = {action: info} - self.run_hooks('modify_body_for_action', body, **kwargs) - url = '/cgsnapshots/%s/action' % base.getid(cgsnapshot) - resp, body = self.api.client.post(url, body=body) - return common_base.TupleWithMeta((resp, body), resp) diff --git a/cinderclient/v2/client.py b/cinderclient/v2/client.py deleted file mode 100644 index a111c51a5..000000000 --- a/cinderclient/v2/client.py +++ /dev/null @@ -1,141 +0,0 @@ -# Copyright (c) 2013 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. - -import logging - -from cinderclient import api_versions -from cinderclient import client -from cinderclient.v2 import availability_zones -from cinderclient.v2 import capabilities -from cinderclient.v2 import cgsnapshots -from cinderclient.v2 import consistencygroups -from cinderclient.v2 import limits -from cinderclient.v2 import pools -from cinderclient.v2 import qos_specs -from cinderclient.v2 import quota_classes -from cinderclient.v2 import quotas -from cinderclient.v2 import services -from cinderclient.v2 import volume_backups -from cinderclient.v2 import volume_backups_restore -from cinderclient.v2 import volume_encryption_types -from cinderclient.v2 import volume_snapshots -from cinderclient.v2 import volume_transfers -from cinderclient.v2 import volume_type_access -from cinderclient.v2 import volume_types -from cinderclient.v2 import volumes - - -class Client(object): - """Top-level object to access the OpenStack Volume API. - - Create an instance with your creds:: - - >>> client = Client(USERNAME, PASSWORD, PROJECT_ID, AUTH_URL) - - Then call methods on its managers:: - - >>> client.volumes.list() - ... - """ - - def __init__(self, username=None, api_key=None, project_id=None, - auth_url='', insecure=False, timeout=None, tenant_id=None, - proxy_tenant_id=None, proxy_token=None, region_name=None, - endpoint_type='publicURL', extensions=None, - service_type='volumev2', service_name=None, - volume_service_name=None, os_endpoint=None, retries=0, - http_log_debug=False, cacert=None, cert=None, - auth_system='keystone', auth_plugin=None, session=None, - api_version=None, logger=None, **kwargs): - # FIXME(comstud): Rename the api_key argument above when we - # know it's not being used as keyword argument - password = api_key - self.version = '2.0' - self.limits = limits.LimitsManager(self) - - # extensions - self.volumes = volumes.VolumeManager(self) - self.volume_snapshots = volume_snapshots.SnapshotManager(self) - self.volume_types = volume_types.VolumeTypeManager(self) - self.volume_type_access = \ - volume_type_access.VolumeTypeAccessManager(self) - self.volume_encryption_types = \ - volume_encryption_types.VolumeEncryptionTypeManager(self) - self.qos_specs = qos_specs.QoSSpecsManager(self) - self.quota_classes = quota_classes.QuotaClassSetManager(self) - self.quotas = quotas.QuotaSetManager(self) - self.backups = volume_backups.VolumeBackupManager(self) - self.restores = volume_backups_restore.VolumeBackupRestoreManager(self) - self.transfers = volume_transfers.VolumeTransferManager(self) - self.services = services.ServiceManager(self) - self.consistencygroups = consistencygroups.\ - ConsistencygroupManager(self) - self.cgsnapshots = cgsnapshots.CgsnapshotManager(self) - self.availability_zones = \ - availability_zones.AvailabilityZoneManager(self) - self.pools = pools.PoolManager(self) - self.capabilities = capabilities.CapabilitiesManager(self) - self.api_version = api_version or api_versions.APIVersion(self.version) - - # Add in any extensions... - if extensions: - for extension in extensions: - if extension.manager_class: - setattr(self, extension.name, - extension.manager_class(self)) - - if not logger: - logger = logging.getLogger(__name__) - - self.client = client._construct_http_client( - username=username, - password=password, - project_id=project_id, - auth_url=auth_url, - insecure=insecure, - timeout=timeout, - tenant_id=tenant_id, - proxy_tenant_id=tenant_id, - proxy_token=proxy_token, - region_name=region_name, - endpoint_type=endpoint_type, - service_type=service_type, - service_name=service_name, - volume_service_name=volume_service_name, - os_endpoint=os_endpoint, - retries=retries, - http_log_debug=http_log_debug, - cacert=cacert, - cert=cert, - auth_system=auth_system, - auth_plugin=auth_plugin, - session=session, - api_version=self.api_version, - logger=logger, - **kwargs) - - def authenticate(self): - """Authenticate against the server. - - Normally this is called automatically when you first access the API, - but you can call this method to force authentication right now. - - Returns on success; raises :exc:`exceptions.Unauthorized` if the - credentials are wrong. - """ - self.client.authenticate() - - def get_volume_api_version_from_endpoint(self): - return self.client.get_volume_api_version_from_endpoint() diff --git a/cinderclient/v2/consistencygroups.py b/cinderclient/v2/consistencygroups.py deleted file mode 100644 index d5e5bbf77..000000000 --- a/cinderclient/v2/consistencygroups.py +++ /dev/null @@ -1,149 +0,0 @@ -# Copyright (C) 2012 - 2014 EMC Corporation. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -"""Consistencygroup interface (v2 extension).""" - -from cinderclient.apiclient import base as common_base -from cinderclient import base -from cinderclient import utils - - -class Consistencygroup(base.Resource): - """A Consistencygroup of volumes.""" - def __repr__(self): - return "" % self.id - - def delete(self, force='False'): - """Delete this consistency group.""" - return self.manager.delete(self, force) - - def update(self, **kwargs): - """Update the name or description for this consistency group.""" - return self.manager.update(self, **kwargs) - - -class ConsistencygroupManager(base.ManagerWithFind): - """Manage :class:`Consistencygroup` resources.""" - resource_class = Consistencygroup - - def create(self, volume_types, name=None, - description=None, user_id=None, - project_id=None, availability_zone=None): - """Creates a consistency group. - - :param name: Name of the ConsistencyGroup - :param description: Description of the ConsistencyGroup - :param volume_types: Types of volume - :param user_id: User id derived from context - :param project_id: Project id derived from context - :param availability_zone: Availability Zone to use - :rtype: :class:`Consistencygroup` - """ - - body = {'consistencygroup': {'name': name, - 'description': description, - 'volume_types': volume_types, - 'user_id': user_id, - 'project_id': project_id, - 'availability_zone': availability_zone, - 'status': "creating", - }} - - return self._create('/consistencygroups', body, 'consistencygroup') - - def create_from_src(self, cgsnapshot_id, source_cgid, name=None, - description=None, user_id=None, - project_id=None): - """Creates a consistency group from a cgsnapshot or a source CG. - - :param cgsnapshot_id: UUID of a CGSnapshot - :param source_cgid: UUID of a source CG - :param name: Name of the ConsistencyGroup - :param description: Description of the ConsistencyGroup - :param user_id: User id derived from context - :param project_id: Project id derived from context - :rtype: A dictionary containing Consistencygroup metadata - """ - body = {'consistencygroup-from-src': {'name': name, - 'description': description, - 'cgsnapshot_id': cgsnapshot_id, - 'source_cgid': source_cgid, - 'user_id': user_id, - 'project_id': project_id, - 'status': "creating", - }} - - self.run_hooks('modify_body_for_update', body, - 'consistencygroup-from-src') - resp, body = self.api.client.post( - "/consistencygroups/create_from_src", body=body) - return common_base.DictWithMeta(body['consistencygroup'], resp) - - def get(self, group_id): - """Get a consistency group. - - :param group_id: The ID of the consistency group to get. - :rtype: :class:`Consistencygroup` - """ - return self._get("/consistencygroups/%s" % group_id, - "consistencygroup") - - def list(self, detailed=True, search_opts=None): - """Lists all consistency groups. - - :rtype: list of :class:`Consistencygroup` - """ - - query_string = utils.build_query_param(search_opts) - - detail = "" - if detailed: - detail = "/detail" - - return self._list("/consistencygroups%s%s" % (detail, query_string), - "consistencygroups") - - def delete(self, consistencygroup, force=False): - """Delete a consistency group. - - :param Consistencygroup: The :class:`Consistencygroup` to delete. - """ - body = {'consistencygroup': {'force': force}} - self.run_hooks('modify_body_for_action', body, 'consistencygroup') - url = '/consistencygroups/%s/delete' % base.getid(consistencygroup) - resp, body = self.api.client.post(url, body=body) - return common_base.TupleWithMeta((resp, body), resp) - - def update(self, consistencygroup, **kwargs): - """Update the name or description for a consistency group. - - :param Consistencygroup: The :class:`Consistencygroup` to update. - """ - if not kwargs: - return - - body = {"consistencygroup": kwargs} - - return self._update("/consistencygroups/%s" % - base.getid(consistencygroup), body) - - def _action(self, action, consistencygroup, info=None, **kwargs): - """Perform a consistency group "action." - """ - body = {action: info} - self.run_hooks('modify_body_for_action', body, **kwargs) - url = '/consistencygroups/%s/action' % base.getid(consistencygroup) - resp, body = self.api.client.post(url, body=body) - return common_base.TupleWithMeta((resp, body), resp) diff --git a/cinderclient/v2/contrib/__init__.py b/cinderclient/v2/contrib/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/cinderclient/v2/contrib/list_extensions.py b/cinderclient/v2/contrib/list_extensions.py deleted file mode 100644 index 937d34b53..000000000 --- a/cinderclient/v2/contrib/list_extensions.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright (c) 2013 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 cinderclient import base -from cinderclient import utils - - -class ListExtResource(base.Resource): - @property - def summary(self): - descr = self.description.strip() - if not descr: - return '??' - lines = descr.split("\n") - if len(lines) == 1: - return lines[0] - else: - return lines[0] + "..." - - -class ListExtManager(base.Manager): - resource_class = ListExtResource - - def show_all(self): - return self._list("/extensions", 'extensions') - - -def do_list_extensions(client, _args): - """Lists all available os-api extensions.""" - extensions = client.list_extensions.show_all() - fields = ["Name", "Summary", "Alias", "Updated"] - utils.print_list(extensions, fields) diff --git a/cinderclient/v2/limits.py b/cinderclient/v2/limits.py deleted file mode 100644 index dd1666da0..000000000 --- a/cinderclient/v2/limits.py +++ /dev/null @@ -1,99 +0,0 @@ -# Copyright 2013 OpenStack Foundation -# -# 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. -"""Limits interface (v2 extension)""" - -from cinderclient import base -from cinderclient import utils - - -class Limits(base.Resource): - """A collection of RateLimit and AbsoluteLimit objects.""" - - def __repr__(self): - return "" - - @property - def absolute(self): - for (name, value) in list(self._info['absolute'].items()): - yield AbsoluteLimit(name, value) - - @property - def rate(self): - for group in self._info['rate']: - uri = group['uri'] - regex = group['regex'] - for rate in group['limit']: - yield RateLimit(rate['verb'], uri, regex, rate['value'], - rate['remaining'], rate['unit'], - rate['next-available']) - - -class RateLimit(object): - """Data model that represents a flattened view of a single rate limit.""" - - def __init__(self, verb, uri, regex, value, remain, - unit, next_available): - self.verb = verb - self.uri = uri - self.regex = regex - self.value = value - self.remain = remain - self.unit = unit - self.next_available = next_available - - def __eq__(self, other): - return self.uri == other.uri \ - and self.regex == other.regex \ - and self.value == other.value \ - and self.verb == other.verb \ - and self.remain == other.remain \ - and self.unit == other.unit \ - and self.next_available == other.next_available - - def __repr__(self): - return "" % (self.verb, self.uri) - - -class AbsoluteLimit(object): - """Data model that represents a single absolute limit.""" - - def __init__(self, name, value): - self.name = name - self.value = value - - def __eq__(self, other): - return self.value == other.value and self.name == other.name - - def __repr__(self): - return "" % (self.name) - - -class LimitsManager(base.Manager): - """Manager object used to interact with limits resource.""" - - resource_class = Limits - - def get(self, tenant_id=None): - """Get a specific extension. - - :rtype: :class:`Limits` - """ - opts = {} - if tenant_id: - opts['tenant_id'] = tenant_id - - query_string = utils.build_query_param(opts) - - return self._get("/limits%s" % query_string, "limits") diff --git a/cinderclient/v2/pools.py b/cinderclient/v2/pools.py deleted file mode 100644 index 2a55e77bc..000000000 --- a/cinderclient/v2/pools.py +++ /dev/null @@ -1,60 +0,0 @@ -# Copyright (C) 2015 Hewlett-Packard Development Company, L.P. -# 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. - -"""Pools interface (v2 extension)""" - -from cinderclient import base - - -class Pool(base.Resource): - NAME_ATTR = 'name' - - def __repr__(self): - return "" % self.name - - -class PoolManager(base.Manager): - """Manage :class:`Pool` resources.""" - resource_class = Pool - - def list(self, detailed=False): - """Lists all - - :rtype: list of :class:`Pool` - """ - if detailed is True: - pools = self._list("/scheduler-stats/get_pools?detail=True", - "pools") - # Other than the name, all of the pool data is buried below in - # a 'capabilities' dictionary. In order to be consistent with the - # get-pools command line, these elements are moved up a level to - # be attributes of the pool itself. - for pool in pools: - if hasattr(pool, 'capabilities'): - for k, v in pool.capabilities.items(): - setattr(pool, k, v) - - # Remove the capabilities dictionary since all of its - # elements have been copied up to the containing pool - del pool.capabilities - return pools - else: - pools = self._list("/scheduler-stats/get_pools", "pools") - - # avoid cluttering the basic pool list with capabilities dict - for pool in pools: - if hasattr(pool, 'capabilities'): - del pool.capabilities - return pools diff --git a/cinderclient/v2/qos_specs.py b/cinderclient/v2/qos_specs.py deleted file mode 100644 index 147132a8a..000000000 --- a/cinderclient/v2/qos_specs.py +++ /dev/null @@ -1,155 +0,0 @@ -# Copyright (c) 2013 eBay Inc. -# Copyright (c) OpenStack Foundation -# -# 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. - -""" -QoS Specs interface. -""" - -from cinderclient.apiclient import base as common_base -from cinderclient import base - - -class QoSSpecs(base.Resource): - """QoS specs entity represents quality-of-service parameters/requirements. - - A QoS specs is a set of parameters or requirements for quality-of-service - purpose, which can be associated with volume types (for now). In future, - QoS specs may be extended to be associated other entities, such as single - volume. - """ - def __repr__(self): - return "" % self.name - - def delete(self): - return self.manager.delete(self) - - -class QoSSpecsManager(base.ManagerWithFind): - """ - Manage :class:`QoSSpecs` resources. - """ - resource_class = QoSSpecs - - def list(self, search_opts=None): - """Get a list of all qos specs. - - :rtype: list of :class:`QoSSpecs`. - """ - return self._list("/qos-specs", "qos_specs") - - def get(self, qos_specs): - """Get a specific qos specs. - - :param qos_specs: The ID of the :class:`QoSSpecs` to get. - :rtype: :class:`QoSSpecs` - """ - return self._get("/qos-specs/%s" % base.getid(qos_specs), "qos_specs") - - def delete(self, qos_specs, force=False): - """Delete a specific qos specs. - - :param qos_specs: The ID of the :class:`QoSSpecs` to be removed. - :param force: Flag that indicates whether to delete target qos specs - if it was in-use. - """ - return self._delete("/qos-specs/%s?force=%s" % - (base.getid(qos_specs), force)) - - def create(self, name, specs): - """Create a qos specs. - - :param name: Descriptive name of the qos specs, must be unique - :param specs: A dict of key/value pairs to be set - :rtype: :class:`QoSSpecs` - """ - - body = { - "qos_specs": { - "name": name, - } - } - - body["qos_specs"].update(specs) - return self._create("/qos-specs", body, "qos_specs") - - def set_keys(self, qos_specs, specs): - """Add/Update keys in qos specs. - - :param qos_specs: The ID of qos specs - :param specs: A dict of key/value pairs to be set - :rtype: :class:`QoSSpecs` - """ - - body = { - "qos_specs": {} - } - - body["qos_specs"].update(specs) - return self._update("/qos-specs/%s" % qos_specs, body) - - def unset_keys(self, qos_specs, specs): - """Remove keys from a qos specs. - - :param qos_specs: The ID of qos specs - :param specs: A list of key to be unset - :rtype: :class:`QoSSpecs` - """ - - body = {'keys': specs} - - return self._update("/qos-specs/%s/delete_keys" % qos_specs, - body) - - def get_associations(self, qos_specs): - """Get associated entities of a qos specs. - - :param qos_specs: The id of the :class: `QoSSpecs` - :return: a list of entities that associated with specific qos specs. - """ - return self._list("/qos-specs/%s/associations" % base.getid(qos_specs), - "qos_associations") - - def associate(self, qos_specs, vol_type_id): - """Associate a volume type with specific qos specs. - - :param qos_specs: The qos specs to be associated with - :param vol_type_id: The volume type id to be associated with - """ - resp, body = self.api.client.get( - "/qos-specs/%s/associate?vol_type_id=%s" % - (base.getid(qos_specs), vol_type_id)) - return common_base.TupleWithMeta((resp, body), resp) - - def disassociate(self, qos_specs, vol_type_id): - """Disassociate qos specs from volume type. - - :param qos_specs: The qos specs to be associated with - :param vol_type_id: The volume type id to be associated with - """ - resp, body = self.api.client.get( - "/qos-specs/%s/disassociate?vol_type_id=%s" % - (base.getid(qos_specs), vol_type_id)) - return common_base.TupleWithMeta((resp, body), resp) - - def disassociate_all(self, qos_specs): - """Disassociate all entities from specific qos specs. - - :param qos_specs: The qos specs to be associated with - """ - resp, body = self.api.client.get( - "/qos-specs/%s/disassociate_all" % - base.getid(qos_specs)) - return common_base.TupleWithMeta((resp, body), resp) diff --git a/cinderclient/v2/quota_classes.py b/cinderclient/v2/quota_classes.py deleted file mode 100644 index 1958fa133..000000000 --- a/cinderclient/v2/quota_classes.py +++ /dev/null @@ -1,47 +0,0 @@ -# Copyright (c) 2013 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 cinderclient import base - - -class QuotaClassSet(base.Resource): - - @property - def id(self): - """Needed by base.Resource to self-refresh and be indexed.""" - return self.class_name - - def update(self, *args, **kwargs): - return self.manager.update(self.class_name, *args, **kwargs) - - -class QuotaClassSetManager(base.Manager): - resource_class = QuotaClassSet - - def get(self, class_name): - return self._get("/os-quota-class-sets/%s" % (class_name), - "quota_class_set") - - def update(self, class_name, **updates): - quota_class_set = {} - - for update in updates: - quota_class_set[update] = updates[update] - - result = self._update('/os-quota-class-sets/%s' % (class_name), - {'quota_class_set': quota_class_set}) - return self.resource_class(self, - result['quota_class_set'], loaded=True, - resp=result.request_ids) diff --git a/cinderclient/v2/quotas.py b/cinderclient/v2/quotas.py deleted file mode 100644 index bebf32a39..000000000 --- a/cinderclient/v2/quotas.py +++ /dev/null @@ -1,56 +0,0 @@ -# Copyright (c) 2013 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 cinderclient import base - - -class QuotaSet(base.Resource): - - @property - def id(self): - """Needed by base.Resource to self-refresh and be indexed.""" - return self.tenant_id - - def update(self, *args, **kwargs): - return self.manager.update(self.tenant_id, *args, **kwargs) - - -class QuotaSetManager(base.Manager): - resource_class = QuotaSet - - def get(self, tenant_id, usage=False): - if hasattr(tenant_id, 'tenant_id'): - tenant_id = tenant_id.tenant_id - return self._get("/os-quota-sets/%s?usage=%s" % (tenant_id, usage), - "quota_set") - - def update(self, tenant_id, **updates): - body = {'quota_set': {'tenant_id': tenant_id}} - - for update in updates: - body['quota_set'][update] = updates[update] - - result = self._update('/os-quota-sets/%s' % (tenant_id), body) - return self.resource_class(self, result['quota_set'], loaded=True, - resp=result.request_ids) - - def defaults(self, tenant_id): - return self._get('/os-quota-sets/%s/defaults' % tenant_id, - 'quota_set') - - def delete(self, tenant_id): - if hasattr(tenant_id, 'tenant_id'): - tenant_id = tenant_id.tenant_id - return self._delete("/os-quota-sets/%s" % tenant_id) diff --git a/cinderclient/v2/services.py b/cinderclient/v2/services.py deleted file mode 100644 index 68ea1bca9..000000000 --- a/cinderclient/v2/services.py +++ /dev/null @@ -1,80 +0,0 @@ -# Copyright (c) 2013 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. - -""" -service interface -""" - -from cinderclient import base - - -class Service(base.Resource): - - def __repr__(self): - return "" % (self.binary, self.host) - - -class ServiceManager(base.ManagerWithFind): - resource_class = Service - - def list(self, host=None, binary=None): - """ - Describes service list for host. - - :param host: destination host name. - :param binary: service binary. - """ - url = "/os-services" - filters = [] - if host: - filters.append("host=%s" % host) - if binary: - filters.append("binary=%s" % binary) - if filters: - url = "%s?%s" % (url, "&".join(filters)) - return self._list(url, "services") - - def enable(self, host, binary): - """Enable the service specified by hostname and binary.""" - body = {"host": host, "binary": binary} - result = self._update("/os-services/enable", body) - return self.resource_class(self, result, resp=result.request_ids) - - def disable(self, host, binary): - """Disable the service specified by hostname and binary.""" - body = {"host": host, "binary": binary} - result = self._update("/os-services/disable", body) - return self.resource_class(self, result, resp=result.request_ids) - - def disable_log_reason(self, host, binary, reason): - """Disable the service with reason.""" - body = {"host": host, "binary": binary, "disabled_reason": reason} - result = self._update("/os-services/disable-log-reason", body) - return self.resource_class(self, result, resp=result.request_ids) - - def freeze_host(self, host): - """Freeze the service specified by hostname.""" - body = {"host": host} - return self._update("/os-services/freeze", body) - - def thaw_host(self, host): - """Thaw the service specified by hostname.""" - body = {"host": host} - return self._update("/os-services/thaw", body) - - def failover_host(self, host, backend_id): - """Failover a replicated backend by hostname.""" - body = {"host": host, "backend_id": backend_id} - return self._update("/os-services/failover_host", body) diff --git a/cinderclient/v2/volume_backups.py b/cinderclient/v2/volume_backups.py deleted file mode 100644 index bcf3e01fd..000000000 --- a/cinderclient/v2/volume_backups.py +++ /dev/null @@ -1,137 +0,0 @@ -# Copyright (C) 2013 Hewlett-Packard Development Company, L.P. -# 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. - -""" -Volume Backups interface (v2 extension). -""" - -from cinderclient.apiclient import base as common_base -from cinderclient import base - - -class VolumeBackup(base.Resource): - """A volume backup is a block level backup of a volume.""" - - def __repr__(self): - return "" % self.id - - def delete(self, force=False): - """Delete this volume backup.""" - return self.manager.delete(self, force) - - def reset_state(self, state): - return self.manager.reset_state(self, state) - - def update(self, **kwargs): - """Update the name or description for this backup.""" - return self.manager.update(self, **kwargs) - - -class VolumeBackupManager(base.ManagerWithFind): - """Manage :class:`VolumeBackup` resources.""" - resource_class = VolumeBackup - - def create(self, volume_id, container=None, - name=None, description=None, - incremental=False, force=False, - snapshot_id=None): - """Creates a volume backup. - - :param volume_id: The ID of the volume to backup. - :param container: The name of the backup service container. - :param name: The name of the backup. - :param description: The description of the backup. - :param incremental: Incremental backup. - :param force: If True, allows an in-use volume to be backed up. - :param snapshot_id: The ID of the snapshot to backup. This should - be a snapshot of the src volume, when specified, - the new backup will be based on the snapshot. - :rtype: :class:`VolumeBackup` - """ - body = {'backup': {'volume_id': volume_id, - 'container': container, - 'name': name, - 'description': description, - 'incremental': incremental, - 'force': force, - 'snapshot_id': snapshot_id, }} - return self._create('/backups', body, 'backup') - - def get(self, backup_id): - """Show volume backup details. - - :param backup_id: The ID of the backup to display. - :rtype: :class:`VolumeBackup` - """ - return self._get("/backups/%s" % backup_id, "backup") - - def list(self, detailed=True, search_opts=None, marker=None, limit=None, - sort=None): - """Get a list of all volume backups. - - :rtype: list of :class:`VolumeBackup` - """ - resource_type = "backups" - url = self._build_list_url(resource_type, detailed=detailed, - search_opts=search_opts, marker=marker, - limit=limit, sort=sort) - return self._list(url, resource_type, limit=limit) - - def delete(self, backup, force=False): - """Delete a volume backup. - - :param backup: The :class:`VolumeBackup` to delete. - :param force: Allow delete in state other than error or available. - """ - if force: - return self._action('os-force_delete', backup) - else: - return self._delete("/backups/%s" % base.getid(backup)) - - def reset_state(self, backup, state): - """Update the specified volume backup with the provided state.""" - return self._action('os-reset_status', backup, - {'status': state} if state else {}) - - def _action(self, action, backup, info=None, **kwargs): - """Perform a volume backup action.""" - body = {action: info} - self.run_hooks('modify_body_for_action', body, **kwargs) - url = '/backups/%s/action' % base.getid(backup) - resp, body = self.api.client.post(url, body=body) - return common_base.TupleWithMeta((resp, body), resp) - - def export_record(self, backup_id): - """Export volume backup metadata record. - - :param backup_id: The ID of the backup to export. - :rtype: A dictionary containing 'backup_url' and 'backup_service'. - """ - resp, body = \ - self.api.client.get("/backups/%s/export_record" % backup_id) - return common_base.DictWithMeta(body['backup-record'], resp) - - def import_record(self, backup_service, backup_url): - """Import volume backup metadata record. - - :param backup_service: Backup service to use for importing the backup - :param backup_url: Backup URL for importing the backup metadata - :rtype: A dictionary containing volume backup metadata. - """ - body = {'backup-record': {'backup_service': backup_service, - 'backup_url': backup_url}} - self.run_hooks('modify_body_for_update', body, 'backup-record') - resp, body = self.api.client.post("/backups/import_record", body=body) - return common_base.DictWithMeta(body['backup'], resp) diff --git a/cinderclient/v2/volume_backups_restore.py b/cinderclient/v2/volume_backups_restore.py deleted file mode 100644 index a76cb090e..000000000 --- a/cinderclient/v2/volume_backups_restore.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright (C) 2013 Hewlett-Packard Development Company, L.P. -# 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. - -"""Volume Backups Restore interface (v2 extension). - -This is part of the Volume Backups interface. -""" - -from cinderclient import base - - -class VolumeBackupsRestore(base.Resource): - """A Volume Backups Restore represents a restore operation.""" - def __repr__(self): - return "" % self.volume_id - - -class VolumeBackupRestoreManager(base.Manager): - """Manage :class:`VolumeBackupsRestore` resources.""" - resource_class = VolumeBackupsRestore - - def restore(self, backup_id, volume_id=None, name=None): - """Restore a backup to a volume. - - :param backup_id: The ID of the backup to restore. - :param volume_id: The ID of the volume to restore the backup to. - :param name : The name for new volume creation to restore. - :rtype: :class:`Restore` - """ - body = {'restore': {'volume_id': volume_id, 'name': name}} - return self._create("/backups/%s/restore" % backup_id, - body, "restore") diff --git a/cinderclient/v2/volume_encryption_types.py b/cinderclient/v2/volume_encryption_types.py deleted file mode 100644 index 9edacf978..000000000 --- a/cinderclient/v2/volume_encryption_types.py +++ /dev/null @@ -1,104 +0,0 @@ -# Copyright (c) 2013 The Johns Hopkins University/Applied Physics Laboratory -# 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. - -""" -Volume Encryption Type interface -""" - -from cinderclient.apiclient import base as common_base -from cinderclient import base - - -class VolumeEncryptionType(base.Resource): - """ - A Volume Encryption Type is a collection of settings used to conduct - encryption for a specific volume type. - """ - def __repr__(self): - return "" % self.encryption_id - - -class VolumeEncryptionTypeManager(base.ManagerWithFind): - """ - Manage :class: `VolumeEncryptionType` resources. - """ - resource_class = VolumeEncryptionType - - def list(self, search_opts=None): - """ - List all volume encryption types. - - :param search_opts: Search options to filter out volume - encryption types - :return: a list of :class: VolumeEncryptionType instances - """ - # Since the encryption type is a volume type extension, we cannot get - # all encryption types without going through all volume types. - volume_types = self.api.volume_types.list() - encryption_types = [] - list_of_resp = [] - for volume_type in volume_types: - encryption_type = self._get("/types/%s/encryption" - % base.getid(volume_type)) - if hasattr(encryption_type, 'volume_type_id'): - encryption_types.append(encryption_type) - - list_of_resp.extend(encryption_type.request_ids) - - return common_base.ListWithMeta(encryption_types, list_of_resp) - - def get(self, volume_type): - """ - Get the volume encryption type for the specified volume type. - - :param volume_type: the volume type to query - :return: an instance of :class: VolumeEncryptionType - """ - return self._get("/types/%s/encryption" % base.getid(volume_type)) - - def create(self, volume_type, specs): - """ - Creates encryption type for a volume type. Default: admin only. - - :param volume_type: the volume type on which to add an encryption type - :param specs: the encryption type specifications to add - :return: an instance of :class: VolumeEncryptionType - """ - body = {'encryption': specs} - return self._create("/types/%s/encryption" % base.getid(volume_type), - body, "encryption") - - def update(self, volume_type, specs): - """ - Update the encryption type information for the specified volume type. - - :param volume_type: the volume type whose encryption type information - must be updated - :param specs: the encryption type specifications to update - :return: an instance of :class: VolumeEncryptionType - """ - body = {'encryption': specs} - return self._update("/types/%s/encryption/provider" % - base.getid(volume_type), body) - - def delete(self, volume_type): - """ - Delete the encryption type information for the specified volume type. - - :param volume_type: the volume type whose encryption type information - must be deleted - """ - return self._delete("/types/%s/encryption/provider" % - base.getid(volume_type)) diff --git a/cinderclient/v2/volume_snapshots.py b/cinderclient/v2/volume_snapshots.py deleted file mode 100644 index a89c135f4..000000000 --- a/cinderclient/v2/volume_snapshots.py +++ /dev/null @@ -1,39 +0,0 @@ -# Copyright (c) 2013 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. - -"""Volume snapshot interface (v2 extension).""" - -from cinderclient import api_versions -from cinderclient.v3 import volume_snapshots - - -class Snapshot(volume_snapshots.Snapshot): - def list_manageable(self, host, detailed=True, marker=None, limit=None, - offset=None, sort=None): - return self.manager.list_manageable(host, detailed=detailed, - marker=marker, limit=limit, - offset=offset, sort=sort) - - -class SnapshotManager(volume_snapshots.SnapshotManager): - resource_class = Snapshot - - @api_versions.wraps("2.0") - def list_manageable(self, host, detailed=True, marker=None, limit=None, - offset=None, sort=None): - url = self._build_list_url("os-snapshot-manage", detailed=detailed, - search_opts={'host': host}, marker=marker, - limit=limit, offset=offset, sort=sort) - return self._list(url, "manageable-snapshots") diff --git a/cinderclient/v2/volume_transfers.py b/cinderclient/v2/volume_transfers.py deleted file mode 100644 index 00508c0d0..000000000 --- a/cinderclient/v2/volume_transfers.py +++ /dev/null @@ -1,88 +0,0 @@ -# Copyright (C) 2013 Hewlett-Packard Development Company, L.P. -# 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. - -""" -Volume transfer interface (v2 extension). -""" - -from cinderclient import base -from cinderclient import utils - - -class VolumeTransfer(base.Resource): - """Transfer a volume from one tenant to another""" - - def __repr__(self): - return "" % self.id - - def delete(self): - """Delete this volume transfer.""" - return self.manager.delete(self) - - -class VolumeTransferManager(base.ManagerWithFind): - """Manage :class:`VolumeTransfer` resources.""" - resource_class = VolumeTransfer - - def create(self, volume_id, name=None): - """Creates a volume transfer. - - :param volume_id: The ID of the volume to transfer. - :param name: The name of the transfer. - :rtype: :class:`VolumeTransfer` - """ - body = {'transfer': {'volume_id': volume_id, - 'name': name}} - return self._create('/os-volume-transfer', body, 'transfer') - - def accept(self, transfer_id, auth_key): - """Accept a volume transfer. - - :param transfer_id: The ID of the transfer to accept. - :param auth_key: The auth_key of the transfer. - :rtype: :class:`VolumeTransfer` - """ - body = {'accept': {'auth_key': auth_key}} - return self._create('/os-volume-transfer/%s/accept' % transfer_id, - body, 'transfer') - - def get(self, transfer_id): - """Show details of a volume transfer. - - :param transfer_id: The ID of the volume transfer to display. - :rtype: :class:`VolumeTransfer` - """ - return self._get("/os-volume-transfer/%s" % transfer_id, "transfer") - - def list(self, detailed=True, search_opts=None): - """Get a list of all volume transfer. - - :rtype: list of :class:`VolumeTransfer` - """ - query_string = utils.build_query_param(search_opts) - - detail = "" - if detailed: - detail = "/detail" - - return self._list("/os-volume-transfer%s%s" % (detail, query_string), - "transfers") - - def delete(self, transfer_id): - """Delete a volume transfer. - - :param transfer_id: The :class:`VolumeTransfer` to delete. - """ - return self._delete("/os-volume-transfer/%s" % base.getid(transfer_id)) diff --git a/cinderclient/v2/volume_type_access.py b/cinderclient/v2/volume_type_access.py deleted file mode 100644 index bdd2e7028..000000000 --- a/cinderclient/v2/volume_type_access.py +++ /dev/null @@ -1,53 +0,0 @@ -# Copyright 2014 OpenStack Foundation -# -# 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. - -"""Volume type access interface.""" - -from cinderclient.apiclient import base as common_base -from cinderclient import base - - -class VolumeTypeAccess(base.Resource): - def __repr__(self): - return "" % self.project_id - - -class VolumeTypeAccessManager(base.ManagerWithFind): - """ - Manage :class:`VolumeTypeAccess` resources. - """ - resource_class = VolumeTypeAccess - - def list(self, volume_type): - return self._list( - '/types/%s/os-volume-type-access' % base.getid(volume_type), - 'volume_type_access') - - def add_project_access(self, volume_type, project): - """Add a project to the given volume type access list.""" - info = {'project': project} - return self._action('addProjectAccess', volume_type, info) - - def remove_project_access(self, volume_type, project): - """Remove a project from the given volume type access list.""" - info = {'project': project} - return self._action('removeProjectAccess', volume_type, info) - - def _action(self, action, volume_type, info, **kwargs): - """Perform a volume type action.""" - body = {action: info} - self.run_hooks('modify_body_for_action', body, **kwargs) - url = '/types/%s/action' % base.getid(volume_type) - resp, body = self.api.client.post(url, body=body) - return common_base.TupleWithMeta((resp, body), resp) diff --git a/cinderclient/v2/volume_types.py b/cinderclient/v2/volume_types.py deleted file mode 100644 index 546d776d2..000000000 --- a/cinderclient/v2/volume_types.py +++ /dev/null @@ -1,153 +0,0 @@ -# Copyright (c) 2013 OpenStack Foundation -# -# 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. - -"""Volume Type interface.""" - -from cinderclient.apiclient import base as common_base -from cinderclient import base - - -class VolumeType(base.Resource): - """A Volume Type is the type of volume to be created.""" - def __repr__(self): - return "" % self.name - - @property - def is_public(self): - """ - Provide a user-friendly accessor to os-volume-type-access:is_public - """ - return self._info.get("os-volume-type-access:is_public", - self._info.get("is_public", 'N/A')) - - def get_keys(self): - """Get extra specs from a volume type. - - :param vol_type: The :class:`VolumeType` to get extra specs from - """ - _resp, body = self.manager.api.client.get( - "/types/%s/extra_specs" % - base.getid(self)) - return body["extra_specs"] - - def set_keys(self, metadata): - """Set extra specs on a volume type. - - :param type : The :class:`VolumeType` to set extra spec on - :param metadata: A dict of key/value pairs to be set - """ - body = {'extra_specs': metadata} - return self.manager._create( - "/types/%s/extra_specs" % base.getid(self), - body, - "extra_specs", - return_raw=True) - - def unset_keys(self, keys): - """Unset extra specs on a volue type. - - :param type_id: The :class:`VolumeType` to unset extra spec on - :param keys: A list of keys to be unset - """ - - # NOTE(jdg): This wasn't actually doing all of the keys before - # the return in the loop resulted in only ONE key being unset, - # since on success the return was ListWithMeta class, we'll only - # interrupt the loop and if an exception is raised. - response_list = [] - for k in keys: - resp, body = self.manager._delete( - "/types/%s/extra_specs/%s" % ( - base.getid(self), k)) - response_list.append(resp) - - return common_base.ListWithMeta([], response_list) - - -class VolumeTypeManager(base.ManagerWithFind): - """Manage :class:`VolumeType` resources.""" - resource_class = VolumeType - - def list(self, search_opts=None, is_public=None): - """Lists all volume types. - - :rtype: list of :class:`VolumeType`. - """ - query_string = '' - if not is_public: - query_string = '?is_public=%s' % is_public - return self._list("/types%s" % (query_string), "volume_types") - - def get(self, volume_type): - """Get a specific volume type. - - :param volume_type: The ID of the :class:`VolumeType` to get. - :rtype: :class:`VolumeType` - """ - return self._get("/types/%s" % base.getid(volume_type), "volume_type") - - def default(self): - """Get the default volume type. - - :rtype: :class:`VolumeType` - """ - return self._get("/types/default", "volume_type") - - def delete(self, volume_type): - """Deletes a specific volume_type. - - :param volume_type: The name or ID of the :class:`VolumeType` to get. - """ - return self._delete("/types/%s" % base.getid(volume_type)) - - def create(self, name, description=None, is_public=True): - """Creates a volume type. - - :param name: Descriptive name of the volume type - :param description: Description of the volume type - :param is_public: Volume type visibility - :rtype: :class:`VolumeType` - """ - - body = { - "volume_type": { - "name": name, - "description": description, - "os-volume-type-access:is_public": is_public, - } - } - - return self._create("/types", body, "volume_type") - - def update(self, volume_type, name=None, description=None, is_public=None): - """Update the name and/or description for a volume type. - - :param volume_type: The ID of the :class:`VolumeType` to update. - :param name: Descriptive name of the volume type. - :param description: Description of the volume type. - :rtype: :class:`VolumeType` - """ - - body = { - "volume_type": { - "name": name, - "description": description - } - } - if is_public is not None: - body["volume_type"]["is_public"] = is_public - - return self._update("/types/%s" % base.getid(volume_type), - body, response_key="volume_type") diff --git a/cinderclient/v3/capabilities.py b/cinderclient/v3/capabilities.py index 76a3b4e0e..c837a4009 100644 --- a/cinderclient/v3/capabilities.py +++ b/cinderclient/v3/capabilities.py @@ -16,4 +16,24 @@ """Capabilities interface (v3 extension)""" -from cinderclient.v2.capabilities import * # noqa +from cinderclient import base + + +class Capabilities(base.Resource): + NAME_ATTR = 'name' + + def __repr__(self): + return "" % self._info.get('namespace') + + +class CapabilitiesManager(base.Manager): + """Manage :class:`Capabilities` resources.""" + resource_class = Capabilities + + def get(self, host): + """Show backend volume stats and properties. + + :param host: Specified backend to obtain volume stats and properties. + :rtype: :class:`Capabilities` + """ + return self._get('/capabilities/%s' % host, None) diff --git a/cinderclient/v3/cgsnapshots.py b/cinderclient/v3/cgsnapshots.py index 0ed0cdcf8..1f5abc6e3 100644 --- a/cinderclient/v3/cgsnapshots.py +++ b/cinderclient/v3/cgsnapshots.py @@ -15,4 +15,98 @@ """cgsnapshot interface (v3 extension).""" -from cinderclient.v2.cgsnapshots import * # noqa +from cinderclient.apiclient import base as common_base +from cinderclient import base +from cinderclient import utils + + +class Cgsnapshot(base.Resource): + """A cgsnapshot is snapshot of a consistency group.""" + def __repr__(self): + return "" % self.id + + def delete(self): + """Delete this cgsnapshot.""" + return self.manager.delete(self) + + def update(self, **kwargs): + """Update the name or description for this cgsnapshot.""" + return self.manager.update(self, **kwargs) + + +class CgsnapshotManager(base.ManagerWithFind): + """Manage :class:`Cgsnapshot` resources.""" + resource_class = Cgsnapshot + + def create(self, consistencygroup_id, name=None, description=None, + user_id=None, + project_id=None): + """Creates a cgsnapshot. + + :param consistencygroup: Name or uuid of a consistency group + :param name: Name of the cgsnapshot + :param description: Description of the cgsnapshot + :param user_id: User id derived from context + :param project_id: Project id derived from context + :rtype: :class:`Cgsnapshot` + """ + + body = {'cgsnapshot': {'consistencygroup_id': consistencygroup_id, + 'name': name, + 'description': description, + 'user_id': user_id, + 'project_id': project_id, + 'status': "creating", + }} + + return self._create('/cgsnapshots', body, 'cgsnapshot') + + def get(self, cgsnapshot_id): + """Get a cgsnapshot. + + :param cgsnapshot_id: The ID of the cgsnapshot to get. + :rtype: :class:`Cgsnapshot` + """ + return self._get("/cgsnapshots/%s" % cgsnapshot_id, "cgsnapshot") + + def list(self, detailed=True, search_opts=None): + """Lists all cgsnapshots. + + :rtype: list of :class:`Cgsnapshot` + """ + query_string = utils.build_query_param(search_opts) + + detail = "" + if detailed: + detail = "/detail" + + return self._list("/cgsnapshots%s%s" % (detail, query_string), + "cgsnapshots") + + def delete(self, cgsnapshot): + """Delete a cgsnapshot. + + :param cgsnapshot: The :class:`Cgsnapshot` to delete. + """ + return self._delete("/cgsnapshots/%s" % base.getid(cgsnapshot)) + + def update(self, cgsnapshot, **kwargs): + """Update the name or description for a cgsnapshot. + + :param cgsnapshot: The :class:`Cgsnapshot` to update. + """ + if not kwargs: + return + + body = {"cgsnapshot": kwargs} + + return self._update("/cgsnapshots/%s" % base.getid(cgsnapshot), body) + + def _action(self, action, cgsnapshot, info=None, **kwargs): + """Perform a cgsnapshot "action." + """ + body = {action: info} + self.run_hooks('modify_body_for_action', body, **kwargs) + url = '/cgsnapshots/%s/action' % base.getid(cgsnapshot) + resp, body = self.api.client.post(url, body=body) + return common_base.TupleWithMeta((resp, body), resp) diff --git a/cinderclient/v3/consistencygroups.py b/cinderclient/v3/consistencygroups.py index a29fd8332..13bc2eefb 100644 --- a/cinderclient/v3/consistencygroups.py +++ b/cinderclient/v3/consistencygroups.py @@ -15,4 +15,135 @@ """Consistencygroup interface (v3 extension).""" -from cinderclient.v2.consistencygroups import * # noqa +from cinderclient.apiclient import base as common_base +from cinderclient import base +from cinderclient import utils + + +class Consistencygroup(base.Resource): + """A Consistencygroup of volumes.""" + def __repr__(self): + return "" % self.id + + def delete(self, force='False'): + """Delete this consistency group.""" + return self.manager.delete(self, force) + + def update(self, **kwargs): + """Update the name or description for this consistency group.""" + return self.manager.update(self, **kwargs) + + +class ConsistencygroupManager(base.ManagerWithFind): + """Manage :class:`Consistencygroup` resources.""" + resource_class = Consistencygroup + + def create(self, volume_types, name=None, + description=None, user_id=None, + project_id=None, availability_zone=None): + """Creates a consistency group. + + :param name: Name of the ConsistencyGroup + :param description: Description of the ConsistencyGroup + :param volume_types: Types of volume + :param user_id: User id derived from context + :param project_id: Project id derived from context + :param availability_zone: Availability Zone to use + :rtype: :class:`Consistencygroup` + """ + + body = {'consistencygroup': {'name': name, + 'description': description, + 'volume_types': volume_types, + 'user_id': user_id, + 'project_id': project_id, + 'availability_zone': availability_zone, + 'status': "creating", + }} + + return self._create('/consistencygroups', body, 'consistencygroup') + + def create_from_src(self, cgsnapshot_id, source_cgid, name=None, + description=None, user_id=None, + project_id=None): + """Creates a consistency group from a cgsnapshot or a source CG. + + :param cgsnapshot_id: UUID of a CGSnapshot + :param source_cgid: UUID of a source CG + :param name: Name of the ConsistencyGroup + :param description: Description of the ConsistencyGroup + :param user_id: User id derived from context + :param project_id: Project id derived from context + :rtype: A dictionary containing Consistencygroup metadata + """ + body = {'consistencygroup-from-src': {'name': name, + 'description': description, + 'cgsnapshot_id': cgsnapshot_id, + 'source_cgid': source_cgid, + 'user_id': user_id, + 'project_id': project_id, + 'status': "creating", + }} + + self.run_hooks('modify_body_for_update', body, + 'consistencygroup-from-src') + resp, body = self.api.client.post( + "/consistencygroups/create_from_src", body=body) + return common_base.DictWithMeta(body['consistencygroup'], resp) + + def get(self, group_id): + """Get a consistency group. + + :param group_id: The ID of the consistency group to get. + :rtype: :class:`Consistencygroup` + """ + return self._get("/consistencygroups/%s" % group_id, + "consistencygroup") + + def list(self, detailed=True, search_opts=None): + """Lists all consistency groups. + + :rtype: list of :class:`Consistencygroup` + """ + + query_string = utils.build_query_param(search_opts) + + detail = "" + if detailed: + detail = "/detail" + + return self._list("/consistencygroups%s%s" % (detail, query_string), + "consistencygroups") + + def delete(self, consistencygroup, force=False): + """Delete a consistency group. + + :param Consistencygroup: The :class:`Consistencygroup` to delete. + """ + body = {'consistencygroup': {'force': force}} + self.run_hooks('modify_body_for_action', body, 'consistencygroup') + url = '/consistencygroups/%s/delete' % base.getid(consistencygroup) + resp, body = self.api.client.post(url, body=body) + return common_base.TupleWithMeta((resp, body), resp) + + def update(self, consistencygroup, **kwargs): + """Update the name or description for a consistency group. + + :param Consistencygroup: The :class:`Consistencygroup` to update. + """ + if not kwargs: + return + + body = {"consistencygroup": kwargs} + + return self._update("/consistencygroups/%s" % + base.getid(consistencygroup), body) + + def _action(self, action, consistencygroup, info=None, **kwargs): + """Perform a consistency group "action." + """ + body = {action: info} + self.run_hooks('modify_body_for_action', body, **kwargs) + url = '/consistencygroups/%s/action' % base.getid(consistencygroup) + resp, body = self.api.client.post(url, body=body) + return common_base.TupleWithMeta((resp, body), resp) diff --git a/cinderclient/v3/contrib/list_extensions.py b/cinderclient/v3/contrib/list_extensions.py index 0b5e84584..937d34b53 100644 --- a/cinderclient/v3/contrib/list_extensions.py +++ b/cinderclient/v3/contrib/list_extensions.py @@ -13,4 +13,32 @@ # License for the specific language governing permissions and limitations # under the License. -from cinderclient.v2.contrib.list_extensions import * # noqa +from cinderclient import base +from cinderclient import utils + + +class ListExtResource(base.Resource): + @property + def summary(self): + descr = self.description.strip() + if not descr: + return '??' + lines = descr.split("\n") + if len(lines) == 1: + return lines[0] + else: + return lines[0] + "..." + + +class ListExtManager(base.Manager): + resource_class = ListExtResource + + def show_all(self): + return self._list("/extensions", 'extensions') + + +def do_list_extensions(client, _args): + """Lists all available os-api extensions.""" + extensions = client.list_extensions.show_all() + fields = ["Name", "Summary", "Alias", "Updated"] + utils.print_list(extensions, fields) diff --git a/cinderclient/v3/limits.py b/cinderclient/v3/limits.py index 29d1dca45..69f053f11 100644 --- a/cinderclient/v3/limits.py +++ b/cinderclient/v3/limits.py @@ -13,4 +13,86 @@ # See the License for the specific language governing permissions and # limitations under the License. -from cinderclient.v2.limits import * # noqa +from cinderclient import base +from cinderclient import utils + + +class Limits(base.Resource): + """A collection of RateLimit and AbsoluteLimit objects.""" + + def __repr__(self): + return "" + + @property + def absolute(self): + for (name, value) in list(self._info['absolute'].items()): + yield AbsoluteLimit(name, value) + + @property + def rate(self): + for group in self._info['rate']: + uri = group['uri'] + regex = group['regex'] + for rate in group['limit']: + yield RateLimit(rate['verb'], uri, regex, rate['value'], + rate['remaining'], rate['unit'], + rate['next-available']) + + +class RateLimit(object): + """Data model that represents a flattened view of a single rate limit.""" + + def __init__(self, verb, uri, regex, value, remain, + unit, next_available): + self.verb = verb + self.uri = uri + self.regex = regex + self.value = value + self.remain = remain + self.unit = unit + self.next_available = next_available + + def __eq__(self, other): + return self.uri == other.uri \ + and self.regex == other.regex \ + and self.value == other.value \ + and self.verb == other.verb \ + and self.remain == other.remain \ + and self.unit == other.unit \ + and self.next_available == other.next_available + + def __repr__(self): + return "" % (self.verb, self.uri) + + +class AbsoluteLimit(object): + """Data model that represents a single absolute limit.""" + + def __init__(self, name, value): + self.name = name + self.value = value + + def __eq__(self, other): + return self.value == other.value and self.name == other.name + + def __repr__(self): + return "" % (self.name) + + +class LimitsManager(base.Manager): + """Manager object used to interact with limits resource.""" + + resource_class = Limits + + def get(self, tenant_id=None): + """Get a specific extension. + + :rtype: :class:`Limits` + """ + opts = {} + if tenant_id: + opts['tenant_id'] = tenant_id + + query_string = utils.build_query_param(opts) + + return self._get("/limits%s" % query_string, "limits") diff --git a/cinderclient/v3/pools.py b/cinderclient/v3/pools.py index f4ab422aa..5303f8422 100644 --- a/cinderclient/v3/pools.py +++ b/cinderclient/v3/pools.py @@ -15,4 +15,46 @@ """Pools interface (v3 extension)""" -from cinderclient.v2.pools import * # noqa +from cinderclient import base + + +class Pool(base.Resource): + NAME_ATTR = 'name' + + def __repr__(self): + return "" % self.name + + +class PoolManager(base.Manager): + """Manage :class:`Pool` resources.""" + resource_class = Pool + + def list(self, detailed=False): + """Lists all + + :rtype: list of :class:`Pool` + """ + if detailed is True: + pools = self._list("/scheduler-stats/get_pools?detail=True", + "pools") + # Other than the name, all of the pool data is buried below in + # a 'capabilities' dictionary. In order to be consistent with the + # get-pools command line, these elements are moved up a level to + # be attributes of the pool itself. + for pool in pools: + if hasattr(pool, 'capabilities'): + for k, v in pool.capabilities.items(): + setattr(pool, k, v) + + # Remove the capabilities dictionary since all of its + # elements have been copied up to the containing pool + del pool.capabilities + return pools + else: + pools = self._list("/scheduler-stats/get_pools", "pools") + + # avoid cluttering the basic pool list with capabilities dict + for pool in pools: + if hasattr(pool, 'capabilities'): + del pool.capabilities + return pools diff --git a/cinderclient/v3/qos_specs.py b/cinderclient/v3/qos_specs.py index f70b56949..972316482 100644 --- a/cinderclient/v3/qos_specs.py +++ b/cinderclient/v3/qos_specs.py @@ -19,4 +19,138 @@ QoS Specs interface. """ -from cinderclient.v2.qos_specs import * # noqa +from cinderclient.apiclient import base as common_base +from cinderclient import base + + +class QoSSpecs(base.Resource): + """QoS specs entity represents quality-of-service parameters/requirements. + + A QoS specs is a set of parameters or requirements for quality-of-service + purpose, which can be associated with volume types (for now). In future, + QoS specs may be extended to be associated other entities, such as single + volume. + """ + def __repr__(self): + return "" % self.name + + def delete(self): + return self.manager.delete(self) + + +class QoSSpecsManager(base.ManagerWithFind): + """ + Manage :class:`QoSSpecs` resources. + """ + resource_class = QoSSpecs + + def list(self, search_opts=None): + """Get a list of all qos specs. + + :rtype: list of :class:`QoSSpecs`. + """ + return self._list("/qos-specs", "qos_specs") + + def get(self, qos_specs): + """Get a specific qos specs. + + :param qos_specs: The ID of the :class:`QoSSpecs` to get. + :rtype: :class:`QoSSpecs` + """ + return self._get("/qos-specs/%s" % base.getid(qos_specs), "qos_specs") + + def delete(self, qos_specs, force=False): + """Delete a specific qos specs. + + :param qos_specs: The ID of the :class:`QoSSpecs` to be removed. + :param force: Flag that indicates whether to delete target qos specs + if it was in-use. + """ + return self._delete("/qos-specs/%s?force=%s" % + (base.getid(qos_specs), force)) + + def create(self, name, specs): + """Create a qos specs. + + :param name: Descriptive name of the qos specs, must be unique + :param specs: A dict of key/value pairs to be set + :rtype: :class:`QoSSpecs` + """ + + body = { + "qos_specs": { + "name": name, + } + } + + body["qos_specs"].update(specs) + return self._create("/qos-specs", body, "qos_specs") + + def set_keys(self, qos_specs, specs): + """Add/Update keys in qos specs. + + :param qos_specs: The ID of qos specs + :param specs: A dict of key/value pairs to be set + :rtype: :class:`QoSSpecs` + """ + + body = { + "qos_specs": {} + } + + body["qos_specs"].update(specs) + return self._update("/qos-specs/%s" % qos_specs, body) + + def unset_keys(self, qos_specs, specs): + """Remove keys from a qos specs. + + :param qos_specs: The ID of qos specs + :param specs: A list of key to be unset + :rtype: :class:`QoSSpecs` + """ + + body = {'keys': specs} + + return self._update("/qos-specs/%s/delete_keys" % qos_specs, + body) + + def get_associations(self, qos_specs): + """Get associated entities of a qos specs. + + :param qos_specs: The id of the :class: `QoSSpecs` + :return: a list of entities that associated with specific qos specs. + """ + return self._list("/qos-specs/%s/associations" % base.getid(qos_specs), + "qos_associations") + + def associate(self, qos_specs, vol_type_id): + """Associate a volume type with specific qos specs. + + :param qos_specs: The qos specs to be associated with + :param vol_type_id: The volume type id to be associated with + """ + resp, body = self.api.client.get( + "/qos-specs/%s/associate?vol_type_id=%s" % + (base.getid(qos_specs), vol_type_id)) + return common_base.TupleWithMeta((resp, body), resp) + + def disassociate(self, qos_specs, vol_type_id): + """Disassociate qos specs from volume type. + + :param qos_specs: The qos specs to be associated with + :param vol_type_id: The volume type id to be associated with + """ + resp, body = self.api.client.get( + "/qos-specs/%s/disassociate?vol_type_id=%s" % + (base.getid(qos_specs), vol_type_id)) + return common_base.TupleWithMeta((resp, body), resp) + + def disassociate_all(self, qos_specs): + """Disassociate all entities from specific qos specs. + + :param qos_specs: The qos specs to be associated with + """ + resp, body = self.api.client.get( + "/qos-specs/%s/disassociate_all" % + base.getid(qos_specs)) + return common_base.TupleWithMeta((resp, body), resp) diff --git a/cinderclient/v3/quota_classes.py b/cinderclient/v3/quota_classes.py index 693621237..1958fa133 100644 --- a/cinderclient/v3/quota_classes.py +++ b/cinderclient/v3/quota_classes.py @@ -13,4 +13,35 @@ # License for the specific language governing permissions and limitations # under the License. -from cinderclient.v2.quota_classes import * # noqa +from cinderclient import base + + +class QuotaClassSet(base.Resource): + + @property + def id(self): + """Needed by base.Resource to self-refresh and be indexed.""" + return self.class_name + + def update(self, *args, **kwargs): + return self.manager.update(self.class_name, *args, **kwargs) + + +class QuotaClassSetManager(base.Manager): + resource_class = QuotaClassSet + + def get(self, class_name): + return self._get("/os-quota-class-sets/%s" % (class_name), + "quota_class_set") + + def update(self, class_name, **updates): + quota_class_set = {} + + for update in updates: + quota_class_set[update] = updates[update] + + result = self._update('/os-quota-class-sets/%s' % (class_name), + {'quota_class_set': quota_class_set}) + return self.resource_class(self, + result['quota_class_set'], loaded=True, + resp=result.request_ids) diff --git a/cinderclient/v3/quotas.py b/cinderclient/v3/quotas.py index 63dfba835..1295f6064 100644 --- a/cinderclient/v3/quotas.py +++ b/cinderclient/v3/quotas.py @@ -13,10 +13,28 @@ # License for the specific language governing permissions and limitations # under the License. -from cinderclient.v2 import quotas +from cinderclient import base -class QuotaSetManager(quotas.QuotaSetManager): +class QuotaSet(base.Resource): + + @property + def id(self): + """Needed by base.Resource to self-refresh and be indexed.""" + return self.tenant_id + + def update(self, *args, **kwargs): + return self.manager.update(self.tenant_id, *args, **kwargs) + + +class QuotaSetManager(base.Manager): + resource_class = QuotaSet + + def get(self, tenant_id, usage=False): + if hasattr(tenant_id, 'tenant_id'): + tenant_id = tenant_id.tenant_id + return self._get("/os-quota-sets/%s?usage=%s" % (tenant_id, usage), + "quota_set") def update(self, tenant_id, **updates): skip_validation = updates.pop('skip_validation', True) @@ -32,3 +50,12 @@ class QuotaSetManager(quotas.QuotaSetManager): result = self._update(request_url, body) return self.resource_class(self, result['quota_set'], loaded=True, resp=result.request_ids) + + def defaults(self, tenant_id): + return self._get('/os-quota-sets/%s/defaults' % tenant_id, + 'quota_set') + + def delete(self, tenant_id): + if hasattr(tenant_id, 'tenant_id'): + tenant_id = tenant_id.tenant_id + return self._delete("/os-quota-sets/%s" % tenant_id) diff --git a/cinderclient/v3/services.py b/cinderclient/v3/services.py index 55d3ee476..b48691f8e 100644 --- a/cinderclient/v3/services.py +++ b/cinderclient/v3/services.py @@ -19,9 +19,12 @@ service interface from cinderclient import api_versions from cinderclient import base -from cinderclient.v2 import services -Service = services.Service + +class Service(base.Resource): + + def __repr__(self): + return "" % (self.binary, self.host) class LogLevel(base.Resource): @@ -30,7 +33,61 @@ class LogLevel(base.Resource): self.binary, self.host, self.prefix, self.level) -class ServiceManager(services.ServiceManager): +class ServiceManagerBase(base.ManagerWithFind): + resource_class = Service + + def list(self, host=None, binary=None): + """ + Describes service list for host. + + :param host: destination host name. + :param binary: service binary. + """ + url = "/os-services" + filters = [] + if host: + filters.append("host=%s" % host) + if binary: + filters.append("binary=%s" % binary) + if filters: + url = "%s?%s" % (url, "&".join(filters)) + return self._list(url, "services") + + def enable(self, host, binary): + """Enable the service specified by hostname and binary.""" + body = {"host": host, "binary": binary} + result = self._update("/os-services/enable", body) + return self.resource_class(self, result, resp=result.request_ids) + + def disable(self, host, binary): + """Disable the service specified by hostname and binary.""" + body = {"host": host, "binary": binary} + result = self._update("/os-services/disable", body) + return self.resource_class(self, result, resp=result.request_ids) + + def disable_log_reason(self, host, binary, reason): + """Disable the service with reason.""" + body = {"host": host, "binary": binary, "disabled_reason": reason} + result = self._update("/os-services/disable-log-reason", body) + return self.resource_class(self, result, resp=result.request_ids) + + def freeze_host(self, host): + """Freeze the service specified by hostname.""" + body = {"host": host} + return self._update("/os-services/freeze", body) + + def thaw_host(self, host): + """Thaw the service specified by hostname.""" + body = {"host": host} + return self._update("/os-services/thaw", body) + + def failover_host(self, host, backend_id): + """Failover a replicated backend by hostname.""" + body = {"host": host, "backend_id": backend_id} + return self._update("/os-services/failover_host", body) + + +class ServiceManager(ServiceManagerBase): @api_versions.wraps("3.0") def server_api_version(self): """Returns the API Version supported by the server. diff --git a/cinderclient/v3/shell_base.py b/cinderclient/v3/shell_base.py index e3f868200..4dc6da9a4 100644 --- a/cinderclient/v3/shell_base.py +++ b/cinderclient/v3/shell_base.py @@ -25,7 +25,7 @@ from cinderclient import base from cinderclient import exceptions from cinderclient import shell_utils from cinderclient import utils -from cinderclient.v2 import availability_zones +from cinderclient.v3 import availability_zones def _translate_attachments(info): diff --git a/cinderclient/v3/volume_backups.py b/cinderclient/v3/volume_backups.py index 22d25a338..61069c8e5 100644 --- a/cinderclient/v3/volume_backups.py +++ b/cinderclient/v3/volume_backups.py @@ -18,14 +18,32 @@ Volume Backups interface (v3 extension). """ from cinderclient import api_versions +from cinderclient.apiclient import base as common_base from cinderclient import base -from cinderclient.v2 import volume_backups -VolumeBackup = volume_backups.VolumeBackup +class VolumeBackup(base.Resource): + """A volume backup is a block level backup of a volume.""" + + def __repr__(self): + return "" % self.id + + def delete(self, force=False): + """Delete this volume backup.""" + return self.manager.delete(self, force) + + def reset_state(self, state): + return self.manager.reset_state(self, state) + + def update(self, **kwargs): + """Update the name or description for this backup.""" + return self.manager.update(self, **kwargs) -class VolumeBackupManager(volume_backups.VolumeBackupManager): +class VolumeBackupManager(base.ManagerWithFind): + """Manage :class:`VolumeBackup` resources.""" + resource_class = VolumeBackup + @api_versions.wraps("3.9") def update(self, backup, **kwargs): """Update the name or description for a backup. @@ -124,3 +142,70 @@ class VolumeBackupManager(volume_backups.VolumeBackupManager): if availability_zone: body['backup']['availability_zone'] = availability_zone return self._create('/backups', body, 'backup') + + def get(self, backup_id): + """Show volume backup details. + + :param backup_id: The ID of the backup to display. + :rtype: :class:`VolumeBackup` + """ + return self._get("/backups/%s" % backup_id, "backup") + + def list(self, detailed=True, search_opts=None, marker=None, limit=None, + sort=None): + """Get a list of all volume backups. + + :rtype: list of :class:`VolumeBackup` + """ + resource_type = "backups" + url = self._build_list_url(resource_type, detailed=detailed, + search_opts=search_opts, marker=marker, + limit=limit, sort=sort) + return self._list(url, resource_type, limit=limit) + + def delete(self, backup, force=False): + """Delete a volume backup. + + :param backup: The :class:`VolumeBackup` to delete. + :param force: Allow delete in state other than error or available. + """ + if force: + return self._action('os-force_delete', backup) + else: + return self._delete("/backups/%s" % base.getid(backup)) + + def reset_state(self, backup, state): + """Update the specified volume backup with the provided state.""" + return self._action('os-reset_status', backup, + {'status': state} if state else {}) + + def _action(self, action, backup, info=None, **kwargs): + """Perform a volume backup action.""" + body = {action: info} + self.run_hooks('modify_body_for_action', body, **kwargs) + url = '/backups/%s/action' % base.getid(backup) + resp, body = self.api.client.post(url, body=body) + return common_base.TupleWithMeta((resp, body), resp) + + def export_record(self, backup_id): + """Export volume backup metadata record. + + :param backup_id: The ID of the backup to export. + :rtype: A dictionary containing 'backup_url' and 'backup_service'. + """ + resp, body = \ + self.api.client.get("/backups/%s/export_record" % backup_id) + return common_base.DictWithMeta(body['backup-record'], resp) + + def import_record(self, backup_service, backup_url): + """Import volume backup metadata record. + + :param backup_service: Backup service to use for importing the backup + :param backup_url: Backup URL for importing the backup metadata + :rtype: A dictionary containing volume backup metadata. + """ + body = {'backup-record': {'backup_service': backup_service, + 'backup_url': backup_url}} + self.run_hooks('modify_body_for_update', body, 'backup-record') + resp, body = self.api.client.post("/backups/import_record", body=body) + return common_base.DictWithMeta(body['backup'], resp) diff --git a/cinderclient/v3/volume_backups_restore.py b/cinderclient/v3/volume_backups_restore.py index 7e38c00a3..8a35ed162 100644 --- a/cinderclient/v3/volume_backups_restore.py +++ b/cinderclient/v3/volume_backups_restore.py @@ -18,4 +18,27 @@ This is part of the Volume Backups interface. """ -from cinderclient.v2.volume_backups_restore import * # noqa +from cinderclient import base + + +class VolumeBackupsRestore(base.Resource): + """A Volume Backups Restore represents a restore operation.""" + def __repr__(self): + return "" % self.volume_id + + +class VolumeBackupRestoreManager(base.Manager): + """Manage :class:`VolumeBackupsRestore` resources.""" + resource_class = VolumeBackupsRestore + + def restore(self, backup_id, volume_id=None, name=None): + """Restore a backup to a volume. + + :param backup_id: The ID of the backup to restore. + :param volume_id: The ID of the volume to restore the backup to. + :param name : The name for new volume creation to restore. + :rtype: :class:`Restore` + """ + body = {'restore': {'volume_id': volume_id, 'name': name}} + return self._create("/backups/%s/restore" % backup_id, + body, "restore") diff --git a/cinderclient/v3/volume_encryption_types.py b/cinderclient/v3/volume_encryption_types.py index 507709726..531e4d229 100644 --- a/cinderclient/v3/volume_encryption_types.py +++ b/cinderclient/v3/volume_encryption_types.py @@ -18,4 +18,88 @@ Volume Encryption Type interface """ -from cinderclient.v2.volume_encryption_types import * # noqa +from cinderclient.apiclient import base as common_base +from cinderclient import base + + +class VolumeEncryptionType(base.Resource): + """ + A Volume Encryption Type is a collection of settings used to conduct + encryption for a specific volume type. + """ + def __repr__(self): + return "" % self.encryption_id + + +class VolumeEncryptionTypeManager(base.ManagerWithFind): + """ + Manage :class: `VolumeEncryptionType` resources. + """ + resource_class = VolumeEncryptionType + + def list(self, search_opts=None): + """ + List all volume encryption types. + + :param search_opts: Search options to filter out volume + encryption types + :return: a list of :class: VolumeEncryptionType instances + """ + # Since the encryption type is a volume type extension, we cannot get + # all encryption types without going through all volume types. + volume_types = self.api.volume_types.list() + encryption_types = [] + list_of_resp = [] + for volume_type in volume_types: + encryption_type = self._get("/types/%s/encryption" + % base.getid(volume_type)) + if hasattr(encryption_type, 'volume_type_id'): + encryption_types.append(encryption_type) + + list_of_resp.extend(encryption_type.request_ids) + + return common_base.ListWithMeta(encryption_types, list_of_resp) + + def get(self, volume_type): + """ + Get the volume encryption type for the specified volume type. + + :param volume_type: the volume type to query + :return: an instance of :class: VolumeEncryptionType + """ + return self._get("/types/%s/encryption" % base.getid(volume_type)) + + def create(self, volume_type, specs): + """ + Creates encryption type for a volume type. Default: admin only. + + :param volume_type: the volume type on which to add an encryption type + :param specs: the encryption type specifications to add + :return: an instance of :class: VolumeEncryptionType + """ + body = {'encryption': specs} + return self._create("/types/%s/encryption" % base.getid(volume_type), + body, "encryption") + + def update(self, volume_type, specs): + """ + Update the encryption type information for the specified volume type. + + :param volume_type: the volume type whose encryption type information + must be updated + :param specs: the encryption type specifications to update + :return: an instance of :class: VolumeEncryptionType + """ + body = {'encryption': specs} + return self._update("/types/%s/encryption/provider" % + base.getid(volume_type), body) + + def delete(self, volume_type): + """ + Delete the encryption type information for the specified volume type. + + :param volume_type: the volume type whose encryption type information + must be deleted + """ + return self._delete("/types/%s/encryption/provider" % + base.getid(volume_type)) diff --git a/cinderclient/v3/volume_snapshots.py b/cinderclient/v3/volume_snapshots.py index 3691d2fb8..ce7f4e07a 100644 --- a/cinderclient/v3/volume_snapshots.py +++ b/cinderclient/v3/volume_snapshots.py @@ -213,9 +213,17 @@ class SnapshotManager(base.ManagerWithFind): } return self._create('/os-snapshot-manage', body, 'snapshot') + @api_versions.wraps("3.0") + def list_manageable(self, host, detailed=True, marker=None, + limit=None, offset=None, sort=None): + url = self._build_list_url("os-snapshot-manage", detailed=detailed, + search_opts={'host': host}, marker=marker, + limit=limit, offset=offset, sort=sort) + return self._list(url, "manageable-snapshots") + @api_versions.wraps('3.8') - def list_manageable(self, host, detailed=True, marker=None, limit=None, - offset=None, sort=None, cluster=None): + def list_manageable(self, host, detailed=True, marker=None, # noqa: F811 + limit=None, offset=None, sort=None, cluster=None): search_opts = {'cluster': cluster} if cluster else {'host': host} url = self._build_list_url("manageable_snapshots", detailed=detailed, search_opts=search_opts, marker=marker, diff --git a/cinderclient/v3/volume_transfers.py b/cinderclient/v3/volume_transfers.py index f40c5199d..bcf0e0cc0 100644 --- a/cinderclient/v3/volume_transfers.py +++ b/cinderclient/v3/volume_transfers.py @@ -16,10 +16,23 @@ """Volume transfer interface (v3 extension).""" from cinderclient import base -from cinderclient.v2 import volume_transfers -class VolumeTransferManager(volume_transfers.VolumeTransferManager): +class VolumeTransfer(base.Resource): + """Transfer a volume from one tenant to another""" + + def __repr__(self): + return "" % self.id + + def delete(self): + """Delete this volume transfer.""" + return self.manager.delete(self) + + +class VolumeTransferManager(base.ManagerWithFind): + """Manage :class:`VolumeTransfer` resources.""" + resource_class = VolumeTransfer + def create(self, volume_id, name=None, no_snapshots=False): """Creates a volume transfer. diff --git a/cinderclient/v3/volume_type_access.py b/cinderclient/v3/volume_type_access.py index d9fe2eff8..bdd2e7028 100644 --- a/cinderclient/v3/volume_type_access.py +++ b/cinderclient/v3/volume_type_access.py @@ -14,4 +14,40 @@ """Volume type access interface.""" -from cinderclient.v2.volume_type_access import * # noqa +from cinderclient.apiclient import base as common_base +from cinderclient import base + + +class VolumeTypeAccess(base.Resource): + def __repr__(self): + return "" % self.project_id + + +class VolumeTypeAccessManager(base.ManagerWithFind): + """ + Manage :class:`VolumeTypeAccess` resources. + """ + resource_class = VolumeTypeAccess + + def list(self, volume_type): + return self._list( + '/types/%s/os-volume-type-access' % base.getid(volume_type), + 'volume_type_access') + + def add_project_access(self, volume_type, project): + """Add a project to the given volume type access list.""" + info = {'project': project} + return self._action('addProjectAccess', volume_type, info) + + def remove_project_access(self, volume_type, project): + """Remove a project from the given volume type access list.""" + info = {'project': project} + return self._action('removeProjectAccess', volume_type, info) + + def _action(self, action, volume_type, info, **kwargs): + """Perform a volume type action.""" + body = {action: info} + self.run_hooks('modify_body_for_action', body, **kwargs) + url = '/types/%s/action' % base.getid(volume_type) + resp, body = self.api.client.post(url, body=body) + return common_base.TupleWithMeta((resp, body), resp) diff --git a/cinderclient/v3/volumes.py b/cinderclient/v3/volumes.py index 8ea388008..42527f7e7 100644 --- a/cinderclient/v3/volumes.py +++ b/cinderclient/v3/volumes.py @@ -18,10 +18,10 @@ from cinderclient import api_versions from cinderclient.apiclient import base as common_base from cinderclient import base -from cinderclient.v2 import volumes +from cinderclient.v3 import volumes_base -class Volume(volumes.Volume): +class Volume(volumes_base.Volume): def upload_to_image(self, force, image_name, container_format, disk_format, visibility=None, @@ -68,7 +68,7 @@ class Volume(volumes.Volume): cluster=cluster) -class VolumeManager(volumes.VolumeManager): +class VolumeManager(volumes_base.VolumeManager): resource_class = Volume def create(self, size, consistencygroup_id=None, @@ -246,16 +246,24 @@ class VolumeManager(volumes.VolumeManager): body['volume']['cluster'] = cluster return self._create('/os-volume-manage', body, 'volume') + @api_versions.wraps('3.0') + def list_manageable(self, host, detailed=True, marker=None, + limit=None, offset=None, sort=None): + url = self._build_list_url("os-volume-manage", detailed=detailed, + search_opts={'host': host}, marker=marker, + limit=limit, offset=offset, sort=sort) + return self._list(url, "manageable-volumes") + @api_versions.wraps('3.8') - def list_manageable(self, host, detailed=True, marker=None, limit=None, - offset=None, sort=None, cluster=None): + def list_manageable(self, host, detailed=True, marker=None, # noqa: F811 + limit=None, offset=None, sort=None, cluster=None): search_opts = {'cluster': cluster} if cluster else {'host': host} url = self._build_list_url("manageable_volumes", detailed=detailed, search_opts=search_opts, marker=marker, limit=limit, offset=offset, sort=sort) return self._list(url, "manageable-volumes") - @api_versions.wraps("2.0", "3.32") + @api_versions.wraps("3.0", "3.32") def get_pools(self, detail): """Show pool information for backends.""" query_string = "" diff --git a/cinderclient/v2/volumes.py b/cinderclient/v3/volumes_base.py similarity index 82% rename from cinderclient/v2/volumes.py rename to cinderclient/v3/volumes_base.py index 4c380fbd0..3b00b59d7 100644 --- a/cinderclient/v2/volumes.py +++ b/cinderclient/v3/volumes_base.py @@ -13,7 +13,7 @@ # License for the specific language governing permissions and limitations # under the License. -"""Volume interface (v2 extension).""" +"""Base Volume interface.""" from cinderclient.apiclient import base as common_base from cinderclient import base @@ -130,24 +130,6 @@ class Volume(base.Resource): """ return self.manager.show_image_metadata(self) - def upload_to_image(self, force, image_name, container_format, - disk_format, visibility=None, - protected=None): - """Upload a volume to image service as an image. - - :param force: Boolean to enables or disables upload of a volume that - is attached to an instance. - :param image_name: The new image name. - :param container_format: Container format type. - :param disk_format: Disk format type. - :param visibility: The accessibility of image (allowed for - 3.1-latest). - :param protected: Boolean to decide whether prevents image from being - deleted (allowed for 3.1-latest). - """ - return self.manager.upload_to_image(self, force, image_name, - container_format, disk_format) - def force_delete(self): """Delete the specified volume ignoring its current state. @@ -175,11 +157,6 @@ class Volume(base.Resource): """ return self.manager.extend(self, new_size) - def migrate_volume(self, host, force_host_copy, lock_volume): - """Migrate the volume to a new host.""" - return self.manager.migrate_volume(self, host, force_host_copy, - lock_volume) - def retype(self, volume_type, policy): """Change a volume's type.""" return self.manager.retype(self, volume_type, policy) @@ -197,16 +174,6 @@ class Volume(base.Resource): """ return self.manager.update_readonly_flag(self, read_only) - def manage(self, host, ref, name=None, description=None, - volume_type=None, availability_zone=None, metadata=None, - bootable=False): - """Manage an existing volume.""" - return self.manager.manage(host=host, ref=ref, name=name, - description=description, - volume_type=volume_type, - availability_zone=availability_zone, - metadata=metadata, bootable=bootable) - def list_manageable(self, host, detailed=True, marker=None, limit=None, offset=None, sort=None): return self.manager.list_manageable(host, detailed=detailed, @@ -226,52 +193,6 @@ class VolumeManager(base.ManagerWithFind): """Manage :class:`Volume` resources.""" resource_class = Volume - def create(self, size, consistencygroup_id=None, - snapshot_id=None, - source_volid=None, name=None, description=None, - volume_type=None, user_id=None, - project_id=None, availability_zone=None, - metadata=None, imageRef=None, scheduler_hints=None): - """Create a volume. - - :param size: Size of volume in GB - :param consistencygroup_id: ID of the consistencygroup - :param snapshot_id: ID of the snapshot - :param name: Name of the volume - :param description: Description of the volume - :param volume_type: Type of volume - :param user_id: User id derived from context (IGNORED) - :param project_id: Project id derived from context (IGNORED) - :param availability_zone: Availability Zone to use - :param metadata: Optional metadata to set on volume creation - :param imageRef: reference to an image stored in glance - :param source_volid: ID of source volume to clone from - :param scheduler_hints: (optional extension) arbitrary key-value pairs - specified by the client to help boot an instance - :rtype: :class:`Volume` - """ - if metadata is None: - volume_metadata = {} - else: - volume_metadata = metadata - - body = {'volume': {'size': size, - 'consistencygroup_id': consistencygroup_id, - 'snapshot_id': snapshot_id, - 'name': name, - 'description': description, - 'volume_type': volume_type, - 'availability_zone': availability_zone, - 'metadata': volume_metadata, - 'imageRef': imageRef, - 'source_volid': source_volid, - }} - - if scheduler_hints: - body['OS-SCH-HNT:scheduler_hints'] = scheduler_hints - - return self._create('/volumes', body, 'volume') - def get(self, volume_id): """Get a volume. @@ -603,13 +524,6 @@ class VolumeManager(base.ManagerWithFind): }} return self._create('/os-volume-manage', body, 'volume') - def list_manageable(self, host, detailed=True, marker=None, limit=None, - offset=None, sort=None): - url = self._build_list_url("os-volume-manage", detailed=detailed, - search_opts={'host': host}, marker=marker, - limit=limit, offset=offset, sort=sort) - return self._list(url, "manageable-volumes") - def unmanage(self, volume): """Unmanage a volume.""" return self._action('os-unmanage', volume, None) diff --git a/doc/source/contributor/unit_tests.rst b/doc/source/contributor/unit_tests.rst index 387ecb00b..248e1beb5 100644 --- a/doc/source/contributor/unit_tests.rst +++ b/doc/source/contributor/unit_tests.rst @@ -29,11 +29,11 @@ Running a subset of tests using tox One common activity is to just run a single test, you can do this with tox simply by specifying to just run py3 tests against a single test:: - tox -e py3 -- -n cinderclient.tests.unit.v2.test_volumes.VolumesTest.test_attach + tox -e py3 -- -n cinderclient.tests.unit.v3.test_volumes.VolumesTest.test_create_volume Or all tests in the test_volumes.py file:: - tox -e py3 -- -n cinderclient.tests.unit.v2.test_volumes + tox -e py3 -- -n cinderclient.tests.unit.v3.test_volumes For more information on these options and how to run tests, please see the `stestr documentation `_. diff --git a/releasenotes/notes/drop-v2-support-e578ca21c7c6b532.yaml b/releasenotes/notes/drop-v2-support-e578ca21c7c6b532.yaml new file mode 100644 index 000000000..8360a601f --- /dev/null +++ b/releasenotes/notes/drop-v2-support-e578ca21c7c6b532.yaml @@ -0,0 +1,5 @@ +--- +upgrade: + - | + This release drops support of the Block Storage API v2. The last version + of the python-cinderclient supporting that API is the 7.x series.