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 if self.state != ConfigState.CLEAN:
653 for vlan in interface.config.all():
654 # TODO: should this come from the interface?
655 # e.g. will different interfaces for different resources need different configs?
656 d[hid][interface.mac_address].append({"vlan_id": vlan.vlan_id, "tagged": vlan.tagged})
661 return json.dumps(self.to_dict())
665 self.delta = self.to_json()
667 d = json.loads(self.delta)
668 d['lab_token'] = self.hostnetworkrelation.lab_token
671 def clear_delta(self):
672 self.delta = json.dumps(self.to_dict())
675 def add_interface(self, interface):
676 self.interfaces.add(interface)
677 d = json.loads(self.delta)
678 hid = self.hostnetworkrelation.resource_id
681 d[hid][interface.mac_address] = []
682 for vlan in interface.config.all():
683 d[hid][interface.mac_address].append({"vlan_id": vlan.vlan_id, "tagged": vlan.tagged})
684 self.delta = json.dumps(d)
687 class SnapshotConfig(TaskConfig):
689 resource_id = models.CharField(max_length=200, default="default_id")
690 image = models.IntegerField(null=True)
691 dashboard_id = models.IntegerField()
692 delta = models.TextField(default="{}")
697 d['host'] = self.host.labid
699 d['image'] = self.image
700 d['dashboard_id'] = self.dashboard_id
704 return json.dumps(self.to_dict())
708 self.delta = self.to_json()
711 d = json.loads(self.delta)
714 def clear_delta(self):
715 self.delta = json.dumps(self.to_dict())
718 def set_host(self, host):
720 d = json.loads(self.delta)
721 d['host'] = host.labid
722 self.delta = json.dumps(d)
724 def set_image(self, image):
726 d = json.loads(self.delta)
727 d['image'] = self.image
728 self.delta = json.dumps(d)
730 def clear_image(self):
732 d = json.loads(self.delta)
734 self.delta = json.dumps(d)
736 def set_dashboard_id(self, dash):
737 self.dashboard_id = dash
738 d = json.loads(self.delta)
739 d['dashboard_id'] = self.dashboard_id
740 self.delta = json.dumps(d)
742 def save(self, *args, **kwargs):
743 if len(ResourceQuery.filter(labid=self.resource_id)) != 1:
744 raise ValidationError("resource_id " + str(self.resource_id) + " does not refer to a single resource")
745 super().save(*args, **kwargs)
748 def get_task(task_id):
749 for taskclass in [AccessRelation, SoftwareRelation, HostHardwareRelation, HostNetworkRelation, SnapshotRelation]:
751 ret = taskclass.objects.get(task_id=task_id)
753 except taskclass.DoesNotExist:
755 from django.core.exceptions import ObjectDoesNotExist
756 raise ObjectDoesNotExist("Could not find matching TaskRelation instance")
760 return str(uuid.uuid4())
763 class TaskRelation(models.Model):
765 Relates a Job to a TaskConfig.
767 superclass that relates a Job to tasks anc maintains information
768 like status and messages from the lab
771 status = models.IntegerField(default=JobStatus.NEW)
772 job = models.ForeignKey(Job, on_delete=models.CASCADE)
773 config = models.OneToOneField(TaskConfig, on_delete=models.CASCADE)
774 task_id = models.CharField(default=get_task_uuid, max_length=37)
775 lab_token = models.CharField(default="null", max_length=50)
776 message = models.TextField(default="")
780 def delete(self, *args, **kwargs):
782 return super(self.__class__, self).delete(*args, **kwargs)
785 return "Generic Task"
791 class AccessRelation(TaskRelation):
792 config = models.OneToOneField(AccessConfig, on_delete=models.CASCADE)
798 def delete(self, *args, **kwargs):
800 return super(self.__class__, self).delete(*args, **kwargs)
803 class SoftwareRelation(TaskRelation):
804 config = models.OneToOneField(SoftwareConfig, on_delete=models.CASCADE)
808 return "Software Configuration Task"
810 def delete(self, *args, **kwargs):
812 return super(self.__class__, self).delete(*args, **kwargs)
815 class HostHardwareRelation(TaskRelation):
816 resource_id = models.CharField(max_length=200, default="default_id")
817 config = models.OneToOneField(HardwareConfig, on_delete=models.CASCADE)
821 return "Hardware Configuration Task"
824 return self.config.to_dict()
826 def delete(self, *args, **kwargs):
828 return super(self.__class__, self).delete(*args, **kwargs)
830 def save(self, *args, **kwargs):
831 if len(ResourceQuery.filter(labid=self.resource_id)) != 1:
832 raise ValidationError("resource_id " + str(self.resource_id) + " does not refer to a single resource")
833 super().save(*args, **kwargs)
835 def get_resource(self):
836 return ResourceQuery.get(labid=self.resource_id)
839 class HostNetworkRelation(TaskRelation):
840 resource_id = models.CharField(max_length=200, default="default_id")
841 config = models.OneToOneField(NetworkConfig, on_delete=models.CASCADE)
845 return "Network Configuration Task"
847 def delete(self, *args, **kwargs):
849 return super(self.__class__, self).delete(*args, **kwargs)
851 def save(self, *args, **kwargs):
852 if len(ResourceQuery.filter(labid=self.resource_id)) != 1:
853 raise ValidationError("resource_id " + str(self.resource_id) + " does not refer to a single resource")
854 super().save(*args, **kwargs)
856 def get_resource(self):
857 return ResourceQuery.get(labid=self.resource_id)
860 class SnapshotRelation(TaskRelation):
861 snapshot = models.ForeignKey(Image, on_delete=models.CASCADE)
862 config = models.OneToOneField(SnapshotConfig, on_delete=models.CASCADE)
866 return "Snapshot Task"
869 return self.config.to_dict()
871 def delete(self, *args, **kwargs):
873 return super(self.__class__, self).delete(*args, **kwargs)
876 class JobFactory(object):
877 """This class creates all the API models (jobs, tasks, etc) needed to fulfill a booking."""
880 def reimageHost(cls, new_image, booking, host):
881 """Modify an existing job to reimage the given host."""
882 job = Job.objects.get(booking=booking)
883 # make hardware task new
884 hardware_relation = HostHardwareRelation.objects.get(host=host, job=job)
885 hardware_relation.config.set_image(new_image.lab_id)
886 hardware_relation.config.save()
887 hardware_relation.status = JobStatus.NEW
889 # re-apply networking after host is reset
890 net_relation = HostNetworkRelation.objects.get(host=host, job=job)
891 net_relation.status = JobStatus.NEW
893 # re-apply ssh access after host is reset
894 for relation in AccessRelation.objects.filter(job=job, config__access_type="ssh"):
895 relation.status = JobStatus.NEW
898 hardware_relation.save()
902 def makeSnapshotTask(cls, image, booking, host):
903 relation = SnapshotRelation()
904 job = Job.objects.get(booking=booking)
905 config = SnapshotConfig.objects.create(dashboard_id=image.id)
908 relation.config = config
909 relation.config.save()
910 relation.config = relation.config
911 relation.snapshot = image
915 config.set_host(host)
919 def makeCompleteJob(cls, booking):
920 """Create everything that is needed to fulfill the given booking."""
921 resources = booking.resource.get_resources()
924 job = Job.objects.get(booking=booking)
926 job = Job.objects.create(status=JobStatus.NEW, booking=booking)
927 cls.makeHardwareConfigs(
931 cls.makeNetworkConfigs(
939 all_users = list(booking.collaborators.all())
940 all_users.append(booking.owner)
941 cls.makeAccessConfig(
947 for user in all_users:
949 cls.makeAccessConfig(
955 "key": user.userprofile.ssh_public_key.open().read().decode(encoding="UTF-8"),
956 "hosts": [r.labid for r in resources]
963 def makeHardwareConfigs(cls, resources=[], job=Job()):
965 Create and save HardwareConfig.
967 Helper function to create the tasks related to
968 configuring the hardware
970 for res in resources:
971 hardware_config = None
973 hardware_config = HardwareConfig.objects.get(relation__resource_id=res.labid)
975 hardware_config = HardwareConfig()
977 relation = HostHardwareRelation()
978 relation.resource_id = res.labid
980 relation.config = hardware_config
981 relation.config.save()
982 relation.config = relation.config
985 hardware_config.set("id", "image", "hostname", "power", "ipmi_create")
986 hardware_config.save()
989 def makeAccessConfig(cls, users, access_type, revoke=False, job=Job(), context=False):
991 Create and save AccessConfig.
993 Helper function to create the tasks related to
994 configuring the VPN, SSH, etc access for users
997 relation = AccessRelation()
999 config = AccessConfig()
1000 config.access_type = access_type
1003 relation.config = config
1005 config.clear_delta()
1007 config.set_context(context)
1008 config.set_access_type(access_type)
1009 config.set_revoke(revoke)
1010 config.set_user(user)
1014 def makeNetworkConfigs(cls, resources=[], job=Job()):
1016 Create and save NetworkConfig.
1018 Helper function to create the tasks related to
1019 configuring the networking
1021 for res in resources:
1022 network_config = None
1024 network_config = NetworkConfig.objects.get(relation__host=res)
1026 network_config = NetworkConfig.objects.create()
1028 relation = HostNetworkRelation()
1029 relation.resource_id = res.labid
1031 network_config.save()
1032 relation.config = network_config
1034 network_config.clear_delta()
1036 # TODO: use get_interfaces() on resource
1037 for interface in res.interfaces.all():
1038 network_config.add_interface(interface)
1039 network_config.save()
1042 def make_bridge_config(cls, booking):
1043 if len(booking.resource.get_resources()) < 2:
1046 jumphost_config = ResourceOPNFVConfig.objects.filter(
1047 role__name__iexact="jumphost"
1049 jumphost = ResourceQuery.filter(
1050 bundle=booking.resource,
1051 config=jumphost_config.resource_config
1055 br_config = BridgeConfig.objects.create(opnfv_config=booking.opnfv_config)
1056 for iface in jumphost.interfaces.all():
1057 br_config.interfaces.add(iface)
1061 def makeSoftware(cls, booking=None, job=Job()):
1063 Create and save SoftwareConfig.
1065 Helper function to create the tasks related to
1066 configuring the desired software, e.g. an OPNFV deployment
1068 if not booking.opnfv_config:
1071 opnfv_api_config = OpnfvApiConfig.objects.create(
1072 opnfv_config=booking.opnfv_config,
1073 installer=booking.opnfv_config.installer.name,
1074 scenario=booking.opnfv_config.scenario.name,
1075 bridge_config=cls.make_bridge_config(booking)
1078 opnfv_api_config.set_xdf(booking, False)
1079 opnfv_api_config.save()
1081 for host in booking.resource.get_resources():
1082 opnfv_api_config.roles.add(host)
1083 software_config = SoftwareConfig.objects.create(opnfv=opnfv_api_config)
1084 software_relation = SoftwareRelation.objects.create(job=job, config=software_config)
1085 return software_relation
1088 JOB_TASK_CLASSLIST = [
1089 HostHardwareRelation,
1091 HostNetworkRelation,
1097 class JobTaskQuery(AbstractModelQuery):
1098 model_list = JOB_TASK_CLASSLIST