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 ##############################################################################
14 from django.db.models import Q
15 from django.contrib.auth.models import User
16 from datetime import timedelta
17 from django.utils import timezone
18 from account.models import Lab
20 from resource_inventory.models import (
23 GenericResourceBundle,
35 from resource_inventory.resource_manager import ResourceManager
36 from booking.models import Booking
37 from dashboard.exceptions import (
38 InvalidHostnameException,
39 ResourceAvailabilityException,
40 ModelValidationException
42 from api.models import JobFactory
45 # model validity exceptions
46 class IncompatibleInstallerForOS(Exception):
50 class IncompatibleScenarioForInstaller(Exception):
54 class IncompatibleImageForHost(Exception):
58 class ImageOwnershipInvalid(Exception):
62 class ImageNotAvailableAtLab(Exception):
66 class LabDNE(Exception):
70 class HostProfileDNE(Exception):
74 class HostNotAvailable(Exception):
78 class NoLabSelectedError(Exception):
82 class OPNFVRoleDNE(Exception):
86 class NoRemainingPublicNetwork(Exception):
90 def create_from_form(form, request):
91 quick_booking_id = str(uuid.uuid4())
93 host_field = form.cleaned_data['filter_field']
94 host_json = json.loads(host_field)
95 purpose_field = form.cleaned_data['purpose']
96 project_field = form.cleaned_data['project']
97 users_field = form.cleaned_data['users']
98 host_name = form.cleaned_data['hostname']
99 length = form.cleaned_data['length']
101 image = form.cleaned_data['image']
102 scenario = form.cleaned_data['scenario']
103 installer = form.cleaned_data['installer']
105 # get all initial info we need to validate
106 lab_dict = host_json['labs'][0]
107 lab_id = list(lab_dict.keys())[0]
108 lab_user_id = int(lab_id.split("_")[-1])
109 lab = Lab.objects.get(lab_user__id=lab_user_id)
111 host_dict = host_json['hosts'][0]
112 profile_id = list(host_dict.keys())[0]
113 profile_id = int(profile_id.split("_")[-1])
114 profile = HostProfile.objects.get(id=profile_id)
116 # check validity of field data before trying to apply to models
118 raise LabDNE("Lab with provided ID does not exist")
120 raise HostProfileDNE("Host type with provided ID does not exist")
122 # check that hostname is valid
123 if not re.match(r"(?=^.{1,253}$)(^([A-Za-z0-9-_]{1,62}\.)*[A-Za-z0-9-_]{1,63})$", host_name):
124 raise InvalidHostnameException("Hostname must comply to RFC 952 and all extensions to it until this point")
125 # check that image os is compatible with installer
126 if installer in image.os.sup_installers.all():
127 #if installer not here, we can omit that and not check for scenario
129 raise IncompatibleScenarioForInstaller("An OPNFV Installer needs a scenario to be chosen to work properly")
130 if scenario not in installer.sup_scenarios.all():
131 raise IncompatibleScenarioForInstaller("The chosen installer does not support the chosen scenario")
132 if image.from_lab != lab:
133 raise ImageNotAvailableAtLab("The chosen image is not available at the chosen hosting lab")
134 if image.host_type != profile:
135 raise IncompatibleImageForHost("The chosen image is not available for the chosen host type")
136 if not image.public and image.owner != request.user:
137 raise ImageOwnershipInvalid("You are not the owner of the chosen private image")
139 # check if host type is available
140 #ResourceManager.getInstance().acquireHost(ghost, lab.name)
141 available_host_types = ResourceManager.getInstance().getAvailableHostTypes(lab)
142 if not profile in available_host_types:
143 # TODO: handle deleting generic resource in this instance along with grb
144 raise HostNotAvailable("Could not book selected host due to changed availability. Try again later")
146 # check if any hosts with profile at lab are still available
147 hostset = Host.objects.filter(lab=lab, profile=profile).filter(booked=False).filter(working=True)
148 if not hostset.first():
149 raise HostNotAvailable("Couldn't find any matching unbooked hosts")
151 # generate GenericResourceBundle
152 if len(host_json['labs']) != 1:
153 raise NoLabSelectedError("No lab was selected")
155 grbundle = GenericResourceBundle(owner=request.user)
157 grbundle.name = "grbundle for quick booking with uid " + quick_booking_id
158 grbundle.description = "grbundle created for quick-deploy booking"
161 # generate GenericResource, GenericHost
162 gresource = GenericResource(bundle=grbundle, name=host_name)
165 ghost = GenericHost()
166 ghost.resource = gresource
167 ghost.profile = profile
170 # generate config bundle
171 cbundle = ConfigBundle()
172 cbundle.owner = request.user
173 cbundle.name = "configbundle for quick booking with uid " + quick_booking_id
174 cbundle.description = "configbundle created for quick-deploy booking"
175 cbundle.bundle = grbundle
178 # generate OPNFVConfig pointing to cbundle
180 opnfvconfig = OPNFVConfig()
181 opnfvconfig.scenario = scenario
182 opnfvconfig.installer = installer
183 opnfvconfig.bundle = cbundle
186 # generate HostConfiguration pointing to cbundle
187 hconf = HostConfiguration()
190 hconf.opnfvRole = OPNFVRole.objects.get(name="Jumphost")
191 if not hconf.opnfvRole:
192 raise OPNFVRoleDNE("No jumphost role was found")
193 hconf.bundle = cbundle
196 # construct generic interfaces
197 for interface_profile in profile.interfaceprofile.all():
198 generic_interface = GenericInterface.objects.create(profile=interface_profile, host=ghost)
199 generic_interface.save()
202 # get vlan, assign to first interface
203 publicnetwork = lab.vlan_manager.get_public_vlan()
204 publicvlan = publicnetwork.vlan
205 if not publicnetwork:
206 raise NoRemainingPublicNetwork("No public networks were available for your pod")
207 lab.vlan_manager.reserve_public_vlan(publicvlan)
209 vlan = Vlan.objects.create(vlan_id=publicvlan, tagged=False, public=True)
211 ghost.generic_interfaces.first().vlans.add(vlan)
212 ghost.generic_interfaces.first().save()
214 # generate resource bundle
216 resource_bundle = ResourceManager.getInstance().convertResourceBundle(grbundle, config=cbundle)
217 except ResourceAvailabilityException:
218 raise ResourceAvailabilityException("Requested resources not available")
219 except ModelValidationException:
220 raise ModelValidationException("Encountered error while saving grbundle")
224 booking.purpose = purpose_field
225 booking.project = project_field
227 booking.owner = request.user
228 booking.start = timezone.now()
229 booking.end = timezone.now() + timedelta(days=int(length))
230 booking.resource = resource_bundle
231 booking.pdf = ResourceManager().makePDF(booking.resource)
232 booking.config_bundle = cbundle
234 print("users field:")
236 print(type(users_field))
237 #users_field = json.loads(users_field)
238 users_field = users_field[2:-2]
239 if users_field: #may be empty after split, if no collaborators entered
240 users_field = json.loads(users_field)
241 for collaborator in users_field:
242 user = User.objects.get(id=collaborator['id'])
243 booking.collaborators.add(user)
247 JobFactory.makeCompleteJob(booking)
250 def drop_filter(user):
251 installer_filter = {}
252 for image in Image.objects.all():
253 installer_filter[image.id] = {}
254 for installer in image.os.sup_installers.all():
255 installer_filter[image.id][installer.id] = 1
258 for installer in Installer.objects.all():
259 scenario_filter[installer.id] = {}
260 for scenario in installer.sup_scenarios.all():
261 scenario_filter[installer.id][scenario.id] = 1
263 images = Image.objects.filter(Q(public=True) | Q(owner=user))
266 image_filter[image.id] = {}
267 image_filter[image.id]['lab'] = 'lab_' + str(image.from_lab.lab_user.id)
268 image_filter[image.id]['host_profile'] = 'host_' + str(image.host_type.id)
269 image_filter[image.id]['name'] = image.name
271 return {'installer_filter': json.dumps(installer_filter),
272 'scenario_filter': json.dumps(scenario_filter),
273 'image_filter': json.dumps(image_filter)}