API for getting and updating quotas

Adding API for getting and updating default
quotas information from nova and cinder.

The patch for the panel is at
https://review.openstack.org/#/c/205296/

Co-Authored-By: Kyle Olivo <keolivo@thoughtworks.com>
Co-Authored-By: Kristine Brown<kbrown@thoughtworks.com>
Co-Authored-By: Dan Siwiec<dan.siwiec@thoughtworks.com>
Co-Authored-By: Paulo Ewerton <pauloewerton@lsd.ufcg.edu.br>

Change-Id: Ia8e26e10b816d0dba69542b8da4ad18cccf5173a
Partially-Implements: blueprint ng-defaults-panel
This commit is contained in:
Rajat Vig 2015-10-20 13:55:59 -07:00 committed by Paulo Ewerton
parent ff265cdb6b
commit df14d977c9
8 changed files with 415 additions and 2 deletions

View File

@ -14,11 +14,13 @@
"""API over the cinder service.
"""
from django.utils.translation import ugettext_lazy as _
from django.views import generic
from openstack_dashboard import api
from openstack_dashboard.api.rest import urls
from openstack_dashboard.api.rest import utils as rest_utils
from openstack_dashboard.usage import quotas
CLIENT_KEYWORDS = {'marker', 'sort_dir', 'paginate'}
@ -250,3 +252,51 @@ class Services(generic.View):
} for idx, u in enumerate(result)]}
else:
raise rest_utils.AjaxError(501, '')
@urls.register
class DefaultQuotaSets(generic.View):
"""API for getting default quotas for cinder
"""
url_regex = r'cinder/quota-sets/defaults/$'
@rest_utils.ajax()
def get(self, request):
"""Get the values for Cinder specific quotas
Example GET:
http://localhost/api/cinder/quota-sets/defaults/
"""
if api.cinder.is_volume_service_enabled():
quota_set = api.cinder.default_quota_get(
request, request.user.tenant_id)
result = [
{
'display_name':
quotas.QUOTA_NAMES.get(
quota.name,
quota.name.replace("_", " ").title()
) + '',
'name': quota.name,
'limit': quota.limit
}
for quota in quota_set]
return {'items': result}
else:
raise rest_utils.AjaxError(501, _('Service Cinder is disabled.'))
@rest_utils.ajax(data_required=True)
def patch(self, request):
"""Update the values for Cinder specific quotas
This method returns HTTP 204 (no content) on success.
"""
if api.cinder.is_volume_service_enabled():
cinder_data = {
key: request.DATA[key] for key in quotas.CINDER_QUOTA_FIELDS
}
api.cinder.default_quota_update(request, **cinder_data)
else:
raise rest_utils.AjaxError(501, _('Service Cinder is disabled.'))

View File

@ -16,6 +16,7 @@
from django.http import HttpResponse
from django.template.defaultfilters import slugify
from django.utils import http as utils_http
from django.utils.translation import ugettext_lazy as _
from django.views import generic
from novaclient import exceptions
@ -24,6 +25,7 @@ from openstack_dashboard import api
from openstack_dashboard.api.rest import json_encoder
from openstack_dashboard.api.rest import urls
from openstack_dashboard.api.rest import utils as rest_utils
from openstack_dashboard.usage import quotas
@urls.register
@ -509,3 +511,64 @@ class AggregateExtraSpecs(generic.View):
for name in request.DATA.get('removed'):
updated[name] = None
api.nova.aggregate_set_metadata(request, aggregate_id, updated)
@urls.register
class DefaultQuotaSets(generic.View):
"""API for getting default quotas for nova
"""
url_regex = r'nova/quota-sets/defaults/$'
@rest_utils.ajax()
def get(self, request):
"""Get the values for Nova specific quotas
Example GET:
http://localhost/api/nova/quota-sets/defaults/
"""
if api.base.is_service_enabled(request, 'compute'):
quota_set = api.nova.default_quota_get(request,
request.user.tenant_id)
disabled_quotas = quotas.get_disabled_quotas(request)
filtered_quotas = [quota for quota in quota_set
if quota.name not in disabled_quotas]
result = [{
'display_name': quotas.QUOTA_NAMES.get(
quota.name,
quota.name.replace("_", " ").title()
) + '',
'name': quota.name,
'limit': quota.limit
} for quota in filtered_quotas]
return {'items': result}
else:
raise rest_utils.AjaxError(501, _('Service Nova is disabled.'))
@rest_utils.ajax(data_required=True)
def patch(self, request):
"""Update the values for Nova specific quotas
This method returns HTTP 204 (no content) on success.
"""
if api.base.is_service_enabled(request, 'compute'):
disabled_quotas = quotas.get_disabled_quotas(request)
all_quotas = quotas.NOVA_QUOTA_FIELDS + quotas.MISSING_QUOTA_FIELDS
filtered_quotas = [quota for quota in all_quotas
if quota not in disabled_quotas]
request_data = {
key: request.DATA.get(key, None) for key in filtered_quotas
}
nova_data = {key: value for key, value in request_data.items()
if value is not None}
api.nova.default_quota_update(request, **nova_data)
else:
raise rest_utils.AjaxError(501, _('Service Nova is disabled.'))

