Merge "Allow horizon to function without nova"

This commit is contained in:
Jenkins 2016-07-26 20:59:55 +00:00 committed by Gerrit Code Review
commit 4aa405bc08
17 changed files with 187 additions and 69 deletions

View File

@ -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):

View File

@ -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 = {

View File

@ -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()

View File

@ -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():

View File

@ -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)

View File

@ -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'

View File

@ -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])

View File

@ -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

View File

@ -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)) \

View File

@ -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

View File

@ -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

View File

@ -24,3 +24,4 @@ import horizon
class Overview(horizon.Panel):
name = _("Overview")
slug = 'overview'
permissions = ('openstack.services.compute',)

View File

@ -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 = \

View File

@ -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):

View File

@ -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),

View File

@ -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)

View File

@ -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.