Merge "Pay attention to Nova disabled quotas defined in a config file"

This commit is contained in:
Jenkins 2016-07-21 10:48:25 +00:00 committed by Gerrit Code Review
commit dc015f556c
8 changed files with 130 additions and 59 deletions

View File

@ -781,6 +781,7 @@ Default::
'can_set_mount_point': False,
'can_set_password': False,
'requires_keypair': False,
'enable_quotas': True
}
A dictionary containing settings which can be used to identify the
@ -797,6 +798,9 @@ an administrator password when launching or rebuilding an instance.
Setting ``requires_keypair`` to ``True`` will require users to select
a key pair when launching an instance.
Setting ``enable_quotas`` to ``False`` will make Horizon treat all Nova
quotas as disabled, thus it won't try to modify them. By default, quotas are
enabled.
``OPENSTACK_IMAGE_BACKEND``
---------------------------

View File

@ -811,7 +811,8 @@ def tenant_quota_get(request, tenant_id):
def tenant_quota_update(request, tenant_id, **kwargs):
novaclient(request).quotas.update(tenant_id, **kwargs)
if kwargs:
novaclient(request).quotas.update(tenant_id, **kwargs)
def default_quota_get(request, tenant_id):
@ -1083,3 +1084,8 @@ def can_set_mount_point():
def requires_keypair():
features = getattr(settings, 'OPENSTACK_HYPERVISOR_FEATURES', {})
return features.get('requires_keypair', False)
def can_set_quotas():
features = getattr(settings, 'OPENSTACK_HYPERVISOR_FEATURES', {})
return features.get('enable_quotas', True)

View File

