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:
parent
ff265cdb6b
commit
df14d977c9
|
@ -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.'))
|
||||
|
|
|
@ -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.'))
|
||||
|
|
|
@ -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.'));
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
}());
|
||||
|
|
|
@ -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.'
|
||||
}
|
||||
];
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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."
|
||||
}
|
||||
];
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
|
|
Loading…
Reference in New Issue