From b7ebc731caea4727b4ea346c0ffef624895ea9ed Mon Sep 17 00:00:00 2001 From: Artem Goncharov Date: Tue, 18 May 2021 17:21:55 +0200 Subject: [PATCH] Introduce QuotaSet in the compute service Start using newly introduced QuotaSet base resource in the compute area. Change-Id: I3165dd717035319c713a94326bd964fc63e2acf7 --- doc/source/user/proxies/compute.rst | 7 ++ doc/source/user/resources/compute/index.rst | 1 + .../user/resources/compute/v2/quota_set.rst | 12 +++ openstack/compute/v2/_proxy.py | 70 +++++++++++++++ openstack/compute/v2/quota_set.py | 54 ++++++++++++ .../functional/compute/v2/test_quota_set.py | 48 +++++++++++ openstack/tests/unit/compute/v2/test_proxy.py | 85 +++++++++++++++++++ .../compute-quota-set-e664412d089945d2.yaml | 4 + 8 files changed, 281 insertions(+) create mode 100644 doc/source/user/resources/compute/v2/quota_set.rst create mode 100644 openstack/compute/v2/quota_set.py create mode 100644 openstack/tests/functional/compute/v2/test_quota_set.py create mode 100644 releasenotes/notes/compute-quota-set-e664412d089945d2.yaml diff --git a/doc/source/user/proxies/compute.rst b/doc/source/user/proxies/compute.rst index a4b771551..8c9551212 100644 --- a/doc/source/user/proxies/compute.rst +++ b/doc/source/user/proxies/compute.rst @@ -149,3 +149,10 @@ Extension Operations .. autoclass:: openstack.compute.v2._proxy.Proxy :noindex: :members: find_extension, extensions + +QuotaSet Operations +^^^^^^^^^^^^^^^^^^^ +.. autoclass:: openstack.compute.v2._proxy.Proxy + :noindex: + :members: get_quota_set, get_quota_set_defaults, + revert_quota_set, update_quota_set diff --git a/doc/source/user/resources/compute/index.rst b/doc/source/user/resources/compute/index.rst index e56d2a058..0db8a314b 100644 --- a/doc/source/user/resources/compute/index.rst +++ b/doc/source/user/resources/compute/index.rst @@ -13,3 +13,4 @@ Compute Resources v2/server_interface v2/server_ip v2/hypervisor + v2/quota_set diff --git a/doc/source/user/resources/compute/v2/quota_set.rst b/doc/source/user/resources/compute/v2/quota_set.rst new file mode 100644 index 000000000..8a5d91dfc --- /dev/null +++ b/doc/source/user/resources/compute/v2/quota_set.rst @@ -0,0 +1,12 @@ +openstack.compute.v2.quota_set +============================== + +.. automodule:: openstack.compute.v2.quota_set + +The QuotaSet Class +------------------ + +The ``QuotaSet`` class inherits from :class:`~openstack.resource.Resource`. + +.. autoclass:: openstack.compute.v2.quota_set.QuotaSet + :members: diff --git a/openstack/compute/v2/_proxy.py b/openstack/compute/v2/_proxy.py index d520cdac1..85083bea5 100644 --- a/openstack/compute/v2/_proxy.py +++ b/openstack/compute/v2/_proxy.py @@ -20,6 +20,7 @@ from openstack.compute.v2 import hypervisor as _hypervisor from openstack.compute.v2 import image as _image from openstack.compute.v2 import keypair as _keypair from openstack.compute.v2 import limits +from openstack.compute.v2 import quota_set as _quota_set from openstack.compute.v2 import server as _server from openstack.compute.v2 import server_diagnostics as _server_diagnostics from openstack.compute.v2 import server_group as _server_group @@ -29,6 +30,7 @@ from openstack.compute.v2 import server_remote_console as _src from openstack.compute.v2 import service as _service from openstack.compute.v2 import volume_attachment as _volume_attachment from openstack import exceptions +from openstack.identity.v3 import project as _project from openstack.network.v2 import security_group as _sg from openstack import proxy from openstack import resource @@ -1818,6 +1820,74 @@ class Proxy(proxy.Proxy): else: return server.get_console_url(self, console_type) + 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.compute.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.compute.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.compute.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.compute.v2.quota_set.QuotaSet` + """ + res = self._get_resource(_quota_set.QuotaSet, quota_set, **attrs) + return res.commit(self, **query) + def _get_cleanup_dependencies(self): return { 'compute': { diff --git a/openstack/compute/v2/quota_set.py b/openstack/compute/v2/quota_set.py new file mode 100644 index 000000000..890a819c1 --- /dev/null +++ b/openstack/compute/v2/quota_set.py @@ -0,0 +1,54 @@ +# 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): + # We generally only want compute QS support max_microversion. Otherwise be + # explicit and list all the attributes + _max_microversion = '2.56' + + #: Properties + #: The number of allowed server cores for each tenant. + cores = resource.Body('cores', type=int) + #: The number of allowed fixed IP addresses for each tenant. Must be + #: equal to or greater than the number of allowed servers. + fixed_ips = resource.Body('fixed_ips', type=int) + #: The number of allowed floating IP addresses for each tenant. + floating_ips = resource.Body('floating_ips', type=int) + #: The number of allowed bytes of content for each injected file. + injected_file_content_bytes = resource.Body( + 'injected_file_content_bytes', type=int) + #: The number of allowed bytes for each injected file path. + injected_file_path_bytes = resource.Body( + 'injected_file_path_bytes', type=int) + #: The number of allowed injected files for each tenant. + injected_files = resource.Body('injected_files', type=int) + #: The number of allowed servers for each tenant. + instances = resource.Body('instances', type=int) + #: The number of allowed key pairs for each user. + key_pairs = resource.Body('key_pairs', type=int) + #: The number of allowed metadata items for each server. + metadata_items = resource.Body('metadata_items', type=int) + #: The number of private networks that can be created per project. + networks = resource.Body('networks', type=int) + #: The amount of allowed server RAM, in MiB, for each tenant. + ram = resource.Body('ram', type=int) + #: The number of allowed rules for each security group. + security_group_rules = resource.Body('security_group_rules', type=int) + #: The number of allowed security groups for each tenant. + security_groups = resource.Body('security_groups', type=int) + #: The number of allowed server groups for each tenant. + server_groups = resource.Body('server_groups', type=int) + #: The number of allowed members for each server group. + server_group_members = resource.Body('server_group_members', type=int) diff --git a/openstack/tests/functional/compute/v2/test_quota_set.py b/openstack/tests/functional/compute/v2/test_quota_set.py new file mode 100644 index 000000000..5fa04431d --- /dev/null +++ b/openstack/tests/functional/compute/v2/test_quota_set.py @@ -0,0 +1,48 @@ +# 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.tests.functional import base + + +class TestQS(base.BaseFunctionalTest): + + def test_qs(self): + sot = self.conn.compute.get_quota_set( + self.conn.current_project_id + ) + self.assertIsNotNone(sot.key_pairs) + + def test_qs_user(self): + sot = self.conn.compute.get_quota_set( + self.conn.current_project_id, + user_id=self.conn.session.auth.get_user_id(self.conn.compute) + ) + self.assertIsNotNone(sot.key_pairs) + + def test_update(self): + sot = self.conn.compute.get_quota_set( + self.conn.current_project_id + ) + self.conn.compute.update_quota_set( + sot, + query={ + 'user_id': self.conn.session.auth.get_user_id( + self.conn.compute) + }, + key_pairs=100 + ) + + def test_revert(self): + self.conn.compute.revert_quota_set( + self.conn.current_project_id, + user_id=self.conn.session.auth.get_user_id(self.conn.compute) + ) diff --git a/openstack/tests/unit/compute/v2/test_proxy.py b/openstack/tests/unit/compute/v2/test_proxy.py index a0ed8da44..f9b14e010 100644 --- a/openstack/tests/unit/compute/v2/test_proxy.py +++ b/openstack/tests/unit/compute/v2/test_proxy.py @@ -21,12 +21,14 @@ from openstack.compute.v2 import hypervisor from openstack.compute.v2 import image from openstack.compute.v2 import keypair from openstack.compute.v2 import limits +from openstack.compute.v2 import quota_set from openstack.compute.v2 import server from openstack.compute.v2 import server_group from openstack.compute.v2 import server_interface from openstack.compute.v2 import server_ip from openstack.compute.v2 import server_remote_console from openstack.compute.v2 import service +from openstack import resource from openstack.tests.unit import test_proxy_base @@ -1079,3 +1081,86 @@ class TestCompute(TestComputeProxy): type='fake_type', protocol=None) self.assertEqual(console_fake['url'], ret['url']) + + +class TestQuota(TestComputeProxy): + 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/compute-quota-set-e664412d089945d2.yaml b/releasenotes/notes/compute-quota-set-e664412d089945d2.yaml new file mode 100644 index 000000000..63f23898e --- /dev/null +++ b/releasenotes/notes/compute-quota-set-e664412d089945d2.yaml @@ -0,0 +1,4 @@ +--- +features: + - | + Add support for QuotaSet in the compute service.