@ -312,9 +312,10 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
'tenant_quota_usages',),
api.cinder: ('tenant_quota_update',),
api.nova: ('tenant_quota_update',)})
def test_add_project_post(self, neutron=False):
def test_add_project_post(self):
project = self.tenants.first()
quota = self.quotas.first()
disabled_quotas = self.disabled_quotas.first()
default_role = self.roles.first()
default_domain = self._get_default_domain()
domain_id = default_domain.id
@ -325,9 +326,6 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
# init
quotas.get_disabled_quotas(IsA(http.HttpRequest)) \
.AndReturn(self.disabled_quotas.first())
if neutron:
quotas.get_disabled_quotas(IsA(http.HttpRequest)) \
.AndReturn(self.disabled_quotas.first())
quotas.get_default_quota_data(IsA(http.HttpRequest)).AndReturn(quota)
api.keystone.get_default_role(IsA(http.HttpRequest)) \
@ -364,8 +362,10 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
group=group_id,
project=self.tenant.id)
nova_updated_quota = dict([(key, quota_data[key]) for key in
quotas.NOVA_QUOTA_FIELDS])
nova_updated_quota = {key: quota_data[key] for key in
set(quotas.NOVA_QUOTA_FIELDS) - disabled_quotas}
quotas.get_disabled_quotas(IsA(http.HttpRequest)) \
.AndReturn(disabled_quotas)
api.nova.tenant_quota_update(IsA(http.HttpRequest),
project.id,
**nova_updated_quota)
@ -406,7 +406,7 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
api.neutron.tenant_quota_update(IsA(http.HttpRequest),
self.tenant.id,
**neutron_updated_quota)
self.test_add_project_post(neutron=True)
self.test_add_project_post()
@test.create_stubs({api.keystone: ('user_list',
'role_list',
@ -531,6 +531,7 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
def test_add_project_quota_update_error(self):
project = self.tenants.first()
quota = self.quotas.first()
disabled_quotas = self.disabled_quotas.first()
default_role = self.roles.first()
default_domain = self._get_default_domain()
domain_id = default_domain.id
@ -579,8 +580,10 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
group=group_id,
project=self.tenant.id)
nova_updated_quota = dict([(key, quota_data[key]) for key in
quotas.NOVA_QUOTA_FIELDS])
nova_updated_quota = {key: quota_data[key] for key in
set(quotas.NOVA_QUOTA_FIELDS) - disabled_quotas}
quotas.get_disabled_quotas(IsA(http.HttpRequest)) \
.AndReturn(disabled_quotas)
api.nova.tenant_quota_update(IsA(http.HttpRequest),
project.id,
**nova_updated_quota) \
@ -617,6 +620,7 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
def test_add_project_user_update_error(self):
project = self.tenants.first()
quota = self.quotas.first()
disabled_quotas = self.disabled_quotas.first()
default_role = self.roles.first()
default_domain = self._get_default_domain()
domain_id = default_domain.id
@ -660,8 +664,10 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
break
break
nova_updated_quota = dict([(key, quota_data[key]) for key in
quotas.NOVA_QUOTA_FIELDS])
nova_updated_quota = {key: quota_data[key] for key in
set(quotas.NOVA_QUOTA_FIELDS) - disabled_quotas}
quotas.get_disabled_quotas(IsA(http.HttpRequest)) \
.AndReturn(disabled_quotas)
api.nova.tenant_quota_update(IsA(http.HttpRequest),
project.id,
**nova_updated_quota)
@ -961,11 +967,12 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
quotas: ('get_tenant_quota_data',
'get_disabled_quotas',
'tenant_quota_usages',)})
def test_update_project_save(self, neutron=False):
def test_update_project_save(self):
keystone_api_version = api.keystone.VERSIONS.active
project = self.tenants.first()
quota = self.quotas.first()
disabled_quotas = self.disabled_quotas.first()
default_role = self.roles.first()
domain_id = project.domain_id
users = self._get_all_users(domain_id)
@ -983,9 +990,6 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
.AndReturn(self.domain)
quotas.get_disabled_quotas(IsA(http.HttpRequest)) \
.AndReturn(self.disabled_quotas.first())
if neutron:
quotas.get_disabled_quotas(IsA(http.HttpRequest)) \
.AndReturn(self.disabled_quotas.first())
quotas.get_tenant_quota_data(IsA(http.HttpRequest),
tenant_id=self.tenant.id) \
.AndReturn(quota)
@ -1047,8 +1051,10 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
quotas.tenant_quota_usages(IsA(http.HttpRequest), tenant_id=project.id) \
.AndReturn(quota_usages)
nova_updated_quota = dict([(key, updated_quota[key]) for key in
quotas.NOVA_QUOTA_FIELDS])
nova_updated_quota = {key: updated_quota[key] for key in
set(quotas.NOVA_QUOTA_FIELDS) - disabled_quotas}
quotas.get_disabled_quotas(IsA(http.HttpRequest)) \
.AndReturn(disabled_quotas)
api.nova.tenant_quota_update(IsA(http.HttpRequest),
project.id,
**nova_updated_quota)
@ -1093,7 +1099,7 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
api.neutron.tenant_quota_update(IsA(http.HttpRequest),
self.tenant.id,
**neutron_updated_quota)
self.test_update_project_save(neutron=True)
self.test_update_project_save()
@test.create_stubs({api.keystone: ('tenant_get',)})
def test_update_project_get_error(self):
@ -1256,6 +1262,7 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
project = self.tenants.first()
quota = self.quotas.first()
disabled_quotas = self.disabled_quotas.first()
default_role = self.roles.first()
domain_id = project.domain_id
users = self._get_all_users(domain_id)
@ -1335,8 +1342,10 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
quotas.tenant_quota_usages(IsA(http.HttpRequest), tenant_id=project.id) \
.AndReturn(quota_usages)
nova_updated_quota = dict([(key, updated_quota[key]) for key in
quotas.NOVA_QUOTA_FIELDS])
nova_updated_quota = {key: updated_quota[key] for key in
set(quotas.NOVA_QUOTA_FIELDS) - disabled_quotas}
quotas.get_disabled_quotas(IsA(http.HttpRequest)) \
.AndReturn(disabled_quotas)
api.nova.tenant_quota_update(IsA(http.HttpRequest),
project.id,
**nova_updated_quota) \
@ -1458,6 +1467,8 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
api.keystone.user_list(IsA(http.HttpRequest),
domain=domain_id).AndReturn(users)
quotas.get_disabled_quotas(IsA(http.HttpRequest)) \
.AndReturn(self.disabled_quotas.first())
self._check_role_list(keystone_api_version, role_assignments, groups,
proj_users, roles, workflow_data)

View File

@ -385,9 +385,9 @@ class UpdateProjectGroups(workflows.UpdateMembersStep):
class CommonQuotaWorkflow(workflows.Workflow):
def _update_project_quota(self, request, data, project_id):
# Update the project quota.
nova_data = dict(
[(key, data[key]) for key in quotas.NOVA_QUOTA_FIELDS])
disabled_quotas = quotas.get_disabled_quotas(request)
nova_data = {key: data[key] for key in
set(quotas.NOVA_QUOTA_FIELDS) - disabled_quotas}
nova.tenant_quota_update(request, project_id, **nova_data)
if cinder.is_volume_service_enabled(request):
@ -400,7 +400,6 @@ class CommonQuotaWorkflow(workflows.Workflow):
if api.base.is_service_enabled(request, 'network') and \
api.neutron.is_quotas_extension_supported(request):
neutron_data = {}
disabled_quotas = quotas.get_disabled_quotas(request)
for key in quotas.NEUTRON_QUOTA_FIELDS:
if key not in disabled_quotas:
neutron_data[key] = data[key]

View File

