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):
529 self.delta = self.to_json()
531 return json.loads(self.delta)
534 class AccessConfig(TaskConfig):
535 access_type = models.CharField(max_length=50)
536 user = models.ForeignKey(User, on_delete=models.CASCADE)
537 revoke = models.BooleanField(default=False)
538 context = models.TextField(default="")
539 delta = models.TextField(default="{}")
543 d['access_type'] = self.access_type
544 d['user'] = self.user.id
545 d['revoke'] = self.revoke
547 d['context'] = json.loads(self.context)
554 self.delta = self.to_json()
556 d = json.loads(self.delta)
557 d["lab_token"] = self.accessrelation.lab_token
562 return json.dumps(self.to_dict())
564 def clear_delta(self):
566 d["lab_token"] = self.accessrelation.lab_token
567 self.delta = json.dumps(d)
569 def set_access_type(self, access_type):
570 self.access_type = access_type
571 d = json.loads(self.delta)
572 d['access_type'] = access_type
573 self.delta = json.dumps(d)
575 def set_user(self, user):
577 d = json.loads(self.delta)
578 d['user'] = self.user.id
579 self.delta = json.dumps(d)
581 def set_revoke(self, revoke):
583 d = json.loads(self.delta)
585 self.delta = json.dumps(d)
587 def set_context(self, context):
588 self.context = json.dumps(context)
589 d = json.loads(self.delta)
590 d['context'] = context
591 self.delta = json.dumps(d)
594 class SoftwareConfig(TaskConfig):
595 """Handles software installations, such as OPNFV or ONAP."""
597 opnfv = models.ForeignKey(OpnfvApiConfig, on_delete=models.CASCADE)
602 d['opnfv'] = self.opnfv.to_dict()
604 d["lab_token"] = self.softwarerelation.lab_token
605 self.delta = json.dumps(d)
611 d['opnfv'] = self.opnfv.get_delta()
612 d['lab_token'] = self.softwarerelation.lab_token
616 def clear_delta(self):
617 self.opnfv.clear_delta()
620 return json.dumps(self.to_dict())
623 class HardwareConfig(TaskConfig):
624 """Describes the desired configuration of the hardware."""
626 image = models.CharField(max_length=100, default="defimage")
627 power = models.CharField(max_length=100, default="off")
628 hostname = models.CharField(max_length=100, default="hostname")
629 ipmi_create = models.BooleanField(default=False)
630 delta = models.TextField()
632 keys = set(["id", "image", "power", "hostname", "ipmi_create"])
635 return self.format_delta(
636 self.hosthardwarerelation.get_resource().get_configuration(self.state),
637 self.hosthardwarerelation.lab_token)
640 class NetworkConfig(TaskConfig):
641 """Handles network configuration."""
643 interfaces = models.ManyToManyField(Interface)
644 delta = models.TextField()
648 hid = self.hostnetworkrelation.resource_id
650 for interface in self.interfaces.all():
651 d[hid][interface.mac_address] = []
652 for vlan in interface.config.all():
653 # TODO: should this come from the interface?
654 # e.g. will different interfaces for different resources need different configs?
655 d[hid][interface.mac_address].append({"vlan_id": vlan.vlan_id, "tagged": vlan.tagged})
660 return json.dumps(self.to_dict())
664 self.delta = self.to_json()
666 d = json.loads(self.delta)
667 d['lab_token'] = self.hostnetworkrelation.lab_token
670 def clear_delta(self):
671 self.delta = json.dumps(self.to_dict())
674 def add_interface(self, interface):
675 self.interfaces.add(interface)
676 d = json.loads(self.delta)
677 hid = self.hostnetworkrelation.resource_id
680 d[hid][interface.mac_address] = []
681 for vlan in interface.config.all():
682 d[hid][interface.mac_address].append({"vlan_id": vlan.vlan_id, "tagged": vlan.tagged})
683 self.delta = json.dumps(d)
686 class SnapshotConfig(TaskConfig):
688 resource_id = models.CharField(max_length=200, default="default_id")
689 image = models.IntegerField(null=True)
690 dashboard_id = models.IntegerField()
691 delta = models.TextField(default="{}")
696 d['host'] = self.host.labid
698 d['image'] = self.image
699 d['dashboard_id'] = self.dashboard_id
703 return json.dumps(self.to_dict())
707 self.delta = self.to_json()
710 d = json.loads(self.delta)
713 def clear_delta(self):
714 self.delta = json.dumps(self.to_dict())
717 def set_host(self, host):
719 d = json.loads(self.delta)
720 d['host'] = host.labid
721 self.delta = json.dumps(d)
723 def set_image(self, image):
725 d = json.loads(self.delta)
726 d['image'] = self.image
727 self.delta = json.dumps(d)
729 def clear_image(self):
731 d = json.loads(self.delta)
733 self.delta = json.dumps(d)
735 def set_dashboard_id(self, dash):
736 self.dashboard_id = dash
737 d = json.loads(self.delta)
738 d['dashboard_id'] = self.dashboard_id
739 self.delta = json.dumps(d)
741 def save(self, *args, **kwargs):
742 if len(ResourceQuery.filter(labid=self.resource_id)) != 1:
743 raise ValidationError("resource_id " + str(self.resource_id) + " does not refer to a single resource")
744 super().save(*args, **kwargs)
747 def get_task(task_id):
748 for taskclass in [AccessRelation, SoftwareRelation, HostHardwareRelation, HostNetworkRelation, SnapshotRelation]:
750 ret = taskclass.objects.get(task_id=task_id)
752 except taskclass.DoesNotExist:
754 from django.core.exceptions import ObjectDoesNotExist
755 raise ObjectDoesNotExist("Could not find matching TaskRelation instance")
759 return str(uuid.uuid4())
762 class TaskRelation(models.Model):
764 Relates a Job to a TaskConfig.
766 superclass that relates a Job to tasks anc maintains information
767 like status and messages from the lab
770 status = models.IntegerField(default=JobStatus.NEW)
771 job = models.ForeignKey(Job, on_delete=models.CASCADE)
772 config = models.OneToOneField(TaskConfig, on_delete=models.CASCADE)
773 task_id = models.CharField(default=get_task_uuid, max_length=37)
774 lab_token = models.CharField(default="null", max_length=50)
775 message = models.TextField(default="")
779 def delete(self, *args, **kwargs):
781 return super(self.__class__, self).delete(*args, **kwargs)
784 return "Generic Task"
790 class AccessRelation(TaskRelation):
791 config = models.OneToOneField(AccessConfig, on_delete=models.CASCADE)
797 def delete(self, *args, **kwargs):
799 return super(self.__class__, self).delete(*args, **kwargs)
802 class SoftwareRelation(TaskRelation):
803 config = models.OneToOneField(SoftwareConfig, on_delete=models.CASCADE)
807 return "Software Configuration Task"
809 def delete(self, *args, **kwargs):
811 return super(self.__class__, self).delete(*args, **kwargs)
814 class HostHardwareRelation(TaskRelation):
815 resource_id = models.CharField(max_length=200, default="default_id")
816 config = models.OneToOneField(HardwareConfig, on_delete=models.CASCADE)
820 return "Hardware Configuration Task"
823 return self.config.to_dict()
825 def delete(self, *args, **kwargs):
827 return super(self.__class__, self).delete(*args, **kwargs)
829 def save(self, *args, **kwargs):
830 if len(ResourceQuery.filter(labid=self.resource_id)) != 1:
831 raise ValidationError("resource_id " + str(self.resource_id) + " does not refer to a single resource")
832 super().save(*args, **kwargs)
834 def get_resource(self):
835 return ResourceQuery.get(labid=self.resource_id)
838 class HostNetworkRelation(TaskRelation):
839 resource_id = models.CharField(max_length=200, default="default_id")
840 config = models.OneToOneField(NetworkConfig, on_delete=models.CASCADE)
844 return "Network Configuration Task"
846 def delete(self, *args, **kwargs):
848 return super(self.__class__, self).delete(*args, **kwargs)
850 def save(self, *args, **kwargs):
851 if len(ResourceQuery.filter(labid=self.resource_id)) != 1:
852 raise ValidationError("resource_id " + str(self.resource_id) + " does not refer to a single resource")
853 super().save(*args, **kwargs)
855 def get_resource(self):
856 return ResourceQuery.get(labid=self.resource_id)
859 class SnapshotRelation(TaskRelation):
860 snapshot = models.ForeignKey(Image, on_delete=models.CASCADE)
861 config = models.OneToOneField(SnapshotConfig, on_delete=models.CASCADE)
865 return "Snapshot Task"
868 return self.config.to_dict()
870 def delete(self, *args, **kwargs):
872 return super(self.__class__, self).delete(*args, **kwargs)
875 class JobFactory(object):
876 """This class creates all the API models (jobs, tasks, etc) needed to fulfill a booking."""
879 def reimageHost(cls, new_image, booking, host):
880 """Modify an existing job to reimage the given host."""
881 job = Job.objects.get(booking=booking)
882 # make hardware task new
883 hardware_relation = HostHardwareRelation.objects.get(host=host, job=job)
884 hardware_relation.config.set_image(new_image.lab_id)
885 hardware_relation.config.save()
886 hardware_relation.status = JobStatus.NEW
888 # re-apply networking after host is reset
889 net_relation = HostNetworkRelation.objects.get(host=host, job=job)
890 net_relation.status = JobStatus.NEW
892 # re-apply ssh access after host is reset
893 for relation in AccessRelation.objects.filter(job=job, config__access_type="ssh"):
894 relation.status = JobStatus.NEW
897 hardware_relation.save()
901 def makeSnapshotTask(cls, image, booking, host):
902 relation = SnapshotRelation()
903 job = Job.objects.get(booking=booking)
904 config = SnapshotConfig.objects.create(dashboard_id=image.id)
907 relation.config = config
908 relation.config.save()
909 relation.config = relation.config
910 relation.snapshot = image
914 config.set_host(host)
918 def makeCompleteJob(cls, booking):
919 """Create everything that is needed to fulfill the given booking."""
920 resources = booking.resource.get_resources()
923 job = Job.objects.get(booking=booking)
925 job = Job.objects.create(status=JobStatus.NEW, booking=booking)
926 cls.makeHardwareConfigs(
930 cls.makeNetworkConfigs(
938 all_users = list(booking.collaborators.all())
939 all_users.append(booking.owner)
940 cls.makeAccessConfig(
946 for user in all_users:
948 cls.makeAccessConfig(
954 "key": user.userprofile.ssh_public_key.open().read().decode(encoding="UTF-8"),
955 "hosts": [r.labid for r in resources]
962 def makeHardwareConfigs(cls, resources=[], job=Job()):
964 Create and save HardwareConfig.
966 Helper function to create the tasks related to
967 configuring the hardware
969 for res in resources:
970 hardware_config = None
972 hardware_config = HardwareConfig.objects.get(relation__resource_id=res.labid)
974 hardware_config = HardwareConfig()
976 relation = HostHardwareRelation()
977 relation.resource_id = res.labid
979 relation.config = hardware_config
980 relation.config.save()
981 relation.config = relation.config
984 hardware_config.set("id", "image", "hostname", "power", "ipmi_create")
985 hardware_config.save()
988 def makeAccessConfig(cls, users, access_type, revoke=False, job=Job(), context=False):
990 Create and save AccessConfig.
992 Helper function to create the tasks related to
993 configuring the VPN, SSH, etc access for users
996 relation = AccessRelation()
998 config = AccessConfig()
999 config.access_type = access_type
1002 relation.config = config
1004 config.clear_delta()
1006 config.set_context(context)
1007 config.set_access_type(access_type)
1008 config.set_revoke(revoke)
1009 config.set_user(user)
1013 def makeNetworkConfigs(cls, resources=[], job=Job()):
1015 Create and save NetworkConfig.
1017 Helper function to create the tasks related to
1018 configuring the networking
1020 for res in resources:
1021 network_config = None
1023 network_config = NetworkConfig.objects.get(relation__host=res)
1025 network_config = NetworkConfig.objects.create()
1027 relation = HostNetworkRelation()
1028 relation.resource_id = res.labid
1030 network_config.save()
1031 relation.config = network_config
1033 network_config.clear_delta()
1035 # TODO: use get_interfaces() on resource
1036 for interface in res.interfaces.all():
1037 network_config.add_interface(interface)
1038 network_config.save()
1041 def make_bridge_config(cls, booking):
1042 if len(booking.resource.get_resources()) < 2:
1045 jumphost_config = ResourceOPNFVConfig.objects.filter(
1046 role__name__iexact="jumphost"
1048 jumphost = ResourceQuery.filter(
1049 bundle=booking.resource,
1050 config=jumphost_config.resource_config
1054 br_config = BridgeConfig.objects.create(opnfv_config=booking.opnfv_config)
1055 for iface in jumphost.interfaces.all():
1056 br_config.interfaces.add(iface)
1060 def makeSoftware(cls, booking=None, job=Job()):
1062 Create and save SoftwareConfig.
1064 Helper function to create the tasks related to
1065 configuring the desired software, e.g. an OPNFV deployment
1067 if not booking.opnfv_config:
1070 opnfv_api_config = OpnfvApiConfig.objects.create(
1071 opnfv_config=booking.opnfv_config,
1072 installer=booking.opnfv_config.installer.name,
1073 scenario=booking.opnfv_config.scenario.name,
1074 bridge_config=cls.make_bridge_config(booking)
1077 opnfv_api_config.set_xdf(booking, False)
1078 opnfv_api_config.save()
1080 for host in booking.resource.get_resources():
1081 opnfv_api_config.roles.add(host)
1082 software_config = SoftwareConfig.objects.create(opnfv=opnfv_api_config)
1083 software_relation = SoftwareRelation.objects.create(job=job, config=software_config)
1084 return software_relation
1087 JOB_TASK_CLASSLIST = [
1088 HostHardwareRelation,
1090 HostNetworkRelation,
1096 class JobTaskQuery(AbstractModelQuery):
1097 model_list = JOB_TASK_CLASSLIST