View File

@ -45,7 +45,9 @@
getQoSSpecs: getQoSSpecs,
createVolume: createVolume,
getAbsoluteLimits: getAbsoluteLimits,
getServices: getServices
getServices: getServices,
getDefaultQuotaSets: getDefaultQuotaSets,
setDefaultQuotaSets: setDefaultQuotaSets
};
return service;
@ -288,5 +290,37 @@
gettext('Unable to retrieve the Absolute Limits.'));
});
}
// Default Quota Sets
/**
* @name horizon.app.core.openstack-service-api.cinder.getDefaultQuotaSets
* @description
* Get default quotasets
*
* The listing result is an object with property "items." Each item is
* a quota.
*
*/
function getDefaultQuotaSets() {
return apiService.get('/api/cinder/quota-sets/defaults/')
.error(function () {
toastService.add('error', gettext('Unable to retrieve the default quotas.'));
});
}
/**
* @name horizon.app.core.openstack-service-api.cinder.setDefaultQuotaSets
* @description
* Set default quota sets
*
*/
function setDefaultQuotaSets(quotas) {
return apiService.patch('/api/cinder/quota-sets/defaults/', quotas)
.error(function () {
toastService.add('error', gettext('Unable to set the default quotas.'));
});
}
}
}());

View File

@ -138,6 +138,26 @@
data: { params: 'config' },
error: 'Unable to retrieve the QoS Specs.',
testInput: [ 'config' ]
},
{
func: 'getDefaultQuotaSets',
method: 'get',
path: '/api/cinder/quota-sets/defaults/',
error: 'Unable to retrieve the default quotas.'
},
{
func: 'setDefaultQuotaSets',
"data": {
"id": 42
},
"testInput": [
{
"id": 42
}
],
method: 'patch',
path: '/api/cinder/quota-sets/defaults/',
error: 'Unable to set the default quotas.'
}
];

View File

