From e6be9a3edf0904762cb6816a04b8d3d146a1f639 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 13 Mar 2025 11:39:24 +0000 Subject: [PATCH] volume: Remove Cinder v1 support The Cinder v1 API was removed in Queens [1]. Its replacement, the v2 API, has existed since Grizzly [2]. More importantly, the v1 commands are implemented using python-cinderclient but support for the v1 API was removed from python-cinderclient in Train [3], meaning none of these have worked since then. Clearly if no one has noticed or cared in the 6 years or so since that happened, it's safe to say we can delete these commands. [1] https://opendev.org/openstack/cinder/commit/3e91de956e1947a7014709010b99df380242ac74 [2] https://opendev.org/openstack/cinder/commit/75ca60f619c953df1d95ff2eab7de78f7d5aebe8 [3] https://opendev.org/openstack/python-cinderclient/commit/2189e5702b7ba91a87e1db21024799e1520d8ad0 Change-Id: Ibe1cd6461d2cb78826467078aa17272f171746aa Signed-off-by: Stephen Finucane --- doc/source/cli/_hidden/image.rst | 2 +- doc/source/cli/command-objects/limits.rst | 2 +- doc/source/cli/command-objects/quota.rst | 2 +- .../cli/command-objects/volume-backup.rst | 2 +- doc/source/cli/command-objects/volume-qos.rst | 2 +- .../cli/command-objects/volume-service.rst | 2 +- .../cli/command-objects/volume-snapshot.rst | 2 +- .../volume-transfer-request.rst | 2 +- .../cli/command-objects/volume-type.rst | 2 +- doc/source/cli/command-objects/volume.rst | 2 +- .../tests/functional/volume/v1/__init__.py | 0 .../tests/functional/volume/v1/common.py | 35 - .../tests/functional/volume/v1/test_qos.py | 100 -- .../functional/volume/v1/test_service.py | 76 - .../functional/volume/v1/test_snapshot.py | 232 --- .../volume/v1/test_transfer_request.py | 111 -- .../tests/functional/volume/v1/test_volume.py | 228 --- .../functional/volume/v1/test_volume_type.py | 213 --- openstackclient/tests/unit/image/v1/fakes.py | 4 +- .../tests/unit/volume/v1/__init__.py | 0 openstackclient/tests/unit/volume/v1/fakes.py | 615 ------- .../tests/unit/volume/v1/test_qos_specs.py | 471 ------ .../tests/unit/volume/v1/test_service.py | 295 ---- .../unit/volume/v1/test_transfer_request.py | 380 ----- .../tests/unit/volume/v1/test_type.py | 633 ------- .../tests/unit/volume/v1/test_volume.py | 1447 ----------------- .../unit/volume/v1/test_volume_backup.py | 435 ----- openstackclient/volume/v1/__init__.py | 0 openstackclient/volume/v1/qos_specs.py | 376 ----- openstackclient/volume/v1/service.py | 136 -- openstackclient/volume/v1/volume.py | 727 --------- openstackclient/volume/v1/volume_backup.py | 301 ---- openstackclient/volume/v1/volume_snapshot.py | 432 ----- .../volume/v1/volume_transfer_request.py | 200 --- openstackclient/volume/v1/volume_type.py | 519 ------ ...e-volume-v1-commands-bfa14e9cae54929f.yaml | 35 + setup.cfg | 47 - 37 files changed, 47 insertions(+), 8021 deletions(-) delete mode 100644 openstackclient/tests/functional/volume/v1/__init__.py delete mode 100644 openstackclient/tests/functional/volume/v1/common.py delete mode 100644 openstackclient/tests/functional/volume/v1/test_qos.py delete mode 100644 openstackclient/tests/functional/volume/v1/test_service.py delete mode 100644 openstackclient/tests/functional/volume/v1/test_snapshot.py delete mode 100644 openstackclient/tests/functional/volume/v1/test_transfer_request.py delete mode 100644 openstackclient/tests/functional/volume/v1/test_volume.py delete mode 100644 openstackclient/tests/functional/volume/v1/test_volume_type.py delete mode 100644 openstackclient/tests/unit/volume/v1/__init__.py delete mode 100644 openstackclient/tests/unit/volume/v1/fakes.py delete mode 100644 openstackclient/tests/unit/volume/v1/test_qos_specs.py delete mode 100644 openstackclient/tests/unit/volume/v1/test_service.py delete mode 100644 openstackclient/tests/unit/volume/v1/test_transfer_request.py delete mode 100644 openstackclient/tests/unit/volume/v1/test_type.py delete mode 100644 openstackclient/tests/unit/volume/v1/test_volume.py delete mode 100644 openstackclient/tests/unit/volume/v1/test_volume_backup.py delete mode 100644 openstackclient/volume/v1/__init__.py delete mode 100644 openstackclient/volume/v1/qos_specs.py delete mode 100644 openstackclient/volume/v1/service.py delete mode 100644 openstackclient/volume/v1/volume.py delete mode 100644 openstackclient/volume/v1/volume_backup.py delete mode 100644 openstackclient/volume/v1/volume_snapshot.py delete mode 100644 openstackclient/volume/v1/volume_transfer_request.py delete mode 100644 openstackclient/volume/v1/volume_type.py create mode 100644 releasenotes/notes/remove-volume-v1-commands-bfa14e9cae54929f.yaml diff --git a/doc/source/cli/_hidden/image.rst b/doc/source/cli/_hidden/image.rst index 85ffde6f39..06919e7ab9 100644 --- a/doc/source/cli/_hidden/image.rst +++ b/doc/source/cli/_hidden/image.rst @@ -3,7 +3,7 @@ image ===== .. NOTE(efried): This page is hidden from the main TOC; it's here so links in - the wild redirect somewhere sane, because previously identity v2 and v3 were + the wild redirect somewhere sane, because previously image v2 and v3 were combined in a single page. .. toctree:: diff --git a/doc/source/cli/command-objects/limits.rst b/doc/source/cli/command-objects/limits.rst index 3a0f99b376..11d53802c6 100644 --- a/doc/source/cli/command-objects/limits.rst +++ b/doc/source/cli/command-objects/limits.rst @@ -4,7 +4,7 @@ limits The Compute and Block Storage APIs have resource usage limits. -Compute v2, Block Storage v1 +Block Storage v2, v3; Compute v2 .. autoprogram-cliff:: openstack.common diff --git a/doc/source/cli/command-objects/quota.rst b/doc/source/cli/command-objects/quota.rst index cab1265240..59a8a9bb4e 100644 --- a/doc/source/cli/command-objects/quota.rst +++ b/doc/source/cli/command-objects/quota.rst @@ -5,7 +5,7 @@ quota Resource quotas appear in multiple APIs, OpenStackClient presents them as a single object with multiple properties. -Block Storage v1, v2, Compute v2, Network v2 +Block Storage v1, v3; Compute v2; Network v2 .. autoprogram-cliff:: openstack.common :command: quota * diff --git a/doc/source/cli/command-objects/volume-backup.rst b/doc/source/cli/command-objects/volume-backup.rst index 63e5f3655b..7b036ca0ce 100644 --- a/doc/source/cli/command-objects/volume-backup.rst +++ b/doc/source/cli/command-objects/volume-backup.rst @@ -2,7 +2,7 @@ volume backup ============= -Block Storage v1, v2, v3 +Block Storage v2, v3 .. autoprogram-cliff:: openstack.volume.v3 :command: volume backup * diff --git a/doc/source/cli/command-objects/volume-qos.rst b/doc/source/cli/command-objects/volume-qos.rst index 3475b93860..82c4d540c7 100644 --- a/doc/source/cli/command-objects/volume-qos.rst +++ b/doc/source/cli/command-objects/volume-qos.rst @@ -2,7 +2,7 @@ volume qos ========== -Block Storage v1, v2, v3 +Block Storage v2, v3 .. autoprogram-cliff:: openstack.volume.v3 :command: volume qos * diff --git a/doc/source/cli/command-objects/volume-service.rst b/doc/source/cli/command-objects/volume-service.rst index 3283b29e29..43d455ff5a 100644 --- a/doc/source/cli/command-objects/volume-service.rst +++ b/doc/source/cli/command-objects/volume-service.rst @@ -2,7 +2,7 @@ volume service ============== -Block Storage v1, v2, v3 +Block Storage v2, v3 .. autoprogram-cliff:: openstack.volume.v3 :command: volume service * diff --git a/doc/source/cli/command-objects/volume-snapshot.rst b/doc/source/cli/command-objects/volume-snapshot.rst index be3ad303b9..e63e436dff 100644 --- a/doc/source/cli/command-objects/volume-snapshot.rst +++ b/doc/source/cli/command-objects/volume-snapshot.rst @@ -2,7 +2,7 @@ volume snapshot =============== -Block Storage v1, v2, v3 +Block Storage v2, v3 .. autoprogram-cliff:: openstack.volume.v3 :command: volume snapshot * diff --git a/doc/source/cli/command-objects/volume-transfer-request.rst b/doc/source/cli/command-objects/volume-transfer-request.rst index 61e38c1cf7..97dac02a0a 100644 --- a/doc/source/cli/command-objects/volume-transfer-request.rst +++ b/doc/source/cli/command-objects/volume-transfer-request.rst @@ -2,7 +2,7 @@ volume transfer request ======================= -Block Storage v1, v2, v3 +Block Storage v2, v3 .. autoprogram-cliff:: openstack.volume.v3 :command: volume transfer request * diff --git a/doc/source/cli/command-objects/volume-type.rst b/doc/source/cli/command-objects/volume-type.rst index 1a74a8a66c..003ee67306 100644 --- a/doc/source/cli/command-objects/volume-type.rst +++ b/doc/source/cli/command-objects/volume-type.rst @@ -2,7 +2,7 @@ volume type =========== -Block Storage v1, v2, v3 +Block Storage v2, v3 .. autoprogram-cliff:: openstack.volume.v3 :command: volume type * diff --git a/doc/source/cli/command-objects/volume.rst b/doc/source/cli/command-objects/volume.rst index d7e8291332..337bb9fa2b 100644 --- a/doc/source/cli/command-objects/volume.rst +++ b/doc/source/cli/command-objects/volume.rst @@ -2,7 +2,7 @@ volume ====== -Block Storage v1, v2 +Block Storage v2, v3 .. autoprogram-cliff:: openstack.volume.v3 :command: volume create diff --git a/openstackclient/tests/functional/volume/v1/__init__.py b/openstackclient/tests/functional/volume/v1/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/openstackclient/tests/functional/volume/v1/common.py b/openstackclient/tests/functional/volume/v1/common.py deleted file mode 100644 index 755874785d..0000000000 --- a/openstackclient/tests/functional/volume/v1/common.py +++ /dev/null @@ -1,35 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import fixtures - -from openstackclient.tests.functional.volume import base as volume_base - - -class BaseVolumeTests(volume_base.BaseVolumeTests): - """Base class for Volume functional tests""" - - @classmethod - def setUpClass(cls): - super().setUpClass() - cls.haz_volume_v1 = cls.is_service_enabled('block-storage', '1.0') - - def setUp(self): - super().setUp() - - if not self.haz_volume_v1: - self.skipTest("No Volume v1 service present") - - ver_fixture = fixtures.EnvironmentVariable( - 'OS_VOLUME_API_VERSION', '1' - ) - self.useFixture(ver_fixture) diff --git a/openstackclient/tests/functional/volume/v1/test_qos.py b/openstackclient/tests/functional/volume/v1/test_qos.py deleted file mode 100644 index 5d0aa41f67..0000000000 --- a/openstackclient/tests/functional/volume/v1/test_qos.py +++ /dev/null @@ -1,100 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import uuid - -from openstackclient.tests.functional.volume.v1 import common - - -class QosTests(common.BaseVolumeTests): - """Functional tests for volume qos.""" - - def test_volume_qos_create_list(self): - """Test create, list, delete multiple""" - name1 = uuid.uuid4().hex - cmd_output = self.openstack( - 'volume qos create ' + name1, - parse_output=True, - ) - self.assertEqual(name1, cmd_output['name']) - - name2 = uuid.uuid4().hex - cmd_output = self.openstack( - 'volume qos create ' + name2, - parse_output=True, - ) - self.assertEqual(name2, cmd_output['name']) - - # Test list - cmd_output = self.openstack( - 'volume qos list', - parse_output=True, - ) - names = [x["Name"] for x in cmd_output] - self.assertIn(name1, names) - self.assertIn(name2, names) - - # Test delete multiple - del_output = self.openstack('volume qos delete ' + name1 + ' ' + name2) - self.assertOutput('', del_output) - - def test_volume_qos_set_show_unset(self): - """Tests create volume qos, set, unset, show, delete""" - - name = uuid.uuid4().hex - cmd_output = self.openstack( - 'volume qos create ' - + '--consumer front-end ' - + '--property Alpha=a ' - + name, - parse_output=True, - ) - self.addCleanup(self.openstack, 'volume qos delete ' + name) - self.assertEqual(name, cmd_output['name']) - - self.assertEqual("front-end", cmd_output['consumer']) - - # Test volume qos set - raw_output = self.openstack( - 'volume qos set ' - + '--no-property ' - + '--property Beta=b ' - + '--property Charlie=c ' - + name, - ) - self.assertOutput('', raw_output) - - # Test volume qos show - cmd_output = self.openstack( - 'volume qos show ' + name, - parse_output=True, - ) - self.assertEqual(name, cmd_output['name']) - self.assertEqual( - {'Beta': 'b', 'Charlie': 'c'}, - cmd_output['properties'], - ) - - # Test volume qos unset - raw_output = self.openstack( - 'volume qos unset ' + '--property Charlie ' + name, - ) - self.assertOutput('', raw_output) - - cmd_output = self.openstack( - 'volume qos show ' + name, - parse_output=True, - ) - self.assertEqual(name, cmd_output['name']) - self.assertEqual({'Beta': 'b'}, cmd_output['properties']) - - # TODO(qiangjiahui): Add tests for associate and disassociate volume type diff --git a/openstackclient/tests/functional/volume/v1/test_service.py b/openstackclient/tests/functional/volume/v1/test_service.py deleted file mode 100644 index 8d058c26fe..0000000000 --- a/openstackclient/tests/functional/volume/v1/test_service.py +++ /dev/null @@ -1,76 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from openstackclient.tests.functional.volume.v1 import common - - -class VolumeServiceTests(common.BaseVolumeTests): - """Functional tests for volume service.""" - - def test_volume_service_list(self): - cmd_output = self.openstack('volume service list', parse_output=True) - - # Get the nonredundant services and hosts - services = list({x['Binary'] for x in cmd_output}) - - # Test volume service list --service - cmd_output = self.openstack( - 'volume service list ' + '--service ' + services[0], - parse_output=True, - ) - for x in cmd_output: - self.assertEqual(services[0], x['Binary']) - - # TODO(zhiyong.dai): test volume service list --host after solving - # https://bugs.launchpad.net/python-openstackclient/+bug/1664451 - - def test_volume_service_set(self): - # Get a service and host - cmd_output = self.openstack( - 'volume service list', - parse_output=True, - ) - service_1 = cmd_output[0]['Binary'] - host_1 = cmd_output[0]['Host'] - - # Test volume service set --enable - raw_output = self.openstack( - 'volume service set --enable ' + host_1 + ' ' + service_1 - ) - self.assertOutput('', raw_output) - - cmd_output = self.openstack( - 'volume service list --long', - parse_output=True, - ) - self.assertEqual('enabled', cmd_output[0]['Status']) - self.assertIsNone(cmd_output[0]['Disabled Reason']) - - # Test volume service set --disable and --disable-reason - disable_reason = 'disable_reason' - raw_output = self.openstack( - 'volume service set --disable ' - + '--disable-reason ' - + disable_reason - + ' ' - + host_1 - + ' ' - + service_1 - ) - self.assertOutput('', raw_output) - - cmd_output = self.openstack( - 'volume service list --long', - parse_output=True, - ) - self.assertEqual('disabled', cmd_output[0]['Status']) - self.assertEqual(disable_reason, cmd_output[0]['Disabled Reason']) diff --git a/openstackclient/tests/functional/volume/v1/test_snapshot.py b/openstackclient/tests/functional/volume/v1/test_snapshot.py deleted file mode 100644 index 1acd014a64..0000000000 --- a/openstackclient/tests/functional/volume/v1/test_snapshot.py +++ /dev/null @@ -1,232 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import uuid - -from openstackclient.tests.functional.volume.v1 import common - - -class VolumeSnapshotTests(common.BaseVolumeTests): - """Functional tests for volume snapshot.""" - - VOLLY = uuid.uuid4().hex - - @classmethod - def setUpClass(cls): - super().setUpClass() - # create a volume for all tests to create snapshot - cmd_output = cls.openstack( - 'volume create ' + '--size 1 ' + cls.VOLLY, - parse_output=True, - ) - cls.wait_for_status('volume', cls.VOLLY, 'available') - cls.VOLUME_ID = cmd_output['id'] - - @classmethod - def tearDownClass(cls): - try: - cls.wait_for_status('volume', cls.VOLLY, 'available') - raw_output = cls.openstack('volume delete --force ' + cls.VOLLY) - cls.assertOutput('', raw_output) - finally: - super().tearDownClass() - - def test_volume_snapshot_delete(self): - """Test create, delete multiple""" - name1 = uuid.uuid4().hex - cmd_output = self.openstack( - 'volume snapshot create ' + name1 + ' --volume ' + self.VOLLY, - parse_output=True, - ) - self.assertEqual( - name1, - cmd_output["display_name"], - ) - - name2 = uuid.uuid4().hex - cmd_output = self.openstack( - 'volume snapshot create ' + name2 + ' --volume ' + self.VOLLY, - parse_output=True, - ) - self.assertEqual( - name2, - cmd_output["display_name"], - ) - - self.wait_for_status('volume snapshot', name1, 'available') - self.wait_for_status('volume snapshot', name2, 'available') - - del_output = self.openstack( - 'volume snapshot delete ' + name1 + ' ' + name2 - ) - self.assertOutput('', del_output) - self.wait_for_delete('volume snapshot', name1) - self.wait_for_delete('volume snapshot', name2) - - def test_volume_snapshot_list(self): - """Test create, list filter""" - name1 = uuid.uuid4().hex - cmd_output = self.openstack( - 'volume snapshot create ' + name1 + ' --volume ' + self.VOLLY, - parse_output=True, - ) - self.addCleanup(self.wait_for_delete, 'volume snapshot', name1) - self.addCleanup(self.openstack, 'volume snapshot delete ' + name1) - self.assertEqual( - name1, - cmd_output["display_name"], - ) - self.assertEqual( - self.VOLUME_ID, - cmd_output["volume_id"], - ) - self.assertEqual( - 1, - cmd_output["size"], - ) - self.wait_for_status('volume snapshot', name1, 'available') - - name2 = uuid.uuid4().hex - cmd_output = self.openstack( - 'volume snapshot create ' + name2 + ' --volume ' + self.VOLLY, - parse_output=True, - ) - self.addCleanup(self.wait_for_delete, 'volume snapshot', name2) - self.addCleanup(self.openstack, 'volume snapshot delete ' + name2) - self.assertEqual( - name2, - cmd_output["display_name"], - ) - self.assertEqual( - self.VOLUME_ID, - cmd_output["volume_id"], - ) - self.assertEqual( - 1, - cmd_output["size"], - ) - self.wait_for_status('volume snapshot', name2, 'available') - - # Test list --long, --status - cmd_output = self.openstack( - 'volume snapshot list ' + '--long ' + '--status error', - parse_output=True, - ) - names = [x["Name"] for x in cmd_output] - self.assertNotIn(name1, names) - self.assertNotIn(name2, names) - - # Test list --volume - cmd_output = self.openstack( - 'volume snapshot list ' + '--volume ' + self.VOLLY, - parse_output=True, - ) - names = [x["Name"] for x in cmd_output] - self.assertIn(name1, names) - self.assertIn(name2, names) - - # Test list --name - cmd_output = self.openstack( - 'volume snapshot list ' + '--name ' + name1, - parse_output=True, - ) - names = [x["Name"] for x in cmd_output] - self.assertIn(name1, names) - self.assertNotIn(name2, names) - - def test_snapshot_set(self): - """Test create, set, unset, show, delete volume snapshot""" - name = uuid.uuid4().hex - new_name = name + "_" - cmd_output = self.openstack( - 'volume snapshot create ' - + '--volume ' - + self.VOLLY - + ' --description aaaa ' - + name, - parse_output=True, - ) - self.addCleanup(self.wait_for_delete, 'volume snapshot', new_name) - self.addCleanup(self.openstack, 'volume snapshot delete ' + new_name) - self.assertEqual( - name, - cmd_output["display_name"], - ) - self.assertEqual( - 1, - cmd_output["size"], - ) - self.assertEqual( - 'aaaa', - cmd_output["display_description"], - ) - self.wait_for_status('volume snapshot', name, 'available') - - # Test volume snapshot set - raw_output = self.openstack( - 'volume snapshot set ' - + '--name ' - + new_name - + ' --description bbbb ' - + '--property Alpha=a ' - + '--property Beta=b ' - + name, - ) - self.assertOutput('', raw_output) - - # Show snapshot set result - cmd_output = self.openstack( - 'volume snapshot show ' + new_name, - parse_output=True, - ) - self.assertEqual( - new_name, - cmd_output["display_name"], - ) - self.assertEqual( - 1, - cmd_output["size"], - ) - self.assertEqual( - 'bbbb', - cmd_output["display_description"], - ) - self.assertEqual( - {'Alpha': 'a', 'Beta': 'b'}, - cmd_output["properties"], - ) - - # Test volume unset - raw_output = self.openstack( - 'volume snapshot unset ' + '--property Alpha ' + new_name, - ) - self.assertOutput('', raw_output) - - cmd_output = self.openstack( - 'volume snapshot show ' + new_name, - parse_output=True, - ) - self.assertEqual( - {'Beta': 'b'}, - cmd_output["properties"], - ) - - # Test volume snapshot set --no-property - raw_output = self.openstack( - 'volume snapshot set ' + '--no-property ' + new_name, - ) - self.assertOutput('', raw_output) - cmd_output = self.openstack( - 'volume snapshot show ' + new_name, - parse_output=True, - ) - self.assertEqual({}, cmd_output["properties"]) diff --git a/openstackclient/tests/functional/volume/v1/test_transfer_request.py b/openstackclient/tests/functional/volume/v1/test_transfer_request.py deleted file mode 100644 index 68fe6666ff..0000000000 --- a/openstackclient/tests/functional/volume/v1/test_transfer_request.py +++ /dev/null @@ -1,111 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import uuid - -from openstackclient.tests.functional.volume.v1 import common - - -class TransferRequestTests(common.BaseVolumeTests): - """Functional tests for transfer request.""" - - NAME = uuid.uuid4().hex - VOLUME_NAME = uuid.uuid4().hex - - @classmethod - def setUpClass(cls): - super().setUpClass() - cmd_output = cls.openstack( - 'volume create --size 1 ' + cls.VOLUME_NAME, - parse_output=True, - ) - cls.assertOutput(cls.VOLUME_NAME, cmd_output['name']) - - cls.wait_for_status("volume", cls.VOLUME_NAME, "available") - - @classmethod - def tearDownClass(cls): - try: - raw_output_volume = cls.openstack( - 'volume delete ' + cls.VOLUME_NAME - ) - cls.assertOutput('', raw_output_volume) - finally: - super().tearDownClass() - - def test_volume_transfer_request_accept(self): - volume_name = uuid.uuid4().hex - name = uuid.uuid4().hex - - # create a volume - cmd_output = self.openstack( - 'volume create --size 1 ' + volume_name, - parse_output=True, - ) - self.assertEqual(volume_name, cmd_output['name']) - - # create volume transfer request for the volume - # and get the auth_key of the new transfer request - cmd_output = self.openstack( - 'volume transfer request create ' - + volume_name - + ' --name ' - + name, - parse_output=True, - ) - auth_key = cmd_output['auth_key'] - self.assertTrue(auth_key) - - # accept the volume transfer request - output = self.openstack( - 'volume transfer request accept ' - + name - + ' ' - + '--auth-key ' - + auth_key, - parse_output=True, - ) - self.assertEqual(name, output.get('name')) - - # the volume transfer will be removed by default after accepted - # so just need to delete the volume here - raw_output = self.openstack('volume delete ' + volume_name) - self.assertEqual('', raw_output) - - def test_volume_transfer_request_list_show(self): - name = uuid.uuid4().hex - cmd_output = self.openstack( - 'volume transfer request create ' - + ' --name ' - + name - + ' ' - + self.VOLUME_NAME, - parse_output=True, - ) - self.addCleanup( - self.openstack, 'volume transfer request delete ' + name - ) - self.assertOutput(name, cmd_output['name']) - auth_key = cmd_output['auth_key'] - self.assertTrue(auth_key) - - cmd_output = self.openstack( - 'volume transfer request list', - parse_output=True, - ) - self.assertIn(name, [req['Name'] for req in cmd_output]) - - cmd_output = self.openstack( - 'volume transfer request show ' + name, - parse_output=True, - ) - self.assertEqual(name, cmd_output['name']) diff --git a/openstackclient/tests/functional/volume/v1/test_volume.py b/openstackclient/tests/functional/volume/v1/test_volume.py deleted file mode 100644 index 7c6cc03569..0000000000 --- a/openstackclient/tests/functional/volume/v1/test_volume.py +++ /dev/null @@ -1,228 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import uuid - -from openstackclient.tests.functional.volume.v1 import common - - -class VolumeTests(common.BaseVolumeTests): - """Functional tests for volume.""" - - def test_volume_create_and_delete(self): - """Test create, delete multiple""" - name1 = uuid.uuid4().hex - cmd_output = self.openstack( - 'volume create ' + '--size 1 ' + name1, - parse_output=True, - ) - self.assertEqual( - 1, - cmd_output["size"], - ) - - name2 = uuid.uuid4().hex - cmd_output = self.openstack( - 'volume create ' + '--size 2 ' + name2, - parse_output=True, - ) - self.assertEqual( - 2, - cmd_output["size"], - ) - - self.wait_for_status("volume", name1, "available") - self.wait_for_status("volume", name2, "available") - del_output = self.openstack('volume delete ' + name1 + ' ' + name2) - self.assertOutput('', del_output) - - def test_volume_list(self): - """Test create, list filter""" - name1 = uuid.uuid4().hex - cmd_output = self.openstack( - 'volume create ' + '--size 1 ' + name1, - parse_output=True, - ) - self.addCleanup(self.openstack, 'volume delete ' + name1) - self.assertEqual( - 1, - cmd_output["size"], - ) - self.wait_for_status("volume", name1, "available") - - name2 = uuid.uuid4().hex - cmd_output = self.openstack( - 'volume create ' + '--size 2 ' + name2, - parse_output=True, - ) - self.addCleanup(self.openstack, 'volume delete ' + name2) - self.assertEqual( - 2, - cmd_output["size"], - ) - self.wait_for_status("volume", name2, "available") - - # Test list - cmd_output = self.openstack( - 'volume list ', - parse_output=True, - ) - names = [x["Name"] for x in cmd_output] - self.assertIn(name1, names) - self.assertIn(name2, names) - - # Test list --long - cmd_output = self.openstack( - 'volume list --long', - parse_output=True, - ) - bootable = [x["Bootable"] for x in cmd_output] - self.assertIn('false', bootable) - - # Test list --name - cmd_output = self.openstack( - 'volume list ' + '--name ' + name1, - parse_output=True, - ) - names = [x["Name"] for x in cmd_output] - self.assertIn(name1, names) - self.assertNotIn(name2, names) - - def test_volume_set_and_unset(self): - """Tests create volume, set, unset, show, delete""" - name = uuid.uuid4().hex - cmd_output = self.openstack( - 'volume create ' - + '--size 1 ' - + '--description aaaa ' - + '--property Alpha=a ' - + name, - parse_output=True, - ) - self.assertEqual( - name, - cmd_output["name"], - ) - self.assertEqual( - 1, - cmd_output["size"], - ) - self.assertEqual( - 'aaaa', - cmd_output["display_description"], - ) - self.assertEqual( - {'Alpha': 'a'}, - cmd_output["properties"], - ) - self.assertEqual( - 'false', - cmd_output["bootable"], - ) - self.wait_for_status("volume", name, "available") - - # Test volume set - new_name = uuid.uuid4().hex - self.addCleanup(self.openstack, 'volume delete ' + new_name) - raw_output = self.openstack( - 'volume set ' - + '--name ' - + new_name - + ' --size 2 ' - + '--description bbbb ' - + '--no-property ' - + '--property Beta=b ' - + '--property Gamma=c ' - + '--bootable ' - + name, - ) - self.assertOutput('', raw_output) - - cmd_output = self.openstack( - 'volume show ' + new_name, - parse_output=True, - ) - self.assertEqual( - new_name, - cmd_output["name"], - ) - self.assertEqual( - 2, - cmd_output["size"], - ) - self.assertEqual( - 'bbbb', - cmd_output["display_description"], - ) - self.assertEqual( - {'Beta': 'b', 'Gamma': 'c'}, - cmd_output["properties"], - ) - self.assertEqual( - 'true', - cmd_output["bootable"], - ) - - # Test volume unset - raw_output = self.openstack( - 'volume unset ' + '--property Beta ' + new_name, - ) - self.assertOutput('', raw_output) - - cmd_output = self.openstack( - 'volume show ' + new_name, - parse_output=True, - ) - self.assertEqual( - {'Gamma': 'c'}, - cmd_output["properties"], - ) - - def test_volume_create_and_list_and_show_backward_compatibility(self): - """Test backward compatibility of create, list, show""" - name1 = uuid.uuid4().hex - output = self.openstack( - 'volume create ' + '-c display_name -c id ' + '--size 1 ' + name1, - parse_output=True, - ) - self.assertIn('display_name', output) - self.assertEqual(name1, output['display_name']) - self.assertIn('id', output) - volume_id = output['id'] - self.assertIsNotNone(volume_id) - self.assertNotIn('name', output) - self.addCleanup(self.openstack, 'volume delete ' + volume_id) - - self.wait_for_status("volume", name1, "available") - - output = self.openstack( - 'volume list ' + '-c "Display Name"', - parse_output=True, - ) - for each_volume in output: - self.assertIn('Display Name', each_volume) - - output = self.openstack( - 'volume list ' + '-c "Name"', - parse_output=True, - ) - for each_volume in output: - self.assertIn('Name', each_volume) - - output = self.openstack( - 'volume show ' + '-c display_name -c id ' + name1, - parse_output=True, - ) - self.assertIn('display_name', output) - self.assertEqual(name1, output['display_name']) - self.assertIn('id', output) - self.assertNotIn('name', output) diff --git a/openstackclient/tests/functional/volume/v1/test_volume_type.py b/openstackclient/tests/functional/volume/v1/test_volume_type.py deleted file mode 100644 index b48dec642e..0000000000 --- a/openstackclient/tests/functional/volume/v1/test_volume_type.py +++ /dev/null @@ -1,213 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import time -import uuid - -from openstackclient.tests.functional.volume.v1 import common - - -class VolumeTypeTests(common.BaseVolumeTests): - """Functional tests for volume type.""" - - def test_volume_type_create_list(self): - name = uuid.uuid4().hex - cmd_output = self.openstack( - 'volume type create --private ' + name, - parse_output=True, - ) - self.addCleanup( - self.openstack, - 'volume type delete ' + name, - ) - self.assertEqual(name, cmd_output['name']) - - cmd_output = self.openstack( - f'volume type show {name}', - parse_output=True, - ) - self.assertEqual(self.NAME, cmd_output['name']) - - cmd_output = self.openstack('volume type list', parse_output=True) - self.assertIn(self.NAME, [t['Name'] for t in cmd_output]) - - cmd_output = self.openstack( - 'volume type list --default', - parse_output=True, - ) - self.assertEqual(1, len(cmd_output)) - self.assertEqual('lvmdriver-1', cmd_output[0]['Name']) - - def test_volume_type_set_unset_properties(self): - name = uuid.uuid4().hex - cmd_output = self.openstack( - 'volume type create --private ' + name, - parse_output=True, - ) - self.addCleanup(self.openstack, 'volume type delete ' + name) - self.assertEqual(name, cmd_output['name']) - - raw_output = self.openstack( - f'volume type set --property a=b --property c=d {name}' - ) - self.assertEqual("", raw_output) - cmd_output = self.openstack( - f'volume type show {name}', - parse_output=True, - ) - self.assertEqual({'a': 'b', 'c': 'd'}, cmd_output['properties']) - - raw_output = self.openstack(f'volume type unset --property a {name}') - self.assertEqual("", raw_output) - cmd_output = self.openstack( - f'volume type show {name}', - parse_output=True, - ) - self.assertEqual({'c': 'd'}, cmd_output['properties']) - - def test_volume_type_set_unset_multiple_properties(self): - name = uuid.uuid4().hex - cmd_output = self.openstack( - 'volume type create --private ' + name, - parse_output=True, - ) - self.addCleanup(self.openstack, 'volume type delete ' + name) - self.assertEqual(name, cmd_output['name']) - - raw_output = self.openstack( - f'volume type set --property a=b --property c=d {name}' - ) - self.assertEqual("", raw_output) - cmd_output = self.openstack( - f'volume type show {name}', - parse_output=True, - ) - self.assertEqual({'a': 'b', 'c': 'd'}, cmd_output['properties']) - - raw_output = self.openstack( - f'volume type unset --property a --property c {name}' - ) - self.assertEqual("", raw_output) - cmd_output = self.openstack( - f'volume type show {name}', - parse_output=True, - ) - self.assertEqual({}, cmd_output['properties']) - - def test_multi_delete(self): - vol_type1 = uuid.uuid4().hex - vol_type2 = uuid.uuid4().hex - self.openstack(f'volume type create {vol_type1}') - time.sleep(5) - self.openstack(f'volume type create {vol_type2}') - time.sleep(5) - cmd = f'volume type delete {vol_type1} {vol_type2}' - raw_output = self.openstack(cmd) - self.assertOutput('', raw_output) - - # NOTE: Add some basic functional tests with the old format to - # make sure the command works properly, need to change - # these to new test format when beef up all tests for - # volume type commands. - def test_encryption_type(self): - encryption_type = uuid.uuid4().hex - # test create new encryption type - cmd_output = self.openstack( - 'volume type create ' - '--encryption-provider LuksEncryptor ' - '--encryption-cipher aes-xts-plain64 ' - '--encryption-key-size 128 ' - '--encryption-control-location front-end ' + encryption_type - ) - expected = { - 'provider': 'LuksEncryptor', - 'cipher': 'aes-xts-plain64', - 'key_size': 128, - 'control_location': 'front-end', - } - for attr, value in expected.items(): - self.assertEqual(value, cmd_output['encryption'][attr]) - # test show encryption type - cmd_output = self.openstack( - 'volume type show --encryption-type ' + encryption_type, - parse_output=True, - ) - expected = { - 'provider': 'LuksEncryptor', - 'cipher': 'aes-xts-plain64', - 'key_size': 128, - 'control_location': 'front-end', - } - for attr, value in expected.items(): - self.assertEqual(value, cmd_output['encryption'][attr]) - # test list encryption type - cmd_output = self.openstack( - 'volume type list --encryption-type', - parse_output=True, - ) - encryption_output = [ - t['Encryption'] for t in cmd_output if t['Name'] == encryption_type - ][0] - expected = { - 'provider': 'LuksEncryptor', - 'cipher': 'aes-xts-plain64', - 'key_size': 128, - 'control_location': 'front-end', - } - for attr, value in expected.items(): - self.assertEqual(value, encryption_output[attr]) - # test set new encryption type - raw_output = self.openstack( - 'volume type set ' - '--encryption-provider LuksEncryptor ' - '--encryption-cipher aes-xts-plain64 ' - '--encryption-key-size 128 ' - '--encryption-control-location front-end ' + self.NAME - ) - self.assertEqual('', raw_output) - - name = uuid.uuid4().hex - cmd_output = self.openstack( - 'volume type create --private ' + name, - parse_output=True, - ) - self.addCleanup( - self.openstack, - 'volume type delete ' + name, - ) - self.assertEqual(name, cmd_output['name']) - - cmd_output = self.openstack( - 'volume type show --encryption-type ' + name, - parse_output=True, - ) - expected = { - 'provider': 'LuksEncryptor', - 'cipher': 'aes-xts-plain64', - 'key_size': 128, - 'control_location': 'front-end', - } - for attr, value in expected.items(): - self.assertEqual(value, cmd_output['encryption'][attr]) - # test unset encryption type - raw_output = self.openstack( - 'volume type unset --encryption-type ' + name - ) - self.assertEqual('', raw_output) - cmd_output = self.openstack( - 'volume type show --encryption-type ' + name, - parse_output=True, - ) - self.assertEqual({}, cmd_output['encryption']) - # test delete encryption type - raw_output = self.openstack('volume type delete ' + encryption_type) - self.assertEqual('', raw_output) diff --git a/openstackclient/tests/unit/image/v1/fakes.py b/openstackclient/tests/unit/image/v1/fakes.py index 44845fb82d..30503e1f02 100644 --- a/openstackclient/tests/unit/image/v1/fakes.py +++ b/openstackclient/tests/unit/image/v1/fakes.py @@ -20,7 +20,7 @@ from openstack.image.v1 import image from openstackclient.tests.unit import fakes from openstackclient.tests.unit import utils -from openstackclient.tests.unit.volume.v1 import fakes as volume_fakes +from openstackclient.tests.unit.volume.v2 import fakes as volume_fakes class FakeClientMixin: @@ -35,7 +35,7 @@ class TestImagev1(FakeClientMixin, utils.TestCommand): def setUp(self): super().setUp() - self.app.client_manager.volume = volume_fakes.FakeVolumev1Client( + self.app.client_manager.volume = volume_fakes.FakeVolumeClient( endpoint=fakes.AUTH_URL, token=fakes.AUTH_TOKEN, ) diff --git a/openstackclient/tests/unit/volume/v1/__init__.py b/openstackclient/tests/unit/volume/v1/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/openstackclient/tests/unit/volume/v1/fakes.py b/openstackclient/tests/unit/volume/v1/fakes.py deleted file mode 100644 index 9b4dc126de..0000000000 --- a/openstackclient/tests/unit/volume/v1/fakes.py +++ /dev/null @@ -1,615 +0,0 @@ -# Copyright 2013 Nebula Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -import copy -import random -from unittest import mock -import uuid - -from openstack.image.v1 import _proxy as image_v1_proxy - -from openstackclient.tests.unit import fakes -from openstackclient.tests.unit.identity.v2_0 import fakes as identity_fakes -from openstackclient.tests.unit import utils - - -class FakeVolumev1Client: - def __init__(self, **kwargs): - self.volumes = mock.Mock() - self.volumes.resource_class = fakes.FakeResource(None, {}) - self.services = mock.Mock() - self.services.resource_class = fakes.FakeResource(None, {}) - self.extensions = mock.Mock() - self.extensions.resource_class = fakes.FakeResource(None, {}) - self.qos_specs = mock.Mock() - self.qos_specs.resource_class = fakes.FakeResource(None, {}) - self.volume_types = mock.Mock() - self.volume_types.resource_class = fakes.FakeResource(None, {}) - self.volume_encryption_types = mock.Mock() - self.volume_encryption_types.resource_class = fakes.FakeResource( - None, {} - ) - self.transfers = mock.Mock() - self.transfers.resource_class = fakes.FakeResource(None, {}) - self.volume_snapshots = mock.Mock() - self.volume_snapshots.resource_class = fakes.FakeResource(None, {}) - self.backups = mock.Mock() - self.backups.resource_class = fakes.FakeResource(None, {}) - self.restores = mock.Mock() - self.restores.resource_class = fakes.FakeResource(None, {}) - self.auth_token = kwargs['token'] - self.management_url = kwargs['endpoint'] - - -class FakeClientMixin: - def setUp(self): - super().setUp() - - self.app.client_manager.volume = FakeVolumev1Client( - endpoint=fakes.AUTH_URL, - token=fakes.AUTH_TOKEN, - ) - self.volume_client = self.app.client_manager.volume - - -class TestVolumev1( - identity_fakes.FakeClientMixin, - FakeClientMixin, - utils.TestCommand, -): - def setUp(self): - super().setUp() - - # avoid circular imports by defining this manually rather than using - # openstackclient.tests.unit.image.v1.fakes.FakeClientMixin - self.app.client_manager.image = mock.Mock(spec=image_v1_proxy.Proxy) - self.image_client = self.app.client_manager.image - - -def create_one_transfer(attrs=None): - """Create a fake transfer. - - :param Dictionary attrs: - A dictionary with all attributes of Transfer Request - :return: - A FakeResource object with volume_id, name, id. - """ - # Set default attribute - transfer_info = { - 'volume_id': 'volume-id-' + uuid.uuid4().hex, - 'name': 'fake_transfer_name', - 'id': 'id-' + uuid.uuid4().hex, - 'links': 'links-' + uuid.uuid4().hex, - } - - # Overwrite default attributes if there are some attributes set - attrs = attrs or {} - - transfer_info.update(attrs) - - transfer = fakes.FakeResource(None, transfer_info, loaded=True) - - return transfer - - -def create_transfers(attrs=None, count=2): - """Create multiple fake transfers. - - :param Dictionary attrs: - A dictionary with all attributes of transfer - :param Integer count: - The number of transfers to be faked - :return: - A list of FakeResource objects - """ - transfers = [] - for n in range(0, count): - transfers.append(create_one_transfer(attrs)) - - return transfers - - -def get_transfers(transfers=None, count=2): - """Get an iterable MagicMock object with a list of faked transfers. - - If transfers list is provided, then initialize the Mock object with the - list. Otherwise create one. - - :param List transfers: - A list of FakeResource objects faking transfers - :param Integer count: - The number of transfers to be faked - :return - An iterable Mock object with side_effect set to a list of faked - transfers - """ - if transfers is None: - transfers = create_transfers(count) - - return mock.Mock(side_effect=transfers) - - -def create_one_service(attrs=None): - """Create a fake service. - - :param Dictionary attrs: - A dictionary with all attributes of service - :return: - A FakeResource object with host, status, etc. - """ - # Set default attribute - service_info = { - 'host': 'host_test', - 'binary': 'cinder_test', - 'status': 'enabled', - 'disabled_reason': 'LongHoliday-GoldenWeek', - 'zone': 'fake_zone', - 'updated_at': 'fake_date', - 'state': 'fake_state', - } - - # Overwrite default attributes if there are some attributes set - attrs = attrs or {} - - service_info.update(attrs) - - service = fakes.FakeResource(None, service_info, loaded=True) - - return service - - -def create_services(attrs=None, count=2): - """Create multiple fake services. - - :param Dictionary attrs: - A dictionary with all attributes of service - :param Integer count: - The number of services to be faked - :return: - A list of FakeResource objects - """ - services = [] - for n in range(0, count): - services.append(create_one_service(attrs)) - - return services - - -def get_services(services=None, count=2): - """Get an iterable MagicMock object with a list of faked services. - - If services list is provided, then initialize the Mock object with the - list. Otherwise create one. - - :param List services: - A list of FakeResource objects faking services - :param Integer count: - The number of services to be faked - :return - An iterable Mock object with side_effect set to a list of faked - services - """ - if services is None: - services = create_services(count) - - return mock.Mock(side_effect=services) - - -def create_one_qos(attrs=None): - """Create a fake Qos specification. - - :param Dictionary attrs: - A dictionary with all attributes - :return: - A FakeResource object with id, name, consumer, etc. - """ - attrs = attrs or {} - - # Set default attributes. - qos_info = { - "id": 'qos-id-' + uuid.uuid4().hex, - "name": 'qos-name-' + uuid.uuid4().hex, - "consumer": 'front-end', - "specs": {"foo": "bar", "iops": "9001"}, - } - - # Overwrite default attributes. - qos_info.update(attrs) - - qos = fakes.FakeResource(info=copy.deepcopy(qos_info), loaded=True) - return qos - - -def create_one_qos_association(attrs=None): - """Create a fake Qos specification association. - - :param Dictionary attrs: - A dictionary with all attributes - :return: - A FakeResource object with id, name, association_type, etc. - """ - attrs = attrs or {} - - # Set default attributes. - qos_association_info = { - "id": 'type-id-' + uuid.uuid4().hex, - "name": 'type-name-' + uuid.uuid4().hex, - "association_type": 'volume_type', - } - - # Overwrite default attributes. - qos_association_info.update(attrs) - - qos_association = fakes.FakeResource( - info=copy.deepcopy(qos_association_info), loaded=True - ) - return qos_association - - -def create_qoses(attrs=None, count=2): - """Create multiple fake Qos specifications. - - :param Dictionary attrs: - A dictionary with all attributes - :param int count: - The number of Qos specifications to fake - :return: - A list of FakeResource objects faking the Qos specifications - """ - qoses = [] - for i in range(0, count): - qos = create_one_qos(attrs) - qoses.append(qos) - - return qoses - - -def get_qoses(qoses=None, count=2): - """Get an iterable MagicMock object with a list of faked qoses. - - If qoses list is provided, then initialize the Mock object with the - list. Otherwise create one. - - :param List volumes: - A list of FakeResource objects faking qoses - :param Integer count: - The number of qoses to be faked - :return - An iterable Mock object with side_effect set to a list of faked - qoses - """ - if qoses is None: - qoses = create_qoses(count) - - return mock.Mock(side_effect=qoses) - - -def create_one_volume(attrs=None): - """Create a fake volume. - - :param Dictionary attrs: - A dictionary with all attributes of volume - :return: - A FakeResource object with id, name, status, etc. - """ - attrs = attrs or {} - - # Set default attribute - volume_info = { - 'id': 'volume-id' + uuid.uuid4().hex, - 'display_name': 'volume-name' + uuid.uuid4().hex, - 'display_description': 'description' + uuid.uuid4().hex, - 'status': 'available', - 'size': 10, - 'volume_type': random.choice(['fake_lvmdriver-1', 'fake_lvmdriver-2']), - 'bootable': 'true', - 'metadata': { - 'key' + uuid.uuid4().hex: 'val' + uuid.uuid4().hex, - 'key' + uuid.uuid4().hex: 'val' + uuid.uuid4().hex, - 'key' + uuid.uuid4().hex: 'val' + uuid.uuid4().hex, - }, - 'snapshot_id': 'snapshot-id-' + uuid.uuid4().hex, - 'availability_zone': 'zone' + uuid.uuid4().hex, - 'attachments': [ - { - 'device': '/dev/' + uuid.uuid4().hex, - 'server_id': uuid.uuid4().hex, - }, - ], - 'created_at': 'time-' + uuid.uuid4().hex, - } - - # Overwrite default attributes if there are some attributes set - volume_info.update(attrs) - - volume = fakes.FakeResource(None, volume_info, loaded=True) - return volume - - -def create_volumes(attrs=None, count=2): - """Create multiple fake volumes. - - :param Dictionary attrs: - A dictionary with all attributes of volume - :param Integer count: - The number of volumes to be faked - :return: - A list of FakeResource objects - """ - volumes = [] - for n in range(0, count): - volumes.append(create_one_volume(attrs)) - - return volumes - - -def get_volumes(volumes=None, count=2): - """Get an iterable MagicMock object with a list of faked volumes. - - If volumes list is provided, then initialize the Mock object with the - list. Otherwise create one. - - :param List volumes: - A list of FakeResource objects faking volumes - :param Integer count: - The number of volumes to be faked - :return - An iterable Mock object with side_effect set to a list of faked - volumes - """ - if volumes is None: - volumes = create_volumes(count) - - return mock.Mock(side_effect=volumes) - - -def create_one_volume_type(attrs=None, methods=None): - """Create a fake volume type. - - :param Dictionary attrs: - A dictionary with all attributes - :param Dictionary methods: - A dictionary with all methods - :return: - A FakeResource object with id, name, description, etc. - """ - attrs = attrs or {} - methods = methods or {} - - # Set default attributes. - volume_type_info = { - "id": 'type-id-' + uuid.uuid4().hex, - "name": 'type-name-' + uuid.uuid4().hex, - "description": 'type-description-' + uuid.uuid4().hex, - "extra_specs": {"foo": "bar"}, - "is_public": True, - } - - # Overwrite default attributes. - volume_type_info.update(attrs) - - volume_type = fakes.FakeResource( - info=copy.deepcopy(volume_type_info), methods=methods, loaded=True - ) - return volume_type - - -def create_volume_types(attrs=None, count=2): - """Create multiple fake types. - - :param Dictionary attrs: - A dictionary with all attributes - :param int count: - The number of types to fake - :return: - A list of FakeResource objects faking the types - """ - volume_types = [] - for i in range(0, count): - volume_type = create_one_volume_type(attrs) - volume_types.append(volume_type) - - return volume_types - - -def get_volume_types(volume_types=None, count=2): - """Get an iterable MagicMock object with a list of faked types. - - If types list is provided, then initialize the Mock object with the - list. Otherwise create one. - - :param List volume_types: - A list of FakeResource objects faking types - :param Integer count: - The number of types to be faked - :return - An iterable Mock object with side_effect set to a list of faked - types - """ - if volume_types is None: - volume_types = create_volume_types(count) - - return mock.Mock(side_effect=volume_types) - - -def create_one_encryption_volume_type(attrs=None): - """Create a fake encryption volume type. - - :param Dictionary attrs: - A dictionary with all attributes - :return: - A FakeResource object with volume_type_id etc. - """ - attrs = attrs or {} - - # Set default attributes. - encryption_info = { - "volume_type_id": 'type-id-' + uuid.uuid4().hex, - 'provider': 'LuksEncryptor', - 'cipher': None, - 'key_size': None, - 'control_location': 'front-end', - } - - # Overwrite default attributes. - encryption_info.update(attrs) - - encryption_type = fakes.FakeResource( - info=copy.deepcopy(encryption_info), loaded=True - ) - return encryption_type - - -def create_one_snapshot(attrs=None): - """Create a fake snapshot. - - :param Dictionary attrs: - A dictionary with all attributes - :return: - A FakeResource object with id, name, description, etc. - """ - attrs = attrs or {} - - # Set default attributes. - snapshot_info = { - "id": 'snapshot-id-' + uuid.uuid4().hex, - "display_name": 'snapshot-name-' + uuid.uuid4().hex, - "display_description": 'snapshot-description-' + uuid.uuid4().hex, - "size": 10, - "status": "available", - "metadata": {"foo": "bar"}, - "created_at": "2015-06-03T18:49:19.000000", - "volume_id": 'vloume-id-' + uuid.uuid4().hex, - } - - # Overwrite default attributes. - snapshot_info.update(attrs) - - snapshot_method = {'update': None} - - snapshot = fakes.FakeResource( - info=copy.deepcopy(snapshot_info), - methods=copy.deepcopy(snapshot_method), - loaded=True, - ) - return snapshot - - -def create_snapshots(attrs=None, count=2): - """Create multiple fake snapshots. - - :param Dictionary attrs: - A dictionary with all attributes - :param int count: - The number of snapshots to fake - :return: - A list of FakeResource objects faking the snapshots - """ - snapshots = [] - for i in range(0, count): - snapshot = create_one_snapshot(attrs) - snapshots.append(snapshot) - - return snapshots - - -def get_snapshots(snapshots=None, count=2): - """Get an iterable MagicMock object with a list of faked snapshots. - - If snapshots list is provided, then initialize the Mock object with the - list. Otherwise create one. - - :param List volumes: - A list of FakeResource objects faking snapshots - :param Integer count: - The number of snapshots to be faked - :return - An iterable Mock object with side_effect set to a list of faked - snapshots - """ - if snapshots is None: - snapshots = create_snapshots(count) - - return mock.Mock(side_effect=snapshots) - - -def create_one_backup(attrs=None): - """Create a fake backup. - - :param Dictionary attrs: - A dictionary with all attributes - :return: - A FakeResource object with id, name, volume_id, etc. - """ - attrs = attrs or {} - - # Set default attributes. - backup_info = { - "id": 'backup-id-' + uuid.uuid4().hex, - "name": 'backup-name-' + uuid.uuid4().hex, - "volume_id": 'volume-id-' + uuid.uuid4().hex, - "snapshot_id": 'snapshot-id' + uuid.uuid4().hex, - "description": 'description-' + uuid.uuid4().hex, - "object_count": None, - "container": 'container-' + uuid.uuid4().hex, - "size": random.randint(1, 20), - "status": "error", - "availability_zone": 'zone' + uuid.uuid4().hex, - "links": 'links-' + uuid.uuid4().hex, - } - - # Overwrite default attributes. - backup_info.update(attrs) - - backup = fakes.FakeResource(info=copy.deepcopy(backup_info), loaded=True) - return backup - - -def create_backups(attrs=None, count=2): - """Create multiple fake backups. - - :param Dictionary attrs: - A dictionary with all attributes - :param int count: - The number of backups to fake - :return: - A list of FakeResource objects faking the backups - """ - backups = [] - for i in range(0, count): - backup = create_one_backup(attrs) - backups.append(backup) - - return backups - - -def get_backups(backups=None, count=2): - """Get an iterable MagicMock object with a list of faked backups. - - If backups list is provided, then initialize the Mock object with the - list. Otherwise create one. - - :param List volumes: - A list of FakeResource objects faking backups - :param Integer count: - The number of backups to be faked - :return - An iterable Mock object with side_effect set to a list of faked - backups - """ - if backups is None: - backups = create_backups(count) - - return mock.Mock(side_effect=backups) diff --git a/openstackclient/tests/unit/volume/v1/test_qos_specs.py b/openstackclient/tests/unit/volume/v1/test_qos_specs.py deleted file mode 100644 index 004749fa28..0000000000 --- a/openstackclient/tests/unit/volume/v1/test_qos_specs.py +++ /dev/null @@ -1,471 +0,0 @@ -# Copyright 2015 iWeb Technologies Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -import copy -from unittest import mock -from unittest.mock import call - -from osc_lib.cli import format_columns -from osc_lib import exceptions -from osc_lib import utils - -from openstackclient.tests.unit.volume.v1 import fakes as volume_fakes -from openstackclient.volume.v1 import qos_specs - - -class TestQos(volume_fakes.TestVolumev1): - def setUp(self): - super().setUp() - - self.qos_mock = self.volume_client.qos_specs - self.qos_mock.reset_mock() - - self.types_mock = self.volume_client.volume_types - self.types_mock.reset_mock() - - -class TestQosAssociate(TestQos): - volume_type = volume_fakes.create_one_volume_type() - qos_spec = volume_fakes.create_one_qos() - - def setUp(self): - super().setUp() - - self.qos_mock.get.return_value = self.qos_spec - self.types_mock.get.return_value = self.volume_type - # Get the command object to test - self.cmd = qos_specs.AssociateQos(self.app, None) - - def test_qos_associate(self): - arglist = [self.qos_spec.id, self.volume_type.id] - verifylist = [ - ('qos_spec', self.qos_spec.id), - ('volume_type', self.volume_type.id), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - self.qos_mock.associate.assert_called_with( - self.qos_spec.id, self.volume_type.id - ) - self.assertIsNone(result) - - -class TestQosCreate(TestQos): - columns = ('consumer', 'id', 'name', 'properties') - - def setUp(self): - super().setUp() - self.new_qos_spec = volume_fakes.create_one_qos() - self.datalist = ( - self.new_qos_spec.consumer, - self.new_qos_spec.id, - self.new_qos_spec.name, - format_columns.DictColumn(self.new_qos_spec.specs), - ) - self.qos_mock.create.return_value = self.new_qos_spec - # Get the command object to test - self.cmd = qos_specs.CreateQos(self.app, None) - - def test_qos_create_without_properties(self): - arglist = [ - self.new_qos_spec.name, - ] - verifylist = [ - ('name', self.new_qos_spec.name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - - self.qos_mock.create.assert_called_with( - self.new_qos_spec.name, {'consumer': 'both'} - ) - - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) - - def test_qos_create_with_consumer(self): - arglist = [ - '--consumer', - self.new_qos_spec.consumer, - self.new_qos_spec.name, - ] - verifylist = [ - ('consumer', self.new_qos_spec.consumer), - ('name', self.new_qos_spec.name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - - self.qos_mock.create.assert_called_with( - self.new_qos_spec.name, {'consumer': self.new_qos_spec.consumer} - ) - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) - - def test_qos_create_with_properties(self): - arglist = [ - '--consumer', - self.new_qos_spec.consumer, - '--property', - 'foo=bar', - '--property', - 'iops=9001', - self.new_qos_spec.name, - ] - verifylist = [ - ('consumer', self.new_qos_spec.consumer), - ('property', self.new_qos_spec.specs), - ('name', self.new_qos_spec.name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - - self.new_qos_spec.specs.update( - {'consumer': self.new_qos_spec.consumer} - ) - self.qos_mock.create.assert_called_with( - self.new_qos_spec.name, self.new_qos_spec.specs - ) - - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) - - -class TestQosDelete(TestQos): - qos_specs = volume_fakes.create_qoses(count=2) - - def setUp(self): - super().setUp() - - self.qos_mock.get = volume_fakes.get_qoses(self.qos_specs) - # Get the command object to test - self.cmd = qos_specs.DeleteQos(self.app, None) - - def test_qos_delete_with_id(self): - arglist = [self.qos_specs[0].id] - verifylist = [('qos_specs', [self.qos_specs[0].id])] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - self.qos_mock.delete.assert_called_with(self.qos_specs[0].id, False) - self.assertIsNone(result) - - def test_qos_delete_with_name(self): - arglist = [self.qos_specs[0].name] - verifylist = [('qos_specs', [self.qos_specs[0].name])] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - self.qos_mock.delete.assert_called_with(self.qos_specs[0].id, False) - self.assertIsNone(result) - - def test_qos_delete_with_force(self): - arglist = ['--force', self.qos_specs[0].id] - verifylist = [('force', True), ('qos_specs', [self.qos_specs[0].id])] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - self.qos_mock.delete.assert_called_with(self.qos_specs[0].id, True) - self.assertIsNone(result) - - def test_delete_multiple_qoses(self): - arglist = [] - for q in self.qos_specs: - arglist.append(q.id) - verifylist = [ - ('qos_specs', arglist), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - - calls = [] - for q in self.qos_specs: - calls.append(call(q.id, False)) - self.qos_mock.delete.assert_has_calls(calls) - self.assertIsNone(result) - - def test_delete_multiple_qoses_with_exception(self): - arglist = [ - self.qos_specs[0].id, - 'unexist_qos', - ] - verifylist = [ - ('qos_specs', arglist), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - find_mock_result = [self.qos_specs[0], exceptions.CommandError] - with mock.patch.object( - utils, 'find_resource', side_effect=find_mock_result - ) as find_mock: - try: - self.cmd.take_action(parsed_args) - self.fail('CommandError should be raised.') - except exceptions.CommandError as e: - self.assertEqual( - '1 of 2 QoS specifications failed to delete.', str(e) - ) - - find_mock.assert_any_call(self.qos_mock, self.qos_specs[0].id) - find_mock.assert_any_call(self.qos_mock, 'unexist_qos') - - self.assertEqual(2, find_mock.call_count) - self.qos_mock.delete.assert_called_once_with( - self.qos_specs[0].id, False - ) - - -class TestQosDisassociate(TestQos): - volume_type = volume_fakes.create_one_volume_type() - qos_spec = volume_fakes.create_one_qos() - - def setUp(self): - super().setUp() - - self.qos_mock.get.return_value = self.qos_spec - self.types_mock.get.return_value = self.volume_type - # Get the command object to test - self.cmd = qos_specs.DisassociateQos(self.app, None) - - def test_qos_disassociate_with_volume_type(self): - arglist = [ - '--volume-type', - self.volume_type.id, - self.qos_spec.id, - ] - verifylist = [ - ('volume_type', self.volume_type.id), - ('qos_spec', self.qos_spec.id), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - self.qos_mock.disassociate.assert_called_with( - self.qos_spec.id, self.volume_type.id - ) - self.assertIsNone(result) - - def test_qos_disassociate_with_all_volume_types(self): - arglist = [ - '--all', - self.qos_spec.id, - ] - verifylist = [('qos_spec', self.qos_spec.id)] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - self.qos_mock.disassociate_all.assert_called_with(self.qos_spec.id) - self.assertIsNone(result) - - -class TestQosList(TestQos): - qos_specs = volume_fakes.create_qoses(count=2) - qos_association = volume_fakes.create_one_qos_association() - - columns = ( - 'ID', - 'Name', - 'Consumer', - 'Associations', - 'Properties', - ) - data = [] - for q in qos_specs: - data.append( - ( - q.id, - q.name, - q.consumer, - format_columns.ListColumn([qos_association.name]), - format_columns.DictColumn(q.specs), - ) - ) - - def setUp(self): - super().setUp() - - self.qos_mock.list.return_value = self.qos_specs - self.qos_mock.get_associations.return_value = [self.qos_association] - - # Get the command object to test - self.cmd = qos_specs.ListQos(self.app, None) - - def test_qos_list(self): - arglist = [] - verifylist = [] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - self.qos_mock.list.assert_called_with() - - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.data, list(data)) - - def test_qos_list_no_association(self): - self.qos_mock.reset_mock() - self.qos_mock.get_associations.side_effect = [ - [self.qos_association], - exceptions.NotFound("NotFound"), - ] - - arglist = [] - verifylist = [] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - self.qos_mock.list.assert_called_with() - - self.assertEqual(self.columns, columns) - - ex_data = copy.deepcopy(self.data) - ex_data[1] = ( - self.qos_specs[1].id, - self.qos_specs[1].name, - self.qos_specs[1].consumer, - format_columns.ListColumn(None), - format_columns.DictColumn(self.qos_specs[1].specs), - ) - self.assertCountEqual(ex_data, list(data)) - - -class TestQosSet(TestQos): - qos_spec = volume_fakes.create_one_qos() - - def setUp(self): - super().setUp() - - self.qos_mock.get.return_value = self.qos_spec - # Get the command object to test - self.cmd = qos_specs.SetQos(self.app, None) - - def test_qos_set_with_properties_with_id(self): - arglist = [ - '--no-property', - '--property', - 'a=b', - '--property', - 'c=d', - self.qos_spec.id, - ] - new_property = {"a": "b", "c": "d"} - verifylist = [ - ('no_property', True), - ('property', new_property), - ('qos_spec', self.qos_spec.id), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - self.qos_mock.unset_keys.assert_called_with( - self.qos_spec.id, - list(self.qos_spec.specs.keys()), - ) - self.qos_mock.set_keys.assert_called_with( - self.qos_spec.id, - {"a": "b", "c": "d"}, - ) - self.assertIsNone(result) - - -class TestQosShow(TestQos): - qos_spec = volume_fakes.create_one_qos() - qos_association = volume_fakes.create_one_qos_association() - - def setUp(self): - super().setUp() - self.qos_mock.get.return_value = self.qos_spec - self.qos_mock.get_associations.return_value = [self.qos_association] - # Get the command object to test - self.cmd = qos_specs.ShowQos(self.app, None) - - def test_qos_show(self): - arglist = [self.qos_spec.id] - verifylist = [('qos_spec', self.qos_spec.id)] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - self.qos_mock.get.assert_called_with(self.qos_spec.id) - - collist = ('associations', 'consumer', 'id', 'name', 'properties') - self.assertEqual(collist, columns) - datalist = ( - format_columns.ListColumn([self.qos_association.name]), - self.qos_spec.consumer, - self.qos_spec.id, - self.qos_spec.name, - format_columns.DictColumn(self.qos_spec.specs), - ) - self.assertCountEqual(datalist, tuple(data)) - - -class TestQosUnset(TestQos): - qos_spec = volume_fakes.create_one_qos() - - def setUp(self): - super().setUp() - - self.qos_mock.get.return_value = self.qos_spec - # Get the command object to test - self.cmd = qos_specs.UnsetQos(self.app, None) - - def test_qos_unset_with_properties(self): - arglist = [ - '--property', - 'iops', - '--property', - 'foo', - self.qos_spec.id, - ] - verifylist = [ - ('property', ['iops', 'foo']), - ('qos_spec', self.qos_spec.id), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - self.qos_mock.unset_keys.assert_called_with( - self.qos_spec.id, ['iops', 'foo'] - ) - self.assertIsNone(result) - - def test_qos_unset_nothing(self): - arglist = [ - self.qos_spec.id, - ] - - verifylist = [ - ('qos_spec', self.qos_spec.id), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - self.assertIsNone(result) diff --git a/openstackclient/tests/unit/volume/v1/test_service.py b/openstackclient/tests/unit/volume/v1/test_service.py deleted file mode 100644 index 95a21994fb..0000000000 --- a/openstackclient/tests/unit/volume/v1/test_service.py +++ /dev/null @@ -1,295 +0,0 @@ -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -from osc_lib import exceptions - -from openstackclient.tests.unit.volume.v1 import fakes as volume_fakes -from openstackclient.volume.v1 import service - - -class TestService(volume_fakes.TestVolumev1): - def setUp(self): - super().setUp() - - # Get a shortcut to the ServiceManager Mock - self.service_mock = self.volume_client.services - self.service_mock.reset_mock() - - -class TestServiceList(TestService): - # The service to be listed - services = volume_fakes.create_one_service() - - def setUp(self): - super().setUp() - - self.service_mock.list.return_value = [self.services] - - # Get the command object to test - self.cmd = service.ListService(self.app, None) - - def test_service_list(self): - arglist = [ - '--host', - self.services.host, - '--service', - self.services.binary, - ] - verifylist = [ - ('host', self.services.host), - ('service', self.services.binary), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - # In base command class Lister in cliff, abstract method take_action() - # returns a tuple containing the column names and an iterable - # containing the data to be listed. - columns, data = self.cmd.take_action(parsed_args) - - expected_columns = [ - 'Binary', - 'Host', - 'Zone', - 'Status', - 'State', - 'Updated At', - ] - - # confirming if all expected columns are present in the result. - self.assertEqual(expected_columns, columns) - - datalist = ( - ( - self.services.binary, - self.services.host, - self.services.zone, - self.services.status, - self.services.state, - self.services.updated_at, - ), - ) - - # confirming if all expected values are present in the result. - self.assertEqual(datalist, tuple(data)) - - # checking if proper call was made to list services - self.service_mock.list.assert_called_with( - self.services.host, - self.services.binary, - ) - - # checking if prohibited columns are present in output - self.assertNotIn("Disabled Reason", columns) - self.assertNotIn(self.services.disabled_reason, tuple(data)) - - def test_service_list_with_long_option(self): - arglist = [ - '--host', - self.services.host, - '--service', - self.services.binary, - '--long', - ] - verifylist = [ - ('host', self.services.host), - ('service', self.services.binary), - ('long', True), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - # In base command class Lister in cliff, abstract method take_action() - # returns a tuple containing the column names and an iterable - # containing the data to be listed. - columns, data = self.cmd.take_action(parsed_args) - - expected_columns = [ - 'Binary', - 'Host', - 'Zone', - 'Status', - 'State', - 'Updated At', - 'Disabled Reason', - ] - - # confirming if all expected columns are present in the result. - self.assertEqual(expected_columns, columns) - - datalist = ( - ( - self.services.binary, - self.services.host, - self.services.zone, - self.services.status, - self.services.state, - self.services.updated_at, - self.services.disabled_reason, - ), - ) - - # confirming if all expected values are present in the result. - self.assertEqual(datalist, tuple(data)) - - self.service_mock.list.assert_called_with( - self.services.host, - self.services.binary, - ) - - -class TestServiceSet(TestService): - service = volume_fakes.create_one_service() - - def setUp(self): - super().setUp() - - self.service_mock.enable.return_value = self.service - self.service_mock.disable.return_value = self.service - self.service_mock.disable_log_reason.return_value = self.service - - self.cmd = service.SetService(self.app, None) - - def test_service_set_nothing(self): - arglist = [ - self.service.host, - self.service.binary, - ] - verifylist = [ - ('host', self.service.host), - ('service', self.service.binary), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - - self.service_mock.enable.assert_not_called() - self.service_mock.disable.assert_not_called() - self.service_mock.disable_log_reason.assert_not_called() - self.assertIsNone(result) - - def test_service_set_enable(self): - arglist = [ - '--enable', - self.service.host, - self.service.binary, - ] - verifylist = [ - ('enable', True), - ('host', self.service.host), - ('service', self.service.binary), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - self.service_mock.enable.assert_called_with( - self.service.host, self.service.binary - ) - self.service_mock.disable.assert_not_called() - self.service_mock.disable_log_reason.assert_not_called() - self.assertIsNone(result) - - def test_service_set_disable(self): - arglist = [ - '--disable', - self.service.host, - self.service.binary, - ] - verifylist = [ - ('disable', True), - ('host', self.service.host), - ('service', self.service.binary), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - self.service_mock.disable.assert_called_with( - self.service.host, self.service.binary - ) - self.service_mock.enable.assert_not_called() - self.service_mock.disable_log_reason.assert_not_called() - self.assertIsNone(result) - - def test_service_set_disable_with_reason(self): - reason = 'earthquake' - arglist = [ - '--disable', - '--disable-reason', - reason, - self.service.host, - self.service.binary, - ] - verifylist = [ - ('disable', True), - ('disable_reason', reason), - ('host', self.service.host), - ('service', self.service.binary), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - self.service_mock.disable_log_reason.assert_called_with( - self.service.host, self.service.binary, reason - ) - self.assertIsNone(result) - - def test_service_set_only_with_disable_reason(self): - reason = 'earthquake' - arglist = [ - '--disable-reason', - reason, - self.service.host, - self.service.binary, - ] - verifylist = [ - ('disable_reason', reason), - ('host', self.service.host), - ('service', self.service.binary), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - try: - self.cmd.take_action(parsed_args) - self.fail("CommandError should be raised.") - except exceptions.CommandError as e: - self.assertEqual( - "Cannot specify option --disable-reason without " - "--disable specified.", - str(e), - ) - - def test_service_set_enable_with_disable_reason(self): - reason = 'earthquake' - arglist = [ - '--enable', - '--disable-reason', - reason, - self.service.host, - self.service.binary, - ] - verifylist = [ - ('enable', True), - ('disable_reason', reason), - ('host', self.service.host), - ('service', self.service.binary), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - try: - self.cmd.take_action(parsed_args) - self.fail("CommandError should be raised.") - except exceptions.CommandError as e: - self.assertEqual( - "Cannot specify option --disable-reason without " - "--disable specified.", - str(e), - ) diff --git a/openstackclient/tests/unit/volume/v1/test_transfer_request.py b/openstackclient/tests/unit/volume/v1/test_transfer_request.py deleted file mode 100644 index 47d925d4c4..0000000000 --- a/openstackclient/tests/unit/volume/v1/test_transfer_request.py +++ /dev/null @@ -1,380 +0,0 @@ -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -from unittest import mock -from unittest.mock import call - -from osc_lib import exceptions -from osc_lib import utils - -from openstackclient.tests.unit.volume.v1 import fakes as volume_fakes -from openstackclient.volume.v1 import volume_transfer_request - - -class TestTransfer(volume_fakes.TestVolumev1): - def setUp(self): - super().setUp() - - # Get a shortcut to the TransferManager Mock - self.transfer_mock = self.volume_client.transfers - self.transfer_mock.reset_mock() - - # Get a shortcut to the VolumeManager Mock - self.volumes_mock = self.volume_client.volumes - self.volumes_mock.reset_mock() - - -class TestTransferAccept(TestTransfer): - columns = ( - 'id', - 'name', - 'volume_id', - ) - - def setUp(self): - super().setUp() - - self.volume_transfer = volume_fakes.create_one_transfer() - self.data = ( - self.volume_transfer.id, - self.volume_transfer.name, - self.volume_transfer.volume_id, - ) - - self.transfer_mock.get.return_value = self.volume_transfer - self.transfer_mock.accept.return_value = self.volume_transfer - - # Get the command object to test - self.cmd = volume_transfer_request.AcceptTransferRequest( - self.app, None - ) - - def test_transfer_accept(self): - arglist = [ - '--auth-key', - 'key_value', - self.volume_transfer.id, - ] - verifylist = [ - ('transfer_request', self.volume_transfer.id), - ('auth_key', 'key_value'), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - - self.transfer_mock.get.assert_called_once_with( - self.volume_transfer.id, - ) - self.transfer_mock.accept.assert_called_once_with( - self.volume_transfer.id, - 'key_value', - ) - self.assertEqual(self.columns, columns) - self.assertEqual(self.data, data) - - def test_transfer_accept_no_option(self): - arglist = [ - self.volume_transfer.id, - ] - verifylist = [ - ('transfer_request', self.volume_transfer.id), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - self.assertRaises( - exceptions.CommandError, - self.cmd.take_action, - parsed_args, - ) - - -class TestTransferCreate(TestTransfer): - volume = volume_fakes.create_one_volume() - - columns = ( - 'auth_key', - 'created_at', - 'id', - 'name', - 'volume_id', - ) - - def setUp(self): - super().setUp() - - self.volume_transfer = volume_fakes.create_one_transfer( - attrs={ - 'volume_id': self.volume.id, - 'auth_key': 'key', - 'created_at': 'time', - }, - ) - self.data = ( - self.volume_transfer.auth_key, - self.volume_transfer.created_at, - self.volume_transfer.id, - self.volume_transfer.name, - self.volume_transfer.volume_id, - ) - - self.transfer_mock.create.return_value = self.volume_transfer - self.volumes_mock.get.return_value = self.volume - - # Get the command object to test - self.cmd = volume_transfer_request.CreateTransferRequest( - self.app, None - ) - - def test_transfer_create_without_name(self): - arglist = [ - self.volume.id, - ] - verifylist = [ - ('volume', self.volume.id), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - - self.transfer_mock.create.assert_called_once_with(self.volume.id, None) - self.assertEqual(self.columns, columns) - self.assertEqual(self.data, data) - - def test_transfer_create_with_name(self): - arglist = [ - '--name', - self.volume_transfer.name, - self.volume.id, - ] - verifylist = [ - ('name', self.volume_transfer.name), - ('volume', self.volume.id), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - - self.transfer_mock.create.assert_called_once_with( - self.volume.id, - self.volume_transfer.name, - ) - self.assertEqual(self.columns, columns) - self.assertEqual(self.data, data) - - -class TestTransferDelete(TestTransfer): - volume_transfers = volume_fakes.create_transfers(count=2) - - def setUp(self): - super().setUp() - - self.transfer_mock.get = volume_fakes.get_transfers( - self.volume_transfers, - ) - self.transfer_mock.delete.return_value = None - - # Get the command object to mock - self.cmd = volume_transfer_request.DeleteTransferRequest( - self.app, None - ) - - def test_transfer_delete(self): - arglist = [self.volume_transfers[0].id] - verifylist = [("transfer_request", [self.volume_transfers[0].id])] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - self.transfer_mock.delete.assert_called_with( - self.volume_transfers[0].id - ) - self.assertIsNone(result) - - def test_delete_multiple_transfers(self): - arglist = [] - for v in self.volume_transfers: - arglist.append(v.id) - verifylist = [ - ('transfer_request', arglist), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - - calls = [] - for v in self.volume_transfers: - calls.append(call(v.id)) - self.transfer_mock.delete.assert_has_calls(calls) - self.assertIsNone(result) - - def test_delete_multiple_transfers_with_exception(self): - arglist = [ - self.volume_transfers[0].id, - 'unexist_transfer', - ] - verifylist = [ - ('transfer_request', arglist), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - find_mock_result = [self.volume_transfers[0], exceptions.CommandError] - with mock.patch.object( - utils, 'find_resource', side_effect=find_mock_result - ) as find_mock: - try: - self.cmd.take_action(parsed_args) - self.fail('CommandError should be raised.') - except exceptions.CommandError as e: - self.assertEqual( - '1 of 2 volume transfer requests failed to delete', - str(e), - ) - - find_mock.assert_any_call( - self.transfer_mock, self.volume_transfers[0].id - ) - find_mock.assert_any_call(self.transfer_mock, 'unexist_transfer') - - self.assertEqual(2, find_mock.call_count) - self.transfer_mock.delete.assert_called_once_with( - self.volume_transfers[0].id, - ) - - -class TestTransferList(TestTransfer): - # The Transfers to be listed - volume_transfers = volume_fakes.create_one_transfer() - - def setUp(self): - super().setUp() - - self.transfer_mock.list.return_value = [self.volume_transfers] - - # Get the command object to test - self.cmd = volume_transfer_request.ListTransferRequest(self.app, None) - - def test_transfer_list_without_argument(self): - arglist = [] - verifylist = [] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - # In base command class Lister in cliff, abstract method take_action() - # returns a tuple containing the column names and an iterable - # containing the data to be listed. - columns, data = self.cmd.take_action(parsed_args) - - expected_columns = [ - 'ID', - 'Name', - 'Volume', - ] - - # confirming if all expected columns are present in the result. - self.assertEqual(expected_columns, columns) - - datalist = ( - ( - self.volume_transfers.id, - self.volume_transfers.name, - self.volume_transfers.volume_id, - ), - ) - - # confirming if all expected values are present in the result. - self.assertEqual(datalist, tuple(data)) - - # checking if proper call was made to list volume_transfers - self.transfer_mock.list.assert_called_with( - detailed=True, search_opts={'all_tenants': 0} - ) - - def test_transfer_list_with_argument(self): - arglist = ["--all-projects"] - verifylist = [("all_projects", True)] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - # In base command class Lister in cliff, abstract method take_action() - # returns a tuple containing the column names and an iterable - # containing the data to be listed. - columns, data = self.cmd.take_action(parsed_args) - - expected_columns = [ - 'ID', - 'Name', - 'Volume', - ] - - # confirming if all expected columns are present in the result. - self.assertEqual(expected_columns, columns) - - datalist = ( - ( - self.volume_transfers.id, - self.volume_transfers.name, - self.volume_transfers.volume_id, - ), - ) - - # confirming if all expected values are present in the result. - self.assertEqual(datalist, tuple(data)) - - # checking if proper call was made to list volume_transfers - self.transfer_mock.list.assert_called_with( - detailed=True, search_opts={'all_tenants': 1} - ) - - -class TestTransferShow(TestTransfer): - columns = ( - 'created_at', - 'id', - 'name', - 'volume_id', - ) - - def setUp(self): - super().setUp() - - self.volume_transfer = volume_fakes.create_one_transfer( - attrs={'created_at': 'time'} - ) - self.data = ( - self.volume_transfer.created_at, - self.volume_transfer.id, - self.volume_transfer.name, - self.volume_transfer.volume_id, - ) - - self.transfer_mock.get.return_value = self.volume_transfer - - # Get the command object to test - self.cmd = volume_transfer_request.ShowTransferRequest(self.app, None) - - def test_transfer_show(self): - arglist = [ - self.volume_transfer.id, - ] - verifylist = [ - ('transfer_request', self.volume_transfer.id), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - - self.transfer_mock.get.assert_called_once_with(self.volume_transfer.id) - self.assertEqual(self.columns, columns) - self.assertEqual(self.data, data) diff --git a/openstackclient/tests/unit/volume/v1/test_type.py b/openstackclient/tests/unit/volume/v1/test_type.py deleted file mode 100644 index bc15bcc4d3..0000000000 --- a/openstackclient/tests/unit/volume/v1/test_type.py +++ /dev/null @@ -1,633 +0,0 @@ -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -from unittest import mock -from unittest.mock import call - -from osc_lib.cli import format_columns -from osc_lib import exceptions -from osc_lib import utils - -from openstackclient.tests.unit import utils as tests_utils -from openstackclient.tests.unit.volume.v1 import fakes as volume_fakes -from openstackclient.volume.v1 import volume_type - - -class TestType(volume_fakes.TestVolumev1): - def setUp(self): - super().setUp() - - self.types_mock = self.volume_client.volume_types - self.types_mock.reset_mock() - - self.encryption_types_mock = self.volume_client.volume_encryption_types - self.encryption_types_mock.reset_mock() - - -class TestTypeCreate(TestType): - columns = ( - 'description', - 'id', - 'is_public', - 'name', - ) - - def setUp(self): - super().setUp() - - self.new_volume_type = volume_fakes.create_one_volume_type( - methods={'set_keys': {'myprop': 'myvalue'}}, - ) - self.data = ( - self.new_volume_type.description, - self.new_volume_type.id, - True, - self.new_volume_type.name, - ) - - self.types_mock.create.return_value = self.new_volume_type - # Get the command object to test - self.cmd = volume_type.CreateVolumeType(self.app, None) - - def test_type_create(self): - arglist = [ - self.new_volume_type.name, - ] - verifylist = [ - ("name", self.new_volume_type.name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - self.types_mock.create.assert_called_with( - self.new_volume_type.name, - ) - - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.data, data) - - def test_type_create_with_encryption(self): - encryption_info = { - 'provider': 'LuksEncryptor', - 'cipher': 'aes-xts-plain64', - 'key_size': '128', - 'control_location': 'front-end', - } - encryption_type = volume_fakes.create_one_encryption_volume_type( - attrs=encryption_info, - ) - self.new_volume_type = volume_fakes.create_one_volume_type( - attrs={'encryption': encryption_info}, - ) - self.types_mock.create.return_value = self.new_volume_type - self.encryption_types_mock.create.return_value = encryption_type - encryption_columns = ( - 'description', - 'encryption', - 'id', - 'is_public', - 'name', - ) - encryption_data = ( - self.new_volume_type.description, - format_columns.DictColumn(encryption_info), - self.new_volume_type.id, - True, - self.new_volume_type.name, - ) - arglist = [ - '--encryption-provider', - 'LuksEncryptor', - '--encryption-cipher', - 'aes-xts-plain64', - '--encryption-key-size', - '128', - '--encryption-control-location', - 'front-end', - self.new_volume_type.name, - ] - verifylist = [ - ('encryption_provider', 'LuksEncryptor'), - ('encryption_cipher', 'aes-xts-plain64'), - ('encryption_key_size', 128), - ('encryption_control_location', 'front-end'), - ('name', self.new_volume_type.name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - self.types_mock.create.assert_called_with( - self.new_volume_type.name, - ) - body = { - 'provider': 'LuksEncryptor', - 'cipher': 'aes-xts-plain64', - 'key_size': 128, - 'control_location': 'front-end', - } - self.encryption_types_mock.create.assert_called_with( - self.new_volume_type, - body, - ) - self.assertEqual(encryption_columns, columns) - self.assertCountEqual(encryption_data, data) - - -class TestTypeDelete(TestType): - volume_types = volume_fakes.create_volume_types(count=2) - - def setUp(self): - super().setUp() - - self.types_mock.get = volume_fakes.get_volume_types(self.volume_types) - self.types_mock.delete.return_value = None - - # Get the command object to mock - self.cmd = volume_type.DeleteVolumeType(self.app, None) - - def test_type_delete(self): - arglist = [self.volume_types[0].id] - verifylist = [("volume_types", [self.volume_types[0].id])] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - self.types_mock.delete.assert_called_with(self.volume_types[0]) - self.assertIsNone(result) - - def test_delete_multiple_types(self): - arglist = [] - for t in self.volume_types: - arglist.append(t.id) - verifylist = [ - ('volume_types', arglist), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - - calls = [] - for t in self.volume_types: - calls.append(call(t)) - self.types_mock.delete.assert_has_calls(calls) - self.assertIsNone(result) - - def test_delete_multiple_types_with_exception(self): - arglist = [ - self.volume_types[0].id, - 'unexist_type', - ] - verifylist = [ - ('volume_types', arglist), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - find_mock_result = [self.volume_types[0], exceptions.CommandError] - with mock.patch.object( - utils, 'find_resource', side_effect=find_mock_result - ) as find_mock: - try: - self.cmd.take_action(parsed_args) - self.fail('CommandError should be raised.') - except exceptions.CommandError as e: - self.assertEqual( - '1 of 2 volume types failed to delete.', str(e) - ) - - find_mock.assert_any_call(self.types_mock, self.volume_types[0].id) - find_mock.assert_any_call(self.types_mock, 'unexist_type') - - self.assertEqual(2, find_mock.call_count) - self.types_mock.delete.assert_called_once_with( - self.volume_types[0] - ) - - -class TestTypeList(TestType): - volume_types = volume_fakes.create_volume_types() - - columns = [ - "ID", - "Name", - "Is Public", - ] - columns_long = ["ID", "Name", "Is Public", "Properties"] - - data = [] - for t in volume_types: - data.append( - ( - t.id, - t.name, - t.is_public, - ) - ) - data_long = [] - for t in volume_types: - data_long.append( - ( - t.id, - t.name, - t.is_public, - format_columns.DictColumn(t.extra_specs), - ) - ) - - def setUp(self): - super().setUp() - - self.types_mock.list.return_value = self.volume_types - self.encryption_types_mock.create.return_value = None - self.encryption_types_mock.update.return_value = None - # get the command to test - self.cmd = volume_type.ListVolumeType(self.app, None) - - def test_type_list_without_options(self): - arglist = [] - verifylist = [ - ("long", False), - ("encryption_type", False), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - self.types_mock.list.assert_called_once_with() - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.data, list(data)) - - def test_type_list_with_options(self): - arglist = [ - "--long", - ] - verifylist = [ - ("long", True), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - self.types_mock.list.assert_called_once_with() - self.assertEqual(self.columns_long, columns) - self.assertCountEqual(self.data_long, list(data)) - - def test_type_list_with_encryption(self): - encryption_type = volume_fakes.create_one_encryption_volume_type( - attrs={'volume_type_id': self.volume_types[0].id}, - ) - encryption_info = { - 'provider': 'LuksEncryptor', - 'cipher': None, - 'key_size': None, - 'control_location': 'front-end', - } - encryption_columns = self.columns + [ - "Encryption", - ] - encryption_data = [] - encryption_data.append( - ( - self.volume_types[0].id, - self.volume_types[0].name, - self.volume_types[0].is_public, - volume_type.EncryptionInfoColumn( - self.volume_types[0].id, - {self.volume_types[0].id: encryption_info}, - ), - ) - ) - encryption_data.append( - ( - self.volume_types[1].id, - self.volume_types[1].name, - self.volume_types[1].is_public, - volume_type.EncryptionInfoColumn(self.volume_types[1].id, {}), - ) - ) - - self.encryption_types_mock.list.return_value = [encryption_type] - arglist = [ - "--encryption-type", - ] - verifylist = [ - ("encryption_type", True), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - self.encryption_types_mock.list.assert_called_once_with() - self.types_mock.list.assert_called_once_with() - self.assertEqual(encryption_columns, columns) - self.assertCountEqual(encryption_data, list(data)) - - -class TestTypeSet(TestType): - volume_type = volume_fakes.create_one_volume_type( - methods={'set_keys': None}, - ) - - def setUp(self): - super().setUp() - - self.types_mock.get.return_value = self.volume_type - - # Get the command object to test - self.cmd = volume_type.SetVolumeType(self.app, None) - - def test_type_set_nothing(self): - arglist = [ - self.volume_type.id, - ] - verifylist = [ - ('volume_type', self.volume_type.id), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - self.assertIsNone(result) - - def test_type_set_property(self): - arglist = [ - '--property', - 'myprop=myvalue', - self.volume_type.id, - ] - verifylist = [ - ('property', {'myprop': 'myvalue'}), - ('volume_type', self.volume_type.id), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - self.volume_type.set_keys.assert_called_once_with( - {'myprop': 'myvalue'} - ) - self.assertIsNone(result) - - def test_type_set_new_encryption(self): - arglist = [ - '--encryption-provider', - 'LuksEncryptor', - '--encryption-cipher', - 'aes-xts-plain64', - '--encryption-key-size', - '128', - '--encryption-control-location', - 'front-end', - self.volume_type.id, - ] - verifylist = [ - ('encryption_provider', 'LuksEncryptor'), - ('encryption_cipher', 'aes-xts-plain64'), - ('encryption_key_size', 128), - ('encryption_control_location', 'front-end'), - ('volume_type', self.volume_type.id), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - body = { - 'provider': 'LuksEncryptor', - 'cipher': 'aes-xts-plain64', - 'key_size': 128, - 'control_location': 'front-end', - } - self.encryption_types_mock.create.assert_called_with( - self.volume_type, - body, - ) - self.assertIsNone(result) - - def test_type_set_new_encryption_without_provider(self): - arglist = [ - '--encryption-cipher', - 'aes-xts-plain64', - '--encryption-key-size', - '128', - '--encryption-control-location', - 'front-end', - self.volume_type.id, - ] - verifylist = [ - ('encryption_cipher', 'aes-xts-plain64'), - ('encryption_key_size', 128), - ('encryption_control_location', 'front-end'), - ('volume_type', self.volume_type.id), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - try: - self.cmd.take_action(parsed_args) - self.fail('CommandError should be raised.') - except exceptions.CommandError as e: - self.assertEqual( - "Command Failed: One or more of the operations failed", - str(e), - ) - self.encryption_types_mock.create.assert_not_called() - self.encryption_types_mock.update.assert_not_called() - - -class TestTypeShow(TestType): - columns = ( - 'description', - 'id', - 'is_public', - 'name', - 'properties', - ) - - def setUp(self): - super().setUp() - - self.volume_type = volume_fakes.create_one_volume_type() - self.data = ( - self.volume_type.description, - self.volume_type.id, - True, - self.volume_type.name, - format_columns.DictColumn(self.volume_type.extra_specs), - ) - - self.types_mock.get.return_value = self.volume_type - - # Get the command object to test - self.cmd = volume_type.ShowVolumeType(self.app, None) - - def test_type_show(self): - arglist = [self.volume_type.id] - verifylist = [ - ("volume_type", self.volume_type.id), - ("encryption_type", False), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - self.types_mock.get.assert_called_with(self.volume_type.id) - - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.data, data) - - def test_type_show_with_encryption(self): - encryption_type = volume_fakes.create_one_encryption_volume_type() - encryption_info = { - 'provider': 'LuksEncryptor', - 'cipher': None, - 'key_size': None, - 'control_location': 'front-end', - } - self.volume_type = volume_fakes.create_one_volume_type( - attrs={'encryption': encryption_info}, - ) - self.types_mock.get.return_value = self.volume_type - self.encryption_types_mock.get.return_value = encryption_type - encryption_columns = ( - 'description', - 'encryption', - 'id', - 'is_public', - 'name', - 'properties', - ) - encryption_data = ( - self.volume_type.description, - format_columns.DictColumn(encryption_info), - self.volume_type.id, - True, - self.volume_type.name, - format_columns.DictColumn(self.volume_type.extra_specs), - ) - arglist = ['--encryption-type', self.volume_type.id] - verifylist = [ - ('encryption_type', True), - ("volume_type", self.volume_type.id), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - self.types_mock.get.assert_called_with(self.volume_type.id) - self.encryption_types_mock.get.assert_called_with(self.volume_type.id) - self.assertEqual(encryption_columns, columns) - self.assertCountEqual(encryption_data, data) - - -class TestTypeUnset(TestType): - volume_type = volume_fakes.create_one_volume_type( - methods={'unset_keys': None}, - ) - - def setUp(self): - super().setUp() - - self.types_mock.get.return_value = self.volume_type - - # Get the command object to test - self.cmd = volume_type.UnsetVolumeType(self.app, None) - - def test_type_unset_property(self): - arglist = [ - '--property', - 'property', - '--property', - 'multi_property', - self.volume_type.id, - ] - verifylist = [ - ('encryption_type', False), - ('property', ['property', 'multi_property']), - ('volume_type', self.volume_type.id), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - self.volume_type.unset_keys.assert_called_once_with( - ['property', 'multi_property'] - ) - self.encryption_types_mock.delete.assert_not_called() - self.assertIsNone(result) - - def test_type_unset_failed_with_missing_volume_type_argument(self): - arglist = [ - '--property', - 'property', - '--property', - 'multi_property', - ] - verifylist = [ - ('property', ['property', 'multi_property']), - ] - - self.assertRaises( - tests_utils.ParserException, - self.check_parser, - self.cmd, - arglist, - verifylist, - ) - - def test_type_unset_nothing(self): - arglist = [ - self.volume_type.id, - ] - verifylist = [ - ('volume_type', self.volume_type.id), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - self.assertIsNone(result) - - def test_type_unset_encryption_type(self): - arglist = [ - '--encryption-type', - self.volume_type.id, - ] - verifylist = [ - ('encryption_type', True), - ('volume_type', self.volume_type.id), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - self.encryption_types_mock.delete.assert_called_with(self.volume_type) - self.assertIsNone(result) - - -class TestColumns(TestType): - def test_encryption_info_column_with_info(self): - fake_volume_type = volume_fakes.create_one_volume_type() - type_id = fake_volume_type.id - - encryption_info = { - 'provider': 'LuksEncryptor', - 'cipher': None, - 'key_size': None, - 'control_location': 'front-end', - } - col = volume_type.EncryptionInfoColumn( - type_id, {type_id: encryption_info} - ) - self.assertEqual( - utils.format_dict(encryption_info), col.human_readable() - ) - self.assertEqual(encryption_info, col.machine_readable()) - - def test_encryption_info_column_without_info(self): - fake_volume_type = volume_fakes.create_one_volume_type() - type_id = fake_volume_type.id - - col = volume_type.EncryptionInfoColumn(type_id, {}) - self.assertEqual('-', col.human_readable()) - self.assertIsNone(col.machine_readable()) diff --git a/openstackclient/tests/unit/volume/v1/test_volume.py b/openstackclient/tests/unit/volume/v1/test_volume.py deleted file mode 100644 index 0f0d532dde..0000000000 --- a/openstackclient/tests/unit/volume/v1/test_volume.py +++ /dev/null @@ -1,1447 +0,0 @@ -# Copyright 2013 Nebula Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from unittest import mock -from unittest.mock import call - -from osc_lib.cli import format_columns -from osc_lib import exceptions -from osc_lib import utils - -from openstackclient.tests.unit.identity.v2_0 import fakes as identity_fakes -from openstackclient.tests.unit.image.v1 import fakes as image_fakes -from openstackclient.tests.unit import utils as test_utils -from openstackclient.tests.unit.volume.v1 import fakes as volume_fakes -from openstackclient.volume.v1 import volume - - -class TestVolume(volume_fakes.TestVolumev1): - def setUp(self): - super().setUp() - - # Get a shortcut to the VolumeManager Mock - self.volumes_mock = self.volume_client.volumes - self.volumes_mock.reset_mock() - - # Get a shortcut to the TenantManager Mock - self.projects_mock = self.identity_client.tenants - self.projects_mock.reset_mock() - - # Get a shortcut to the UserManager Mock - self.users_mock = self.identity_client.users - self.users_mock.reset_mock() - - def setup_volumes_mock(self, count): - volumes = volume_fakes.create_volumes(count=count) - - self.volumes_mock.get = volume_fakes.get_volumes(volumes, 0) - return volumes - - -class TestVolumeCreate(TestVolume): - project = identity_fakes.FakeProject.create_one_project() - user = identity_fakes.FakeUser.create_one_user() - - columns = ( - 'attachments', - 'availability_zone', - 'bootable', - 'created_at', - 'display_description', - 'id', - 'name', - 'properties', - 'size', - 'snapshot_id', - 'status', - 'type', - ) - - def setUp(self): - super().setUp() - self.new_volume = volume_fakes.create_one_volume() - self.datalist = ( - self.new_volume.attachments, - self.new_volume.availability_zone, - self.new_volume.bootable, - self.new_volume.created_at, - self.new_volume.display_description, - self.new_volume.id, - self.new_volume.display_name, - format_columns.DictColumn(self.new_volume.metadata), - self.new_volume.size, - self.new_volume.snapshot_id, - self.new_volume.status, - self.new_volume.volume_type, - ) - self.volumes_mock.create.return_value = self.new_volume - - # Get the command object to test - self.cmd = volume.CreateVolume(self.app, None) - - def test_volume_create_min_options(self): - arglist = [ - '--size', - str(self.new_volume.size), - self.new_volume.display_name, - ] - verifylist = [ - ('size', self.new_volume.size), - ('name', self.new_volume.display_name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - # In base command class ShowOne in cliff, abstract method take_action() - # returns a two-part tuple with a tuple of column names and a tuple of - # data to be shown. - columns, data = self.cmd.take_action(parsed_args) - - # VolumeManager.create(size, snapshot_id=, source_volid=, - # display_name=, display_description=, - # volume_type=, user_id=, - # project_id=, availability_zone=, - # metadata=, imageRef=) - self.volumes_mock.create.assert_called_with( - self.new_volume.size, - None, - None, - self.new_volume.display_name, - None, - None, - None, - None, - None, - None, - None, - ) - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) - - def test_volume_create_options(self): - arglist = [ - '--size', - str(self.new_volume.size), - '--description', - self.new_volume.display_description, - '--type', - self.new_volume.volume_type, - '--availability-zone', - self.new_volume.availability_zone, - self.new_volume.display_name, - ] - verifylist = [ - ('size', self.new_volume.size), - ('description', self.new_volume.display_description), - ('type', self.new_volume.volume_type), - ('availability_zone', self.new_volume.availability_zone), - ('name', self.new_volume.display_name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - # In base command class ShowOne in cliff, abstract method take_action() - # returns a two-part tuple with a tuple of column names and a tuple of - # data to be shown. - columns, data = self.cmd.take_action(parsed_args) - - # VolumeManager.create(size, snapshot_id=, source_volid=, - # display_name=, display_description=, - # volume_type=, user_id=, - # project_id=, availability_zone=, - # metadata=, imageRef=) - self.volumes_mock.create.assert_called_with( - self.new_volume.size, - None, - None, - self.new_volume.display_name, - self.new_volume.display_description, - self.new_volume.volume_type, - None, - None, - self.new_volume.availability_zone, - None, - None, - ) - - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) - - def test_volume_create_user_project_id(self): - # Return a project - self.projects_mock.get.return_value = self.project - # Return a user - self.users_mock.get.return_value = self.user - - arglist = [ - '--size', - str(self.new_volume.size), - '--project', - self.project.id, - '--user', - self.user.id, - self.new_volume.display_name, - ] - verifylist = [ - ('size', self.new_volume.size), - ('project', self.project.id), - ('user', self.user.id), - ('name', self.new_volume.display_name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - # In base command class ShowOne in cliff, abstract method take_action() - # returns a two-part tuple with a tuple of column names and a tuple of - # data to be shown. - columns, data = self.cmd.take_action(parsed_args) - - # VolumeManager.create(size, snapshot_id=, source_volid=, - # display_name=, display_description=, - # volume_type=, user_id=, - # project_id=, availability_zone=, - # metadata=, imageRef=) - self.volumes_mock.create.assert_called_with( - self.new_volume.size, - None, - None, - self.new_volume.display_name, - None, - None, - self.user.id, - self.project.id, - None, - None, - None, - ) - - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) - - def test_volume_create_user_project_name(self): - # Return a project - self.projects_mock.get.return_value = self.project - # Return a user - self.users_mock.get.return_value = self.user - - arglist = [ - '--size', - str(self.new_volume.size), - '--project', - self.project.name, - '--user', - self.user.name, - self.new_volume.display_name, - ] - verifylist = [ - ('size', self.new_volume.size), - ('project', self.project.name), - ('user', self.user.name), - ('name', self.new_volume.display_name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - # In base command class ShowOne in cliff, abstract method take_action() - # returns a two-part tuple with a tuple of column names and a tuple of - # data to be shown. - columns, data = self.cmd.take_action(parsed_args) - - # VolumeManager.create(size, snapshot_id=, source_volid=, - # display_name=, display_description=, - # volume_type=, user_id=, - # project_id=, availability_zone=, - # metadata=, imageRef=) - self.volumes_mock.create.assert_called_with( - self.new_volume.size, - None, - None, - self.new_volume.display_name, - None, - None, - self.user.id, - self.project.id, - None, - None, - None, - ) - - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) - - def test_volume_create_properties(self): - arglist = [ - '--property', - 'Alpha=a', - '--property', - 'Beta=b', - '--size', - str(self.new_volume.size), - self.new_volume.display_name, - ] - verifylist = [ - ('property', {'Alpha': 'a', 'Beta': 'b'}), - ('size', self.new_volume.size), - ('name', self.new_volume.display_name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - # In base command class ShowOne in cliff, abstract method take_action() - # returns a two-part tuple with a tuple of column names and a tuple of - # data to be shown. - columns, data = self.cmd.take_action(parsed_args) - - # VolumeManager.create(size, snapshot_id=, source_volid=, - # display_name=, display_description=, - # volume_type=, user_id=, - # project_id=, availability_zone=, - # metadata=, imageRef=) - self.volumes_mock.create.assert_called_with( - self.new_volume.size, - None, - None, - self.new_volume.display_name, - None, - None, - None, - None, - None, - {'Alpha': 'a', 'Beta': 'b'}, - None, - ) - - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) - - def test_volume_create_image_id(self): - image = image_fakes.create_one_image() - self.image_client.find_image.return_value = image - - arglist = [ - '--image', - image.id, - '--size', - str(self.new_volume.size), - self.new_volume.display_name, - ] - verifylist = [ - ('image', image.id), - ('size', self.new_volume.size), - ('name', self.new_volume.display_name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - # In base command class ShowOne in cliff, abstract method take_action() - # returns a two-part tuple with a tuple of column names and a tuple of - # data to be shown. - columns, data = self.cmd.take_action(parsed_args) - - # VolumeManager.create(size, snapshot_id=, source_volid=, - # display_name=, display_description=, - # volume_type=, user_id=, - # project_id=, availability_zone=, - # metadata=, imageRef=) - self.volumes_mock.create.assert_called_with( - self.new_volume.size, - None, - None, - self.new_volume.display_name, - None, - None, - None, - None, - None, - None, - image.id, - ) - - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) - - def test_volume_create_image_name(self): - image = image_fakes.create_one_image() - self.image_client.find_image.return_value = image - - arglist = [ - '--image', - image.name, - '--size', - str(self.new_volume.size), - self.new_volume.display_name, - ] - verifylist = [ - ('image', image.name), - ('size', self.new_volume.size), - ('name', self.new_volume.display_name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - # In base command class ShowOne in cliff, abstract method take_action() - # returns a two-part tuple with a tuple of column names and a tuple of - # data to be shown. - columns, data = self.cmd.take_action(parsed_args) - - # VolumeManager.create(size, snapshot_id=, source_volid=, - # display_name=, display_description=, - # volume_type=, user_id=, - # project_id=, availability_zone=, - # metadata=, imageRef=) - self.volumes_mock.create.assert_called_with( - self.new_volume.size, - None, - None, - self.new_volume.display_name, - None, - None, - None, - None, - None, - None, - image.id, - ) - - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) - - def test_volume_create_with_source(self): - self.volumes_mock.get.return_value = self.new_volume - arglist = [ - '--source', - self.new_volume.id, - self.new_volume.display_name, - ] - verifylist = [ - ('source', self.new_volume.id), - ('name', self.new_volume.display_name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - - self.volumes_mock.create.assert_called_with( - None, - None, - self.new_volume.id, - self.new_volume.display_name, - None, - None, - None, - None, - None, - None, - None, - ) - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) - - @mock.patch.object(utils, 'wait_for_status', return_value=True) - def test_volume_create_with_bootable_and_readonly(self, mock_wait): - arglist = [ - '--bootable', - '--read-only', - '--size', - str(self.new_volume.size), - self.new_volume.display_name, - ] - verifylist = [ - ('bootable', True), - ('non_bootable', False), - ('read_only', True), - ('read_write', False), - ('size', self.new_volume.size), - ('name', self.new_volume.display_name), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - - self.volumes_mock.create.assert_called_with( - self.new_volume.size, - None, - None, - self.new_volume.display_name, - None, - None, - None, - None, - None, - None, - None, - ) - - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) - self.volumes_mock.set_bootable.assert_called_with( - self.new_volume.id, True - ) - self.volumes_mock.update_readonly_flag.assert_called_with( - self.new_volume.id, True - ) - - @mock.patch.object(utils, 'wait_for_status', return_value=True) - def test_volume_create_with_nonbootable_and_readwrite(self, mock_wait): - arglist = [ - '--non-bootable', - '--read-write', - '--size', - str(self.new_volume.size), - self.new_volume.display_name, - ] - verifylist = [ - ('bootable', False), - ('non_bootable', True), - ('read_only', False), - ('read_write', True), - ('size', self.new_volume.size), - ('name', self.new_volume.display_name), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - - self.volumes_mock.create.assert_called_with( - self.new_volume.size, - None, - None, - self.new_volume.display_name, - None, - None, - None, - None, - None, - None, - None, - ) - - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) - self.volumes_mock.set_bootable.assert_called_with( - self.new_volume.id, False - ) - self.volumes_mock.update_readonly_flag.assert_called_with( - self.new_volume.id, False - ) - - @mock.patch.object(volume.LOG, 'error') - @mock.patch.object(utils, 'wait_for_status', return_value=True) - def test_volume_create_with_bootable_and_readonly_fail( - self, mock_wait, mock_error - ): - self.volumes_mock.set_bootable.side_effect = exceptions.CommandError() - - self.volumes_mock.update_readonly_flag.side_effect = ( - exceptions.CommandError() - ) - - arglist = [ - '--bootable', - '--read-only', - '--size', - str(self.new_volume.size), - self.new_volume.display_name, - ] - verifylist = [ - ('bootable', True), - ('non_bootable', False), - ('read_only', True), - ('read_write', False), - ('size', self.new_volume.size), - ('name', self.new_volume.display_name), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - - self.volumes_mock.create.assert_called_with( - self.new_volume.size, - None, - None, - self.new_volume.display_name, - None, - None, - None, - None, - None, - None, - None, - ) - - self.assertEqual(2, mock_error.call_count) - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) - self.volumes_mock.set_bootable.assert_called_with( - self.new_volume.id, True - ) - self.volumes_mock.update_readonly_flag.assert_called_with( - self.new_volume.id, True - ) - - @mock.patch.object(volume.LOG, 'error') - @mock.patch.object(utils, 'wait_for_status', return_value=False) - def test_volume_create_non_available_with_readonly( - self, mock_wait, mock_error - ): - arglist = [ - '--non-bootable', - '--read-only', - '--size', - str(self.new_volume.size), - self.new_volume.display_name, - ] - verifylist = [ - ('bootable', False), - ('non_bootable', True), - ('read_only', True), - ('read_write', False), - ('size', self.new_volume.size), - ('name', self.new_volume.display_name), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - - self.volumes_mock.create.assert_called_with( - self.new_volume.size, - None, - None, - self.new_volume.display_name, - None, - None, - None, - None, - None, - None, - None, - ) - - self.assertEqual(2, mock_error.call_count) - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) - - def test_volume_create_without_size(self): - arglist = [ - self.new_volume.display_name, - ] - verifylist = [ - ('name', self.new_volume.display_name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - self.assertRaises( - exceptions.CommandError, self.cmd.take_action, parsed_args - ) - - def test_volume_create_with_multi_source(self): - arglist = [ - '--image', - 'source_image', - '--source', - 'source_volume', - '--snapshot', - 'source_snapshot', - '--size', - str(self.new_volume.size), - self.new_volume.display_name, - ] - verifylist = [ - ('image', 'source_image'), - ('source', 'source_volume'), - ('snapshot', 'source_snapshot'), - ('size', self.new_volume.size), - ('name', self.new_volume.display_name), - ] - - self.assertRaises( - test_utils.ParserException, - self.check_parser, - self.cmd, - arglist, - verifylist, - ) - - def test_volume_create_backward_compatibility(self): - arglist = [ - '-c', - 'display_name', - '--size', - str(self.new_volume.size), - self.new_volume.display_name, - ] - verifylist = [ - ('columns', ['display_name']), - ('size', self.new_volume.size), - ('name', self.new_volume.display_name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - - self.volumes_mock.create.assert_called_with( - self.new_volume.size, - None, - None, - self.new_volume.display_name, - None, - None, - None, - None, - None, - None, - None, - ) - self.assertIn('display_name', columns) - self.assertNotIn('name', columns) - self.assertIn(self.new_volume.display_name, data) - - -class TestVolumeDelete(TestVolume): - def setUp(self): - super().setUp() - - self.volumes_mock.delete.return_value = None - - # Get the command object to mock - self.cmd = volume.DeleteVolume(self.app, None) - - def test_volume_delete_one_volume(self): - volumes = self.setup_volumes_mock(count=1) - - arglist = [volumes[0].id] - verifylist = [ - ("force", False), - ("volumes", [volumes[0].id]), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - self.volumes_mock.delete.assert_called_once_with(volumes[0].id) - self.assertIsNone(result) - - def test_volume_delete_multi_volumes(self): - volumes = self.setup_volumes_mock(count=3) - - arglist = [v.id for v in volumes] - verifylist = [ - ('force', False), - ('volumes', arglist), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - calls = [call(v.id) for v in volumes] - self.volumes_mock.delete.assert_has_calls(calls) - self.assertIsNone(result) - - def test_volume_delete_multi_volumes_with_exception(self): - volumes = self.setup_volumes_mock(count=2) - - arglist = [ - volumes[0].id, - 'unexist_volume', - ] - verifylist = [ - ('force', False), - ('volumes', arglist), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - find_mock_result = [volumes[0], exceptions.CommandError] - with mock.patch.object( - utils, 'find_resource', side_effect=find_mock_result - ) as find_mock: - try: - self.cmd.take_action(parsed_args) - self.fail('CommandError should be raised.') - except exceptions.CommandError as e: - self.assertEqual('1 of 2 volumes failed to delete.', str(e)) - - find_mock.assert_any_call(self.volumes_mock, volumes[0].id) - find_mock.assert_any_call(self.volumes_mock, 'unexist_volume') - - self.assertEqual(2, find_mock.call_count) - self.volumes_mock.delete.assert_called_once_with(volumes[0].id) - - def test_volume_delete_with_force(self): - volumes = self.setup_volumes_mock(count=1) - - arglist = [ - '--force', - volumes[0].id, - ] - verifylist = [ - ('force', True), - ('volumes', [volumes[0].id]), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - self.volumes_mock.force_delete.assert_called_once_with(volumes[0].id) - self.assertIsNone(result) - - -class TestVolumeList(TestVolume): - _volume = volume_fakes.create_one_volume() - columns = ( - 'ID', - 'Name', - 'Status', - 'Size', - 'Attached to', - ) - datalist = ( - ( - _volume.id, - _volume.display_name, - _volume.status, - _volume.size, - volume.AttachmentsColumn(_volume.attachments), - ), - ) - - def setUp(self): - super().setUp() - - self.volumes_mock.list.return_value = [self._volume] - - # Get the command object to test - self.cmd = volume.ListVolume(self.app, None) - - def test_volume_list_no_options(self): - arglist = [] - verifylist = [ - ('long', False), - ('all_projects', False), - ('name', None), - ('status', None), - ('limit', None), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, tuple(data)) - - def test_volume_list_name(self): - arglist = [ - '--name', - self._volume.display_name, - ] - verifylist = [ - ('long', False), - ('all_projects', False), - ('name', self._volume.display_name), - ('status', None), - ('limit', None), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - self.assertEqual(self.columns, tuple(columns)) - self.assertCountEqual(self.datalist, tuple(data)) - - def test_volume_list_status(self): - arglist = [ - '--status', - self._volume.status, - ] - verifylist = [ - ('long', False), - ('all_projects', False), - ('name', None), - ('status', self._volume.status), - ('limit', None), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - self.assertEqual(self.columns, tuple(columns)) - self.assertCountEqual(self.datalist, tuple(data)) - - def test_volume_list_all_projects(self): - arglist = [ - '--all-projects', - ] - verifylist = [ - ('long', False), - ('all_projects', True), - ('name', None), - ('status', None), - ('limit', None), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - self.assertEqual(self.columns, tuple(columns)) - self.assertCountEqual(self.datalist, tuple(data)) - - def test_volume_list_long(self): - arglist = [ - '--long', - ] - verifylist = [ - ('long', True), - ('all_projects', False), - ('name', None), - ('status', None), - ('limit', None), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - - collist = ( - 'ID', - 'Name', - 'Status', - 'Size', - 'Type', - 'Bootable', - 'Attached to', - 'Properties', - ) - self.assertEqual(collist, columns) - - datalist = ( - ( - self._volume.id, - self._volume.display_name, - self._volume.status, - self._volume.size, - self._volume.volume_type, - self._volume.bootable, - volume.AttachmentsColumn(self._volume.attachments), - format_columns.DictColumn(self._volume.metadata), - ), - ) - self.assertCountEqual(datalist, tuple(data)) - - def test_volume_list_with_limit_and_offset(self): - arglist = [ - '--limit', - '2', - '--offset', - '5', - ] - verifylist = [ - ('long', False), - ('all_projects', False), - ('name', None), - ('status', None), - ('limit', 2), - ('offset', 5), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - - self.volumes_mock.list.assert_called_once_with( - limit=2, - search_opts={ - 'offset': 5, - 'status': None, - 'display_name': None, - 'all_tenants': False, - }, - ) - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, tuple(data)) - - def test_volume_list_negative_limit(self): - arglist = [ - "--limit", - "-2", - ] - verifylist = [ - ("limit", -2), - ] - self.assertRaises( - test_utils.ParserException, - self.check_parser, - self.cmd, - arglist, - verifylist, - ) - - def test_volume_list_backward_compatibility(self): - arglist = [ - '-c', - 'Display Name', - ] - verifylist = [ - ('columns', ['Display Name']), - ('long', False), - ('all_projects', False), - ('name', None), - ('status', None), - ('limit', None), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - - self.assertIn('Display Name', columns) - self.assertNotIn('Name', columns) - for each_volume in data: - self.assertIn(self._volume.display_name, each_volume) - - -class TestVolumeMigrate(TestVolume): - _volume = volume_fakes.create_one_volume() - - def setUp(self): - super().setUp() - - self.volumes_mock.get.return_value = self._volume - self.volumes_mock.migrate_volume.return_value = None - # Get the command object to test - self.cmd = volume.MigrateVolume(self.app, None) - - def test_volume_migrate(self): - arglist = [ - "--host", - "host@backend-name#pool", - self._volume.id, - ] - verifylist = [ - ("force_host_copy", False), - ("host", "host@backend-name#pool"), - ("volume", self._volume.id), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - self.volumes_mock.get.assert_called_once_with(self._volume.id) - self.volumes_mock.migrate_volume.assert_called_once_with( - self._volume.id, "host@backend-name#pool", False - ) - self.assertIsNone(result) - - def test_volume_migrate_with_option(self): - arglist = [ - "--force-host-copy", - "--host", - "host@backend-name#pool", - self._volume.id, - ] - verifylist = [ - ("force_host_copy", True), - ("host", "host@backend-name#pool"), - ("volume", self._volume.id), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - self.volumes_mock.get.assert_called_once_with(self._volume.id) - self.volumes_mock.migrate_volume.assert_called_once_with( - self._volume.id, "host@backend-name#pool", True - ) - self.assertIsNone(result) - - def test_volume_migrate_without_host(self): - arglist = [ - self._volume.id, - ] - verifylist = [ - ("force_host_copy", False), - ("volume", self._volume.id), - ] - - self.assertRaises( - test_utils.ParserException, - self.check_parser, - self.cmd, - arglist, - verifylist, - ) - - -class TestVolumeSet(TestVolume): - _volume = volume_fakes.create_one_volume() - - def setUp(self): - super().setUp() - - self.volumes_mock.get.return_value = self._volume - - self.volumes_mock.update.return_value = self._volume - # Get the command object to test - self.cmd = volume.SetVolume(self.app, None) - - def test_volume_set_no_options(self): - arglist = [ - self._volume.display_name, - ] - verifylist = [ - ('name', None), - ('description', None), - ('size', None), - ('property', None), - ('volume', self._volume.display_name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - self.assertIsNone(result) - - def test_volume_set_name(self): - arglist = [ - '--name', - 'qwerty', - self._volume.display_name, - ] - verifylist = [ - ('name', 'qwerty'), - ('description', None), - ('size', None), - ('property', None), - ('volume', self._volume.display_name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - # Set expected values - kwargs = { - 'display_name': 'qwerty', - } - self.volumes_mock.update.assert_called_with(self._volume.id, **kwargs) - self.assertIsNone(result) - - def test_volume_set_description(self): - arglist = [ - '--description', - 'new desc', - self._volume.display_name, - ] - verifylist = [ - ('name', None), - ('description', 'new desc'), - ('size', None), - ('property', None), - ('volume', self._volume.display_name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - # Set expected values - kwargs = { - 'display_description': 'new desc', - } - self.volumes_mock.update.assert_called_with(self._volume.id, **kwargs) - self.assertIsNone(result) - - def test_volume_set_size(self): - arglist = [ - '--size', - '130', - self._volume.display_name, - ] - verifylist = [ - ('name', None), - ('description', None), - ('size', 130), - ('property', None), - ('volume', self._volume.display_name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - # Set expected values - size = 130 - self.volumes_mock.extend.assert_called_with(self._volume.id, size) - self.assertIsNone(result) - - def test_volume_set_size_smaller(self): - self._volume.status = 'available' - arglist = [ - '--size', - '1', - self._volume.display_name, - ] - verifylist = [ - ('name', None), - ('description', None), - ('size', 1), - ('property', None), - ('volume', self._volume.display_name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - self.assertRaises( - exceptions.CommandError, self.cmd.take_action, parsed_args - ) - - def test_volume_set_size_not_available(self): - self._volume.status = 'error' - arglist = [ - '--size', - '130', - self._volume.display_name, - ] - verifylist = [ - ('name', None), - ('description', None), - ('size', 130), - ('property', None), - ('volume', self._volume.display_name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - self.assertRaises( - exceptions.CommandError, self.cmd.take_action, parsed_args - ) - - def test_volume_set_property(self): - arglist = [ - '--no-property', - '--property', - 'myprop=myvalue', - self._volume.display_name, - ] - verifylist = [ - ('read_only', False), - ('read_write', False), - ('name', None), - ('description', None), - ('size', None), - ('no_property', True), - ('property', {'myprop': 'myvalue'}), - ('volume', self._volume.display_name), - ('bootable', False), - ('non_bootable', False), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - # Set expected values - metadata = {'myprop': 'myvalue'} - self.volumes_mock.set_metadata.assert_called_with( - self._volume.id, metadata - ) - self.volumes_mock.delete_metadata.assert_called_with( - self._volume.id, self._volume.metadata.keys() - ) - self.volumes_mock.update_readonly_flag.assert_not_called() - self.assertIsNone(result) - - def test_volume_set_bootable(self): - arglist = [ - ['--bootable', self._volume.id], - ['--non-bootable', self._volume.id], - ] - verifylist = [ - [ - ('bootable', True), - ('non_bootable', False), - ('volume', self._volume.id), - ], - [ - ('bootable', False), - ('non_bootable', True), - ('volume', self._volume.id), - ], - ] - for index in range(len(arglist)): - parsed_args = self.check_parser( - self.cmd, arglist[index], verifylist[index] - ) - - self.cmd.take_action(parsed_args) - self.volumes_mock.set_bootable.assert_called_with( - self._volume.id, verifylist[index][0][1] - ) - - def test_volume_set_readonly(self): - arglist = ['--read-only', self._volume.id] - verifylist = [ - ('read_only', True), - ('read_write', False), - ('volume', self._volume.id), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - self.volumes_mock.update_readonly_flag.assert_called_once_with( - self._volume.id, True - ) - self.assertIsNone(result) - - def test_volume_set_read_write(self): - arglist = ['--read-write', self._volume.id] - verifylist = [ - ('read_only', False), - ('read_write', True), - ('volume', self._volume.id), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - self.volumes_mock.update_readonly_flag.assert_called_once_with( - self._volume.id, False - ) - self.assertIsNone(result) - - -class TestVolumeShow(TestVolume): - columns = ( - 'attachments', - 'availability_zone', - 'bootable', - 'created_at', - 'display_description', - 'id', - 'name', - 'properties', - 'size', - 'snapshot_id', - 'status', - 'type', - ) - - def setUp(self): - super().setUp() - self._volume = volume_fakes.create_one_volume() - self.datalist = ( - self._volume.attachments, - self._volume.availability_zone, - self._volume.bootable, - self._volume.created_at, - self._volume.display_description, - self._volume.id, - self._volume.display_name, - format_columns.DictColumn(self._volume.metadata), - self._volume.size, - self._volume.snapshot_id, - self._volume.status, - self._volume.volume_type, - ) - self.volumes_mock.get.return_value = self._volume - # Get the command object to test - self.cmd = volume.ShowVolume(self.app, None) - - def test_volume_show(self): - arglist = [self._volume.id] - verifylist = [("volume", self._volume.id)] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - self.volumes_mock.get.assert_called_with(self._volume.id) - - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) - - def test_volume_show_backward_compatibility(self): - arglist = [ - '-c', - 'display_name', - self._volume.id, - ] - verifylist = [ - ('columns', ['display_name']), - ('volume', self._volume.id), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - - self.volumes_mock.get.assert_called_with(self._volume.id) - - self.assertIn('display_name', columns) - self.assertNotIn('name', columns) - self.assertIn(self._volume.display_name, data) - - -class TestVolumeUnset(TestVolume): - _volume = volume_fakes.create_one_volume() - - def setUp(self): - super().setUp() - - self.volumes_mock.get.return_value = self._volume - - self.volumes_mock.delete_metadata.return_value = None - # Get the command object to test - self.cmd = volume.UnsetVolume(self.app, None) - - def test_volume_unset_no_options(self): - arglist = [ - self._volume.display_name, - ] - verifylist = [ - ('property', None), - ('volume', self._volume.display_name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - self.assertIsNone(result) - - def test_volume_unset_property(self): - arglist = [ - '--property', - 'myprop', - self._volume.display_name, - ] - verifylist = [ - ('property', ['myprop']), - ('volume', self._volume.display_name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - self.volumes_mock.delete_metadata.assert_called_with( - self._volume.id, ['myprop'] - ) - self.assertIsNone(result) - - -class TestColumns(TestVolume): - def test_attachments_column_without_server_cache(self): - _volume = volume_fakes.create_one_volume() - server_id = _volume.attachments[0]['server_id'] - device = _volume.attachments[0]['device'] - - col = volume.AttachmentsColumn(_volume.attachments, {}) - self.assertEqual( - f'Attached to {server_id} on {device} ', - col.human_readable(), - ) - self.assertEqual(_volume.attachments, col.machine_readable()) - - def test_attachments_column_with_server_cache(self): - _volume = volume_fakes.create_one_volume() - - server_id = _volume.attachments[0]['server_id'] - device = _volume.attachments[0]['device'] - fake_server = mock.Mock() - fake_server.name = 'fake-server-name' - server_cache = {server_id: fake_server} - - col = volume.AttachmentsColumn(_volume.attachments, server_cache) - self.assertEqual( - 'Attached to {} on {} '.format('fake-server-name', device), - col.human_readable(), - ) - self.assertEqual(_volume.attachments, col.machine_readable()) diff --git a/openstackclient/tests/unit/volume/v1/test_volume_backup.py b/openstackclient/tests/unit/volume/v1/test_volume_backup.py deleted file mode 100644 index c551d159e7..0000000000 --- a/openstackclient/tests/unit/volume/v1/test_volume_backup.py +++ /dev/null @@ -1,435 +0,0 @@ -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -from unittest import mock -from unittest.mock import call - -from osc_lib import exceptions -from osc_lib import utils - -from openstackclient.tests.unit.volume.v1 import fakes as volume_fakes -from openstackclient.volume.v1 import volume_backup - - -class TestBackup(volume_fakes.TestVolumev1): - def setUp(self): - super().setUp() - - self.backups_mock = self.volume_client.backups - self.backups_mock.reset_mock() - self.volumes_mock = self.volume_client.volumes - self.volumes_mock.reset_mock() - self.snapshots_mock = self.volume_client.volume_snapshots - self.snapshots_mock.reset_mock() - self.restores_mock = self.volume_client.restores - self.restores_mock.reset_mock() - - -class TestBackupCreate(TestBackup): - volume = volume_fakes.create_one_volume() - - columns = ( - 'availability_zone', - 'container', - 'description', - 'id', - 'name', - 'object_count', - 'size', - 'snapshot_id', - 'status', - 'volume_id', - ) - - def setUp(self): - super().setUp() - self.new_backup = volume_fakes.create_one_backup( - attrs={'volume_id': self.volume.id}, - ) - self.data = ( - self.new_backup.availability_zone, - self.new_backup.container, - self.new_backup.description, - self.new_backup.id, - self.new_backup.name, - self.new_backup.object_count, - self.new_backup.size, - self.new_backup.snapshot_id, - self.new_backup.status, - self.new_backup.volume_id, - ) - self.volumes_mock.get.return_value = self.volume - self.backups_mock.create.return_value = self.new_backup - - # Get the command object to test - self.cmd = volume_backup.CreateVolumeBackup(self.app, None) - - def test_backup_create(self): - arglist = [ - "--name", - self.new_backup.name, - "--description", - self.new_backup.description, - "--container", - self.new_backup.container, - self.new_backup.volume_id, - ] - verifylist = [ - ("name", self.new_backup.name), - ("description", self.new_backup.description), - ("container", self.new_backup.container), - ("volume", self.new_backup.volume_id), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - - self.backups_mock.create.assert_called_with( - self.new_backup.volume_id, - self.new_backup.container, - self.new_backup.name, - self.new_backup.description, - ) - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.data, data) - - def test_backup_create_without_name(self): - arglist = [ - "--description", - self.new_backup.description, - "--container", - self.new_backup.container, - self.new_backup.volume_id, - ] - verifylist = [ - ("description", self.new_backup.description), - ("container", self.new_backup.container), - ("volume", self.new_backup.volume_id), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - - self.backups_mock.create.assert_called_with( - self.new_backup.volume_id, - self.new_backup.container, - None, - self.new_backup.description, - ) - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.data, data) - - -class TestBackupDelete(TestBackup): - backups = volume_fakes.create_backups(count=2) - - def setUp(self): - super().setUp() - - self.backups_mock.get = volume_fakes.get_backups(self.backups) - self.backups_mock.delete.return_value = None - - # Get the command object to mock - self.cmd = volume_backup.DeleteVolumeBackup(self.app, None) - - def test_backup_delete(self): - arglist = [self.backups[0].id] - verifylist = [("backups", [self.backups[0].id])] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - self.backups_mock.delete.assert_called_with(self.backups[0].id) - self.assertIsNone(result) - - def test_delete_multiple_backups(self): - arglist = [] - for b in self.backups: - arglist.append(b.id) - verifylist = [ - ('backups', arglist), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - - calls = [] - for b in self.backups: - calls.append(call(b.id)) - self.backups_mock.delete.assert_has_calls(calls) - self.assertIsNone(result) - - def test_delete_multiple_backups_with_exception(self): - arglist = [ - self.backups[0].id, - 'unexist_backup', - ] - verifylist = [ - ('backups', arglist), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - find_mock_result = [self.backups[0], exceptions.CommandError] - with mock.patch.object( - utils, 'find_resource', side_effect=find_mock_result - ) as find_mock: - try: - self.cmd.take_action(parsed_args) - self.fail('CommandError should be raised.') - except exceptions.CommandError as e: - self.assertEqual('1 of 2 backups failed to delete.', str(e)) - - find_mock.assert_any_call(self.backups_mock, self.backups[0].id) - find_mock.assert_any_call(self.backups_mock, 'unexist_backup') - - self.assertEqual(2, find_mock.call_count) - self.backups_mock.delete.assert_called_once_with( - self.backups[0].id, - ) - - -class TestBackupList(TestBackup): - volume = volume_fakes.create_one_volume() - backups = volume_fakes.create_backups( - attrs={'volume_id': volume.display_name}, - count=3, - ) - - columns = [ - 'ID', - 'Name', - 'Description', - 'Status', - 'Size', - ] - columns_long = columns + [ - 'Availability Zone', - 'Volume', - 'Container', - ] - - data = [] - for b in backups: - data.append( - ( - b.id, - b.name, - b.description, - b.status, - b.size, - ) - ) - data_long = [] - for b in backups: - data_long.append( - ( - b.id, - b.name, - b.description, - b.status, - b.size, - b.availability_zone, - volume_backup.VolumeIdColumn(b.volume_id), - b.container, - ) - ) - - def setUp(self): - super().setUp() - - self.volumes_mock.list.return_value = [self.volume] - self.backups_mock.list.return_value = self.backups - self.volumes_mock.get.return_value = self.volume - # Get the command to test - self.cmd = volume_backup.ListVolumeBackup(self.app, None) - - def test_backup_list_without_options(self): - arglist = [] - verifylist = [ - ("long", False), - ("name", None), - ("status", None), - ("volume", None), - ('all_projects', False), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - columns, data = self.cmd.take_action(parsed_args) - - search_opts = { - "name": None, - "status": None, - "volume_id": None, - "all_tenants": False, - } - self.volumes_mock.get.assert_not_called() - self.backups_mock.list.assert_called_with( - search_opts=search_opts, - ) - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.data, list(data)) - - def test_backup_list_with_options(self): - arglist = [ - "--long", - "--name", - self.backups[0].name, - "--status", - "error", - "--volume", - self.volume.id, - "--all-projects", - ] - verifylist = [ - ("long", True), - ("name", self.backups[0].name), - ("status", "error"), - ("volume", self.volume.id), - ('all_projects', True), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - columns, data = self.cmd.take_action(parsed_args) - - search_opts = { - "name": self.backups[0].name, - "status": "error", - "volume_id": self.volume.id, - "all_tenants": True, - } - self.volumes_mock.get.assert_called_once_with(self.volume.id) - self.backups_mock.list.assert_called_with( - search_opts=search_opts, - ) - self.assertEqual(self.columns_long, columns) - self.assertCountEqual(self.data_long, list(data)) - - -class TestBackupRestore(TestBackup): - volume = volume_fakes.create_one_volume() - backup = volume_fakes.create_one_backup( - attrs={'volume_id': volume.id}, - ) - - def setUp(self): - super().setUp() - - self.backups_mock.get.return_value = self.backup - self.volumes_mock.get.return_value = self.volume - self.restores_mock.restore.return_value = ( - volume_fakes.create_one_volume( - {'id': self.volume['id']}, - ) - ) - # Get the command object to mock - self.cmd = volume_backup.RestoreVolumeBackup(self.app, None) - - def test_backup_restore(self): - arglist = [ - self.backup.id, - ] - verifylist = [ - ("backup", self.backup.id), - ("volume", None), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - self.restores_mock.restore.assert_called_with(self.backup.id, None) - self.assertIsNotNone(result) - - def test_backup_restore_with_existing_volume(self): - arglist = [ - self.backup.id, - self.backup.volume_id, - ] - verifylist = [ - ("backup", self.backup.id), - ("volume", self.backup.volume_id), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - self.restores_mock.restore.assert_called_with( - self.backup.id, - self.backup.volume_id, - ) - self.assertIsNotNone(result) - - def test_backup_restore_with_invalid_volume(self): - arglist = [ - self.backup.id, - "unexist_volume", - ] - verifylist = [ - ("backup", self.backup.id), - ("volume", "unexist_volume"), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - with mock.patch.object( - utils, - 'find_resource', - side_effect=exceptions.CommandError(), - ): - self.assertRaises( - exceptions.CommandError, - self.cmd.take_action, - parsed_args, - ) - - -class TestBackupShow(TestBackup): - columns = ( - 'availability_zone', - 'container', - 'description', - 'id', - 'name', - 'object_count', - 'size', - 'snapshot_id', - 'status', - 'volume_id', - ) - - def setUp(self): - super().setUp() - self.backup = volume_fakes.create_one_backup() - self.data = ( - self.backup.availability_zone, - self.backup.container, - self.backup.description, - self.backup.id, - self.backup.name, - self.backup.object_count, - self.backup.size, - self.backup.snapshot_id, - self.backup.status, - self.backup.volume_id, - ) - self.backups_mock.get.return_value = self.backup - # Get the command object to test - self.cmd = volume_backup.ShowVolumeBackup(self.app, None) - - def test_backup_show(self): - arglist = [self.backup.id] - verifylist = [("backup", self.backup.id)] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - self.backups_mock.get.assert_called_with(self.backup.id) - - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.data, data) diff --git a/openstackclient/volume/v1/__init__.py b/openstackclient/volume/v1/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/openstackclient/volume/v1/qos_specs.py b/openstackclient/volume/v1/qos_specs.py deleted file mode 100644 index 93f7f48c64..0000000000 --- a/openstackclient/volume/v1/qos_specs.py +++ /dev/null @@ -1,376 +0,0 @@ -# Copyright 2015 iWeb Technologies Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -"""Volume v1 QoS action implementations""" - -import logging - -from osc_lib.cli import format_columns -from osc_lib.cli import parseractions -from osc_lib.command import command -from osc_lib import exceptions -from osc_lib import utils - -from openstackclient.i18n import _ - - -LOG = logging.getLogger(__name__) - - -class AssociateQos(command.Command): - _description = _("Associate a QoS specification to a volume type") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'qos_spec', - metavar='', - help=_('QoS specification to modify (name or ID)'), - ) - parser.add_argument( - 'volume_type', - metavar='', - help=_('Volume type to associate the QoS (name or ID)'), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - qos_spec = utils.find_resource( - volume_client.qos_specs, parsed_args.qos_spec - ) - volume_type = utils.find_resource( - volume_client.volume_types, parsed_args.volume_type - ) - - volume_client.qos_specs.associate(qos_spec.id, volume_type.id) - - -class CreateQos(command.ShowOne): - _description = _("Create new QoS specification") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'name', - metavar='', - help=_('New QoS specification name'), - ) - consumer_choices = ['front-end', 'back-end', 'both'] - parser.add_argument( - '--consumer', - metavar='', - choices=consumer_choices, - default='both', - help=( - _( - 'Consumer of the QoS. Valid consumers: %s ' - "(defaults to 'both')" - ) - % utils.format_list(consumer_choices) - ), - ) - parser.add_argument( - '--property', - metavar='', - action=parseractions.KeyValueAction, - help=_( - 'Set a QoS specification property ' - '(repeat option to set multiple properties)' - ), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - specs = {} - specs.update({'consumer': parsed_args.consumer}) - - if parsed_args.property: - specs.update(parsed_args.property) - - qos_spec = volume_client.qos_specs.create(parsed_args.name, specs) - qos_spec._info.update( - { - 'properties': format_columns.DictColumn( - qos_spec._info.pop('specs') - ) - } - ) - return zip(*sorted(qos_spec._info.items())) - - -class DeleteQos(command.Command): - _description = _("Delete QoS specification") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'qos_specs', - metavar='', - nargs="+", - help=_('QoS specification(s) to delete (name or ID)'), - ) - parser.add_argument( - '--force', - action='store_true', - default=False, - help=_("Allow to delete in-use QoS specification(s)"), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - result = 0 - - for i in parsed_args.qos_specs: - try: - qos_spec = utils.find_resource(volume_client.qos_specs, i) - volume_client.qos_specs.delete(qos_spec.id, parsed_args.force) - except Exception as e: - result += 1 - LOG.error( - _( - "Failed to delete QoS specification with " - "name or ID '%(qos)s': %(e)s" - ), - {'qos': i, 'e': e}, - ) - - if result > 0: - total = len(parsed_args.qos_specs) - msg = _( - "%(result)s of %(total)s QoS specifications failed to delete." - ) % {'result': result, 'total': total} - raise exceptions.CommandError(msg) - - -class DisassociateQos(command.Command): - _description = _("Disassociate a QoS specification from a volume type") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'qos_spec', - metavar='', - help=_('QoS specification to modify (name or ID)'), - ) - volume_type_group = parser.add_mutually_exclusive_group() - volume_type_group.add_argument( - '--volume-type', - metavar='', - help=_('Volume type to disassociate the QoS from (name or ID)'), - ) - volume_type_group.add_argument( - '--all', - action='store_true', - default=False, - help=_('Disassociate the QoS from every volume type'), - ) - - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - qos_spec = utils.find_resource( - volume_client.qos_specs, parsed_args.qos_spec - ) - - if parsed_args.volume_type: - volume_type = utils.find_resource( - volume_client.volume_types, parsed_args.volume_type - ) - volume_client.qos_specs.disassociate(qos_spec.id, volume_type.id) - elif parsed_args.all: - volume_client.qos_specs.disassociate_all(qos_spec.id) - - -class ListQos(command.Lister): - _description = _("List QoS specifications") - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - qos_specs_list = volume_client.qos_specs.list() - - for qos in qos_specs_list: - try: - qos_associations = volume_client.qos_specs.get_associations( - qos, - ) - if qos_associations: - associations = [ - association.name for association in qos_associations - ] - qos._info.update({'associations': associations}) - except Exception as ex: - if type(ex).__name__ == 'NotFound': - qos._info.update({'associations': None}) - else: - raise - - display_columns = ( - 'ID', - 'Name', - 'Consumer', - 'Associations', - 'Properties', - ) - columns = ('ID', 'Name', 'Consumer', 'Associations', 'Specs') - return ( - display_columns, - ( - utils.get_dict_properties( - s._info, - columns, - formatters={ - 'Specs': format_columns.DictColumn, - 'Associations': format_columns.ListColumn, - }, - ) - for s in qos_specs_list - ), - ) - - -class SetQos(command.Command): - _description = _("Set QoS specification properties") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'qos_spec', - metavar='', - help=_('QoS specification to modify (name or ID)'), - ) - parser.add_argument( - '--no-property', - dest='no_property', - action='store_true', - help=_( - 'Remove all properties from ' - '(specify both --no-property and --property to remove the ' - 'current properties before setting new properties)' - ), - ) - parser.add_argument( - '--property', - metavar='', - action=parseractions.KeyValueAction, - help=_( - 'Property to add or modify for this QoS specification ' - '(repeat option to set multiple properties)' - ), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - qos_spec = utils.find_resource( - volume_client.qos_specs, parsed_args.qos_spec - ) - - result = 0 - if parsed_args.no_property: - try: - key_list = list(qos_spec._info['specs'].keys()) - volume_client.qos_specs.unset_keys(qos_spec.id, key_list) - except Exception as e: - LOG.error(_("Failed to clean qos properties: %s"), e) - result += 1 - - if parsed_args.property: - try: - volume_client.qos_specs.set_keys( - qos_spec.id, - parsed_args.property, - ) - except Exception as e: - LOG.error(_("Failed to set qos property: %s"), e) - result += 1 - - if result > 0: - raise exceptions.CommandError( - _("One or more of the set operations failed") - ) - - -class ShowQos(command.ShowOne): - _description = _("Display QoS specification details") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'qos_spec', - metavar='', - help=_('QoS specification to display (name or ID)'), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - qos_spec = utils.find_resource( - volume_client.qos_specs, parsed_args.qos_spec - ) - - qos_associations = volume_client.qos_specs.get_associations(qos_spec) - if qos_associations: - associations = [ - association.name for association in qos_associations - ] - qos_spec._info.update( - {'associations': format_columns.ListColumn(associations)} - ) - qos_spec._info.update( - { - 'properties': format_columns.DictColumn( - qos_spec._info.pop('specs') - ) - } - ) - - return zip(*sorted(qos_spec._info.items())) - - -class UnsetQos(command.Command): - _description = _("Unset QoS specification properties") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'qos_spec', - metavar='', - help=_('QoS specification to modify (name or ID)'), - ) - parser.add_argument( - '--property', - metavar='', - action='append', - help=_( - 'Property to remove from the QoS specification. ' - '(repeat option to unset multiple properties)' - ), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - qos_spec = utils.find_resource( - volume_client.qos_specs, parsed_args.qos_spec - ) - - if parsed_args.property: - volume_client.qos_specs.unset_keys( - qos_spec.id, parsed_args.property - ) diff --git a/openstackclient/volume/v1/service.py b/openstackclient/volume/v1/service.py deleted file mode 100644 index 2a33fc0b9a..0000000000 --- a/openstackclient/volume/v1/service.py +++ /dev/null @@ -1,136 +0,0 @@ -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -"""Service action implementations""" - -from osc_lib.command import command -from osc_lib import exceptions -from osc_lib import utils - -from openstackclient.i18n import _ - - -class ListService(command.Lister): - _description = _("List service command") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - "--host", - metavar="", - help=_("List services on specified host (name only)"), - ) - parser.add_argument( - "--service", - metavar="", - help=_("List only specified service (name only)"), - ) - parser.add_argument( - "--long", - action="store_true", - default=False, - help=_("List additional fields in output"), - ) - return parser - - def take_action(self, parsed_args): - service_client = self.app.client_manager.volume - - if parsed_args.long: - columns = [ - "Binary", - "Host", - "Zone", - "Status", - "State", - "Updated At", - "Disabled Reason", - ] - else: - columns = [ - "Binary", - "Host", - "Zone", - "Status", - "State", - "Updated At", - ] - - data = service_client.services.list( - parsed_args.host, parsed_args.service - ) - return ( - columns, - ( - utils.get_item_properties( - s, - columns, - ) - for s in data - ), - ) - - -class SetService(command.Command): - _description = _("Set volume service properties") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument("host", metavar="", help=_("Name of host")) - parser.add_argument( - "service", - metavar="", - help=_("Name of service (Binary name)"), - ) - enabled_group = parser.add_mutually_exclusive_group() - enabled_group.add_argument( - "--enable", action="store_true", help=_("Enable volume service") - ) - enabled_group.add_argument( - "--disable", action="store_true", help=_("Disable volume service") - ) - parser.add_argument( - "--disable-reason", - metavar="", - help=_( - "Reason for disabling the service " - "(should be used with --disable option)" - ), - ) - return parser - - def take_action(self, parsed_args): - if parsed_args.disable_reason and not parsed_args.disable: - msg = _( - "Cannot specify option --disable-reason without " - "--disable specified." - ) - raise exceptions.CommandError(msg) - - service_client = self.app.client_manager.volume - if parsed_args.enable: - service_client.services.enable( - parsed_args.host, parsed_args.service - ) - if parsed_args.disable: - if parsed_args.disable_reason: - service_client.services.disable_log_reason( - parsed_args.host, - parsed_args.service, - parsed_args.disable_reason, - ) - else: - service_client.services.disable( - parsed_args.host, parsed_args.service - ) diff --git a/openstackclient/volume/v1/volume.py b/openstackclient/volume/v1/volume.py deleted file mode 100644 index 1634624c02..0000000000 --- a/openstackclient/volume/v1/volume.py +++ /dev/null @@ -1,727 +0,0 @@ -# Copyright 2012-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 v1 Volume action implementations""" - -import argparse -import functools -import logging - -from cliff import columns as cliff_columns -from osc_lib.cli import format_columns -from osc_lib.cli import parseractions -from osc_lib.command import command -from osc_lib import exceptions -from osc_lib import utils - -from openstackclient.common import pagination -from openstackclient.i18n import _ - - -LOG = logging.getLogger(__name__) - - -class AttachmentsColumn(cliff_columns.FormattableColumn): - """Formattable column for attachments column. - - Unlike the parent FormattableColumn class, the initializer of the - class takes server_cache as the second argument. - osc_lib.utils.get_item_properties instantiate cliff FormattableColumn - object with a single parameter "column value", so you need to pass - a partially initialized class like - ``functools.partial(AttachmentsColumn, server_cache)``. - """ - - def __init__(self, value, server_cache=None): - super().__init__(value) - self._server_cache = server_cache or {} - - def human_readable(self): - """Return a formatted string of a volume's attached instances - - :rtype: a string of formatted instances - """ - - msg = '' - for attachment in self._value: - server = attachment['server_id'] - if server in self._server_cache.keys(): - server = self._server_cache[server].name - device = attachment['device'] - msg += f'Attached to {server} on {device} ' - return msg - - -def _check_size_arg(args): - """Check whether --size option is required or not. - - Require size parameter only in case when snapshot or source - volume is not specified. - """ - - if (args.snapshot or args.source) is None and args.size is None: - msg = _( - "--size is a required option if snapshot " - "or source volume is not specified." - ) - raise exceptions.CommandError(msg) - - -class CreateVolume(command.ShowOne): - _description = _("Create new volume") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'name', - metavar='', - help=_('Volume name'), - ) - parser.add_argument( - '--size', - metavar='', - type=int, - help=_( - "Volume size in GB (Required unless --snapshot or " - "--source is specified)" - ), - ) - parser.add_argument( - '--type', - metavar='', - help=_("Set the type of volume"), - ) - source_group = parser.add_mutually_exclusive_group() - source_group.add_argument( - '--image', - metavar='', - help=_('Use as source of volume (name or ID)'), - ) - source_group.add_argument( - '--snapshot', - metavar='', - help=_('Use as source of volume (name or ID)'), - ) - source_group.add_argument( - '--snapshot-id', - metavar='', - help=argparse.SUPPRESS, - ) - source_group.add_argument( - '--source', - metavar='', - help=_('Volume to clone (name or ID)'), - ) - parser.add_argument( - '--description', - metavar='', - help=_('Volume description'), - ) - parser.add_argument( - '--user', - metavar='', - help=_('Specify an alternate user (name or ID)'), - ) - parser.add_argument( - '--project', - metavar='', - help=_('Specify an alternate project (name or ID)'), - ) - parser.add_argument( - '--availability-zone', - metavar='', - help=_('Create volume in '), - ) - parser.add_argument( - '--property', - metavar='', - action=parseractions.KeyValueAction, - help=_( - 'Set a property on this volume ' - '(repeat option to set multiple properties)' - ), - ) - bootable_group = parser.add_mutually_exclusive_group() - bootable_group.add_argument( - "--bootable", - action="store_true", - help=_("Mark volume as bootable"), - ) - bootable_group.add_argument( - "--non-bootable", - action="store_true", - help=_("Mark volume as non-bootable (default)"), - ) - readonly_group = parser.add_mutually_exclusive_group() - readonly_group.add_argument( - "--read-only", - action="store_true", - help=_("Set volume to read-only access mode"), - ) - readonly_group.add_argument( - "--read-write", - action="store_true", - help=_("Set volume to read-write access mode (default)"), - ) - - return parser - - def take_action(self, parsed_args): - _check_size_arg(parsed_args) - identity_client = self.app.client_manager.identity - image_client = self.app.client_manager.image - volume_client = self.app.client_manager.volume - - source_volume = None - if parsed_args.source: - source_volume = utils.find_resource( - volume_client.volumes, - parsed_args.source, - ).id - - project = None - if parsed_args.project: - project = utils.find_resource( - identity_client.tenants, - parsed_args.project, - ).id - - user = None - if parsed_args.user: - user = utils.find_resource( - identity_client.users, - parsed_args.user, - ).id - - image = None - if parsed_args.image: - image = image_client.find_image( - parsed_args.image, - ignore_missing=False, - ).id - - snapshot = parsed_args.snapshot or parsed_args.snapshot_id - - volume = volume_client.volumes.create( - parsed_args.size, - snapshot, - source_volume, - parsed_args.name, - parsed_args.description, - parsed_args.type, - user, - project, - parsed_args.availability_zone, - parsed_args.property, - image, - ) - - if parsed_args.bootable or parsed_args.non_bootable: - try: - if utils.wait_for_status( - volume_client.volumes.get, - volume.id, - success_status=['available'], - error_status=['error'], - sleep_time=1, - ): - volume_client.volumes.set_bootable( - volume.id, parsed_args.bootable - ) - else: - msg = _( - "Volume status is not available for setting boot state" - ) - raise exceptions.CommandError(msg) - except Exception as e: - LOG.error(_("Failed to set volume bootable property: %s"), e) - if parsed_args.read_only or parsed_args.read_write: - try: - if utils.wait_for_status( - volume_client.volumes.get, - volume.id, - success_status=['available'], - error_status=['error'], - sleep_time=1, - ): - volume_client.volumes.update_readonly_flag( - volume.id, parsed_args.read_only - ) - else: - msg = _( - "Volume status is not available for setting it" - "read only." - ) - raise exceptions.CommandError(msg) - except Exception as e: - LOG.error( - _("Failed to set volume read-only access mode flag: %s"), - e, - ) - - # Map 'metadata' column to 'properties' - volume._info.update( - { - 'properties': format_columns.DictColumn( - volume._info.pop('metadata') - ), - 'type': volume._info.pop('volume_type'), - }, - ) - # Replace "display_name" by "name", keep consistent in v1 and v2 - if 'display_name' in volume._info: - volume._info.update({'name': volume._info.pop('display_name')}) - volume_info = utils.backward_compat_col_showone( - volume._info, parsed_args.columns, {'display_name': 'name'} - ) - - return zip(*sorted(volume_info.items())) - - -class DeleteVolume(command.Command): - _description = _("Delete volume(s)") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'volumes', - metavar='', - nargs="+", - help=_('Volume(s) to delete (name or ID)'), - ) - parser.add_argument( - '--force', - action='store_true', - default=False, - help=_( - 'Attempt forced removal of volume(s), regardless of state ' - '(defaults to False)' - ), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - result = 0 - - for i in parsed_args.volumes: - try: - volume_obj = utils.find_resource(volume_client.volumes, i) - if parsed_args.force: - volume_client.volumes.force_delete(volume_obj.id) - else: - volume_client.volumes.delete(volume_obj.id) - except Exception as e: - result += 1 - LOG.error( - _( - "Failed to delete volume with " - "name or ID '%(volume)s': %(e)s" - ), - {'volume': i, 'e': e}, - ) - - if result > 0: - total = len(parsed_args.volumes) - msg = _("%(result)s of %(total)s volumes failed to delete.") % { - 'result': result, - 'total': total, - } - raise exceptions.CommandError(msg) - - -class ListVolume(command.Lister): - _description = _("List volumes") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - '--name', - metavar='', - help=_('Filter results by volume name'), - ) - parser.add_argument( - '--status', - metavar='', - help=_('Filter results by status'), - ) - parser.add_argument( - '--all-projects', - action='store_true', - default=False, - help=_('Include all projects (admin only)'), - ) - parser.add_argument( - '--long', - action='store_true', - default=False, - help=_('List additional fields in output'), - ) - pagination.add_offset_pagination_option_to_parser(parser) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - - if parsed_args.long: - columns: tuple[str, ...] = ( - 'ID', - 'Display Name', - 'Status', - 'Size', - 'Volume Type', - 'Bootable', - 'Attachments', - 'Metadata', - ) - column_headers: tuple[str, ...] = ( - 'ID', - 'Name', - 'Status', - 'Size', - 'Type', - 'Bootable', - 'Attached to', - 'Properties', - ) - else: - columns = ( - 'ID', - 'Display Name', - 'Status', - 'Size', - 'Attachments', - ) - column_headers = ( - 'ID', - 'Name', - 'Status', - 'Size', - 'Attached to', - ) - - # Cache the server list - server_cache = {} - try: - compute_client = self.app.client_manager.sdk_connection.compute - for s in compute_client.servers(): - server_cache[s.id] = s - except Exception: # noqa: S110 - # Just forget it if there's any trouble - pass - AttachmentsColumnWithCache = functools.partial( - AttachmentsColumn, server_cache=server_cache - ) - - search_opts = { - 'all_tenants': parsed_args.all_projects, - 'display_name': parsed_args.name, - 'status': parsed_args.status, - } - - if parsed_args.offset: - search_opts['offset'] = parsed_args.offset - - data = volume_client.volumes.list( - search_opts=search_opts, - limit=parsed_args.limit, - ) - column_headers = utils.backward_compat_col_lister( - column_headers, parsed_args.columns, {'Display Name': 'Name'} - ) - - return ( - column_headers, - ( - utils.get_item_properties( - s, - columns, - formatters={ - 'Metadata': format_columns.DictColumn, - 'Attachments': AttachmentsColumnWithCache, - }, - ) - for s in data - ), - ) - - -class MigrateVolume(command.Command): - _description = _("Migrate volume to a new host") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'volume', - metavar="", - help=_("Volume to migrate (name or ID)"), - ) - parser.add_argument( - '--host', - metavar="", - required=True, - help=_( - "Destination host (takes the form: host@backend-name#pool)" - ), - ) - parser.add_argument( - '--force-host-copy', - action="store_true", - help=_( - "Enable generic host-based force-migration, " - "which bypasses driver optimizations" - ), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - volume = utils.find_resource(volume_client.volumes, parsed_args.volume) - volume_client.volumes.migrate_volume( - volume.id, - parsed_args.host, - parsed_args.force_host_copy, - ) - - -class SetVolume(command.Command): - _description = _("Set volume properties") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'volume', - metavar='', - help=_('Volume to modify (name or ID)'), - ) - parser.add_argument( - '--name', - metavar='', - help=_('New volume name'), - ) - parser.add_argument( - '--description', - metavar='', - help=_('New volume description'), - ) - parser.add_argument( - '--size', - metavar='', - type=int, - help=_('Extend volume size in GB'), - ) - parser.add_argument( - "--no-property", - dest="no_property", - action="store_true", - help=_( - "Remove all properties from " - "(specify both --no-property and --property to " - "remove the current properties before setting " - "new properties.)" - ), - ) - parser.add_argument( - '--property', - metavar='', - action=parseractions.KeyValueAction, - help=_( - 'Set a property on this volume ' - '(repeat option to set multiple properties)' - ), - ) - bootable_group = parser.add_mutually_exclusive_group() - bootable_group.add_argument( - "--bootable", - action="store_true", - help=_("Mark volume as bootable"), - ) - bootable_group.add_argument( - "--non-bootable", - action="store_true", - help=_("Mark volume as non-bootable"), - ) - readonly_group = parser.add_mutually_exclusive_group() - readonly_group.add_argument( - "--read-only", - action="store_true", - help=_("Set volume to read-only access mode"), - ) - readonly_group.add_argument( - "--read-write", - action="store_true", - help=_("Set volume to read-write access mode"), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - volume = utils.find_resource(volume_client.volumes, parsed_args.volume) - - result = 0 - if parsed_args.size: - try: - if volume.status != 'available': - msg = ( - _( - "Volume is in %s state, it must be available " - "before size can be extended" - ) - % volume.status - ) - raise exceptions.CommandError(msg) - if parsed_args.size <= volume.size: - msg = ( - _("New size must be greater than %s GB") % volume.size - ) - raise exceptions.CommandError(msg) - volume_client.volumes.extend(volume.id, parsed_args.size) - except Exception as e: - LOG.error(_("Failed to set volume size: %s"), e) - result += 1 - - if parsed_args.no_property: - try: - volume_client.volumes.delete_metadata( - volume.id, volume.metadata.keys() - ) - except Exception as e: - LOG.error(_("Failed to clean volume properties: %s"), e) - result += 1 - - if parsed_args.property: - try: - volume_client.volumes.set_metadata( - volume.id, parsed_args.property - ) - except Exception as e: - LOG.error(_("Failed to set volume property: %s"), e) - result += 1 - if parsed_args.bootable or parsed_args.non_bootable: - try: - volume_client.volumes.set_bootable( - volume.id, parsed_args.bootable - ) - except Exception as e: - LOG.error(_("Failed to set volume bootable property: %s"), e) - result += 1 - if parsed_args.read_only or parsed_args.read_write: - try: - volume_client.volumes.update_readonly_flag( - volume.id, parsed_args.read_only - ) - except Exception as e: - LOG.error( - _("Failed to set volume read-only access mode flag: %s"), - e, - ) - result += 1 - kwargs = {} - if parsed_args.name: - kwargs['display_name'] = parsed_args.name - if parsed_args.description: - kwargs['display_description'] = parsed_args.description - if kwargs: - try: - volume_client.volumes.update(volume.id, **kwargs) - except Exception as e: - LOG.error( - _( - "Failed to update volume display name " - "or display description: %s" - ), - e, - ) - result += 1 - - if result > 0: - raise exceptions.CommandError( - _("One or more of the set operations failed") - ) - - -class ShowVolume(command.ShowOne): - _description = _("Show volume details") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'volume', - metavar='', - help=_('Volume to display (name or ID)'), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - volume = utils.find_resource(volume_client.volumes, parsed_args.volume) - # Map 'metadata' column to 'properties' - volume._info.update( - { - 'properties': format_columns.DictColumn( - volume._info.pop('metadata') - ), - 'type': volume._info.pop('volume_type'), - }, - ) - if 'os-vol-tenant-attr:tenant_id' in volume._info: - volume._info.update( - { - 'project_id': volume._info.pop( - 'os-vol-tenant-attr:tenant_id' - ) - } - ) - # Replace "display_name" by "name", keep consistent in v1 and v2 - if 'display_name' in volume._info: - volume._info.update({'name': volume._info.pop('display_name')}) - - volume_info = utils.backward_compat_col_showone( - volume._info, parsed_args.columns, {'display_name': 'name'} - ) - - return zip(*sorted(volume_info.items())) - - -class UnsetVolume(command.Command): - _description = _("Unset volume properties") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'volume', - metavar='', - help=_('Volume to modify (name or ID)'), - ) - parser.add_argument( - '--property', - metavar='', - action='append', - help=_( - 'Remove a property from volume ' - '(repeat option to remove multiple properties)' - ), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - volume = utils.find_resource(volume_client.volumes, parsed_args.volume) - - if parsed_args.property: - volume_client.volumes.delete_metadata( - volume.id, - parsed_args.property, - ) diff --git a/openstackclient/volume/v1/volume_backup.py b/openstackclient/volume/v1/volume_backup.py deleted file mode 100644 index 4819609d34..0000000000 --- a/openstackclient/volume/v1/volume_backup.py +++ /dev/null @@ -1,301 +0,0 @@ -# Copyright 2012-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 v1 Backup action implementations""" - -import copy -import functools -import logging - -from cliff import columns as cliff_columns -from osc_lib.command import command -from osc_lib import exceptions -from osc_lib import utils - -from openstackclient.i18n import _ - - -LOG = logging.getLogger(__name__) - - -class VolumeIdColumn(cliff_columns.FormattableColumn): - """Formattable column for volume ID column. - - Unlike the parent FormattableColumn class, the initializer of the - class takes volume_cache as the second argument. - osc_lib.utils.get_item_properties instantiate cliff FormattableColumn - object with a single parameter "column value", so you need to pass - a partially initialized class like - ``functools.partial(VolumeIdColumn, volume_cache)``. - """ - - def __init__(self, value, volume_cache=None): - super().__init__(value) - self._volume_cache = volume_cache or {} - - def human_readable(self): - """Return a volume name if available - - :rtype: either the volume ID or name - """ - volume_id = self._value - volume = volume_id - if volume_id in self._volume_cache.keys(): - volume = self._volume_cache[volume_id].display_name - return volume - - -class CreateVolumeBackup(command.ShowOne): - _description = _("Create new volume backup") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'volume', - metavar='', - help=_('Volume to backup (name or ID)'), - ) - parser.add_argument( - '--container', - metavar='', - required=False, - help=_('Optional backup container name'), - ) - parser.add_argument( - '--name', - metavar='', - help=_('Name of the backup'), - ) - parser.add_argument( - '--description', - metavar='', - help=_('Description of the backup'), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - volume_id = utils.find_resource( - volume_client.volumes, parsed_args.volume - ).id - backup = volume_client.backups.create( - volume_id, - parsed_args.container, - parsed_args.name, - parsed_args.description, - ) - - backup._info.pop('links') - return zip(*sorted(backup._info.items())) - - -class DeleteVolumeBackup(command.Command): - _description = _("Delete volume backup(s)") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'backups', - metavar='', - nargs="+", - help=_('Backup(s) to delete (name or ID)'), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - result = 0 - - for i in parsed_args.backups: - try: - backup_id = utils.find_resource(volume_client.backups, i).id - volume_client.backups.delete(backup_id) - except Exception as e: - result += 1 - LOG.error( - _( - "Failed to delete backup with " - "name or ID '%(backup)s': %(e)s" - ), - {'backup': i, 'e': e}, - ) - - if result > 0: - total = len(parsed_args.backups) - msg = _("%(result)s of %(total)s backups failed to delete.") % { - 'result': result, - 'total': total, - } - raise exceptions.CommandError(msg) - - -class ListVolumeBackup(command.Lister): - _description = _("List volume backups") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - '--long', - action='store_true', - default=False, - help=_('List additional fields in output'), - ) - parser.add_argument( - "--name", - metavar="", - help=_("Filters results by the backup name"), - ) - parser.add_argument( - "--status", - metavar="", - choices=[ - 'creating', - 'available', - 'deleting', - 'error', - 'restoring', - 'error_restoring', - ], - help=_( - "Filters results by the backup status " - "('creating', 'available', 'deleting', " - "'error', 'restoring' or 'error_restoring')" - ), - ) - parser.add_argument( - "--volume", - metavar="", - help=_( - "Filters results by the volume which they backup (name or ID)" - ), - ) - parser.add_argument( - '--all-projects', - action='store_true', - default=False, - help=_('Include all projects (admin only)'), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - - if parsed_args.long: - columns = [ - 'ID', - 'Name', - 'Description', - 'Status', - 'Size', - 'Availability Zone', - 'Volume ID', - 'Container', - ] - column_headers = copy.deepcopy(columns) - column_headers[6] = 'Volume' - else: - columns = ['ID', 'Name', 'Description', 'Status', 'Size'] - column_headers = columns - - # Cache the volume list - volume_cache = {} - try: - for s in volume_client.volumes.list(): - volume_cache[s.id] = s - except Exception: # noqa: S110 - # Just forget it if there's any trouble - pass - VolumeIdColumnWithCache = functools.partial( - VolumeIdColumn, volume_cache=volume_cache - ) - - filter_volume_id = None - if parsed_args.volume: - filter_volume_id = utils.find_resource( - volume_client.volumes, parsed_args.volume - ).id - search_opts = { - 'name': parsed_args.name, - 'status': parsed_args.status, - 'volume_id': filter_volume_id, - 'all_tenants': parsed_args.all_projects, - } - data = volume_client.backups.list( - search_opts=search_opts, - ) - - return ( - column_headers, - ( - utils.get_item_properties( - s, - columns, - formatters={'Volume ID': VolumeIdColumnWithCache}, - ) - for s in data - ), - ) - - -class RestoreVolumeBackup(command.Command): - _description = _("Restore volume backup") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'backup', - metavar='', - help=_('Backup to restore (name or ID)'), - ) - parser.add_argument( - 'volume', - metavar='', - nargs='?', - help=_('Volume to restore to (name or ID) (default to None)'), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - backup = utils.find_resource( - volume_client.backups, - parsed_args.backup, - ) - volume_id = None - if parsed_args.volume is not None: - volume_id = utils.find_resource( - volume_client.volumes, - parsed_args.volume, - ).id - return volume_client.restores.restore(backup.id, volume_id) - - -class ShowVolumeBackup(command.ShowOne): - _description = _("Display volume backup details") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'backup', - metavar='', - help=_('Backup to display (name or ID)'), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - backup = utils.find_resource(volume_client.backups, parsed_args.backup) - backup._info.pop('links') - return zip(*sorted(backup._info.items())) diff --git a/openstackclient/volume/v1/volume_snapshot.py b/openstackclient/volume/v1/volume_snapshot.py deleted file mode 100644 index 1d9fb86b15..0000000000 --- a/openstackclient/volume/v1/volume_snapshot.py +++ /dev/null @@ -1,432 +0,0 @@ -# Copyright 2012-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 v1 Snapshot action implementations""" - -import copy -import functools -import logging - -from cliff import columns as cliff_columns -from osc_lib.cli import format_columns -from osc_lib.cli import parseractions -from osc_lib.command import command -from osc_lib import exceptions -from osc_lib import utils - -from openstackclient.i18n import _ - - -LOG = logging.getLogger(__name__) - - -class VolumeIdColumn(cliff_columns.FormattableColumn): - """Formattable column for volume ID column. - - Unlike the parent FormattableColumn class, the initializer of the - class takes volume_cache as the second argument. - osc_lib.utils.get_item_properties instantiate cliff FormattableColumn - object with a single parameter "column value", so you need to pass - a partially initialized class like - ``functools.partial(VolumeIdColumn, volume_cache)``. - """ - - def __init__(self, value, volume_cache=None): - super().__init__(value) - self._volume_cache = volume_cache or {} - - def human_readable(self): - """Return a volume name if available - - :rtype: either the volume ID or name - """ - volume_id = self._value - volume = volume_id - if volume_id in self._volume_cache.keys(): - volume = self._volume_cache[volume_id].display_name - return volume - - -class CreateVolumeSnapshot(command.ShowOne): - _description = _("Create new volume snapshot") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'snapshot_name', - metavar='', - help=_('Name of the new snapshot'), - ) - parser.add_argument( - '--volume', - metavar='', - help=_( - 'Volume to snapshot (name or ID) (default is )' - ), - ) - parser.add_argument( - '--description', - metavar='', - help=_('Description of the snapshot'), - ) - parser.add_argument( - '--force', - dest='force', - action='store_true', - default=False, - help=_( - 'Create a snapshot attached to an instance. Default is False' - ), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - volume = parsed_args.volume - if not parsed_args.volume: - volume = parsed_args.snapshot_name - volume_id = utils.find_resource(volume_client.volumes, volume).id - snapshot = volume_client.volume_snapshots.create( - volume_id, - parsed_args.force, - parsed_args.snapshot_name, - parsed_args.description, - ) - - snapshot._info.update( - { - 'properties': format_columns.DictColumn( - snapshot._info.pop('metadata') - ) - } - ) - - return zip(*sorted(snapshot._info.items())) - - -class DeleteVolumeSnapshot(command.Command): - _description = _("Delete volume snapshot(s)") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'snapshots', - metavar='', - nargs="+", - help=_('Snapshot(s) to delete (name or ID)'), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - result = 0 - - for i in parsed_args.snapshots: - try: - snapshot_id = utils.find_resource( - volume_client.volume_snapshots, i - ).id - volume_client.volume_snapshots.delete(snapshot_id) - except Exception as e: - result += 1 - LOG.error( - _( - "Failed to delete snapshot with " - "name or ID '%(snapshot)s': %(e)s" - ), - {'snapshot': i, 'e': e}, - ) - - if result > 0: - total = len(parsed_args.snapshots) - msg = _("%(result)s of %(total)s snapshots failed to delete.") % { - 'result': result, - 'total': total, - } - raise exceptions.CommandError(msg) - - -class ListVolumeSnapshot(command.Lister): - _description = _("List volume snapshots") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - '--all-projects', - action='store_true', - default=False, - help=_('Include all projects (admin only)'), - ) - parser.add_argument( - '--long', - action='store_true', - default=False, - help=_('List additional fields in output'), - ) - parser.add_argument( - '--name', - metavar='', - default=None, - help=_('Filters results by a name.'), - ) - parser.add_argument( - '--status', - metavar='', - choices=[ - 'available', - 'error', - 'creating', - 'deleting', - 'error_deleting', - ], - help=_( - "Filters results by a status. " - "('available', 'error', 'creating', 'deleting'" - " or 'error_deleting')" - ), - ) - parser.add_argument( - '--volume', - metavar='', - default=None, - help=_('Filters results by a volume (name or ID).'), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - - if parsed_args.long: - columns = [ - 'ID', - 'Display Name', - 'Display Description', - 'Status', - 'Size', - 'Created At', - 'Volume ID', - 'Metadata', - ] - column_headers = copy.deepcopy(columns) - column_headers[6] = 'Volume' - column_headers[7] = 'Properties' - else: - columns = [ - 'ID', - 'Display Name', - 'Display Description', - 'Status', - 'Size', - ] - column_headers = copy.deepcopy(columns) - - # Always update Name and Description - column_headers[1] = 'Name' - column_headers[2] = 'Description' - - # Cache the volume list - volume_cache = {} - try: - for s in volume_client.volumes.list(): - volume_cache[s.id] = s - except Exception: # noqa: S110 - # Just forget it if there's any trouble - pass - VolumeIdColumnWithCache = functools.partial( - VolumeIdColumn, volume_cache=volume_cache - ) - - volume_id = None - if parsed_args.volume: - volume_id = utils.find_resource( - volume_client.volumes, parsed_args.volume - ).id - - search_opts = { - 'all_tenants': parsed_args.all_projects, - 'display_name': parsed_args.name, - 'status': parsed_args.status, - 'volume_id': volume_id, - } - - data = volume_client.volume_snapshots.list(search_opts=search_opts) - return ( - column_headers, - ( - utils.get_item_properties( - s, - columns, - formatters={ - 'Metadata': format_columns.DictColumn, - 'Volume ID': VolumeIdColumnWithCache, - }, - ) - for s in data - ), - ) - - -class SetVolumeSnapshot(command.Command): - _description = _("Set volume snapshot properties") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'snapshot', - metavar='', - help=_('Snapshot to modify (name or ID)'), - ) - parser.add_argument( - '--name', metavar='', help=_('New snapshot name') - ) - parser.add_argument( - '--description', - metavar='', - help=_('New snapshot description'), - ) - parser.add_argument( - "--no-property", - dest="no_property", - action="store_true", - help=_( - "Remove all properties from " - "(specify both --no-property and --property to " - "remove the current properties before setting " - "new properties.)" - ), - ) - parser.add_argument( - '--property', - metavar='', - action=parseractions.KeyValueAction, - help=_( - 'Property to add/change for this snapshot ' - '(repeat option to set multiple properties)' - ), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - snapshot = utils.find_resource( - volume_client.volume_snapshots, parsed_args.snapshot - ) - - result = 0 - if parsed_args.no_property: - try: - key_list = snapshot.metadata.keys() - volume_client.volume_snapshots.delete_metadata( - snapshot.id, - list(key_list), - ) - except Exception as e: - LOG.error(_("Failed to clean snapshot properties: %s"), e) - result += 1 - - if parsed_args.property: - try: - volume_client.volume_snapshots.set_metadata( - snapshot.id, parsed_args.property - ) - except Exception as e: - LOG.error(_("Failed to set snapshot property: %s"), e) - result += 1 - - kwargs = {} - if parsed_args.name: - kwargs['display_name'] = parsed_args.name - if parsed_args.description: - kwargs['display_description'] = parsed_args.description - if kwargs: - try: - snapshot.update(**kwargs) - except Exception as e: - LOG.error( - _( - "Failed to update snapshot display name " - "or display description: %s" - ), - e, - ) - result += 1 - - if result > 0: - raise exceptions.CommandError( - _("One or more of the set operations failed") - ) - - -class ShowVolumeSnapshot(command.ShowOne): - _description = _("Display volume snapshot details") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'snapshot', - metavar='', - help=_('Snapshot to display (name or ID)'), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - snapshot = utils.find_resource( - volume_client.volume_snapshots, parsed_args.snapshot - ) - - snapshot._info.update( - { - 'properties': format_columns.DictColumn( - snapshot._info.pop('metadata') - ) - } - ) - - return zip(*sorted(snapshot._info.items())) - - -class UnsetVolumeSnapshot(command.Command): - _description = _("Unset volume snapshot properties") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'snapshot', - metavar='', - help=_('Snapshot to modify (name or ID)'), - ) - parser.add_argument( - '--property', - metavar='', - action='append', - help=_( - 'Property to remove from snapshot ' - '(repeat option to remove multiple properties)' - ), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - snapshot = utils.find_resource( - volume_client.volume_snapshots, parsed_args.snapshot - ) - - if parsed_args.property: - volume_client.volume_snapshots.delete_metadata( - snapshot.id, - parsed_args.property, - ) diff --git a/openstackclient/volume/v1/volume_transfer_request.py b/openstackclient/volume/v1/volume_transfer_request.py deleted file mode 100644 index d82584dc79..0000000000 --- a/openstackclient/volume/v1/volume_transfer_request.py +++ /dev/null @@ -1,200 +0,0 @@ -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -"""Volume v1 transfer action implementations""" - -import logging - -from osc_lib.command import command -from osc_lib import exceptions -from osc_lib import utils - -from openstackclient.i18n import _ - - -LOG = logging.getLogger(__name__) - - -class AcceptTransferRequest(command.ShowOne): - _description = _("Accept volume transfer request.") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'transfer_request', - metavar="", - help=_('Volume transfer request to accept (ID only)'), - ) - parser.add_argument( - '--auth-key', - metavar="", - help=_('Volume transfer request authentication key'), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - - try: - transfer_request_id = utils.find_resource( - volume_client.transfers, parsed_args.transfer_request - ).id - except exceptions.CommandError: - # Non-admin users will fail to lookup name -> ID so we just - # move on and attempt with the user-supplied information - transfer_request_id = parsed_args.transfer_request - - if not parsed_args.auth_key: - msg = _("argument --auth-key is required") - raise exceptions.CommandError(msg) - - transfer_accept = volume_client.transfers.accept( - transfer_request_id, - parsed_args.auth_key, - ) - transfer_accept._info.pop("links", None) - - return zip(*sorted(transfer_accept._info.items())) - - -class CreateTransferRequest(command.ShowOne): - _description = _("Create volume transfer request.") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - '--name', - metavar="", - help=_('New transfer request name (default to None)'), - ) - parser.add_argument( - 'volume', - metavar="", - help=_('Volume to transfer (name or ID)'), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - volume_id = utils.find_resource( - volume_client.volumes, - parsed_args.volume, - ).id - volume_transfer_request = volume_client.transfers.create( - volume_id, - parsed_args.name, - ) - volume_transfer_request._info.pop("links", None) - - return zip(*sorted(volume_transfer_request._info.items())) - - -class DeleteTransferRequest(command.Command): - _description = _("Delete volume transfer request(s).") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'transfer_request', - metavar="", - nargs="+", - help=_('Volume transfer request(s) to delete (name or ID)'), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - result = 0 - - for t in parsed_args.transfer_request: - try: - transfer_request_id = utils.find_resource( - volume_client.transfers, - t, - ).id - volume_client.transfers.delete(transfer_request_id) - except Exception as e: - result += 1 - LOG.error( - _( - "Failed to delete volume transfer request " - "with name or ID '%(transfer)s': %(e)s" - ) - % {'transfer': t, 'e': e} - ) - - if result > 0: - total = len(parsed_args.transfer_request) - msg = _( - "%(result)s of %(total)s volume transfer requests failed" - " to delete" - ) % {'result': result, 'total': total} - raise exceptions.CommandError(msg) - - -class ListTransferRequest(command.Lister): - _description = _("Lists all volume transfer requests.") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - '--all-projects', - dest='all_projects', - action="store_true", - default=False, - help=_('Include all projects (admin only)'), - ) - return parser - - def take_action(self, parsed_args): - columns = ['ID', 'Name', 'Volume ID'] - column_headers = ['ID', 'Name', 'Volume'] - - volume_client = self.app.client_manager.volume - - volume_transfer_result = volume_client.transfers.list( - detailed=True, - search_opts={'all_tenants': parsed_args.all_projects}, - ) - - return ( - column_headers, - ( - utils.get_item_properties(s, columns) - for s in volume_transfer_result - ), - ) - - -class ShowTransferRequest(command.ShowOne): - _description = _("Show volume transfer request details.") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'transfer_request', - metavar="", - help=_('Volume transfer request to display (name or ID)'), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - volume_transfer_request = utils.find_resource( - volume_client.transfers, - parsed_args.transfer_request, - ) - volume_transfer_request._info.pop("links", None) - - return zip(*sorted(volume_transfer_request._info.items())) diff --git a/openstackclient/volume/v1/volume_type.py b/openstackclient/volume/v1/volume_type.py deleted file mode 100644 index 5bd1cfdc95..0000000000 --- a/openstackclient/volume/v1/volume_type.py +++ /dev/null @@ -1,519 +0,0 @@ -# Copyright 2012-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 v1 Type action implementations""" - -import functools -import logging - -from cliff import columns as cliff_columns -from osc_lib.cli import format_columns -from osc_lib.cli import parseractions -from osc_lib.command import command -from osc_lib import exceptions -from osc_lib import utils - -from openstackclient.i18n import _ - - -LOG = logging.getLogger(__name__) - - -class EncryptionInfoColumn(cliff_columns.FormattableColumn): - """Formattable column for encryption info column. - - Unlike the parent FormattableColumn class, the initializer of the - class takes encryption_data as the second argument. - osc_lib.utils.get_item_properties instantiate cliff FormattableColumn - object with a single parameter "column value", so you need to pass - a partially initialized class like - ``functools.partial(EncryptionInfoColumn encryption_data)``. - """ - - def __init__(self, value, encryption_data=None): - super().__init__(value) - self._encryption_data = encryption_data or {} - - def _get_encryption_info(self): - type_id = self._value - return self._encryption_data.get(type_id) - - def human_readable(self): - encryption_info = self._get_encryption_info() - if encryption_info: - return utils.format_dict(encryption_info) - else: - return '-' - - def machine_readable(self): - return self._get_encryption_info() - - -def _create_encryption_type(volume_client, volume_type, parsed_args): - if not parsed_args.encryption_provider: - msg = _( - "'--encryption-provider' should be specified while " - "creating a new encryption type" - ) - raise exceptions.CommandError(msg) - # set the default of control location while creating - control_location = 'front-end' - if parsed_args.encryption_control_location: - control_location = parsed_args.encryption_control_location - body = { - 'provider': parsed_args.encryption_provider, - 'cipher': parsed_args.encryption_cipher, - 'key_size': parsed_args.encryption_key_size, - 'control_location': control_location, - } - encryption = volume_client.volume_encryption_types.create( - volume_type, body - ) - return encryption - - -class CreateVolumeType(command.ShowOne): - _description = _("Create new volume type") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'name', - metavar='', - help=_('Volume type name'), - ) - parser.add_argument( - '--property', - metavar='', - action=parseractions.KeyValueAction, - help=_( - 'Set a property on this volume type ' - '(repeat option to set multiple properties)' - ), - ) - # TODO(Huanxuan Ao): Add choices for each "--encryption-*" option. - parser.add_argument( - '--encryption-provider', - metavar='', - help=_( - 'Set the encryption provider format for ' - 'this volume type (e.g "luks" or "plain") (admin only) ' - '(This option is required when setting encryption type ' - 'of a volume. Consider using other encryption options ' - 'such as: "--encryption-cipher", "--encryption-key-size" ' - 'and "--encryption-control-location")' - ), - ) - parser.add_argument( - '--encryption-cipher', - metavar='', - help=_( - 'Set the encryption algorithm or mode for this ' - 'volume type (e.g "aes-xts-plain64") (admin only)' - ), - ) - parser.add_argument( - '--encryption-key-size', - metavar='', - type=int, - help=_( - 'Set the size of the encryption key of this ' - 'volume type (e.g "128" or "256") (admin only)' - ), - ) - parser.add_argument( - '--encryption-control-location', - metavar='', - choices=['front-end', 'back-end'], - help=_( - 'Set the notional service where the encryption is ' - 'performed ("front-end" or "back-end") (admin only) ' - '(The default value for this option is "front-end" ' - 'when setting encryption type of a volume. Consider ' - 'using other encryption options such as: ' - '"--encryption-cipher", "--encryption-key-size" and ' - '"--encryption-provider")' - ), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - volume_type = volume_client.volume_types.create(parsed_args.name) - volume_type._info.pop('extra_specs') - if parsed_args.property: - result = volume_type.set_keys(parsed_args.property) - volume_type._info.update( - {'properties': format_columns.DictColumn(result)} - ) - if ( - parsed_args.encryption_provider - or parsed_args.encryption_cipher - or parsed_args.encryption_key_size - or parsed_args.encryption_control_location - ): - try: - # create new encryption - encryption = _create_encryption_type( - volume_client, volume_type, parsed_args - ) - except Exception as e: - LOG.error( - _( - "Failed to set encryption information for this " - "volume type: %s" - ), - e, - ) - # add encryption info in result - encryption._info.pop("volume_type_id", None) - volume_type._info.update( - {'encryption': format_columns.DictColumn(encryption._info)} - ) - volume_type._info.pop("os-volume-type-access:is_public", None) - - return zip(*sorted(volume_type._info.items())) - - -class DeleteVolumeType(command.Command): - _description = _("Delete volume type(s)") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'volume_types', - metavar='', - nargs='+', - help=_('Volume type(s) to delete (name or ID)'), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - result = 0 - - for volume_type in parsed_args.volume_types: - try: - vol_type = utils.find_resource( - volume_client.volume_types, volume_type - ) - - volume_client.volume_types.delete(vol_type) - except Exception as e: - result += 1 - LOG.error( - _( - "Failed to delete volume type with " - "name or ID '%(volume_type)s': %(e)s" - ) - % {'volume_type': volume_type, 'e': e} - ) - - if result > 0: - total = len(parsed_args.volume_types) - msg = _( - "%(result)s of %(total)s volume types failed to delete." - ) % {'result': result, 'total': total} - raise exceptions.CommandError(msg) - - -class ListVolumeType(command.Lister): - _description = _("List volume types") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - '--long', - action='store_true', - default=False, - help=_('List additional fields in output'), - ) - parser.add_argument( - "--encryption-type", - action="store_true", - help=_( - "Display encryption information for each volume type " - "(admin only)" - ), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - if parsed_args.long: - columns = ['ID', 'Name', 'Is Public', 'Extra Specs'] - column_headers = ['ID', 'Name', 'Is Public', 'Properties'] - else: - columns = ['ID', 'Name', 'Is Public'] - column_headers = ['ID', 'Name', 'Is Public'] - data = volume_client.volume_types.list() - - formatters = {'Extra Specs': format_columns.DictColumn} - - if parsed_args.encryption_type: - encryption = {} - for d in volume_client.volume_encryption_types.list(): - volume_type_id = d._info['volume_type_id'] - # remove some redundant information - del_key = [ - 'deleted', - 'created_at', - 'updated_at', - 'deleted_at', - 'volume_type_id', - ] - for key in del_key: - d._info.pop(key, None) - # save the encryption information with their volume type ID - encryption[volume_type_id] = d._info - # We need to get volume type ID, then show encryption - # information according to the ID, so use "id" to keep - # difference to the real "ID" column. - columns += ['id'] - column_headers += ['Encryption'] - - _EncryptionInfoColumn = functools.partial( - EncryptionInfoColumn, encryption_data=encryption - ) - formatters['id'] = _EncryptionInfoColumn - - return ( - column_headers, - ( - utils.get_item_properties( - s, - columns, - formatters=formatters, - ) - for s in data - ), - ) - - -class SetVolumeType(command.Command): - _description = _("Set volume type properties") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'volume_type', - metavar='', - help=_('Volume type to modify (name or ID)'), - ) - parser.add_argument( - '--property', - metavar='', - action=parseractions.KeyValueAction, - help=_( - 'Set a property on this volume type ' - '(repeat option to set multiple properties)' - ), - ) - # TODO(Huanxuan Ao): Add choices for each "--encryption-*" option. - parser.add_argument( - '--encryption-provider', - metavar='', - help=_( - 'Set the encryption provider format for ' - 'this volume type (e.g "luks" or "plain") (admin only) ' - '(This option is required when setting encryption type ' - 'of a volume. Consider using other encryption options ' - 'such as: "--encryption-cipher", "--encryption-key-size" ' - 'and "--encryption-control-location")' - ), - ) - parser.add_argument( - '--encryption-cipher', - metavar='', - help=_( - 'Set the encryption algorithm or mode for this ' - 'volume type (e.g "aes-xts-plain64") (admin only)' - ), - ) - parser.add_argument( - '--encryption-key-size', - metavar='', - type=int, - help=_( - 'Set the size of the encryption key of this ' - 'volume type (e.g "128" or "256") (admin only)' - ), - ) - parser.add_argument( - '--encryption-control-location', - metavar='', - choices=['front-end', 'back-end'], - help=_( - 'Set the notional service where the encryption is ' - 'performed ("front-end" or "back-end") (admin only) ' - '(The default value for this option is "front-end" ' - 'when setting encryption type of a volume. Consider ' - 'using other encryption options such as: ' - '"--encryption-cipher", "--encryption-key-size" and ' - '"--encryption-provider")' - ), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - volume_type = utils.find_resource( - volume_client.volume_types, parsed_args.volume_type - ) - - result = 0 - if parsed_args.property: - try: - volume_type.set_keys(parsed_args.property) - except Exception as e: - LOG.error(_("Failed to set volume type property: %s"), e) - result += 1 - - if ( - parsed_args.encryption_provider - or parsed_args.encryption_cipher - or parsed_args.encryption_key_size - or parsed_args.encryption_control_location - ): - try: - _create_encryption_type( - volume_client, volume_type, parsed_args - ) - except Exception as e: - LOG.error( - _( - "Failed to set encryption information for this " - "volume type: %s" - ), - e, - ) - result += 1 - - if result > 0: - raise exceptions.CommandError( - _("Command Failed: One or more of the operations failed") - ) - - -class ShowVolumeType(command.ShowOne): - _description = _("Display volume type details") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - "volume_type", - metavar="", - help=_("Volume type to display (name or ID)"), - ) - parser.add_argument( - "--encryption-type", - action="store_true", - help=_( - "Display encryption information of this volume type " - "(admin only)" - ), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - volume_type = utils.find_resource( - volume_client.volume_types, parsed_args.volume_type - ) - properties = format_columns.DictColumn( - volume_type._info.pop('extra_specs') - ) - volume_type._info.update({'properties': properties}) - if parsed_args.encryption_type: - # show encryption type information for this volume type - try: - encryption = volume_client.volume_encryption_types.get( - volume_type.id - ) - encryption._info.pop("volume_type_id", None) - volume_type._info.update( - {'encryption': format_columns.DictColumn(encryption._info)} - ) - except Exception as e: - LOG.error( - _( - "Failed to display the encryption information " - "of this volume type: %s" - ), - e, - ) - volume_type._info.pop("os-volume-type-access:is_public", None) - return zip(*sorted(volume_type._info.items())) - - -class UnsetVolumeType(command.Command): - _description = _("Unset volume type properties") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'volume_type', - metavar='', - help=_('Volume type to modify (name or ID)'), - ) - parser.add_argument( - '--property', - metavar='', - action='append', - help=_( - 'Remove a property from this volume type ' - '(repeat option to remove multiple properties)' - ), - ) - parser.add_argument( - "--encryption-type", - action="store_true", - help=_( - "Remove the encryption type for this volume type (admin only)" - ), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - volume_type = utils.find_resource( - volume_client.volume_types, - parsed_args.volume_type, - ) - - result = 0 - if parsed_args.property: - try: - volume_type.unset_keys(parsed_args.property) - except Exception as e: - LOG.error(_("Failed to unset volume type property: %s"), e) - result += 1 - if parsed_args.encryption_type: - try: - volume_client.volume_encryption_types.delete(volume_type) - except Exception as e: - LOG.error( - _( - "Failed to remove the encryption type for this " - "volume type: %s" - ), - e, - ) - result += 1 - - if result > 0: - raise exceptions.CommandError( - _("Command Failed: One or more of the operations failed") - ) diff --git a/releasenotes/notes/remove-volume-v1-commands-bfa14e9cae54929f.yaml b/releasenotes/notes/remove-volume-v1-commands-bfa14e9cae54929f.yaml new file mode 100644 index 0000000000..0aa6929b07 --- /dev/null +++ b/releasenotes/notes/remove-volume-v1-commands-bfa14e9cae54929f.yaml @@ -0,0 +1,35 @@ +--- +upgrade: + - | + Support for the Block Storage (Cinder) v1 API has been officially removed + as it had been broken for some time. If you haven't noticed then you likely + don't need to do anything. However, in the unlikely event that your cloud + is using the Block Storage v1 API - or incorrectly advertises the Block + Storage v1 API - consider overriding the API version to use v2 as this + behaves very similarly. It may also be necessary to set an endpoint + override for the Block Storage API if your clouds service catalog is not + configured correctly. For example: + + .. code-block:: yaml + + example: + regions: + - name: regionOne + values: + block_storage_endpoint_override: 'https://blockstorage.api.cloud.example/' + volume_api_version: 2 + + If using a public cloud provider, there may also be a profile already + published that sets these. These are listed in the `Vendor Support`__ + doc. For example: + + .. code-block:: yaml + + example: + profile: rackspace + + Alternatively, consider use versions of OSC < 3.19 and python-cinderclient + < 5.0 (both Stein), since these were the last versions to fully support + Cinder v1. + + .. __: https://docs.openstack.org/openstacksdk/latest/user/config/vendor-support.html diff --git a/setup.cfg b/setup.cfg index 43d9588bfa..a770b4b62d 100644 --- a/setup.cfg +++ b/setup.cfg @@ -635,53 +635,6 @@ openstack.object_store.v1 = object_show = openstackclient.object.v1.object:ShowObject object_unset = openstackclient.object.v1.object:UnsetObject -openstack.volume.v1 = - volume_create = openstackclient.volume.v1.volume:CreateVolume - volume_delete = openstackclient.volume.v1.volume:DeleteVolume - volume_list = openstackclient.volume.v1.volume:ListVolume - volume_migrate = openstackclient.volume.v1.volume:MigrateVolume - volume_set = openstackclient.volume.v1.volume:SetVolume - volume_show = openstackclient.volume.v1.volume:ShowVolume - volume_unset = openstackclient.volume.v1.volume:UnsetVolume - - volume_backup_create = openstackclient.volume.v1.volume_backup:CreateVolumeBackup - volume_backup_delete = openstackclient.volume.v1.volume_backup:DeleteVolumeBackup - volume_backup_list = openstackclient.volume.v1.volume_backup:ListVolumeBackup - volume_backup_restore = openstackclient.volume.v1.volume_backup:RestoreVolumeBackup - volume_backup_show = openstackclient.volume.v1.volume_backup:ShowVolumeBackup - - volume_snapshot_create = openstackclient.volume.v1.volume_snapshot:CreateVolumeSnapshot - volume_snapshot_delete = openstackclient.volume.v1.volume_snapshot:DeleteVolumeSnapshot - volume_snapshot_list = openstackclient.volume.v1.volume_snapshot:ListVolumeSnapshot - volume_snapshot_set = openstackclient.volume.v1.volume_snapshot:SetVolumeSnapshot - volume_snapshot_show = openstackclient.volume.v1.volume_snapshot:ShowVolumeSnapshot - volume_snapshot_unset = openstackclient.volume.v1.volume_snapshot:UnsetVolumeSnapshot - - volume_type_create = openstackclient.volume.v1.volume_type:CreateVolumeType - volume_type_delete = openstackclient.volume.v1.volume_type:DeleteVolumeType - volume_type_list = openstackclient.volume.v1.volume_type:ListVolumeType - volume_type_set = openstackclient.volume.v1.volume_type:SetVolumeType - volume_type_show = openstackclient.volume.v1.volume_type:ShowVolumeType - volume_type_unset = openstackclient.volume.v1.volume_type:UnsetVolumeType - - volume_qos_associate = openstackclient.volume.v1.qos_specs:AssociateQos - volume_qos_create = openstackclient.volume.v1.qos_specs:CreateQos - volume_qos_delete = openstackclient.volume.v1.qos_specs:DeleteQos - volume_qos_disassociate = openstackclient.volume.v1.qos_specs:DisassociateQos - volume_qos_list = openstackclient.volume.v1.qos_specs:ListQos - volume_qos_set = openstackclient.volume.v1.qos_specs:SetQos - volume_qos_show = openstackclient.volume.v1.qos_specs:ShowQos - volume_qos_unset = openstackclient.volume.v1.qos_specs:UnsetQos - - volume_service_list = openstackclient.volume.v1.service:ListService - volume_service_set = openstackclient.volume.v1.service:SetService - - volume_transfer_request_accept = openstackclient.volume.v1.volume_transfer_request:AcceptTransferRequest - volume_transfer_request_create = openstackclient.volume.v1.volume_transfer_request:CreateTransferRequest - volume_transfer_request_delete = openstackclient.volume.v1.volume_transfer_request:DeleteTransferRequest - volume_transfer_request_list = openstackclient.volume.v1.volume_transfer_request:ListTransferRequest - volume_transfer_request_show = openstackclient.volume.v1.volume_transfer_request:ShowTransferRequest - openstack.volume.v2 = consistency_group_add_volume = openstackclient.volume.v2.consistency_group:AddVolumeToConsistencyGroup consistency_group_create = openstackclient.volume.v2.consistency_group:CreateConsistencyGroup