@ -251,6 +251,7 @@ OPENSTACK_HYPERVISOR_FEATURES = {
'can_set_mount_point': False,
'can_set_password': False,
'requires_keypair': False,
'enable_quotas': True
}
# The OPENSTACK_CINDER_FEATURES settings can be used to enable optional

View File

@ -403,8 +403,8 @@ def data(TEST):
TEST.quotas.add(base.QuotaSet(quota))
# nova quotas disabled when neutron is enabled
disabled_quotas_nova = ['floating_ips', 'fixed_ips',
'security_groups', 'security_group_rules']
disabled_quotas_nova = {'floating_ips', 'fixed_ips',
'security_groups', 'security_group_rules'}
TEST.disabled_quotas.add(disabled_quotas_nova)
# Quota Usages

View File

@ -20,6 +20,7 @@
from __future__ import absolute_import
from django import http
from django.test.utils import override_settings
from django.utils.translation import ugettext_lazy as _
from mox3.mox import IsA # noqa
@ -32,17 +33,26 @@ from openstack_dashboard.usage import quotas
class QuotaTests(test.APITestCase):
def get_usages(self, with_volume=True):
usages = {'injected_file_content_bytes': {'quota': 1},
'metadata_items': {'quota': 1},
'injected_files': {'quota': 1},
'security_groups': {'quota': 10},
'security_group_rules': {'quota': 20},
'fixed_ips': {'quota': 10},
'ram': {'available': 8976, 'used': 1024, 'quota': 10000},
'floating_ips': {'available': 0, 'used': 2, 'quota': 1},
'instances': {'available': 8, 'used': 2, 'quota': 10},
'cores': {'available': 8, 'used': 2, 'quota': 10}}
def get_usages(self, with_volume=True, nova_quotas_enabled=True):
if nova_quotas_enabled:
usages = {'injected_file_content_bytes': {'quota': 1},
'metadata_items': {'quota': 1},
'injected_files': {'quota': 1},
'security_groups': {'quota': 10},
'security_group_rules': {'quota': 20},
'fixed_ips': {'quota': 10},
'ram': {'available': 8976, 'used': 1024, 'quota': 10000},
'floating_ips': {'available': 0, 'used': 2, 'quota': 1},
'instances': {'available': 8, 'used': 2, 'quota': 10},
'cores': {'available': 8, 'used': 2, 'quota': 10}}
else:
inf = float('inf')
usages = {'security_groups': {'available': inf, 'quota': inf},
'ram': {'available': inf, 'used': 1024, 'quota': inf},
'floating_ips': {
'available': inf, 'used': 2, 'quota': inf},
'instances': {'available': inf, 'used': 2, 'quota': inf},
'cores': {'available': inf, 'used': 2, 'quota': inf}}
if with_volume:
usages.update({'volumes': {'available': 0, 'used': 4, 'quota': 1},
'snapshots': {'available': 0, 'used': 3,
@ -51,6 +61,13 @@ class QuotaTests(test.APITestCase):
'quota': 1000}})
return usages
def assertAvailableQuotasEqual(self, expected_usages, actual_usages):
expected_available = {key: value['available'] for key, value in
expected_usages.items() if 'available' in value}
actual_available = {key: value['available'] for key, value in
actual_usages.items() if 'available' in value}
self.assertEqual(expected_available, actual_available)
@test.create_stubs({api.nova: ('server_list',
'flavor_list',
'tenant_quota_get',),
@ -60,13 +77,13 @@ class QuotaTests(test.APITestCase):
cinder: ('volume_list', 'volume_snapshot_list',
'tenant_quota_get',
'is_volume_service_enabled')})
def test_tenant_quota_usages(self):
def _test_tenant_quota_usages(self, nova_quotas_enabled=True,
with_volume=True):
servers = [s for s in self.servers.list()
if s.tenant_id == self.request.user.tenant_id]
cinder.is_volume_service_enabled(
IsA(http.HttpRequest)
).AndReturn(True)
cinder.is_volume_service_enabled(IsA(http.HttpRequest)).AndReturn(
with_volume)
api.base.is_service_enabled(IsA(http.HttpRequest),
'network').AndReturn(False)
api.nova.flavor_list(IsA(http.HttpRequest)) \
@ -81,21 +98,49 @@ class QuotaTests(test.APITestCase):
api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts,
all_tenants=True) \
.AndReturn([servers, False])
opts = {'all_tenants': 1, 'project_id': self.request.user.tenant_id}
cinder.volume_list(IsA(http.HttpRequest), opts) \
.AndReturn(self.volumes.list())
cinder.volume_snapshot_list(IsA(http.HttpRequest), opts) \
.AndReturn(self.cinder_volume_snapshots.list())
cinder.tenant_quota_get(IsA(http.HttpRequest), '1') \
.AndReturn(self.cinder_quotas.first())
if with_volume:
opts = {'all_tenants': 1,
'project_id': self.request.user.tenant_id}
cinder.volume_list(IsA(http.HttpRequest), opts) \
.AndReturn(self.volumes.list())
cinder.volume_snapshot_list(IsA(http.HttpRequest), opts) \
.AndReturn(self.cinder_volume_snapshots.list())
cinder.tenant_quota_get(IsA(http.HttpRequest), '1') \
.AndReturn(self.cinder_quotas.first())
self.mox.ReplayAll()
quota_usages = quotas.tenant_quota_usages(self.request)
expected_output = self.get_usages()
expected_output = self.get_usages(
nova_quotas_enabled=nova_quotas_enabled, with_volume=with_volume)
# Compare internal structure of usages to expected.
self.assertItemsEqual(expected_output, quota_usages.usages)
# Compare available resources
self.assertAvailableQuotasEqual(expected_output, quota_usages.usages)
def test_tenant_quota_usages(self):
self._test_tenant_quota_usages()
@override_settings(OPENSTACK_HYPERVISOR_FEATURES={'enable_quotas': False})
def test_tenant_quota_usages_wo_nova_quotas(self):
self._test_tenant_quota_usages(nova_quotas_enabled=False,
with_volume=False)
@override_settings(OPENSTACK_HYPERVISOR_FEATURES={'enable_quotas': False})
@test.create_stubs({api.base: ('is_service_enabled',),
cinder: ('is_volume_service_enabled',)})
def test_get_all_disabled_quotas(self):
cinder.is_volume_service_enabled(IsA(http.HttpRequest)).AndReturn(
False)
api.base.is_service_enabled(IsA(http.HttpRequest),
'network').AndReturn(False)
self.mox.ReplayAll()
result_quotas = quotas.get_disabled_quotas(self.request)
expected_quotas = list(quotas.CINDER_QUOTA_FIELDS) + \
list(quotas.NEUTRON_QUOTA_FIELDS) + list(quotas.NOVA_QUOTA_FIELDS)
self.assertItemsEqual(result_quotas, expected_quotas)
@test.create_stubs({api.nova: ('server_list',
'flavor_list',

View File

@ -129,7 +129,8 @@ class QuotaUsage(dict):
def update_available(self, name):
"""Updates the "available" metric for the given quota."""
available = self.usages[name]['quota'] - self.usages[name]['used']
quota = self.usages.get(name, {}).get('quota', float('inf'))
available = quota - self.usages[name]['used']
if available < 0:
available = 0
self.usages[name]['available'] = available
@ -148,7 +149,7 @@ def _get_quota_data(request, method_name, disabled_quotas=None,
try:
quotasets.append(getattr(cinder, method_name)(request, tenant_id))
except cinder.cinder_exception.ClientException:
disabled_quotas.extend(CINDER_QUOTA_FIELDS)
disabled_quotas.update(CINDER_QUOTA_FIELDS)
msg = _("Unable to retrieve volume limit information.")
exceptions.handle(request, msg)
for quota in itertools.chain(*quotasets):
@ -228,29 +229,33 @@ def get_tenant_quota_data(request, disabled_quotas=None, tenant_id=None):
def get_disabled_quotas(request):
disabled_quotas = []
disabled_quotas = set([])
# Nova
if not nova.can_set_quotas():
disabled_quotas.update(NOVA_QUOTA_FIELDS)
# Cinder
if not cinder.is_volume_service_enabled(request):
disabled_quotas.extend(CINDER_QUOTA_FIELDS)
disabled_quotas.update(CINDER_QUOTA_FIELDS)
# Neutron
if not base.is_service_enabled(request, 'network'):
disabled_quotas.extend(NEUTRON_QUOTA_FIELDS)
disabled_quotas.update(NEUTRON_QUOTA_FIELDS)
else:
# Remove the nova network quotas
disabled_quotas.extend(['floating_ips', 'fixed_ips'])
disabled_quotas.update(['floating_ips', 'fixed_ips'])
if neutron.is_extension_supported(request, 'security-group'):
# If Neutron security group is supported, disable Nova quotas
disabled_quotas.extend(['security_groups', 'security_group_rules'])
disabled_quotas.update(['security_groups', 'security_group_rules'])
else:
# If Nova security group is used, disable Neutron quotas
disabled_quotas.extend(['security_group', 'security_group_rule'])
disabled_quotas.update(['security_group', 'security_group_rule'])
try:
if not neutron.is_quotas_extension_supported(request):
disabled_quotas.extend(NEUTRON_QUOTA_FIELDS)
disabled_quotas.update(NEUTRON_QUOTA_FIELDS)
except Exception:
LOG.exception("There was an error checking if the Neutron "
"quotas extension is enabled.")