aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/__init__.py8
-rw-r--r--src/account/tests/test_general.py2
-rw-r--r--src/api/models.py30
-rw-r--r--src/api/tests/test_serializers.py229
-rw-r--r--src/api/urls.py2
-rw-r--r--src/api/views.py11
-rw-r--r--src/booking/quick_deployer.py208
-rw-r--r--src/booking/tests/test_models.py7
-rw-r--r--src/booking/tests/test_quick_booking.py155
-rw-r--r--src/dashboard/exceptions.py4
-rw-r--r--src/dashboard/testing_utils.py324
-rw-r--r--src/resource_inventory/models.py21
12 files changed, 682 insertions, 319 deletions
diff --git a/src/__init__.py b/src/__init__.py
deleted file mode 100644
index b6fef6c..0000000
--- a/src/__init__.py
+++ /dev/null
@@ -1,8 +0,0 @@
-##############################################################################
-# Copyright (c) 2016 Max Breitenfeldt 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
-##############################################################################
diff --git a/src/account/tests/test_general.py b/src/account/tests/test_general.py
index 57ad291..3fb52b0 100644
--- a/src/account/tests/test_general.py
+++ b/src/account/tests/test_general.py
@@ -47,7 +47,7 @@ class AccountMiddlewareTestCase(TestCase):
self.user1profile.timezone = 'Etc/Greenwich'
self.user1profile.save()
self.client.get(url)
- self.assertEqual(timezone.get_current_timezone_name(), 'Etc/Greenwich')
+ self.assertEqual(timezone.get_current_timezone_name(), 'GMT')
# if there is no profile for a user, it should be created
user2 = User.objects.create(username='user2')
diff --git a/src/api/models.py b/src/api/models.py
index 30f0f75..7109bbe 100644
--- a/src/api/models.py
+++ b/src/api/models.py
@@ -11,6 +11,7 @@
from django.contrib.auth.models import User
from django.db import models
from django.core.exceptions import PermissionDenied
+from django.shortcuts import get_object_or_404
import json
import uuid
@@ -21,7 +22,8 @@ from resource_inventory.models import (
HostProfile,
Host,
Image,
- Interface
+ Interface,
+ RemoteInfo
)
@@ -60,6 +62,32 @@ class LabManager(object):
def __init__(self, lab):
self.lab = lab
+ def update_host_remote_info(self, data, host_id):
+ host = get_object_or_404(Host, labid=host_id, lab=self.lab)
+ info = {}
+ try:
+ info['address'] = data['address']
+ info['mac_address'] = data['mac_address']
+ info['password'] = data['password']
+ info['user'] = data['user']
+ info['type'] = data['type']
+ info['versions'] = json.dumps(data['versions'])
+ except Exception as e:
+ return {"error": "invalid arguement: " + str(e)}
+ remote_info = host.remote_management
+ if "default" in remote_info.mac_address:
+ remote_info = RemoteInfo()
+ remote_info.address = info['address']
+ remote_info.mac_address = info['mac_address']
+ remote_info.password = info['password']
+ remote_info.user = info['user']
+ remote_info.type = info['type']
+ remote_info.versions = info['versions']
+ remote_info.save()
+ host.remote_management = remote_info
+ host.save()
+ return {"status": "success"}
+
def get_profile(self):
prof = {}
prof['name'] = self.lab.name
diff --git a/src/api/tests/test_serializers.py b/src/api/tests/test_serializers.py
deleted file mode 100644
index c1fa5af..0000000
--- a/src/api/tests/test_serializers.py
+++ /dev/null
@@ -1,229 +0,0 @@
-##############################################################################
-# Copyright (c) 2018 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
-##############################################################################
-from django.test import TestCase
-from booking.models import Booking
-from account.models import Lab
-from api.serializers.booking_serializer import BookingField
-from datetime import timedelta
-from django.utils import timezone
-from django.contrib.auth.models import Permission, User
-from resource_inventory.models import (
- Image,
- OPNFVRole,
- HostConfiguration,
- HostProfile,
- InterfaceProfile,
- DiskProfile,
- CpuProfile,
- RamProfile,
- GenericResourceBundle,
- GenericResource,
- GenericHost,
- Host,
- Vlan,
- Interface,
- ConfigBundle,
- ResourceBundle
-)
-
-
-class BookingSerializerTestCase(TestCase):
-
- count = 0
-
- def makeHostConfigurations(self, hosts, config):
- lab_user = User.objects.create(username="asfasdfasdf")
- owner = User.objects.create(username="asfasdfasdffffff")
- lab = Lab.objects.create(
- lab_user=lab_user,
- name="TestLab123123",
- contact_email="mail@email.com",
- contact_phone=""
- )
- jumphost = True
- for host in hosts:
- image = Image.objects.create(
- lab_id=12,
- from_lab=lab,
- name="this is a test image",
- owner=owner
- )
- name = "jumphost"
- if not jumphost:
- name = "compute"
- role = OPNFVRole.objects.create(
- name=name,
- description="stuff"
- )
-
- HostConfiguration.objects.create(
- host=host,
- image=image,
- bundle=config,
- opnfvRole=role
- )
- jumphost = False
-
- def setUp(self):
- self.serializer = BookingField()
- lab_user = User.objects.create(username="lab user")
- lab = Lab.objects.create(name="test lab", lab_user=lab_user)
- # create hostProfile
- hostProfile = HostProfile.objects.create(
- host_type=0,
- name='Test profile',
- description='a test profile'
- )
- InterfaceProfile.objects.create(
- speed=1000,
- name='eno3',
- host=hostProfile
- )
- DiskProfile.objects.create(
- size=1000,
- media_type="SSD",
- name='/dev/sda',
- host=hostProfile
- )
- CpuProfile.objects.create(
- cores=96,
- architecture="x86_64",
- cpus=2,
- host=hostProfile
- )
- RamProfile.objects.create(
- amount=256,
- channels=4,
- host=hostProfile
- )
-
- # create GenericResourceBundle
- genericBundle = GenericResourceBundle.objects.create()
-
- gres1 = GenericResource.objects.create(
- bundle=genericBundle,
- name='generic resource ' + str(self.count)
- )
- self.count += 1
- gHost1 = GenericHost.objects.create(
- resource=gres1,
- profile=hostProfile
- )
-
- gres2 = GenericResource.objects.create(
- bundle=genericBundle,
- name='generic resource ' + str(self.count)
- )
- self.count += 1
- gHost2 = GenericHost.objects.create(
- resource=gres2,
- profile=hostProfile
- )
- user1 = User.objects.create(username='user1')
-
- add_booking_perm = Permission.objects.get(codename='add_booking')
- user1.user_permissions.add(add_booking_perm)
-
- user1 = User.objects.get(pk=user1.id)
-
- conf = ConfigBundle.objects.create(owner=user1, name="test conf")
- self.makeHostConfigurations([gHost1, gHost2], conf)
-
- # actual resource bundle
- bundle = ResourceBundle.objects.create(
- template=genericBundle
- )
-
- host1 = Host.objects.create(
- template=gHost1,
- booked=True,
- name='host1',
- bundle=bundle,
- profile=hostProfile,
- lab=lab
- )
-
- host2 = Host.objects.create(
- template=gHost2,
- booked=True,
- name='host2',
- bundle=bundle,
- profile=hostProfile,
- lab=lab
- )
-
- vlan1 = Vlan.objects.create(vlan_id=300, tagged=False)
- vlan2 = Vlan.objects.create(vlan_id=300, tagged=False)
-
- iface1 = Interface.objects.create(
- mac_address='00:11:22:33:44:55',
- bus_address='some bus address',
- switch_name='switch1',
- port_name='port10',
- host=host1
- )
-
- iface1.config = [vlan1]
-
- iface2 = Interface.objects.create(
- mac_address='00:11:22:33:44:56',
- bus_address='some bus address',
- switch_name='switch1',
- port_name='port12',
- host=host2
- )
-
- iface2.config = [vlan2]
-
- # finally, can create booking
- self.booking = Booking.objects.create(
- owner=user1,
- start=timezone.now(),
- end=timezone.now() + timedelta(weeks=1),
- purpose='Testing',
- resource=bundle,
- config_bundle=conf
- )
-
- serialized_booking = {}
-
- host1 = {}
- host1['hostname'] = 'host1'
- host1['image'] = {} # TODO: Images
- host1['deploy_image'] = True
- host2 = {}
- host2['hostname'] = 'host2'
- host2['image'] = {} # TODO: Images
- host2['deploy_image'] = True
-
- serialized_booking['hosts'] = [host1, host2]
-
- net = {}
- net['name'] = 'network_name'
- net['vlan_id'] = 300
- netHost1 = {}
- netHost1['hostname'] = 'host1'
- netHost1['tagged'] = False
- netHost1['interface'] = 0
- netHost2 = {}
- netHost2['hostname'] = 'host2'
- netHost2['tagged'] = False
- netHost2['interface'] = 0
- net['hosts'] = [netHost1, netHost2]
-
- serialized_booking['networking'] = [net]
- serialized_booking['jumphost'] = 'host1'
-
- self.serialized_booking = serialized_booking
-
- def test_to_representation(self):
- keys = ['hosts', 'networking', 'jumphost']
- serialized_form = self.serializer.to_representation(self.booking)
- for key in keys:
- self.assertEquals(serialized_form[key], self.serialized_booking)
diff --git a/src/api/urls.py b/src/api/urls.py
index 50cc6ac..33a8289 100644
--- a/src/api/urls.py
+++ b/src/api/urls.py
@@ -39,6 +39,7 @@ from api.views import (
new_jobs,
current_jobs,
done_jobs,
+ update_host_bmc,
GenerateTokenView
)
@@ -51,6 +52,7 @@ urlpatterns = [
path('labs/<slug:lab_name>/profile', lab_profile),
path('labs/<slug:lab_name>/status', lab_status),
path('labs/<slug:lab_name>/inventory', lab_inventory),
+ path('labs/<slug:lab_name>/hosts/<slug:host_id>/bmc', update_host_bmc),
path('labs/<slug:lab_name>/jobs/<int:job_id>', specific_job),
path('labs/<slug:lab_name>/jobs/<int:job_id>/<slug:task_id>', specific_task),
path('labs/<slug:lab_name>/jobs/new', new_jobs),
diff --git a/src/api/views.py b/src/api/views.py
index c72c85c..90f87d9 100644
--- a/src/api/views.py
+++ b/src/api/views.py
@@ -62,6 +62,17 @@ def lab_status(request, lab_name=""):
return JsonResponse(lab_manager.get_status(), safe=False)
+def update_host_bmc(request, lab_name="", host_id=""):
+ lab_token = request.META.get('HTTP_AUTH_TOKEN')
+ lab_manager = LabManagerTracker.get(lab_name, lab_token)
+ if request.method == "POST":
+ # update / create RemoteInfo for host
+ return JsonResponse(
+ lab_manager.update_host_remote_info(request.POST, host_id),
+ safe=False
+ )
+
+
def lab_profile(request, lab_name=""):
lab_token = request.META.get('HTTP_AUTH_TOKEN')
lab_manager = LabManagerTracker.get(lab_name, lab_token)
diff --git a/src/booking/quick_deployer.py b/src/booking/quick_deployer.py
index bc714da..8a81d18 100644
--- a/src/booking/quick_deployer.py
+++ b/src/booking/quick_deployer.py
@@ -39,7 +39,8 @@ from booking.models import Booking
from dashboard.exceptions import (
InvalidHostnameException,
ResourceAvailabilityException,
- ModelValidationException
+ ModelValidationException,
+ BookingLengthException
)
from api.models import JobFactory
@@ -89,22 +90,8 @@ 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
+def parse_host_field(host_field_contents):
+ host_json = json.loads(host_field_contents)
lab_dict = host_json['labs'][0]
lab_id = list(lab_dict.keys())[0]
lab_user_id = int(lab_id.split("_")[-1])
@@ -116,110 +103,185 @@ def create_from_form(form, request):
profile = HostProfile.objects.get(id=profile_id)
# check validity of field data before trying to apply to models
+ if len(host_json['labs']) != 1:
+ raise NoLabSelectedError("No lab was selected")
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")
+ return lab, profile
+
- # check if host type is available
- # ResourceManager.getInstance().acquireHost(ghost, lab.name)
+def check_available_matching_host(lab, hostprofile):
available_host_types = ResourceManager.getInstance().getAvailableHostTypes(lab)
- if profile not in available_host_types:
+ if hostprofile not 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():
+ 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")
- # generate GenericResourceBundle
- if len(host_json['labs']) != 1:
- raise NoLabSelectedError("No lab was selected")
+ return True
+
- grbundle = GenericResourceBundle(owner=request.user)
+def generate_grb(owner, lab, common_id):
+ grbundle = GenericResourceBundle(owner=owner)
grbundle.lab = lab
- grbundle.name = "grbundle for quick booking with uid " + quick_booking_id
+ grbundle.name = "grbundle for quick booking with uid " + common_id
grbundle.description = "grbundle created for quick-deploy booking"
grbundle.save()
- # generate GenericResource, GenericHost
- gresource = GenericResource(bundle=grbundle, name=host_name)
+ return grbundle
+
+
+def generate_gresource(bundle, hostname):
+ 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):
ghost = GenericHost()
- ghost.resource = gresource
- ghost.profile = profile
+ ghost.resource = generic_resource
+ ghost.profile = host_profile
ghost.save()
- # generate config bundle
+ return ghost
+
+
+def generate_config_bundle(owner, common_id, grbundle):
cbundle = ConfigBundle()
- cbundle.owner = request.user
- cbundle.name = "configbundle for quick booking with uid " + quick_booking_id
+ 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()
- # generate OPNFVConfig pointing to cbundle
- if installer:
- opnfvconfig = OPNFVConfig()
- opnfvconfig.scenario = scenario
- opnfvconfig.installer = installer
- opnfvconfig.bundle = cbundle
- opnfvconfig.save()
+ return cbundle
+
+
+def generate_opnfvconfig(scenario, installer, config_bundle):
+ opnfvconfig = OPNFVConfig()
+ opnfvconfig.scenario = scenario
+ opnfvconfig.installer = installer
+ opnfvconfig.bundle = config_bundle
+ opnfvconfig.save()
+
+ return opnfvconfig
+
- # generate HostConfiguration pointing to cbundle
+def generate_hostconfig(generic_host, image, config_bundle):
hconf = HostConfiguration()
- hconf.host = ghost
+ hconf.host = generic_host
hconf.image = image
- hconf.opnfvRole = OPNFVRole.objects.get(name="Jumphost")
- if not hconf.opnfvRole:
- raise OPNFVRoleDNE("No jumphost role was found")
- hconf.bundle = cbundle
+
+ opnfvrole = OPNFVRole.objects.get(name="Jumphost")
+ if not opnfvrole:
+ raise OPNFVRoleDNE("No jumphost role was found.")
+
+ hconf.opnfvRole = opnfvrole
+ hconf.bundle = config_bundle
hconf.save()
+ return hconf
+
+
+def generate_resource_bundle(generic_resource_bundle, config_bundle): # warning: requires cleanup
+ 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 check_invariants(request, **kwargs):
+ installer = kwargs['installer']
+ image = kwargs['image']
+ scenario = kwargs['scenario']
+ lab = kwargs['lab']
+ host_profile = kwargs['host_profile']
+ length = kwargs['length']
+ # 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 != host_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")
+ if length < 1 or length > 21:
+ raise BookingLengthException("Booking must be between 1 and 21 days long")
+
+
+def create_from_form(form, request):
+ quick_booking_id = str(uuid.uuid4())
+
+ host_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 = form.cleaned_data['hostname']
+ length = form.cleaned_data['length']
+
+ image = form.cleaned_data['image']
+ scenario = form.cleaned_data['scenario']
+ installer = form.cleaned_data['installer']
+
+ lab, host_profile = parse_host_field(host_field)
+ data = form.cleaned_data
+ data['lab'] = lab
+ data['host_profile'] = host_profile
+ check_invariants(request, **data)
+
+ check_available_matching_host(lab, host_profile) # requires cleanup if failure after this point
+
+ 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)
+
+ # if no installer provided, just create blank host
+ if installer:
+ generate_opnfvconfig(scenario, installer, cbundle)
+
+ generate_hostconfig(ghost, image, cbundle)
+
# construct generic interfaces
- for interface_profile in profile.interfaceprofile.all():
+ for interface_profile in host_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")
+ publicvlan = publicnetwork.vlan
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")
+ resource_bundle = generate_resource_bundle(grbundle, cbundle)
# generate booking
booking = Booking()
diff --git a/src/booking/tests/test_models.py b/src/booking/tests/test_models.py
index c7fb25d..6170295 100644
--- a/src/booking/tests/test_models.py
+++ b/src/booking/tests/test_models.py
@@ -230,10 +230,3 @@ class BookingModelTestCase(TestCase):
booking.save()
except Exception:
self.fail("save() threw an exception")
- booking.end = booking.end + timedelta(weeks=2)
- self.assertRaises(ValueError, booking.save)
- booking.end = booking.end - timedelta(days=8)
- try:
- self.assertTrue(booking.save())
- except Exception:
- self.fail("save() threw an exception")
diff --git a/src/booking/tests/test_quick_booking.py b/src/booking/tests/test_quick_booking.py
new file mode 100644
index 0000000..936a9a5
--- /dev/null
+++ b/src/booking/tests/test_quick_booking.py
@@ -0,0 +1,155 @@
+##############################################################################
+# 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 datetime
+
+from django.test import TestCase, Client
+
+from booking.models import Booking
+from dashboard.testing_utils import (
+ instantiate_host,
+ instantiate_user,
+ instantiate_userprofile,
+ instantiate_lab,
+ instantiate_installer,
+ instantiate_image,
+ instantiate_scenario,
+ instantiate_os,
+ make_hostprofile_set,
+ instantiate_opnfvrole,
+ instantiate_publicnet,
+)
+# from dashboard import test_utils
+
+
+class QuickBookingValidFormTestCase(TestCase):
+ @classmethod
+ def setUpTestData(cls):
+ cls.loginuser = instantiate_user(False, username="newtestuser", password="testpassword")
+ instantiate_userprofile(cls.loginuser, True)
+
+ lab_user = instantiate_user(True)
+ cls.lab = instantiate_lab(lab_user)
+
+ cls.host_profile = make_hostprofile_set(cls.lab)
+ cls.scenario = instantiate_scenario()
+ cls.installer = instantiate_installer([cls.scenario])
+ os = instantiate_os([cls.installer])
+ cls.image = instantiate_image(cls.lab, 1, cls.loginuser, os, cls.host_profile)
+ cls.host = instantiate_host(cls.host_profile, cls.lab)
+ cls.role = instantiate_opnfvrole()
+ cls.pubnet = instantiate_publicnet(10, cls.lab)
+
+ cls.lab_selected = 'lab_' + str(cls.lab.lab_user.id) + '_selected'
+ cls.host_selected = 'host_' + str(cls.host_profile.id) + '_selected'
+
+ cls.post_data = cls.build_post_data()
+
+ cls.client = Client()
+
+ @classmethod
+ def build_post_data(cls):
+ post_data = {}
+ post_data['filter_field'] = '{"hosts":[{"host_' + str(cls.host_profile.id) + '":"true"}], "labs": [{"lab_' + str(cls.lab.lab_user.id) + '":"true"}]}'
+ post_data['purpose'] = 'purposefieldcontentstring'
+ post_data['project'] = 'projectfieldcontentstring'
+ post_data['length'] = '3'
+ post_data['ignore_this'] = 1
+ post_data['users'] = ''
+ post_data['hostname'] = 'hostnamefieldcontentstring'
+ post_data['image'] = str(cls.image.id)
+ post_data['installer'] = str(cls.installer.id)
+ post_data['scenario'] = str(cls.scenario.id)
+ return post_data
+
+ def post(self, changed_fields={}):
+ payload = self.post_data.copy()
+ payload.update(changed_fields)
+ response = self.client.post('/booking/quick/', payload)
+ return response
+
+ def setUp(self):
+ self.client.login(
+ username=self.loginuser.username, password="testpassword")
+
+ def is_valid_booking(self, booking):
+ self.assertEqual(booking.owner, self.loginuser)
+ self.assertEqual(booking.purpose, 'purposefieldcontentstring')
+ self.assertEqual(booking.project, 'projectfieldcontentstring')
+ delta = booking.end - booking.start
+ delta -= datetime.timedelta(days=3)
+ self.assertLess(delta, datetime.timedelta(minutes=1))
+
+ resourcebundle = booking.resource
+ configbundle = booking.config_bundle
+
+ self.assertEqual(self.installer, configbundle.opnfv_config.first().installer)
+ self.assertEqual(self.scenario, configbundle.opnfv_config.first().scenario)
+ self.assertEqual(resourcebundle.template.getHosts()[0].profile, self.host_profile)
+ self.assertEqual(resourcebundle.template.getHosts()[0].resource.name, 'hostnamefieldcontentstring')
+
+ return True
+
+ def test_with_too_long_length(self):
+ response = self.post({'length': '22'})
+
+ self.assertEqual(response.status_code, 200)
+ self.assertIsNone(Booking.objects.first())
+
+ def test_with_negative_length(self):
+ response = self.post({'length': '-1'})
+
+ self.assertEqual(response.status_code, 200)
+ self.assertIsNone(Booking.objects.first())
+
+ def test_with_invalid_installer(self):
+ response = self.post({'installer': str(self.installer.id + 100)})
+
+ self.assertEqual(response.status_code, 200)
+ self.assertIsNone(Booking.objects.first())
+
+ def test_with_invalid_scenario(self):
+ response = self.post({'scenario': str(self.scenario.id + 100)})
+
+ self.assertEqual(response.status_code, 200)
+ self.assertIsNone(Booking.objects.first())
+
+ def test_with_invalid_host_id(self):
+ response = self.post({'filter_field': '{"hosts":[{"host_' + str(self.host_profile.id + 100) + '":"true"}], "labs": [{"lab_' + str(self.lab.lab_user.id) + '":"true"}]}'})
+
+ self.assertEqual(response.status_code, 200)
+ self.assertIsNone(Booking.objects.first())
+
+ def test_with_invalid_lab_id(self):
+ response = self.post({'filter_field': '{"hosts":[{"host_' + str(self.host_profile.id) + '":"true"}], "labs": [{"lab_' + str(self.lab.lab_user.id + 100) + '":"true"}]}'})
+
+ self.assertEqual(response.status_code, 200)
+ self.assertIsNone(Booking.objects.first())
+
+ def test_with_invalid_empty_filter_field(self):
+ response = self.post({'filter_field': ''})
+
+ self.assertEqual(response.status_code, 200)
+ self.assertIsNone(Booking.objects.first())
+
+ def test_with_garbage_users_field(self): # expected behavior: treat as though field is empty if it has garbage data
+ response = self.post({'users': 'X�]QP�槰DP�+m���h�U�_�yJA:.rDi��QN|.��C��n�P��F!��D�����5ȅj�9�LV��'}) # output from /dev/urandom
+
+ self.assertEqual(response.status_code, 200)
+ booking = Booking.objects.first()
+ self.assertIsNotNone(booking)
+ self.assertTrue(self.is_valid_booking(booking))
+
+ def test_with_valid_form(self):
+ response = self.post()
+
+ self.assertEqual(response.status_code, 200)
+ booking = Booking.objects.first()
+ self.assertIsNotNone(booking)
+ self.assertTrue(self.is_valid_booking(booking))
diff --git a/src/dashboard/exceptions.py b/src/dashboard/exceptions.py
index 9c16a06..7111bf8 100644
--- a/src/dashboard/exceptions.py
+++ b/src/dashboard/exceptions.py
@@ -50,3 +50,7 @@ class InvalidVlanConfigurationException(Exception):
class NetworkExistsException(Exception):
pass
+
+
+class BookingLengthException(Exception):
+ pass
diff --git a/src/dashboard/testing_utils.py b/src/dashboard/testing_utils.py
new file mode 100644
index 0000000..e98b5e6
--- /dev/null
+++ b/src/dashboard/testing_utils.py
@@ -0,0 +1,324 @@
+##############################################################################
+# 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
+##############################################################################
+
+from django.contrib.auth.models import User
+
+import json
+
+from account.models import UserProfile, Lab, LabStatus, VlanManager, PublicNetwork
+from resource_inventory.models import (
+ Host,
+ HostProfile,
+ InterfaceProfile,
+ DiskProfile,
+ CpuProfile,
+ Opsys,
+ Image,
+ Scenario,
+ Installer,
+ OPNFVRole,
+ RamProfile,
+)
+
+
+class BookingContextData(object):
+ def prepopulate(self, *args, **kwargs):
+ self.loginuser = instantiate_user(False, username=kwargs.get("login_username", "newtestuser"), password="testpassword")
+ instantiate_userprofile(self.loginuser, True)
+
+ lab_user = kwargs.get("lab_user", instantiate_user(True))
+ self.lab = instantiate_lab(lab_user)
+
+ self.host_profile = make_hostprofile_set(self.lab)
+ self.scenario = instantiate_scenario()
+ self.installer = instantiate_installer([self.scenario])
+ os = instantiate_os([self.installer])
+ self.image = instantiate_image(self.lab, 1, self.loginuser, os, self.host_profile)
+ self.host = instantiate_host(self.host_profile, self.lab)
+ self.role = instantiate_opnfvrole()
+ self.pubnet = instantiate_publicnet(10, self.lab)
+
+
+def instantiate_user(is_superuser,
+ username="testuser",
+ password="testpassword",
+ email="default_email@user.com"
+ ):
+ user = User.objects.create_user(username=username, email=email, password=password)
+ user.is_superuser = is_superuser
+
+ user.save()
+
+ return user
+
+
+def instantiate_userprofile(user=None, can_book_multiple=False):
+ if not user:
+ user = instantiate_user(True, 'test_user', 'test_pass', 'test_user@test_site.org')
+ userprofile = UserProfile()
+ userprofile.user = user
+ userprofile.booking_privledge = can_book_multiple
+
+ userprofile.save()
+
+ return user
+
+
+def instantiate_vlanmanager(vlans=None,
+ block_size=20,
+ allow_overlapping=False,
+ reserved_vlans=None
+ ):
+ vlanmanager = VlanManager()
+ if not vlans:
+ vlans = []
+ for vlan in range(0, 4095):
+ vlans.append(vlan % 2)
+ vlanmanager.vlans = json.dumps(vlans)
+ if not reserved_vlans:
+ reserved_vlans = []
+ for vlan in range(0, 4095):
+ reserved_vlans.append(0)
+ vlanmanager.reserved_vlans = json.dumps(vlans)
+ vlanmanager.block_size = block_size
+ vlanmanager.allow_overlapping = allow_overlapping
+
+ vlanmanager.save()
+
+ return vlanmanager
+
+
+def instantiate_lab(user=None,
+ name="Test Lab Instance",
+ status=LabStatus.UP,
+ vlan_manager=None
+ ):
+ if not vlan_manager:
+ vlan_manager = instantiate_vlanmanager()
+
+ if not user:
+ user = instantiate_user(True, 'test_user', 'test_pass', 'test_user@test_site.org')
+
+ lab = Lab()
+ lab.lab_user = user
+ lab.name = name
+ lab.contact_email = 'test_lab@test_site.org'
+ lab.contact_phone = '603 123 4567'
+ lab.status = status
+ lab.vlan_manager = vlan_manager
+ lab.description = 'test lab instantiation'
+ lab.api_token = '12345678'
+
+ lab.save()
+
+ return lab
+
+
+"""
+resource_inventory instantiation section for permenant resources
+"""
+
+
+def make_hostprofile_set(lab, name="test_hostprofile"):
+ hostprof = instantiate_hostprofile(lab, name=name)
+ instantiate_diskprofile(hostprof, 500, name=name)
+ instantiate_cpuprofile(hostprof)
+ instantiate_interfaceprofile(hostprof, name=name)
+ instantiate_ramprofile(hostprof)
+
+ return hostprof
+
+
+def instantiate_hostprofile(lab,
+ host_type=0,
+ name="test hostprofile instance"
+ ):
+ hostprof = HostProfile()
+ hostprof.host_type = host_type
+ hostprof.name = name
+ hostprof.description = 'test hostprofile instance'
+ hostprof.save()
+ hostprof.labs.add(lab)
+
+ hostprof.save()
+
+ return hostprof
+
+
+def instantiate_ramprofile(host,
+ channels=4,
+ amount=256):
+ ramprof = RamProfile()
+ ramprof.host = host
+ ramprof.amount = amount
+ ramprof.channels = channels
+ ramprof.save()
+
+ return ramprof
+
+
+def instantiate_diskprofile(hostprofile,
+ size=0,
+ media_type="SSD",
+ name="test diskprofile",
+ rotation=0,
+ interface="sata"):
+
+ diskprof = DiskProfile()
+ diskprof.name = name
+ diskprof.size = size
+ diskprof.media_type = media_type
+ diskprof.host = hostprofile
+ diskprof.rotation = rotation
+ diskprof.interface = interface
+
+ diskprof.save()
+
+ return diskprof
+
+
+def instantiate_cpuprofile(hostprofile,
+ cores=4,
+ architecture="x86_64",
+ cpus=4,
+ ):
+ cpuprof = CpuProfile()
+ cpuprof.cores = cores
+ cpuprof.architecture = architecture
+ cpuprof.cpus = cpus
+ cpuprof.host = hostprofile
+ cpuprof.cflags = ''
+
+ cpuprof.save()
+
+ return cpuprof
+
+
+def instantiate_interfaceprofile(hostprofile,
+ speed=1000,
+ name="test interface profile",
+ nic_type="pcie"
+ ):
+ intprof = InterfaceProfile()
+ intprof.host = hostprofile
+ intprof.name = name
+ intprof.speed = speed
+ intprof.nic_type = nic_type
+
+ intprof.save()
+
+ return intprof
+
+
+def instantiate_image(lab,
+ lab_id,
+ owner,
+ os,
+ host_profile,
+ public=True,
+ name="default image",
+ description="default image"
+ ):
+ image = Image()
+ image.from_lab = lab
+ image.lab_id = lab_id
+ image.os = os
+ image.host_type = host_profile
+ image.public = public
+ image.name = name
+ image.description = description
+
+ image.save()
+
+ return image
+
+
+def instantiate_scenario(name="test scenario"):
+ scenario = Scenario()
+ scenario.name = name
+ scenario.save()
+ return scenario
+
+
+def instantiate_installer(supported_scenarios,
+ name="test installer"
+ ):
+ installer = Installer()
+ installer.name = name
+ installer.save()
+ for scenario in supported_scenarios:
+ installer.sup_scenarios.add(scenario)
+
+ installer.save()
+ return installer
+
+
+def instantiate_os(supported_installers,
+ name="test operating system",
+ ):
+ os = Opsys()
+ os.name = name
+ os.save()
+ for installer in supported_installers:
+ os.sup_installers.add(installer)
+ os.save()
+ return os
+
+
+def instantiate_host(host_profile,
+ lab,
+ labid="test_host",
+ name="test_host",
+ booked=False,
+ working=True,
+ config=None,
+ template=None,
+ bundle=None,
+ model="Model 1",
+ vendor="ACME"):
+ host = Host()
+ host.lab = lab
+ host.profile = host_profile
+ host.name = name
+ host.booked = booked
+ host.working = working
+ host.config = config
+ host.template = template
+ host.bundle = bundle
+ host.model = model
+ host.vendor = vendor
+
+ host.save()
+
+ return host
+
+
+def instantiate_opnfvrole(name="Jumphost",
+ description="test opnfvrole"):
+ role = OPNFVRole()
+ role.name = name
+ role.description = description
+ role.save()
+
+ return role
+
+
+def instantiate_publicnet(vlan,
+ lab,
+ in_use=False,
+ cidr="0.0.0.0/0",
+ gateway="0.0.0.0"):
+ pubnet = PublicNetwork()
+ pubnet.lab = lab
+ pubnet.vlan = vlan
+ pubnet.cidr = cidr
+ pubnet.gateway = gateway
+ pubnet.save()
+
+ return pubnet
diff --git a/src/resource_inventory/models.py b/src/resource_inventory/models.py
index ebf63cc..5f7f3d3 100644
--- a/src/resource_inventory/models.py
+++ b/src/resource_inventory/models.py
@@ -291,6 +291,26 @@ class HostConfiguration(models.Model):
return "config with " + str(self.host) + " and image " + str(self.image)
+class RemoteInfo(models.Model):
+ address = models.CharField(max_length=15)
+ mac_address = models.CharField(max_length=17)
+ password = models.CharField(max_length=100)
+ user = models.CharField(max_length=100)
+ management_type = models.CharField(max_length=50, default="ipmi")
+ versions = models.CharField(max_length=100) # json serialized list of floats
+
+
+def get_default_remote_info():
+ RemoteInfo.objects.get_or_create(
+ address="default",
+ mac_address="default",
+ password="default",
+ user="default",
+ management_type="default",
+ versions="[default]"
+ )
+
+
# Concrete host, actual machine in a lab
class Host(models.Model):
id = models.AutoField(primary_key=True)
@@ -305,6 +325,7 @@ class Host(models.Model):
working = models.BooleanField(default=True)
vendor = models.CharField(max_length=100, default="unknown")
model = models.CharField(max_length=150, default="unknown")
+ remote_management = models.ForeignKey(RemoteInfo, default=get_default_remote_info, on_delete=models.SET(get_default_remote_info))
def __str__(self):
return self.name