Allow horizon to function without nova
Adds conditional block to nova quotas to exclude them if nova is not enabled; adds 'permission' checks to the project overview and access_and_security panels to only enable them if compute is enabled; adds permission checks on compute and image to the admin overview and metadef panels; disables 'modify quota' and 'view usage' project actions; disables 'update defaults' if there are no quotas available. The 'access and security' panel still appears (under Compute) but tabs other than the keystone endpoint and RC download tab are hidden. Closes-Bug: #1580116 Change-Id: I1b2ddee0395ad9f55692111604b31618c4eaf69e
This commit is contained in:
parent
d3ad1040b2
commit
018e99d20e
@ -27,18 +27,24 @@ from openstack_dashboard.api import nova
|
||||
class NetworkClient(object):
|
||||
def __init__(self, request):
|
||||
neutron_enabled = base.is_service_enabled(request, 'network')
|
||||
nova_enabled = base.is_service_enabled(request, 'compute')
|
||||
|
||||
self.secgroups, self.floating_ips = None, None
|
||||
if neutron_enabled:
|
||||
self.floating_ips = neutron.FloatingIpManager(request)
|
||||
else:
|
||||
elif nova_enabled:
|
||||
self.floating_ips = nova.FloatingIpManager(request)
|
||||
|
||||
if (neutron_enabled and
|
||||
neutron.is_extension_supported(request, 'security-group')):
|
||||
self.secgroups = neutron.SecurityGroupManager(request)
|
||||
else:
|
||||
elif nova_enabled:
|
||||
self.secgroups = nova.SecurityGroupManager(request)
|
||||
|
||||
@property
|
||||
def enabled(self):
|
||||
return self.floating_ips is not None
|
||||
|
||||
|
||||
def floating_ip_pools_list(request):
|
||||
return NetworkClient(request).floating_ips.list_pools()
|
||||
@ -88,7 +94,8 @@ def floating_ip_simple_associate_supported(request):
|
||||
|
||||
|
||||
def floating_ip_supported(request):
|
||||
return NetworkClient(request).floating_ips.is_supported()
|
||||
nwc = NetworkClient(request)
|
||||
return nwc.enabled and nwc.floating_ips.is_supported()
|
||||
|
||||
|
||||
def security_group_list(request):
|
||||
|
@ -16,6 +16,8 @@ from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from horizon import tables
|
||||
|
||||
from openstack_dashboard.usage import quotas
|
||||
|
||||
|
||||
class QuotaFilterAction(tables.FilterAction):
|
||||
def filter(self, table, tenants, filter_string):
|
||||
@ -36,6 +38,9 @@ class UpdateDefaultQuotas(tables.LinkAction):
|
||||
classes = ("ajax-modal",)
|
||||
icon = "pencil"
|
||||
|
||||
def allowed(self, request, _=None):
|
||||
return quotas.enabled_quotas(request)
|
||||
|
||||
|
||||
def get_quota_name(quota):
|
||||
QUOTA_NAMES = {
|
||||
|
@ -46,7 +46,9 @@ class ServicesViewTests(test.BaseAdminViewTests):
|
||||
self.mox.StubOutWithMock(api.neutron, 'is_extension_supported')
|
||||
|
||||
api.cinder.is_volume_service_enabled(IsA(http.HttpRequest)) \
|
||||
.AndReturn(True)
|
||||
.MultipleTimes().AndReturn(True)
|
||||
api.base.is_service_enabled(IsA(http.HttpRequest), 'compute') \
|
||||
.MultipleTimes().AndReturn(True)
|
||||
api.base.is_service_enabled(IsA(http.HttpRequest), 'network') \
|
||||
.MultipleTimes().AndReturn(neutron_enabled)
|
||||
|
||||
@ -57,7 +59,7 @@ class ServicesViewTests(test.BaseAdminViewTests):
|
||||
if neutron_enabled:
|
||||
api.neutron.is_extension_supported(
|
||||
IsA(http.HttpRequest),
|
||||
'security-group').AndReturn(neutron_sg_enabled)
|
||||
'security-group').MultipleTimes().AndReturn(neutron_sg_enabled)
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
|
@ -24,6 +24,7 @@ class MetadataDefinitions(horizon.Panel):
|
||||
name = _("Metadata Definitions")
|
||||
slug = 'metadata_defs'
|
||||
policy_rules = (("image", "get_metadef_namespaces"),)
|
||||
permissions = ('openstack.services.image',)
|
||||
|
||||
@staticmethod
|
||||
def can_register():
|
||||
|
@ -27,6 +27,7 @@ class Overview(horizon.Panel):
|
||||
name = _("Overview")
|
||||
slug = 'overview'
|
||||
policy_rules = (('identity', 'identity:list_projects'),)
|
||||
permissions = ('openstack.services.compute',)
|
||||
|
||||
|
||||
dashboard.Admin.register(Overview)
|
||||
|
@ -24,6 +24,7 @@ from keystoneclient.exceptions import Conflict # noqa
|
||||
|
||||
from openstack_dashboard import api
|
||||
from openstack_dashboard import policy
|
||||
from openstack_dashboard.usage import quotas
|
||||
|
||||
|
||||
class RescopeTokenToProject(tables.LinkAction):
|
||||
@ -103,7 +104,8 @@ class UsageLink(tables.LinkAction):
|
||||
policy_rules = (("compute", "compute_extension:simple_tenant_usage:show"),)
|
||||
|
||||
def allowed(self, request, project):
|
||||
return request.user.is_superuser
|
||||
return (request.user.is_superuser and
|
||||
api.base.is_service_enabled(request, 'compute'))
|
||||
|
||||
|
||||
class CreateProject(tables.LinkAction):
|
||||
@ -153,7 +155,8 @@ class ModifyQuotas(tables.LinkAction):
|
||||
if api.keystone.VERSIONS.active < 3:
|
||||
return True
|
||||
else:
|
||||
return api.keystone.is_cloud_admin(request)
|
||||
return (api.keystone.is_cloud_admin(request) and
|
||||
quotas.enabled_quotas(request))
|
||||
|
||||
def get_link_url(self, project):
|
||||
step = 'update_quotas'
|
||||
|
@ -53,7 +53,8 @@ PROJECT_DETAIL_URL = reverse('horizon:identity:projects:detail', args=[1])
|
||||
class TenantsViewTests(test.BaseAdminViewTests):
|
||||
@test.create_stubs({api.keystone: ('domain_get',
|
||||
'tenant_list',
|
||||
'domain_lookup')})
|
||||
'domain_lookup'),
|
||||
quotas: ('enabled_quotas',)})
|
||||
def test_index(self):
|
||||
domain = self.domains.get(id="1")
|
||||
api.keystone.domain_get(IsA(http.HttpRequest), '1').AndReturn(domain)
|
||||
@ -64,6 +65,8 @@ class TenantsViewTests(test.BaseAdminViewTests):
|
||||
.AndReturn([self.tenants.list(), False])
|
||||
api.keystone.domain_lookup(IgnoreArg()).AndReturn({domain.id:
|
||||
domain.name})
|
||||
quotas.enabled_quotas(IsA(http.HttpRequest)).MultipleTimes()\
|
||||
.AndReturn(('instances',))
|
||||
self.mox.ReplayAll()
|
||||
|
||||
res = self.client.get(INDEX_URL)
|
||||
@ -72,7 +75,8 @@ class TenantsViewTests(test.BaseAdminViewTests):
|
||||
|
||||
@test.create_stubs({api.keystone: ('tenant_list',
|
||||
'get_effective_domain_id',
|
||||
'domain_lookup')})
|
||||
'domain_lookup'),
|
||||
quotas: ('enabled_quotas',)})
|
||||
def test_index_with_domain_context(self):
|
||||
domain = self.domains.get(id="1")
|
||||
|
||||
@ -91,6 +95,7 @@ class TenantsViewTests(test.BaseAdminViewTests):
|
||||
.AndReturn([domain_tenants, False])
|
||||
api.keystone.domain_lookup(IgnoreArg()).AndReturn({domain.id:
|
||||
domain.name})
|
||||
quotas.enabled_quotas(IsA(http.HttpRequest)).AndReturn(('instances',))
|
||||
self.mox.ReplayAll()
|
||||
|
||||
res = self.client.get(INDEX_URL)
|
||||
@ -201,6 +206,8 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
|
||||
# init
|
||||
api.base.is_service_enabled(IsA(http.HttpRequest), 'network') \
|
||||
.MultipleTimes().AndReturn(True)
|
||||
api.base.is_service_enabled(IsA(http.HttpRequest), 'compute') \
|
||||
.MultipleTimes().AndReturn(True)
|
||||
api.cinder.is_volume_service_enabled(IsA(http.HttpRequest)) \
|
||||
.MultipleTimes().AndReturn(True)
|
||||
api.keystone.get_default_domain(IsA(http.HttpRequest)) \
|
||||
@ -1607,12 +1614,14 @@ class UsageViewTests(test.BaseAdminViewTests):
|
||||
|
||||
|
||||
class DetailProjectViewTests(test.BaseAdminViewTests):
|
||||
@test.create_stubs({api.keystone: ('tenant_get',)})
|
||||
@test.create_stubs({api.keystone: ('tenant_get',),
|
||||
quotas: ('enabled_quotas',)})
|
||||
def test_detail_view(self):
|
||||
project = self.tenants.first()
|
||||
|
||||
api.keystone.tenant_get(IsA(http.HttpRequest), self.tenant.id) \
|
||||
.AndReturn(project)
|
||||
quotas.enabled_quotas(IsA(http.HttpRequest)).AndReturn(('instances',))
|
||||
self.mox.ReplayAll()
|
||||
|
||||
res = self.client.get(PROJECT_DETAIL_URL, args=[project.id])
|
||||
|
@ -386,9 +386,12 @@ class UpdateProjectGroups(workflows.UpdateMembersStep):
|
||||
class CommonQuotaWorkflow(workflows.Workflow):
|
||||
def _update_project_quota(self, request, data, project_id):
|
||||
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)
|
||||
|
||||
# Update the project quotas.
|
||||
if api.base.is_service_enabled(request, 'compute'):
|
||||
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):
|
||||
cinder_data = dict([(key, data[key]) for key in
|
||||
|
@ -374,6 +374,8 @@ class FloatingIpNeutronViewTests(FloatingIpViewTests):
|
||||
.AndReturn(False)
|
||||
api.base.is_service_enabled(IsA(http.HttpRequest), 'network') \
|
||||
.MultipleTimes().AndReturn(True)
|
||||
api.base.is_service_enabled(IsA(http.HttpRequest), 'compute') \
|
||||
.MultipleTimes().AndReturn(True)
|
||||
api.nova.tenant_quota_get(IsA(http.HttpRequest), '1') \
|
||||
.AndReturn(self.quotas.first())
|
||||
api.nova.flavor_list(IsA(http.HttpRequest)) \
|
||||
@ -435,6 +437,8 @@ class FloatingIpNeutronViewTests(FloatingIpViewTests):
|
||||
.AndReturn(False)
|
||||
api.base.is_service_enabled(IsA(http.HttpRequest), 'network') \
|
||||
.MultipleTimes().AndReturn(True)
|
||||
api.base.is_service_enabled(IsA(http.HttpRequest), 'compute') \
|
||||
.MultipleTimes().AndReturn(True)
|
||||
api.nova.tenant_quota_get(IsA(http.HttpRequest), '1') \
|
||||
.AndReturn(self.quotas.first())
|
||||
api.nova.flavor_list(IsA(http.HttpRequest)) \
|
||||
|
@ -51,6 +51,8 @@ class LaunchImage(tables.LinkAction):
|
||||
return "?".join([base_url, params])
|
||||
|
||||
def allowed(self, request, image=None):
|
||||
if not api.base.is_service_enabled(request, 'compute'):
|
||||
return False
|
||||
if image and image.container_format not in NOT_LAUNCHABLE_FORMATS:
|
||||
return image.status in ("active",)
|
||||
return False
|
||||
|
@ -12,6 +12,7 @@
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
from openstack_dashboard.api import base
|
||||
from openstack_dashboard.usage import quotas
|
||||
|
||||
|
||||
@ -49,8 +50,10 @@ def get_context(request, context=None):
|
||||
_has_permission(request, (("network", "create_router"),)))
|
||||
context['router_quota_exceeded'] = _quota_exceeded(request, 'routers')
|
||||
context['console_type'] = getattr(settings, 'CONSOLE_TYPE', 'AUTO')
|
||||
context['show_ng_launch'] = getattr(
|
||||
settings, 'LAUNCH_INSTANCE_NG_ENABLED', True)
|
||||
context['show_legacy_launch'] = getattr(
|
||||
settings, 'LAUNCH_INSTANCE_LEGACY_ENABLED', False)
|
||||
context['show_ng_launch'] = (
|
||||
base.is_service_enabled(request, 'compute') and
|
||||
getattr(settings, 'LAUNCH_INSTANCE_NG_ENABLED', True))
|
||||
context['show_legacy_launch'] = (
|
||||
base.is_service_enabled(request, 'compute') and
|
||||
getattr(settings, 'LAUNCH_INSTANCE_LEGACY_ENABLED', False))
|
||||
return context
|
||||
|
@ -24,3 +24,4 @@ import horizon
|
||||
class Overview(horizon.Panel):
|
||||
name = _("Overview")
|
||||
slug = 'overview'
|
||||
permissions = ('openstack.services.compute',)
|
||||
|
@ -58,6 +58,8 @@ class LaunchVolume(tables.LinkAction):
|
||||
return "?".join([base_url, params])
|
||||
|
||||
def allowed(self, request, volume=None):
|
||||
if not api.base.is_service_enabled(request, 'compute'):
|
||||
return False
|
||||
if getattr(volume, 'bootable', '') == 'true':
|
||||
return volume.status == "available"
|
||||
return False
|
||||
@ -179,6 +181,9 @@ class EditAttachments(tables.LinkAction):
|
||||
icon = "pencil"
|
||||
|
||||
def allowed(self, request, volume=None):
|
||||
if not api.base.is_service_enabled(request, 'compute'):
|
||||
return False
|
||||
|
||||
if volume:
|
||||
project_id = getattr(volume, "os-vol-tenant-attr:tenant_id", None)
|
||||
attach_allowed = \
|
||||
|
@ -32,6 +32,8 @@ class NetworkClientTestCase(test.APITestCase):
|
||||
self.mox.StubOutWithMock(api.base, 'is_service_enabled')
|
||||
api.base.is_service_enabled(IsA(http.HttpRequest), 'network') \
|
||||
.AndReturn(False)
|
||||
api.base.is_service_enabled(IsA(http.HttpRequest), 'compute') \
|
||||
.AndReturn(True)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
nc = api.network.NetworkClient(self.request)
|
||||
@ -42,6 +44,8 @@ class NetworkClientTestCase(test.APITestCase):
|
||||
self.mox.StubOutWithMock(api.base, 'is_service_enabled')
|
||||
api.base.is_service_enabled(IsA(http.HttpRequest), 'network') \
|
||||
.AndReturn(True)
|
||||
api.base.is_service_enabled(IsA(http.HttpRequest), 'compute') \
|
||||
.AndReturn(True)
|
||||
self.neutronclient = self.stub_neutronclient()
|
||||
self.neutronclient.list_extensions() \
|
||||
.AndReturn({'extensions': self.api_extensions.list()})
|
||||
@ -55,6 +59,8 @@ class NetworkClientTestCase(test.APITestCase):
|
||||
self.mox.StubOutWithMock(api.base, 'is_service_enabled')
|
||||
api.base.is_service_enabled(IsA(http.HttpRequest), 'network') \
|
||||
.AndReturn(True)
|
||||
api.base.is_service_enabled(IsA(http.HttpRequest), 'compute') \
|
||||
.AndReturn(True)
|
||||
self.neutronclient = self.stub_neutronclient()
|
||||
self.neutronclient.list_extensions().AndReturn({'extensions': []})
|
||||
self.mox.ReplayAll()
|
||||
@ -70,6 +76,8 @@ class NetworkApiNovaTestBase(test.APITestCase):
|
||||
self.mox.StubOutWithMock(api.base, 'is_service_enabled')
|
||||
api.base.is_service_enabled(IsA(http.HttpRequest), 'network') \
|
||||
.AndReturn(False)
|
||||
api.base.is_service_enabled(IsA(http.HttpRequest), 'compute') \
|
||||
.AndReturn(True)
|
||||
|
||||
|
||||
class NetworkApiNovaSecurityGroupTests(NetworkApiNovaTestBase):
|
||||
@ -339,6 +347,8 @@ class NetworkApiNeutronSecurityGroupTests(NetworkApiNeutronTestBase):
|
||||
super(NetworkApiNeutronSecurityGroupTests, self).setUp()
|
||||
self.qclient.list_extensions() \
|
||||
.AndReturn({'extensions': self.api_extensions.list()})
|
||||
api.base.is_service_enabled(IsA(http.HttpRequest), 'compute') \
|
||||
.AndReturn(True)
|
||||
self.sg_dict = dict([(sg['id'], sg['name']) for sg
|
||||
in self.api_q_secgroups.list()])
|
||||
|
||||
@ -521,6 +531,8 @@ class NetworkApiNeutronFloatingIpTests(NetworkApiNeutronTestBase):
|
||||
super(NetworkApiNeutronFloatingIpTests, self).setUp()
|
||||
self.qclient.list_extensions() \
|
||||
.AndReturn({'extensions': self.api_extensions.list()})
|
||||
api.base.is_service_enabled(IsA(http.HttpRequest), 'compute') \
|
||||
.AndReturn(True)
|
||||
|
||||
@override_settings(OPENSTACK_NEUTRON_NETWORK={'enable_router': True})
|
||||
def test_floating_ip_supported(self):
|
||||
|
@ -33,26 +33,36 @@ from openstack_dashboard.usage import quotas
|
||||
|
||||
class QuotaTests(test.APITestCase):
|
||||
|
||||
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}}
|
||||
def get_usages(self, with_volume=True, with_compute=True,
|
||||
nova_quotas_enabled=True):
|
||||
usages = {}
|
||||
if with_compute:
|
||||
# These are all nova fields; the neutron ones are named slightly
|
||||
# differently and aren't included in here yet
|
||||
if nova_quotas_enabled:
|
||||
usages.update({
|
||||
'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.update({
|
||||
'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,
|
||||
@ -78,26 +88,34 @@ class QuotaTests(test.APITestCase):
|
||||
'tenant_quota_get',
|
||||
'is_volume_service_enabled')})
|
||||
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]
|
||||
with_compute=True, with_volume=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)) \
|
||||
.AndReturn(self.flavors.list())
|
||||
api.nova.tenant_quota_get(IsA(http.HttpRequest), '1') \
|
||||
.AndReturn(self.quotas.first())
|
||||
api.network.floating_ip_supported(IsA(http.HttpRequest)) \
|
||||
.AndReturn(True)
|
||||
api.network.tenant_floating_ip_list(IsA(http.HttpRequest)) \
|
||||
.AndReturn(self.floating_ips.list())
|
||||
search_opts = {'tenant_id': self.request.user.tenant_id}
|
||||
api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts,
|
||||
all_tenants=True) \
|
||||
.AndReturn([servers, False])
|
||||
api.base.is_service_enabled(
|
||||
IsA(http.HttpRequest), 'compute'
|
||||
).MultipleTimes().AndReturn(with_compute)
|
||||
if with_compute:
|
||||
servers = [s for s in self.servers.list()
|
||||
if s.tenant_id == self.request.user.tenant_id]
|
||||
api.nova.flavor_list(IsA(http.HttpRequest)) \
|
||||
.AndReturn(self.flavors.list())
|
||||
api.network.floating_ip_supported(IsA(http.HttpRequest)) \
|
||||
.AndReturn(True)
|
||||
api.network.tenant_floating_ip_list(IsA(http.HttpRequest)) \
|
||||
.AndReturn(self.floating_ips.list())
|
||||
search_opts = {'tenant_id': self.request.user.tenant_id}
|
||||
api.nova.server_list(IsA(http.HttpRequest),
|
||||
search_opts=search_opts,
|
||||
all_tenants=True) \
|
||||
.AndReturn([servers, False])
|
||||
|
||||
if nova_quotas_enabled:
|
||||
api.nova.tenant_quota_get(IsA(http.HttpRequest), '1') \
|
||||
.AndReturn(self.quotas.first())
|
||||
|
||||
if with_volume:
|
||||
opts = {'all_tenants': 1,
|
||||
'project_id': self.request.user.tenant_id}
|
||||
@ -112,7 +130,8 @@ class QuotaTests(test.APITestCase):
|
||||
|
||||
quota_usages = quotas.tenant_quota_usages(self.request)
|
||||
expected_output = self.get_usages(
|
||||
nova_quotas_enabled=nova_quotas_enabled, with_volume=with_volume)
|
||||
nova_quotas_enabled=nova_quotas_enabled, with_volume=with_volume,
|
||||
with_compute=with_compute)
|
||||
|
||||
# Compare internal structure of usages to expected.
|
||||
self.assertItemsEqual(expected_output, quota_usages.usages)
|
||||
@ -125,6 +144,7 @@ class QuotaTests(test.APITestCase):
|
||||
@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_compute=True,
|
||||
with_volume=False)
|
||||
|
||||
@override_settings(OPENSTACK_HYPERVISOR_FEATURES={'enable_quotas': False})
|
||||
@ -135,11 +155,15 @@ class QuotaTests(test.APITestCase):
|
||||
False)
|
||||
api.base.is_service_enabled(IsA(http.HttpRequest),
|
||||
'network').AndReturn(False)
|
||||
# Nova enabled but quotas disabled
|
||||
api.base.is_service_enabled(IsA(http.HttpRequest),
|
||||
'compute').AndReturn(True)
|
||||
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)
|
||||
list(quotas.NEUTRON_QUOTA_FIELDS) + \
|
||||
list(quotas.NOVA_QUOTA_FIELDS) + list(quotas.MISSING_QUOTA_FIELDS)
|
||||
self.assertItemsEqual(result_quotas, expected_quotas)
|
||||
|
||||
@test.create_stubs({api.nova: ('server_list',
|
||||
@ -158,6 +182,8 @@ class QuotaTests(test.APITestCase):
|
||||
).AndReturn(False)
|
||||
api.base.is_service_enabled(IsA(http.HttpRequest),
|
||||
'network').AndReturn(False)
|
||||
api.base.is_service_enabled(IsA(http.HttpRequest),
|
||||
'compute').MultipleTimes().AndReturn(True)
|
||||
api.nova.flavor_list(IsA(http.HttpRequest)) \
|
||||
.AndReturn(self.flavors.list())
|
||||
api.nova.tenant_quota_get(IsA(http.HttpRequest), '1') \
|
||||
@ -197,6 +223,8 @@ class QuotaTests(test.APITestCase):
|
||||
).AndReturn(False)
|
||||
api.base.is_service_enabled(IsA(http.HttpRequest),
|
||||
'network').AndReturn(False)
|
||||
api.base.is_service_enabled(IsA(http.HttpRequest),
|
||||
'compute').MultipleTimes().AndReturn(True)
|
||||
api.nova.flavor_list(IsA(http.HttpRequest)) \
|
||||
.AndReturn(self.flavors.list())
|
||||
api.nova.tenant_quota_get(IsA(http.HttpRequest), '1') \
|
||||
@ -244,6 +272,8 @@ class QuotaTests(test.APITestCase):
|
||||
).AndReturn(True)
|
||||
api.base.is_service_enabled(IsA(http.HttpRequest),
|
||||
'network').AndReturn(False)
|
||||
api.base.is_service_enabled(IsA(http.HttpRequest),
|
||||
'compute').MultipleTimes().AndReturn(True)
|
||||
api.nova.flavor_list(IsA(http.HttpRequest)) \
|
||||
.AndReturn(self.flavors.list())
|
||||
api.nova.tenant_quota_get(IsA(http.HttpRequest), '1') \
|
||||
@ -293,6 +323,8 @@ class QuotaTests(test.APITestCase):
|
||||
).AndReturn(True)
|
||||
api.base.is_service_enabled(IsA(http.HttpRequest),
|
||||
'network').AndReturn(False)
|
||||
api.base.is_service_enabled(IsA(http.HttpRequest),
|
||||
'compute').MultipleTimes().AndReturn(True)
|
||||
api.nova.flavor_list(IsA(http.HttpRequest)) \
|
||||
.AndReturn(self.flavors.list())
|
||||
api.nova.tenant_quota_get(IsA(http.HttpRequest), '1') \
|
||||
@ -332,8 +364,7 @@ class QuotaTests(test.APITestCase):
|
||||
|
||||
quotas._get_tenant_volume_usages(self.request, {}, [], None)
|
||||
|
||||
@test.create_stubs({api.nova: ('tenant_quota_get',),
|
||||
api.base: ('is_service_enabled',),
|
||||
@test.create_stubs({api.base: ('is_service_enabled',),
|
||||
api.cinder: ('tenant_quota_get',
|
||||
'is_volume_service_enabled'),
|
||||
exceptions: ('handle',)})
|
||||
@ -343,8 +374,8 @@ class QuotaTests(test.APITestCase):
|
||||
).AndReturn(True)
|
||||
api.base.is_service_enabled(IsA(http.HttpRequest),
|
||||
'network').AndReturn(False)
|
||||
api.nova.tenant_quota_get(IsA(http.HttpRequest), '1') \
|
||||
.AndReturn(self.quotas.first())
|
||||
api.base.is_service_enabled(IsA(http.HttpRequest),
|
||||
'compute').AndReturn(False)
|
||||
api.cinder.tenant_quota_get(IsA(http.HttpRequest), '1') \
|
||||
.AndRaise(cinder.cinder_exception.ClientException('test'))
|
||||
exceptions.handle(IsA(http.HttpRequest),
|
||||
@ -353,17 +384,16 @@ class QuotaTests(test.APITestCase):
|
||||
|
||||
quotas._get_quota_data(self.request, 'tenant_quota_get')
|
||||
|
||||
@test.create_stubs({api.nova: ('tenant_absolute_limits',),
|
||||
api.base: ('is_service_enabled',),
|
||||
@test.create_stubs({api.base: ('is_service_enabled',),
|
||||
api.cinder: ('tenant_absolute_limits',
|
||||
'is_volume_service_enabled'),
|
||||
exceptions: ('handle',)})
|
||||
def test_tenant_limit_usages_cinder_exception(self):
|
||||
api.base.is_service_enabled(IsA(http.HttpRequest),
|
||||
'compute').AndReturn(False)
|
||||
api.cinder.is_volume_service_enabled(
|
||||
IsA(http.HttpRequest)
|
||||
).AndReturn(True)
|
||||
api.nova.tenant_absolute_limits(IsA(http.HttpRequest),
|
||||
reserved=True).AndReturn({})
|
||||
api.cinder.tenant_absolute_limits(IsA(http.HttpRequest)) \
|
||||
.AndRaise(cinder.cinder_exception.ClientException('test'))
|
||||
exceptions.handle(IsA(http.HttpRequest),
|
||||
|
@ -141,10 +141,14 @@ def _get_quota_data(request, method_name, disabled_quotas=None,
|
||||
quotasets = []
|
||||
if not tenant_id:
|
||||
tenant_id = request.user.tenant_id
|
||||
quotasets.append(getattr(nova, method_name)(request, tenant_id))
|
||||
qs = base.QuotaSet()
|
||||
if disabled_quotas is None:
|
||||
disabled_quotas = get_disabled_quotas(request)
|
||||
|
||||
qs = base.QuotaSet()
|
||||
|
||||
if 'instances' not in disabled_quotas:
|
||||
quotasets.append(getattr(nova, method_name)(request, tenant_id))
|
||||
|
||||
if 'volumes' not in disabled_quotas:
|
||||
try:
|
||||
quotasets.append(getattr(cinder, method_name)(request, tenant_id))
|
||||
@ -231,10 +235,6 @@ def get_tenant_quota_data(request, disabled_quotas=None, tenant_id=None):
|
||||
def get_disabled_quotas(request):
|
||||
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.update(CINDER_QUOTA_FIELDS)
|
||||
@ -260,10 +260,25 @@ def get_disabled_quotas(request):
|
||||
LOG.exception("There was an error checking if the Neutron "
|
||||
"quotas extension is enabled.")
|
||||
|
||||
# Nova
|
||||
if not (base.is_service_enabled(request, 'compute') and
|
||||
nova.can_set_quotas()):
|
||||
disabled_quotas.update(NOVA_QUOTA_FIELDS)
|
||||
# The 'missing' quota fields are all nova (this is hardcoded in
|
||||
# dashboards.admin.defaults.workflows)
|
||||
disabled_quotas.update(MISSING_QUOTA_FIELDS)
|
||||
|
||||
# There appear to be no glance quota fields currently
|
||||
return disabled_quotas
|
||||
|
||||
|
||||
def _get_tenant_compute_usages(request, usages, disabled_quotas, tenant_id):
|
||||
# Unlike the other services it can be the case that nova is enabled but
|
||||
# doesn't support quotas, in which case we still want to get usage info,
|
||||
# so don't rely on '"instances" in disabled_quotas' as elsewhere
|
||||
if not base.is_service_enabled(request, 'compute'):
|
||||
return
|
||||
|
||||
if tenant_id:
|
||||
# determine if the user has permission to view across projects
|
||||
# there are cases where an administrator wants to check the quotas
|
||||
@ -396,7 +411,8 @@ def tenant_limit_usages(request):
|
||||
limits = {}
|
||||
|
||||
try:
|
||||
limits.update(nova.tenant_absolute_limits(request, reserved=True))
|
||||
if base.is_service_enabled(request, 'compute'):
|
||||
limits.update(nova.tenant_absolute_limits(request, reserved=True))
|
||||
except Exception:
|
||||
msg = _("Unable to retrieve compute limit information.")
|
||||
exceptions.handle(request, msg)
|
||||
@ -419,3 +435,8 @@ def tenant_limit_usages(request):
|
||||
exceptions.handle(request, msg)
|
||||
|
||||
return limits
|
||||
|
||||
|
||||
def enabled_quotas(request):
|
||||
"""Returns the list of quotas available minus those that are disabled"""
|
||||
return set(QUOTA_FIELDS) - get_disabled_quotas(request)
|
||||
|
@ -0,0 +1,9 @@
|
||||
---
|
||||
prelude: >
|
||||
Horizon no longer requires Nova (or Glance) to function;
|
||||
it will run as long as keystone is present (for instance,
|
||||
swift-only deployments).
|
||||
features:
|
||||
Nova and Glance are no longer required in order to run
|
||||
Horizon. As long as keystone is present, Horizon will
|
||||
run correctly.
|
Loading…
Reference in New Issue
Block a user