Adding Identity NGProjects API
This patch adds some projects-related endpoints to the Nova, Cinder and Neutron REST/Angular APIs. These changes are needed for the angular Identity Projects actions that handle mainly project quota updates. Partially-Implements: blueprint angularize-identity-projects Change-Id: I98b8eb9e5e7bfb0d49a77c00115fe62d412abe21
This commit is contained in:
parent
20bfc93dc5
commit
3945b24062
@ -300,3 +300,32 @@ class DefaultQuotaSets(generic.View):
|
|||||||
api.cinder.default_quota_update(request, **cinder_data)
|
api.cinder.default_quota_update(request, **cinder_data)
|
||||||
else:
|
else:
|
||||||
raise rest_utils.AjaxError(501, _('Service Cinder is disabled.'))
|
raise rest_utils.AjaxError(501, _('Service Cinder is disabled.'))
|
||||||
|
|
||||||
|
|
||||||
|
@urls.register
|
||||||
|
class QuotaSets(generic.View):
|
||||||
|
"""API for setting quotas for a given project.
|
||||||
|
"""
|
||||||
|
url_regex = r'cinder/quota-sets/(?P<project_id>[0-9a-f]+)$'
|
||||||
|
|
||||||
|
@rest_utils.ajax(data_required=True)
|
||||||
|
def patch(self, request, project_id):
|
||||||
|
"""Update a single project quota data.
|
||||||
|
|
||||||
|
The PATCH data should be an application/json object with the
|
||||||
|
attributes to set to new quota values.
|
||||||
|
|
||||||
|
This method returns HTTP 204 (no content) on success.
|
||||||
|
"""
|
||||||
|
# Filters cinder quota fields
|
||||||
|
disabled_quotas = quotas.get_disabled_quotas(request)
|
||||||
|
|
||||||
|
if api.cinder.is_volume_service_enabled():
|
||||||
|
cinder_data = {
|
||||||
|
key: request.DATA[key] for key in quotas.CINDER_QUOTA_FIELDS
|
||||||
|
if key not in disabled_quotas
|
||||||
|
}
|
||||||
|
|
||||||
|
api.cinder.tenant_quota_update(request, project_id, **cinder_data)
|
||||||
|
else:
|
||||||
|
raise rest_utils.AjaxError(501, _('Service Cinder is disabled.'))
|
||||||
|
@ -15,11 +15,13 @@
|
|||||||
"""API over the neutron service.
|
"""API over the neutron service.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.views import generic
|
from django.views import generic
|
||||||
|
|
||||||
from openstack_dashboard import api
|
from openstack_dashboard import api
|
||||||
from openstack_dashboard.api.rest import urls
|
from openstack_dashboard.api.rest import urls
|
||||||
from openstack_dashboard.api.rest import utils as rest_utils
|
from openstack_dashboard.api.rest import utils as rest_utils
|
||||||
|
from openstack_dashboard.usage import quotas
|
||||||
|
|
||||||
|
|
||||||
@urls.register
|
@urls.register
|
||||||
@ -173,3 +175,62 @@ class Extensions(generic.View):
|
|||||||
"""
|
"""
|
||||||
result = api.neutron.list_extensions(request)
|
result = api.neutron.list_extensions(request)
|
||||||
return {'items': [e for e in result]}
|
return {'items': [e for e in result]}
|
||||||
|
|
||||||
|
|
||||||
|
class DefaultQuotaSets(generic.View):
|
||||||
|
"""API for getting default quotas for neutron
|
||||||
|
"""
|
||||||
|
url_regex = r'neutron/quota-sets/defaults/$'
|
||||||
|
|
||||||
|
@rest_utils.ajax()
|
||||||
|
def get(self, request):
|
||||||
|
if api.base.is_service_enabled(request, 'network'):
|
||||||
|
quota_set = api.neutron.tenant_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 Neutron is disabled.'))
|
||||||
|
|
||||||
|
|
||||||
|
@urls.register
|
||||||
|
class QuotasSets(generic.View):
|
||||||
|
"""API for setting quotas of a given project.
|
||||||
|
"""
|
||||||
|
url_regex = r'neutron/quotas-sets/(?P<project_id>[0-9a-f]+)$'
|
||||||
|
|
||||||
|
@rest_utils.ajax(data_required=True)
|
||||||
|
def patch(self, request, project_id):
|
||||||
|
"""Update a single project quota data.
|
||||||
|
|
||||||
|
The PATCH data should be an application/json object with the
|
||||||
|
attributes to set to new quota values.
|
||||||
|
|
||||||
|
This method returns HTTP 204 (no content) on success.
|
||||||
|
"""
|
||||||
|
# Filters only neutron quota fields
|
||||||
|
disabled_quotas = quotas.get_disabled_quotas(request)
|
||||||
|
|
||||||
|
if api.base.is_service_enabled(request, 'network') and \
|
||||||
|
api.neutron.is_extension_supported(request, 'quotas'):
|
||||||
|
neutron_data = {
|
||||||
|
key: request.DATA[key] for key in quotas.NEUTRON_QUOTA_FIELDS
|
||||||
|
if key not in disabled_quotas
|
||||||
|
}
|
||||||
|
|
||||||
|
api.neutron.tenant_quota_update(request,
|
||||||
|
project_id,
|
||||||
|
**neutron_data)
|
||||||
|
else:
|
||||||
|
message = _('Service Neutron is disabled or quotas extension not '
|
||||||
|
'available.')
|
||||||
|
raise rest_utils.AjaxError(501, message)
|
||||||
|
@ -572,3 +572,51 @@ class DefaultQuotaSets(generic.View):
|
|||||||
api.nova.default_quota_update(request, **nova_data)
|
api.nova.default_quota_update(request, **nova_data)
|
||||||
else:
|
else:
|
||||||
raise rest_utils.AjaxError(501, _('Service Nova is disabled.'))
|
raise rest_utils.AjaxError(501, _('Service Nova is disabled.'))
|
||||||
|
|
||||||
|
|
||||||
|
@urls.register
|
||||||
|
class EditableQuotaSets(generic.View):
|
||||||
|
"""API for editable quotas.
|
||||||
|
"""
|
||||||
|
url_regex = r'nova/quota-sets/editable/$'
|
||||||
|
|
||||||
|
@rest_utils.ajax()
|
||||||
|
def get(self, request):
|
||||||
|
"""Get a list of editable quota fields.
|
||||||
|
|
||||||
|
The listing result is an object with property "items". Each item
|
||||||
|
is an editable quota. Returns an empty list in case no editable
|
||||||
|
quota is found.
|
||||||
|
"""
|
||||||
|
disabled_quotas = quotas.get_disabled_quotas(request)
|
||||||
|
editable_quotas = [quota for quota in quotas.QUOTA_FIELDS
|
||||||
|
if quota not in disabled_quotas]
|
||||||
|
return {'items': editable_quotas}
|
||||||
|
|
||||||
|
|
||||||
|
@urls.register
|
||||||
|
class QuotaSets(generic.View):
|
||||||
|
"""API for setting quotas for a given project.
|
||||||
|
"""
|
||||||
|
url_regex = r'nova/quota-sets/(?P<project_id>[0-9a-f]+)$'
|
||||||
|
|
||||||
|
@rest_utils.ajax(data_required=True)
|
||||||
|
def patch(self, request, project_id):
|
||||||
|
"""Update a single project quota data.
|
||||||
|
|
||||||
|
The PATCH data should be an application/json object with the
|
||||||
|
attributes to set to new quota values.
|
||||||
|
|
||||||
|
This method returns HTTP 204 (no content) on success.
|
||||||
|
"""
|
||||||
|
disabled_quotas = quotas.get_disabled_quotas(request)
|
||||||
|
|
||||||
|
if api.base.is_service_enabled(request, 'compute'):
|
||||||
|
nova_data = {
|
||||||
|
key: request.DATA[key] for key in quotas.NOVA_QUOTA_FIELDS
|
||||||
|
if key not in disabled_quotas
|
||||||
|
}
|
||||||
|
|
||||||
|
api.nova.tenant_quota_update(request, project_id, **nova_data)
|
||||||
|
else:
|
||||||
|
raise rest_utils.AjaxError(501, _('Service Nova is disabled.'))
|
||||||
|
@ -47,7 +47,8 @@
|
|||||||
getAbsoluteLimits: getAbsoluteLimits,
|
getAbsoluteLimits: getAbsoluteLimits,
|
||||||
getServices: getServices,
|
getServices: getServices,
|
||||||
getDefaultQuotaSets: getDefaultQuotaSets,
|
getDefaultQuotaSets: getDefaultQuotaSets,
|
||||||
setDefaultQuotaSets: setDefaultQuotaSets
|
setDefaultQuotaSets: setDefaultQuotaSets,
|
||||||
|
updateProjectQuota: updateProjectQuota
|
||||||
};
|
};
|
||||||
|
|
||||||
return service;
|
return service;
|
||||||
@ -199,8 +200,7 @@
|
|||||||
var config = params ? {'params': params} : {};
|
var config = params ? {'params': params} : {};
|
||||||
return apiService.get('/api/cinder/volumesnapshots/', config)
|
return apiService.get('/api/cinder/volumesnapshots/', config)
|
||||||
.error(function () {
|
.error(function () {
|
||||||
toastService.add('error',
|
toastService.add('error', gettext('Unable to retrieve the volume snapshots.'));
|
||||||
gettext('Unable to retrieve the volume snapshots.'));
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -322,5 +322,24 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Quota Sets
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name updateProjectQuota
|
||||||
|
* @description
|
||||||
|
* Update a single project quota data.
|
||||||
|
* @param {application/json} quota
|
||||||
|
* A JSON object with the atributes to set to new quota values.
|
||||||
|
* @param {string} projectId
|
||||||
|
* Specifies the id of the project that'll have the quota data updated.
|
||||||
|
*/
|
||||||
|
function updateProjectQuota(quota, projectId) {
|
||||||
|
var url = '/api/cinder/quota-sets/' + projectId;
|
||||||
|
return apiService.patch(url, quota)
|
||||||
|
.error(function() {
|
||||||
|
toastService.add('error', gettext('Unable to update project quota data.'));
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}());
|
}());
|
||||||
|
@ -158,6 +158,13 @@
|
|||||||
method: 'patch',
|
method: 'patch',
|
||||||
path: '/api/cinder/quota-sets/defaults/',
|
path: '/api/cinder/quota-sets/defaults/',
|
||||||
error: 'Unable to set the default quotas.'
|
error: 'Unable to set the default quotas.'
|
||||||
|
},
|
||||||
|
{ func: 'updateProjectQuota',
|
||||||
|
method: 'patch',
|
||||||
|
path: '/api/cinder/quota-sets/42',
|
||||||
|
data: {'volumes': 42},
|
||||||
|
error: 'Unable to update project quota data.',
|
||||||
|
testInput: [{'volumes': 42}, 42]
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -41,7 +41,9 @@
|
|||||||
createSubnet: createSubnet,
|
createSubnet: createSubnet,
|
||||||
getPorts: getPorts,
|
getPorts: getPorts,
|
||||||
getAgents: getAgents,
|
getAgents: getAgents,
|
||||||
getExtensions: getExtensions
|
getExtensions: getExtensions,
|
||||||
|
getDefaultQuotaSets: getDefaultQuotaSets,
|
||||||
|
updateProjectQuota: updateProjectQuota
|
||||||
};
|
};
|
||||||
|
|
||||||
return service;
|
return service;
|
||||||
@ -297,6 +299,43 @@
|
|||||||
toastService.add('error', gettext('Unable to retrieve the extensions.'));
|
toastService.add('error', gettext('Unable to retrieve the extensions.'));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Default Quota Sets
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name 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/neutron/quota-sets/defaults/')
|
||||||
|
.error(function() {
|
||||||
|
toastService.add('error', gettext('Unable to retrieve the default quotas.'));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Quotas Extension
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name updateProjectQuota
|
||||||
|
* @description
|
||||||
|
* Update a single project quota data.
|
||||||
|
* @param {application/json} quota
|
||||||
|
* A JSON object with the atributes to set to new quota values.
|
||||||
|
* @param {string} projectId
|
||||||
|
* Specifies the id of the project that'll have the quota data updated.
|
||||||
|
*/
|
||||||
|
function updateProjectQuota(quota, projectId) {
|
||||||
|
var url = '/api/neutron/quotas-sets/' + projectId;
|
||||||
|
return apiService.patch(url, quota)
|
||||||
|
.error(function() {
|
||||||
|
toastService.add('error', gettext('Unable to update project quota data.'));
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}());
|
}());
|
||||||
|
@ -104,6 +104,27 @@
|
|||||||
"method": "get",
|
"method": "get",
|
||||||
"path": "/api/neutron/extensions/",
|
"path": "/api/neutron/extensions/",
|
||||||
"error": "Unable to retrieve the extensions."
|
"error": "Unable to retrieve the extensions."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"func": "getDefaultQuotaSets",
|
||||||
|
"method": "get",
|
||||||
|
"path": "/api/neutron/quota-sets/defaults/",
|
||||||
|
"error": "Unable to retrieve the default quotas."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"func": "updateProjectQuota",
|
||||||
|
"method": "patch",
|
||||||
|
"path": "/api/neutron/quotas-sets/42",
|
||||||
|
"data": {
|
||||||
|
"network": 42
|
||||||
|
},
|
||||||
|
"error": "Unable to update project quota data.",
|
||||||
|
"testInput": [
|
||||||
|
{
|
||||||
|
"network": 42
|
||||||
|
},
|
||||||
|
42
|
||||||
|
]
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -62,7 +62,9 @@
|
|||||||
updateFlavor: updateFlavor,
|
updateFlavor: updateFlavor,
|
||||||
deleteFlavor: deleteFlavor,
|
deleteFlavor: deleteFlavor,
|
||||||
getDefaultQuotaSets: getDefaultQuotaSets,
|
getDefaultQuotaSets: getDefaultQuotaSets,
|
||||||
setDefaultQuotaSets: setDefaultQuotaSets
|
setDefaultQuotaSets: setDefaultQuotaSets,
|
||||||
|
getEditableQuotas: getEditableQuotas,
|
||||||
|
updateProjectQuota: updateProjectQuota
|
||||||
};
|
};
|
||||||
|
|
||||||
return service;
|
return service;
|
||||||
@ -516,7 +518,7 @@
|
|||||||
// Default Quota Sets
|
// Default Quota Sets
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @name horizon.app.core.openstack-service-api.nova.getDefaultQuotaSets
|
* @name getDefaultQuotaSets
|
||||||
* @description
|
* @description
|
||||||
* Get default quotasets
|
* Get default quotasets
|
||||||
*
|
*
|
||||||
@ -532,7 +534,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @name horizon.app.core.openstack-service-api.nova.setDefaultQuotaSets
|
* @name setDefaultQuotaSets
|
||||||
* @description
|
* @description
|
||||||
* Set default quotasets
|
* Set default quotasets
|
||||||
*
|
*
|
||||||
@ -544,6 +546,40 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Quota Sets
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name getEditableQuotas
|
||||||
|
* @description
|
||||||
|
* Get a list of editable quota fields.
|
||||||
|
* The listing result is an object with property "items." Each item is
|
||||||
|
* an editable quota field.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function getEditableQuotas() {
|
||||||
|
return apiService.get('/api/nova/quota-sets/editable/')
|
||||||
|
.error(function() {
|
||||||
|
toastService.add('error', gettext('Unable to retrieve the editable quotas.'));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name updateProjectQuota
|
||||||
|
* @description
|
||||||
|
* Update a single project quota data.
|
||||||
|
* @param {application/json} quota
|
||||||
|
* A JSON object with the atributes to set to new quota values.
|
||||||
|
* @param {string} projectId
|
||||||
|
* Specifies the id of the project that'll have the quota data updated.
|
||||||
|
*/
|
||||||
|
function updateProjectQuota(quota, projectId) {
|
||||||
|
var url = '/api/nova/quota-sets/' + projectId;
|
||||||
|
return apiService.patch(url, quota)
|
||||||
|
.error(function() {
|
||||||
|
toastService.add('error', gettext('Unable to update project quota data.'));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ngdoc function
|
* @ngdoc function
|
||||||
* @name getCreateKeypairUrl
|
* @name getCreateKeypairUrl
|
||||||
|
@ -343,6 +343,27 @@
|
|||||||
],
|
],
|
||||||
"path": "/api/nova/quota-sets/defaults/",
|
"path": "/api/nova/quota-sets/defaults/",
|
||||||
"error": "Unable to set the default quotas."
|
"error": "Unable to set the default quotas."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"func": "getEditableQuotas",
|
||||||
|
"method": "get",
|
||||||
|
"path": "/api/nova/quota-sets/editable/",
|
||||||
|
"error": "Unable to retrieve the editable quotas."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"func": "updateProjectQuota",
|
||||||
|
"method": "patch",
|
||||||
|
"path": "/api/nova/quota-sets/42",
|
||||||
|
"data": {
|
||||||
|
"cores": 42
|
||||||
|
},
|
||||||
|
"error": "Unable to update project quota data.",
|
||||||
|
"testInput": [
|
||||||
|
{
|
||||||
|
"cores": 42
|
||||||
|
},
|
||||||
|
42
|
||||||
|
]
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -346,3 +346,55 @@ class CinderRestTestCase(test.TestCase):
|
|||||||
'"Service Cinder is disabled."')
|
'"Service Cinder is disabled."')
|
||||||
|
|
||||||
cc.default_quota_update.assert_not_called()
|
cc.default_quota_update.assert_not_called()
|
||||||
|
|
||||||
|
@mock.patch.object(cinder.api, 'cinder')
|
||||||
|
@mock.patch.object(cinder, 'quotas')
|
||||||
|
def test_quota_sets_patch(self, qc, cc):
|
||||||
|
quota_set = self.cinder_quotas.list()[0]
|
||||||
|
quota_data = {}
|
||||||
|
|
||||||
|
for quota in quota_set:
|
||||||
|
quota_data[quota.name] = quota.limit
|
||||||
|
|
||||||
|
request = self.mock_rest_request(body='''
|
||||||
|
{"volumes": "15", "snapshots": "5000",
|
||||||
|
"gigabytes": "5", "cores": "10"}
|
||||||
|
''')
|
||||||
|
|
||||||
|
qc.get_disabled_quotas.return_value = []
|
||||||
|
qc.CINDER_QUOTA_FIELDS = (n for n in quota_data)
|
||||||
|
cc.is_volume_service_enabled.return_value = True
|
||||||
|
|
||||||
|
response = cinder.QuotaSets().patch(request, 'spam123')
|
||||||
|
|
||||||
|
self.assertStatusCode(response, 204)
|
||||||
|
self.assertEqual(response.content.decode('utf-8'), '')
|
||||||
|
cc.tenant_quota_update.assert_called_once_with(request, 'spam123',
|
||||||
|
volumes='15',
|
||||||
|
snapshots='5000',
|
||||||
|
gigabytes='5')
|
||||||
|
|
||||||
|
@mock.patch.object(cinder.api, 'cinder')
|
||||||
|
@mock.patch.object(cinder, 'quotas')
|
||||||
|
def test_quota_sets_when_service_is_disabled(self, qc, cc):
|
||||||
|
quota_set = self.cinder_quotas.list()[0]
|
||||||
|
quota_data = {}
|
||||||
|
|
||||||
|
for quota in quota_set:
|
||||||
|
quota_data[quota.name] = quota.limit
|
||||||
|
|
||||||
|
request = self.mock_rest_request(body='''
|
||||||
|
{"volumes": "15", "snapshots": "5000",
|
||||||
|
"gigabytes": "5", "cores": "10"}
|
||||||
|
''')
|
||||||
|
|
||||||
|
qc.get_disabled_quotas.return_value = []
|
||||||
|
qc.CINDER_QUOTA_FIELDS = (n for n in quota_data)
|
||||||
|
cc.is_volume_service_enabled.return_value = False
|
||||||
|
|
||||||
|
response = cinder.QuotaSets().patch(request, 'spam123')
|
||||||
|
|
||||||
|
self.assertStatusCode(response, 501)
|
||||||
|
self.assertEqual(response.content.decode('utf-8'),
|
||||||
|
'"Service Cinder is disabled."')
|
||||||
|
cc.tenant_quota_update.assert_not_called()
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
import mock
|
import mock
|
||||||
|
|
||||||
from openstack_dashboard import api
|
from openstack_dashboard import api
|
||||||
|
from openstack_dashboard.api import base
|
||||||
from openstack_dashboard.api.rest import neutron
|
from openstack_dashboard.api.rest import neutron
|
||||||
from openstack_dashboard.test import helpers as test
|
from openstack_dashboard.test import helpers as test
|
||||||
from openstack_dashboard.test.test_data import neutron_data
|
from openstack_dashboard.test.test_data import neutron_data
|
||||||
@ -174,6 +175,109 @@ class NeutronExtensionsTestCase(test.TestCase):
|
|||||||
nc.list_extensions.assert_called_once_with(request)
|
nc.list_extensions.assert_called_once_with(request)
|
||||||
|
|
||||||
|
|
||||||
|
class NeutronDefaultQuotasTestCase(test.TestCase):
|
||||||
|
@test.create_stubs({base: ('is_service_enabled',)})
|
||||||
|
@mock.patch.object(neutron.api, 'neutron')
|
||||||
|
def test_quotas_sets_defaults_get_when_service_is_enabled(self, client):
|
||||||
|
filters = {'user': {'tenant_id': 'tenant'}}
|
||||||
|
request = self.mock_rest_request(**{'GET': dict(filters)})
|
||||||
|
|
||||||
|
base.is_service_enabled(request, 'network').AndReturn(True)
|
||||||
|
|
||||||
|
client.tenant_quota_get.return_value = [
|
||||||
|
base.Quota("network", 100),
|
||||||
|
base.Quota("q2", 101)]
|
||||||
|
|
||||||
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
|
response = neutron.DefaultQuotaSets().get(request)
|
||||||
|
self.assertStatusCode(response, 200)
|
||||||
|
self.assertItemsCollectionEqual(response, [
|
||||||
|
{'limit': 100, 'display_name': 'Networks', 'name': 'network'},
|
||||||
|
{'limit': 101, 'display_name': 'Q2', 'name': 'q2'}])
|
||||||
|
|
||||||
|
client.tenant_quota_get.assert_called_once_with(
|
||||||
|
request,
|
||||||
|
request.user.tenant_id)
|
||||||
|
|
||||||
|
@test.create_stubs({neutron.api.base: ('is_service_enabled',)})
|
||||||
|
@mock.patch.object(neutron.api, 'neutron')
|
||||||
|
def test_quota_sets_defaults_get_when_service_is_disabled(self, client):
|
||||||
|
filters = {'user': {'tenant_id': 'tenant'}}
|
||||||
|
request = self.mock_rest_request(**{'GET': dict(filters)})
|
||||||
|
|
||||||
|
base.is_service_enabled(request, 'network').AndReturn(False)
|
||||||
|
|
||||||
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
|
response = neutron.DefaultQuotaSets().get(request)
|
||||||
|
self.assertStatusCode(response, 501)
|
||||||
|
self.assertEqual(response.content.decode('utf-8'),
|
||||||
|
'"Service Neutron is disabled."')
|
||||||
|
|
||||||
|
client.tenant_quota_get.assert_not_called()
|
||||||
|
|
||||||
|
|
||||||
|
class NeutronQuotaSetsTestCase(test.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
super(NeutronQuotaSetsTestCase, self).setUp()
|
||||||
|
|
||||||
|
quota_set = self.neutron_quotas.list()[0]
|
||||||
|
self._quota_data = {}
|
||||||
|
|
||||||
|
for quota in quota_set:
|
||||||
|
self._quota_data[quota.name] = quota.limit
|
||||||
|
|
||||||
|
@mock.patch.object(neutron, 'quotas')
|
||||||
|
@mock.patch.object(neutron.api, 'neutron')
|
||||||
|
@mock.patch.object(neutron.api, 'base')
|
||||||
|
def test_quotas_sets_patch(self, bc, nc, qc):
|
||||||
|
request = self.mock_rest_request(body='''
|
||||||
|
{"network": "5", "subnet": "5", "port": "50",
|
||||||
|
"router": "5", "floatingip": "50",
|
||||||
|
"security_group": "5", "security_group_rule": "50",
|
||||||
|
"volumes": "5", "cores": "50"}
|
||||||
|
''')
|
||||||
|
|
||||||
|
qc.get_disabled_quotas.return_value = []
|
||||||
|
qc.NEUTRON_QUOTA_FIELDS = (n for n in self._quota_data)
|
||||||
|
bc.is_service_enabled.return_value = True
|
||||||
|
nc.is_extension_supported.return_value = True
|
||||||
|
|
||||||
|
response = neutron.QuotasSets().patch(request, 'spam123')
|
||||||
|
|
||||||
|
self.assertStatusCode(response, 204)
|
||||||
|
self.assertEqual(response.content.decode('utf-8'), '')
|
||||||
|
nc.tenant_quota_update.assert_called_once_with(
|
||||||
|
request, 'spam123', network='5',
|
||||||
|
subnet='5', port='50', router='5',
|
||||||
|
floatingip='50', security_group='5',
|
||||||
|
security_group_rule='50')
|
||||||
|
|
||||||
|
@mock.patch.object(neutron, 'quotas')
|
||||||
|
@mock.patch.object(neutron.api, 'neutron')
|
||||||
|
@mock.patch.object(neutron.api, 'base')
|
||||||
|
def test_quotas_sets_patch_when_service_is_disabled(self, bc, nc, qc):
|
||||||
|
request = self.mock_rest_request(body='''
|
||||||
|
{"network": "5", "subnet": "5", "port": "50",
|
||||||
|
"router": "5", "floatingip": "50",
|
||||||
|
"security_group": "5", "security_group_rule": "50",
|
||||||
|
"volumes": "5", "cores": "50"}
|
||||||
|
''')
|
||||||
|
|
||||||
|
qc.get_disabled_quotas.return_value = []
|
||||||
|
qc.NEUTRON_QUOTA_FIELDS = (n for n in self._quota_data)
|
||||||
|
bc.is_service_enabled.return_value = False
|
||||||
|
|
||||||
|
response = neutron.QuotasSets().patch(request, 'spam123')
|
||||||
|
message = \
|
||||||
|
'"Service Neutron is disabled or quotas extension not available."'
|
||||||
|
|
||||||
|
self.assertStatusCode(response, 501)
|
||||||
|
self.assertEqual(response.content.decode('utf-8'), message)
|
||||||
|
nc.tenant_quota_update.assert_not_called()
|
||||||
|
|
||||||
|
|
||||||
def mock_obj_to_dict(r):
|
def mock_obj_to_dict(r):
|
||||||
return mock.Mock(**{'to_dict.return_value': r})
|
return mock.Mock(**{'to_dict.return_value': r})
|
||||||
|
|
||||||
|
@ -769,3 +769,79 @@ class NovaRestTestCase(test.TestCase):
|
|||||||
'"Service Nova is disabled."')
|
'"Service Nova is disabled."')
|
||||||
|
|
||||||
nc.default_quota_update.assert_not_called()
|
nc.default_quota_update.assert_not_called()
|
||||||
|
|
||||||
|
@mock.patch.object(nova, 'quotas')
|
||||||
|
@mock.patch.object(nova.api, 'nova')
|
||||||
|
def test_editable_quotas_get(self, nc, qc):
|
||||||
|
disabled_quotas = ['floating_ips', 'fixed_ips',
|
||||||
|
'security_groups', 'security_group_rules']
|
||||||
|
editable_quotas = ['cores', 'volumes', 'network', 'fixed_ips']
|
||||||
|
qc.get_disabled_quotas.return_value = disabled_quotas
|
||||||
|
qc.QUOTA_FIELDS = editable_quotas
|
||||||
|
request = self.mock_rest_request()
|
||||||
|
response = nova.EditableQuotaSets().get(request)
|
||||||
|
self.assertStatusCode(response, 200)
|
||||||
|
self.assertItemsCollectionEqual(response,
|
||||||
|
['cores', 'volumes', 'network'])
|
||||||
|
|
||||||
|
@mock.patch.object(nova.api, 'nova')
|
||||||
|
@mock.patch.object(nova.api, 'base')
|
||||||
|
@mock.patch.object(nova, 'quotas')
|
||||||
|
def test_quota_sets_patch(self, qc, bc, nc):
|
||||||
|
quota_data = dict(cores='15', instances='5',
|
||||||
|
ram='50000', metadata_items='150',
|
||||||
|
injected_files='5',
|
||||||
|
injected_file_content_bytes='10240',
|
||||||
|
floating_ips='50', fixed_ips='5',
|
||||||
|
security_groups='10',
|
||||||
|
security_group_rules='100')
|
||||||
|
|
||||||
|
request = self.mock_rest_request(body='''
|
||||||
|
{"cores": "15", "ram": "50000", "instances": "5",
|
||||||
|
"metadata_items": "150", "injected_files": "5",
|
||||||
|
"injected_file_content_bytes": "10240", "floating_ips": "50",
|
||||||
|
"fixed_ips": "5", "security_groups": "10" ,
|
||||||
|
"security_group_rules": "100", "volumes": "10"}
|
||||||
|
''')
|
||||||
|
|
||||||
|
qc.get_disabled_quotas.return_value = []
|
||||||
|
qc.NOVA_QUOTA_FIELDS = (n for n in quota_data)
|
||||||
|
bc.is_service_enabled.return_value = True
|
||||||
|
|
||||||
|
response = nova.QuotaSets().patch(request, 'spam123')
|
||||||
|
|
||||||
|
self.assertStatusCode(response, 204)
|
||||||
|
self.assertEqual(response.content.decode('utf-8'), '')
|
||||||
|
nc.tenant_quota_update.assert_called_once_with(
|
||||||
|
request, 'spam123', **quota_data)
|
||||||
|
|
||||||
|
@mock.patch.object(nova.api, 'nova')
|
||||||
|
@mock.patch.object(nova.api, 'base')
|
||||||
|
@mock.patch.object(nova, 'quotas')
|
||||||
|
def test_quota_sets_patch_when_service_is_disabled(self, qc, bc, nc):
|
||||||
|
quota_data = dict(cores='15', instances='5',
|
||||||
|
ram='50000', metadata_items='150',
|
||||||
|
injected_files='5',
|
||||||
|
injected_file_content_bytes='10240',
|
||||||
|
floating_ips='50', fixed_ips='5',
|
||||||
|
security_groups='10',
|
||||||
|
security_group_rules='100')
|
||||||
|
|
||||||
|
request = self.mock_rest_request(body='''
|
||||||
|
{"cores": "15", "ram": "50000", "instances": "5",
|
||||||
|
"metadata_items": "150", "injected_files": "5",
|
||||||
|
"injected_file_content_bytes": "10240", "floating_ips": "50",
|
||||||
|
"fixed_ips": "5", "security_groups": "10" ,
|
||||||
|
"security_group_rules": "100", "volumes": "10"}
|
||||||
|
''')
|
||||||
|
|
||||||
|
qc.get_disabled_quotas.return_value = []
|
||||||
|
qc.NOVA_QUOTA_FIELDS = (n for n in quota_data)
|
||||||
|
bc.is_service_enabled.return_value = False
|
||||||
|
|
||||||
|
response = nova.QuotaSets().patch(request, 'spam123')
|
||||||
|
|
||||||
|
self.assertStatusCode(response, 501)
|
||||||
|
self.assertEqual(response.content.decode('utf-8'),
|
||||||
|
'"Service Nova is disabled."')
|
||||||
|
nc.tenant_quota_update.assert_not_called()
|
||||||
|
Loading…
Reference in New Issue
Block a user