summaryrefslogtreecommitdiffstats
path: root/dashboard/src
diff options
context:
space:
mode:
authorSawyer Bergeron <sawyerbergeron@gmail.com>2019-03-05 15:28:51 -0500
committerSawyer Bergeron <sawyerbergeron@gmail.com>2019-03-14 12:21:45 -0400
commita9dfa98ad457863b1a0cdaaef4fe57797f92660f (patch)
treec49927b92f01cdc10067a59aac424ae86342f41d /dashboard/src
parent357ca93d21500d4d5a55f1bdd0550326d424bb26 (diff)
Add API tests
Change-Id: Ic26d0b6de63405d239a9260b862158962c3140ac Signed-off-by: Sawyer Bergeron <sawyerbergeron@gmail.com>
Diffstat (limited to 'dashboard/src')
-rw-r--r--dashboard/src/api/tests/test_models_unittest.py278
-rw-r--r--dashboard/src/dashboard/testing_utils.py210
2 files changed, 478 insertions, 10 deletions
diff --git a/dashboard/src/api/tests/test_models_unittest.py b/dashboard/src/api/tests/test_models_unittest.py
new file mode 100644
index 0000000..971f757
--- /dev/null
+++ b/dashboard/src/api/tests/test_models_unittest.py
@@ -0,0 +1,278 @@
+##############################################################################
+# Copyright (c) 2019 Sawyer Bergeron, Parker Berberian, 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 datetime import timedelta
+from django.utils import timezone
+
+from booking.models import Booking
+from api.models import (
+ Job,
+ JobStatus,
+ JobFactory,
+ AccessRelation,
+ HostNetworkRelation,
+ HostHardwareRelation,
+ SoftwareRelation,
+)
+
+from resource_inventory.models import (
+ OPNFVRole,
+ HostProfile,
+)
+
+from django.test import TestCase, Client
+
+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,
+ instantiate_booking,
+)
+
+
+class ValidBookingCreatesValidJob(TestCase):
+ @classmethod
+ def setUpTestData(cls):
+ cls.loginuser = instantiate_user(False, username="newtestuser", password="testpassword")
+ cls.userprofile = instantiate_userprofile(cls.loginuser)
+
+ 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)
+ for i in range(30):
+ instantiate_host(cls.host_profile, cls.lab, name="host" + str(i), labid="host" + str(i))
+ cls.role = instantiate_opnfvrole("Jumphost")
+ cls.computerole = instantiate_opnfvrole("Compute")
+ instantiate_publicnet(10, cls.lab)
+ instantiate_publicnet(12, cls.lab)
+ instantiate_publicnet(14, 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()
+
+ def setUp(self):
+ self.client.login(
+ username=self.loginuser.username, password="testpassword")
+ self.booking, self.compute_hostnames, self.jump_hostname = self.create_multinode_generic_booking()
+
+ @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 generate_booking(self):
+ self.post()
+ return Booking.objects.first()
+
+ def test_valid_access_configs(self):
+ job = Job.objects.get(booking=self.booking)
+ self.assertIsNotNone(job)
+
+ access_configs = [r.config for r in AccessRelation.objects.filter(job=job).all()]
+
+ vpnconfigs = []
+ sshconfigs = []
+
+ for config in access_configs:
+ if config.access_type == "vpn":
+ vpnconfigs.append(config)
+ elif config.access_type == "ssh":
+ sshconfigs.append(config)
+ else:
+ self.fail(msg="Undefined accessconfig: " + config.access_type + " found")
+
+ user_set = []
+ user_set.append(self.booking.owner)
+ user_set += self.booking.collaborators.all()
+
+ for configs in [vpnconfigs, sshconfigs]:
+ for user in user_set:
+ configusers = [c.user for c in configs]
+ self.assertTrue(user in configusers)
+
+ def test_valid_network_configs(self):
+ job = Job.objects.get(booking=self.booking)
+ self.assertIsNotNone(job)
+
+ booking_hosts = self.booking.resource.hosts.all()
+
+ netrelation_set = HostNetworkRelation.objects.filter(job=job)
+ netconfig_set = [r.config for r in netrelation_set]
+
+ netrelation_hosts = [r.host for r in netrelation_set]
+
+ for config in netconfig_set:
+ for interface in config.interfaces.all():
+ self.assertTrue(interface.host in booking_hosts)
+
+ # if no interfaces are referenced that shouldn't have vlans,
+ # and no vlans exist outside those accounted for in netconfigs,
+ # then the api is faithfully representing networks
+ # as netconfigs reference resource_inventory models directly
+
+ # this test relies on the assumption that
+ # every interface is configured, whether it does or does not have vlans
+ # if this is not true, the test fails
+
+ for host in booking_hosts:
+ self.assertTrue(host in netrelation_hosts)
+ relation = HostNetworkRelation.objects.filter(job=job).get(host=host)
+
+ # do 2 direction matching that interfaces are one to one
+ config = relation.config
+ for interface in config.interfaces.all():
+ self.assertTrue(interface in host.interfaces)
+ for interface in host.interfaces.all():
+ self.assertTrue(interface in config.interfaces)
+
+ for host in netrelation_hosts:
+ self.assertTrue(host in booking_hosts)
+
+ def test_valid_hardware_configs(self):
+ job = Job.objects.get(booking=self.booking)
+ self.assertIsNotNone(job)
+
+ hrelations = HostHardwareRelation.objects.filter(job=job).all()
+
+ job_hosts = [r.host for r in hrelations]
+
+ booking_hosts = self.booking.resource.hosts.all()
+
+ self.assertEqual(len(booking_hosts), len(job_hosts))
+
+ for relation in hrelations:
+ self.assertTrue(relation.host in booking_hosts)
+ self.assertEqual(relation.status, JobStatus.NEW)
+ config = relation.config
+ host = relation.host
+ self.assertEqual(config.hostname, host.template.resource.name)
+
+ def test_valid_software_configs(self):
+ job = Job.objects.get(booking=self.booking)
+ self.assertIsNotNone(job)
+
+ srelation = SoftwareRelation.objects.filter(job=job).first()
+ self.assertIsNotNone(srelation)
+
+ sconfig = srelation.config
+ self.assertIsNotNone(sconfig)
+
+ oconfig = sconfig.opnfv
+ self.assertIsNotNone(oconfig)
+
+ # not onetoone in models, but first() is safe here based on how ConfigBundle and a matching OPNFVConfig are created
+ # this should, however, be made explicit
+ self.assertEqual(oconfig.installer, self.booking.config_bundle.opnfv_config.first().installer.name)
+ self.assertEqual(oconfig.scenario, self.booking.config_bundle.opnfv_config.first().scenario.name)
+
+ for host in oconfig.roles.all():
+ role_name = host.config.opnfvRole.name
+ if str(role_name) == "Jumphost":
+ self.assertEqual(host.template.resource.name, self.jump_hostname)
+ elif str(role_name) == "Compute":
+ self.assertTrue(host.template.resource.name in self.compute_hostnames)
+ else:
+ self.fail(msg="Host with non-configured role name related to job: " + str(role_name))
+
+ def create_multinode_generic_booking(self):
+ topology = {}
+
+ compute_hostnames = ["cmp01", "cmp02", "cmp03"]
+
+ host_type = HostProfile.objects.first()
+
+ universal_networks = [
+ {"name": "public", "tagged": False, "public": True},
+ {"name": "admin", "tagged": True, "public": False}]
+ just_compute_networks = [{"name": "private", "tagged": True, "public": False}]
+ just_jumphost_networks = [{"name": "external", "tagged": True, "public": True}]
+
+ # generate a bunch of extra networks
+ for i in range(10):
+ net = {"tagged": False, "public": False}
+ net["name"] = "u_net" + str(i)
+ universal_networks.append(net)
+
+ jhost_info = {}
+ jhost_info["type"] = host_type
+ jhost_info["role"] = OPNFVRole.objects.get(name="Jumphost")
+ jhost_info["nets"] = self.make_networks(host_type, list(just_jumphost_networks + universal_networks))
+ jhost_info["image"] = self.image
+ topology["jump"] = jhost_info
+
+ for hostname in compute_hostnames:
+ host_info = {}
+ host_info["type"] = host_type
+ host_info["role"] = OPNFVRole.objects.get(name="Compute")
+ host_info["nets"] = self.make_networks(host_type, list(just_compute_networks + universal_networks))
+ host_info["image"] = self.image
+ topology[hostname] = host_info
+
+ booking = instantiate_booking(self.loginuser,
+ timezone.now(),
+ timezone.now() + timedelta(days=1),
+ "demobooking",
+ self.lab,
+ topology=topology,
+ installer=self.installer,
+ scenario=self.scenario)
+
+ if not booking.resource:
+ raise Exception("Booking does not have a resource when trying to pass to makeCompleteJob")
+ JobFactory.makeCompleteJob(booking)
+
+ return booking, compute_hostnames, "jump"
+
+ """
+ evenly distributes networks given across a given profile's interfaces
+ """
+ def make_networks(self, hostprofile, nets):
+ network_struct = []
+ count = hostprofile.interfaceprofile.all().count()
+ for i in range(count):
+ network_struct.append([])
+ while(nets):
+ index = len(nets) % count
+ network_struct[index].append(nets.pop())
+
+ return network_struct
diff --git a/dashboard/src/dashboard/testing_utils.py b/dashboard/src/dashboard/testing_utils.py
index e98b5e6..558031d 100644
--- a/dashboard/src/dashboard/testing_utils.py
+++ b/dashboard/src/dashboard/testing_utils.py
@@ -8,9 +8,15 @@
##############################################################################
from django.contrib.auth.models import User
+from django.core.files.base import ContentFile
import json
+import re
+from dashboard.exceptions import (
+ InvalidHostnameException
+)
+from booking.models import Booking
from account.models import UserProfile, Lab, LabStatus, VlanManager, PublicNetwork
from resource_inventory.models import (
Host,
@@ -24,13 +30,23 @@ from resource_inventory.models import (
Installer,
OPNFVRole,
RamProfile,
+ Network,
+ Vlan,
+ GenericResourceBundle,
+ GenericResource,
+ GenericHost,
+ ConfigBundle,
+ GenericInterface,
+ HostConfiguration,
+ OPNFVConfig,
)
+from resource_inventory.resource_manager import ResourceManager
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)
+ instantiate_userprofile(self.loginuser)
lab_user = kwargs.get("lab_user", instantiate_user(True))
self.lab = instantiate_lab(lab_user)
@@ -45,6 +61,179 @@ class BookingContextData(object):
self.pubnet = instantiate_publicnet(10, self.lab)
+"""
+Info for instantiate_booking() function:
+[topology] argument structure:
+ the [topology] argument should describe the structure of the pod
+ the top level should be a dictionary, with each key being a hostname
+ each value in the top level should be a dictionary with two keys:
+ "type" should map to a host profile instance
+ "nets" should map to a list of interfaces each with a list of
+ dictionaries each defining a network in the format
+ { "name": "netname", "tagged": True|False, "public": True|False }
+ each network is defined if a matching name is not found
+
+ sample argument structure:
+ topology={
+ "host1": {
+ "type": instanceOf HostProfile,
+ "role": instanceOf OPNFVRole
+ "image": instanceOf Image
+ "nets": [
+ 0: [
+ 0: { "name": "public", "tagged": True, "public": True },
+ 1: { "name": "private", "tagged": False, "public": False },
+ ]
+ 1: []
+ ]
+ }
+ }
+"""
+
+
+def instantiate_booking(owner,
+ start,
+ end,
+ booking_identifier,
+ lab=Lab.objects.first(),
+ purpose="purposetext",
+ project="projecttext",
+ collaborators=[],
+ topology={},
+ installer=None,
+ scenario=None):
+ (grb, host_set) = instantiate_grb(topology, owner, lab, booking_identifier)
+ cb = instantiate_cb(grb, owner, booking_identifier, topology, host_set, installer, scenario)
+
+ resource = ResourceManager.getInstance().convertResourceBundle(grb, lab, cb)
+
+ booking = Booking()
+
+ booking.resource = resource
+ if not resource:
+ raise Exception("Resource not created")
+ booking.config_bundle = cb
+ booking.start = start
+ booking.end = end
+ booking.owner = owner
+ booking.purpose = purpose
+ booking.project = project
+ booking.lab = lab
+ booking.save()
+
+ return booking
+
+
+def instantiate_cb(grb,
+ owner,
+ booking_identifier,
+ topology={},
+ host_set={},
+ installer=None,
+ scenario=None):
+ cb = ConfigBundle()
+ cb.owner = owner
+ cb.name = str(booking_identifier) + "_cb"
+ cb.description = "cb generated by instantiate_cb() method"
+ cb.save()
+
+ opnfvconfig = OPNFVConfig()
+ opnfvconfig.installer = installer
+ opnfvconfig.scenario = scenario
+ opnfvconfig.bundle = cb
+ opnfvconfig.save()
+
+ # generate host configurations based on topology and host set
+ for hostname, host_info in topology.items():
+ hconf = HostConfiguration()
+ hconf.bundle = cb
+ hconf.host = host_set[hostname]
+ hconf.image = host_info["image"]
+ hconf.opnfvRole = host_info["role"]
+ hconf.save()
+ return cb
+
+
+def instantiate_grb(topology,
+ owner,
+ lab,
+ booking_identifier):
+
+ grb = GenericResourceBundle(owner=owner, lab=lab)
+ grb.name = str(booking_identifier) + "_grb"
+ grb.description = "grb generated by instantiate_grb() method"
+ grb.save()
+
+ networks = {}
+ host_set = {}
+
+ for hostname in topology.keys():
+ info = topology[hostname]
+ host_profile = info["type"]
+
+ # need to construct host from hostname and type
+ ghost = instantiate_ghost(grb, host_profile, hostname)
+ host_set[hostname] = ghost
+
+ ghost.save()
+
+ # set up networks
+ nets = info["nets"]
+ for interface_index, interface_profile in enumerate(host_profile.interfaceprofile.all()):
+ generic_interface = GenericInterface()
+ generic_interface.host = ghost
+ generic_interface.profile = interface_profile
+ generic_interface.save()
+
+ netconfig = nets[interface_index]
+ for network_index, network_info in enumerate(netconfig):
+ network_name = network_info["name"]
+ network = None
+ if network_name in networks:
+ network = networks[network_name]
+ else:
+ network = Network()
+ network.name = network_name
+ network.vlan_id = lab.vlan_manager.get_vlan()
+ network.save()
+ networks[network_name] = network
+ if network_info["public"]:
+ public_net = lab.vlan_manager.get_public_vlan()
+ if not public_net:
+ raise Exception("No more public networks available")
+ lab.vlan_manager.reserve_public_vlan(public_net.vlan)
+ network.vlan_id = public_net.vlan
+ else:
+ private_net = lab.vlan_manager.get_vlan()
+ if not private_net:
+ raise Exception("No more generic vlans are available")
+ lab.vlan_manager.reserve_vlans([private_net])
+ network.vlan_id = private_net
+
+ vlan = Vlan()
+ vlan.vlan_id = network.vlan_id
+ vlan.public = network_info["public"]
+ vlan.tagged = network_info["tagged"]
+ vlan.save()
+ generic_interface.vlans.add(vlan)
+
+ return (grb, host_set)
+
+
+def instantiate_ghost(grb, host_profile, 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=grb, name=hostname)
+ gresource.save()
+
+ ghost = GenericHost()
+ ghost.resource = gresource
+ ghost.profile = host_profile
+ ghost.save()
+
+ return ghost
+
+
def instantiate_user(is_superuser,
username="testuser",
password="testpassword",
@@ -58,16 +247,17 @@ def instantiate_user(is_superuser,
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
+def instantiate_userprofile(user, email_addr="email@email.com", company="company", full_name="John Doe", booking_privledge=True, ssh_file=None):
+ up = UserProfile()
+ up.email_address = email_addr
+ up.company = company
+ up.full_name = full_name
+ up.booking_privledge = booking_privledge
+ up.user = user
+ up.save()
+ up.ssh_public_key.save("user_ssh_key", ssh_file if ssh_file else ContentFile("public key content string"))
- userprofile.save()
-
- return user
+ return up
def instantiate_vlanmanager(vlans=None,