summaryrefslogtreecommitdiffstats
path: root/dashboard/src/api
diff options
context:
space:
mode:
authorParker Berberian <pberberian@iol.unh.edu>2018-10-10 16:06:47 -0400
committerParker Berberian <pberberian@iol.unh.edu>2018-10-15 13:16:11 -0400
commit25275685e9a735e51fae8b1a936ba5733f6fb770 (patch)
treec3af41d1986c0812ca7ed059c16c7c7c7bd354cf /dashboard/src/api
parentf2e83b24260b018bb7cc30421eda6c9a8cebc309 (diff)
Lab as a Service 2.0
See changes here: https://wiki.opnfv.org/display/INF/Pharos+Laas Change-Id: I59ada5f98e70a28d7f8c14eab3239597e236ca26 Signed-off-by: Sawyer Bergeron <sbergeron@iol.unh.edu> Signed-off-by: Parker Berberian <pberberian@iol.unh.edu>
Diffstat (limited to 'dashboard/src/api')
-rw-r--r--dashboard/src/api/admin.py28
-rw-r--r--dashboard/src/api/migrations/0001_initial.py185
-rw-r--r--dashboard/src/api/migrations/__init__.py4
-rw-r--r--dashboard/src/api/models.py713
-rw-r--r--dashboard/src/api/serializers.py53
-rw-r--r--dashboard/src/api/serializers/__init__.py8
-rw-r--r--dashboard/src/api/serializers/booking_serializer.py156
-rw-r--r--dashboard/src/api/serializers/old_serializers.py28
-rw-r--r--dashboard/src/api/tests/__init__.py8
-rw-r--r--dashboard/src/api/tests/test_serializers.py213
-rw-r--r--dashboard/src/api/urls.py13
-rw-r--r--dashboard/src/api/views.py101
12 files changed, 1433 insertions, 77 deletions
diff --git a/dashboard/src/api/admin.py b/dashboard/src/api/admin.py
new file mode 100644
index 0000000..f1bc70a
--- /dev/null
+++ b/dashboard/src/api/admin.py
@@ -0,0 +1,28 @@
+##############################################################################
+# 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
+##############################################################################
+
+
+from django.apps import AppConfig
+from django.contrib import admin
+
+from api.models import *
+
+
+class ApiConfig(AppConfig):
+ name = 'apiJobs'
+
+admin.site.register(Job)
+admin.site.register(OpnfvApiConfig)
+admin.site.register(HardwareConfig)
+admin.site.register(NetworkConfig)
+admin.site.register(SoftwareConfig)
+admin.site.register(AccessRelation)
+admin.site.register(SoftwareRelation)
+admin.site.register(HostHardwareRelation)
+admin.site.register(HostNetworkRelation)
diff --git a/dashboard/src/api/migrations/0001_initial.py b/dashboard/src/api/migrations/0001_initial.py
new file mode 100644
index 0000000..abe6f5e
--- /dev/null
+++ b/dashboard/src/api/migrations/0001_initial.py
@@ -0,0 +1,185 @@
+##############################################################################
+# 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
+##############################################################################
+# Generated by Django 2.1 on 2018-09-14 14:48
+
+import api.models
+from django.conf import settings
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ initial = True
+
+ dependencies = [
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+ ('booking', '__first__'),
+ ('resource_inventory', '__first__'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='AccessRelation',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('status', models.IntegerField(default=0)),
+ ('task_id', models.CharField(default=api.models.get_task_uuid, max_length=37)),
+ ('lab_token', models.CharField(default='null', max_length=50)),
+ ('message', models.TextField(default='')),
+ ],
+ options={
+ 'abstract': False,
+ },
+ ),
+ migrations.CreateModel(
+ name='HostHardwareRelation',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('status', models.IntegerField(default=0)),
+ ('task_id', models.CharField(default=api.models.get_task_uuid, max_length=37)),
+ ('lab_token', models.CharField(default='null', max_length=50)),
+ ('message', models.TextField(default='')),
+ ('host', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='resource_inventory.Host')),
+ ],
+ options={
+ 'abstract': False,
+ },
+ ),
+ migrations.CreateModel(
+ name='HostNetworkRelation',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('status', models.IntegerField(default=0)),
+ ('task_id', models.CharField(default=api.models.get_task_uuid, max_length=37)),
+ ('lab_token', models.CharField(default='null', max_length=50)),
+ ('message', models.TextField(default='')),
+ ('host', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='resource_inventory.Host')),
+ ],
+ options={
+ 'abstract': False,
+ },
+ ),
+ migrations.CreateModel(
+ name='Job',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('status', models.IntegerField(default=0)),
+ ('delta', models.TextField()),
+ ('complete', models.BooleanField(default=False)),
+ ('booking', models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, to='booking.Booking')),
+ ],
+ ),
+ migrations.CreateModel(
+ name='OpnfvApiConfig',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('installer', models.CharField(max_length=100)),
+ ('scenario', models.CharField(max_length=100)),
+ ('delta', models.TextField()),
+ ('roles', models.ManyToManyField(to='resource_inventory.Host')),
+ ],
+ ),
+ migrations.CreateModel(
+ name='SoftwareRelation',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('status', models.IntegerField(default=0)),
+ ('task_id', models.CharField(default=api.models.get_task_uuid, max_length=37)),
+ ('lab_token', models.CharField(default='null', max_length=50)),
+ ('message', models.TextField(default='')),
+ ('job', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.Job')),
+ ],
+ options={
+ 'abstract': False,
+ },
+ ),
+ migrations.CreateModel(
+ name='TaskConfig',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ],
+ ),
+ migrations.CreateModel(
+ name='AccessConfig',
+ fields=[
+ ('taskconfig_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='api.TaskConfig')),
+ ('access_type', models.CharField(max_length=50)),
+ ('revoke', models.BooleanField(default=False)),
+ ('context', models.TextField(default='')),
+ ('delta', models.TextField()),
+ ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
+ ],
+ bases=('api.taskconfig',),
+ ),
+ migrations.CreateModel(
+ name='HardwareConfig',
+ fields=[
+ ('taskconfig_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='api.TaskConfig')),
+ ('image', models.CharField(default='defimage', max_length=100)),
+ ('power', models.CharField(default='off', max_length=100)),
+ ('hostname', models.CharField(default='hostname', max_length=100)),
+ ('ipmi_create', models.BooleanField(default=False)),
+ ('delta', models.TextField()),
+ ],
+ bases=('api.taskconfig',),
+ ),
+ migrations.CreateModel(
+ name='NetworkConfig',
+ fields=[
+ ('taskconfig_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='api.TaskConfig')),
+ ('delta', models.TextField()),
+ ('interfaces', models.ManyToManyField(to='resource_inventory.Interface')),
+ ],
+ bases=('api.taskconfig',),
+ ),
+ migrations.CreateModel(
+ name='SoftwareConfig',
+ fields=[
+ ('taskconfig_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='api.TaskConfig')),
+ ('opnfv', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.OpnfvApiConfig')),
+ ],
+ bases=('api.taskconfig',),
+ ),
+ migrations.AddField(
+ model_name='hostnetworkrelation',
+ name='job',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.Job'),
+ ),
+ migrations.AddField(
+ model_name='hosthardwarerelation',
+ name='job',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.Job'),
+ ),
+ migrations.AddField(
+ model_name='accessrelation',
+ name='job',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.Job'),
+ ),
+ migrations.AddField(
+ model_name='softwarerelation',
+ name='config',
+ field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='api.SoftwareConfig'),
+ ),
+ migrations.AddField(
+ model_name='hostnetworkrelation',
+ name='config',
+ field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='api.NetworkConfig'),
+ ),
+ migrations.AddField(
+ model_name='hosthardwarerelation',
+ name='config',
+ field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='api.HardwareConfig'),
+ ),
+ migrations.AddField(
+ model_name='accessrelation',
+ name='config',
+ field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='api.AccessConfig'),
+ ),
+ ]
diff --git a/dashboard/src/api/migrations/__init__.py b/dashboard/src/api/migrations/__init__.py
index b5914ce..e0408fa 100644
--- a/dashboard/src/api/migrations/__init__.py
+++ b/dashboard/src/api/migrations/__init__.py
@@ -1,10 +1,8 @@
##############################################################################
-# Copyright (c) 2016 Max Breitenfeldt and others.
+# 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
##############################################################################
-
-
diff --git a/dashboard/src/api/models.py b/dashboard/src/api/models.py
new file mode 100644
index 0000000..f1e9130
--- /dev/null
+++ b/dashboard/src/api/models.py
@@ -0,0 +1,713 @@
+##############################################################################
+# Copyright (c) 2018 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 django.db import models
+from django.core.exceptions import PermissionDenied
+
+import json
+import uuid
+
+from resource_inventory.models import *
+from booking.models import Booking
+
+
+class JobStatus(object):
+ NEW = 0
+ CURRENT = 100
+ DONE = 200
+ ERROR = 300
+
+
+class LabManagerTracker(object):
+
+ @classmethod
+ def get(cls, lab_name, token):
+ """
+ Takes in a lab name (from a url path)
+ returns a lab manager instance for that lab, if it exists
+ """
+ try:
+ lab = Lab.objects.get(name=lab_name)
+ except:
+ raise PermissionDenied("Lab not found")
+ if lab.api_token == token:
+ return LabManager(lab)
+ raise PermissionDenied("Lab not authorized")
+
+
+class LabManager(object):
+ """
+ This is the class that will ultimately handle all REST calls to
+ lab endpoints.
+ handles jobs, inventory, status, etc
+ may need to create helper classes
+ """
+
+ def __init__(self, lab):
+ self.lab = lab
+
+ def get_profile(self):
+ prof = {}
+ prof['name'] = self.lab.name
+ prof['contact'] = {
+ "phone": self.lab.contact_phone,
+ "email": self.lab.contact_email
+ }
+ prof['host_count'] = []
+ for host in HostProfile.objects.filter(labs=self.lab):
+ count = Host.objects.filter(profile=host, lab=self.lab).count()
+ prof['host_count'].append({
+ "type": host.name,
+ "count": count
+ })
+ return prof
+
+ def get_inventory(self):
+ inventory = {}
+ hosts = Host.objects.filter(lab=self.lab)
+ images = Image.objects.filter(from_lab=self.lab)
+ profiles = HostProfile.objects.filter(labs=self.lab)
+ inventory['hosts'] = self.serialize_hosts(hosts)
+ inventory['images'] = self.serialize_images(images)
+ inventory['host_types'] = self.serialize_host_profiles(profiles)
+ return inventory
+
+ def get_status(self):
+ return {"status": self.lab.status}
+
+ def set_status(self, payload):
+ {}
+
+ def get_current_jobs(self):
+ jobs = Job.objects.filter(booking__lab=self.lab)
+
+ return self.serialize_jobs(jobs, status=JobStatus.CURRENT)
+
+ def get_new_jobs(self):
+ jobs = Job.objects.filter(booking__lab=self.lab)
+
+ return self.serialize_jobs(jobs, status=JobStatus.NEW)
+
+ def get_done_jobs(self):
+ jobs = Job.objects.filter(booking__lab=self.lab)
+
+ return self.serialize_jobs(jobs, status=JobStatus.DONE)
+
+ def get_job(self, jobid):
+ return Job.objects.get(pk=jobid).to_dict()
+
+ def update_job(self, jobid, data):
+ {}
+
+ def serialize_jobs(self, jobs, status=JobStatus.NEW):
+ job_ser = []
+ for job in jobs:
+ jsonized_job = job.get_delta(status)
+ if len(jsonized_job['payload']) < 1:
+ continue
+ job_ser.append(jsonized_job)
+
+ return job_ser
+
+ def serialize_hosts(self, hosts):
+ host_ser = []
+ for host in hosts:
+ h = {}
+ h['interfaces'] = []
+ h['hostname'] = host.name
+ h['host_type'] = host.profile.name
+ for iface in host.interfaces.all():
+ eth = {}
+ eth['mac'] = iface.mac_address
+ eth['busaddr'] = iface.bus_address
+ eth['name'] = iface.name
+ eth['switchport'] = {"switch_name": iface.switch_name, "port_name": iface.port_name}
+ h['interfaces'].append(eth)
+ return host_ser
+
+ def serialize_images(self, images):
+ images_ser = []
+ for image in images:
+ images_ser.append({
+ "name": image.name,
+ "lab_id": image.lab_id,
+ "dashboard_id": image.id
+ })
+ return images_ser
+
+ def serialize_host_profiles(self, profiles):
+ profile_ser = []
+ for profile in profiles:
+ p = {}
+ p['cpu'] = {
+ "cores": profile.cpuprofile.first().cores,
+ "arch": profile.cpuprofile.first().architecture,
+ "cpus": profile.cpuprofile.first().cpus,
+ }
+ p['disks'] = []
+ for disk in profile.storageprofile.all():
+ d = {
+ "size": disk.size,
+ "type": disk.media_type,
+ "name": disk.name
+ }
+ p['disks'].append(d)
+ p['description'] = profile.description
+ p['interfaces'] = []
+ for iface in profile.interfaceprofile.all():
+ p['interfaces'].append({
+ "speed": iface.speed,
+ "name": iface.name
+ })
+
+ p['ram'] = {"amount": profile.ramprofile.first().amount}
+ p['name'] = profile.name
+ profile_ser.append(p)
+ return profile_ser
+
+
+class Job(models.Model):
+ """
+ This is the class that is serialized and put into the api
+ """
+ booking = models.OneToOneField(Booking, on_delete=models.CASCADE, null=True)
+ status = models.IntegerField(default=JobStatus.NEW)
+ complete = models.BooleanField(default=False)
+
+ def to_dict(self):
+ d = {}
+ j = {}
+ j['id'] = self.id
+ for relation in AccessRelation.objects.filter(job=self):
+ if 'access' not in d:
+ d['access'] = {}
+ d['access'][relation.task_id] = relation.config.to_dict()
+ for relation in SoftwareRelation.objects.filter(job=self):
+ if 'software' not in d:
+ d['software'] = {}
+ d['software'][relation.task_id] = relation.config.to_dict()
+ for relation in HostHardwareRelation.objects.filter(job=self):
+ if 'hardware' not in d:
+ d['hardware'] = {}
+ d['hardware'][relation.task_id] = relation.config.to_dict()
+ for relation in HostNetworkRelation.objects.filter(job=self):
+ if 'network' not in d:
+ d['network'] = {}
+ d['network'][relation.task_id] = relation.config.to_dict()
+
+ j['payload'] = d
+
+ return j
+
+ def get_tasklist(self, status="all"):
+ tasklist = []
+ clist = [HostHardwareRelation, AccessRelation, HostNetworkRelation, SoftwareRelation]
+ if status == "all":
+ for cls in clist:
+ tasklist += list(cls.objects.filter(job=self))
+ else:
+ for cls in clist:
+ tasklist += list(cls.objects.filter(job=self).filter(status=status))
+ return tasklist
+
+ def get_delta(self, status):
+ d = {}
+ j = {}
+ j['id'] = self.id
+ for relation in AccessRelation.objects.filter(job=self).filter(status=status):
+ if 'access' not in d:
+ d['access'] = {}
+ d['access'][relation.task_id] = relation.config.get_delta()
+ for relation in SoftwareRelation.objects.filter(job=self).filter(status=status):
+ if 'software' not in d:
+ d['software'] = {}
+ d['software'][relation.task_id] = relation.config.get_delta()
+ for relation in HostHardwareRelation.objects.filter(job=self).filter(status=status):
+ if 'hardware' not in d:
+ d['hardware'] = {}
+ d['hardware'][relation.task_id] = relation.config.get_delta()
+ for relation in HostNetworkRelation.objects.filter(job=self).filter(status=status):
+ if 'network' not in d:
+ d['network'] = {}
+ d['network'][relation.task_id] = relation.config.get_delta()
+
+ j['payload'] = d
+ return j
+
+ def to_json(self):
+ return json.dumps(self.to_dict())
+
+
+class TaskConfig(models.Model):
+ def to_dict(self):
+ pass
+
+ def get_delta(self):
+ pass
+
+ def to_json(self):
+ return json.dumps(self.to_dict())
+
+ def clear_delta(self):
+ self.delta = '{}'
+
+class OpnfvApiConfig(models.Model):
+
+ installer = models.CharField(max_length=100)
+ scenario = models.CharField(max_length=100)
+ roles = models.ManyToManyField(Host)
+ delta = models.TextField()
+
+ def to_dict(self):
+ d = {}
+ if self.installer:
+ d['installer'] = self.installer
+ if self.scenario:
+ d['scenario'] = self.scenario
+
+ hosts = self.roles.all()
+ if hosts.exists():
+ d['roles'] = []
+ for host in self.roles.all():
+ d['roles'].append({host.labid: host.config.opnfvRole.name})
+
+ return d
+
+ def to_json(self):
+ return json.dumps(self.to_dict())
+
+ def set_installer(self, installer):
+ self.installer = installer
+ d = json.loads(self.delta)
+ d['installer'] = installer
+ self.delta = json.dumps(d)
+
+ def set_scenario(self, scenario):
+ self.scenario = scenario
+ d = json.loads(self.delta)
+ d['scenario'] = scenario
+ self.delta = json.dumps(d)
+
+ def add_role(self, host):
+ self.roles.add(host)
+ d = json.loads(self.delta)
+ if 'role' not in d:
+ d['role'] = []
+ d['roles'].append({host.labid: host.config.opnfvRole.name})
+ self.delta = json.dumps(d)
+
+ def clear_delta(self):
+ self.delta = '{}'
+
+ def get_delta(self):
+ if not self.delta:
+ self.delta = self.to_json()
+ self.save()
+ return json.loads(self.delta)
+
+class AccessConfig(TaskConfig):
+ access_type = models.CharField(max_length=50)
+ user = models.ForeignKey(User, on_delete=models.CASCADE)
+ revoke = models.BooleanField(default=False)
+ context = models.TextField(default="")
+ delta = models.TextField()
+
+ def to_dict(self):
+ d = {}
+ d['access_type'] = self.access_type
+ d['user'] = self.user.id
+ d['revoke'] = self.revoke
+ d['context'] = self.context
+ return d
+
+ def get_delta(self):
+ if not self.delta:
+ self.delta = self.to_json()
+ self.save()
+ d = json.loads(self.delta)
+ d["lab_token"] = self.accessrelation.lab_token
+
+ return d
+
+ def to_json(self):
+ return json.dumps(self.to_dict())
+
+ def clear_delta(self):
+ d = {}
+ d["lab_token"] = self.accessrelation.lab_token
+ self.delta = json.dumps(d)
+
+ def set_access_type(self, access_type):
+ self.access_type = access_type
+ d = json.loads(self.delta)
+ d['access_type'] = access_type
+ self.delta = json.dumps(d)
+
+ def set_user(self, user):
+ self.user = user
+ d = json.loads(self.delta)
+ d['user'] = self.user.id
+ self.delta = json.dumps(d)
+
+ def set_revoke(self, revoke):
+ self.revoke = revoke
+ d = json.loads(self.delta)
+ d['revoke'] = revoke
+ self.delta = json.dumps(d)
+
+ def set_context(self, context):
+ self.context = context
+ d = json.loads(self.delta)
+ d['context'] = context
+ self.delta = json.dumps(d)
+
+class SoftwareConfig(TaskConfig):
+ """
+ handled opnfv installations, etc
+ """
+ opnfv = models.ForeignKey(OpnfvApiConfig, on_delete=models.CASCADE)
+
+ def to_dict(self):
+ d = {}
+ if self.opnfv:
+ d['opnfv'] = self.opnfv.to_dict()
+
+ d["lab_token"] = self.softwarerelation.lab_token
+ self.delta = json.dumps(d)
+
+ return d
+
+ def get_delta(self):
+ d = {}
+ d['opnfv'] = self.opnfv.get_delta()
+ d['lab_token'] = self.softwarerelation.lab_token
+
+ return d
+
+ def clear_delta(self):
+ self.opnfv.clear_delta()
+
+ def to_json(self):
+ return json.dumps(self.to_dict())
+
+class HardwareConfig(TaskConfig):
+ """
+ handles imaging, user accounts, etc
+ """
+ image = models.CharField(max_length=100, default="defimage")
+ power = models.CharField(max_length=100, default="off")
+ hostname = models.CharField(max_length=100, default="hostname")
+ ipmi_create = models.BooleanField(default=False)
+ delta = models.TextField()
+
+ def to_dict(self):
+ d = {}
+ d['image'] = self.image
+ d['power'] = self.power
+ d['hostname'] = self.hostname
+ d['ipmi_create'] = str(self.ipmi_create)
+ d['id'] = self.hosthardwarerelation.host.labid
+ return d
+
+ def to_json(self):
+ return json.dumps(self.to_dict())
+
+ def get_delta(self):
+ if not self.delta:
+ self.delta = self.to_json()
+ self.save()
+ d = json.loads(self.delta)
+ d['lab_token'] = self.hosthardwarerelation.lab_token
+ return d
+
+ def clear_delta(self):
+ d = {}
+ d["id"] = self.hosthardwarerelation.host.labid
+ d["lab_token"] = self.hosthardwarerelation.lab_token
+ self.delta = json.dumps(d)
+
+ def set_image(self, image):
+ self.image = image
+ d = json.loads(self.delta)
+ d['image'] = self.image
+ self.delta = json.dumps(d)
+
+ def set_power(self, power):
+ self.power = power
+ d = json.loads(self.delta)
+ d['power'] = power
+ self.delta = json.dumps(d)
+
+ def set_hostname(self, hostname):
+ self.hostname = hostname
+ d = json.loads(self.delta)
+ d['hostname'] = hostname
+ self.delta = json.dumps(d)
+
+ def set_ipmi_create(self, ipmi_create):
+ self.ipmi_create = ipmi_create
+ d = json.loads(self.delta)
+ d['ipmi_create'] = ipmi_create
+ self.delta = json.dumps(d)
+
+
+class NetworkConfig(TaskConfig):
+ """
+ handles network configuration
+ """
+ interfaces = models.ManyToManyField(Interface)
+ delta = models.TextField()
+
+ def to_dict(self):
+ d = {}
+ hid = self.hostnetworkrelation.host.labid
+ d[hid] = {}
+ for interface in self.interfaces.all():
+ d[hid][interface.mac_address] = []
+ for vlan in interface.config.all():
+ d[hid][interface.mac_address].append({"vlan_id": vlan.vlan_id, "tagged": vlan.tagged})
+
+ return d
+
+ def to_json(self):
+ return json.dumps(self.to_dict())
+
+ def get_delta(self):
+ if not self.delta:
+ self.delta = self.to_json()
+ self.save()
+ d = json.loads(self.delta)
+ d['lab_token'] = self.hostnetworkrelation.lab_token
+ return d
+
+ def clear_delta(self):
+ pass
+
+ def add_interface(self, interface):
+ self.interfaces.add(interface)
+ d = json.loads(self.delta)
+ hid = self.hostnetworkrelation.host.labid
+ if hid not in d:
+ d[hid] = {}
+ d[hid][interface.mac_address] = []
+ for vlan in interface.config.all():
+ d[hid][interface.mac_address].append({"vlan_id": vlan.vlan_id, "tagged": vlan.tagged})
+ self.delta = json.dumps(d)
+
+
+def get_task(task_id):
+ for taskclass in [AccessRelation, SoftwareRelation, HostHardwareRelation, HostNetworkRelation]:
+ try:
+ ret = taskclass.objects.get(task_id=task_id)
+ return ret
+ except taskclass.DoesNotExist:
+ pass
+ from django.core.exceptions import ObjectDoesNotExist
+ raise ObjectDoesNotExist("Could not find matching TaskRelation instance")
+
+
+def get_task_uuid():
+ return str(uuid.uuid4())
+
+
+class TaskRelation(models.Model):
+ status = models.IntegerField(default=JobStatus.NEW)
+ job = models.ForeignKey(Job, on_delete=models.CASCADE)
+ config = models.OneToOneField(TaskConfig, on_delete=models.CASCADE)
+ task_id = models.CharField(default=get_task_uuid, max_length=37)
+ lab_token = models.CharField(default="null", max_length=50)
+ message = models.TextField(default="")
+
+ def delete(self, *args, **kwargs):
+ self.config.delete()
+ return super(self.__class__, self).delete(*args, **kwargs)
+
+ def type_str(self):
+ return "Generic Task"
+
+ class Meta:
+ abstract = True
+
+
+class AccessRelation(TaskRelation):
+ config = models.OneToOneField(AccessConfig, on_delete=models.CASCADE)
+
+ def type_str(self):
+ return "Access Task"
+
+ def delete(self, *args, **kwargs):
+ self.config.delete()
+ return super(self.__class__, self).delete(*args, **kwargs)
+
+
+class SoftwareRelation(TaskRelation):
+ config = models.OneToOneField(SoftwareConfig, on_delete=models.CASCADE)
+
+ def type_str(self):
+ return "Software Configuration Task"
+
+ def delete(self, *args, **kwargs):
+ self.config.delete()
+ return super(self.__class__, self).delete(*args, **kwargs)
+
+
+class HostHardwareRelation(TaskRelation):
+ host = models.ForeignKey(Host, on_delete=models.CASCADE)
+ config = models.OneToOneField(HardwareConfig, on_delete=models.CASCADE)
+
+ def type_str(self):
+ return "Hardware Configuration Task"
+
+ def get_delta(self):
+ return self.config.to_dict()
+
+ def delete(self, *args, **kwargs):
+ self.config.delete()
+ return super(self.__class__, self).delete(*args, **kwargs)
+
+
+class HostNetworkRelation(TaskRelation):
+ host = models.ForeignKey(Host, on_delete=models.CASCADE)
+ config = models.OneToOneField(NetworkConfig, on_delete=models.CASCADE)
+
+ def type_str(self):
+ return "Network Configuration Task"
+
+ def delete(self, *args, **kwargs):
+ self.config.delete()
+ return super(self.__class__, self).delete(*args, **kwargs)
+
+
+class JobFactory(object):
+
+ @classmethod
+ def makeCompleteJob(cls, booking):
+ hosts = Host.objects.filter(bundle=booking.resource)
+ job = None
+ try:
+ job = Job.objects.get(booking=booking)
+ except:
+ job = Job.objects.create(status=JobStatus.NEW, booking=booking)
+ cls.makeHardwareConfigs(
+ hosts=hosts,
+ job=job
+ )
+ cls.makeNetworkConfigs(
+ hosts=hosts,
+ job=job
+ )
+ cls.makeSoftware(
+ hosts=hosts,
+ job=job
+ )
+ cls.makeAccessConfig(
+ users=booking.collaborators.all(),
+ access_type="vpn",
+ revoke=False,
+ job=job
+ )
+ cls.makeAccessConfig(
+ users=[booking.owner],
+ access_type="vpn",
+ revoke=False,
+ job=job
+ )
+
+ @classmethod
+ def makeHardwareConfigs(cls, hosts=[], job=Job()):
+ for host in hosts:
+ hardware_config = None
+ try:
+ hardware_config = HardwareConfig.objects.get(relation__host=host)
+ except:
+ hardware_config = HardwareConfig()
+
+ relation = HostHardwareRelation()
+ relation.host = host
+ relation.job = job
+ relation.config = hardware_config
+ relation.config.save()
+ relation.config = relation.config
+ relation.save()
+
+ hardware_config.clear_delta()
+ hardware_config.set_image(host.config.image.lab_id)
+ hardware_config.set_hostname(host.template.resource.name)
+ hardware_config.set_power("on")
+ hardware_config.set_ipmi_create(True)
+ hardware_config.save()
+
+ @classmethod
+ def makeAccessConfig(cls, users, access_type, revoke=False, job=Job()):
+ for user in users:
+ relation = AccessRelation()
+ relation.job = job
+ config = AccessConfig()
+ config.access_type = access_type
+ config.user = user
+ config.save()
+ relation.config = config
+ relation.save()
+ config.clear_delta()
+ config.set_access_type(access_type)
+ config.set_revoke(revoke)
+ config.set_user(user)
+ config.save()
+
+ @classmethod
+ def makeNetworkConfigs(cls, hosts=[], job=Job()):
+ for host in hosts:
+ network_config = None
+ try:
+ network_config = NetworkConfig.objects.get(relation__host=host)
+ except:
+ network_config = NetworkConfig.objects.create()
+
+ relation = HostNetworkRelation()
+ relation.host = host
+ relation.job = job
+ network_config.save()
+ relation.config = network_config
+ relation.save()
+ network_config.clear_delta()
+
+ for interface in host.interfaces.all():
+ network_config.add_interface(interface)
+ network_config.save()
+
+ @classmethod
+ def makeSoftware(cls, hosts=[], job=Job()):
+ def init_config(host):
+ opnfv_config = OpnfvApiConfig()
+ if host is not None:
+ opnfv = host.config.bundle.opnfv_config.first()
+ opnfv_config.installer = opnfv.installer.name
+ opnfv_config.scenario = opnfv.scenario.name
+ opnfv_config.save()
+ return opnfv_config
+
+ try:
+ host = None
+ if len(hosts) > 0:
+ host = hosts[0]
+ opnfv_config = init_config(host)
+
+ for host in hosts:
+ opnfv_config.roles.add(host)
+ software_config = SoftwareConfig.objects.create(opnfv=opnfv_config)
+ software_config.save()
+ software_relation = SoftwareRelation.objects.create(job=job, config=software_config)
+ software_relation.save()
+ return software_relation
+ except:
+ return None
+
+ def makeAccess(cls, user, access_type, revoke):
+ pass
diff --git a/dashboard/src/api/serializers.py b/dashboard/src/api/serializers.py
deleted file mode 100644
index 10e1975..0000000
--- a/dashboard/src/api/serializers.py
+++ /dev/null
@@ -1,53 +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
-##############################################################################
-
-
-from rest_framework import serializers
-
-from account.models import UserProfile
-from notifier.models import Notifier
-from booking.models import Booking
-from dashboard.models import Server, Resource, ResourceStatus
-
-class BookingSerializer(serializers.ModelSerializer):
- installer_name = serializers.CharField(source='installer.name')
- scenario_name = serializers.CharField(source='scenario.name')
- opsys_name = serializers.CharField(source='opsys.name')
-
- class Meta:
- model = Booking
- fields = ('id', 'changeid', 'reset', 'user', 'resource_id', 'opsys_name', 'start', 'end', 'installer_name', 'scenario_name', 'purpose')
-
-
-class ServerSerializer(serializers.ModelSerializer):
- class Meta:
- model = Server
- fields = ('id', 'resource_id', 'name', 'model', 'cpu', 'ram', 'storage')
-
-
-class ResourceSerializer(serializers.ModelSerializer):
- class Meta:
- model = Resource
- fields = ('id', 'name', 'description', 'resource_lab', 'url', 'server_set', 'dev_pod')
-
-class ResourceStatusSerializer(serializers.ModelSerializer):
- class Meta:
- model = ResourceStatus
- fields = ('id', 'resource', 'timestamp','type', 'title', 'content')
-
-class NotifierSerializer(serializers.ModelSerializer):
- class Meta:
- model = Notifier
- fields = ('id', 'title', 'content', 'user', 'sender', 'message_type', 'msg_sent')
-
-class UserSerializer(serializers.ModelSerializer):
- username = serializers.CharField(source='user.username')
- class Meta:
- model = UserProfile
- fields = ('user', 'username', 'ssh_public_key', 'pgp_public_key', 'email_addr')
diff --git a/dashboard/src/api/serializers/__init__.py b/dashboard/src/api/serializers/__init__.py
new file mode 100644
index 0000000..e0408fa
--- /dev/null
+++ b/dashboard/src/api/serializers/__init__.py
@@ -0,0 +1,8 @@
+##############################################################################
+# 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
+##############################################################################
diff --git a/dashboard/src/api/serializers/booking_serializer.py b/dashboard/src/api/serializers/booking_serializer.py
new file mode 100644
index 0000000..e891de4
--- /dev/null
+++ b/dashboard/src/api/serializers/booking_serializer.py
@@ -0,0 +1,156 @@
+##############################################################################
+# 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 rest_framework import serializers
+
+from resource_inventory.models import *
+
+class BookingField(serializers.Field):
+
+ def to_representation(self, booking):
+ """
+ Takes in a booking object.
+ Returns a dictionary of primitives representing that booking
+ """
+ ser = {}
+ ser['id'] = booking.id
+ # main loop to grab relevant info out of booking
+ host_configs = {} # mapping hostname -> config
+ networks = {} # mapping vlan id -> network_hosts
+ for host in booking.resource.hosts.all():
+ host_configs[host.name] = HostConfiguration.objects.get(host=host.template)
+ if "jumphost" not in ser and host_configs[host.name].opnfvRole.name.lower() == "jumphost":
+ ser['jumphost'] = host.name
+ #host is a Host model
+ for i in range(len(host.interfaces.all())):
+ interface = host.interfaces.all()[i]
+ #interface is an Interface model
+ for vlan in interface.config.all():
+ #vlan is Vlan model
+ if vlan.id not in networks:
+ networks[vlan.id] = []
+ net_host = {"hostname": host.name, "tagged": vlan.tagged, "interface":i}
+ networks[vlan.id].append(net_host)
+ #creates networking object of proper form
+ networking = []
+ for vlanid in networks:
+ network = {}
+ network['vlan_id'] = vlanid
+ network['hosts'] = networks[vlanid]
+
+ ser['networking'] = networking
+
+ #creates hosts object of correct form
+ hosts = []
+ for hostname in host_configs:
+ host = {"hostname": hostname}
+ host['deploy_image'] = True # TODO?
+ image = host_configs[hostname].image
+ host['image'] = {
+ "name": image.name,
+ "lab_id": image.lab_id,
+ "dashboard_id": image.id
+ }
+ hosts.append(host)
+
+ ser['hosts'] = hosts
+
+ return ser
+
+ def to_internal_value(self, data):
+ """
+ Takes in a dictionary of primitives
+ Returns a booking object
+
+ This is not going to be implemented or allowed.
+ If someone needs to create a booking through the api,
+ they will send a different booking object
+ """
+ return None
+
+class BookingSerializer(serializers.Serializer):
+
+ booking = BookingField()
+
+#Host Type stuff, for inventory
+
+class CPUSerializer(serializers.ModelSerializer):
+ class Meta:
+ model = CpuProfile
+ fields = ('cores', 'architecture', 'cpus')
+
+class DiskSerializer(serializers.ModelSerializer):
+ class Meta:
+ model = DiskProfile
+ fields = ('size', 'media_type', 'name')
+
+class InterfaceProfileSerializer(serializers.ModelSerializer):
+ class Meta:
+ model = InterfaceProfile
+ fields = ('speed', 'name')
+
+class RamSerializer(serializers.ModelSerializer):
+ class Meta:
+ model = RamProfile
+ fields = ('amount', 'channels')
+
+class HostTypeSerializer(serializers.Serializer):
+ name = serializers.CharField(max_length=200)
+ ram = RamSerializer()
+ interface = InterfaceProfileSerializer()
+ description = serializers.CharField(max_length=1000)
+ disks = DiskSerializer()
+ cpu = CPUSerializer()
+
+#the rest of the inventory stuff
+class NetworkSerializer(serializers.Serializer):
+ cidr = serializers.CharField(max_length=200)
+ gateway = serializers.IPAddressField(max_length=200)
+ vlan = serializers.IntegerField()
+
+class ImageSerializer(serializers.ModelSerializer):
+ lab_id = serializers.IntegerField()
+ id = serializers.IntegerField(source="dashboard_id")
+ name = serializers.CharField(max_length=50)
+ description = serializers.CharField(max_length=200)
+ class Meta:
+ model = Image
+
+class InterfaceField(serializers.Field):
+ def to_representation(self, interface):
+ pass
+
+ def to_internal_value(self, data):
+ """
+ takes in a serialized interface and creates an Interface model
+ """
+ mac = data['mac']
+ bus_address = data['busaddr']
+ switch_name = data['switchport']['switch_name']
+ port_name = data['switchport']['port_name']
+ # TODO config??
+ return Interface.objects.create(
+ mac_address=mac,
+ bus_address=bus_address,
+ switch_name=switch_name,
+ port_name=port_name
+ )
+
+class InventoryHostSerializer(serializers.Serializer):
+ hostname = serializers.CharField(max_length=100)
+ host_type = serializers.CharField(max_length=100)
+ interfaces = InterfaceField()
+
+
+class InventorySerializer(serializers.Serializer):
+ hosts = InventoryHostSerializer()
+ networks = NetworkSerializer()
+ images = ImageSerializer()
+ host_types = HostTypeSerializer()
diff --git a/dashboard/src/api/serializers/old_serializers.py b/dashboard/src/api/serializers/old_serializers.py
new file mode 100644
index 0000000..f50b90b
--- /dev/null
+++ b/dashboard/src/api/serializers/old_serializers.py
@@ -0,0 +1,28 @@
+##############################################################################
+# 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
+##############################################################################
+
+
+from rest_framework import serializers
+
+from account.models import UserProfile
+from notifier.models import Notifier
+
+
+class NotifierSerializer(serializers.ModelSerializer):
+ class Meta:
+ model = Notifier
+ fields = ('id', 'title', 'content', 'user', 'sender', 'message_type', 'msg_sent')
+
+
+class UserSerializer(serializers.ModelSerializer):
+ username = serializers.CharField(source='user.username')
+
+ class Meta:
+ model = UserProfile
+ fields = ('user', 'username', 'ssh_public_key', 'pgp_public_key', 'email_addr')
diff --git a/dashboard/src/api/tests/__init__.py b/dashboard/src/api/tests/__init__.py
new file mode 100644
index 0000000..fe2a32d
--- /dev/null
+++ b/dashboard/src/api/tests/__init__.py
@@ -0,0 +1,8 @@
+##############################################################################
+# Copyright (c) 2016 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
+############################################################################## \ No newline at end of file
diff --git a/dashboard/src/api/tests/test_serializers.py b/dashboard/src/api/tests/test_serializers.py
new file mode 100644
index 0000000..c49010c
--- /dev/null
+++ b/dashboard/src/api/tests/test_serializers.py
@@ -0,0 +1,213 @@
+##############################################################################
+# 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 resource_inventory.models import *
+from account.models import Lab
+from api.serializers.booking_serializer import *
+from datetime import timedelta
+from django.utils import timezone
+from django.contrib.auth.models import Permission, User
+
+
+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 = InterfaceProfile.objects.create(
+ speed=1000,
+ name='eno3',
+ host=hostProfile
+ )
+ diskProfile = DiskProfile.objects.create(
+ size=1000,
+ media_type="SSD",
+ name='/dev/sda',
+ host=hostProfile
+ )
+ cpuProfile = CpuProfile.objects.create(
+ cores=96,
+ architecture="x86_64",
+ cpus=2,
+ host=hostProfile
+ )
+ ramProfile = 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/dashboard/src/api/urls.py b/dashboard/src/api/urls.py
index c2cd510..94f8279 100644
--- a/dashboard/src/api/urls.py
+++ b/dashboard/src/api/urls.py
@@ -1,5 +1,6 @@
##############################################################################
# Copyright (c) 2016 Max Breitenfeldt and others.
+# Copyright (c) 2018 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
@@ -24,19 +25,25 @@ Including another URLconf
2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls'))
"""
from django.conf.urls import url, include
+from django.urls import path
from rest_framework import routers
from api.views import *
router = routers.DefaultRouter()
-router.register(r'resources', ResourceViewSet)
-router.register(r'servers', ServerViewSet)
router.register(r'bookings', BookingViewSet)
-router.register(r'resource_status', ResourceStatusViewSet)
router.register(r'notifier', NotifierViewSet)
router.register(r'user', UserViewSet)
urlpatterns = [
url(r'^', include(router.urls)),
+ 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>/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),
+ path('labs/<slug:lab_name>/jobs/current', current_jobs),
+ path('labs/<slug:lab_name>/jobs/done', done_jobs),
url(r'^token$', GenerateTokenView.as_view(), name='generate_token'),
]
diff --git a/dashboard/src/api/views.py b/dashboard/src/api/views.py
index b873ef1..cefd131 100644
--- a/dashboard/src/api/views.py
+++ b/dashboard/src/api/views.py
@@ -1,5 +1,6 @@
##############################################################################
# Copyright (c) 2016 Max Breitenfeldt and others.
+# 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
@@ -8,17 +9,23 @@
##############################################################################
-from django.contrib.auth.decorators import login_required, user_passes_test
+from django.contrib.auth.decorators import login_required
from django.shortcuts import redirect
from django.utils.decorators import method_decorator
from django.views import View
+from django.http.response import JsonResponse
from rest_framework import viewsets
from rest_framework.authtoken.models import Token
+from django.views.decorators.csrf import csrf_exempt
-from api.serializers import *
+import json
+
+from api.serializers.booking_serializer import *
+from api.serializers.old_serializers import NotifierSerializer, UserSerializer
from account.models import UserProfile
from booking.models import Booking
-from dashboard.models import Resource, Server, ResourceStatus
+from notifier.models import Notifier
+from api.models import *
class BookingViewSet(viewsets.ModelViewSet):
@@ -27,29 +34,16 @@ class BookingViewSet(viewsets.ModelViewSet):
filter_fields = ('resource', 'id')
-class ServerViewSet(viewsets.ModelViewSet):
- queryset = Server.objects.all()
- serializer_class = ServerSerializer
- filter_fields = ('resource', 'name')
-
-
-class ResourceViewSet(viewsets.ModelViewSet):
- queryset = Resource.objects.all()
- serializer_class = ResourceSerializer
- filter_fields = ('name', 'id')
-
-class ResourceStatusViewSet(viewsets.ModelViewSet):
- queryset = ResourceStatus.objects.all()
- serializer_class = ResourceStatusSerializer
-
class NotifierViewSet(viewsets.ModelViewSet):
queryset = Notifier.objects.none()
serializer_class = NotifierSerializer
+
class UserViewSet(viewsets.ModelViewSet):
queryset = UserProfile.objects.all()
serializer_class = UserSerializer
+
@method_decorator(login_required, name='dispatch')
class GenerateTokenView(View):
def get(self, request, *args, **kwargs):
@@ -59,3 +53,74 @@ class GenerateTokenView(View):
token.delete()
Token.objects.create(user=user)
return redirect('account:settings')
+
+
+def lab_inventory(request, lab_name=""):
+ lab_token = request.META.get('HTTP_AUTH_TOKEN')
+ lab_manager = LabManagerTracker.get(lab_name, lab_token)
+ return JsonResponse(lab_manager.get_inventory(), safe=False)
+
+
+def lab_status(request, lab_name=""):
+ lab_token = request.META.get('HTTP_AUTH_TOKEN')
+ lab_manager = LabManagerTracker.get(lab_name, lab_token)
+ if request.method == "POST":
+ return JsonResponse(lab_manager.set_status(request.POST), safe=False)
+ return JsonResponse(lab_manager.get_status(), safe=False)
+
+
+def lab_profile(request, lab_name=""):
+ lab_token = request.META.get('HTTP_AUTH_TOKEN')
+ lab_manager = LabManagerTracker.get(lab_name, lab_token)
+ return JsonResponse(lab_manager.get_profile(), safe=False)
+
+
+@csrf_exempt
+def specific_task(request, lab_name="", job_id="", task_id=""):
+ lab_token = request.META.get('HTTP_AUTH_TOKEN')
+ LabManagerTracker.get(lab_name, lab_token) # Authorize caller, but we dont need the result
+
+ if request.method == "POST":
+ task = get_task(task_id)
+ if 'status' in request.POST:
+ task.status = request.POST.get('status')
+ if 'message' in request.POST:
+ task.message = request.POST.get('message')
+ task.save()
+ d = {}
+ d['task'] = task.config.get_delta()
+ m = {}
+ m['status'] = task.status
+ m['job'] = str(task.job)
+ m['message'] = task.message
+ d['meta'] = m
+ response = json.dumps(d)
+ return JsonResponse(response)
+ elif request.method == "GET":
+ return JsonResponse(get_task(task_id).config.get_delta())
+
+
+def specific_job(request, lab_name="", job_id=""):
+ lab_token = request.META.get('HTTP_AUTH_TOKEN')
+ lab_manager = LabManagerTracker.get(lab_name, lab_token)
+ if request.method == "POST":
+ return JsonResponse(lab_manager.update_job(job_id, request.POST), safe=False)
+ return JsonResponse(lab_manager.get_job(job_id), safe=False)
+
+
+def new_jobs(request, lab_name=""):
+ lab_token = request.META.get('HTTP_AUTH_TOKEN')
+ lab_manager = LabManagerTracker.get(lab_name, lab_token)
+ return JsonResponse(lab_manager.get_new_jobs(), safe=False)
+
+
+def current_jobs(request, lab_name=""):
+ lab_token = request.META.get('HTTP_AUTH_TOKEN')
+ lab_manager = LabManagerTracker.get(lab_name, lab_token)
+ return JsonResponse(lab_manager.get_current_jobs(), safe=False)
+
+
+def done_jobs(request, lab_name=""):
+ lab_token = request.META.get('HTTP_AUTH_TOKEN')
+ lab_manager = LabManagerTracker.get(lab_name, lab_token)
+ return JsonResponse(lab_manager.get_done_jobs(), safe=False)