From f3df2095ac5aec23ab4fbd9f9af8f170d9e00220 Mon Sep 17 00:00:00 2001 From: Artem Goncharov Date: Wed, 19 May 2021 09:58:56 +0200 Subject: [PATCH] Introduce QuotaSet in block storage service Start using common QuotaSet class in the block storage service. Change-Id: I741c74e596571dae6d28e305b89aafa4b51f4f3c --- doc/source/user/proxies/block_storage_v2.rst | 7 ++ doc/source/user/proxies/block_storage_v3.rst | 7 ++ .../user/resources/block_storage/index.rst | 2 + .../resources/block_storage/v2/quota_set.rst | 12 +++ .../resources/block_storage/v3/quota_set.rst | 12 +++ openstack/block_storage/v2/_proxy.py | 70 +++++++++++++++ openstack/block_storage/v2/quota_set.py | 33 +++++++ openstack/block_storage/v3/_proxy.py | 70 +++++++++++++++ openstack/block_storage/v3/quota_set.py | 33 +++++++ .../tests/unit/block_storage/v2/test_proxy.py | 85 +++++++++++++++++++ .../tests/unit/block_storage/v3/test_proxy.py | 85 +++++++++++++++++++ .../block-storage-qs-0e3b69be2e709b65.yaml | 4 + 12 files changed, 420 insertions(+) create mode 100644 doc/source/user/resources/block_storage/v2/quota_set.rst create mode 100644 doc/source/user/resources/block_storage/v3/quota_set.rst create mode 100644 openstack/block_storage/v2/quota_set.py create mode 100644 openstack/block_storage/v3/quota_set.py create mode 100644 releasenotes/notes/block-storage-qs-0e3b69be2e709b65.yaml diff --git a/doc/source/user/proxies/block_storage_v2.rst b/doc/source/user/proxies/block_storage_v2.rst index d169611ca..e3d650969 100644 --- a/doc/source/user/proxies/block_storage_v2.rst +++ b/doc/source/user/proxies/block_storage_v2.rst @@ -46,3 +46,10 @@ Stats Operations .. autoclass:: openstack.block_storage.v2._proxy.Proxy :noindex: :members: backend_pools + +QuotaSet Operations +^^^^^^^^^^^^^^^^^^^ +.. autoclass:: openstack.block_storage.v2._proxy.Proxy + :noindex: + :members: get_quota_set, get_quota_set_defaults, + revert_quota_set, update_quota_set diff --git a/doc/source/user/proxies/block_storage_v3.rst b/doc/source/user/proxies/block_storage_v3.rst index bfbbed2fb..a7a1e7747 100644 --- a/doc/source/user/proxies/block_storage_v3.rst +++ b/doc/source/user/proxies/block_storage_v3.rst @@ -46,3 +46,10 @@ Stats Operations .. autoclass:: openstack.block_storage.v3._proxy.Proxy :noindex: :members: backend_pools + +QuotaSet Operations +^^^^^^^^^^^^^^^^^^^ +.. autoclass:: openstack.block_storage.v3._proxy.Proxy + :noindex: + :members: get_quota_set, get_quota_set_defaults, + revert_quota_set, update_quota_set diff --git a/doc/source/user/resources/block_storage/index.rst b/doc/source/user/resources/block_storage/index.rst index fdcf654f1..7f9e16d21 100644 --- a/doc/source/user/resources/block_storage/index.rst +++ b/doc/source/user/resources/block_storage/index.rst @@ -5,11 +5,13 @@ Block Storage Resources :maxdepth: 1 v2/backup + v2/quota_set v2/snapshot v2/type v2/volume v3/backup + v3/quota_set v3/snapshot v3/type v3/volume diff --git a/doc/source/user/resources/block_storage/v2/quota_set.rst b/doc/source/user/resources/block_storage/v2/quota_set.rst new file mode 100644 index 000000000..35211aec4 --- /dev/null +++ b/doc/source/user/resources/block_storage/v2/quota_set.rst @@ -0,0 +1,12 @@ +openstack.block_storage.v2.quota_set +==================================== + +.. automodule:: openstack.block_storage.v2.quota_set + +The QuotaSet Class +------------------ + +The ``QuotaSet`` class inherits from :class:`~openstack.resource.Resource`. + +.. autoclass:: openstack.block_storage.v2.quota_set.QuotaSet + :members: diff --git a/doc/source/user/resources/block_storage/v3/quota_set.rst b/doc/source/user/resources/block_storage/v3/quota_set.rst new file mode 100644 index 000000000..69a287b9c --- /dev/null +++ b/doc/source/user/resources/block_storage/v3/quota_set.rst @@ -0,0 +1,12 @@ +openstack.block_storage.v3.quota_set +==================================== + +.. automodule:: openstack.block_storage.v3.quota_set + +The QuotaSet Class +------------------ + +The ``QuotaSet`` class inherits from :class:`~openstack.resource.Resource`. + +.. autoclass:: openstack.block_storage.v3.quota_set.QuotaSet + :members: diff --git a/openstack/block_storage/v2/_proxy.py b/openstack/block_storage/v2/_proxy.py index e8fe5bdec..72c6acf1c 100644 --- a/openstack/block_storage/v2/_proxy.py +++ b/openstack/block_storage/v2/_proxy.py @@ -12,10 +12,12 @@ from openstack.block_storage import _base_proxy from openstack.block_storage.v2 import backup as _backup +from openstack.block_storage.v2 import quota_set as _quota_set from openstack.block_storage.v2 import snapshot as _snapshot from openstack.block_storage.v2 import stats as _stats from openstack.block_storage.v2 import type as _type from openstack.block_storage.v2 import volume as _volume +from openstack.identity.v3 import project as _project from openstack import resource @@ -554,3 +556,71 @@ class Proxy(_base_proxy.BaseBlockStorageProxy): to delete failed to occur in the specified seconds. """ return resource.wait_for_delete(self, res, interval, wait) + + def get_quota_set(self, project, usage=False, **query): + """Show QuotaSet information for the project + + :param project: ID or instance of + :class:`~openstack.identity.project.Project` of the project for + which the quota should be retrieved + :param bool usage: When set to ``True`` quota usage and reservations + would be filled. + :param dict query: Additional query parameters to use. + + :returns: One :class:`~openstack.block_storage.v2.quota_set.QuotaSet` + :raises: :class:`~openstack.exceptions.ResourceNotFound` + when no resource can be found. + """ + project = self._get_resource(_project.Project, project) + res = self._get_resource( + _quota_set.QuotaSet, None, project_id=project.id) + return res.fetch( + self, usage=usage, **query) + + def get_quota_set_defaults(self, project): + """Show QuotaSet defaults for the project + + :param project: ID or instance of + :class:`~openstack.identity.project.Project` of the project for + which the quota should be retrieved + + :returns: One :class:`~openstack.block_storage.v2.quota_set.QuotaSet` + :raises: :class:`~openstack.exceptions.ResourceNotFound` + when no resource can be found. + """ + project = self._get_resource(_project.Project, project) + res = self._get_resource( + _quota_set.QuotaSet, None, project_id=project.id) + return res.fetch( + self, base_path='/os-quota-sets/defaults') + + def revert_quota_set(self, project, **query): + """Reset Quota for the project/user. + + :param project: ID or instance of + :class:`~openstack.identity.project.Project` of the project for + which the quota should be resetted. + :param dict query: Additional parameters to be used. + + :returns: ``None`` + """ + project = self._get_resource(_project.Project, project) + res = self._get_resource( + _quota_set.QuotaSet, None, project_id=project.id) + + return res.delete(self, **query) + + def update_quota_set(self, quota_set, query=None, **attrs): + """Update a QuotaSet. + + :param quota_set: Either the ID of a quota_set or a + :class:`~openstack.block_storage.v2.quota_set.QuotaSet` instance. + :param dict query: Optional parameters to be used with update call. + :attrs kwargs: The attributes to update on the QuotaSet represented + by ``quota_set``. + + :returns: The updated QuotaSet + :rtype: :class:`~openstack.block_storage.v2.quota_set.QuotaSet` + """ + res = self._get_resource(_quota_set.QuotaSet, quota_set, **attrs) + return res.commit(self, **query) diff --git a/openstack/block_storage/v2/quota_set.py b/openstack/block_storage/v2/quota_set.py new file mode 100644 index 000000000..0ef3b51f9 --- /dev/null +++ b/openstack/block_storage/v2/quota_set.py @@ -0,0 +1,33 @@ +# 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.common import quota_set +from openstack import resource + + +class QuotaSet(quota_set.QuotaSet): + + #: Properties + #: The size (GB) of backups that are allowed for each project. + backup_gigabytes = resource.Body('backup_gigabytes', type=int) + #: The number of backups that are allowed for each project. + backups = resource.Body('backups', type=int) + #: The size (GB) of volumes and snapshots that are allowed for each + #: project. + gigabytes = resource.Body('gigabytes', type=int) + #: The number of groups that are allowed for each project. + groups = resource.Body('groups', type=int) + #: The size (GB) of volumes in request that are allowed for each volume. + per_volume_gigabytes = resource.Body('per_volume_gigabytes', type=int) + #: The number of snapshots that are allowed for each project. + snapshots = resource.Body('snapshots', type=int) + #: The number of volumes that are allowed for each project. + volumes = resource.Body('volumes', type=int) diff --git a/openstack/block_storage/v3/_proxy.py b/openstack/block_storage/v3/_proxy.py index 659b8b787..cc8384ed3 100644 --- a/openstack/block_storage/v3/_proxy.py +++ b/openstack/block_storage/v3/_proxy.py @@ -17,12 +17,14 @@ from openstack.block_storage.v3 import capabilities as _capabilities from openstack.block_storage.v3 import extension as _extension from openstack.block_storage.v3 import group_type as _group_type from openstack.block_storage.v3 import limits as _limits +from openstack.block_storage.v3 import quota_set as _quota_set from openstack.block_storage.v3 import resource_filter as _resource_filter from openstack.block_storage.v3 import snapshot as _snapshot from openstack.block_storage.v3 import stats as _stats from openstack.block_storage.v3 import type as _type from openstack.block_storage.v3 import volume as _volume from openstack import exceptions +from openstack.identity.v3 import project as _project from openstack import resource @@ -1027,6 +1029,74 @@ class Proxy(_base_proxy.BaseBlockStorageProxy): """ return self._list(_extension.Extension) + def get_quota_set(self, project, usage=False, **query): + """Show QuotaSet information for the project + + :param project: ID or instance of + :class:`~openstack.identity.project.Project` of the project for + which the quota should be retrieved + :param bool usage: When set to ``True`` quota usage and reservations + would be filled. + :param dict query: Additional query parameters to use. + + :returns: One :class:`~openstack.block_storage.v3.quota_set.QuotaSet` + :raises: :class:`~openstack.exceptions.ResourceNotFound` + when no resource can be found. + """ + project = self._get_resource(_project.Project, project) + res = self._get_resource( + _quota_set.QuotaSet, None, project_id=project.id) + return res.fetch( + self, usage=usage, **query) + + def get_quota_set_defaults(self, project): + """Show QuotaSet defaults for the project + + :param project: ID or instance of + :class:`~openstack.identity.project.Project` of the project for + which the quota should be retrieved + + :returns: One :class:`~openstack.block_storage.v3.quota_set.QuotaSet` + :raises: :class:`~openstack.exceptions.ResourceNotFound` + when no resource can be found. + """ + project = self._get_resource(_project.Project, project) + res = self._get_resource( + _quota_set.QuotaSet, None, project_id=project.id) + return res.fetch( + self, base_path='/os-quota-sets/defaults') + + def revert_quota_set(self, project, **query): + """Reset Quota for the project/user. + + :param project: ID or instance of + :class:`~openstack.identity.project.Project` of the project for + which the quota should be resetted. + :param dict query: Additional parameters to be used. + + :returns: ``None`` + """ + project = self._get_resource(_project.Project, project) + res = self._get_resource( + _quota_set.QuotaSet, None, project_id=project.id) + + return res.delete(self, **query) + + def update_quota_set(self, quota_set, query=None, **attrs): + """Update a QuotaSet. + + :param quota_set: Either the ID of a quota_set or a + :class:`~openstack.block_storage.v3.quota_set.QuotaSet` instance. + :param dict query: Optional parameters to be used with update call. + :attrs kwargs: The attributes to update on the QuotaSet represented + by ``quota_set``. + + :returns: The updated QuotaSet + :rtype: :class:`~openstack.block_storage.v3.quota_set.QuotaSet` + """ + res = self._get_resource(_quota_set.QuotaSet, quota_set, **attrs) + return res.commit(self, **query) + def _get_cleanup_dependencies(self): return { 'block_storage': { diff --git a/openstack/block_storage/v3/quota_set.py b/openstack/block_storage/v3/quota_set.py new file mode 100644 index 000000000..0ef3b51f9 --- /dev/null +++ b/openstack/block_storage/v3/quota_set.py @@ -0,0 +1,33 @@ +# 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.common import quota_set +from openstack import resource + + +class QuotaSet(quota_set.QuotaSet): + + #: Properties + #: The size (GB) of backups that are allowed for each project. + backup_gigabytes = resource.Body('backup_gigabytes', type=int) + #: The number of backups that are allowed for each project. + backups = resource.Body('backups', type=int) + #: The size (GB) of volumes and snapshots that are allowed for each + #: project. + gigabytes = resource.Body('gigabytes', type=int) + #: The number of groups that are allowed for each project. + groups = resource.Body('groups', type=int) + #: The size (GB) of volumes in request that are allowed for each volume. + per_volume_gigabytes = resource.Body('per_volume_gigabytes', type=int) + #: The number of snapshots that are allowed for each project. + snapshots = resource.Body('snapshots', type=int) + #: The number of volumes that are allowed for each project. + volumes = resource.Body('volumes', type=int) diff --git a/openstack/tests/unit/block_storage/v2/test_proxy.py b/openstack/tests/unit/block_storage/v2/test_proxy.py index a3c76c6f5..4513da4b9 100644 --- a/openstack/tests/unit/block_storage/v2/test_proxy.py +++ b/openstack/tests/unit/block_storage/v2/test_proxy.py @@ -13,10 +13,12 @@ from unittest import mock from openstack.block_storage.v2 import _proxy from openstack.block_storage.v2 import backup +from openstack.block_storage.v2 import quota_set from openstack.block_storage.v2 import snapshot from openstack.block_storage.v2 import stats from openstack.block_storage.v2 import type from openstack.block_storage.v2 import volume +from openstack import resource from openstack.tests.unit import test_proxy_base @@ -304,3 +306,86 @@ class TestType(TestVolumeProxy): self.proxy.remove_type_access, method_args=["value", "a"], expected_args=[self.proxy, "a"]) + + +class TestQuota(TestVolumeProxy): + def test_get(self): + self._verify( + 'openstack.resource.Resource.fetch', + self.proxy.get_quota_set, + method_args=['prj'], + expected_args=[self.proxy], + expected_kwargs={ + 'error_message': None, + 'requires_id': False, + 'usage': False, + }, + method_result=quota_set.QuotaSet(), + expected_result=quota_set.QuotaSet() + ) + + def test_get_query(self): + self._verify( + 'openstack.resource.Resource.fetch', + self.proxy.get_quota_set, + method_args=['prj'], + method_kwargs={ + 'usage': True, + 'user_id': 'uid' + }, + expected_args=[self.proxy], + expected_kwargs={ + 'error_message': None, + 'requires_id': False, + 'usage': True, + 'user_id': 'uid' + } + ) + + def test_get_defaults(self): + self._verify( + 'openstack.resource.Resource.fetch', + self.proxy.get_quota_set_defaults, + method_args=['prj'], + expected_args=[self.proxy], + expected_kwargs={ + 'error_message': None, + 'requires_id': False, + 'base_path': '/os-quota-sets/defaults' + } + ) + + def test_reset(self): + self._verify( + 'openstack.resource.Resource.delete', + self.proxy.revert_quota_set, + method_args=['prj'], + method_kwargs={'user_id': 'uid'}, + expected_args=[self.proxy], + expected_kwargs={ + 'user_id': 'uid' + } + ) + + @mock.patch('openstack.proxy.Proxy._get_resource', autospec=True) + def test_update(self, gr_mock): + gr_mock.return_value = resource.Resource() + gr_mock.commit = mock.Mock() + self._verify( + 'openstack.resource.Resource.commit', + self.proxy.update_quota_set, + method_args=['qs'], + method_kwargs={ + 'query': {'user_id': 'uid'}, + 'a': 'b', + }, + expected_args=[self.proxy], + expected_kwargs={ + 'user_id': 'uid' + } + ) + gr_mock.assert_called_with( + self.proxy, + quota_set.QuotaSet, + 'qs', a='b' + ) diff --git a/openstack/tests/unit/block_storage/v3/test_proxy.py b/openstack/tests/unit/block_storage/v3/test_proxy.py index b711fc086..46dfe044e 100644 --- a/openstack/tests/unit/block_storage/v3/test_proxy.py +++ b/openstack/tests/unit/block_storage/v3/test_proxy.py @@ -17,11 +17,13 @@ from openstack.block_storage.v3 import capabilities from openstack.block_storage.v3 import extension from openstack.block_storage.v3 import group_type from openstack.block_storage.v3 import limits +from openstack.block_storage.v3 import quota_set from openstack.block_storage.v3 import resource_filter from openstack.block_storage.v3 import snapshot from openstack.block_storage.v3 import stats from openstack.block_storage.v3 import type from openstack.block_storage.v3 import volume +from openstack import resource from openstack.tests.unit import test_proxy_base @@ -545,3 +547,86 @@ class TestType(TestVolumeProxy): def test_type_encryption_delete_ignore(self): self.verify_delete( self.proxy.delete_type_encryption, type.TypeEncryption, True) + + +class TestQuota(TestVolumeProxy): + def test_get(self): + self._verify( + 'openstack.resource.Resource.fetch', + self.proxy.get_quota_set, + method_args=['prj'], + expected_args=[self.proxy], + expected_kwargs={ + 'error_message': None, + 'requires_id': False, + 'usage': False, + }, + method_result=quota_set.QuotaSet(), + expected_result=quota_set.QuotaSet() + ) + + def test_get_query(self): + self._verify( + 'openstack.resource.Resource.fetch', + self.proxy.get_quota_set, + method_args=['prj'], + method_kwargs={ + 'usage': True, + 'user_id': 'uid' + }, + expected_args=[self.proxy], + expected_kwargs={ + 'error_message': None, + 'requires_id': False, + 'usage': True, + 'user_id': 'uid' + } + ) + + def test_get_defaults(self): + self._verify( + 'openstack.resource.Resource.fetch', + self.proxy.get_quota_set_defaults, + method_args=['prj'], + expected_args=[self.proxy], + expected_kwargs={ + 'error_message': None, + 'requires_id': False, + 'base_path': '/os-quota-sets/defaults' + } + ) + + def test_reset(self): + self._verify( + 'openstack.resource.Resource.delete', + self.proxy.revert_quota_set, + method_args=['prj'], + method_kwargs={'user_id': 'uid'}, + expected_args=[self.proxy], + expected_kwargs={ + 'user_id': 'uid' + } + ) + + @mock.patch('openstack.proxy.Proxy._get_resource', autospec=True) + def test_update(self, gr_mock): + gr_mock.return_value = resource.Resource() + gr_mock.commit = mock.Mock() + self._verify( + 'openstack.resource.Resource.commit', + self.proxy.update_quota_set, + method_args=['qs'], + method_kwargs={ + 'query': {'user_id': 'uid'}, + 'a': 'b', + }, + expected_args=[self.proxy], + expected_kwargs={ + 'user_id': 'uid' + } + ) + gr_mock.assert_called_with( + self.proxy, + quota_set.QuotaSet, + 'qs', a='b' + ) diff --git a/releasenotes/notes/block-storage-qs-0e3b69be2e709b65.yaml b/releasenotes/notes/block-storage-qs-0e3b69be2e709b65.yaml new file mode 100644 index 000000000..01adc564e --- /dev/null +++ b/releasenotes/notes/block-storage-qs-0e3b69be2e709b65.yaml @@ -0,0 +1,4 @@ +--- +features: + - | + Add block storage QuotaSet resource and proxy methods.