usage: Use tenant_quota_usages() for Limit Summary
Previously we have almost duplicated logic for the limit summary and quota usages. This commit shares the logic. blueprint make-quotas-great-again Change-Id: Ie5d7e51c9424701fdbcbfbcb1032c0b833a69371
This commit is contained in:
parent
0e0db28969
commit
7fabda7ef1
|
@ -12,11 +12,11 @@
|
|||
<div class="quota_title" title="{{ quota.name }}" data-toggle="tooltip"> {{ quota.name }}</div>
|
||||
<div class="quota_subtitle">
|
||||
{% if quota.max|quotainf != '-1' %}
|
||||
{% if quota.type == "totalRAMUsed" %}
|
||||
{% if quota.type == "ram" %}
|
||||
{% blocktrans trimmed with usedphrase=quota.text used=quota.used|mb_float_format available=quota.max|quotainf|mb_float_format %}
|
||||
{{ usedphrase }} <span> {{ used }} </span>of<span> {{ available }} </span>
|
||||
{% endblocktrans %}
|
||||
{% elif quota.type == "totalGigabytesUsed" %}
|
||||
{% elif quota.type == "gigabytes" %}
|
||||
{% blocktrans trimmed with usedphrase=quota.text used=quota.used|diskgbformat available=quota.max|quotainf|diskgbformat %}
|
||||
{{ usedphrase }} <span> {{ used }} </span>of<span> {{ available }} </span>
|
||||
{% endblocktrans %}
|
||||
|
|
|
@ -37,77 +37,65 @@ class UsageViewTests(test.TestCase):
|
|||
|
||||
@test.create_mocks({api.nova: (
|
||||
'usage_get',
|
||||
('tenant_absolute_limits', 'nova_tenant_absolute_limits'),
|
||||
'extension_supported',
|
||||
)})
|
||||
def _stub_nova_api_calls(self,
|
||||
nova_stu_enabled=True,
|
||||
tenant_limits_exception=False,
|
||||
stu_exception=False, overview_days_range=1):
|
||||
def _stub_api_calls(self, nova_stu_enabled=True,
|
||||
stu_exception=False, overview_days_range=1,
|
||||
quota_usage_overrides=None):
|
||||
self.mock_extension_supported.side_effect = [nova_stu_enabled,
|
||||
nova_stu_enabled]
|
||||
if tenant_limits_exception:
|
||||
self.mock_nova_tenant_absolute_limits.side_effect = \
|
||||
tenant_limits_exception
|
||||
else:
|
||||
self.mock_nova_tenant_absolute_limits.return_value = \
|
||||
self.limits['absolute']
|
||||
if nova_stu_enabled:
|
||||
self._nova_stu_enabled(stu_exception,
|
||||
overview_days_range=overview_days_range)
|
||||
|
||||
def _check_nova_api_calls(self,
|
||||
nova_stu_enabled=True,
|
||||
tenant_limits_exception=False,
|
||||
stu_exception=False, overview_days_range=1):
|
||||
self._stub_tenant_quota_usages(overrides=quota_usage_overrides)
|
||||
|
||||
def _check_api_calls(self, nova_stu_enabled=True,
|
||||
stu_exception=False, overview_days_range=1):
|
||||
self.assert_mock_multiple_calls_with_same_arguments(
|
||||
self.mock_extension_supported, 2,
|
||||
mock.call('SimpleTenantUsage', test.IsHttpRequest()))
|
||||
self.mock_nova_tenant_absolute_limits.assert_called_once_with(
|
||||
test.IsHttpRequest(), reserved=True)
|
||||
if nova_stu_enabled:
|
||||
self._check_stu_enabled(stu_exception,
|
||||
overview_days_range=overview_days_range)
|
||||
else:
|
||||
self.mock_usage_get.assert_not_called()
|
||||
self._check_tenant_quota_usages()
|
||||
|
||||
@test.create_mocks({api.cinder: (
|
||||
('tenant_absolute_limits', 'cinder_tenant_absolute_limits'),
|
||||
)})
|
||||
def _stub_cinder_api_calls(self):
|
||||
self.mock_cinder_tenant_absolute_limits.return_value = \
|
||||
self.cinder_limits['absolute']
|
||||
@staticmethod
|
||||
def _add_quota_usages(usages, quota_usages, excludes=None):
|
||||
excludes = excludes or []
|
||||
for k in quota_usages.usages:
|
||||
if k in excludes:
|
||||
continue
|
||||
quota = quota_usages[k]['quota']
|
||||
if quota == float('inf'):
|
||||
quota = -1
|
||||
usages.add_quota(api.base.Quota(k, quota))
|
||||
usages.tally(k, quota_usages[k]['used'])
|
||||
|
||||
def _check_cinder_api_calls(self):
|
||||
self.mock_cinder_tenant_absolute_limits.assert_called_once_with(
|
||||
@test.create_mocks({usage.quotas: ('tenant_quota_usages',)})
|
||||
def _stub_tenant_quota_usages(self, overrides):
|
||||
usages_data = usage.quotas.QuotaUsage()
|
||||
self._add_quota_usages(usages_data, self.quota_usages.first(),
|
||||
# At now, nova quota_usages contains
|
||||
# volumes and gigabytes.
|
||||
excludes=('volumes', 'gigabytes'))
|
||||
self._add_quota_usages(
|
||||
usages_data, self.neutron_quota_usages.first())
|
||||
self._add_quota_usages(usages_data, self.cinder_quota_usages.first())
|
||||
if overrides:
|
||||
for key, value in overrides.items():
|
||||
if 'quota' in value:
|
||||
usages_data.add_quota(api.base.Quota(key, value['quota']))
|
||||
if 'used' in value:
|
||||
usages_data.tally(key, value['used'])
|
||||
self.mock_tenant_quota_usages.return_value = usages_data
|
||||
|
||||
def _check_tenant_quota_usages(self):
|
||||
self.mock_tenant_quota_usages.assert_called_once_with(
|
||||
test.IsHttpRequest())
|
||||
|
||||
@test.create_mocks({api.neutron: ('security_group_list',
|
||||
'tenant_floating_ip_list',
|
||||
'floating_ip_supported',
|
||||
'is_extension_supported')})
|
||||
def _stub_neutron_api_calls(self, neutron_sg_enabled=True):
|
||||
self.mock_is_extension_supported.return_value = neutron_sg_enabled
|
||||
self.mock_floating_ip_supported.return_value = True
|
||||
self.mock_tenant_floating_ip_list.return_value = \
|
||||
self.floating_ips.list()
|
||||
if neutron_sg_enabled:
|
||||
self.mock_security_group_list.return_value = \
|
||||
self.security_groups.list()
|
||||
|
||||
def _check_neutron_api_calls(self, neutron_sg_enabled=True):
|
||||
self.mock_is_extension_supported.assert_called_once_with(
|
||||
test.IsHttpRequest(), 'security-group')
|
||||
self.mock_floating_ip_supported.assert_called_once_with(
|
||||
test.IsHttpRequest())
|
||||
self.mock_tenant_floating_ip_list.assert_called_once_with(
|
||||
test.IsHttpRequest())
|
||||
if neutron_sg_enabled:
|
||||
self.mock_security_group_list.assert_called_once_with(
|
||||
test.IsHttpRequest())
|
||||
else:
|
||||
self.mock_security_group_list.assert_not_called()
|
||||
|
||||
def _nova_stu_enabled(self, exception=False, overview_days_range=1):
|
||||
if exception:
|
||||
self.mock_usage_get.side_effect = exception
|
||||
|
@ -129,7 +117,7 @@ class UsageViewTests(test.TestCase):
|
|||
test.IsHttpRequest(), self.tenant.id, start, end)
|
||||
|
||||
def _common_assertions(self, nova_stu_enabled,
|
||||
maxTotalFloatingIps=float("inf")):
|
||||
maxTotalFloatingIps=50):
|
||||
res = self.client.get(reverse('horizon:project:overview:index'))
|
||||
usages = res.context['usage']
|
||||
self.assertTemplateUsed(res, 'project/overview/usage.html')
|
||||
|
@ -140,7 +128,7 @@ class UsageViewTests(test.TestCase):
|
|||
self.assertContains(res, 'form-inline')
|
||||
else:
|
||||
self.assertNotContains(res, 'form-inline')
|
||||
self.assertEqual(usages.limits['maxTotalFloatingIps'],
|
||||
self.assertEqual(usages.limits['floatingip']['quota'],
|
||||
maxTotalFloatingIps)
|
||||
|
||||
@override_settings(OVERVIEW_DAYS_RANGE=None)
|
||||
|
@ -155,37 +143,13 @@ class UsageViewTests(test.TestCase):
|
|||
self._test_usage(nova_stu_enabled=False, overview_days_range=None)
|
||||
|
||||
def _test_usage(self, nova_stu_enabled, overview_days_range=1):
|
||||
self._stub_nova_api_calls(nova_stu_enabled,
|
||||
overview_days_range=overview_days_range)
|
||||
self._stub_neutron_api_calls()
|
||||
self._stub_cinder_api_calls()
|
||||
self._stub_api_calls(nova_stu_enabled,
|
||||
overview_days_range=overview_days_range)
|
||||
|
||||
self._common_assertions(nova_stu_enabled)
|
||||
|
||||
self._check_nova_api_calls(nova_stu_enabled,
|
||||
overview_days_range=overview_days_range)
|
||||
self._check_neutron_api_calls()
|
||||
self._check_cinder_api_calls()
|
||||
|
||||
def test_usage_nova_network(self):
|
||||
self._test_usage_nova_network(nova_stu_enabled=True)
|
||||
|
||||
def test_usage_nova_network_disabled(self):
|
||||
self._test_usage_nova_network(nova_stu_enabled=False)
|
||||
|
||||
@test.create_mocks({api.base: ('is_service_enabled',),
|
||||
api.cinder: ('is_volume_service_enabled',)})
|
||||
def _test_usage_nova_network(self, nova_stu_enabled):
|
||||
self._stub_nova_api_calls(nova_stu_enabled)
|
||||
self.mock_is_service_enabled.return_value = False
|
||||
self.mock_is_volume_service_enabled.return_value = False
|
||||
|
||||
self._common_assertions(nova_stu_enabled, maxTotalFloatingIps=10)
|
||||
self._check_nova_api_calls(nova_stu_enabled)
|
||||
self.mock_is_service_enabled.assert_called_once_with(
|
||||
test.IsHttpRequest(), 'network')
|
||||
self.mock_is_volume_service_enabled.assert_called_once_with(
|
||||
test.IsHttpRequest())
|
||||
self._check_api_calls(nova_stu_enabled,
|
||||
overview_days_range=overview_days_range)
|
||||
|
||||
def test_unauthorized(self):
|
||||
url = reverse('horizon:admin:volumes:index')
|
||||
|
@ -209,59 +173,33 @@ class UsageViewTests(test.TestCase):
|
|||
self._test_usage_csv(nova_stu_enabled=False)
|
||||
|
||||
def _test_usage_csv(self, nova_stu_enabled=True, overview_days_range=1):
|
||||
self._stub_nova_api_calls(nova_stu_enabled,
|
||||
overview_days_range=overview_days_range)
|
||||
self._stub_neutron_api_calls()
|
||||
self._stub_cinder_api_calls()
|
||||
self._stub_api_calls(nova_stu_enabled,
|
||||
overview_days_range=overview_days_range)
|
||||
|
||||
res = self.client.get(reverse('horizon:project:overview:index') +
|
||||
"?format=csv")
|
||||
self.assertTemplateUsed(res, 'project/overview/usage.csv')
|
||||
self.assertIsInstance(res.context['usage'], usage.ProjectUsage)
|
||||
self._check_nova_api_calls(nova_stu_enabled,
|
||||
overview_days_range=overview_days_range)
|
||||
self._check_neutron_api_calls()
|
||||
self._check_cinder_api_calls()
|
||||
self._check_api_calls(nova_stu_enabled,
|
||||
overview_days_range=overview_days_range)
|
||||
|
||||
def test_usage_exception_usage(self):
|
||||
self._stub_nova_api_calls(stu_exception=self.exceptions.nova)
|
||||
self._stub_neutron_api_calls()
|
||||
self._stub_cinder_api_calls()
|
||||
self._stub_api_calls(stu_exception=self.exceptions.nova)
|
||||
|
||||
res = self.client.get(reverse('horizon:project:overview:index'))
|
||||
self.assertTemplateUsed(res, 'project/overview/usage.html')
|
||||
self.assertEqual(res.context['usage'].usage_list, [])
|
||||
|
||||
self._check_nova_api_calls(stu_exception=self.exceptions.nova)
|
||||
self._check_neutron_api_calls()
|
||||
self._check_cinder_api_calls()
|
||||
|
||||
def test_usage_exception_quota(self):
|
||||
self._stub_nova_api_calls(tenant_limits_exception=self.exceptions.nova)
|
||||
self._stub_neutron_api_calls()
|
||||
self._stub_cinder_api_calls()
|
||||
|
||||
res = self.client.get(reverse('horizon:project:overview:index'))
|
||||
self.assertTemplateUsed(res, 'project/overview/usage.html')
|
||||
self.assertEqual(res.context['usage'].quotas, {})
|
||||
|
||||
self._check_nova_api_calls(
|
||||
tenant_limits_exception=self.exceptions.nova)
|
||||
self._check_neutron_api_calls()
|
||||
self._check_cinder_api_calls()
|
||||
self._check_api_calls(stu_exception=self.exceptions.nova)
|
||||
|
||||
def test_usage_default_tenant(self):
|
||||
self._stub_nova_api_calls()
|
||||
self._stub_neutron_api_calls()
|
||||
self._stub_cinder_api_calls()
|
||||
self._stub_api_calls()
|
||||
|
||||
res = self.client.get(reverse('horizon:project:overview:index'))
|
||||
self.assertTemplateUsed(res, 'project/overview/usage.html')
|
||||
self.assertIsInstance(res.context['usage'], usage.ProjectUsage)
|
||||
|
||||
self._check_nova_api_calls()
|
||||
self._check_neutron_api_calls()
|
||||
self._check_cinder_api_calls()
|
||||
self._check_api_calls()
|
||||
|
||||
@test.update_settings(OPENSTACK_NEUTRON_NETWORK={'enable_quotas': True})
|
||||
def test_usage_with_neutron(self):
|
||||
|
@ -275,59 +213,13 @@ class UsageViewTests(test.TestCase):
|
|||
def test_usage_with_neutron_floating_ip_disabled(self):
|
||||
self._test_usage_with_neutron(neutron_fip_enabled=False)
|
||||
|
||||
def _test_usage_with_neutron_prepare(self):
|
||||
self._stub_nova_api_calls()
|
||||
self._stub_cinder_api_calls()
|
||||
|
||||
def _check_nova_cinder_calls_with_neutron_prepare(self):
|
||||
self._check_nova_api_calls()
|
||||
self._check_cinder_api_calls()
|
||||
|
||||
@test.create_mocks({api.neutron: ('tenant_quota_get',
|
||||
'is_extension_supported',
|
||||
'floating_ip_supported',
|
||||
'tenant_floating_ip_list',
|
||||
'security_group_list')})
|
||||
def _test_usage_with_neutron(self,
|
||||
neutron_sg_enabled=True,
|
||||
neutron_fip_enabled=True):
|
||||
self._test_usage_with_neutron_prepare()
|
||||
|
||||
self.mock_is_extension_supported.side_effect = [True,
|
||||
neutron_sg_enabled]
|
||||
self.mock_floating_ip_supported.return_value = neutron_fip_enabled
|
||||
if neutron_fip_enabled:
|
||||
self.mock_tenant_floating_ip_list.return_value = \
|
||||
self.floating_ips.list()
|
||||
if neutron_sg_enabled:
|
||||
self.mock_security_group_list.return_value = \
|
||||
self.security_groups.list()
|
||||
self.mock_tenant_quota_get.return_value = self.neutron_quotas.first()
|
||||
|
||||
self._stub_api_calls()
|
||||
self._test_usage_with_neutron_check(neutron_sg_enabled,
|
||||
neutron_fip_enabled)
|
||||
|
||||
self.mock_is_extension_supported.assert_has_calls([
|
||||
mock.call(test.IsHttpRequest(), 'quotas'),
|
||||
mock.call(test.IsHttpRequest(), 'security-group'),
|
||||
])
|
||||
self.assertEqual(2, self.mock_is_extension_supported.call_count)
|
||||
self.mock_floating_ip_supported.assert_called_once_with(
|
||||
test.IsHttpRequest())
|
||||
if neutron_fip_enabled:
|
||||
self.mock_tenant_floating_ip_list.assert_called_once_with(
|
||||
test.IsHttpRequest())
|
||||
else:
|
||||
self.mock_tenant_floating_ip_list.assert_not_called()
|
||||
if neutron_sg_enabled:
|
||||
self.mock_security_group_list.assert_called_once_with(
|
||||
test.IsHttpRequest())
|
||||
else:
|
||||
self.mock_security_group_list.assert_not_called()
|
||||
self.mock_tenant_quota_get.assert_called_once_with(
|
||||
test.IsHttpRequest(), self.tenant.id)
|
||||
|
||||
self._check_nova_cinder_calls_with_neutron_prepare()
|
||||
self._check_api_calls()
|
||||
|
||||
def _test_usage_with_neutron_check(self, neutron_sg_enabled=True,
|
||||
neutron_fip_expected=True,
|
||||
|
@ -339,98 +231,37 @@ class UsageViewTests(test.TestCase):
|
|||
self.assertContains(res, 'Security Groups')
|
||||
|
||||
res_limits = res.context['usage'].limits
|
||||
# Make sure the floating IPs comes from Neutron (50 vs. 10)
|
||||
max_floating_ips = res_limits['maxTotalFloatingIps']
|
||||
max_floating_ips = res_limits['floatingip']['quota']
|
||||
self.assertEqual(max_floating_ips, max_fip_expected)
|
||||
if neutron_sg_enabled:
|
||||
# Make sure the security group limit comes from Neutron (20 vs. 10)
|
||||
max_security_groups = res_limits['maxSecurityGroups']
|
||||
max_security_groups = res_limits['security_group']['quota']
|
||||
self.assertEqual(max_security_groups, max_sg_expected)
|
||||
|
||||
@test.update_settings(OPENSTACK_NEUTRON_NETWORK={'enable_quotas': True})
|
||||
@mock.patch.object(api.neutron, 'is_extension_supported')
|
||||
def test_usage_with_neutron_quotas_ext_error(self,
|
||||
mock_is_extension_supported):
|
||||
self._test_usage_with_neutron_prepare()
|
||||
mock_is_extension_supported.side_effect = self.exceptions.neutron
|
||||
|
||||
self._test_usage_with_neutron_check(neutron_fip_expected=False,
|
||||
max_fip_expected=float("inf"),
|
||||
max_sg_expected=float("inf"))
|
||||
|
||||
mock_is_extension_supported.assert_called_once_with(
|
||||
test.IsHttpRequest(), 'quotas')
|
||||
self._check_nova_cinder_calls_with_neutron_prepare()
|
||||
|
||||
@test.update_settings(OPENSTACK_NEUTRON_NETWORK={'enable_quotas': True})
|
||||
@mock.patch.object(api.neutron, 'is_extension_supported')
|
||||
def test_usage_with_neutron_sg_ext_error(self,
|
||||
mock_is_extension_supported):
|
||||
self._test_usage_with_neutron_prepare()
|
||||
mock_is_extension_supported.side_effect = [
|
||||
True, # quotas
|
||||
self.exceptions.neutron, # security-group
|
||||
]
|
||||
|
||||
self._test_usage_with_neutron_check(neutron_fip_expected=False,
|
||||
max_fip_expected=float("inf"),
|
||||
max_sg_expected=float("inf"))
|
||||
|
||||
self.assertEqual(2, mock_is_extension_supported.call_count)
|
||||
mock_is_extension_supported.assert_has_calls([
|
||||
mock.call(test.IsHttpRequest(), 'quotas'),
|
||||
mock.call(test.IsHttpRequest(), 'security-group'),
|
||||
])
|
||||
self._check_nova_cinder_calls_with_neutron_prepare()
|
||||
|
||||
def test_usage_with_cinder(self):
|
||||
self._test_usage_cinder(cinder_enabled=True)
|
||||
|
||||
def test_usage_without_cinder(self):
|
||||
self._test_usage_cinder(cinder_enabled=False)
|
||||
|
||||
@test.create_mocks({api.base: ('is_service_enabled',),
|
||||
api.cinder: ('is_volume_service_enabled',)})
|
||||
def _test_usage_cinder(self, cinder_enabled):
|
||||
self._stub_nova_api_calls(nova_stu_enabled=True)
|
||||
|
||||
if cinder_enabled:
|
||||
self._stub_cinder_api_calls()
|
||||
|
||||
self.mock_is_service_enabled.return_value = False
|
||||
self.mock_is_volume_service_enabled.return_value = cinder_enabled
|
||||
def test_usage_cinder(self):
|
||||
self._stub_api_calls(
|
||||
quota_usage_overrides={'volumes': {'used': 4},
|
||||
'gigabytes': {'used': 400}}
|
||||
)
|
||||
|
||||
res = self.client.get(reverse('horizon:project:overview:index'))
|
||||
usages = res.context['usage']
|
||||
self.assertTemplateUsed(res, 'project/overview/usage.html')
|
||||
self.assertIsInstance(usages, usage.ProjectUsage)
|
||||
if cinder_enabled:
|
||||
self.assertEqual(usages.limits['totalVolumesUsed'], 4)
|
||||
self.assertEqual(usages.limits['maxTotalVolumes'], 20)
|
||||
self.assertEqual(usages.limits['totalGigabytesUsed'], 400)
|
||||
self.assertEqual(usages.limits['maxTotalVolumeGigabytes'], 1000)
|
||||
else:
|
||||
self.assertNotIn('totalVolumesUsed', usages.limits)
|
||||
|
||||
self._check_nova_api_calls(nova_stu_enabled=True)
|
||||
if cinder_enabled:
|
||||
self._check_cinder_api_calls()
|
||||
self.assertEqual(usages.limits['volumes']['used'], 4)
|
||||
self.assertEqual(usages.limits['volumes']['quota'], 10)
|
||||
self.assertEqual(usages.limits['gigabytes']['used'], 400)
|
||||
self.assertEqual(usages.limits['gigabytes']['quota'], 1000)
|
||||
|
||||
self.mock_is_service_enabled.assert_called_once_with(
|
||||
test.IsHttpRequest(), 'network')
|
||||
self.mock_is_volume_service_enabled.assert_called_once_with(
|
||||
test.IsHttpRequest())
|
||||
self._check_api_calls(nova_stu_enabled=True)
|
||||
|
||||
def _test_usage_charts(self):
|
||||
self._stub_nova_api_calls(nova_stu_enabled=False)
|
||||
self._stub_neutron_api_calls()
|
||||
self._stub_cinder_api_calls()
|
||||
def _test_usage_charts(self, quota_usage_overrides=None):
|
||||
self._stub_api_calls(nova_stu_enabled=False,
|
||||
quota_usage_overrides=quota_usage_overrides)
|
||||
|
||||
res = self.client.get(reverse('horizon:project:overview:index'))
|
||||
|
||||
self._check_nova_api_calls(nova_stu_enabled=False)
|
||||
self._check_neutron_api_calls()
|
||||
self._check_cinder_api_calls()
|
||||
self._check_api_calls(nova_stu_enabled=False)
|
||||
|
||||
return res
|
||||
|
||||
|
@ -439,9 +270,10 @@ class UsageViewTests(test.TestCase):
|
|||
self.assertIn('charts', res.context)
|
||||
|
||||
def test_usage_charts_infinite_quota(self):
|
||||
res = self._test_usage_charts()
|
||||
res = self._test_usage_charts(
|
||||
quota_usage_overrides={'floatingip': {'quota': -1}})
|
||||
|
||||
max_floating_ips = res.context['usage'].limits['maxTotalFloatingIps']
|
||||
max_floating_ips = res.context['usage'].limits['floatingip']['quota']
|
||||
self.assertEqual(max_floating_ips, float("inf"))
|
||||
|
||||
self.assertContains(res, '(No Limit)')
|
||||
|
|
|
@ -307,8 +307,8 @@ def data(TEST):
|
|||
# Quota Usages
|
||||
quota_usage_data = {'gigabytes': {'used': 0,
|
||||
'quota': 1000},
|
||||
'instances': {'used': 0,
|
||||
'quota': 10},
|
||||
'volumes': {'used': 0,
|
||||
'quota': 10},
|
||||
'snapshots': {'used': 0,
|
||||
'quota': 10}}
|
||||
quota_usage = usage_quotas.QuotaUsage()
|
||||
|
|
|
@ -634,7 +634,9 @@ def data(TEST):
|
|||
'subnet': {'used': 0, 'quota': 5},
|
||||
'port': {'used': 0, 'quota': 5},
|
||||
'router': {'used': 0, 'quota': 5},
|
||||
'floatingip': {'used': 0, 'quota': 10},
|
||||
'floatingip': {'used': 0, 'quota': 50},
|
||||
'security_group': {'used': 0, 'quota': 20},
|
||||
'security_group_rule': {'used': 0, 'quota': 100},
|
||||
}
|
||||
quota_usage = usage_quotas.QuotaUsage()
|
||||
for k, v in quota_usage_data.items():
|
||||
|
|
|
@ -23,6 +23,7 @@ from horizon import forms
|
|||
from horizon import messages
|
||||
|
||||
from openstack_dashboard import api
|
||||
from openstack_dashboard.usage import quotas
|
||||
|
||||
|
||||
class BaseUsage(object):
|
||||
|
@ -191,96 +192,4 @@ class ProjectUsage(BaseUsage):
|
|||
return (usage,)
|
||||
|
||||
def get_limits(self):
|
||||
try:
|
||||
self.limits = api.nova.tenant_absolute_limits(self.request,
|
||||
reserved=True)
|
||||
except Exception:
|
||||
exceptions.handle(self.request,
|
||||
_("Unable to retrieve limit information."))
|
||||
self._get_neutron_limits()
|
||||
self._get_cinder_limits()
|
||||
|
||||
def _get_neutron_usage(self, limits, resource_name):
|
||||
resource_map = {
|
||||
'floatingip': {
|
||||
'api': api.neutron.tenant_floating_ip_list,
|
||||
'limit_name': 'totalFloatingIpsUsed',
|
||||
'message': _('Unable to retrieve floating IP addresses.')
|
||||
},
|
||||
'security_group': {
|
||||
'api': api.neutron.security_group_list,
|
||||
'limit_name': 'totalSecurityGroupsUsed',
|
||||
'message': _('Unable to retrieve security groups.')
|
||||
}
|
||||
}
|
||||
|
||||
resource = resource_map[resource_name]
|
||||
try:
|
||||
method = resource['api']
|
||||
current_used = len(method(self.request))
|
||||
except Exception:
|
||||
current_used = 0
|
||||
msg = resource['message']
|
||||
exceptions.handle(self.request, msg)
|
||||
|
||||
limits[resource['limit_name']] = current_used
|
||||
|
||||
def _set_neutron_limit(self, limits, neutron_quotas, resource_name):
|
||||
limit_name_map = {
|
||||
'floatingip': 'maxTotalFloatingIps',
|
||||
'security_group': 'maxSecurityGroups',
|
||||
}
|
||||
if neutron_quotas is None:
|
||||
resource_max = float("inf")
|
||||
else:
|
||||
resource_max = getattr(neutron_quotas.get(resource_name),
|
||||
'limit', float("inf"))
|
||||
if resource_max == -1:
|
||||
resource_max = float("inf")
|
||||
|
||||
limits[limit_name_map[resource_name]] = resource_max
|
||||
|
||||
def _get_neutron_limits(self):
|
||||
if not api.base.is_service_enabled(self.request, 'network'):
|
||||
return
|
||||
try:
|
||||
neutron_quotas_supported = (
|
||||
api.neutron.is_quotas_extension_supported(self.request))
|
||||
neutron_sg_used = (
|
||||
api.neutron.is_extension_supported(self.request,
|
||||
'security-group'))
|
||||
if api.neutron.floating_ip_supported(self.request):
|
||||
self._get_neutron_usage(self.limits, 'floatingip')
|
||||
if neutron_sg_used:
|
||||
self._get_neutron_usage(self.limits, 'security_group')
|
||||
# Quotas are an optional extension in Neutron. If it isn't
|
||||
# enabled, assume the floating IP limit is infinite.
|
||||
if neutron_quotas_supported:
|
||||
neutron_quotas = api.neutron.tenant_quota_get(self.request,
|
||||
self.project_id)
|
||||
else:
|
||||
neutron_quotas = None
|
||||
except Exception:
|
||||
# Assume neutron security group and quotas are enabled
|
||||
# because they are enabled in most Neutron plugins.
|
||||
neutron_sg_used = True
|
||||
neutron_quotas = None
|
||||
msg = _('Unable to retrieve network quota information.')
|
||||
exceptions.handle(self.request, msg)
|
||||
|
||||
self._set_neutron_limit(self.limits, neutron_quotas, 'floatingip')
|
||||
if neutron_sg_used:
|
||||
self._set_neutron_limit(self.limits, neutron_quotas,
|
||||
'security_group')
|
||||
|
||||
def _get_cinder_limits(self):
|
||||
"""Get volume limits if cinder is enabled."""
|
||||
if not api.cinder.is_volume_service_enabled(self.request):
|
||||
return
|
||||
try:
|
||||
self.limits.update(api.cinder.tenant_absolute_limits(self.request))
|
||||
except Exception:
|
||||
msg = _("Unable to retrieve volume limit information.")
|
||||
exceptions.handle(self.request, msg)
|
||||
|
||||
return
|
||||
self.limits = quotas.tenant_quota_usages(self.request)
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import collections
|
||||
|
||||
from django.utils.translation import pgettext_lazy
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
|
@ -83,41 +85,43 @@ class UsageView(tables.DataTableView):
|
|||
return resp
|
||||
|
||||
|
||||
ChartDef = collections.namedtuple(
|
||||
'ChartDef',
|
||||
('quota_key', 'label', 'used_phrase'))
|
||||
# (quota key, Human Readable Name, text to display when
|
||||
# describing the quota by default it is 'Used')
|
||||
CHART_DEFS = [
|
||||
ChartDef("instances", _("Instances"), None),
|
||||
ChartDef("cores", _("VCPUs"), None),
|
||||
ChartDef("ram", _("RAM"), None),
|
||||
ChartDef("floatingip", _("Floating IPs"),
|
||||
pgettext_lazy('Label in the limit summary', "Allocated")),
|
||||
ChartDef("security_group", _("Security Groups"), None),
|
||||
ChartDef("volumes", _("Volumes"), None),
|
||||
ChartDef("gigabytes", _("Volume Storage"), None),
|
||||
]
|
||||
|
||||
|
||||
class ProjectUsageView(UsageView):
|
||||
|
||||
def _get_charts_data(self):
|
||||
charts = []
|
||||
|
||||
# (Used key, Max key, Human Readable Name, text to display when
|
||||
# describing the quota by default it is 'Used')
|
||||
types = [("totalInstancesUsed", "maxTotalInstances", _("Instances")),
|
||||
("totalCoresUsed", "maxTotalCores", _("VCPUs")),
|
||||
("totalRAMUsed", "maxTotalRAMSize", _("RAM")),
|
||||
("totalFloatingIpsUsed", "maxTotalFloatingIps",
|
||||
_("Floating IPs"),
|
||||
pgettext_lazy('Label in the limit summary', "Allocated")),
|
||||
("totalSecurityGroupsUsed", "maxSecurityGroups",
|
||||
_("Security Groups"))]
|
||||
# Check for volume usage
|
||||
if 'totalVolumesUsed' in self.usage.limits and self.usage.limits[
|
||||
'totalVolumesUsed'] >= 0:
|
||||
types.append(("totalVolumesUsed", "maxTotalVolumes",
|
||||
_("Volumes")))
|
||||
types.append(("totalGigabytesUsed", "maxTotalVolumeGigabytes",
|
||||
_("Volume Storage")))
|
||||
for t in types:
|
||||
if t[0] in self.usage.limits and t[1] in self.usage.limits:
|
||||
for t in CHART_DEFS:
|
||||
if t.quota_key not in self.usage.limits:
|
||||
continue
|
||||
key = t.quota_key
|
||||
used = self.usage.limits[key]['used']
|
||||
quota = self.usage.limits[key]['quota']
|
||||
text = t.used_phrase
|
||||
if text is None:
|
||||
text = pgettext_lazy('Label in the limit summary', 'Used')
|
||||
if len(t) > 3:
|
||||
text = t[3]
|
||||
charts.append({
|
||||
'type': t[0],
|
||||
'name': t[2],
|
||||
'used': self.usage.limits[t[0]],
|
||||
'max': self.usage.limits[t[1]],
|
||||
'text': text
|
||||
})
|
||||
|
||||
charts.append({
|
||||
'type': key,
|
||||
'name': t.label,
|
||||
'used': used,
|
||||
'max': quota,
|
||||
'text': text
|
||||
})
|
||||
return charts
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
|
|
Loading…
Reference in New Issue