project: Split quota update into a separate workflow
This commit converts the quota update workflow tab into a separate workflow. After this change, admin cannot set a project's quotas in the project creation workflow, but the admin can update its quotas just after creating a project, so I think it is not a problem. Because of splitting quota update from a project create/update workflow, our unit tests has been simplified significantly. Previously we needed tests for **combination** of create/update project and quota update, but we now need only tests for create/update project and quota update separately. Part of blueprint horizon-plugin-tab-for-info-and-quotas Change-Id: I7b95428e89ddc1c7a85a1162db29cef9a9674129
This commit is contained in:
parent
711d6f01ae
commit
a257b52b85
|
@ -143,7 +143,7 @@ class UpdateProject(policy.PolicyTargetMixin, tables.LinkAction):
|
|||
class ModifyQuotas(tables.LinkAction):
|
||||
name = "quotas"
|
||||
verbose_name = _("Modify Quotas")
|
||||
url = "horizon:identity:projects:update"
|
||||
url = "horizon:identity:projects:update_quotas"
|
||||
classes = ("ajax-modal",)
|
||||
icon = "pencil"
|
||||
policy_rules = (('compute', "os_compute_api:os-quota-sets:update"),)
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -30,4 +30,6 @@ urlpatterns = [
|
|||
views.ProjectUsageView.as_view(), name='usage'),
|
||||
url(r'^(?P<project_id>[^/]+)/detail/$',
|
||||
views.DetailProjectView.as_view(), name='detail'),
|
||||
url(r'^(?P<tenant_id>[^/]+)/update_quotas/$',
|
||||
views.UpdateQuotasView.as_view(), name='update_quotas'),
|
||||
]
|
||||
|
|
|
@ -154,11 +154,6 @@ class CreateProjectView(workflows.WorkflowView):
|
|||
workflow_class = project_workflows.CreateProject
|
||||
|
||||
def get_initial(self):
|
||||
|
||||
if (api.keystone.is_multi_domain_enabled() and
|
||||
not api.keystone.is_cloud_admin(self.request)):
|
||||
self.workflow_class = project_workflows.CreateProjectNoQuota
|
||||
|
||||
initial = super(CreateProjectView, self).get_initial()
|
||||
|
||||
# Set the domain of the project
|
||||
|
@ -166,16 +161,6 @@ class CreateProjectView(workflows.WorkflowView):
|
|||
initial["domain_id"] = domain.id
|
||||
initial["domain_name"] = domain.name
|
||||
|
||||
# get initial quota defaults
|
||||
if api.keystone.is_cloud_admin(self.request):
|
||||
try:
|
||||
quota_defaults = quotas.get_default_quota_data(self.request)
|
||||
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, 'create_quotas')
|
||||
|
||||
return initial
|
||||
|
||||
|
||||
|
@ -183,11 +168,6 @@ class UpdateProjectView(workflows.WorkflowView):
|
|||
workflow_class = project_workflows.UpdateProject
|
||||
|
||||
def get_initial(self):
|
||||
|
||||
if (api.keystone.is_multi_domain_enabled() and
|
||||
not api.keystone.is_cloud_admin(self.request)):
|
||||
self.workflow_class = project_workflows.UpdateProjectNoQuota
|
||||
|
||||
initial = super(UpdateProjectView, self).get_initial()
|
||||
|
||||
project_id = self.kwargs['tenant_id']
|
||||
|
@ -222,7 +202,21 @@ class UpdateProjectView(workflows.WorkflowView):
|
|||
exceptions.handle(self.request,
|
||||
_('Unable to retrieve project domain.'),
|
||||
redirect=reverse(INDEX_URL))
|
||||
except Exception:
|
||||
exceptions.handle(self.request,
|
||||
_('Unable to retrieve project details.'),
|
||||
redirect=reverse(INDEX_URL))
|
||||
return initial
|
||||
|
||||
|
||||
class UpdateQuotasView(workflows.WorkflowView):
|
||||
workflow_class = project_workflows.UpdateQuota
|
||||
|
||||
def get_initial(self):
|
||||
initial = super(UpdateQuotasView, self).get_initial()
|
||||
project_id = self.kwargs['tenant_id']
|
||||
initial['project_id'] = project_id
|
||||
try:
|
||||
# get initial project quota
|
||||
if keystone.is_cloud_admin(self.request):
|
||||
quota_data = quotas.get_tenant_quota_data(self.request,
|
||||
|
@ -231,7 +225,7 @@ class UpdateProjectView(workflows.WorkflowView):
|
|||
initial[field] = quota_data.get(field).limit
|
||||
except Exception:
|
||||
exceptions.handle(self.request,
|
||||
_('Unable to retrieve project details.'),
|
||||
_('Unable to retrieve project quotas.'),
|
||||
redirect=reverse(INDEX_URL))
|
||||
return initial
|
||||
|
||||
|
|
|
@ -49,19 +49,19 @@ COMMON_HORIZONTAL_TEMPLATE = "identity/projects/_common_horizontal_form.html"
|
|||
|
||||
|
||||
class ProjectQuotaAction(workflows.Action):
|
||||
ifcb_label = _("Injected File Content (Bytes)")
|
||||
ifpb_label = _("Length of Injected File Path")
|
||||
metadata_items = forms.IntegerField(min_value=-1,
|
||||
label=_("Metadata Items"))
|
||||
cores = forms.IntegerField(min_value=-1, label=_("VCPUs"))
|
||||
instances = forms.IntegerField(min_value=-1, label=_("Instances"))
|
||||
injected_files = forms.IntegerField(min_value=-1,
|
||||
label=_("Injected Files"))
|
||||
injected_file_content_bytes = forms.IntegerField(min_value=-1,
|
||||
label=ifcb_label)
|
||||
injected_file_content_bytes = forms.IntegerField(
|
||||
min_value=-1,
|
||||
label=_("Injected File Content (Bytes)"))
|
||||
key_pairs = forms.IntegerField(min_value=-1, label=_("Key Pairs"))
|
||||
injected_file_path_bytes = forms.IntegerField(min_value=-1,
|
||||
label=ifpb_label)
|
||||
injected_file_path_bytes = forms.IntegerField(
|
||||
min_value=-1,
|
||||
label=_("Length of Injected File Path"))
|
||||
volumes = forms.IntegerField(min_value=-1, label=_("Volumes"))
|
||||
snapshots = forms.IntegerField(min_value=-1, label=_("Volume Snapshots"))
|
||||
gigabytes = forms.IntegerField(
|
||||
|
@ -89,10 +89,8 @@ class ProjectQuotaAction(workflows.Action):
|
|||
self.fields[field].required = False
|
||||
self.fields[field].widget = forms.HiddenInput()
|
||||
|
||||
|
||||
class UpdateProjectQuotaAction(ProjectQuotaAction):
|
||||
def clean(self):
|
||||
cleaned_data = super(UpdateProjectQuotaAction, self).clean()
|
||||
cleaned_data = super(ProjectQuotaAction, self).clean()
|
||||
usages = quotas.tenant_quota_usages(
|
||||
self.request, tenant_id=self.initial['project_id'])
|
||||
# Validate the quota values before updating quotas.
|
||||
|
@ -118,23 +116,8 @@ class UpdateProjectQuotaAction(ProjectQuotaAction):
|
|||
permissions = ('openstack.roles.admin', 'openstack.services.compute')
|
||||
|
||||
|
||||
class CreateProjectQuotaAction(ProjectQuotaAction):
|
||||
class Meta(object):
|
||||
name = _("Quotas")
|
||||
slug = 'create_quotas'
|
||||
help_text = _("Set maximum quotas for the project.")
|
||||
permissions = ('openstack.roles.admin', 'openstack.services.compute')
|
||||
|
||||
|
||||
class UpdateProjectQuota(workflows.Step):
|
||||
action_class = UpdateProjectQuotaAction
|
||||
template_name = COMMON_HORIZONTAL_TEMPLATE
|
||||
depends_on = ("project_id",)
|
||||
contributes = quotas.QUOTA_FIELDS
|
||||
|
||||
|
||||
class CreateProjectQuota(workflows.Step):
|
||||
action_class = CreateProjectQuotaAction
|
||||
action_class = ProjectQuotaAction
|
||||
template_name = COMMON_HORIZONTAL_TEMPLATE
|
||||
depends_on = ("project_id",)
|
||||
contributes = quotas.QUOTA_FIELDS
|
||||
|
@ -398,33 +381,7 @@ class UpdateProjectGroups(workflows.UpdateMembersStep):
|
|||
return context
|
||||
|
||||
|
||||
class CommonQuotaWorkflow(workflows.Workflow):
|
||||
def _update_project_quota(self, request, data, project_id):
|
||||
disabled_quotas = quotas.get_disabled_quotas(request)
|
||||
|
||||
# Update the project quotas.
|
||||
if api.base.is_service_enabled(request, 'compute'):
|
||||
nova_data = {key: data[key] for key in
|
||||
quotas.NOVA_QUOTA_FIELDS - disabled_quotas}
|
||||
if nova_data:
|
||||
nova.tenant_quota_update(request, project_id, **nova_data)
|
||||
|
||||
if cinder.is_volume_service_enabled(request):
|
||||
cinder_data = {key: data[key] for key in
|
||||
quotas.CINDER_QUOTA_FIELDS - disabled_quotas}
|
||||
if cinder_data:
|
||||
cinder.tenant_quota_update(request, project_id, **cinder_data)
|
||||
|
||||
if (api.base.is_service_enabled(request, 'network') and
|
||||
api.neutron.is_quotas_extension_supported(request)):
|
||||
neutron_data = {key: data[key] for key in
|
||||
quotas.NEUTRON_QUOTA_FIELDS - disabled_quotas}
|
||||
if neutron_data:
|
||||
api.neutron.tenant_quota_update(request, project_id,
|
||||
**neutron_data)
|
||||
|
||||
|
||||
class CreateProject(CommonQuotaWorkflow):
|
||||
class CreateProject(workflows.Workflow):
|
||||
slug = "create_project"
|
||||
name = _("Create Project")
|
||||
finalize_button_name = _("Create Project")
|
||||
|
@ -432,16 +389,14 @@ class CreateProject(CommonQuotaWorkflow):
|
|||
failure_message = _('Unable to create project "%s".')
|
||||
success_url = "horizon:identity:projects:index"
|
||||
default_steps = (CreateProjectInfo,
|
||||
UpdateProjectMembers,
|
||||
CreateProjectQuota)
|
||||
UpdateProjectMembers)
|
||||
|
||||
def __init__(self, request=None, context_seed=None, entry_point=None,
|
||||
*args, **kwargs):
|
||||
if PROJECT_GROUP_ENABLED:
|
||||
self.default_steps = (CreateProjectInfo,
|
||||
UpdateProjectMembers,
|
||||
UpdateProjectGroups,
|
||||
CreateProjectQuota)
|
||||
UpdateProjectGroups)
|
||||
super(CreateProject, self).__init__(request=request,
|
||||
context_seed=context_seed,
|
||||
entry_point=entry_point,
|
||||
|
@ -545,13 +500,6 @@ class CreateProject(CommonQuotaWorkflow):
|
|||
'and update project quotas.')
|
||||
% groups_to_add)
|
||||
|
||||
def _update_project_quota(self, request, data, project_id):
|
||||
try:
|
||||
super(CreateProject, self)._update_project_quota(
|
||||
request, data, project_id)
|
||||
except Exception:
|
||||
exceptions.handle(request, _('Unable to set project quotas.'))
|
||||
|
||||
def handle(self, request, data):
|
||||
project = self._create_project(request, data)
|
||||
if not project:
|
||||
|
@ -560,33 +508,9 @@ class CreateProject(CommonQuotaWorkflow):
|
|||
self._update_project_members(request, data, project_id)
|
||||
if PROJECT_GROUP_ENABLED:
|
||||
self._update_project_groups(request, data, project_id)
|
||||
if keystone.is_cloud_admin(request):
|
||||
self._update_project_quota(request, data, project_id)
|
||||
return True
|
||||
|
||||
|
||||
class CreateProjectNoQuota(CreateProject):
|
||||
slug = "create_project"
|
||||
name = _("Create Project")
|
||||
finalize_button_name = _("Create Project")
|
||||
success_message = _('Created new project "%s".')
|
||||
failure_message = _('Unable to create project "%s".')
|
||||
success_url = "horizon:identity:projects:index"
|
||||
default_steps = (CreateProjectInfo, UpdateProjectMembers)
|
||||
|
||||
def __init__(self, request=None, context_seed=None, entry_point=None,
|
||||
*args, **kwargs):
|
||||
if PROJECT_GROUP_ENABLED:
|
||||
self.default_steps = (CreateProjectInfo,
|
||||
UpdateProjectMembers,
|
||||
UpdateProjectGroups,)
|
||||
super(CreateProject, self).__init__(request=request,
|
||||
context_seed=context_seed,
|
||||
entry_point=entry_point,
|
||||
*args,
|
||||
**kwargs)
|
||||
|
||||
|
||||
class UpdateProjectInfoAction(CreateProjectInfoAction):
|
||||
enabled = forms.BooleanField(required=False, label=_("Enabled"))
|
||||
domain_name = forms.CharField(label=_("Domain Name"),
|
||||
|
@ -636,7 +560,7 @@ class UpdateProjectInfo(workflows.Step):
|
|||
self.contributes += tuple(EXTRA_INFO.keys())
|
||||
|
||||
|
||||
class UpdateProject(CommonQuotaWorkflow):
|
||||
class UpdateProject(workflows.Workflow):
|
||||
slug = "update_project"
|
||||
name = _("Edit Project")
|
||||
finalize_button_name = _("Save")
|
||||
|
@ -644,16 +568,14 @@ class UpdateProject(CommonQuotaWorkflow):
|
|||
failure_message = _('Unable to modify project "%s".')
|
||||
success_url = "horizon:identity:projects:index"
|
||||
default_steps = (UpdateProjectInfo,
|
||||
UpdateProjectMembers,
|
||||
UpdateProjectQuota)
|
||||
UpdateProjectMembers)
|
||||
|
||||
def __init__(self, request=None, context_seed=None, entry_point=None,
|
||||
*args, **kwargs):
|
||||
if PROJECT_GROUP_ENABLED:
|
||||
self.default_steps = (UpdateProjectInfo,
|
||||
UpdateProjectMembers,
|
||||
UpdateProjectGroups,
|
||||
UpdateProjectQuota)
|
||||
UpdateProjectGroups)
|
||||
|
||||
super(UpdateProject, self).__init__(request=request,
|
||||
context_seed=context_seed,
|
||||
|
@ -911,17 +833,6 @@ class UpdateProject(CommonQuotaWorkflow):
|
|||
% groups_to_modify)
|
||||
return False
|
||||
|
||||
def _update_project_quota(self, request, data, project_id):
|
||||
try:
|
||||
super(UpdateProject, self)._update_project_quota(
|
||||
request, data, project_id)
|
||||
return True
|
||||
except Exception:
|
||||
exceptions.handle(request, _('Modified project information and '
|
||||
'members, but unable to modify '
|
||||
'project quotas.'))
|
||||
return False
|
||||
|
||||
def handle(self, request, data):
|
||||
# FIXME(gabriel): This should be refactored to use Python's built-in
|
||||
# sets and do this all in a single "roles to add" and "roles to remove"
|
||||
|
@ -945,32 +856,57 @@ class UpdateProject(CommonQuotaWorkflow):
|
|||
if not ret:
|
||||
return False
|
||||
|
||||
if api.keystone.is_cloud_admin(request):
|
||||
ret = self._update_project_quota(request, data, project_id)
|
||||
if not ret:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
class UpdateProjectNoQuota(UpdateProject):
|
||||
slug = "update_project"
|
||||
name = _("Edit Project")
|
||||
class UpdateQuota(workflows.Workflow):
|
||||
slug = "update_quotas"
|
||||
name = _("Edit Quotas")
|
||||
finalize_button_name = _("Save")
|
||||
success_message = _('Modified project "%s".')
|
||||
failure_message = _('Unable to modify project "%s".')
|
||||
success_message = _('Modified quotas of project "%s".')
|
||||
failure_message = _('Unable to modify quotas of project "%s".')
|
||||
success_url = "horizon:identity:projects:index"
|
||||
default_steps = (UpdateProjectInfo, UpdateProjectMembers)
|
||||
default_steps = (UpdateProjectQuota,)
|
||||
|
||||
def __init__(self, request=None, context_seed=None, entry_point=None,
|
||||
*args, **kwargs):
|
||||
if PROJECT_GROUP_ENABLED:
|
||||
self.default_steps = (UpdateProjectInfo,
|
||||
UpdateProjectMembers,
|
||||
UpdateProjectGroups)
|
||||
def format_status_message(self, message):
|
||||
if "%s" in message:
|
||||
return message % self.context.get('name', 'unknown project')
|
||||
else:
|
||||
return message
|
||||
|
||||
super(UpdateProject, self).__init__(request=request,
|
||||
context_seed=context_seed,
|
||||
entry_point=entry_point,
|
||||
*args,
|
||||
**kwargs)
|
||||
def _update_project_quota(self, request, data, project_id):
|
||||
disabled_quotas = quotas.get_disabled_quotas(request)
|
||||
|
||||
if api.base.is_service_enabled(request, 'compute'):
|
||||
nova_data = {key: data[key] for key in
|
||||
quotas.NOVA_QUOTA_FIELDS - disabled_quotas}
|
||||
if nova_data:
|
||||
nova.tenant_quota_update(request, project_id, **nova_data)
|
||||
|
||||
if cinder.is_volume_service_enabled(request):
|
||||
cinder_data = {key: data[key] for key in
|
||||
quotas.CINDER_QUOTA_FIELDS - disabled_quotas}
|
||||
if cinder_data:
|
||||
cinder.tenant_quota_update(request, project_id, **cinder_data)
|
||||
|
||||
if (api.base.is_service_enabled(request, 'network') and
|
||||
api.neutron.is_quotas_extension_supported(request)):
|
||||
neutron_data = {key: data[key] for key in
|
||||
quotas.NEUTRON_QUOTA_FIELDS - disabled_quotas}
|
||||
if neutron_data:
|
||||
api.neutron.tenant_quota_update(request, project_id,
|
||||
**neutron_data)
|
||||
|
||||
def handle(self, request, data):
|
||||
project_id = data['project_id']
|
||||
if not api.keystone.is_cloud_admin(request):
|
||||
return True
|
||||
try:
|
||||
self._update_project_quota(request, data, project_id)
|
||||
return True
|
||||
except Exception:
|
||||
exceptions.handle(request,
|
||||
_('Modified project information and '
|
||||
'members, but unable to modify '
|
||||
'project quotas.'))
|
||||
return False
|
||||
|
|
Loading…
Reference in New Issue