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
commit3d078d2df3fac997c783435ba07e7710d087fd4b (patch)
treeb9c1cc9d4eb0c7691d2422d105a04e8f9be79ef6
parentdc690ef43cc7af006c0900e8eaf1635141f0a2a1 (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--src/api/migrations/0007_auto_20190417_1511.py25
-rw-r--r--src/api/migrations/0008_auto_20190419_1414.py28
-rw-r--r--src/api/migrations/0009_merge_20190508_1317.py14
-rw-r--r--src/api/models.py92
-rw-r--r--src/api/urls.py4
-rw-r--r--src/api/views.py14
-rw-r--r--src/resource_inventory/idf_templater.py15
7 files changed, 180 insertions, 12 deletions
diff --git a/src/api/migrations/0007_auto_20190417_1511.py b/src/api/migrations/0007_auto_20190417_1511.py
new file mode 100644
index 0000000..e7d2c59
--- /dev/null
+++ b/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/src/api/migrations/0008_auto_20190419_1414.py b/src/api/migrations/0008_auto_20190419_1414.py
new file mode 100644
index 0000000..03c3865
--- /dev/null
+++ b/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/src/api/migrations/0009_merge_20190508_1317.py b/src/api/migrations/0009_merge_20190508_1317.py
new file mode 100644
index 0000000..1a34380
--- /dev/null
+++ b/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/src/api/models.py b/src/api/models.py
index c165454..1f708ae 100644
--- a/src/api/models.py
+++ b/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/src/api/urls.py b/src/api/urls.py
index d18a04d..d1f772a 100644
--- a/src/api/urls.py
+++ b/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/src/api/views.py b/src/api/views.py
index 2ae1ac5..fb28958 100644
--- a/src/api/views.py
+++ b/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/src/resource_inventory/idf_templater.py b/src/resource_inventory/idf_templater.py
index 26307e3..bf6eda0 100644
--- a/src/resource_inventory/idf_templater.py
+++ b/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(