Retrieve quota and usage only for resources really required
tenant_quota_usage() is used to retrieve quota and usage to determine if a resource can be created. However, tenant_quota_usage retrieves quota and usage for all resources and it can be a performance problem. This commit allows to load quota and usage only for resources which are actually required. Closes-Bug: #1675504 Change-Id: Iab7322a337a451a1a040cc2f4b55cc319b1ffc4c
This commit is contained in:
parent
41d6a5352e
commit
359467b401
@ -38,7 +38,8 @@ class FloatingIpAllocate(forms.SelfHandlingForm):
|
||||
def handle(self, request, data):
|
||||
try:
|
||||
# Prevent allocating more IP than the quota allows
|
||||
usages = quotas.tenant_quota_usages(request)
|
||||
usages = quotas.tenant_quota_usages(request,
|
||||
targets=['floating_ips'])
|
||||
if usages['floating_ips']['available'] <= 0:
|
||||
error_message = _('You are already using all of your available'
|
||||
' floating IPs.')
|
||||
|
@ -47,7 +47,8 @@ class AllocateIP(tables.LinkAction):
|
||||
return shortcuts.redirect('horizon:project:floating_ips:index')
|
||||
|
||||
def allowed(self, request, fip=None):
|
||||
usages = quotas.tenant_quota_usages(request)
|
||||
usages = quotas.tenant_quota_usages(request,
|
||||
targets=['floating_ips'])
|
||||
if usages['floating_ips']['available'] <= 0:
|
||||
if "disabled" not in self.classes:
|
||||
self.classes = [c for c in self.classes] + ['disabled']
|
||||
|
@ -266,7 +266,7 @@ class FloatingIpViewTests(test.TestCase):
|
||||
IsA(http.HttpRequest), detailed=False) \
|
||||
.AndReturn([self.servers.list(), False])
|
||||
quotas.tenant_quota_usages(
|
||||
IsA(http.HttpRequest)).MultipleTimes() \
|
||||
IsA(http.HttpRequest), targets=['floating_ips']).MultipleTimes() \
|
||||
.AndReturn(quota_data)
|
||||
|
||||
self.mox.ReplayAll()
|
||||
@ -304,7 +304,7 @@ class FloatingIpViewTests(test.TestCase):
|
||||
IsA(http.HttpRequest), detailed=False) \
|
||||
.AndReturn([self.servers.list(), False])
|
||||
quotas.tenant_quota_usages(
|
||||
IsA(http.HttpRequest)).MultipleTimes() \
|
||||
IsA(http.HttpRequest), targets=['floating_ips']).MultipleTimes() \
|
||||
.AndReturn(quota_data)
|
||||
|
||||
self.mox.ReplayAll()
|
||||
@ -318,38 +318,22 @@ class FloatingIpViewTests(test.TestCase):
|
||||
self.assertEqual('Allocate IP To Project (Quota exceeded)',
|
||||
six.text_type(allocate_action.verbose_name))
|
||||
|
||||
@test.create_stubs({api.nova: ('tenant_quota_get', 'flavor_list',
|
||||
'server_list'),
|
||||
api.neutron: ('floating_ip_pools_list',
|
||||
@test.create_stubs({api.neutron: ('floating_ip_pools_list',
|
||||
'floating_ip_supported',
|
||||
'security_group_list',
|
||||
'tenant_floating_ip_list',
|
||||
'is_extension_supported',
|
||||
'is_router_enabled',
|
||||
'tenant_quota_get',
|
||||
'network_list',
|
||||
'router_list',
|
||||
'subnet_list'),
|
||||
'tenant_quota_get'),
|
||||
api.base: ('is_service_enabled',),
|
||||
api.cinder: ('is_volume_service_enabled',)})
|
||||
@test.update_settings(OPENSTACK_NEUTRON_NETWORK={'enable_quotas': True})
|
||||
def test_correct_quotas_displayed(self):
|
||||
servers = [s for s in self.servers.list()
|
||||
if s.tenant_id == self.request.user.tenant_id]
|
||||
|
||||
api.cinder.is_volume_service_enabled(IsA(http.HttpRequest)) \
|
||||
.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)) \
|
||||
.AndReturn(self.flavors.list())
|
||||
search_opts = {'tenant_id': self.request.user.tenant_id}
|
||||
api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \
|
||||
.AndReturn([servers, False])
|
||||
api.neutron.is_extension_supported(
|
||||
IsA(http.HttpRequest), 'security-group').AndReturn(True)
|
||||
api.neutron.is_extension_supported(IsA(http.HttpRequest), 'quotas') \
|
||||
@ -358,23 +342,12 @@ class FloatingIpViewTests(test.TestCase):
|
||||
.AndReturn(True)
|
||||
api.neutron.tenant_quota_get(IsA(http.HttpRequest), self.tenant.id) \
|
||||
.AndReturn(self.neutron_quotas.first())
|
||||
api.neutron.router_list(IsA(http.HttpRequest),
|
||||
tenant_id=self.tenant.id) \
|
||||
.AndReturn(self.routers.list())
|
||||
api.neutron.subnet_list(IsA(http.HttpRequest),
|
||||
tenant_id=self.tenant.id) \
|
||||
.AndReturn(self.subnets.list())
|
||||
api.neutron.network_list(IsA(http.HttpRequest),
|
||||
tenant_id=self.tenant.id) \
|
||||
.AndReturn(self.networks.list())
|
||||
api.neutron.floating_ip_supported(IsA(http.HttpRequest)) \
|
||||
.AndReturn(True)
|
||||
api.neutron.tenant_floating_ip_list(IsA(http.HttpRequest)) \
|
||||
.MultipleTimes().AndReturn(self.floating_ips.list())
|
||||
api.neutron.floating_ip_pools_list(IsA(http.HttpRequest)) \
|
||||
.AndReturn(self.pools.list())
|
||||
api.neutron.security_group_list(IsA(http.HttpRequest)) \
|
||||
.AndReturn(self.security_groups.list())
|
||||
self.mox.ReplayAll()
|
||||
|
||||
url = reverse('%s:allocate' % NAMESPACE)
|
||||
|
@ -61,7 +61,8 @@ class AllocateView(forms.ModalFormView):
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(AllocateView, self).get_context_data(**kwargs)
|
||||
try:
|
||||
context['usages'] = quotas.tenant_quota_usages(self.request)
|
||||
context['usages'] = quotas.tenant_quota_usages(
|
||||
self.request, targets=['floating_ips'])
|
||||
except Exception:
|
||||
exceptions.handle(self.request)
|
||||
return context
|
||||
|
@ -1995,7 +1995,9 @@ class InstanceTests(helpers.ResetImageAPIVersionMixin, helpers.TestCase):
|
||||
disk_config=disk_config_value,
|
||||
config_drive=config_drive_value,
|
||||
scheduler_hints=scheduler_hints)
|
||||
quotas.tenant_quota_usages(IsA(http.HttpRequest)) \
|
||||
quotas.tenant_quota_usages(
|
||||
IsA(http.HttpRequest),
|
||||
targets=['instances', 'cores', 'ram', 'volumes']) \
|
||||
.AndReturn(quota_usages)
|
||||
api.nova.flavor_list(IsA(http.HttpRequest)) \
|
||||
.AndReturn(self.flavors.list())
|
||||
@ -2123,7 +2125,9 @@ class InstanceTests(helpers.ResetImageAPIVersionMixin, helpers.TestCase):
|
||||
disk_config=u'AUTO',
|
||||
config_drive=True,
|
||||
scheduler_hints={})
|
||||
quotas.tenant_quota_usages(IsA(http.HttpRequest)) \
|
||||
quotas.tenant_quota_usages(
|
||||
IsA(http.HttpRequest),
|
||||
targets=['instances', 'cores', 'ram', 'volumes']) \
|
||||
.AndReturn(quota_usages)
|
||||
|
||||
self.mox.ReplayAll()
|
||||
@ -2208,7 +2212,9 @@ class InstanceTests(helpers.ResetImageAPIVersionMixin, helpers.TestCase):
|
||||
cinder.volume_snapshot_list(IsA(http.HttpRequest),
|
||||
search_opts=SNAPSHOT_SEARCH_OPTS) \
|
||||
.AndReturn([])
|
||||
quotas.tenant_quota_usages(IsA(http.HttpRequest)) \
|
||||
quotas.tenant_quota_usages(
|
||||
IsA(http.HttpRequest),
|
||||
targets=['instances', 'cores', 'ram', 'volumes']) \
|
||||
.AndReturn(quota_usages)
|
||||
|
||||
api.nova.extension_supported('BlockDeviceMappingV2Boot',
|
||||
@ -2312,7 +2318,9 @@ class InstanceTests(helpers.ResetImageAPIVersionMixin, helpers.TestCase):
|
||||
cinder.volume_snapshot_list(IsA(http.HttpRequest),
|
||||
search_opts=SNAPSHOT_SEARCH_OPTS) \
|
||||
.AndReturn([])
|
||||
quotas.tenant_quota_usages(IsA(http.HttpRequest)) \
|
||||
quotas.tenant_quota_usages(
|
||||
IsA(http.HttpRequest),
|
||||
targets=['instances', 'cores', 'ram', 'volumes']) \
|
||||
.AndReturn(quota_usages)
|
||||
|
||||
self.mox.ReplayAll()
|
||||
@ -2426,8 +2434,10 @@ class InstanceTests(helpers.ResetImageAPIVersionMixin, helpers.TestCase):
|
||||
disk_config=u'AUTO',
|
||||
config_drive=True,
|
||||
scheduler_hints={})
|
||||
quotas.tenant_quota_usages(IsA(http.HttpRequest)) \
|
||||
.AndReturn(quota_usages)
|
||||
quotas.tenant_quota_usages(
|
||||
IsA(http.HttpRequest),
|
||||
targets=['instances', 'cores', 'ram', 'volumes']) \
|
||||
.AndReturn(quota_usages)
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
@ -2496,7 +2506,9 @@ class InstanceTests(helpers.ResetImageAPIVersionMixin, helpers.TestCase):
|
||||
'status': 'active'}) \
|
||||
.AndReturn([[], False, False])
|
||||
|
||||
quotas.tenant_quota_usages(IsA(http.HttpRequest)) \
|
||||
quotas.tenant_quota_usages(
|
||||
IsA(http.HttpRequest),
|
||||
targets=['instances', 'cores', 'ram', 'volumes']) \
|
||||
.AndReturn(quota_usages)
|
||||
|
||||
self._mock_neutron_network_and_port_list()
|
||||
@ -2664,7 +2676,9 @@ class InstanceTests(helpers.ResetImageAPIVersionMixin, helpers.TestCase):
|
||||
config_drive=False,
|
||||
scheduler_hints={}) \
|
||||
.AndRaise(self.exceptions.keystone)
|
||||
quotas.tenant_quota_usages(IsA(http.HttpRequest)) \
|
||||
quotas.tenant_quota_usages(
|
||||
IsA(http.HttpRequest),
|
||||
targets=['instances', 'cores', 'ram', 'volumes']) \
|
||||
.AndReturn(quota_usages)
|
||||
api.nova.flavor_list(IsA(http.HttpRequest)) \
|
||||
.AndReturn(self.flavors.list())
|
||||
@ -2748,7 +2762,9 @@ class InstanceTests(helpers.ResetImageAPIVersionMixin, helpers.TestCase):
|
||||
.AndReturn(self.flavors.list())
|
||||
quotas.tenant_limit_usages(IsA(http.HttpRequest)) \
|
||||
.AndReturn(self.limits['absolute'])
|
||||
quotas.tenant_quota_usages(IsA(http.HttpRequest)) \
|
||||
quotas.tenant_quota_usages(
|
||||
IsA(http.HttpRequest),
|
||||
targets=['instances', 'cores', 'ram', 'volumes']) \
|
||||
.AndReturn(quota_usages)
|
||||
api.nova.flavor_list(IsA(http.HttpRequest)) \
|
||||
.AndReturn(self.flavors.list())
|
||||
@ -2835,7 +2851,9 @@ class InstanceTests(helpers.ResetImageAPIVersionMixin, helpers.TestCase):
|
||||
.AndReturn(self.flavors.list())
|
||||
quotas.tenant_limit_usages(IsA(http.HttpRequest)) \
|
||||
.AndReturn(self.limits['absolute'])
|
||||
quotas.tenant_quota_usages(IsA(http.HttpRequest)) \
|
||||
quotas.tenant_quota_usages(
|
||||
IsA(http.HttpRequest),
|
||||
targets=['instances', 'cores', 'ram', 'volumes']) \
|
||||
.AndReturn(quota_usages)
|
||||
api.nova.flavor_list(IsA(http.HttpRequest)) \
|
||||
.AndReturn(self.flavors.list())
|
||||
@ -2930,7 +2948,9 @@ class InstanceTests(helpers.ResetImageAPIVersionMixin, helpers.TestCase):
|
||||
.AndReturn(self.flavors.list())
|
||||
quotas.tenant_limit_usages(IsA(http.HttpRequest)) \
|
||||
.AndReturn(self.limits['absolute'])
|
||||
quotas.tenant_quota_usages(IsA(http.HttpRequest)) \
|
||||
quotas.tenant_quota_usages(
|
||||
IsA(http.HttpRequest),
|
||||
targets=['instances', 'cores', 'ram', 'volumes']) \
|
||||
.AndReturn(quota_usages)
|
||||
api.nova.flavor_list(IsA(http.HttpRequest)) \
|
||||
.AndReturn(self.flavors.list())
|
||||
@ -3075,7 +3095,9 @@ class InstanceTests(helpers.ResetImageAPIVersionMixin, helpers.TestCase):
|
||||
quotas.tenant_limit_usages(
|
||||
IsA(http.HttpRequest)).AndReturn(self.limits['absolute'])
|
||||
quotas.tenant_quota_usages(
|
||||
IsA(http.HttpRequest)).AndReturn(quota_usages)
|
||||
IsA(http.HttpRequest),
|
||||
targets=['instances', 'cores', 'ram', 'volumes']) \
|
||||
.AndReturn(quota_usages)
|
||||
api.nova.flavor_list(
|
||||
IsA(http.HttpRequest)).AndReturn(self.flavors.list())
|
||||
|
||||
@ -3202,7 +3224,9 @@ class InstanceTests(helpers.ResetImageAPIVersionMixin, helpers.TestCase):
|
||||
.AndReturn(self.flavors.list())
|
||||
quotas.tenant_limit_usages(IsA(http.HttpRequest)) \
|
||||
.AndReturn(self.limits['absolute'])
|
||||
quotas.tenant_quota_usages(IsA(http.HttpRequest)) \
|
||||
quotas.tenant_quota_usages(
|
||||
IsA(http.HttpRequest),
|
||||
targets=['instances', 'cores', 'ram', 'volumes']) \
|
||||
.AndReturn(quota_usages)
|
||||
api.nova.flavor_list(IsA(http.HttpRequest)) \
|
||||
.AndReturn(self.flavors.list())
|
||||
@ -3405,7 +3429,9 @@ class InstanceTests(helpers.ResetImageAPIVersionMixin, helpers.TestCase):
|
||||
|
||||
api.nova.flavor_list(IsA(http.HttpRequest)) \
|
||||
.AndReturn(self.flavors.list())
|
||||
quotas.tenant_quota_usages(IsA(http.HttpRequest)) \
|
||||
quotas.tenant_quota_usages(
|
||||
IsA(http.HttpRequest),
|
||||
targets=['instances', 'cores', 'ram', 'volumes']) \
|
||||
.AndReturn(quota_usages)
|
||||
|
||||
api.nova.server_create(IsA(http.HttpRequest),
|
||||
|
@ -207,7 +207,9 @@ class SetInstanceDetailsAction(workflows.Action):
|
||||
count = cleaned_data.get('count', 1)
|
||||
|
||||
# Prevent launching more instances than the quota allows
|
||||
usages = quotas.tenant_quota_usages(self.request)
|
||||
usages = quotas.tenant_quota_usages(
|
||||
self.request,
|
||||
targets=['instances', 'cores', 'ram', 'volumes'])
|
||||
available_count = usages['instances']['available']
|
||||
if available_count < count:
|
||||
msg = (_('The requested instance(s) cannot be launched '
|
||||
|
@ -50,7 +50,7 @@ class DeleteKeyPairs(tables.DeleteAction):
|
||||
|
||||
class QuotaKeypairMixin(object):
|
||||
def allowed(self, request, datum=None):
|
||||
usages = quotas.tenant_quota_usages(request)
|
||||
usages = quotas.tenant_quota_usages(request, targets=['key_pairs'])
|
||||
count = len(self.table.data)
|
||||
if (usages.get('key_pairs') and usages['key_pairs']['quota'] <= count):
|
||||
if "disabled" not in self.classes:
|
||||
|
@ -42,7 +42,8 @@ class KeyPairTests(test.TestCase):
|
||||
keypairs = self.keypairs.list()
|
||||
quota_data = self.quota_usages.first()
|
||||
|
||||
quotas.tenant_quota_usages(IsA(http.HttpRequest)).MultipleTimes() \
|
||||
quotas.tenant_quota_usages(IsA(http.HttpRequest),
|
||||
targets=['key_pairs']).MultipleTimes() \
|
||||
.AndReturn(quota_data)
|
||||
api.nova.keypair_list(IsA(http.HttpRequest)) \
|
||||
.AndReturn(keypairs)
|
||||
|
@ -198,8 +198,14 @@ class NetworkTopologyCreateTests(test.TestCase):
|
||||
quota_data['instances']['available'] = instances_quota
|
||||
|
||||
quotas.tenant_quota_usages(
|
||||
IsA(http.HttpRequest)) \
|
||||
.MultipleTimes().AndReturn(quota_data)
|
||||
IsA(http.HttpRequest), targets=['instances']
|
||||
).MultipleTimes().AndReturn(quota_data)
|
||||
quotas.tenant_quota_usages(
|
||||
IsA(http.HttpRequest), targets=['networks']
|
||||
).MultipleTimes().AndReturn(quota_data)
|
||||
quotas.tenant_quota_usages(
|
||||
IsA(http.HttpRequest), targets=['routers']
|
||||
).MultipleTimes().AndReturn(quota_data)
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
|
@ -18,7 +18,7 @@ from openstack_dashboard.usage import quotas
|
||||
|
||||
|
||||
def _quota_exceeded(request, quota):
|
||||
usages = quotas.tenant_quota_usages(request)
|
||||
usages = quotas.tenant_quota_usages(request, targets=[quota])
|
||||
available = usages.get(quota, {}).get('available', 1)
|
||||
return available <= 0
|
||||
|
||||
|
@ -103,7 +103,7 @@ class CreateSubnet(SubnetPolicyTargetMixin, CheckNetworkEditable,
|
||||
return reverse(self.url, args=(network_id,))
|
||||
|
||||
def allowed(self, request, datum=None):
|
||||
usages = quotas.tenant_quota_usages(request)
|
||||
usages = quotas.tenant_quota_usages(request, targets=['subnets'])
|
||||
|
||||
# when Settings.OPENSTACK_NEUTRON_NETWORK['enable_quotas'] = False
|
||||
# usages["subnets'] is empty
|
||||
|
@ -92,7 +92,7 @@ class CreateNetwork(tables.LinkAction):
|
||||
policy_rules = (("network", "create_network"),)
|
||||
|
||||
def allowed(self, request, datum=None):
|
||||
usages = quotas.tenant_quota_usages(request)
|
||||
usages = quotas.tenant_quota_usages(request, targets=['networks'])
|
||||
# when Settings.OPENSTACK_NEUTRON_NETWORK['enable_quotas'] = False
|
||||
# usages["networks"] is empty
|
||||
if usages.get('networks', {}).get('available', 1) <= 0:
|
||||
@ -129,7 +129,7 @@ class CreateSubnet(policy.PolicyTargetMixin, CheckNetworkEditable,
|
||||
("network:project_id", "tenant_id"),)
|
||||
|
||||
def allowed(self, request, datum=None):
|
||||
usages = quotas.tenant_quota_usages(request)
|
||||
usages = quotas.tenant_quota_usages(request, targets=['subnets'])
|
||||
# when Settings.OPENSTACK_NEUTRON_NETWORK['enable_quotas'] = False
|
||||
# usages["subnets'] is empty
|
||||
if usages.get('subnets', {}).get('available', 1) <= 0:
|
||||
|
@ -132,7 +132,10 @@ class NetworkTests(test.TestCase, NetworkStubMixin):
|
||||
quota_data['subnets']['available'] = 5
|
||||
self._stub_net_list()
|
||||
quotas.tenant_quota_usages(
|
||||
IsA(http.HttpRequest)) \
|
||||
IsA(http.HttpRequest), targets=['networks']) \
|
||||
.MultipleTimes().AndReturn(quota_data)
|
||||
quotas.tenant_quota_usages(
|
||||
IsA(http.HttpRequest), targets=['subnets']) \
|
||||
.MultipleTimes().AndReturn(quota_data)
|
||||
|
||||
self.mox.ReplayAll()
|
||||
@ -151,7 +154,7 @@ class NetworkTests(test.TestCase, NetworkStubMixin):
|
||||
tenant_id=self.tenant.id,
|
||||
shared=False).MultipleTimes().AndRaise(self.exceptions.neutron)
|
||||
quotas.tenant_quota_usages(
|
||||
IsA(http.HttpRequest)) \
|
||||
IsA(http.HttpRequest), targets=['networks']) \
|
||||
.MultipleTimes().AndReturn(quota_data)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
@ -190,7 +193,7 @@ class NetworkTests(test.TestCase, NetworkStubMixin):
|
||||
.AndReturn(mac_learning)
|
||||
|
||||
quotas.tenant_quota_usages(
|
||||
IsA(http.HttpRequest)) \
|
||||
IsA(http.HttpRequest), targets=['subnets']) \
|
||||
.MultipleTimes().AndReturn(quota_data)
|
||||
|
||||
self.mox.ReplayAll()
|
||||
@ -217,7 +220,7 @@ class NetworkTests(test.TestCase, NetworkStubMixin):
|
||||
'mac-learning')\
|
||||
.AndReturn(mac_learning)
|
||||
quotas.tenant_quota_usages(
|
||||
IsA(http.HttpRequest)) \
|
||||
IsA(http.HttpRequest), targets=['subnets']) \
|
||||
.MultipleTimes().AndReturn(quota_data)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
@ -288,7 +291,7 @@ class NetworkTests(test.TestCase, NetworkStubMixin):
|
||||
'mac-learning')\
|
||||
.AndReturn(mac_learning)
|
||||
quotas.tenant_quota_usages(
|
||||
IsA(http.HttpRequest)) \
|
||||
IsA(http.HttpRequest), targets=['subnets']) \
|
||||
.MultipleTimes().AndReturn(quota_data)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
@ -330,7 +333,7 @@ class NetworkTests(test.TestCase, NetworkStubMixin):
|
||||
'mac-learning')\
|
||||
.AndReturn(mac_learning)
|
||||
quotas.tenant_quota_usages(
|
||||
IsA(http.HttpRequest)) \
|
||||
IsA(http.HttpRequest), targets=['subnets']) \
|
||||
.MultipleTimes().AndReturn(quota_data)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
@ -992,7 +995,10 @@ class NetworkViewTests(test.TestCase, NetworkStubMixin):
|
||||
|
||||
self._stub_net_list()
|
||||
quotas.tenant_quota_usages(
|
||||
IsA(http.HttpRequest)) \
|
||||
IsA(http.HttpRequest), targets=['networks']) \
|
||||
.MultipleTimes().AndReturn(quota_data)
|
||||
quotas.tenant_quota_usages(
|
||||
IsA(http.HttpRequest), targets=['subnets']) \
|
||||
.MultipleTimes().AndReturn(quota_data)
|
||||
|
||||
self.mox.ReplayAll()
|
||||
@ -1018,7 +1024,10 @@ class NetworkViewTests(test.TestCase, NetworkStubMixin):
|
||||
|
||||
self._stub_net_list()
|
||||
quotas.tenant_quota_usages(
|
||||
IsA(http.HttpRequest)) \
|
||||
IsA(http.HttpRequest), targets=['networks']) \
|
||||
.MultipleTimes().AndReturn(quota_data)
|
||||
quotas.tenant_quota_usages(
|
||||
IsA(http.HttpRequest), targets=['subnets']) \
|
||||
.MultipleTimes().AndReturn(quota_data)
|
||||
|
||||
self.mox.ReplayAll()
|
||||
@ -1096,7 +1105,7 @@ class NetworkViewTests(test.TestCase, NetworkStubMixin):
|
||||
IsA(http.HttpRequest), 'mac-learning')\
|
||||
.AndReturn(False)
|
||||
quotas.tenant_quota_usages(
|
||||
IsA(http.HttpRequest)) \
|
||||
IsA(http.HttpRequest), targets=['subnets']) \
|
||||
.MultipleTimes().AndReturn(quota_data)
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
@ -96,7 +96,7 @@ class CreateRouter(tables.LinkAction):
|
||||
policy_rules = (("network", "create_router"),)
|
||||
|
||||
def allowed(self, request, datum=None):
|
||||
usages = quotas.tenant_quota_usages(request)
|
||||
usages = quotas.tenant_quota_usages(request, targets=['routers'])
|
||||
# when Settings.OPENSTACK_NEUTRON_NETWORK['enable_quotas'] = False
|
||||
# usages['routers'] is empty
|
||||
if usages.get('routers', {}).get('available', 1) <= 0:
|
||||
|
@ -93,7 +93,7 @@ class RouterTests(RouterMixin, test.TestCase):
|
||||
IsA(http.HttpRequest),
|
||||
tenant_id=self.tenant.id).AndReturn(self.routers.list())
|
||||
quotas.tenant_quota_usages(
|
||||
IsA(http.HttpRequest)) \
|
||||
IsA(http.HttpRequest), targets=['routers']) \
|
||||
.MultipleTimes().AndReturn(quota_data)
|
||||
self._mock_external_network_list()
|
||||
self.mox.ReplayAll()
|
||||
@ -113,7 +113,7 @@ class RouterTests(RouterMixin, test.TestCase):
|
||||
tenant_id=self.tenant.id).MultipleTimes().AndRaise(
|
||||
self.exceptions.neutron)
|
||||
quotas.tenant_quota_usages(
|
||||
IsA(http.HttpRequest)) \
|
||||
IsA(http.HttpRequest), targets=['routers']) \
|
||||
.MultipleTimes().AndReturn(quota_data)
|
||||
self._mock_external_network_list()
|
||||
self.mox.ReplayAll()
|
||||
@ -133,7 +133,7 @@ class RouterTests(RouterMixin, test.TestCase):
|
||||
IsA(http.HttpRequest),
|
||||
tenant_id=self.tenant.id).MultipleTimes().AndReturn([router])
|
||||
quotas.tenant_quota_usages(
|
||||
IsA(http.HttpRequest)) \
|
||||
IsA(http.HttpRequest), targets=['routers']) \
|
||||
.MultipleTimes().AndReturn(quota_data)
|
||||
self._mock_external_network_list(alter_ids=True)
|
||||
self.mox.ReplayAll()
|
||||
@ -177,7 +177,7 @@ class RouterTests(RouterMixin, test.TestCase):
|
||||
IsA(http.HttpRequest),
|
||||
tenant_id=self.tenant.id).AndReturn(self.routers.list())
|
||||
quotas.tenant_quota_usages(
|
||||
IsA(http.HttpRequest)) \
|
||||
IsA(http.HttpRequest), targets=['routers']) \
|
||||
.MultipleTimes().AndReturn(quota_data)
|
||||
self._mock_external_network_list()
|
||||
api.neutron.router_list(
|
||||
@ -215,7 +215,7 @@ class RouterTests(RouterMixin, test.TestCase):
|
||||
IsA(http.HttpRequest),
|
||||
tenant_id=self.tenant.id).AndReturn(self.routers.list())
|
||||
quotas.tenant_quota_usages(
|
||||
IsA(http.HttpRequest)) \
|
||||
IsA(http.HttpRequest), targets=['routers']) \
|
||||
.MultipleTimes().AndReturn(quota_data)
|
||||
self._mock_external_network_list()
|
||||
api.neutron.router_list(
|
||||
@ -801,7 +801,7 @@ class RouterViewTests(RouterMixin, test.TestCase):
|
||||
IsA(http.HttpRequest),
|
||||
tenant_id=self.tenant.id).AndReturn(self.routers.list())
|
||||
quotas.tenant_quota_usages(
|
||||
IsA(http.HttpRequest)) \
|
||||
IsA(http.HttpRequest), targets=['routers']) \
|
||||
.MultipleTimes().AndReturn(quota_data)
|
||||
|
||||
self._mock_external_network_list()
|
||||
@ -828,7 +828,7 @@ class RouterViewTests(RouterMixin, test.TestCase):
|
||||
IsA(http.HttpRequest),
|
||||
tenant_id=self.tenant.id).AndReturn(self.routers.list())
|
||||
quotas.tenant_quota_usages(
|
||||
IsA(http.HttpRequest)) \
|
||||
IsA(http.HttpRequest), targets=['routers']) \
|
||||
.MultipleTimes().AndReturn(quota_data)
|
||||
|
||||
self._mock_external_network_list()
|
||||
@ -855,7 +855,7 @@ class RouterViewTests(RouterMixin, test.TestCase):
|
||||
IsA(http.HttpRequest),
|
||||
tenant_id=self.tenant.id).AndReturn(self.routers.list())
|
||||
quotas.tenant_quota_usages(
|
||||
IsA(http.HttpRequest)) \
|
||||
IsA(http.HttpRequest), targets=['routers']) \
|
||||
.MultipleTimes().AndReturn(quota_data)
|
||||
|
||||
self._mock_external_network_list()
|
||||
|
@ -63,7 +63,8 @@ class CreateGroup(tables.LinkAction):
|
||||
icon = "plus"
|
||||
|
||||
def allowed(self, request, security_group=None):
|
||||
usages = quotas.tenant_quota_usages(request)
|
||||
usages = quotas.tenant_quota_usages(request,
|
||||
targets=['security_groups'])
|
||||
if usages['security_groups'].get('available', 1) <= 0:
|
||||
if "disabled" not in self.classes:
|
||||
self.classes = [c for c in self.classes] + ["disabled"]
|
||||
|
@ -74,7 +74,9 @@ class SecurityGroupsViewTests(test.TestCase):
|
||||
|
||||
api.neutron.security_group_list(IsA(http.HttpRequest)) \
|
||||
.AndReturn(sec_groups)
|
||||
quotas.tenant_quota_usages(IsA(http.HttpRequest)).MultipleTimes() \
|
||||
quotas.tenant_quota_usages(
|
||||
IsA(http.HttpRequest),
|
||||
targets=['security_groups']).MultipleTimes() \
|
||||
.AndReturn(quota_data)
|
||||
|
||||
self.mox.ReplayAll()
|
||||
@ -107,7 +109,8 @@ class SecurityGroupsViewTests(test.TestCase):
|
||||
IsA(http.HttpRequest)) \
|
||||
.AndReturn(sec_groups)
|
||||
quotas.tenant_quota_usages(
|
||||
IsA(http.HttpRequest)).MultipleTimes() \
|
||||
IsA(http.HttpRequest),
|
||||
targets=['security_groups']).MultipleTimes() \
|
||||
.AndReturn(quota_data)
|
||||
|
||||
self.mox.ReplayAll()
|
||||
@ -140,7 +143,8 @@ class SecurityGroupsViewTests(test.TestCase):
|
||||
IsA(http.HttpRequest)) \
|
||||
.AndReturn(sec_groups)
|
||||
quotas.tenant_quota_usages(
|
||||
IsA(http.HttpRequest)).MultipleTimes() \
|
||||
IsA(http.HttpRequest),
|
||||
targets=['security_groups']).MultipleTimes() \
|
||||
.AndReturn(quota_data)
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
@ -412,3 +412,161 @@ class QuotaTests(test.APITestCase):
|
||||
expected = set(['floating_ips', 'fixed_ips', 'security_groups',
|
||||
'security_group_rules', 'router', 'floatingip'])
|
||||
self.assertEqual(expected, disabled_quotas)
|
||||
|
||||
def test_tenant_quota_usages_with_target_instances(self):
|
||||
self._test_tenant_quota_usages_with_target(targets=['instances'])
|
||||
|
||||
def test_tenant_quota_usages_with_target_ram(self):
|
||||
self._test_tenant_quota_usages_with_target(
|
||||
targets=['ram'], use_flavor_list=True)
|
||||
|
||||
def test_tenant_quota_usages_with_target_volume(self):
|
||||
self._test_tenant_quota_usages_with_target(
|
||||
targets=['volumes'], use_compute_call=False, use_cinder_call=True)
|
||||
|
||||
def test_tenant_quota_usages_with_target_compute_volume(self):
|
||||
self._test_tenant_quota_usages_with_target(
|
||||
targets=['instances', 'cores', 'ram', 'volumes'],
|
||||
use_flavor_list=True, use_cinder_call=True)
|
||||
|
||||
@test.create_stubs({api.nova: ('server_list',
|
||||
'flavor_list',
|
||||
'tenant_quota_get',),
|
||||
api.base: ('is_service_enabled',),
|
||||
cinder: ('volume_list', 'volume_snapshot_list',
|
||||
'tenant_quota_get',
|
||||
'is_volume_service_enabled')})
|
||||
def _test_tenant_quota_usages_with_target(
|
||||
self, targets,
|
||||
use_compute_call=True,
|
||||
use_flavor_list=False, use_cinder_call=False):
|
||||
cinder.is_volume_service_enabled(IsA(http.HttpRequest)).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)
|
||||
|
||||
if use_compute_call:
|
||||
servers = [s for s in self.servers.list()
|
||||
if s.tenant_id == self.request.user.tenant_id]
|
||||
if use_flavor_list:
|
||||
api.nova.flavor_list(IsA(http.HttpRequest)) \
|
||||
.AndReturn(self.flavors.list())
|
||||
search_opts = {'tenant_id': self.request.user.tenant_id}
|
||||
api.nova.server_list(IsA(http.HttpRequest),
|
||||
search_opts=search_opts) \
|
||||
.AndReturn([servers, False])
|
||||
api.nova.tenant_quota_get(IsA(http.HttpRequest), '1') \
|
||||
.AndReturn(self.quotas.first())
|
||||
|
||||
if use_cinder_call:
|
||||
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,
|
||||
targets=targets)
|
||||
|
||||
expected = self.get_usages()
|
||||
expected = dict((k, v) for k, v in expected.items() if k in targets)
|
||||
|
||||
# Compare internal structure of usages to expected.
|
||||
self.assertItemsEqual(expected, quota_usages.usages)
|
||||
# Compare available resources
|
||||
self.assertAvailableQuotasEqual(expected, quota_usages.usages)
|
||||
|
||||
def test_tenant_quota_usages_neutron_with_target_network_resources(self):
|
||||
self._test_tenant_quota_usages_neutron_with_target(
|
||||
targets=['networks', 'subnets', 'routers'])
|
||||
|
||||
def test_tenant_quota_usages_neutron_with_target_security_groups(self):
|
||||
self._test_tenant_quota_usages_neutron_with_target(
|
||||
targets=['security_groups'])
|
||||
|
||||
def test_tenant_quota_usages_neutron_with_target_floating_ips(self):
|
||||
self._test_tenant_quota_usages_neutron_with_target(
|
||||
targets=['floating_ips'])
|
||||
|
||||
@test.create_stubs({api.base: ('is_service_enabled',),
|
||||
api.neutron: ('floating_ip_supported',
|
||||
'tenant_floating_ip_list',
|
||||
'security_group_list',
|
||||
'is_extension_supported',
|
||||
'is_router_enabled',
|
||||
'is_quotas_extension_supported',
|
||||
'tenant_quota_get',
|
||||
'network_list',
|
||||
'subnet_list',
|
||||
'router_list'),
|
||||
cinder: ('is_volume_service_enabled',)})
|
||||
def _test_tenant_quota_usages_neutron_with_target(
|
||||
self, targets):
|
||||
cinder.is_volume_service_enabled(IsA(http.HttpRequest)).AndReturn(True)
|
||||
api.base.is_service_enabled(IsA(http.HttpRequest), 'network') \
|
||||
.AndReturn(True)
|
||||
api.neutron.is_extension_supported(IsA(http.HttpRequest),
|
||||
'security-group').AndReturn(True)
|
||||
api.neutron.is_router_enabled(IsA(http.HttpRequest)).AndReturn(True)
|
||||
api.neutron.is_quotas_extension_supported(IsA(http.HttpRequest)) \
|
||||
.AndReturn(True)
|
||||
api.base.is_service_enabled(IsA(http.HttpRequest), 'compute') \
|
||||
.MultipleTimes().AndReturn(True)
|
||||
|
||||
api.neutron.tenant_quota_get(IsA(http.HttpRequest), '1') \
|
||||
.AndReturn(self.neutron_quotas.first())
|
||||
if 'networks' in targets:
|
||||
api.neutron.network_list(IsA(http.HttpRequest),
|
||||
tenant_id=self.request.user.tenant_id) \
|
||||
.AndReturn(self.networks.list())
|
||||
if 'subnets' in targets:
|
||||
api.neutron.subnet_list(IsA(http.HttpRequest),
|
||||
tenant_id=self.request.user.tenant_id) \
|
||||
.AndReturn(self.subnets.list())
|
||||
if 'routers' in targets:
|
||||
api.neutron.router_list(IsA(http.HttpRequest),
|
||||
tenant_id=self.request.user.tenant_id) \
|
||||
.AndReturn(self.routers.list())
|
||||
if 'floating_ips' in targets:
|
||||
api.neutron.floating_ip_supported(IsA(http.HttpRequest)) \
|
||||
.AndReturn(True)
|
||||
api.neutron.tenant_floating_ip_list(IsA(http.HttpRequest)) \
|
||||
.AndReturn(self.floating_ips.list())
|
||||
if 'security_groups' in targets:
|
||||
api.neutron.security_group_list(IsA(http.HttpRequest)) \
|
||||
.AndReturn(self.security_groups.list())
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
quota_usages = quotas.tenant_quota_usages(self.request,
|
||||
targets=targets)
|
||||
|
||||
network_used = len(self.networks.list())
|
||||
subnet_used = len(self.subnets.list())
|
||||
router_used = len(self.routers.list())
|
||||
fip_used = len(self.floating_ips.list())
|
||||
sg_used = len(self.security_groups.list())
|
||||
expected = {
|
||||
'networks': {'used': network_used, 'quota': 10,
|
||||
'available': 10 - network_used},
|
||||
'subnets': {'used': subnet_used, 'quota': 10,
|
||||
'available': 10 - subnet_used},
|
||||
'routers': {'used': router_used, 'quota': 10,
|
||||
'available': 10 - router_used},
|
||||
'security_groups': {'used': sg_used, 'quota': 20,
|
||||
'available': 20 - sg_used},
|
||||
'floating_ips': {'used': fip_used, 'quota': 50,
|
||||
'available': 50 - fip_used},
|
||||
}
|
||||
expected = dict((k, v) for k, v in expected.items() if k in targets)
|
||||
|
||||
# Compare internal structure of usages to expected.
|
||||
self.assertEqual(expected, quota_usages.usages)
|
||||
# Compare available resources
|
||||
self.assertAvailableQuotasEqual(expected, quota_usages.usages)
|
||||
|
@ -292,6 +292,12 @@ def get_disabled_quotas(request):
|
||||
return disabled_quotas
|
||||
|
||||
|
||||
def _add_usage_if_quota_enabled(usage, name, value, disabled_quotas):
|
||||
if name in disabled_quotas:
|
||||
return
|
||||
usage.tally(name, value)
|
||||
|
||||
|
||||
@profiler.trace
|
||||
def _get_tenant_compute_usages(request, usages, disabled_quotas, tenant_id):
|
||||
enabled_compute_quotas = NOVA_COMPUTE_QUOTA_FIELDS - disabled_quotas
|
||||
@ -310,29 +316,36 @@ def _get_tenant_compute_usages(request, usages, disabled_quotas, tenant_id):
|
||||
else:
|
||||
instances, has_more = nova.server_list(request)
|
||||
|
||||
# Fetch deleted flavors if necessary.
|
||||
flavors = dict([(f.id, f) for f in nova.flavor_list(request)])
|
||||
missing_flavors = [instance.flavor['id'] for instance in instances
|
||||
if instance.flavor['id'] not in flavors]
|
||||
for missing in missing_flavors:
|
||||
if missing not in flavors:
|
||||
try:
|
||||
flavors[missing] = nova.flavor_get(request, missing)
|
||||
except Exception:
|
||||
flavors[missing] = {}
|
||||
exceptions.handle(request, ignore=True)
|
||||
_add_usage_if_quota_enabled(usages, 'instances', len(instances),
|
||||
disabled_quotas)
|
||||
|
||||
usages.tally('instances', len(instances))
|
||||
if {'cores', 'ram'} - disabled_quotas:
|
||||
# Fetch deleted flavors if necessary.
|
||||
flavors = dict([(f.id, f) for f in nova.flavor_list(request)])
|
||||
missing_flavors = [instance.flavor['id'] for instance in instances
|
||||
if instance.flavor['id'] not in flavors]
|
||||
for missing in missing_flavors:
|
||||
if missing not in flavors:
|
||||
try:
|
||||
flavors[missing] = nova.flavor_get(request, missing)
|
||||
except Exception:
|
||||
flavors[missing] = {}
|
||||
exceptions.handle(request, ignore=True)
|
||||
|
||||
# Sum our usage based on the flavors of the instances.
|
||||
for flavor in [flavors[instance.flavor['id']] for instance in instances]:
|
||||
usages.tally('cores', getattr(flavor, 'vcpus', None))
|
||||
usages.tally('ram', getattr(flavor, 'ram', None))
|
||||
# Sum our usage based on the flavors of the instances.
|
||||
for flavor in [flavors[instance.flavor['id']]
|
||||
for instance in instances]:
|
||||
_add_usage_if_quota_enabled(
|
||||
usages, 'cores', getattr(flavor, 'vcpus', None),
|
||||
disabled_quotas)
|
||||
_add_usage_if_quota_enabled(
|
||||
usages, 'ram', getattr(flavor, 'ram', None),
|
||||
disabled_quotas)
|
||||
|
||||
# Initialize the tally if no instances have been launched yet
|
||||
if len(instances) == 0:
|
||||
usages.tally('cores', 0)
|
||||
usages.tally('ram', 0)
|
||||
# Initialize the tally if no instances have been launched yet
|
||||
if len(instances) == 0:
|
||||
_add_usage_if_quota_enabled(usages, 'cores', 0, disabled_quotas)
|
||||
_add_usage_if_quota_enabled(usages, 'ram', 0, disabled_quotas)
|
||||
|
||||
|
||||
@profiler.trace
|
||||
@ -384,21 +397,55 @@ def _get_tenant_volume_usages(request, usages, disabled_quotas, tenant_id):
|
||||
snapshots = cinder.volume_snapshot_list(request)
|
||||
volume_usage = sum([int(v.size) for v in volumes])
|
||||
snapshot_usage = sum([int(s.size) for s in snapshots])
|
||||
usages.tally('gigabytes', (snapshot_usage + volume_usage))
|
||||
usages.tally('volumes', len(volumes))
|
||||
usages.tally('snapshots', len(snapshots))
|
||||
_add_usage_if_quota_enabled(
|
||||
usages, 'gigabytes', (snapshot_usage + volume_usage),
|
||||
disabled_quotas)
|
||||
_add_usage_if_quota_enabled(
|
||||
usages, 'volumes', len(volumes), disabled_quotas)
|
||||
_add_usage_if_quota_enabled(
|
||||
usages, 'snapshots', len(snapshots), disabled_quotas)
|
||||
except cinder.cinder_exception.ClientException:
|
||||
msg = _("Unable to retrieve volume limit information.")
|
||||
exceptions.handle(request, msg)
|
||||
|
||||
|
||||
NETWORK_QUOTA_API_KEY_MAP = {
|
||||
'floating_ips': ['floatingip', 'floating_ips'],
|
||||
'security_groups': ['security_group', 'security_groups'],
|
||||
'security_group_rules': ['security_group_rule', 'security_group_rules'],
|
||||
# Singular form key is used as quota field in the Neutron API.
|
||||
# We convert it explicitly here.
|
||||
# NOTE(amotoki): It is better to be converted in the horizon API wrapper
|
||||
# layer. Ideally the REST APIs of back-end services are consistent.
|
||||
'networks': ['network'],
|
||||
'subnets': ['subnet'],
|
||||
'ports': ['port'],
|
||||
'routers': ['router'],
|
||||
}
|
||||
|
||||
|
||||
def _convert_targets_to_quota_keys(targets):
|
||||
quota_keys = set()
|
||||
for target in targets:
|
||||
if target in NETWORK_QUOTA_API_KEY_MAP:
|
||||
quota_keys.update(NETWORK_QUOTA_API_KEY_MAP[target])
|
||||
continue
|
||||
if target in QUOTA_FIELDS:
|
||||
quota_keys.add(target)
|
||||
continue
|
||||
raise ValueError('"%s" is not a valid quota field name.' % target)
|
||||
return quota_keys
|
||||
|
||||
|
||||
@profiler.trace
|
||||
@memoized
|
||||
def tenant_quota_usages(request, tenant_id=None):
|
||||
def tenant_quota_usages(request, tenant_id=None, targets=None):
|
||||
"""Get our quotas and construct our usage object.
|
||||
|
||||
If no tenant_id is provided, a the request.user.project_id
|
||||
is assumed to be used
|
||||
:param tenant_id: Target tenant ID. If no tenant_id is provided,
|
||||
a the request.user.project_id is assumed to be used.
|
||||
:param targets: A list of quota names to be retrieved.
|
||||
If unspecified, all quota and usage information is retrieved.
|
||||
"""
|
||||
if not tenant_id:
|
||||
tenant_id = request.user.project_id
|
||||
@ -406,6 +453,11 @@ def tenant_quota_usages(request, tenant_id=None):
|
||||
disabled_quotas = get_disabled_quotas(request)
|
||||
usages = QuotaUsage()
|
||||
|
||||
if targets:
|
||||
enabled_quotas = set(QUOTA_FIELDS) - disabled_quotas
|
||||
enabled_quotas &= _convert_targets_to_quota_keys(targets)
|
||||
disabled_quotas = set(QUOTA_FIELDS) - enabled_quotas
|
||||
|
||||
for quota in get_tenant_quota_data(request,
|
||||
disabled_quotas=disabled_quotas,
|
||||
tenant_id=tenant_id):
|
||||
|
@ -0,0 +1,11 @@
|
||||
---
|
||||
fixes:
|
||||
- |
|
||||
Unnecessary API calls to back-end services are eliminated when checking
|
||||
the quota and usage in individual panels. Each panel checks a resource
|
||||
can be created by retrieving the current quota and usage for the resource.
|
||||
However, the previous implementation retrieves quota and usage of unrelated
|
||||
resources (For example, Nova usage is retrieved when checking a network
|
||||
usage). It can be a performance problem in large deployments.
|
||||
This behavior is now fixed to load quota and usage only for resources
|
||||
which are really required.
|
Loading…
Reference in New Issue
Block a user