Pass correct project ID to get tenant_usages

The current implementation of tenant_quota_usages did not allow for
queries regarding projects that were not the currently active project,
this meant that when an admin went to edit or create a project it tried
to verify the usages against the wrong project. This patch adds the
code for passing a project id to the tenant_quota_usages function so
that the usages can be fetched for a specific project, as well as
removes the need for usage validation on creation of a new project.

Change-Id: I3ec84d14c8be7e3aae066119e963c4093f8aa345
Closes-Bug: 1380701
This commit is contained in:
Sam Betts 2014-10-16 14:20:36 +01:00
parent d471bae2cc
commit 193d40a414
4 changed files with 57 additions and 91 deletions

View File

@ -217,7 +217,7 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
['<CreateProjectInfo: createprojectinfoaction>',
'<UpdateProjectMembers: update_members>',
'<UpdateProjectGroups: update_group_members>',
'<UpdateProjectQuota: update_quotas>'])
'<CreateProjectQuota: create_quotas>'])
def test_add_project_get_domain(self):
domain = self.domains.get(id="1")
@ -305,7 +305,6 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
users = self._get_all_users(domain_id)
groups = self._get_all_groups(domain_id)
roles = self.roles.list()
quota_usages = self.quota_usages.first()
# init
quotas.get_disabled_quotas(IsA(http.HttpRequest)) \
@ -325,8 +324,6 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
.AndReturn(groups)
# handle
quotas.tenant_quota_usages(IsA(http.HttpRequest)) \
.AndReturn(quota_usages)
project_details = self._get_project_info(project)
quota_data = self._get_quota_info(quota)
@ -459,7 +456,6 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
users = self._get_all_users(domain_id)
groups = self._get_all_groups(domain_id)
roles = self.roles.list()
quota_usages = self.quota_usages.first()
# init
api.keystone.get_default_domain(IsA(http.HttpRequest)) \
@ -478,8 +474,6 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
.AndReturn(groups)
# handle
quotas.tenant_quota_usages(IsA(http.HttpRequest)) \
.AndReturn(quota_usages)
project_details = self._get_project_info(project)
api.keystone.tenant_create(IsA(http.HttpRequest), **project_details) \
@ -501,57 +495,6 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
domain_context_name=domain.name)
self.test_add_project_tenant_create_error()
@test.create_stubs({api.keystone: ('user_list',
'role_list',
'group_list',
'get_default_domain',
'get_default_role',
'add_tenant_user_role'),
quotas: ('get_default_quota_data',
'get_disabled_quotas',
'tenant_quota_usages'),
api.nova: ('tenant_quota_update',)})
def test_project_quota_update_invalid_value(self):
project = self.tenants.first()
quota = self.quotas.first()
default_role = self.roles.first()
default_domain = self._get_default_domain()
domain_id = default_domain.id
users = self._get_all_users(domain_id)
groups = self._get_all_groups(domain_id)
roles = self.roles.list()
quota_usages = self.quota_usages.first()
quota_usages['instances']['used'] = 5
# init
api.keystone.get_default_domain(IsA(http.HttpRequest)) \
.AndReturn(default_domain)
quotas.get_disabled_quotas(IsA(http.HttpRequest)) \
.AndReturn(self.disabled_quotas.first())
quotas.get_default_quota_data(IsA(http.HttpRequest)).AndReturn(quota)
api.keystone.get_default_role(IsA(http.HttpRequest)) \
.MultipleTimes().AndReturn(default_role)
api.keystone.user_list(IsA(http.HttpRequest), domain=domain_id) \
.AndReturn(users)
api.keystone.role_list(IsA(http.HttpRequest)) \
.MultipleTimes().AndReturn(roles)
api.keystone.group_list(IsA(http.HttpRequest), domain=domain_id) \
.AndReturn(groups)
quotas.tenant_quota_usages(IsA(http.HttpRequest)) \
.AndReturn(quota_usages)
self.mox.ReplayAll()
workflow_data = {}
workflow_data.update(self._get_workflow_data(project, quota))
workflow_data['instances'] = 2
url = reverse('horizon:identity:projects:create')
res = self.client.post(url, workflow_data)
msg = 'Quota value(s) cannot be less than the current usage ' \
'value(s): 5 Instances used.'
self.assertContains(res, msg)
@test.create_stubs({api.keystone: ('tenant_create',
'user_list',
'role_list',
@ -572,7 +515,6 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
users = self._get_all_users(domain_id)
groups = self._get_all_groups(domain_id)
roles = self.roles.list()
quota_usages = self.quota_usages.first()
# init
api.keystone.get_default_domain(IsA(http.HttpRequest)) \
@ -591,8 +533,6 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
.AndReturn(groups)
# handle
quotas.tenant_quota_usages(IsA(http.HttpRequest)) \
.AndReturn(quota_usages)
project_details = self._get_project_info(project)
quota_data = self._get_quota_info(quota)
@ -661,7 +601,6 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
users = self._get_all_users(domain_id)
groups = self._get_all_groups(domain_id)
roles = self.roles.list()
quota_usages = self.quota_usages.first()
# init
api.keystone.get_default_domain(IsA(http.HttpRequest)) \
@ -680,8 +619,6 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
.AndReturn(groups)
# handle
quotas.tenant_quota_usages(IsA(http.HttpRequest)) \
.AndReturn(quota_usages)
project_details = self._get_project_info(project)
quota_data = self._get_quota_info(quota)
@ -746,7 +683,6 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
users = self._get_all_users(domain_id)
groups = self._get_all_groups(domain_id)
roles = self.roles.list()
quota_usages = self.quota_usages.first()
# init
api.keystone.get_default_domain(IsA(http.HttpRequest)) \
@ -764,9 +700,6 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
api.keystone.group_list(IsA(http.HttpRequest), domain=domain_id) \
.AndReturn(groups)
quotas.tenant_quota_usages(IsA(http.HttpRequest)) \
.AndReturn(quota_usages)
self.mox.ReplayAll()
workflow_data = self._get_workflow_data(project, quota)
@ -1101,7 +1034,7 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
group='3',
project=self.tenant.id)
quotas.tenant_quota_usages(IsA(http.HttpRequest)) \
quotas.tenant_quota_usages(IsA(http.HttpRequest), tenant_id=project.id) \
.AndReturn(quota_usages)
nova_updated_quota = dict([(key, updated_quota[key]) for key in
@ -1264,7 +1197,7 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
updated_quota = self._get_quota_info(quota)
# handle
quotas.tenant_quota_usages(IsA(http.HttpRequest)) \
quotas.tenant_quota_usages(IsA(http.HttpRequest), tenant_id=project.id) \
.AndReturn(quota_usages)
api.keystone.tenant_update(IsA(http.HttpRequest),
project.id,
@ -1438,7 +1371,7 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
group='3',
project=self.tenant.id)
quotas.tenant_quota_usages(IsA(http.HttpRequest)) \
quotas.tenant_quota_usages(IsA(http.HttpRequest), tenant_id=project.id) \
.AndReturn(quota_usages)
nova_updated_quota = dict([(key, updated_quota[key]) for key in
@ -1556,7 +1489,7 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
updated_quota = self._get_quota_info(quota)
# handle
quotas.tenant_quota_usages(IsA(http.HttpRequest)) \
quotas.tenant_quota_usages(IsA(http.HttpRequest), tenant_id=project.id) \
.AndReturn(quota_usages)
api.keystone.tenant_update(IsA(http.HttpRequest),
project.id,

View File

@ -150,14 +150,14 @@ class CreateProjectView(workflows.WorkflowView):
except Exception:
error_msg = _('Unable to retrieve default Neutron quota '
'values.')
self.add_error_to_step(error_msg, 'update_quotas')
self.add_error_to_step(error_msg, 'create_quotas')
for field in quotas.QUOTA_FIELDS:
initial[field] = quota_defaults.get(field).limit
except Exception:
error_msg = _('Unable to retrieve default quota values.')
self.add_error_to_step(error_msg, 'update_quotas')
self.add_error_to_step(error_msg, 'create_quotas')
return initial

View File

@ -40,7 +40,7 @@ PROJECT_USER_MEMBER_SLUG = "update_members"
PROJECT_GROUP_MEMBER_SLUG = "update_group_members"
class UpdateProjectQuotaAction(workflows.Action):
class ProjectQuotaAction(workflows.Action):
ifcb_label = _("Injected File Content (Bytes)")
metadata_items = forms.IntegerField(min_value=-1,
label=_("Metadata Items"))
@ -74,23 +74,21 @@ class UpdateProjectQuotaAction(workflows.Action):
subnet = forms.IntegerField(min_value=-1, label=_("Subnets"))
def __init__(self, request, *args, **kwargs):
super(UpdateProjectQuotaAction, self).__init__(request,
*args,
**kwargs)
super(ProjectQuotaAction, self).__init__(request,
*args,
**kwargs)
disabled_quotas = quotas.get_disabled_quotas(request)
for field in disabled_quotas:
if field in self.fields:
self.fields[field].required = False
self.fields[field].widget = forms.HiddenInput()
class Meta:
name = _("Quota")
slug = 'update_quotas'
help_text = _("Set maximum quotas for the project.")
class UpdateProjectQuotaAction(ProjectQuotaAction):
def clean(self):
cleaned_data = super(UpdateProjectQuotaAction, self).clean()
usages = quotas.tenant_quota_usages(self.request)
usages = quotas.tenant_quota_usages(
self.request, tenant_id=self.initial['project_id'])
# Validate the quota values before updating quotas.
bad_values = []
for key, value in cleaned_data.items():
@ -107,6 +105,18 @@ class UpdateProjectQuotaAction(workflows.Action):
raise forms.ValidationError(msg)
return cleaned_data
class Meta:
name = _("Quota")
slug = 'update_quotas'
help_text = _("Set maximum quotas for the project.")
class CreateProjectQuotaAction(ProjectQuotaAction):
class Meta:
name = _("Quota")
slug = 'create_quotas'
help_text = _("Set maximum quotas for the project.")
class UpdateProjectQuota(workflows.Step):
action_class = UpdateProjectQuotaAction
@ -114,6 +124,12 @@ class UpdateProjectQuota(workflows.Step):
contributes = quotas.QUOTA_FIELDS
class CreateProjectQuota(workflows.Step):
action_class = CreateProjectQuotaAction
depends_on = ("project_id",)
contributes = quotas.QUOTA_FIELDS
class CreateProjectInfoAction(workflows.Action):
# Hide the domain_id and domain_name by default
domain_id = forms.CharField(label=_("Domain ID"),
@ -362,7 +378,7 @@ class CreateProject(workflows.Workflow):
success_url = "horizon:identity:projects:index"
default_steps = (CreateProjectInfo,
UpdateProjectMembers,
UpdateProjectQuota)
CreateProjectQuota)
def __init__(self, request=None, context_seed=None, entry_point=None,
*args, **kwargs):
@ -370,7 +386,7 @@ class CreateProject(workflows.Workflow):
self.default_steps = (CreateProjectInfo,
UpdateProjectMembers,
UpdateProjectGroups,
UpdateProjectQuota)
CreateProjectQuota)
super(CreateProject, self).__init__(request=request,
context_seed=context_seed,
entry_point=entry_point,

View File

@ -245,13 +245,15 @@ def get_disabled_quotas(request):
@memoized
def tenant_quota_usages(request):
# Get our quotas and construct our usage object.
def tenant_quota_usages(request, tenant_id=None):
"""Get our quotas and construct our usage object."""
disabled_quotas = get_disabled_quotas(request)
usages = QuotaUsage()
for quota in get_tenant_quota_data(request,
disabled_quotas=disabled_quotas):
disabled_quotas=disabled_quotas,
tenant_id=tenant_id):
usages.add_quota(quota)
# Get our usages.
@ -262,7 +264,13 @@ def tenant_quota_usages(request):
except Exception:
pass
flavors = dict([(f.id, f) for f in nova.flavor_list(request)])
instances, has_more = nova.server_list(request)
if tenant_id:
instances, has_more = nova.server_list(
request, search_opts={'tenant_id': tenant_id}, all_tenants=True)
else:
instances, has_more = nova.server_list(request)
# Fetch deleted flavors if necessary.
missing_flavors = [instance.flavor['id'] for instance in instances
if instance.flavor['id'] not in flavors]
@ -285,16 +293,25 @@ def tenant_quota_usages(request):
if 'network' not in disabled_quotas:
networks = []
networks = neutron.network_list(request, shared=False)
if tenant_id:
networks = filter(lambda net: net.tenant_id == tenant_id, networks)
usages.tally('networks', len(networks))
if 'router' not in disabled_quotas:
routers = []
routers = neutron.router_list(request)
if tenant_id:
routers = filter(lambda rou: rou.tenant_id == tenant_id, routers)
usages.tally('routers', len(routers))
if 'volumes' not in disabled_quotas:
volumes = cinder.volume_list(request)
snapshots = cinder.volume_snapshot_list(request)
if tenant_id:
opts = {'alltenants': 1, 'tenant_id': tenant_id}
volumes = cinder.volume_list(request, opts)
snapshots = cinder.volume_snapshot_list(request, opts)
else:
volumes = cinder.volume_list(request)
snapshots = cinder.volume_snapshot_list(request)
usages.tally('gigabytes', sum([int(v.size) for v in volumes]))
usages.tally('volumes', len(volumes))
usages.tally('snapshots', len(snapshots))