Skip to content

Commit 9a3b521

Browse files
committed
Allow validation of Openshift allocations when changing quotas
In combination with changes to `coldfront-plugin-cloud` [1], a Coldfront patch is added to allow validation of Openshift allocation change requests (arc). Currently only one scenario is tested: If the acr requests for quotas below the current usage of the project, the user will be sent back to the change request form with an informative error message. This feature is possible by leveraging Django formsets and their built-in custom validation feature [2] [1] nerc-project/coldfront-plugin-cloud#208 [2] https://docs.djangoproject.com/en/5.1/topics/forms/formsets/#custom-formset-validation
1 parent a224f1a commit 9a3b521

2 files changed

Lines changed: 86 additions & 0 deletions

File tree

Dockerfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,14 @@ COPY patches/01_add_api_urls.patch /opt/venv/lib/python3.12/site-packages/
2626
COPY patches/02_fix_allocation_denied_revoked_PR596.patch /opt/venv/lib/python3.12/site-packages/
2727
COPY patches/03_add_active_needs_renewal_status.patch /opt/venv/lib/python3.12/site-packages/
2828
COPY patches/04_add_allocation_change_request_created_signal.patch /opt/venv/lib/python3.12/site-packages/
29+
COPY patches/05_validate_openshift_quota_change_request.patch /opt/venv/lib/python3.12/site-packages/
2930

3031
RUN cd /opt/venv/lib/python3.12/site-packages && \
3132
patch -p1 < 01_add_api_urls.patch && \
3233
patch -p1 < 02_fix_allocation_denied_revoked_PR596.patch && \
3334
patch -p1 < 03_add_active_needs_renewal_status.patch && \
3435
patch -p1 < 04_add_allocation_change_request_created_signal.patch
36+
patch -p1 < 05_validate_openshift_quota_change_request.patch
3537

3638
# Final Image
3739
FROM python:3.12-slim-bullseye
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
diff --git a/coldfront/core/allocation/forms.py b/coldfront/core/allocation/forms.py
2+
index 9700244..e893e8f 100644
3+
--- a/coldfront/core/allocation/forms.py
4+
+++ b/coldfront/core/allocation/forms.py
5+
@@ -1,5 +1,6 @@
6+
from django import forms
7+
from django.db.models.functions import Lower
8+
+from django.core.exceptions import ValidationError
9+
from django.shortcuts import get_object_or_404
10+
11+
from coldfront.core.allocation.models import (Allocation, AllocationAccount,
12+
@@ -11,6 +12,8 @@ from coldfront.core.project.models import Project
13+
from coldfront.core.resource.models import Resource, ResourceType
14+
from coldfront.core.utils.common import import_from_settings
15+
16+
+from coldfront_plugin_cloud import tasks, utils
17+
+
18+
ALLOCATION_ACCOUNT_ENABLED = import_from_settings(
19+
'ALLOCATION_ACCOUNT_ENABLED', False)
20+
ALLOCATION_CHANGE_REQUEST_EXTENSION_DAYS = import_from_settings(
21+
@@ -201,6 +204,33 @@ class AllocationAttributeChangeForm(forms.Form):
22+
allocation_attribute.clean()
23+
24+
25+
+class AllocationAttributeChangeFormSet(forms.BaseFormSet):
26+
+ def __init__(self, *args, **kwargs):
27+
+ super().__init__(*args, **kwargs)
28+
+
29+
+ def _validate_openshift_quotas(self):
30+
+ """
31+
+ Performs validation on the OpenShift quotas.
32+
+ For now, only check that current usage is lower than requested quota.
33+
+ """
34+
+ allocation_attribute = AllocationAttribute.objects.get(pk=self.cleaned_data[0].get('pk'))
35+
+ allocation = allocation_attribute.allocation
36+
+ if allocator := tasks.find_allocator(allocation):
37+
+ # Only support Openshift allocations for now
38+
+ if allocator.resource_type == 'openshift':
39+
+ requested_quota = utils.get_new_cloud_quota(self.cleaned_data)
40+
+ cloud_quota_usage = tasks.get_allocation_cloud_usage(allocation.pk)
41+
+ usage_errors = utils.check_cloud_usage_is_lower(requested_quota, cloud_quota_usage)
42+
+
43+
+ if usage_errors:
44+
+ raise ValidationError(usage_errors)
45+
+
46+
+ def clean(self):
47+
+ if any(self.errors):
48+
+ return
49+
+ self._validate_openshift_quotas()
50+
+
51+
+
52+
class AllocationAttributeUpdateForm(forms.Form):
53+
change_pk = forms.IntegerField(required=False, disabled=True)
54+
attribute_pk = forms.IntegerField(required=False, disabled=True)
55+
diff --git a/coldfront/core/allocation/views.py b/coldfront/core/allocation/views.py
56+
index 90f04b8..f5296fe 100644
57+
--- a/coldfront/core/allocation/views.py
58+
+++ b/coldfront/core/allocation/views.py
59+
@@ -28,6 +28,7 @@ from coldfront.core.allocation.forms import (AllocationAccountForm,
60+
AllocationChangeForm,
61+
AllocationChangeNoteForm,
62+
AllocationAttributeChangeForm,
63+
+ AllocationAttributeChangeFormSet,
64+
AllocationAttributeUpdateForm,
65+
AllocationForm,
66+
AllocationInvoiceNoteDeleteForm,
67+
@@ -1786,7 +1787,7 @@ class AllocationChangeView(LoginRequiredMixin, UserPassesTestMixin, FormView):
68+
69+
if allocation_attributes_to_change:
70+
formset = formset_factory(self.formset_class, max_num=len(
71+
- allocation_attributes_to_change))
72+
+ allocation_attributes_to_change), formset=AllocationAttributeChangeFormSet)
73+
formset = formset(
74+
request.POST, initial=allocation_attributes_to_change, prefix='attributeform')
75+
76+
@@ -1796,6 +1797,8 @@ class AllocationChangeView(LoginRequiredMixin, UserPassesTestMixin, FormView):
77+
messages.error(request, error)
78+
for error in formset.errors:
79+
if error: attribute_errors += error.get('__all__')
80+
+ for error in formset.non_form_errors():
81+
+ messages.error(request, error)
82+
messages.error(request, attribute_errors)
83+
return HttpResponseRedirect(reverse('allocation-change', kwargs={'pk': pk}))
84+
form_data = form.cleaned_data

0 commit comments

Comments
 (0)