diff options
author | Sawyer Bergeron <sbergeron@iol.unh.edu> | 2021-10-15 17:03:58 -0400 |
---|---|---|
committer | Sawyer Bergeron <sbergeron@iol.unh.edu> | 2021-10-28 15:32:26 -0400 |
commit | 104470ee008817df4a3b78d24e524e8c92eac9a0 (patch) | |
tree | a4a6b70043f34c37b568bbe6a4b6e72d8c619716 /src/booking | |
parent | afcee3cad5c091e78e909b83f8df49accf1af5b6 (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.py | 120 | ||||
-rw-r--r-- | src/booking/stats.py | 2 | ||||
-rw-r--r-- | src/booking/urls.py | 2 | ||||
-rw-r--r-- | src/booking/views.py | 1 |
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)}) |