aboutsummaryrefslogtreecommitdiffstats
path: root/src/booking
diff options
context:
space:
mode:
authorParker Berberian <pberberian@iol.unh.edu>2020-03-16 17:54:09 +0000
committerGerrit Code Review <gerrit@opnfv.org>2020-03-16 17:54:09 +0000
commit0db3a84d9d9ed213983a517efd35c339537ef472 (patch)
treea6d7ac7ba2f2d70e18cb984bda4020c736082c62 /src/booking
parent176ec9aacbc87e6077e8807c60f95a1ccbbc26e3 (diff)
parent064f145f218385a6401fa6be2ccbbc462e915c26 (diff)
Merge "Test resource templates now use the same lab as the image generated alongside it."
Diffstat (limited to 'src/booking')
-rw-r--r--src/booking/forms.py2
-rw-r--r--src/booking/migrations/0007_remove_booking_config_bundle.py17
-rw-r--r--src/booking/models.py4
-rw-r--r--src/booking/quick_deployer.py284
-rw-r--r--src/booking/views.py12
5 files changed, 81 insertions, 238 deletions
diff --git a/src/booking/forms.py b/src/booking/forms.py
index 9b4db86..b9c9231 100644
--- a/src/booking/forms.py
+++ b/src/booking/forms.py
@@ -47,7 +47,7 @@ class QuickBookingForm(forms.Form):
**get_user_field_opts()
)
- attrs = FormUtils.getLabData(0)
+ attrs = FormUtils.getLabData()
self.fields['filter_field'] = MultipleSelectFilterField(widget=MultipleSelectFilterWidget(**attrs))
self.fields['length'] = forms.IntegerField(
widget=NumberInput(
diff --git a/src/booking/migrations/0007_remove_booking_config_bundle.py b/src/booking/migrations/0007_remove_booking_config_bundle.py
new file mode 100644
index 0000000..dcd2e1c
--- /dev/null
+++ b/src/booking/migrations/0007_remove_booking_config_bundle.py
@@ -0,0 +1,17 @@
+# Generated by Django 2.2 on 2020-02-18 15:36
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('booking', '0006_booking_opnfv_config'),
+ ]
+
+ operations = [
+ migrations.RemoveField(
+ model_name='booking',
+ name='config_bundle',
+ ),
+ ]
diff --git a/src/booking/models.py b/src/booking/models.py
index 8f2446f..cf8bf1d 100644
--- a/src/booking/models.py
+++ b/src/booking/models.py
@@ -9,7 +9,7 @@
##############################################################################
-from resource_inventory.models import ResourceBundle, ConfigBundle, OPNFVConfig
+from resource_inventory.models import ResourceBundle, OPNFVConfig
from account.models import Lab
from django.contrib.auth.models import User
from django.db import models
@@ -33,8 +33,6 @@ class Booking(models.Model):
ext_count = models.IntegerField(default=2)
# the hardware that the user has booked
resource = models.ForeignKey(ResourceBundle, on_delete=models.SET_NULL, null=True)
- # configuration for the above hardware
- config_bundle = models.ForeignKey(ConfigBundle, on_delete=models.SET_NULL, null=True)
opnfv_config = models.ForeignKey(OPNFVConfig, on_delete=models.SET_NULL, null=True)
project = models.CharField(max_length=100, default="", blank=True, null=True)
lab = models.ForeignKey(Lab, null=True, on_delete=models.SET_NULL)
diff --git a/src/booking/quick_deployer.py b/src/booking/quick_deployer.py
index 743cdcf..917f578 100644
--- a/src/booking/quick_deployer.py
+++ b/src/booking/quick_deployer.py
@@ -9,208 +9,71 @@
import json
-import uuid
-import re
from django.db.models import Q
from datetime import timedelta
from django.utils import timezone
+from django.core.exceptions import ValidationError
from account.models import Lab
from resource_inventory.models import (
+ ResourceTemplate,
Installer,
Image,
- GenericResourceBundle,
- ConfigBundle,
- Host,
- HostProfile,
- HostConfiguration,
- GenericResource,
- GenericHost,
- GenericInterface,
OPNFVRole,
OPNFVConfig,
- Network,
- NetworkConnection,
- NetworkRole,
- HostOPNFVConfig,
+ ResourceOPNFVConfig,
)
from resource_inventory.resource_manager import ResourceManager
from resource_inventory.pdf_templater import PDFTemplater
from notifier.manager import NotificationHandler
from booking.models import Booking
-from dashboard.exceptions import (
- InvalidHostnameException,
- ResourceAvailabilityException,
- ModelValidationException,
- BookingLengthException
-)
+from dashboard.exceptions import BookingLengthException
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
-
-
-class BookingPermissionException(Exception):
- pass
-
-
-def parse_host_field(host_json):
+def parse_resource_field(resource_json):
"""
Parse the json from the frontend.
- returns a reference to the selected Lab and HostProfile objects
+ returns a reference to the selected Lab and ResourceTemplate objects
"""
- lab, profile = (None, None)
- lab_dict = host_json['lab']
+ lab, template = (None, None)
+ lab_dict = resource_json['lab']
for lab_info in lab_dict.values():
if lab_info['selected']:
lab = Lab.objects.get(lab_user__id=lab_info['id'])
- host_dict = host_json['host']
- for host_info in host_dict.values():
- if host_info['selected']:
- profile = HostProfile.objects.get(pk=host_info['id'])
+ resource_dict = resource_json['resource']
+ for resource_info in resource_dict.values():
+ if resource_info['selected']:
+ template = ResourceTemplate.objects.get(pk=resource_info['id'])
if lab is None:
- raise NoLabSelectedError("No lab was selected")
- if profile is None:
- raise HostProfileDNE("No Host was selected")
+ raise ValidationError("No lab was selected")
+ if template is None:
+ raise ValidationError("No Host was selected")
- return lab, profile
+ return lab, template
-def check_available_matching_host(lab, hostprofile):
+def update_template(template, image, lab, hostname):
"""
- Check the resources are available.
+ Update and copy a resource template to the user's profile.
- Returns true if the requested host type is availble,
- Or throws an exception
+ TODO: How, why, should we?
"""
- available_host_types = ResourceManager.getInstance().getAvailableHostTypes(lab)
- if hostprofile not in available_host_types:
- # TODO: handle deleting generic resource in this instance along with grb
- raise HostNotAvailable('Requested host type is not available. Please try again later. Host availability can be viewed in the "Hosts" tab to the left.')
-
- hostset = Host.objects.filter(lab=lab, profile=hostprofile).filter(booked=False).filter(working=True)
- if not hostset.exists():
- raise HostNotAvailable("Couldn't find any matching unbooked hosts")
-
- return True
-
-
-# Functions to create models
-
-def generate_grb(owner, lab, common_id):
- """Create a Generic Resource Bundle."""
- grbundle = GenericResourceBundle(owner=owner)
- grbundle.lab = lab
- grbundle.name = "grbundle for quick booking with uid " + common_id
- grbundle.description = "grbundle created for quick-deploy booking"
- grbundle.save()
-
- return grbundle
-
-
-def generate_gresource(bundle, hostname):
- """Create a Generic Resource."""
- if not re.match(r"(?=^.{1,253}$)(^([A-Za-z0-9-_]{1,62}\.)*[A-Za-z0-9-_]{1,63})$", hostname):
- raise InvalidHostnameException("Hostname must comply to RFC 952 and all extensions to it until this point")
- gresource = GenericResource(bundle=bundle, name=hostname)
- gresource.save()
-
- return gresource
-
-
-def generate_ghost(generic_resource, host_profile):
- """Create a Generic Host."""
- ghost = GenericHost()
- ghost.resource = generic_resource
- ghost.profile = host_profile
- ghost.save()
-
- return ghost
-
-
-def generate_config_bundle(owner, common_id, grbundle):
- """Create a Configuration Bundle."""
- cbundle = ConfigBundle()
- cbundle.owner = owner
- cbundle.name = "configbundle for quick booking with uid " + common_id
- cbundle.description = "configbundle created for quick-deploy booking"
- cbundle.bundle = grbundle
- cbundle.save()
-
- return cbundle
-
-
-def generate_opnfvconfig(scenario, installer, config_bundle):
- """Create an OPNFV Configuration."""
- opnfvconfig = OPNFVConfig()
- opnfvconfig.scenario = scenario
- opnfvconfig.installer = installer
- opnfvconfig.bundle = config_bundle
- opnfvconfig.save()
-
- return opnfvconfig
-
+ pass
-def generate_hostconfig(generic_host, image, config_bundle):
- """Create a Host Configuration."""
- hconf = HostConfiguration()
- hconf.host = generic_host
- hconf.image = image
- hconf.bundle = config_bundle
- hconf.is_head_node = True
- hconf.save()
- return hconf
+def generate_opnfvconfig(scenario, installer, template):
+ return OPNFVConfig.objects.create(
+ scenario=scenario,
+ installer=installer,
+ template=template
+ )
def generate_hostopnfv(hostconfig, opnfvconfig):
- """Relate the Host and OPNFV Configs."""
- config = HostOPNFVConfig()
role = None
try:
role = OPNFVRole.objects.get(name="Jumphost")
@@ -219,31 +82,21 @@ def generate_hostopnfv(hostconfig, opnfvconfig):
name="Jumphost",
description="Single server jumphost role"
)
- config.role = role
- config.host_config = hostconfig
- config.opnfv_config = opnfvconfig
- config.save()
- return config
+ return ResourceOPNFVConfig.objects.create(
+ role=role,
+ host_config=hostconfig,
+ opnfv_config=opnfvconfig
+ )
-def generate_resource_bundle(generic_resource_bundle, config_bundle): # warning: requires cleanup
- """Create a Resource Bundle."""
- try:
- resource_manager = ResourceManager.getInstance()
- resource_bundle = resource_manager.convertResourceBundle(generic_resource_bundle, config=config_bundle)
- return resource_bundle
- except ResourceAvailabilityException:
- raise ResourceAvailabilityException("Requested resources not available")
- except ModelValidationException:
- raise ModelValidationException("Encountered error while saving grbundle")
+def generate_resource_bundle(template):
+ resource_manager = ResourceManager.getInstance()
+ resource_bundle = resource_manager.convertResourceBundle(template)
+ return resource_bundle
def check_invariants(request, **kwargs):
- """
- Verify all the contraints on the requested booking.
-
- verifies software compatibility, booking length, etc
- """
+ # TODO: This should really happen in the BookingForm validation methods
installer = kwargs['installer']
image = kwargs['image']
scenario = kwargs['scenario']
@@ -254,33 +107,19 @@ def check_invariants(request, **kwargs):
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")
+ raise ValidationError("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")
+ raise ValidationError("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")
+ raise ValidationError("The chosen image is not available at the chosen hosting lab")
if image.host_type != host_profile:
- raise IncompatibleImageForHost("The chosen image is not available for the chosen host type")
+ raise ValidationError("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")
+ 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")
-def configure_networking(grb, config):
- # create network
- net = Network.objects.create(name="public", bundle=grb, is_public=True)
- # connect network to generic host
- grb.getResources()[0].generic_interfaces.first().connections.add(
- NetworkConnection.objects.create(network=net, vlan_is_tagged=False)
- )
- # asign network role
- role = NetworkRole.objects.create(name="public", network=net)
- opnfv_config = config.opnfv_config.first()
- if opnfv_config:
- opnfv_config.networks.add(role)
-
-
def create_from_form(form, request):
"""
Create a Booking from the user's form.
@@ -288,9 +127,7 @@ def create_from_form(form, request):
Large, nasty method to create a booking or return a useful error
based on the form from the frontend
"""
- quick_booking_id = str(uuid.uuid4())
-
- host_field = form.cleaned_data['filter_field']
+ 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']
@@ -301,39 +138,30 @@ def create_from_form(form, request):
scenario = form.cleaned_data['scenario']
installer = form.cleaned_data['installer']
- lab, host_profile = parse_host_field(host_field)
+ lab, resource_template = parse_resource_field(resource_field)
data = form.cleaned_data
data['lab'] = lab
- data['host_profile'] = host_profile
+ data['resource_template'] = resource_template
check_invariants(request, **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:
- raise BookingPermissionException("You do not have permission to have more than 3 bookings at a time.")
+ raise PermissionError("You do not have permission to have more than 3 bookings at a time.")
- check_available_matching_host(lab, host_profile) # requires cleanup if failure after this point
+ ResourceManager.getInstance().templateIsReservable(resource_template)
- grbundle = generate_grb(request.user, lab, quick_booking_id)
- gresource = generate_gresource(grbundle, hostname)
- ghost = generate_ghost(gresource, host_profile)
- cbundle = generate_config_bundle(request.user, quick_booking_id, grbundle)
- hconf = generate_hostconfig(ghost, image, cbundle)
+ hconf = update_template(resource_template, image, lab, hostname)
# if no installer provided, just create blank host
opnfv_config = None
if installer:
- opnfv_config = generate_opnfvconfig(scenario, installer, cbundle)
+ opnfv_config = generate_opnfvconfig(scenario, installer, resource_template)
generate_hostopnfv(hconf, opnfv_config)
- # construct generic interfaces
- for interface_profile in host_profile.interfaceprofile.all():
- generic_interface = GenericInterface.objects.create(profile=interface_profile, host=ghost)
- generic_interface.save()
-
- configure_networking(grbundle, cbundle)
-
# generate resource bundle
- resource_bundle = generate_resource_bundle(grbundle, cbundle)
+ resource_bundle = generate_resource_bundle(resource_template)
# generate booking
booking = Booking.objects.create(
@@ -344,7 +172,6 @@ def create_from_form(form, request):
start=timezone.now(),
end=timezone.now() + timedelta(days=int(length)),
resource=resource_bundle,
- config_bundle=cbundle,
opnfv_config=opnfv_config
)
booking.pdf = PDFTemplater.makePDF(booking)
@@ -384,10 +211,11 @@ def drop_filter(user):
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
+ image_filter[image.id] = {
+ 'lab': 'lab_' + str(image.from_lab.lab_user.id),
+ 'host_profile': 'host_' + str(image.host_type.id),
+ 'name': image.name
+ }
return {'installer_filter': json.dumps(installer_filter),
'scenario_filter': json.dumps(scenario_filter),
diff --git a/src/booking/views.py b/src/booking/views.py
index 8e25952..daaf026 100644
--- a/src/booking/views.py
+++ b/src/booking/views.py
@@ -18,7 +18,7 @@ from django.shortcuts import redirect, render
from django.db.models import Q
from django.urls import reverse
-from resource_inventory.models import ResourceBundle, HostProfile, Image, Host
+from resource_inventory.models import ResourceBundle, ResourceProfile, Image, ResourceQuery
from resource_inventory.resource_manager import ResourceManager
from account.models import Lab, Downtime
from booking.models import Booking
@@ -42,11 +42,11 @@ def quick_create(request):
context = {}
r_manager = ResourceManager.getInstance()
- profiles = {}
+ templates = {}
for lab in Lab.objects.all():
- profiles[str(lab)] = r_manager.getAvailableHostTypes(lab)
+ templates[str(lab)] = r_manager.getAvailableResourceTemplates(lab, request.user)
- context['lab_profile_map'] = profiles
+ context['lab_profile_map'] = templates
context['form'] = QuickBookingForm(default_user=request.user.username, user=request.user)
@@ -130,7 +130,7 @@ class ResourceBookingsJSON(View):
def build_image_mapping(lab, user):
mapping = {}
- for profile in HostProfile.objects.filter(labs=lab):
+ for profile in ResourceProfile.objects.filter(labs=lab):
images = Image.objects.filter(
from_lab=lab,
host_type=profile
@@ -178,7 +178,7 @@ def booking_modify_image(request, booking_id):
if timezone.now() > booking.end:
return HttpResponse("unauthorized")
new_image = Image.objects.get(id=form.cleaned_data['image_id'])
- host = Host.objects.get(id=form.cleaned_data['host_id'])
+ host = ResourceQuery.get(labid=form.cleaned_data['host_id'])
host.config.image = new_image
host.config.save()
JobFactory.reimageHost(new_image, booking, host)