summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorParker Berberian <pberberian@iol.unh.edu>2019-04-17 12:44:04 -0400
committerParker Berberian <pberberian@iol.unh.edu>2019-05-08 10:12:58 -0400
commitb906ab1a020858d99577bd4b6476c6aec965330b (patch)
tree246fc4bcb3f0aeb35a50f048f9e7a181f93088b3
parent8d8f6a41e73e77178274a0fa26a084bf73bfc864 (diff)
Adds pdf and idf into api
The Pod Descriptor File (pdf) and Installer descriptor file (idf) are now hosted in the api. The url endpoint where the lab can retrieve them are now part of a software task. An OPNFV task also contains a new dictionary that describes how bridges should be configured on the jumphost. This information is not contained in the pdf/idf but is needed by the lab. Change-Id: I6971279979ba180725926035bd9db481aafb1073 Signed-off-by: Parker Berberian <pberberian@iol.unh.edu>
-rw-r--r--dashboard/src/api/migrations/0007_auto_20190417_1511.py25
-rw-r--r--dashboard/src/api/migrations/0008_auto_20190419_1414.py28
-rw-r--r--dashboard/src/api/migrations/0009_merge_20190508_1317.py14
-rw-r--r--dashboard/src/api/models.py92
-rw-r--r--dashboard/src/api/urls.py4
-rw-r--r--dashboard/src/api/views.py14
-rw-r--r--dashboard/src/resource_inventory/idf_templater.py15
7 files changed, 180 insertions, 12 deletions
diff --git a/dashboard/src/api/migrations/0007_auto_20190417_1511.py b/dashboard/src/api/migrations/0007_auto_20190417_1511.py
new file mode 100644
index 0000000..e7d2c59
--- /dev/null
+++ b/dashboard/src/api/migrations/0007_auto_20190417_1511.py
@@ -0,0 +1,25 @@
+# Generated by Django 2.1 on 2019-04-17 15:11
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('api', '0006_auto_20190313_1729'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='opnfvapiconfig',
+ name='idf',
+ field=models.CharField(default='', max_length=100),
+ preserve_default=False,
+ ),
+ migrations.AddField(
+ model_name='opnfvapiconfig',
+ name='pdf',
+ field=models.CharField(default='', max_length=100),
+ preserve_default=False,
+ ),
+ ]
diff --git a/dashboard/src/api/migrations/0008_auto_20190419_1414.py b/dashboard/src/api/migrations/0008_auto_20190419_1414.py
new file mode 100644
index 0000000..03c3865
--- /dev/null
+++ b/dashboard/src/api/migrations/0008_auto_20190419_1414.py
@@ -0,0 +1,28 @@
+# Generated by Django 2.1 on 2019-04-19 14:14
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('resource_inventory', '0009_auto_20190315_1757'),
+ ('api', '0007_auto_20190417_1511'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='BridgeConfig',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('interfaces', models.ManyToManyField(to='resource_inventory.Interface')),
+ ('opnfv_config', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='resource_inventory.OPNFVConfig')),
+ ],
+ ),
+ migrations.AddField(
+ model_name='opnfvapiconfig',
+ name='bridge_config',
+ field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, to='api.BridgeConfig'),
+ ),
+ ]
diff --git a/dashboard/src/api/migrations/0009_merge_20190508_1317.py b/dashboard/src/api/migrations/0009_merge_20190508_1317.py
new file mode 100644
index 0000000..1a34380
--- /dev/null
+++ b/dashboard/src/api/migrations/0009_merge_20190508_1317.py
@@ -0,0 +1,14 @@
+# Generated by Django 2.1 on 2019-05-08 13:17
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('api', '0008_auto_20190419_1414'),
+ ('api', '0007_opnfvapiconfig_opnfv_config'),
+ ]
+
+ operations = [
+ ]
diff --git a/dashboard/src/api/models.py b/dashboard/src/api/models.py
index c165454..1f708ae 100644
--- a/dashboard/src/api/models.py
+++ b/dashboard/src/api/models.py
@@ -12,6 +12,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
+from django.urls import reverse
import json
import uuid
@@ -23,6 +24,7 @@ from resource_inventory.models import (
Host,
Image,
Interface,
+ HostOPNFVConfig,
RemoteInfo,
OPNFVConfig
)
@@ -98,6 +100,14 @@ class LabManager(object):
booking.idf = IDFTemplater().makeIDF(booking)
booking.save()
+ def get_pdf(self, booking_id):
+ booking = get_object_or_404(Booking, pk=booking_id, lab=self.lab)
+ return booking.pdf
+
+ def get_idf(self, booking_id):
+ booking = get_object_or_404(Booking, pk=booking_id, lab=self.lab)
+ return booking.idf
+
def get_profile(self):
prof = {}
prof['name'] = self.lab.name
@@ -351,11 +361,44 @@ class TaskConfig(models.Model):
self.delta = '{}'
+class BridgeConfig(models.Model):
+ """
+ Displays mapping between jumphost interfaces and
+ bridges
+ """
+ interfaces = models.ManyToManyField(Interface)
+ opnfv_config = models.ForeignKey(OPNFVConfig, on_delete=models.CASCADE)
+
+ def to_dict(self):
+ d = {}
+ hid = self.interfaces.first().host.labid
+ d[hid] = {}
+ for interface in self.interfaces.all():
+ d[hid][interface.mac_address] = []
+ for vlan in interface.config.all():
+ network_role = self.opnfv_model.networks().filter(network=vlan.network)
+ bridge = IDFTemplater.bridge_names[network_role.name]
+ br_config = {
+ "vlan_id": vlan.vlan_id,
+ "tagged": vlan.tagged,
+ "bridge": bridge
+ }
+ d[hid][interface.mac_address].append(br_config)
+ return d
+
+ def to_json(self):
+ return json.dumps(self.to_dict())
+
+
class OpnfvApiConfig(models.Model):
installer = models.CharField(max_length=200)
scenario = models.CharField(max_length=300)
roles = models.ManyToManyField(Host)
+ # pdf and idf are url endpoints, not the actual file
+ pdf = models.CharField(max_length=100)
+ idf = models.CharField(max_length=100)
+ bridge_config = models.OneToOneField(BridgeConfig, on_delete=models.CASCADE, null=True)
delta = models.TextField()
opnfv_config = models.ForeignKey(OPNFVConfig, null=True, on_delete=models.SET_NULL)
@@ -367,6 +410,12 @@ class OpnfvApiConfig(models.Model):
d['installer'] = self.installer
if self.scenario:
d['scenario'] = self.scenario
+ if self.pdf:
+ d['pdf'] = self.pdf
+ if self.idf:
+ d['idf'] = self.idf
+ if self.bridge_config:
+ d['bridged_interfaces'] = self.bridge_config.to_dict()
hosts = self.roles.all()
if hosts.exists():
@@ -395,6 +444,16 @@ class OpnfvApiConfig(models.Model):
d['scenario'] = scenario
self.delta = json.dumps(d)
+ def set_xdf(self, booking, update_delta=True):
+ kwargs = {'lab_name': booking.lab.name, 'booking_id': booking.id}
+ self.pdf = reverse('get-pdf', kwargs=kwargs)
+ self.idf = reverse('get-idf', kwargs=kwargs)
+ if update_delta:
+ d = json.loads(self.delta)
+ d['pdf'] = self.pdf
+ d['idf'] = self.idf
+ self.delta = json.dumps(d)
+
def add_role(self, host):
self.roles.add(host)
d = json.loads(self.delta)
@@ -632,6 +691,7 @@ class SnapshotConfig(TaskConfig):
if not self.delta:
self.delta = self.to_json()
self.save()
+
d = json.loads(self.delta)
return d
@@ -823,7 +883,6 @@ class JobFactory(object):
job=job
)
cls.makeSoftware(
- hosts=hosts,
booking=booking,
job=job
)
@@ -915,18 +974,41 @@ class JobFactory(object):
network_config.save()
@classmethod
- def makeSoftware(cls, hosts=[], booking=None, job=Job()):
+ def make_bridge_config(cls, booking):
+ if booking.resource.hosts.count() < 2:
+ return None
+ try:
+ jumphost_config = HostOPNFVConfig.objects.filter(
+ role__name__iexact="jumphost"
+ )
+ jumphost = Host.objects.get(
+ bundle=booking.resource,
+ config=jumphost_config.host_config
+ )
+ except Exception:
+ return None
+ br_config = BridgeConfig.objects.create(opnfv_config=booking.opnfv_config)
+ for iface in jumphost.interfaces.all():
+ br_config.interfaces.add(iface)
+ return br_config
+
+ @classmethod
+ def makeSoftware(cls, booking=None, job=Job()):
if not booking.opnfv_config:
return None
opnfv_api_config = OpnfvApiConfig.objects.create(
opnfv_config=booking.opnfv_config,
- installer=booking.opnfv_config.installer,
- scenario=booking.opnfv_config.scenario,
+ installer=booking.opnfv_config.installer.name,
+ scenario=booking.opnfv_config.scenario.name,
+ bridge_config=cls.make_bridge_config(booking)
)
- for host in hosts:
+ opnfv_api_config.set_xdf(booking, False)
+ opnfv_api_config.save()
+
+ for host in booking.resource.hosts.all():
opnfv_api_config.roles.add(host)
software_config = SoftwareConfig.objects.create(opnfv=opnfv_api_config)
software_relation = SoftwareRelation.objects.create(job=job, config=software_config)
diff --git a/dashboard/src/api/urls.py b/dashboard/src/api/urls.py
index d18a04d..d1f772a 100644
--- a/dashboard/src/api/urls.py
+++ b/dashboard/src/api/urls.py
@@ -41,6 +41,8 @@ from api.views import (
done_jobs,
update_host_bmc,
lab_host,
+ get_pdf,
+ get_idf,
GenerateTokenView
)
@@ -55,6 +57,8 @@ urlpatterns = [
path('labs/<slug:lab_name>/inventory', lab_inventory),
path('labs/<slug:lab_name>/hosts/<slug:host_id>', lab_host),
path('labs/<slug:lab_name>/hosts/<slug:host_id>/bmc', update_host_bmc),
+ path('labs/<slug:lab_name>/booking/<slug:booking_id>/pdf', get_pdf, name="get-pdf"),
+ path('labs/<slug:lab_name>/booking/<slug:booking_id>/idf', get_idf, name="get-idf"),
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/dashboard/src/api/views.py b/dashboard/src/api/views.py
index 2ae1ac5..fb28958 100644
--- a/dashboard/src/api/views.py
+++ b/dashboard/src/api/views.py
@@ -13,7 +13,7 @@ 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 django.http.response import JsonResponse, HttpResponse
from rest_framework import viewsets
from rest_framework.authtoken.models import Token
from django.views.decorators.csrf import csrf_exempt
@@ -64,6 +64,18 @@ def lab_host(request, lab_name="", host_id=""):
return JsonResponse(lab_manager.update_host(host_id, request.POST), safe=False)
+def get_pdf(request, lab_name="", booking_id=""):
+ lab_token = request.META.get('HTTP_AUTH_TOKEN')
+ lab_manager = LabManagerTracker.get(lab_name, lab_token)
+ return HttpResponse(lab_manager.get_pdf(booking_id), content_type="text/plain")
+
+
+def get_idf(request, lab_name="", booking_id=""):
+ lab_token = request.META.get('HTTP_AUTH_TOKEN')
+ lab_manager = LabManagerTracker.get(lab_name, lab_token)
+ return HttpResponse(lab_manager.get_idf(booking_id), content_type="text/plain")
+
+
def lab_status(request, lab_name=""):
lab_token = request.META.get('HTTP_AUTH_TOKEN')
lab_manager = LabManagerTracker.get(lab_name, lab_token)
diff --git a/dashboard/src/resource_inventory/idf_templater.py b/dashboard/src/resource_inventory/idf_templater.py
index 26307e3..bf6eda0 100644
--- a/dashboard/src/resource_inventory/idf_templater.py
+++ b/dashboard/src/resource_inventory/idf_templater.py
@@ -19,8 +19,15 @@ class IDFTemplater:
"""
Utility class to create a full IDF yaml file
"""
+ net_names = ["admin", "mgmt", "private", "public"]
+ bridge_names = {
+ "admin": "br-admin",
+ "mgmt": "br-mgmt",
+ "private": "br-private",
+ "public": "br-public"
+ }
+
def __init__(self):
- self.net_names = ["admin", "mgmt", "private", "public"]
self.networks = {}
for i, name in enumerate(self.net_names):
self.networks[name] = {
@@ -117,11 +124,7 @@ class IDFTemplater:
return fuel
def get_fuel_bridges(self):
- bridges = {}
- for net in self.net_names:
- bridges[net] = "br-" + net
-
- return bridges
+ return self.bridge_names
def get_fuel_nodes(self, booking):
jumphost = booking.opnfv_config.host_opnfv_config.get(