1 ##############################################################################
2 # Copyright (c) 2018 Parker Berberian, Sawyer Bergeron, and others.
4 # All rights reserved. This program and the accompanying materials
5 # are made available under the terms of the Apache License, Version 2.0
6 # which accompanies this distribution, and is available at
7 # http://www.apache.org/licenses/LICENSE-2.0
8 ##############################################################################
12 from django.db.models import Q
13 from datetime import timedelta
14 from django.utils import timezone
15 from django.core.exceptions import ValidationError
16 from account.models import Lab
18 from resource_inventory.models import (
26 from resource_inventory.resource_manager import ResourceManager
27 from resource_inventory.pdf_templater import PDFTemplater
28 from notifier.manager import NotificationHandler
29 from booking.models import Booking
30 from dashboard.exceptions import BookingLengthException
31 from api.models import JobFactory
34 def parse_resource_field(resource_json):
36 Parse the json from the frontend.
38 returns a reference to the selected Lab and ResourceTemplate objects
40 lab, template = (None, None)
41 lab_dict = resource_json['lab']
42 for lab_info in lab_dict.values():
43 if lab_info['selected']:
44 lab = Lab.objects.get(lab_user__id=lab_info['id'])
46 resource_dict = resource_json['resource']
47 for resource_info in resource_dict.values():
48 if resource_info['selected']:
49 template = ResourceTemplate.objects.get(pk=resource_info['id'])
52 raise ValidationError("No lab was selected")
54 raise ValidationError("No Host was selected")
59 def update_template(template, image, lab, hostname):
61 Update and copy a resource template to the user's profile.
63 TODO: How, why, should we?
68 def generate_opnfvconfig(scenario, installer, template):
69 return OPNFVConfig.objects.create(
76 def generate_hostopnfv(hostconfig, opnfvconfig):
79 role = OPNFVRole.objects.get(name="Jumphost")
81 role = OPNFVRole.objects.create(
83 description="Single server jumphost role"
85 return ResourceOPNFVConfig.objects.create(
87 host_config=hostconfig,
88 opnfv_config=opnfvconfig
92 def generate_resource_bundle(template):
93 resource_manager = ResourceManager.getInstance()
94 resource_bundle = resource_manager.convertResourceBundle(template)
95 return resource_bundle
98 def check_invariants(request, **kwargs):
99 # TODO: This should really happen in the BookingForm validation methods
100 installer = kwargs['installer']
101 image = kwargs['image']
102 scenario = kwargs['scenario']
104 host_profile = kwargs['host_profile']
105 length = kwargs['length']
106 # check that image os is compatible with installer
107 if installer in image.os.sup_installers.all():
108 # if installer not here, we can omit that and not check for scenario
110 raise ValidationError("An OPNFV Installer needs a scenario to be chosen to work properly")
111 if scenario not in installer.sup_scenarios.all():
112 raise ValidationError("The chosen installer does not support the chosen scenario")
113 if image.from_lab != lab:
114 raise ValidationError("The chosen image is not available at the chosen hosting lab")
115 if image.host_type != host_profile:
116 raise ValidationError("The chosen image is not available for the chosen host type")
117 if not image.public and image.owner != request.user:
118 raise ValidationError("You are not the owner of the chosen private image")
119 if length < 1 or length > 21:
120 raise BookingLengthException("Booking must be between 1 and 21 days long")
123 def create_from_form(form, request):
125 Create a Booking from the user's form.
127 Large, nasty method to create a booking or return a useful error
128 based on the form from the frontend
130 resource_field = form.cleaned_data['filter_field']
131 purpose_field = form.cleaned_data['purpose']
132 project_field = form.cleaned_data['project']
133 users_field = form.cleaned_data['users']
134 hostname = form.cleaned_data['hostname']
135 length = form.cleaned_data['length']
137 image = form.cleaned_data['image']
138 scenario = form.cleaned_data['scenario']
139 installer = form.cleaned_data['installer']
141 lab, resource_template = parse_resource_field(resource_field)
142 data = form.cleaned_data
144 data['resource_template'] = resource_template
145 check_invariants(request, **data)
147 # check booking privileges
148 # TODO: use the canonical booking_allowed method because now template might have multiple
150 if Booking.objects.filter(owner=request.user, end__gt=timezone.now()).count() >= 3 and not request.user.userprofile.booking_privledge:
151 raise PermissionError("You do not have permission to have more than 3 bookings at a time.")
153 ResourceManager.getInstance().templateIsReservable(resource_template)
155 hconf = update_template(resource_template, image, lab, hostname)
157 # if no installer provided, just create blank host
160 opnfv_config = generate_opnfvconfig(scenario, installer, resource_template)
161 generate_hostopnfv(hconf, opnfv_config)
163 # generate resource bundle
164 resource_bundle = generate_resource_bundle(resource_template)
167 booking = Booking.objects.create(
168 purpose=purpose_field,
169 project=project_field,
172 start=timezone.now(),
173 end=timezone.now() + timedelta(days=int(length)),
174 resource=resource_bundle,
175 opnfv_config=opnfv_config
177 booking.pdf = PDFTemplater.makePDF(booking)
179 for collaborator in users_field: # list of UserProfiles
180 booking.collaborators.add(collaborator.user)
185 JobFactory.makeCompleteJob(booking)
186 NotificationHandler.notify_new_booking(booking)
191 def drop_filter(user):
193 Return a dictionary that contains filters.
195 Only certain installlers are supported on certain images, etc
196 so the image filter indexed at [imageid][installerid] is truthy if
197 that installer is supported on that image
199 installer_filter = {}
200 for image in Image.objects.all():
201 installer_filter[image.id] = {}
202 for installer in image.os.sup_installers.all():
203 installer_filter[image.id][installer.id] = 1
206 for installer in Installer.objects.all():
207 scenario_filter[installer.id] = {}
208 for scenario in installer.sup_scenarios.all():
209 scenario_filter[installer.id][scenario.id] = 1
211 images = Image.objects.filter(Q(public=True) | Q(owner=user))
214 image_filter[image.id] = {
215 'lab': 'lab_' + str(image.from_lab.lab_user.id),
216 'host_profile': 'host_' + str(image.host_type.id),
220 return {'installer_filter': json.dumps(installer_filter),
221 'scenario_filter': json.dumps(scenario_filter),
222 'image_filter': json.dumps(image_filter)}