1 ##############################################################################
2 # Copyright (c) 2018 Sawyer Bergeron, Parker Berberian, and others.
4 # All rights reserved. This program and the accompanying materials
5 # are made available under the terms of the Apache License, Version 2.0
6 # which accompanies this distribution, and is available at
7 # http://www.apache.org/licenses/LICENSE-2.0
8 ##############################################################################
11 from django.contrib.auth.models import User
12 from django.db import models
13 from django.core.exceptions import PermissionDenied, ValidationError
14 from django.shortcuts import get_object_or_404
15 from django.http import HttpResponseNotFound
16 from django.urls import reverse
17 from django.utils import timezone
22 from booking.models import Booking
23 from resource_inventory.models import (
34 from resource_inventory.idf_templater import IDFTemplater
35 from resource_inventory.pdf_templater import PDFTemplater
36 from account.models import Downtime
37 from dashboard.utils import AbstractModelQuery
40 class JobStatus(object):
42 A poor man's enum for a job's status.
44 A job is NEW if it has not been started or recognized by the Lab
45 A job is CURRENT if it has been started by the lab but it is not yet completed
46 a job is DONE if all the tasks are complete and the booking is ready to use
55 class LabManagerTracker(object):
58 def get(cls, lab_name, token):
62 Takes in a lab name (from a url path)
63 returns a lab manager instance for that lab, if it exists
64 Also checks that the given API token is correct
67 lab = Lab.objects.get(name=lab_name)
69 raise PermissionDenied("Lab not found")
70 if lab.api_token == token:
71 return LabManager(lab)
72 raise PermissionDenied("Lab not authorized")
75 class LabManager(object):
77 Handles all lab REST calls.
79 handles jobs, inventory, status, etc
80 may need to create helper classes
83 def __init__(self, lab):
86 def get_downtime(self):
87 return Downtime.objects.filter(start__lt=timezone.now(), end__gt=timezone.now(), lab=self.lab)
89 def get_downtime_json(self):
90 downtime = self.get_downtime().first() # should only be one item in queryset
94 "start": downtime.start,
96 "description": downtime.description
98 return {"is_down": False}
100 def create_downtime(self, form):
102 Create a downtime event.
104 Takes in a dictionary that describes the model.
106 "start": utc timestamp
108 "description": human text (optional)
110 For timestamp structure, https://docs.djangoproject.com/en/2.2/ref/forms/fields/#datetimefield
112 Downtime.objects.create(
113 start=form.cleaned_data['start'],
114 end=form.cleaned_data['end'],
115 description=form.cleaned_data['description'],
118 return self.get_downtime_json()
120 def update_host_remote_info(self, data, res_id):
121 resource = ResourceQuery.filter(labid=res_id, lab=self.lab)
122 if len(resource) != 1:
123 return HttpResponseNotFound("Could not find single host with id " + str(res_id))
124 resource = resource[0]
127 info['address'] = data['address']
128 info['mac_address'] = data['mac_address']
129 info['password'] = data['password']
130 info['user'] = data['user']
131 info['type'] = data['type']
132 info['versions'] = json.dumps(data['versions'])
133 except Exception as e:
134 return {"error": "invalid arguement: " + str(e)}
135 remote_info = resource.remote_management
136 if "default" in remote_info.mac_address:
137 remote_info = RemoteInfo()
138 remote_info.address = info['address']
139 remote_info.mac_address = info['mac_address']
140 remote_info.password = info['password']
141 remote_info.user = info['user']
142 remote_info.type = info['type']
143 remote_info.versions = info['versions']
145 resource.remote_management = remote_info
147 booking = Booking.objects.get(resource=resource.bundle)
148 self.update_xdf(booking)
149 return {"status": "success"}
151 def update_xdf(self, booking):
152 booking.pdf = PDFTemplater.makePDF(booking)
153 booking.idf = IDFTemplater().makeIDF(booking)
156 def get_pdf(self, booking_id):
157 booking = get_object_or_404(Booking, pk=booking_id, lab=self.lab)
160 def get_idf(self, booking_id):
161 booking = get_object_or_404(Booking, pk=booking_id, lab=self.lab)
164 def get_profile(self):
166 prof['name'] = self.lab.name
168 "phone": self.lab.contact_phone,
169 "email": self.lab.contact_email
171 prof['host_count'] = [{
172 "type": profile.name,
173 "count": len(profile.get_resources(lab=self.lab))}
174 for profile in ResourceProfile.objects.filter(labs=self.lab)]
177 def get_inventory(self):
179 resources = ResourceQuery.filter(lab=self.lab)
180 images = Image.objects.filter(from_lab=self.lab)
181 profiles = ResourceProfile.objects.filter(labs=self.lab)
182 inventory['resources'] = self.serialize_resources(resources)
183 inventory['images'] = self.serialize_images(images)
184 inventory['host_types'] = self.serialize_host_profiles(profiles)
187 def get_host(self, hostname):
188 resource = ResourceQuery.filter(labid=hostname, lab=self.lab)
189 if len(resource) != 1:
190 return HttpResponseNotFound("Could not find single host with id " + str(hostname))
191 resource = resource[0]
193 "booked": resource.booked,
194 "working": resource.working,
195 "type": resource.profile.name
198 def update_host(self, hostname, data):
199 resource = ResourceQuery.filter(labid=hostname, lab=self.lab)
200 if len(resource) != 1:
201 return HttpResponseNotFound("Could not find single host with id " + str(hostname))
202 resource = resource[0]
203 if "working" in data:
204 working = data['working'] == "true"
205 resource.working = working
207 return self.get_host(hostname)
209 def get_status(self):
210 return {"status": self.lab.status}
212 def set_status(self, payload):
215 def get_current_jobs(self):
216 jobs = Job.objects.filter(booking__lab=self.lab)
218 return self.serialize_jobs(jobs, status=JobStatus.CURRENT)
220 def get_new_jobs(self):
221 jobs = Job.objects.filter(booking__lab=self.lab)
223 return self.serialize_jobs(jobs, status=JobStatus.NEW)
225 def get_done_jobs(self):
226 jobs = Job.objects.filter(booking__lab=self.lab)
228 return self.serialize_jobs(jobs, status=JobStatus.DONE)
230 def get_job(self, jobid):
231 return Job.objects.get(pk=jobid).to_dict()
233 def update_job(self, jobid, data):
236 def serialize_jobs(self, jobs, status=JobStatus.NEW):
239 jsonized_job = job.get_delta(status)
240 if len(jsonized_job['payload']) < 1:
242 job_ser.append(jsonized_job)
246 def serialize_resources(self, resources):
247 # TODO: rewrite for Resource model
249 for res in resources:
252 'hostname': res.name,
253 'host_type': res.profile.name
255 for iface in res.get_interfaces():
256 r['interfaces'].append({
257 'mac': iface.mac_address,
258 'busaddr': iface.bus_address,
260 'switchport': {"switch_name": iface.switch_name, "port_name": iface.port_name}
264 def serialize_images(self, images):
270 "lab_id": image.lab_id,
271 "dashboard_id": image.id
276 def serialize_resource_profiles(self, profiles):
278 for profile in profiles:
281 "cores": profile.cpuprofile.first().cores,
282 "arch": profile.cpuprofile.first().architecture,
283 "cpus": profile.cpuprofile.first().cpus,
286 for disk in profile.storageprofile.all():
289 "type": disk.media_type,
293 p['description'] = profile.description
295 for iface in profile.interfaceprofile.all():
296 p['interfaces'].append(
298 "speed": iface.speed,
303 p['ram'] = {"amount": profile.ramprofile.first().amount}
304 p['name'] = profile.name
305 profile_ser.append(p)
309 class Job(models.Model):
311 A Job to be performed by the Lab.
313 The API uses Jobs and Tasks to communicate actions that need to be taken to the Lab
314 that is hosting a booking. A booking from a user has an associated Job which tells
315 the lab how to configure the hardware, networking, etc to fulfill the booking
317 This is the class that is serialized and put into the api
320 booking = models.OneToOneField(Booking, on_delete=models.CASCADE, null=True)
321 status = models.IntegerField(default=JobStatus.NEW)
322 complete = models.BooleanField(default=False)
326 for relation in self.get_tasklist():
327 if relation.job_key not in d:
328 d[relation.job_key] = {}
329 d[relation.job_key][relation.task_id] = relation.config.to_dict()
331 return {"id": self.id, "payload": d}
333 def get_tasklist(self, status="all"):
335 return JobTaskQuery.filter(job=self, status=status)
336 return JobTaskQuery.filter(job=self)
338 def is_fulfilled(self):
340 If a job has been completed by the lab.
342 This method should return true if all of the job's tasks are done,
345 my_tasks = self.get_tasklist()
346 for task in my_tasks:
347 if task.status != JobStatus.DONE:
351 def get_delta(self, status):
353 for relation in self.get_tasklist(status=status):
354 if relation.job_key not in d:
355 d[relation.job_key] = {}
356 d[relation.job_key][relation.task_id] = relation.config.get_delta()
358 return {"id": self.id, "payload": d}
361 return json.dumps(self.to_dict())
364 class TaskConfig(models.Model):
365 state = models.IntegerField(default=ConfigState.CLEAN)
367 keys = set() # TODO: This needs to be an instance variable, not a class variable
368 delta_keys_list = models.CharField(max_length=200, default="[]")
371 def delta_keys(self):
372 return list(set(json.loads(self.delta_keys_list)))
375 def delta_keys(self, keylist):
376 self.delta_keys_list = json.dumps(keylist)
379 raise NotImplementedError
382 raise NotImplementedError
384 def format_delta(self, config, token):
385 delta = {k: config[k] for k in self.delta_keys}
386 delta['lab_token'] = token
390 return json.dumps(self.to_dict())
392 def clear_delta(self):
395 def set(self, *args):
396 dkeys = self.delta_keys
400 self.delta_keys = dkeys
403 class BridgeConfig(models.Model):
404 """Displays mapping between jumphost interfaces and bridges."""
406 interfaces = models.ManyToManyField(Interface)
407 opnfv_config = models.ForeignKey(OPNFVConfig, on_delete=models.CASCADE)
411 hid = ResourceQuery.get(interface__pk=self.interfaces.first().pk).labid
413 for interface in self.interfaces.all():
414 d[hid][interface.mac_address] = []
415 for vlan in interface.config.all():
416 network_role = self.opnfv_model.networks().filter(network=vlan.network)
417 bridge = IDFTemplater.bridge_names[network_role.name]
419 "vlan_id": vlan.vlan_id,
420 "tagged": vlan.tagged,
423 d[hid][interface.mac_address].append(br_config)
427 return json.dumps(self.to_dict())
430 class OpnfvApiConfig(models.Model):
432 installer = models.CharField(max_length=200)
433 scenario = models.CharField(max_length=300)
434 roles = models.ManyToManyField(ResourceOPNFVConfig)
435 # pdf and idf are url endpoints, not the actual file
436 pdf = models.CharField(max_length=100)
437 idf = models.CharField(max_length=100)
438 bridge_config = models.OneToOneField(BridgeConfig, on_delete=models.CASCADE, null=True)
439 delta = models.TextField()
440 opnfv_config = models.ForeignKey(OPNFVConfig, null=True, on_delete=models.SET_NULL)
444 if not self.opnfv_config:
447 d['installer'] = self.installer
449 d['scenario'] = self.scenario
454 if self.bridge_config:
455 d['bridged_interfaces'] = self.bridge_config.to_dict()
457 hosts = self.roles.all()
462 host.labid: self.opnfv_config.host_opnfv_config.get(
463 host_config__pk=host.config.pk
470 return json.dumps(self.to_dict())
472 def set_installer(self, installer):
473 self.installer = installer
474 d = json.loads(self.delta)
475 d['installer'] = installer
476 self.delta = json.dumps(d)
478 def set_scenario(self, scenario):
479 self.scenario = scenario
480 d = json.loads(self.delta)
481 d['scenario'] = scenario
482 self.delta = json.dumps(d)
484 def set_xdf(self, booking, update_delta=True):
485 kwargs = {'lab_name': booking.lab.name, 'booking_id': booking.id}
486 self.pdf = reverse('get-pdf', kwargs=kwargs)
487 self.idf = reverse('get-idf', kwargs=kwargs)
489 d = json.loads(self.delta)
492 self.delta = json.dumps(d)
494 def add_role(self, host):
496 d = json.loads(self.delta)
499 d['roles'].append({host.labid: host.config.opnfvRole.name})
500 self.delta = json.dumps(d)
502 def clear_delta(self):
507 self.delta = self.to_json()
509 return json.loads(self.delta)
512 class AccessConfig(TaskConfig):
513 access_type = models.CharField(max_length=50)
514 user = models.ForeignKey(User, on_delete=models.CASCADE)
515 revoke = models.BooleanField(default=False)
516 context = models.TextField(default="")
517 delta = models.TextField(default="{}")
521 d['access_type'] = self.access_type
522 d['user'] = self.user.id
523 d['revoke'] = self.revoke
525 d['context'] = json.loads(self.context)
532 self.delta = self.to_json()
534 d = json.loads(self.delta)
535 d["lab_token"] = self.accessrelation.lab_token
540 return json.dumps(self.to_dict())
542 def clear_delta(self):
544 d["lab_token"] = self.accessrelation.lab_token
545 self.delta = json.dumps(d)
547 def set_access_type(self, access_type):
548 self.access_type = access_type
549 d = json.loads(self.delta)
550 d['access_type'] = access_type
551 self.delta = json.dumps(d)
553 def set_user(self, user):
555 d = json.loads(self.delta)
556 d['user'] = self.user.id
557 self.delta = json.dumps(d)
559 def set_revoke(self, revoke):
561 d = json.loads(self.delta)
563 self.delta = json.dumps(d)
565 def set_context(self, context):
566 self.context = json.dumps(context)
567 d = json.loads(self.delta)
568 d['context'] = context
569 self.delta = json.dumps(d)
572 class SoftwareConfig(TaskConfig):
573 """Handles software installations, such as OPNFV or ONAP."""
575 opnfv = models.ForeignKey(OpnfvApiConfig, on_delete=models.CASCADE)
580 d['opnfv'] = self.opnfv.to_dict()
582 d["lab_token"] = self.softwarerelation.lab_token
583 self.delta = json.dumps(d)
589 d['opnfv'] = self.opnfv.get_delta()
590 d['lab_token'] = self.softwarerelation.lab_token
594 def clear_delta(self):
595 self.opnfv.clear_delta()
598 return json.dumps(self.to_dict())
601 class HardwareConfig(TaskConfig):
602 """Describes the desired configuration of the hardware."""
604 image = models.CharField(max_length=100, default="defimage")
605 power = models.CharField(max_length=100, default="off")
606 hostname = models.CharField(max_length=100, default="hostname")
607 ipmi_create = models.BooleanField(default=False)
608 delta = models.TextField()
610 keys = set(["id", "image", "power", "hostname", "ipmi_create"])
613 return self.format_delta(
614 self.hosthardwarerelation.get_resource().get_configuration(self.state),
615 self.hosthardwarerelation.lab_token)
618 class NetworkConfig(TaskConfig):
619 """Handles network configuration."""
621 interfaces = models.ManyToManyField(Interface)
622 delta = models.TextField()
626 hid = self.hostnetworkrelation.resource_id
628 for interface in self.interfaces.all():
629 d[hid][interface.mac_address] = []
630 for vlan in interface.config.all():
631 # TODO: should this come from the interface?
632 # e.g. will different interfaces for different resources need different configs?
633 d[hid][interface.mac_address].append({"vlan_id": vlan.vlan_id, "tagged": vlan.tagged})
638 return json.dumps(self.to_dict())
642 self.delta = self.to_json()
644 d = json.loads(self.delta)
645 d['lab_token'] = self.hostnetworkrelation.lab_token
648 def clear_delta(self):
649 self.delta = json.dumps(self.to_dict())
652 def add_interface(self, interface):
653 self.interfaces.add(interface)
654 d = json.loads(self.delta)
655 hid = self.hostnetworkrelation.resource_id
658 d[hid][interface.mac_address] = []
659 for vlan in interface.config.all():
660 d[hid][interface.mac_address].append({"vlan_id": vlan.vlan_id, "tagged": vlan.tagged})
661 self.delta = json.dumps(d)
664 class SnapshotConfig(TaskConfig):
666 resource_id = models.CharField(max_length=200, default="default_id")
667 image = models.IntegerField(null=True)
668 dashboard_id = models.IntegerField()
669 delta = models.TextField(default="{}")
674 d['host'] = self.host.labid
676 d['image'] = self.image
677 d['dashboard_id'] = self.dashboard_id
681 return json.dumps(self.to_dict())
685 self.delta = self.to_json()
688 d = json.loads(self.delta)
691 def clear_delta(self):
692 self.delta = json.dumps(self.to_dict())
695 def set_host(self, host):
697 d = json.loads(self.delta)
698 d['host'] = host.labid
699 self.delta = json.dumps(d)
701 def set_image(self, image):
703 d = json.loads(self.delta)
704 d['image'] = self.image
705 self.delta = json.dumps(d)
707 def clear_image(self):
709 d = json.loads(self.delta)
711 self.delta = json.dumps(d)
713 def set_dashboard_id(self, dash):
714 self.dashboard_id = dash
715 d = json.loads(self.delta)
716 d['dashboard_id'] = self.dashboard_id
717 self.delta = json.dumps(d)
719 def save(self, *args, **kwargs):
720 if len(ResourceQuery.filter(labid=self.resource_id)) != 1:
721 raise ValidationError("resource_id " + str(self.resource_id) + " does not refer to a single resource")
722 super().save(*args, **kwargs)
725 def get_task(task_id):
726 for taskclass in [AccessRelation, SoftwareRelation, HostHardwareRelation, HostNetworkRelation, SnapshotRelation]:
728 ret = taskclass.objects.get(task_id=task_id)
730 except taskclass.DoesNotExist:
732 from django.core.exceptions import ObjectDoesNotExist
733 raise ObjectDoesNotExist("Could not find matching TaskRelation instance")
737 return str(uuid.uuid4())
740 class TaskRelation(models.Model):
742 Relates a Job to a TaskConfig.
744 superclass that relates a Job to tasks anc maintains information
745 like status and messages from the lab
748 status = models.IntegerField(default=JobStatus.NEW)
749 job = models.ForeignKey(Job, on_delete=models.CASCADE)
750 config = models.OneToOneField(TaskConfig, on_delete=models.CASCADE)
751 task_id = models.CharField(default=get_task_uuid, max_length=37)
752 lab_token = models.CharField(default="null", max_length=50)
753 message = models.TextField(default="")
757 def delete(self, *args, **kwargs):
759 return super(self.__class__, self).delete(*args, **kwargs)
762 return "Generic Task"
768 class AccessRelation(TaskRelation):
769 config = models.OneToOneField(AccessConfig, on_delete=models.CASCADE)
775 def delete(self, *args, **kwargs):
777 return super(self.__class__, self).delete(*args, **kwargs)
780 class SoftwareRelation(TaskRelation):
781 config = models.OneToOneField(SoftwareConfig, on_delete=models.CASCADE)
785 return "Software Configuration Task"
787 def delete(self, *args, **kwargs):
789 return super(self.__class__, self).delete(*args, **kwargs)
792 class HostHardwareRelation(TaskRelation):
793 resource_id = models.CharField(max_length=200, default="default_id")
794 config = models.OneToOneField(HardwareConfig, on_delete=models.CASCADE)
798 return "Hardware Configuration Task"
801 return self.config.to_dict()
803 def delete(self, *args, **kwargs):
805 return super(self.__class__, self).delete(*args, **kwargs)
807 def save(self, *args, **kwargs):
808 if len(ResourceQuery.filter(labid=self.resource_id)) != 1:
809 raise ValidationError("resource_id " + str(self.resource_id) + " does not refer to a single resource")
810 super().save(*args, **kwargs)
812 def get_resource(self):
813 return ResourceQuery.get(labid=self.resource_id)
816 class HostNetworkRelation(TaskRelation):
817 resource_id = models.CharField(max_length=200, default="default_id")
818 config = models.OneToOneField(NetworkConfig, on_delete=models.CASCADE)
822 return "Network Configuration Task"
824 def delete(self, *args, **kwargs):
826 return super(self.__class__, self).delete(*args, **kwargs)
828 def save(self, *args, **kwargs):
829 if len(ResourceQuery.filter(labid=self.resource_id)) != 1:
830 raise ValidationError("resource_id " + str(self.resource_id) + " does not refer to a single resource")
831 super().save(*args, **kwargs)
833 def get_resource(self):
834 return ResourceQuery.get(labid=self.resource_id)
837 class SnapshotRelation(TaskRelation):
838 snapshot = models.ForeignKey(Image, on_delete=models.CASCADE)
839 config = models.OneToOneField(SnapshotConfig, on_delete=models.CASCADE)
843 return "Snapshot Task"
846 return self.config.to_dict()
848 def delete(self, *args, **kwargs):
850 return super(self.__class__, self).delete(*args, **kwargs)
853 class JobFactory(object):
854 """This class creates all the API models (jobs, tasks, etc) needed to fulfill a booking."""
857 def reimageHost(cls, new_image, booking, host):
858 """Modify an existing job to reimage the given host."""
859 job = Job.objects.get(booking=booking)
860 # make hardware task new
861 hardware_relation = HostHardwareRelation.objects.get(host=host, job=job)
862 hardware_relation.config.set_image(new_image.lab_id)
863 hardware_relation.config.save()
864 hardware_relation.status = JobStatus.NEW
866 # re-apply networking after host is reset
867 net_relation = HostNetworkRelation.objects.get(host=host, job=job)
868 net_relation.status = JobStatus.NEW
870 # re-apply ssh access after host is reset
871 for relation in AccessRelation.objects.filter(job=job, config__access_type="ssh"):
872 relation.status = JobStatus.NEW
875 hardware_relation.save()
879 def makeSnapshotTask(cls, image, booking, host):
880 relation = SnapshotRelation()
881 job = Job.objects.get(booking=booking)
882 config = SnapshotConfig.objects.create(dashboard_id=image.id)
885 relation.config = config
886 relation.config.save()
887 relation.config = relation.config
888 relation.snapshot = image
892 config.set_host(host)
896 def makeCompleteJob(cls, booking):
897 """Create everything that is needed to fulfill the given booking."""
898 resources = booking.resource.get_resources()
901 job = Job.objects.get(booking=booking)
903 job = Job.objects.create(status=JobStatus.NEW, booking=booking)
904 cls.makeHardwareConfigs(
908 cls.makeNetworkConfigs(
916 all_users = list(booking.collaborators.all())
917 all_users.append(booking.owner)
918 cls.makeAccessConfig(
924 for user in all_users:
926 cls.makeAccessConfig(
932 "key": user.userprofile.ssh_public_key.open().read().decode(encoding="UTF-8"),
933 "hosts": [r.labid for r in resources]
940 def makeHardwareConfigs(cls, resources=[], job=Job()):
942 Create and save HardwareConfig.
944 Helper function to create the tasks related to
945 configuring the hardware
947 for res in resources:
948 hardware_config = None
950 hardware_config = HardwareConfig.objects.get(relation__resource_id=res.labid)
952 hardware_config = HardwareConfig()
954 relation = HostHardwareRelation()
955 relation.resource_id = res.labid
957 relation.config = hardware_config
958 relation.config.save()
959 relation.config = relation.config
962 hardware_config.set("image", "hostname", "power", "ipmi_create")
963 hardware_config.save()
966 def makeAccessConfig(cls, users, access_type, revoke=False, job=Job(), context=False):
968 Create and save AccessConfig.
970 Helper function to create the tasks related to
971 configuring the VPN, SSH, etc access for users
974 relation = AccessRelation()
976 config = AccessConfig()
977 config.access_type = access_type
980 relation.config = config
984 config.set_context(context)
985 config.set_access_type(access_type)
986 config.set_revoke(revoke)
987 config.set_user(user)
991 def makeNetworkConfigs(cls, resources=[], job=Job()):
993 Create and save NetworkConfig.
995 Helper function to create the tasks related to
996 configuring the networking
998 for res in resources:
999 network_config = None
1001 network_config = NetworkConfig.objects.get(relation__host=res)
1003 network_config = NetworkConfig.objects.create()
1005 relation = HostNetworkRelation()
1006 relation.resource_id = res.labid
1008 network_config.save()
1009 relation.config = network_config
1011 network_config.clear_delta()
1013 # TODO: use get_interfaces() on resource
1014 for interface in res.interfaces.all():
1015 network_config.add_interface(interface)
1016 network_config.save()
1019 def make_bridge_config(cls, booking):
1020 if len(booking.resource.get_resources()) < 2:
1023 jumphost_config = ResourceOPNFVConfig.objects.filter(
1024 role__name__iexact="jumphost"
1026 jumphost = ResourceQuery.filter(
1027 bundle=booking.resource,
1028 config=jumphost_config.resource_config
1032 br_config = BridgeConfig.objects.create(opnfv_config=booking.opnfv_config)
1033 for iface in jumphost.interfaces.all():
1034 br_config.interfaces.add(iface)
1038 def makeSoftware(cls, booking=None, job=Job()):
1040 Create and save SoftwareConfig.
1042 Helper function to create the tasks related to
1043 configuring the desired software, e.g. an OPNFV deployment
1045 if not booking.opnfv_config:
1048 opnfv_api_config = OpnfvApiConfig.objects.create(
1049 opnfv_config=booking.opnfv_config,
1050 installer=booking.opnfv_config.installer.name,
1051 scenario=booking.opnfv_config.scenario.name,
1052 bridge_config=cls.make_bridge_config(booking)
1055 opnfv_api_config.set_xdf(booking, False)
1056 opnfv_api_config.save()
1058 for host in booking.resource.get_resources():
1059 opnfv_api_config.roles.add(host)
1060 software_config = SoftwareConfig.objects.create(opnfv=opnfv_api_config)
1061 software_relation = SoftwareRelation.objects.create(job=job, config=software_config)
1062 return software_relation
1065 JOB_TASK_CLASSLIST = [
1066 HostHardwareRelation,
1068 HostNetworkRelation,
1074 class JobTaskQuery(AbstractModelQuery):
1075 model_list = JOB_TASK_CLASSLIST