From 1cc397991d5f089b348f963ab12f9b0200c289f4 Mon Sep 17 00:00:00 2001 From: Rajat Dhasmana Date: Mon, 27 May 2024 12:31:53 +0530 Subject: [PATCH] Add support for default volume types This patch adds support for default volume type operations: 1. Set 2. Unset 3. List 4. Get Change-Id: Iac22d3c989b2b19fe4f167011aec1304becc4f34 --- doc/source/user/proxies/block_storage_v3.rst | 8 ++ openstack/block_storage/v3/_proxy.py | 78 +++++++++++++++++++ openstack/block_storage/v3/default_type.py | 56 +++++++++++++ .../block_storage/v3/test_default_type.py | 66 ++++++++++++++++ .../block_storage/v3/test_default_type.py | 55 +++++++++++++ ...default-type-support-aaa1e54b8bd16d86.yaml | 9 +++ 6 files changed, 272 insertions(+) create mode 100644 openstack/block_storage/v3/default_type.py create mode 100644 openstack/tests/functional/block_storage/v3/test_default_type.py create mode 100644 openstack/tests/unit/block_storage/v3/test_default_type.py create mode 100644 releasenotes/notes/add-default-type-support-aaa1e54b8bd16d86.yaml diff --git a/doc/source/user/proxies/block_storage_v3.rst b/doc/source/user/proxies/block_storage_v3.rst index 61657eb34..0498baffb 100644 --- a/doc/source/user/proxies/block_storage_v3.rst +++ b/doc/source/user/proxies/block_storage_v3.rst @@ -173,3 +173,11 @@ Helpers .. autoclass:: openstack.block_storage.v3._proxy.Proxy :noindex: :members: wait_for_status, wait_for_delete + +Default Volume Types +^^^^^^^^^^^^^^^^^^^^ + +.. autoclass:: openstack.block_storage.v3._proxy.Proxy + :noindex: + :members: default_types, show_default_type, set_default_type, + unset_default_type diff --git a/openstack/block_storage/v3/_proxy.py b/openstack/block_storage/v3/_proxy.py index a648eb9c3..faad7036d 100644 --- a/openstack/block_storage/v3/_proxy.py +++ b/openstack/block_storage/v3/_proxy.py @@ -19,6 +19,7 @@ from openstack.block_storage.v3 import availability_zone from openstack.block_storage.v3 import backup as _backup from openstack.block_storage.v3 import block_storage_summary as _summary from openstack.block_storage.v3 import capabilities as _capabilities +from openstack.block_storage.v3 import default_type as _default_type from openstack.block_storage.v3 import extension as _extension from openstack.block_storage.v3 import group as _group from openstack.block_storage.v3 import group_snapshot as _group_snapshot @@ -535,6 +536,83 @@ class Proxy(_base_proxy.BaseBlockStorageProxy): return self._update(_type.TypeEncryption, encryption, **attrs) + # ====== DEFAULT TYPES ====== + + def default_types(self): + """Lists default types. + + :returns: List of default types associated to projects. + """ + # This is required since previously default types did not accept + # URL with project ID + if not utils.supports_microversion(self, '3.67'): + raise exceptions.SDKException( + 'List default types require at least microversion 3.67' + ) + + return self._list(_default_type.DefaultType) + + def show_default_type(self, project): + """Show default type for a project. + + :param project: The value can be either the ID of a project or a + :class:`~openstack.identity.v3.project.Project` instance. + + :returns: Default type associated to the project. + """ + # This is required since previously default types did not accept + # URL with project ID + if not utils.supports_microversion(self, '3.67'): + raise exceptions.SDKException( + 'Show default type require at least microversion 3.67' + ) + + project_id = resource.Resource._get_id(project) + return self._get(_default_type.DefaultType, project_id) + + def set_default_type(self, project, type): + """Set default type for a project. + + :param project: The value can be either the ID of a project or a + :class:`~openstack.identity.v3.project.Project` instance. + :param type: The value can be either the ID of a type or a + :class:`~openstack.block_storage.v3.type.Type` instance. + + :returns: Dictionary of project ID and it's associated default type. + """ + # This is required since previously default types did not accept + # URL with project ID + if not utils.supports_microversion(self, '3.67'): + raise exceptions.SDKException( + 'Set default type require at least microversion 3.67' + ) + + type_id = resource.Resource._get_id(type) + project_id = resource.Resource._get_id(project) + return self._create( + _default_type.DefaultType, + id=project_id, + volume_type_id=type_id, + ) + + def unset_default_type(self, project): + """Unset default type for a project. + + :param project: The value can be either the ID of a project or a + :class:`~openstack.identity.v3.project.Project` instance. + + :returns: ``None`` + """ + # This is required since previously default types did not accept + # URL with project ID + if not utils.supports_microversion(self, '3.67'): + raise exceptions.SDKException( + 'Unset default type require at least microversion 3.67' + ) + + project_id = resource.Resource._get_id(project) + self._delete(_default_type.DefaultType, project_id) + # ====== VOLUMES ====== def get_volume(self, volume): """Get a single volume diff --git a/openstack/block_storage/v3/default_type.py b/openstack/block_storage/v3/default_type.py new file mode 100644 index 000000000..c2dc5a36f --- /dev/null +++ b/openstack/block_storage/v3/default_type.py @@ -0,0 +1,56 @@ +# 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 openstack import resource + + +class DefaultType(resource.Resource): + resource_key = "default_type" + resources_key = "default_types" + base_path = "/default-types" + + # capabilities + allow_fetch = True + allow_create = True + allow_delete = True + allow_list = True + + # Create and update use the same PUT API + create_requires_id = True + create_method = 'PUT' + + _max_microversion = "3.67" + + # Properties + #: The UUID of the project. + project_id = resource.Body("project_id") + #: The UUID for an existing volume type. + volume_type_id = resource.Body("volume_type_id") + + def _prepare_request_body( + self, + patch, + prepend_key, + *, + resource_request_key=None, + ): + body = self._body.dirty + # Set operation expects volume_type instead of + # volume_type_id + if body.get('volume_type_id'): + body['volume_type'] = body.pop('volume_type_id') + # When setting a default type, we want the ID to be + # appended in URL but not in the request body + if body.get('id'): + body.pop('id') + body = {self.resource_key: body} + return body diff --git a/openstack/tests/functional/block_storage/v3/test_default_type.py b/openstack/tests/functional/block_storage/v3/test_default_type.py new file mode 100644 index 000000000..82458dd84 --- /dev/null +++ b/openstack/tests/functional/block_storage/v3/test_default_type.py @@ -0,0 +1,66 @@ +# 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 openstack.block_storage.v3 import default_type as _default_type +from openstack.tests.functional.block_storage.v3 import base + + +class TestDefaultType(base.BaseBlockStorageTest): + def setUp(self): + super().setUp() + if not self._op_name: + self.skip("Operator cloud must be set for this test") + self._set_operator_cloud(block_storage_api_version='3.67') + self.PROJECT_ID = self.create_temporary_project().id + + def test_default_type(self): + # Create a volume type + type_name = self.getUniqueString() + volume_type_id = self.operator_cloud.block_storage.create_type( + name=type_name, + ).id + + # Set default type for a project + default_type = self.conn.block_storage.set_default_type( + self.PROJECT_ID, + volume_type_id, + ) + self.assertIsInstance(default_type, _default_type.DefaultType) + + # Show default type for a project + default_type = self.conn.block_storage.show_default_type( + self.PROJECT_ID + ) + self.assertIsInstance(default_type, _default_type.DefaultType) + self.assertEqual(volume_type_id, default_type.volume_type_id) + + # List all default types + default_types = self.conn.block_storage.default_types() + for default_type in default_types: + self.assertIsInstance(default_type, _default_type.DefaultType) + # There could be existing default types set in the environment + # Just verify that the default type we have set is correct + if self.PROJECT_ID == default_type.project_id: + self.assertEqual(volume_type_id, default_type.volume_type_id) + + # Unset default type for a project + default_type = self.conn.block_storage.unset_default_type( + self.PROJECT_ID + ) + self.assertIsNone(default_type) + + # Delete the volume type + vol_type = self.operator_cloud.block_storage.delete_type( + volume_type_id, + ignore_missing=False, + ) + self.assertIsNone(vol_type) diff --git a/openstack/tests/unit/block_storage/v3/test_default_type.py b/openstack/tests/unit/block_storage/v3/test_default_type.py new file mode 100644 index 000000000..447eaac94 --- /dev/null +++ b/openstack/tests/unit/block_storage/v3/test_default_type.py @@ -0,0 +1,55 @@ +# 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 keystoneauth1 import adapter + +from openstack.block_storage.v3 import default_type +from openstack.tests.unit import base + + +PROJECT_ID = 'd5e678b5-f88b-411c-876b-f6ec2ba999bf' +VOLUME_TYPE_ID = 'adef1cf8-736e-4b62-a2db-f8b6b6c1d953' + +DEFAULT_TYPE = { + 'project_id': PROJECT_ID, + 'volume_type_id': VOLUME_TYPE_ID, +} + + +class TestDefaultType(base.TestCase): + def setUp(self): + super().setUp() + self.resp = mock.Mock() + self.resp.body = None + self.resp.status_code = 200 + self.resp.json = mock.Mock(return_value=self.resp.body) + self.sess = mock.Mock(spec=adapter.Adapter) + self.sess.default_microversion = '3.67' + self.sess.post = mock.Mock(return_value=self.resp) + self.sess._get_connection = mock.Mock(return_value=self.cloud) + + def test_basic(self): + sot = default_type.DefaultType(**DEFAULT_TYPE) + self.assertEqual("default_type", sot.resource_key) + self.assertEqual("default_types", sot.resources_key) + self.assertEqual("/default-types", sot.base_path) + self.assertTrue(sot.allow_create) + self.assertTrue(sot.allow_fetch) + self.assertTrue(sot.allow_delete) + self.assertTrue(sot.allow_list) + + def test_create(self): + sot = default_type.DefaultType(**DEFAULT_TYPE) + self.assertEqual(DEFAULT_TYPE["project_id"], sot.project_id) + self.assertEqual(DEFAULT_TYPE["volume_type_id"], sot.volume_type_id) diff --git a/releasenotes/notes/add-default-type-support-aaa1e54b8bd16d86.yaml b/releasenotes/notes/add-default-type-support-aaa1e54b8bd16d86.yaml new file mode 100644 index 000000000..b2ea6adde --- /dev/null +++ b/releasenotes/notes/add-default-type-support-aaa1e54b8bd16d86.yaml @@ -0,0 +1,9 @@ +--- +features: + - | + Added support for the following operations: + + * Set default volume type + * Get default volume type + * List default volume type + * Unset default volume type