Skip to content

Commit

Permalink
Pass correct project ID to get tenant_usages
Browse files Browse the repository at this point in the history
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
  • Loading branch information
Tehsmash committed Oct 17, 2014
1 parent d471bae commit 193d40a
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 91 deletions.
77 changes: 5 additions & 72 deletions openstack_dashboard/dashboards/identity/projects/tests.py
Expand Up @@ -217,7 +217,7 @@ def test_add_project_get(self):
['<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")
Expand Down Expand Up @@ -305,7 +305,6 @@ def test_add_project_post(self, neutron=False):
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)) \
Expand All @@ -325,8 +324,6 @@ def test_add_project_post(self, neutron=False):
.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)

Expand Down Expand Up @@ -459,7 +456,6 @@ def test_add_project_tenant_create_error(self):
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)) \
Expand All @@ -478,8 +474,6 @@ def test_add_project_tenant_create_error(self):
.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) \
Expand All @@ -501,57 +495,6 @@ def test_add_project_tenant_create_error_domain(self):
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',
Expand All @@ -572,7 +515,6 @@ def test_add_project_quota_update_error(self):
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)) \
Expand All @@ -591,8 +533,6 @@ def test_add_project_quota_update_error(self):
.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)

Expand Down Expand Up @@ -661,7 +601,6 @@ def test_add_project_user_update_error(self):
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)) \
Expand All @@ -680,8 +619,6 @@ def test_add_project_user_update_error(self):
.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)

Expand Down Expand Up @@ -746,7 +683,6 @@ def test_add_project_missing_field_error(self):
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)) \
Expand All @@ -764,9 +700,6 @@ def test_add_project_missing_field_error(self):
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)
Expand Down Expand Up @@ -1101,7 +1034,7 @@ def test_update_project_save(self, neutron=False):
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
Expand Down Expand Up @@ -1264,7 +1197,7 @@ def test_update_project_tenant_update_error(self):
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,
Expand Down Expand Up @@ -1438,7 +1371,7 @@ def test_update_project_quota_update_error(self):
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
Expand Down Expand Up @@ -1556,7 +1489,7 @@ def test_update_project_member_update_error(self):
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,
Expand Down
4 changes: 2 additions & 2 deletions openstack_dashboard/dashboards/identity/projects/views.py
Expand Up @@ -150,14 +150,14 @@ def get_initial(self):
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

Expand Down
38 changes: 27 additions & 11 deletions openstack_dashboard/dashboards/identity/projects/workflows.py
Expand Up @@ -40,7 +40,7 @@
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"))
Expand Down Expand Up @@ -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():
Expand All @@ -107,13 +105,31 @@ def clean(self):
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
depends_on = ("project_id",)
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"),
Expand Down Expand Up @@ -362,15 +378,15 @@ 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):
if PROJECT_GROUP_ENABLED:
self.default_steps = (CreateProjectInfo,
UpdateProjectMembers,
UpdateProjectGroups,
UpdateProjectQuota)
CreateProjectQuota)
super(CreateProject, self).__init__(request=request,
context_seed=context_seed,
entry_point=entry_point,
Expand Down
29 changes: 23 additions & 6 deletions openstack_dashboard/usage/quotas.py
Expand Up @@ -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.
Expand All @@ -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]
Expand All @@ -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))
Expand Down

0 comments on commit 193d40a

Please sign in to comment.