@ -60,7 +60,9 @@
getRegenerateKeypairUrl: getRegenerateKeypairUrl,
createFlavor: createFlavor,
updateFlavor: updateFlavor,
deleteFlavor: deleteFlavor
deleteFlavor: deleteFlavor,
getDefaultQuotaSets: getDefaultQuotaSets,
setDefaultQuotaSets: setDefaultQuotaSets
};
return service;
@ -511,6 +513,37 @@
});
}
// Default Quota Sets
/**
* @name horizon.app.core.openstack-service-api.nova.getDefaultQuotaSets
* @description
* Get default quotasets
*
* The listing result is an object with property "items." Each item is
* a quota.
*
*/
function getDefaultQuotaSets() {
return apiService.get('/api/nova/quota-sets/defaults/')
.error(function () {
toastService.add('error', gettext('Unable to retrieve the default quotas.'));
});
}
/**
* @name horizon.app.core.openstack-service-api.nova.setDefaultQuotaSets
* @description
* Set default quotasets
*
*/
function setDefaultQuotaSets(quotas) {
return apiService.patch('/api/nova/quota-sets/defaults/', quotas)
.error(function () {
toastService.add('error', gettext('Unable to set the default quotas.'));
});
}
/**
* @ngdoc function
* @name getCreateKeypairUrl

View File

@ -323,6 +323,26 @@
"path": "/api/nova/flavors/42/",
"error": "Unable to delete the flavor with id: 42",
"testInput": [42]
},
{
"func": "getDefaultQuotaSets",
"method": "get",
"path": "/api/nova/quota-sets/defaults/",
"error": "Unable to retrieve the default quotas."
},
{
"func": "setDefaultQuotaSets",
"method": "patch",
"data": {
"id": 42
},
"testInput": [
{
"id": 42
}
],
"path": "/api/nova/quota-sets/defaults/",
"error": "Unable to set the default quotas."
}
];

View File

@ -17,6 +17,7 @@ import mock
from django.conf import settings
from openstack_dashboard import api
from openstack_dashboard.api.base import Quota
from openstack_dashboard.api.rest import cinder
from openstack_dashboard.test import helpers as test
@ -257,3 +258,91 @@ class CinderRestTestCase(test.TestCase):
response = cinder.Services().get(request)
self.assertStatusCode(response, 501)
@mock.patch.object(cinder.api, 'cinder')
def test_quota_sets_defaults_get_when_service_is_enabled(self, cc):
self.maxDiff = None
filters = {'user': {'tenant_id': 'tenant'}}
request = self.mock_rest_request(**{'GET': dict(filters)})
cc.is_service_enabled.return_value = True
cc.default_quota_get.return_value = [Quota("volumes", 1),
Quota("snapshots", 2),
Quota("gigabytes", 3),
Quota("some_other_1", 100),
Quota("yet_another", 101)]
response = cinder.DefaultQuotaSets().get(request)
self.assertStatusCode(response, 200)
self.assertEqual(response.json,
{"items":
[{"limit": 1,
"display_name": "Volumes", "name": "volumes"},
{"limit": 2,
"display_name": "Volume Snapshots",
"name": "snapshots"},
{"limit": 3,
"display_name":
"Total Size of Volumes and Snapshots (GB)",
"name": "gigabytes"},
{"limit": 100,
"display_name": "Some Other 1",
"name": "some_other_1"},
{"limit": 101,
"display_name": "Yet Another",
"name": "yet_another"}]})
cc.default_quota_get.assert_called_once_with(request,
request.user.tenant_id)
@mock.patch.object(cinder.api, 'cinder')
def test_quota_sets_defaults_get_when_service_is_disabled(self, cc):
filters = {'user': {'tenant_id': 'tenant'}}
request = self.mock_rest_request(**{'GET': dict(filters)})
cc.is_volume_service_enabled.return_value = False
response = cinder.DefaultQuotaSets().get(request)
self.assertStatusCode(response, 501)
self.assertEqual(response.content.decode('utf-8'),
'"Service Cinder is disabled."')
cc.default_quota_get.assert_not_called()
@mock.patch.object(cinder.api, 'cinder')
def test_quota_sets_defaults_patch_when_service_is_enabled(self, cc):
request = self.mock_rest_request(body='''
{"volumes": "15", "snapshots": "5000",
"gigabytes": "5", "cores": "10"}
''')
cc.is_volume_service_enabled.return_value = True
response = cinder.DefaultQuotaSets().patch(request)
self.assertStatusCode(response, 204)
self.assertEqual(response.content.decode('utf-8'), '')
cc.default_quota_update.assert_called_once_with(request,
volumes='15',
snapshots='5000',
gigabytes='5')
@mock.patch.object(cinder.api, 'cinder')
def test_quota_sets_defaults_patch_when_service_is_disabled(self, cc):
request = self.mock_rest_request(body='''
{"volumes": "15", "snapshots": "5000",
"gigabytes": "5", "cores": "10"}
''')
cc.is_volume_service_enabled.return_value = False
response = cinder.DefaultQuotaSets().patch(request)
self.assertStatusCode(response, 501)
self.assertEqual(response.content.decode('utf-8'),
'"Service Cinder is disabled."')
cc.default_quota_update.assert_not_called()

View File

@ -17,8 +17,10 @@ from django.conf import settings
from json import loads as to_json
from openstack_dashboard import api
from openstack_dashboard.api.base import Quota
from openstack_dashboard.api.rest import nova
from openstack_dashboard.test import helpers as test
from openstack_dashboard.usage import quotas
from novaclient import exceptions
@ -665,3 +667,105 @@ class NovaRestTestCase(test.TestCase):
response = nova.Services().get(request)
self.assertStatusCode(response, 501)
@test.create_stubs({api.base: ('is_service_enabled',)})
@test.create_stubs({quotas: ('get_disabled_quotas',)})
@mock.patch.object(nova.api, 'nova')
def test_quota_sets_defaults_get(self, nc):
filters = {'user': {'tenant_id': 'tenant'}}
request = self.mock_rest_request(**{'GET': dict(filters)})
api.base.is_service_enabled(request, 'compute').AndReturn(True)
quotas.get_disabled_quotas(request).AndReturn(['floating_ips'])
nc.default_quota_get.return_value = [
Quota('metadata_items', 100),
Quota('floating_ips', 1),
Quota('q2', 101)
]
self.mox.ReplayAll()
response = nova.DefaultQuotaSets().get(request)
self.assertStatusCode(response, 200)
self.assertEqual(response.json,
{"items": [
{"limit": 100,
"display_name": "Metadata Items",
"name": "metadata_items"},
{"limit": 101,
"display_name": "Q2",
"name": "q2"}
]})
nc.default_quota_get.assert_called_once_with(request,
request.user.tenant_id)
@test.create_stubs({api.base: ('is_service_enabled',)})
@mock.patch.object(nova.api, 'nova')
def test_quota_sets_defaults_get_when_service_is_disabled(self, nc):
filters = {'user': {'tenant_id': 'tenant'}}
request = self.mock_rest_request(**{'GET': dict(filters)})
api.base.is_service_enabled(request, 'compute').AndReturn(False)
self.mox.ReplayAll()
response = nova.DefaultQuotaSets().get(request)
self.assertStatusCode(response, 501)
self.assertEqual(response.content.decode('utf-8'),
'"Service Nova is disabled."')
nc.default_quota_get.assert_not_called()
@test.create_stubs({api.base: ('is_service_enabled',)})
@test.create_stubs({quotas: ('get_disabled_quotas',)})
@mock.patch.object(nova.api, 'nova')
def test_quota_sets_defaults_patch(self, nc):
request = self.mock_rest_request(body='''
{"key_pairs": "15", "metadata_items": "5000",
"cores": "10", "instances": "20", "floating_ips": 10,
"injected_file_content_bytes": "15",
"injected_file_path_bytes": "5000",
"injected_files": "5", "ram": "10", "gigabytes": "5"}
''')
api.base.is_service_enabled(request, 'compute').AndReturn(True)
quotas.get_disabled_quotas(request).AndReturn(['floating_ips'])
self.mox.ReplayAll()
response = nova.DefaultQuotaSets().patch(request)
self.assertStatusCode(response, 204)
self.assertEqual(response.content.decode('utf-8'), '')
nc.default_quota_update.assert_called_once_with(
request, key_pairs='15',
metadata_items='5000', cores='10',
instances='20', injected_file_content_bytes='15',
injected_file_path_bytes='5000',
injected_files='5', ram='10')
@test.create_stubs({api.base: ('is_service_enabled',)})
@mock.patch.object(nova.api, 'nova')
def test_quota_sets_defaults_patch_when_service_is_disabled(self, nc):
request = self.mock_rest_request(body='''
{"key_pairs": "15", "metadata_items": "5000",
"cores": "10", "instances": "20", "floating_ips": 10,
"injected_file_content_bytes": "15",
"injected_file_path_bytes": "5000",
"injected_files": "5", "ram": "10", "gigabytes": "5"}
''')
api.base.is_service_enabled(request, 'compute').AndReturn(False)
self.mox.ReplayAll()
response = nova.DefaultQuotaSets().patch(request)
self.assertStatusCode(response, 501)
self.assertEqual(response.content.decode('utf-8'),
'"Service Nova is disabled."')
nc.default_quota_update.assert_not_called()