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, UserProfile
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 format_user(self, userprofile):
179 "id": userprofile.user.id,
180 "username": userprofile.user.username,
181 "email": userprofile.email_addr,
182 "first_name": userprofile.user.first_name,
183 "last_name": userprofile.user.last_name,
184 "company": userprofile.company
188 userlist = [self.format_user(profile) for profile in UserProfile.objects.select_related("user").all()]
190 return json.dumps({"users": userlist})
192 def get_user(self, user_id):
193 user = User.objects.get(pk=user_id)
195 profile = get_object_or_404(UserProfile, user=user)
197 return json.dumps(self.format_user(profile))
199 def get_inventory(self):
201 resources = ResourceQuery.filter(lab=self.lab)
202 images = Image.objects.filter(from_lab=self.lab)
203 profiles = ResourceProfile.objects.filter(labs=self.lab)
204 inventory['resources'] = self.serialize_resources(resources)
205 inventory['images'] = self.serialize_images(images)
206 inventory['host_types'] = self.serialize_host_profiles(profiles)
209 def get_host(self, hostname):
210 resource = ResourceQuery.filter(labid=hostname, lab=self.lab)
211 if len(resource) != 1:
212 return HttpResponseNotFound("Could not find single host with id " + str(hostname))
213 resource = resource[0]
215 "booked": resource.booked,
216 "working": resource.working,
217 "type": resource.profile.name
220 def update_host(self, hostname, data):
221 resource = ResourceQuery.filter(labid=hostname, lab=self.lab)
222 if len(resource) != 1:
223 return HttpResponseNotFound("Could not find single host with id " + str(hostname))
224 resource = resource[0]
225 if "working" in data:
226 working = data['working'] == "true"
227 resource.working = working
229 return self.get_host(hostname)
231 def get_status(self):
232 return {"status": self.lab.status}
234 def set_status(self, payload):
237 def get_current_jobs(self):
238 jobs = Job.objects.filter(booking__lab=self.lab)
240 return self.serialize_jobs(jobs, status=JobStatus.CURRENT)
242 def get_new_jobs(self):
243 jobs = Job.objects.filter(booking__lab=self.lab)
245 return self.serialize_jobs(jobs, status=JobStatus.NEW)
247 def get_done_jobs(self):
248 jobs = Job.objects.filter(booking__lab=self.lab)
250 return self.serialize_jobs(jobs, status=JobStatus.DONE)
252 def get_job(self, jobid):
253 return Job.objects.get(pk=jobid).to_dict()
255 def update_job(self, jobid, data):
258 def serialize_jobs(self, jobs, status=JobStatus.NEW):
261 jsonized_job = job.get_delta(status)
262 if len(jsonized_job['payload']) < 1:
264 job_ser.append(jsonized_job)
268 def serialize_resources(self, resources):
269 # TODO: rewrite for Resource model
271 for res in resources:
274 'hostname': res.name,
275 'host_type': res.profile.name
277 for iface in res.get_interfaces():
278 r['interfaces'].append({
279 'mac': iface.mac_address,
280 'busaddr': iface.bus_address,
282 'switchport': {"switch_name": iface.switch_name, "port_name": iface.port_name}
286 def serialize_images(self, images):
292 "lab_id": image.lab_id,
293 "dashboard_id": image.id
298 def serialize_resource_profiles(self, profiles):
300 for profile in profiles:
303 "cores": profile.cpuprofile.first().cores,
304 "arch": profile.cpuprofile.first().architecture,
305 "cpus": profile.cpuprofile.first().cpus,
308 for disk in profile.storageprofile.all():
311 "type": disk.media_type,
315 p['description'] = profile.description
317 for iface in profile.interfaceprofile.all():
318 p['interfaces'].append(
320 "speed": iface.speed,
325 p['ram'] = {"amount": profile.ramprofile.first().amount}
326 p['name'] = profile.name
327 profile_ser.append(p)
331 class Job(models.Model):
333 A Job to be performed by the Lab.
335 The API uses Jobs and Tasks to communicate actions that need to be taken to the Lab
336 that is hosting a booking. A booking from a user has an associated Job which tells
337 the lab how to configure the hardware, networking, etc to fulfill the booking
339 This is the class that is serialized and put into the api
342 booking = models.OneToOneField(Booking, on_delete=models.CASCADE, null=True)
343 status = models.IntegerField(default=JobStatus.NEW)
344 complete = models.BooleanField(default=False)
348 for relation in self.get_tasklist():
349 if relation.job_key not in d:
350 d[relation.job_key] = {}
351 d[relation.job_key][relation.task_id] = relation.config.to_dict()
353 return {"id": self.id, "payload": d}
355 def get_tasklist(self, status="all"):
357 return JobTaskQuery.filter(job=self, status=status)
358 return JobTaskQuery.filter(job=self)
360 def is_fulfilled(self):
362 If a job has been completed by the lab.
364 This method should return true if all of the job's tasks are done,
367 my_tasks = self.get_tasklist()
368 for task in my_tasks:
369 if task.status != JobStatus.DONE:
373 def get_delta(self, status):
375 for relation in self.get_tasklist(status=status):
376 if relation.job_key not in d:
377 d[relation.job_key] = {}
378 d[relation.job_key][relation.task_id] = relation.config.get_delta()
380 return {"id": self.id, "payload": d}
383 return json.dumps(self.to_dict())
386 class TaskConfig(models.Model):
387 state = models.IntegerField(default=ConfigState.NEW)
389 keys = set() # TODO: This needs to be an instance variable, not a class variable
390 delta_keys_list = models.CharField(max_length=200, default="[]")
393 def delta_keys(self):
394 return list(set(json.loads(self.delta_keys_list)))
397 def delta_keys(self, keylist):
398 self.delta_keys_list = json.dumps(keylist)
401 raise NotImplementedError
404 raise NotImplementedError
406 def format_delta(self, config, token):
407 delta = {k: config[k] for k in self.delta_keys}
408 delta['lab_token'] = token
412 return json.dumps(self.to_dict())
414 def clear_delta(self):
417 def set(self, *args):
418 dkeys = self.delta_keys
422 self.delta_keys = dkeys
425 class BridgeConfig(models.Model):
426 """Displays mapping between jumphost interfaces and bridges."""
428 interfaces = models.ManyToManyField(Interface)
429 opnfv_config = models.ForeignKey(OPNFVConfig, on_delete=models.CASCADE)
433 hid = ResourceQuery.get(interface__pk=self.interfaces.first().pk).labid
435 for interface in self.interfaces.all():
436 d[hid][interface.mac_address] = []
437 for vlan in interface.config.all():
438 network_role = self.opnfv_model.networks().filter(network=vlan.network)
439 bridge = IDFTemplater.bridge_names[network_role.name]
441 "vlan_id": vlan.vlan_id,
442 "tagged": vlan.tagged,
445 d[hid][interface.mac_address].append(br_config)
449 return json.dumps(self.to_dict())
452 class OpnfvApiConfig(models.Model):
454 installer = models.CharField(max_length=200)
455 scenario = models.CharField(max_length=300)
456 roles = models.ManyToManyField(ResourceOPNFVConfig)
457 # pdf and idf are url endpoints, not the actual file
458 pdf = models.CharField(max_length=100)
459 idf = models.CharField(max_length=100)
460 bridge_config = models.OneToOneField(BridgeConfig, on_delete=models.CASCADE, null=True)
461 delta = models.TextField()
462 opnfv_config = models.ForeignKey(OPNFVConfig, null=True, on_delete=models.SET_NULL)
466 if not self.opnfv_config:
469 d['installer'] = self.installer
471 d['scenario'] = self.scenario
476 if self.bridge_config:
477 d['bridged_interfaces'] = self.bridge_config.to_dict()
479 hosts = self.roles.all()
484 host.labid: self.opnfv_config.host_opnfv_config.get(
485 host_config__pk=host.config.pk
492 return json.dumps(self.to_dict())
494 def set_installer(self, installer):
495 self.installer = installer
496 d = json.loads(self.delta)
497 d['installer'] = installer
498 self.delta = json.dumps(d)
500 def set_scenario(self, scenario):
501 self.scenario = scenario
502 d = json.loads(self.delta)
503 d['scenario'] = scenario
504 self.delta = json.dumps(d)
506 def set_xdf(self, booking, update_delta=True):
507 kwargs = {'lab_name': booking.lab.name, 'booking_id': booking.id}
508 self.pdf = reverse('get-pdf', kwargs=kwargs)
509 self.idf = reverse('get-idf', kwargs=kwargs)
511 d = json.loads(self.delta)
514 self.delta = json.dumps(d)
516 def add_role(self, host):
518 d = json.loads(self.delta)
521 d['roles'].append({host.labid: host.config.opnfvRole.name})
522 self.delta = json.dumps(d)
524 def clear_delta(self):
528 return json.loads(self.to_json())
531 class AccessConfig(TaskConfig):
532 access_type = models.CharField(max_length=50)
533 user = models.ForeignKey(User, on_delete=models.CASCADE)
534 revoke = models.BooleanField(default=False)
535 context = models.TextField(default="")
536 delta = models.TextField(default="{}")
540 d['access_type'] = self.access_type
541 d['user'] = self.user.id
542 d['revoke'] = self.revoke
544 d['context'] = json.loads(self.context)
550 d = json.loads(self.to_json())
551 d["lab_token"] = self.accessrelation.lab_token
556 return json.dumps(self.to_dict())
558 def clear_delta(self):
560 d["lab_token"] = self.accessrelation.lab_token
561 self.delta = json.dumps(d)
563 def set_access_type(self, access_type):
564 self.access_type = access_type
565 d = json.loads(self.delta)
566 d['access_type'] = access_type
567 self.delta = json.dumps(d)
569 def set_user(self, user):
571 d = json.loads(self.delta)
572 d['user'] = self.user.id
573 self.delta = json.dumps(d)
575 def set_revoke(self, revoke):
577 d = json.loads(self.delta)
579 self.delta = json.dumps(d)
581 def set_context(self, context):
582 self.context = json.dumps(context)
583 d = json.loads(self.delta)
584 d['context'] = context
585 self.delta = json.dumps(d)
588 class SoftwareConfig(TaskConfig):
589 """Handles software installations, such as OPNFV or ONAP."""
591 opnfv = models.ForeignKey(OpnfvApiConfig, on_delete=models.CASCADE)
596 d['opnfv'] = self.opnfv.to_dict()
598 d["lab_token"] = self.softwarerelation.lab_token
599 self.delta = json.dumps(d)
605 d['opnfv'] = self.opnfv.get_delta()
606 d['lab_token'] = self.softwarerelation.lab_token
610 def clear_delta(self):
611 self.opnfv.clear_delta()
614 return json.dumps(self.to_dict())
617 class HardwareConfig(TaskConfig):
618 """Describes the desired configuration of the hardware."""
620 image = models.CharField(max_length=100, default="defimage")
621 power = models.CharField(max_length=100, default="off")
622 hostname = models.CharField(max_length=100, default="hostname")
623 ipmi_create = models.BooleanField(default=False)
624 delta = models.TextField()
626 keys = set(["id", "image", "power", "hostname", "ipmi_create"])
629 return self.format_delta(
630 self.hosthardwarerelation.get_resource().get_configuration(self.state),
631 self.hosthardwarerelation.lab_token)
634 class NetworkConfig(TaskConfig):
635 """Handles network configuration."""
637 interfaces = models.ManyToManyField(Interface)
638 delta = models.TextField()
642 hid = self.hostnetworkrelation.resource_id
644 for interface in self.interfaces.all():
645 d[hid][interface.mac_address] = []
646 if self.state != ConfigState.CLEAN:
647 for vlan in interface.config.all():
648 # TODO: should this come from the interface?
649 # e.g. will different interfaces for different resources need different configs?
650 d[hid][interface.mac_address].append({"vlan_id": vlan.vlan_id, "tagged": vlan.tagged})
655 return json.dumps(self.to_dict())
658 d = json.loads(self.to_json())
659 d['lab_token'] = self.hostnetworkrelation.lab_token
662 def clear_delta(self):
663 self.delta = json.dumps(self.to_dict())
666 def add_interface(self, interface):
667 self.interfaces.add(interface)
668 d = json.loads(self.delta)
669 hid = self.hostnetworkrelation.resource_id
672 d[hid][interface.mac_address] = []
673 for vlan in interface.config.all():
674 d[hid][interface.mac_address].append({"vlan_id": vlan.vlan_id, "tagged": vlan.tagged})
675 self.delta = json.dumps(d)
678 class SnapshotConfig(TaskConfig):
680 resource_id = models.CharField(max_length=200, default="default_id")
681 image = models.IntegerField(null=True)
682 dashboard_id = models.IntegerField()
683 delta = models.TextField(default="{}")
688 d['host'] = self.host.labid
690 d['image'] = self.image
691 d['dashboard_id'] = self.dashboard_id
695 return json.dumps(self.to_dict())
698 d = json.loads(self.to_json())
701 def clear_delta(self):
702 self.delta = json.dumps(self.to_dict())
705 def set_host(self, host):
707 d = json.loads(self.delta)
708 d['host'] = host.labid
709 self.delta = json.dumps(d)
711 def set_image(self, image):
713 d = json.loads(self.delta)
714 d['image'] = self.image
715 self.delta = json.dumps(d)
717 def clear_image(self):
719 d = json.loads(self.delta)
721 self.delta = json.dumps(d)
723 def set_dashboard_id(self, dash):
724 self.dashboard_id = dash
725 d = json.loads(self.delta)
726 d['dashboard_id'] = self.dashboard_id
727 self.delta = json.dumps(d)
729 def save(self, *args, **kwargs):
730 if len(ResourceQuery.filter(labid=self.resource_id)) != 1:
731 raise ValidationError("resource_id " + str(self.resource_id) + " does not refer to a single resource")
732 super().save(*args, **kwargs)
735 def get_task(task_id):
736 for taskclass in [AccessRelation, SoftwareRelation, HostHardwareRelation, HostNetworkRelation, SnapshotRelation]:
738 ret = taskclass.objects.get(task_id=task_id)
740 except taskclass.DoesNotExist:
742 from django.core.exceptions import ObjectDoesNotExist
743 raise ObjectDoesNotExist("Could not find matching TaskRelation instance")
747 return str(uuid.uuid4())
750 class TaskRelation(models.Model):
752 Relates a Job to a TaskConfig.
754 superclass that relates a Job to tasks anc maintains information
755 like status and messages from the lab
758 status = models.IntegerField(default=JobStatus.NEW)
759 job = models.ForeignKey(Job, on_delete=models.CASCADE)
760 config = models.OneToOneField(TaskConfig, on_delete=models.CASCADE)
761 task_id = models.CharField(default=get_task_uuid, max_length=37)
762 lab_token = models.CharField(default="null", max_length=50)
763 message = models.TextField(default="")
767 def delete(self, *args, **kwargs):
769 return super(self.__class__, self).delete(*args, **kwargs)
772 return "Generic Task"
778 class AccessRelation(TaskRelation):
779 config = models.OneToOneField(AccessConfig, on_delete=models.CASCADE)
785 def delete(self, *args, **kwargs):
787 return super(self.__class__, self).delete(*args, **kwargs)
790 class SoftwareRelation(TaskRelation):
791 config = models.OneToOneField(SoftwareConfig, on_delete=models.CASCADE)
795 return "Software Configuration Task"
797 def delete(self, *args, **kwargs):
799 return super(self.__class__, self).delete(*args, **kwargs)
802 class HostHardwareRelation(TaskRelation):
803 resource_id = models.CharField(max_length=200, default="default_id")
804 config = models.OneToOneField(HardwareConfig, on_delete=models.CASCADE)
808 return "Hardware Configuration Task"
811 return self.config.to_dict()
813 def delete(self, *args, **kwargs):
815 return super(self.__class__, self).delete(*args, **kwargs)
817 def save(self, *args, **kwargs):
818 if len(ResourceQuery.filter(labid=self.resource_id)) != 1:
819 raise ValidationError("resource_id " + str(self.resource_id) + " does not refer to a single resource")
820 super().save(*args, **kwargs)
822 def get_resource(self):
823 return ResourceQuery.get(labid=self.resource_id)
826 class HostNetworkRelation(TaskRelation):
827 resource_id = models.CharField(max_length=200, default="default_id")
828 config = models.OneToOneField(NetworkConfig, on_delete=models.CASCADE)
832 return "Network Configuration Task"
834 def delete(self, *args, **kwargs):
836 return super(self.__class__, self).delete(*args, **kwargs)
838 def save(self, *args, **kwargs):
839 if len(ResourceQuery.filter(labid=self.resource_id)) != 1:
840 raise ValidationError("resource_id " + str(self.resource_id) + " does not refer to a single resource")
841 super().save(*args, **kwargs)
843 def get_resource(self):
844 return ResourceQuery.get(labid=self.resource_id)
847 class SnapshotRelation(TaskRelation):
848 snapshot = models.ForeignKey(Image, on_delete=models.CASCADE)
849 config = models.OneToOneField(SnapshotConfig, on_delete=models.CASCADE)
853 return "Snapshot Task"
856 return self.config.to_dict()
858 def delete(self, *args, **kwargs):
860 return super(self.__class__, self).delete(*args, **kwargs)
863 class JobFactory(object):
864 """This class creates all the API models (jobs, tasks, etc) needed to fulfill a booking."""
867 def reimageHost(cls, new_image, booking, host):
868 """Modify an existing job to reimage the given host."""
869 job = Job.objects.get(booking=booking)
870 # make hardware task new
871 hardware_relation = HostHardwareRelation.objects.get(host=host, job=job)
872 hardware_relation.config.set_image(new_image.lab_id)
873 hardware_relation.config.save()
874 hardware_relation.status = JobStatus.NEW
876 # re-apply networking after host is reset
877 net_relation = HostNetworkRelation.objects.get(host=host, job=job)
878 net_relation.status = JobStatus.NEW
880 # re-apply ssh access after host is reset
881 for relation in AccessRelation.objects.filter(job=job, config__access_type="ssh"):
882 relation.status = JobStatus.NEW
885 hardware_relation.save()
889 def makeSnapshotTask(cls, image, booking, host):
890 relation = SnapshotRelation()
891 job = Job.objects.get(booking=booking)
892 config = SnapshotConfig.objects.create(dashboard_id=image.id)
895 relation.config = config
896 relation.config.save()
897 relation.config = relation.config
898 relation.snapshot = image
902 config.set_host(host)
906 def makeCompleteJob(cls, booking):
907 """Create everything that is needed to fulfill the given booking."""
908 resources = booking.resource.get_resources()
911 job = Job.objects.get(booking=booking)
913 job = Job.objects.create(status=JobStatus.NEW, booking=booking)
914 cls.makeHardwareConfigs(
918 cls.makeNetworkConfigs(
926 all_users = list(booking.collaborators.all())
927 all_users.append(booking.owner)
928 cls.makeAccessConfig(
934 for user in all_users:
936 cls.makeAccessConfig(
942 "key": user.userprofile.ssh_public_key.open().read().decode(encoding="UTF-8"),
943 "hosts": [r.labid for r in resources]
950 def makeHardwareConfigs(cls, resources=[], job=Job()):
952 Create and save HardwareConfig.
954 Helper function to create the tasks related to
955 configuring the hardware
957 for res in resources:
958 hardware_config = None
960 hardware_config = HardwareConfig.objects.get(relation__resource_id=res.labid)
962 hardware_config = HardwareConfig()
964 relation = HostHardwareRelation()
965 relation.resource_id = res.labid
967 relation.config = hardware_config
968 relation.config.save()
969 relation.config = relation.config
972 hardware_config.set("id", "image", "hostname", "power", "ipmi_create")
973 hardware_config.save()
976 def makeAccessConfig(cls, users, access_type, revoke=False, job=Job(), context=False):
978 Create and save AccessConfig.
980 Helper function to create the tasks related to
981 configuring the VPN, SSH, etc access for users
984 relation = AccessRelation()
986 config = AccessConfig()
987 config.access_type = access_type
990 relation.config = config
994 config.set_context(context)
995 config.set_access_type(access_type)
996 config.set_revoke(revoke)
997 config.set_user(user)
1001 def makeNetworkConfigs(cls, resources=[], job=Job()):
1003 Create and save NetworkConfig.
1005 Helper function to create the tasks related to
1006 configuring the networking
1008 for res in resources:
1009 network_config = None
1011 network_config = NetworkConfig.objects.get(relation__host=res)
1013 network_config = NetworkConfig.objects.create()
1015 relation = HostNetworkRelation()
1016 relation.resource_id = res.labid
1018 network_config.save()
1019 relation.config = network_config
1021 network_config.clear_delta()
1023 # TODO: use get_interfaces() on resource
1024 for interface in res.interfaces.all():
1025 network_config.add_interface(interface)
1026 network_config.save()
1029 def make_bridge_config(cls, booking):
1030 if len(booking.resource.get_resources()) < 2:
1033 jumphost_config = ResourceOPNFVConfig.objects.filter(
1034 role__name__iexact="jumphost"
1036 jumphost = ResourceQuery.filter(
1037 bundle=booking.resource,
1038 config=jumphost_config.resource_config
1042 br_config = BridgeConfig.objects.create(opnfv_config=booking.opnfv_config)
1043 for iface in jumphost.interfaces.all():
1044 br_config.interfaces.add(iface)
1048 def makeSoftware(cls, booking=None, job=Job()):
1050 Create and save SoftwareConfig.
1052 Helper function to create the tasks related to
1053 configuring the desired software, e.g. an OPNFV deployment
1055 if not booking.opnfv_config:
1058 opnfv_api_config = OpnfvApiConfig.objects.create(
1059 opnfv_config=booking.opnfv_config,
1060 installer=booking.opnfv_config.installer.name,
1061 scenario=booking.opnfv_config.scenario.name,
1062 bridge_config=cls.make_bridge_config(booking)
1065 opnfv_api_config.set_xdf(booking, False)
1066 opnfv_api_config.save()
1068 for host in booking.resource.get_resources():
1069 opnfv_api_config.roles.add(host)
1070 software_config = SoftwareConfig.objects.create(opnfv=opnfv_api_config)
1071 software_relation = SoftwareRelation.objects.create(job=job, config=software_config)
1072 return software_relation
1075 JOB_TASK_CLASSLIST = [
1076 HostHardwareRelation,
1078 HostNetworkRelation,
1084 class JobTaskQuery(AbstractModelQuery):
1085 model_list = JOB_TASK_CLASSLIST