aboutsummaryrefslogtreecommitdiffstats
path: root/src/booking/quick_deployer.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/booking/quick_deployer.py')
-rw-r--r--src/booking/quick_deployer.py272
1 files changed, 272 insertions, 0 deletions
diff --git a/src/booking/quick_deployer.py b/src/booking/quick_deployer.py
new file mode 100644
index 0000000..9bc8c66
--- /dev/null
+++ b/src/booking/quick_deployer.py
@@ -0,0 +1,272 @@
+##############################################################################
+# Copyright (c) 2018 Parker Berberian, Sawyer Bergeron, and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+
+import json
+import uuid
+import re
+from django.db.models import Q
+from django.contrib.auth.models import User
+from datetime import timedelta
+from django.utils import timezone
+from account.models import Lab
+
+from resource_inventory.models import (
+ Installer,
+ Image,
+ GenericResourceBundle,
+ ConfigBundle,
+ Vlan,
+ Host,
+ HostProfile,
+ HostConfiguration,
+ GenericResource,
+ GenericHost,
+ GenericInterface,
+ OPNFVRole,
+ OPNFVConfig
+)
+from resource_inventory.resource_manager import ResourceManager
+from booking.models import Booking
+from dashboard.exceptions import (
+ InvalidHostnameException,
+ ResourceAvailabilityException,
+ ModelValidationException
+)
+from api.models import JobFactory
+
+
+# model validity exceptions
+class IncompatibleInstallerForOS(Exception):
+ pass
+
+
+class IncompatibleScenarioForInstaller(Exception):
+ pass
+
+
+class IncompatibleImageForHost(Exception):
+ pass
+
+
+class ImageOwnershipInvalid(Exception):
+ pass
+
+
+class ImageNotAvailableAtLab(Exception):
+ pass
+
+
+class LabDNE(Exception):
+ pass
+
+
+class HostProfileDNE(Exception):
+ pass
+
+
+class HostNotAvailable(Exception):
+ pass
+
+
+class NoLabSelectedError(Exception):
+ pass
+
+
+class OPNFVRoleDNE(Exception):
+ pass
+
+
+class NoRemainingPublicNetwork(Exception):
+ pass
+
+
+def create_from_form(form, request):
+ quick_booking_id = str(uuid.uuid4())
+
+ host_field = form.cleaned_data['filter_field']
+ host_json = json.loads(host_field)
+ purpose_field = form.cleaned_data['purpose']
+ project_field = form.cleaned_data['project']
+ users_field = form.cleaned_data['users']
+ host_name = form.cleaned_data['hostname']
+ length = form.cleaned_data['length']
+
+ image = form.cleaned_data['image']
+ scenario = form.cleaned_data['scenario']
+ installer = form.cleaned_data['installer']
+
+ # get all initial info we need to validate
+ lab_dict = host_json['labs'][0]
+ lab_id = list(lab_dict.keys())[0]
+ lab_user_id = int(lab_id.split("_")[-1])
+ lab = Lab.objects.get(lab_user__id=lab_user_id)
+
+ host_dict = host_json['hosts'][0]
+ profile_id = list(host_dict.keys())[0]
+ profile_id = int(profile_id.split("_")[-1])
+ profile = HostProfile.objects.get(id=profile_id)
+
+ # check validity of field data before trying to apply to models
+ if not lab:
+ raise LabDNE("Lab with provided ID does not exist")
+ if not profile:
+ raise HostProfileDNE("Host type with provided ID does not exist")
+
+ # check that hostname is valid
+ if not re.match(r"(?=^.{1,253}$)(^([A-Za-z0-9-_]{1,62}\.)*[A-Za-z0-9-_]{1,63})$", host_name):
+ raise InvalidHostnameException("Hostname must comply to RFC 952 and all extensions to it until this point")
+ # check that image os is compatible with installer
+ if installer in image.os.sup_installers.all():
+ #if installer not here, we can omit that and not check for scenario
+ if not scenario:
+ raise IncompatibleScenarioForInstaller("An OPNFV Installer needs a scenario to be chosen to work properly")
+ if scenario not in installer.sup_scenarios.all():
+ raise IncompatibleScenarioForInstaller("The chosen installer does not support the chosen scenario")
+ if image.from_lab != lab:
+ raise ImageNotAvailableAtLab("The chosen image is not available at the chosen hosting lab")
+ if image.host_type != profile:
+ raise IncompatibleImageForHost("The chosen image is not available for the chosen host type")
+ if not image.public and image.owner != request.user:
+ raise ImageOwnershipInvalid("You are not the owner of the chosen private image")
+
+ # check if host type is available
+ #ResourceManager.getInstance().acquireHost(ghost, lab.name)
+ available_host_types = ResourceManager.getInstance().getAvailableHostTypes(lab)
+ if not profile in available_host_types:
+ # TODO: handle deleting generic resource in this instance along with grb
+ raise HostNotAvailable("Could not book selected host due to changed availability. Try again later")
+
+ # check if any hosts with profile at lab are still available
+ hostset = Host.objects.filter(lab=lab, profile=profile).filter(booked=False).filter(working=True)
+ if not hostset.first():
+ raise HostNotAvailable("Couldn't find any matching unbooked hosts")
+
+ # generate GenericResourceBundle
+ if len(host_json['labs']) != 1:
+ raise NoLabSelectedError("No lab was selected")
+
+ grbundle = GenericResourceBundle(owner=request.user)
+ grbundle.lab = lab
+ grbundle.name = "grbundle for quick booking with uid " + quick_booking_id
+ grbundle.description = "grbundle created for quick-deploy booking"
+ grbundle.save()
+
+ # generate GenericResource, GenericHost
+ gresource = GenericResource(bundle=grbundle, name=host_name)
+ gresource.save()
+
+ ghost = GenericHost()
+ ghost.resource = gresource
+ ghost.profile = profile
+ ghost.save()
+
+ # generate config bundle
+ cbundle = ConfigBundle()
+ cbundle.owner = request.user
+ cbundle.name = "configbundle for quick booking with uid " + quick_booking_id
+ cbundle.description = "configbundle created for quick-deploy booking"
+ cbundle.bundle = grbundle
+ cbundle.save()
+
+ # generate OPNFVConfig pointing to cbundle
+ if installer:
+ opnfvconfig = OPNFVConfig()
+ opnfvconfig.scenario = scenario
+ opnfvconfig.installer = installer
+ opnfvconfig.bundle = cbundle
+ opnfvconfig.save()
+
+ # generate HostConfiguration pointing to cbundle
+ hconf = HostConfiguration()
+ hconf.host = ghost
+ hconf.image = image
+ hconf.opnfvRole = OPNFVRole.objects.get(name="Jumphost")
+ if not hconf.opnfvRole:
+ raise OPNFVRoleDNE("No jumphost role was found")
+ hconf.bundle = cbundle
+ hconf.save()
+
+ # construct generic interfaces
+ for interface_profile in profile.interfaceprofile.all():
+ generic_interface = GenericInterface.objects.create(profile=interface_profile, host=ghost)
+ generic_interface.save()
+ ghost.save()
+
+ # get vlan, assign to first interface
+ publicnetwork = lab.vlan_manager.get_public_vlan()
+ publicvlan = publicnetwork.vlan
+ if not publicnetwork:
+ raise NoRemainingPublicNetwork("No public networks were available for your pod")
+ lab.vlan_manager.reserve_public_vlan(publicvlan)
+
+ vlan = Vlan.objects.create(vlan_id=publicvlan, tagged=False, public=True)
+ vlan.save()
+ ghost.generic_interfaces.first().vlans.add(vlan)
+ ghost.generic_interfaces.first().save()
+
+ # generate resource bundle
+ try:
+ resource_bundle = ResourceManager.getInstance().convertResourceBundle(grbundle, config=cbundle)
+ except ResourceAvailabilityException:
+ raise ResourceAvailabilityException("Requested resources not available")
+ except ModelValidationException:
+ raise ModelValidationException("Encountered error while saving grbundle")
+
+ # generate booking
+ booking = Booking()
+ booking.purpose = purpose_field
+ booking.project = project_field
+ booking.lab = lab
+ booking.owner = request.user
+ booking.start = timezone.now()
+ booking.end = timezone.now() + timedelta(days=int(length))
+ booking.resource = resource_bundle
+ booking.pdf = ResourceManager().makePDF(booking.resource)
+ booking.save()
+ print("users field:")
+ print(users_field)
+ print(type(users_field))
+ #users_field = json.loads(users_field)
+ users_field = users_field[2:-2]
+ if users_field: #may be empty after split, if no collaborators entered
+ users_field = json.loads(users_field)
+ for collaborator in users_field:
+ user = User.objects.get(id=collaborator['id'])
+ booking.collaborators.add(user)
+ booking.save()
+
+ # generate job
+ JobFactory.makeCompleteJob(booking)
+
+
+def drop_filter(user):
+ installer_filter = {}
+ for image in Image.objects.all():
+ installer_filter[image.id] = {}
+ for installer in image.os.sup_installers.all():
+ installer_filter[image.id][installer.id] = 1
+
+ scenario_filter = {}
+ for installer in Installer.objects.all():
+ scenario_filter[installer.id] = {}
+ for scenario in installer.sup_scenarios.all():
+ scenario_filter[installer.id][scenario.id] = 1
+
+ images = Image.objects.filter(Q(public=True) | Q(owner=user))
+ image_filter = {}
+ for image in images:
+ image_filter[image.id] = {}
+ image_filter[image.id]['lab'] = 'lab_' + str(image.from_lab.lab_user.id)
+ image_filter[image.id]['host_profile'] = 'host_' + str(image.host_type.id)
+ image_filter[image.id]['name'] = image.name
+
+ return {'installer_filter': json.dumps(installer_filter),
+ 'scenario_filter': json.dumps(scenario_filter),
+ 'image_filter': json.dumps(image_filter)}