aboutsummaryrefslogtreecommitdiffstats
path: root/src/booking
diff options
context:
space:
mode:
authorSawyer Bergeron <sbergeron@iol.unh.edu>2021-10-15 17:03:58 -0400
committerSawyer Bergeron <sbergeron@iol.unh.edu>2021-10-28 15:32:26 -0400
commit104470ee008817df4a3b78d24e524e8c92eac9a0 (patch)
treea4a6b70043f34c37b568bbe6a4b6e72d8c619716 /src/booking
parentafcee3cad5c091e78e909b83f8df49accf1af5b6 (diff)
WIP merge of master into cobblercobbler
Squashed commit of the following: commit 35b9f39178cc502a5283a1b37a65f7dd0838ae05 Author: Sawyer Bergeron <sbergeron@iol.unh.edu> Date: Fri Oct 15 14:14:46 2021 -0400 Merge User Booking API Rev 1 (Try 3) Signed-off-by: Sawyer Bergeron <sbergeron@iol.unh.edu> Change-Id: Ie1eee0a59929f8da39f16bb6bc17ae3de4f1cba9 Signed-off-by: Sawyer Bergeron <sbergeron@iol.unh.edu> commit b3ed8ebcf536c021330e7ccbc0376f6b89189348 Author: Jacob Hodgdon <jhodgdon@iol.unh.edu> Date: Tue Jul 20 09:55:04 2021 -0400 Additional changes for rebrand Signed-off-by: Jacob Hodgdon <jhodgdon@iol.unh.edu> Change-Id: Ibd525f966b3ed3aebdbe688e5ee5daa4ea1cf294 Signed-off-by: Jacob Hodgdon <jhodgdon@iol.unh.edu> Signed-off-by: Raven Hodgdon <jhodgdon@iol.unh.edu> commit 17b6c7809771ac8bf3879add033d2169f671591a Author: Jacob Hodgdon <jhodgdon@iol.unh.edu> Date: Fri May 14 15:42:56 2021 -0400 Color fixes for rebrand Signed-off-by: Jacob Hodgdon <jhodgdon@iol.unh.edu> Change-Id: I5cf4ede598afa377db7ecec17d8dfef085e130ac Change-Id: I55494d24bcef74def85236b09168508e428d368e Signed-off-by: Sawyer Bergeron <sbergeron@iol.unh.edu>
Diffstat (limited to 'src/booking')
-rw-r--r--src/booking/quick_deployer.py120
-rw-r--r--src/booking/stats.py2
-rw-r--r--src/booking/urls.py2
-rw-r--r--src/booking/views.py1
4 files changed, 73 insertions, 52 deletions
diff --git a/src/booking/quick_deployer.py b/src/booking/quick_deployer.py
index 2eb9fba..944d161 100644
--- a/src/booking/quick_deployer.py
+++ b/src/booking/quick_deployer.py
@@ -11,10 +11,11 @@
import json
import yaml
from django.db.models import Q
+from django.db import transaction
from datetime import timedelta
from django.utils import timezone
from django.core.exceptions import ValidationError
-from account.models import Lab
+from account.models import Lab, UserProfile
from resource_inventory.models import (
ResourceTemplate,
@@ -114,7 +115,7 @@ def update_template(old_template, image, hostname, user, global_cloud_config=Non
image=image_to_set,
template=template,
is_head_node=old_config.is_head_node,
- name=hostname if len(old_template.getConfigs()) == 1 else old_config.name,
+ name=hostname if (hostname and len(old_template.getConfigs()) == 1) else old_config.name,
#cloud_init_files=old_config.cloud_init_files.set()
)
@@ -177,13 +178,21 @@ def generate_resource_bundle(template):
return resource_bundle
-def check_invariants(request, **kwargs):
+def check_invariants(**kwargs):
# TODO: This should really happen in the BookingForm validation methods
installer = kwargs['installer']
image = kwargs['image']
scenario = kwargs['scenario']
lab = kwargs['lab']
length = kwargs['length']
+
+ user = kwargs['owner']
+
+ if not user.userprofile:
+ raise ValidationError("The given owner did not have a userprofile, owner should be an enrolled user")
+ if not user.userprofile.ssh_public_key:
+ raise ValidationError("User has no uploaded ssh key. User should upload a key in settings")
+
# check that image os is compatible with installer
if image:
if image.from_lab != lab:
@@ -191,7 +200,7 @@ def check_invariants(request, **kwargs):
# TODO
# if image.host_type != host_profile:
# raise ValidationError("The chosen image is not available for the chosen host type")
- if not image.public and image.owner != request.user:
+ if not image.public and image.owner != kwargs['owner']:
raise ValidationError("You are not the owner of the chosen private image")
if length < 1 or length > 21:
raise BookingLengthException("Booking must be between 1 and 21 days long")
@@ -206,76 +215,89 @@ def generate_cloud_configs(resource_bundle, global_cloud_config):
def create_from_form(form, request):
"""
- Create a Booking from the user's form.
-
- Large, nasty method to create a booking or return a useful error
- based on the form from the frontend
+ Parse data from QuickBookingForm to create booking
"""
resource_field = form.cleaned_data['filter_field']
- purpose_field = form.cleaned_data['purpose']
- project_field = form.cleaned_data['project']
- users_field = form.cleaned_data['users']
- hostname = 'opnfv_host' if not form.cleaned_data['hostname'] else form.cleaned_data['hostname']
- length = form.cleaned_data['length']
- global_cloud_config = None if not form.cleaned_data['global_cloud_config'] else form.cleaned_data['global_cloud_config']
-
- if global_cloud_config:
- try:
- d = yaml.load(global_cloud_config)
- if not (type(d) is dict):
- raise Exception("CI file was valid yaml but was not a dict")
- except Exception as e:
- raise ValidationError("The provided Cloud Config is not valid yaml, please refer to the Cloud Init documentation for expected structure")
- print("about to create global cloud config")
- global_cloud_config = CloudInitFile.create(text=global_cloud_config, priority=CloudInitFile.objects.count())
- print("made global cloud config")
-
- image = form.cleaned_data['image']
- scenario = form.cleaned_data['scenario']
- installer = form.cleaned_data['installer']
lab, resource_template = parse_resource_field(resource_field)
data = form.cleaned_data
data['lab'] = lab
data['resource_template'] = resource_template
- check_invariants(request, **data)
+ data['owner'] = request.user
+
+ return _create_booking(data)
+
+
+def create_from_API(body, user):
+ """
+ Parse data from Automation API to create booking
+ """
+ booking_info = json.loads(body.decode('utf-8'))
+
+ data = {}
+ data['purpose'] = booking_info['purpose']
+ data['project'] = booking_info['project']
+ data['users'] = [UserProfile.objects.get(user__username=username)
+ for username in booking_info['collaborators']]
+ data['hostname'] = booking_info['hostname']
+ data['length'] = booking_info['length']
+ data['installer'] = None
+ data['scenario'] = None
+
+ data['image'] = Image.objects.get(pk=booking_info['imageLabID'])
+
+ data['resource_template'] = ResourceTemplate.objects.get(pk=booking_info['templateID'])
+ data['lab'] = data['resource_template'].lab
+ data['owner'] = user
+
+ return _create_booking(data)
+
+@transaction.atomic
+def _create_booking(data):
+ check_invariants(**data)
# check booking privileges
# TODO: use the canonical booking_allowed method because now template might have multiple
# machines
- if Booking.objects.filter(owner=request.user, end__gt=timezone.now()).count() >= 3 and not request.user.userprofile.booking_privledge:
+ if Booking.objects.filter(owner=data['owner'], end__gt=timezone.now()).count() >= 3 and not data['owner'].userprofile.booking_privledge:
raise PermissionError("You do not have permission to have more than 3 bookings at a time.")
- ResourceManager.getInstance().templateIsReservable(resource_template)
+ global_cloud_config = None if not data['global_cloud_config'] else data['global_cloud_config']
- resource_template = update_template(resource_template, image, hostname, request.user, global_cloud_config=global_cloud_config)
+ reservable = ResourceManager.getInstance().templateIsReservable(data['resource_template'])
+ if not reservable:
+ raise ValidationError("The given template is not currently possible to reserve, try again later or try a different host")
- # if no installer provided, just create blank host
- opnfv_config = None
- if installer:
- hconf = resource_template.getConfigs()[0]
- opnfv_config = generate_opnfvconfig(scenario, installer, resource_template)
- generate_hostopnfv(hconf, opnfv_config)
+ if global_cloud_config:
+ try:
+ d = yaml.load(global_cloud_config)
+ if not (type(d) is dict):
+ raise Exception("CI file was valid yaml but was not a dict")
+ except Exception as e:
+ raise ValidationError("The provided Cloud Config is not valid yaml, please refer to the Cloud Init documentation for expected structure")
+ global_cloud_config = CloudInitFile.create(text=global_cloud_config, priority=CloudInitFile.objects.count())
- # generate resource bundle
- resource_bundle = generate_resource_bundle(resource_template)
+ updated_template = update_template(data['resource_template'], data['image'], 'opnfv_host' if not data['hostname'] else data['hostname'], data['owner'], global_cloud_config)
- #generate_cloud_configs(resource_bundle)
+
+ # allocate hosts (as a resource bundle) according to template spec
+ resource_bundle = generate_resource_bundle(updated_template)
# generate booking
booking = Booking.objects.create(
- purpose=purpose_field,
- project=project_field,
- lab=lab,
- owner=request.user,
+ purpose=data['purpose'],
+ project=data['project'],
+ lab=data['lab'],
+ owner=data['owner'],
start=timezone.now(),
- end=timezone.now() + timedelta(days=int(length)),
+ end=timezone.now() + timedelta(days=int(data['length'])),
resource=resource_bundle,
- opnfv_config=opnfv_config
+ opnfv_config=None
)
+
booking.pdf = PDFTemplater.makePDF(booking)
- for collaborator in users_field: # list of Users (not UserProfile)
+ for collaborator in data['users']: # list of UserProfiles
booking.collaborators.add(collaborator.user)
booking.save()
diff --git a/src/booking/stats.py b/src/booking/stats.py
index 626ed79..70f91fa 100644
--- a/src/booking/stats.py
+++ b/src/booking/stats.py
@@ -104,5 +104,5 @@ class StatisticsManager(object):
"user": [x, users],
"utils": [in_use, not_in_use, maintenance],
"projects": [project_keys, project_counts],
- "colors": anuket_colors if os.environ['TEMPLATE_OVERRIDE_DIR'] == 'laas' else lfedge_colors
+ "colors": anuket_colors if os.environ.get('TEMPLATE_OVERRIDE_DIR') == 'laas' else lfedge_colors
}
diff --git a/src/booking/urls.py b/src/booking/urls.py
index cdf18ae..0b60351 100644
--- a/src/booking/urls.py
+++ b/src/booking/urls.py
@@ -38,7 +38,7 @@ from booking.views import (
booking_modify_image
)
-app_name = "booking"
+app_name = 'booking'
urlpatterns = [
url(r'^detail/(?P<booking_id>[0-9]+)/$', booking_detail_view, name='detail'),
url(r'^(?P<booking_id>[0-9]+)/$', booking_detail_view, name='booking_detail'),
diff --git a/src/booking/views.py b/src/booking/views.py
index e7ffc42..940428b 100644
--- a/src/booking/views.py
+++ b/src/booking/views.py
@@ -131,7 +131,6 @@ class ResourceBookingsJSON(View):
'start',
'end',
'purpose',
- 'jira_issue_status',
'config_bundle__name'
)
return JsonResponse({'bookings': list(bookings)})