diff options
author | Parker Berberian <pberberian@iol.unh.edu> | 2019-04-17 12:44:04 -0400 |
---|---|---|
committer | Parker Berberian <pberberian@iol.unh.edu> | 2019-05-08 10:12:58 -0400 |
commit | 3d078d2df3fac997c783435ba07e7710d087fd4b (patch) | |
tree | b9c1cc9d4eb0c7691d2422d105a04e8f9be79ef6 | |
parent | dc690ef43cc7af006c0900e8eaf1635141f0a2a1 (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.py | 25 | ||||
-rw-r--r-- | src/api/migrations/0008_auto_20190419_1414.py | 28 | ||||
-rw-r--r-- | src/api/migrations/0009_merge_20190508_1317.py | 14 | ||||
-rw-r--r-- | src/api/models.py | 92 | ||||
-rw-r--r-- | src/api/urls.py | 4 | ||||
-rw-r--r-- | src/api/views.py | 14 | ||||
-rw-r--r-- | src/resource_inventory/idf_templater.py | 15 |
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( |