Add quotas support
Add the capability to get, update and reset to default quotas in compute service. Change-Id: Id814eaf25547c0272fddc43ae1d89ae613690c57
This commit is contained in:
parent
60d2448051
commit
7f47eb2a64
3
releasenotes/notes/compute-quotas-b07a0f24dfac8444.yaml
Normal file
3
releasenotes/notes/compute-quotas-b07a0f24dfac8444.yaml
Normal file
@ -0,0 +1,3 @@
|
||||
---
|
||||
features:
|
||||
- Add new APIs, OperatorCloud.get_compute_quotas(), OperatorCloud.set_compute_quotas() and OperatorCloud.delete_compute_quotas() to manage nova quotas for projects and users
|
@ -877,3 +877,18 @@ class RecordSetUpdate(task_manager.Task):
|
||||
class RecordSetDelete(task_manager.Task):
|
||||
def main(self, client):
|
||||
return client.designate_client.recordsets.delete(**self.args)
|
||||
|
||||
|
||||
class NovaQuotasSet(task_manager.Task):
|
||||
def main(self, client):
|
||||
return client.nova_client.quotas.update(**self.args)
|
||||
|
||||
|
||||
class NovaQuotasGet(task_manager.Task):
|
||||
def main(self, client):
|
||||
return client.nova_client.quotas.get(**self.args)
|
||||
|
||||
|
||||
class NovaQuotasDelete(task_manager.Task):
|
||||
def main(self, client):
|
||||
return client.nova_client.quotas.delete(**self.args)
|
||||
|
@ -399,7 +399,13 @@ def obj_to_dict(obj, request_id=None):
|
||||
instance = munch.Munch()
|
||||
|
||||
for key in dir(obj):
|
||||
value = getattr(obj, key)
|
||||
try:
|
||||
value = getattr(obj, key)
|
||||
# some attributes can be defined as a @propierty, so we can't assure
|
||||
# to have a valid value
|
||||
# e.g. id in python-novaclient/tree/novaclient/v2/quotas.py
|
||||
except AttributeError:
|
||||
continue
|
||||
if isinstance(value, NON_CALLABLES) and not key.startswith('_'):
|
||||
instance[key] = value
|
||||
return _add_request_id(instance, request_id)
|
||||
|
@ -14,6 +14,7 @@ import jsonpatch
|
||||
|
||||
from ironicclient import client as ironic_client
|
||||
from ironicclient import exceptions as ironic_exceptions
|
||||
from novaclient import exceptions as nova_exceptions
|
||||
|
||||
from shade.exc import * # noqa
|
||||
from shade import openstackcloud
|
||||
@ -1930,3 +1931,68 @@ class OperatorCloud(openstackcloud.OpenStackCloud):
|
||||
name=name_or_id, host=host_name)):
|
||||
return self.manager.submitTask(_tasks.AggregateRemoveHost(
|
||||
aggregate=aggregate['id'], host=host_name))
|
||||
|
||||
def set_compute_quotas(self, name_or_id, **kwargs):
|
||||
""" Set a quota in a project
|
||||
|
||||
:param name_or_id: project name or id
|
||||
:param kwargs: key/value pairs of quota name and quota value
|
||||
|
||||
:raises: OpenStackCloudException if the resource to set the
|
||||
quota does not exist.
|
||||
"""
|
||||
|
||||
proj = self.get_project(name_or_id)
|
||||
if not proj:
|
||||
raise OpenStackCloudException("project does not exist")
|
||||
|
||||
# compute_quotas = {key: val for key, val in kwargs.items()
|
||||
# if key in quota.COMPUTE_QUOTAS}
|
||||
# TODO(ghe): Manage volume and network quotas
|
||||
# network_quotas = {key: val for key, val in kwargs.items()
|
||||
# if key in quota.NETWORK_QUOTAS}
|
||||
# volume_quotas = {key: val for key, val in kwargs.items()
|
||||
# if key in quota.VOLUME_QUOTAS}
|
||||
|
||||
try:
|
||||
self.manager.submitTask(
|
||||
_tasks.NovaQuotasSet(tenant_id=proj.id,
|
||||
force=True,
|
||||
**kwargs))
|
||||
except novaclient.exceptions.BadRequest:
|
||||
raise OpenStackCloudException("No valid quota or resource")
|
||||
|
||||
def get_compute_quotas(self, name_or_id):
|
||||
""" Get quota for a project
|
||||
|
||||
:param name_or_id: project name or id
|
||||
:raises: OpenStackCloudException if it's not a valid project
|
||||
|
||||
:returns: Munch object with the quotas
|
||||
"""
|
||||
proj = self.get_project(name_or_id)
|
||||
if not proj:
|
||||
raise OpenStackCloudException("project does not exist")
|
||||
try:
|
||||
return self.manager.submitTask(
|
||||
_tasks.NovaQuotasGet(tenant_id=proj.id))
|
||||
except nova_exceptions.BadRequest:
|
||||
raise OpenStackCloudException("nova client call failed")
|
||||
|
||||
def delete_compute_quotas(self, name_or_id):
|
||||
""" Delete quota for a project
|
||||
|
||||
:param name_or_id: project name or id
|
||||
:raises: OpenStackCloudException if it's not a valid project or the
|
||||
nova client call failed
|
||||
|
||||
:returns: dict with the quotas
|
||||
"""
|
||||
proj = self.get_project(name_or_id)
|
||||
if not proj:
|
||||
raise OpenStackCloudException("project does not exist")
|
||||
try:
|
||||
return self.manager.submitTask(
|
||||
_tasks.NovaQuotasDelete(tenant_id=proj.id))
|
||||
except novaclient.exceptions.BadRequest:
|
||||
raise OpenStackCloudException("nova client call failed")
|
||||
|
42
shade/tests/functional/test_quotas.py
Normal file
42
shade/tests/functional/test_quotas.py
Normal file
@ -0,0 +1,42 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# 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.
|
||||
|
||||
"""
|
||||
test_quotas
|
||||
----------------------------------
|
||||
|
||||
Functional tests for `shade` quotas methods.
|
||||
"""
|
||||
|
||||
from shade import operator_cloud
|
||||
from shade.tests import base
|
||||
|
||||
|
||||
class TestComputeQuotas(base.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestComputeQuotas, self).setUp()
|
||||
self.cloud = operator_cloud(cloud='devstack-admin')
|
||||
if not self.cloud.has_service('compute'):
|
||||
self.skipTest('compute service not supported by cloud')
|
||||
|
||||
def test_quotas(self):
|
||||
'''Test quotas functionality'''
|
||||
quotas = self.cloud.get_compute_quotas('demo')
|
||||
cores = quotas['cores']
|
||||
self.cloud.set_compute_quotas('demo', cores=cores + 1)
|
||||
self.assertEqual(cores + 1,
|
||||
self.cloud.get_compute_quotas('demo')['cores'])
|
||||
self.cloud.delete_compute_quotas('demo')
|
||||
self.assertEqual(cores, self.cloud.get_compute_quotas('demo')['cores'])
|
55
shade/tests/unit/test_quotas.py
Normal file
55
shade/tests/unit/test_quotas.py
Normal file
@ -0,0 +1,55 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# 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 mock
|
||||
|
||||
import shade
|
||||
from shade.tests.unit import base
|
||||
from shade.tests import fakes
|
||||
|
||||
|
||||
class TestQuotas(base.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestQuotas, self).setUp()
|
||||
self.cloud = shade.operator_cloud(validate=False)
|
||||
|
||||
@mock.patch.object(shade.OpenStackCloud, 'nova_client')
|
||||
@mock.patch.object(shade.OpenStackCloud, 'keystone_client')
|
||||
def test_update_quotas(self, mock_keystone, mock_nova):
|
||||
project = fakes.FakeProject('project_a')
|
||||
mock_keystone.tenants.list.return_value = [project]
|
||||
self.cloud.set_compute_quotas(project, cores=1)
|
||||
|
||||
mock_nova.quotas.update.assert_called_once_with(
|
||||
cores=1, force=True, tenant_id='project_a')
|
||||
|
||||
@mock.patch.object(shade.OpenStackCloud, 'nova_client')
|
||||
@mock.patch.object(shade.OpenStackCloud, 'keystone_client')
|
||||
def test_get_quotas(self, mock_keystone, mock_nova):
|
||||
project = fakes.FakeProject('project_a')
|
||||
mock_keystone.tenants.list.return_value = [project]
|
||||
self.cloud.get_compute_quotas(project)
|
||||
|
||||
mock_nova.quotas.get.assert_called_once_with(tenant_id='project_a')
|
||||
|
||||
@mock.patch.object(shade.OpenStackCloud, 'nova_client')
|
||||
@mock.patch.object(shade.OpenStackCloud, 'keystone_client')
|
||||
def test_delete_quotas(self, mock_keystone, mock_nova):
|
||||
project = fakes.FakeProject('project_a')
|
||||
mock_keystone.tenants.list.return_value = [project]
|
||||
self.cloud.delete_compute_quotas(project)
|
||||
|
||||
mock_nova.quotas.delete.assert_called_once_with(tenant_id='project_a')
|
Loading…
x
Reference in New Issue
Block a user