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 profile = get_object_or_404(UserProfile, pk=user_id)
195 return json.dumps(self.format_user(profile))
197 def get_inventory(self):
199 resources = ResourceQuery.filter(lab=self.lab)
200 images = Image.objects.filter(from_lab=self.lab)
201 profiles = ResourceProfile.objects.filter(labs=self.lab)
202 inventory['resources'] = self.serialize_resources(resources)
203 inventory['images'] = self.serialize_images(images)
204 inventory['host_types'] = self.serialize_host_profiles(profiles)
207 def get_host(self, hostname):
208 resource = ResourceQuery.filter(labid=hostname, lab=self.lab)
209 if len(resource) != 1:
210 return HttpResponseNotFound("Could not find single host with id " + str(hostname))
211 resource = resource[0]
213 "booked": resource.booked,
214 "working": resource.working,
215 "type": resource.profile.name
218 def update_host(self, hostname, data):
219 resource = ResourceQuery.filter(labid=hostname, lab=self.lab)
220 if len(resource) != 1:
221 return HttpResponseNotFound("Could not find single host with id " + str(hostname))
222 resource = resource[0]
223 if "working" in data:
224 working = data['working'] == "true"
225 resource.working = working
227 return self.get_host(hostname)
229 def get_status(self):
230 return {"status": self.lab.status}
232 def set_status(self, payload):
235 def get_current_jobs(self):
236 jobs = Job.objects.filter(booking__lab=self.lab)
238 return self.serialize_jobs(jobs, status=JobStatus.CURRENT)
240 def get_new_jobs(self):
241 jobs = Job.objects.filter(booking__lab=self.lab)
243 return self.serialize_jobs(jobs, status=JobStatus.NEW)
245 def get_done_jobs(self):
246 jobs = Job.objects.filter(booking__lab=self.lab)
248 return self.serialize_jobs(jobs, status=JobStatus.DONE)
250 def get_job(self, jobid):
251 return Job.objects.get(pk=jobid).to_dict()
253 def update_job(self, jobid, data):
256 def serialize_jobs(self, jobs, status=JobStatus.NEW):
259 jsonized_job = job.get_delta(status)
260 if len(jsonized_job['payload']) < 1:
262 job_ser.append(jsonized_job)
266 def serialize_resources(self, resources):
267 # TODO: rewrite for Resource model
269 for res in resources:
272 'hostname': res.name,
273 'host_type': res.profile.name
275 for iface in res.get_interfaces():
276 r['interfaces'].append({
277 'mac': iface.mac_address,
278 'busaddr': iface.bus_address,
280 'switchport': {"switch_name": iface.switch_name, "port_name": iface.port_name}
284 def serialize_images(self, images):
290 "lab_id": image.lab_id,
291 "dashboard_id": image.id
296 def serialize_resource_profiles(self, profiles):
298 for profile in profiles:
301 "cores": profile.cpuprofile.first().cores,
302 "arch": profile.cpuprofile.first().architecture,
303 "cpus": profile.cpuprofile.first().cpus,
306 for disk in profile.storageprofile.all():
309 "type": disk.media_type,
313 p['description'] = profile.description
315 for iface in profile.interfaceprofile.all():
316 p['interfaces'].append(
318 "speed": iface.speed,
323 p['ram'] = {"amount": profile.ramprofile.first().amount}
324 p['name'] = profile.name
325 profile_ser.append(p)
329 class Job(models.Model):
331 A Job to be performed by the Lab.
333 The API uses Jobs and Tasks to communicate actions that need to be taken to the Lab
334 that is hosting a booking. A booking from a user has an associated Job which tells
335 the lab how to configure the hardware, networking, etc to fulfill the booking
337 This is the class that is serialized and put into the api
340 booking = models.OneToOneField(Booking, on_delete=models.CASCADE, null=True)
341 status = models.IntegerField(default=JobStatus.NEW)
342 complete = models.BooleanField(default=False)
346 for relation in self.get_tasklist():
347 if relation.job_key not in d:
348 d[relation.job_key] = {}
349 d[relation.job_key][relation.task_id] = relation.config.to_dict()
351 return {"id": self.id, "payload": d}
353 def get_tasklist(self, status="all"):
355 return JobTaskQuery.filter(job=self, status=status)
356 return JobTaskQuery.filter(job=self)
358 def is_fulfilled(self):
360 If a job has been completed by the lab.
362 This method should return true if all of the job's tasks are done,
365 my_tasks = self.get_tasklist()
366 for task in my_tasks:
367 if task.status != JobStatus.DONE:
371 def get_delta(self, status):
373 for relation in self.get_tasklist(status=status):
374 if relation.job_key not in d:
375 d[relation.job_key] = {}
376 d[relation.job_key][relation.task_id] = relation.config.get_delta()
378 return {"id": self.id, "payload": d}
381 return json.dumps(self.to_dict())
384 class TaskConfig(models.Model):
385 state = models.IntegerField(default=ConfigState.CLEAN)
387 keys = set() # TODO: This needs to be an instance variable, not a class variable
388 delta_keys_list = models.CharField(max_length=200, default="[]")
391 def delta_keys(self):
392 return list(set(json.loads(self.delta_keys_list)))
395 def delta_keys(self, keylist):
396 self.delta_keys_list = json.dumps(keylist)
399 raise NotImplementedError
402 raise NotImplementedError
404 def format_delta(self, config, token):
405 delta = {k: config[k] for k in self.delta_keys}
406 delta['lab_token'] = token
410 return json.dumps(self.to_dict())
412 def clear_delta(self):
415 def set(self, *args):
416 dkeys = self.delta_keys
420 self.delta_keys = dkeys
423 class BridgeConfig(models.Model):
424 """Displays mapping between jumphost interfaces and bridges."""
426 interfaces = models.ManyToManyField(Interface)
427 opnfv_config = models.ForeignKey(OPNFVConfig, on_delete=models.CASCADE)
431 hid = ResourceQuery.get(interface__pk=self.interfaces.first().pk).labid
433 for interface in self.interfaces.all():
434 d[hid][interface.mac_address] = []
435 for vlan in interface.config.all():
436 network_role = self.opnfv_model.networks().filter(network=vlan.network)
437 bridge = IDFTemplater.bridge_names[network_role.name]
439 "vlan_id": vlan.vlan_id,
440 "tagged": vlan.tagged,
443 d[hid][interface.mac_address].append(br_config)
447 return json.dumps(self.to_dict())
450 class OpnfvApiConfig(models.Model):
452 installer = models.CharField(max_length=200)
453 scenario = models.CharField(max_length=300)
454 roles = models.ManyToManyField(ResourceOPNFVConfig)
455 # pdf and idf are url endpoints, not the actual file
456 pdf = models.CharField(max_length=100)
457 idf = models.CharField(max_length=100)
458 bridge_config = models.OneToOneField(BridgeConfig, on_delete=models.CASCADE, null=True)
459 delta = models.TextField()
460 opnfv_config = models.ForeignKey(OPNFVConfig, null=True, on_delete=models.SET_NULL)
464 if not self.opnfv_config:
467 d['installer'] = self.installer
469 d['scenario'] = self.scenario
474 if self.bridge_config:
475 d['bridged_interfaces'] = self.bridge_config.to_dict()
477 hosts = self.roles.all()
482 host.labid: self.opnfv_config.host_opnfv_config.get(
483 host_config__pk=host.config.pk
490 return json.dumps(self.to_dict())
492 def set_installer(self, installer):
493 self.installer = installer
494 d = json.loads(self.delta)
495 d['installer'] = installer
496 self.delta = json.dumps(d)
498 def set_scenario(self, scenario):
499 self.scenario = scenario
500 d = json.loads(self.delta)
501 d['scenario'] = scenario
502 self.delta = json.dumps(d)
504 def set_xdf(self, booking, update_delta=True):
505 kwargs = {'lab_name': booking.lab.name, 'booking_id': booking.id}
506 self.pdf = reverse('get-pdf', kwargs=kwargs)
507 self.idf = reverse('get-idf', kwargs=kwargs)
509 d = json.loads(self.delta)
512 self.delta = json.dumps(d)
514 def add_role(self, host):
516 d = json.loads(self.delta)
519 d['roles'].append({host.labid: host.config.opnfvRole.name})
520 self.delta = json.dumps(d)
522 def clear_delta(self):
527 self.delta = self.to_json()
529 return json.loads(self.delta)
532 class AccessConfig(TaskConfig):
533 access_type = models.CharField(max_length=50)
534 user = models.ForeignKey(User, on_delete=models.CASCADE)
535 revoke = models.BooleanField(default=False)
536 context = models.TextField(default="")
537 delta = models.TextField(default="{}")
541 d['access_type'] = self.access_type
542 d['user'] = self.user.id
543 d['revoke'] = self.revoke
545 d['context'] = json.loads(self.context)
552 self.delta = self.to_json()
554 d = json.loads(self.delta)
555 d["lab_token"] = self.accessrelation.lab_token
560 return json.dumps(self.to_dict())
562 def clear_delta(self):
564 d["lab_token"] = self.accessrelation.lab_token
565 self.delta = json.dumps(d)
567 def set_access_type(self, access_type):
568 self.access_type = access_type
569 d = json.loads(self.delta)
570 d['access_type'] = access_type
571 self.delta = json.dumps(d)
573 def set_user(self, user):
575 d = json.loads(self.delta)
576 d['user'] = self.user.id
577 self.delta = json.dumps(d)
579 def set_revoke(self, revoke):
581 d = json.loads(self.delta)
583 self.delta = json.dumps(d)
585 def set_context(self, context):
586 self.context = json.dumps(context)
587 d = json.loads(self.delta)
588 d['context'] = context
589 self.delta = json.dumps(d)
592 class SoftwareConfig(TaskConfig):
593 """Handles software installations, such as OPNFV or ONAP."""
595 opnfv = models.ForeignKey(OpnfvApiConfig, on_delete=models.CASCADE)
600 d['opnfv'] = self.opnfv.to_dict()
602 d["lab_token"] = self.softwarerelation.lab_token
603 self.delta = json.dumps(d)
609 d['opnfv'] = self.opnfv.get_delta()
610 d['lab_token'] = self.softwarerelation.lab_token
614 def clear_delta(self):
615 self.opnfv.clear_delta()
618 return json.dumps(self.to_dict())
621 class HardwareConfig(TaskConfig):
622 """Describes the desired configuration of the hardware."""
624 image = models.CharField(max_length=100, default="defimage")
625 power = models.CharField(max_length=100, default="off")
626 hostname = models.CharField(max_length=100, default="hostname")
627 ipmi_create = models.BooleanField(default=False)
628 delta = models.TextField()
630 keys = set(["id", "image", "power", "hostname", "ipmi_create"])
633 return self.format_delta(
634 self.hosthardwarerelation.get_resource().get_configuration(self.state),
635 self.hosthardwarerelation.lab_token)
638 class NetworkConfig(TaskConfig):
639 """Handles network configuration."""
641 interfaces = models.ManyToManyField(Interface)
642 delta = models.TextField()
646 hid = self.hostnetworkrelation.resource_id
648 for interface in self.interfaces.all():
649 d[hid][interface.mac_address] = []
650 for vlan in interface.config.all():
651 # TODO: should this come from the interface?
652 # e.g. will different interfaces for different resources need different configs?
653 d[hid][interface.mac_address].append({"vlan_id": vlan.vlan_id, "tagged": vlan.tagged})
658 return json.dumps(self.to_dict())
662 self.delta = self.to_json()
664 d = json.loads(self.delta)
665 d['lab_token'] = self.hostnetworkrelation.lab_token
668 def clear_delta(self):
669 self.delta = json.dumps(self.to_dict())
672 def add_interface(self, interface):
673 self.interfaces.add(interface)
674 d = json.loads(self.delta)
675 hid = self.hostnetworkrelation.resource_id
678 d[hid][interface.mac_address] = []
679 for vlan in interface.config.all():
680 d[hid][interface.mac_address].append({"vlan_id": vlan.vlan_id, "tagged": vlan.tagged})
681 self.delta = json.dumps(d)
684 class SnapshotConfig(TaskConfig):
686 resource_id = models.CharField(max_length=200, default="default_id")
687 image = models.IntegerField(null=True)
688 dashboard_id = models.IntegerField()
689 delta = models.TextField(default="{}")
694 d['host'] = self.host.labid
696 d['image'] = self.image
697 d['dashboard_id'] = self.dashboard_id
701 return json.dumps(self.to_dict())
705 self.delta = self.to_json()
708 d = json.loads(self.delta)
711 def clear_delta(self):
712 self.delta = json.dumps(self.to_dict())
715 def set_host(self, host):
717 d = json.loads(self.delta)
718 d['host'] = host.labid
719 self.delta = json.dumps(d)
721 def set_image(self, image):
723 d = json.loads(self.delta)
724 d['image'] = self.image
725 self.delta = json.dumps(d)
727 def clear_image(self):
729 d = json.loads(self.delta)
731 self.delta = json.dumps(d)
733 def set_dashboard_id(self, dash):
734 self.dashboard_id = dash
735 d = json.loads(self.delta)
736 d['dashboard_id'] = self.dashboard_id
737 self.delta = json.dumps(d)
739 def save(self, *args, **kwargs):
740 if len(ResourceQuery.filter(labid=self.resource_id)) != 1:
741 raise ValidationError("resource_id " + str(self.resource_id) + " does not refer to a single resource")
742 super().save(*args, **kwargs)
745 def get_task(task_id):
746 for taskclass in [AccessRelation, SoftwareRelation, HostHardwareRelation, HostNetworkRelation, SnapshotRelation]:
748 ret = taskclass.objects.get(task_id=task_id)
750 except taskclass.DoesNotExist:
752 from django.core.exceptions import ObjectDoesNotExist
753 raise ObjectDoesNotExist("Could not find matching TaskRelation instance")
757 return str(uuid.uuid4())
760 class TaskRelation(models.Model):
762 Relates a Job to a TaskConfig.
764 superclass that relates a Job to tasks anc maintains information
765 like status and messages from the lab
768 status = models.IntegerField(default=JobStatus.NEW)
769 job = models.ForeignKey(Job, on_delete=models.CASCADE)
770 config = models.OneToOneField(TaskConfig, on_delete=models.CASCADE)
771 task_id = models.CharField(default=get_task_uuid, max_length=37)
772 lab_token = models.CharField(default="null", max_length=50)
773 message = models.TextField(default="")
777 def delete(self, *args, **kwargs):
779 return super(self.__class__, self).delete(*args, **kwargs)
782 return "Generic Task"
788 class AccessRelation(TaskRelation):
789 config = models.OneToOneField(AccessConfig, on_delete=models.CASCADE)
795 def delete(self, *args, **kwargs):
797 return super(self.__class__, self).delete(*args, **kwargs)
800 class SoftwareRelation(TaskRelation):
801 config = models.OneToOneField(SoftwareConfig, on_delete=models.CASCADE)
805 return "Software Configuration Task"
807 def delete(self, *args, **kwargs):
809 return super(self.__class__, self).delete(*args, **kwargs)
812 class HostHardwareRelation(TaskRelation):
813 resource_id = models.CharField(max_length=200, default="default_id")
814 config = models.OneToOneField(HardwareConfig, on_delete=models.CASCADE)
818 return "Hardware Configuration Task"
821 return self.config.to_dict()
823 def delete(self, *args, **kwargs):
825 return super(self.__class__, self).delete(*args, **kwargs)
827 def save(self, *args, **kwargs):
828 if len(ResourceQuery.filter(labid=self.resource_id)) != 1:
829 raise ValidationError("resource_id " + str(self.resource_id) + " does not refer to a single resource")
830 super().save(*args, **kwargs)
832 def get_resource(self):
833 return ResourceQuery.get(labid=self.resource_id)
836 class HostNetworkRelation(TaskRelation):
837 resource_id = models.CharField(max_length=200, default="default_id")
838 config = models.OneToOneField(NetworkConfig, on_delete=models.CASCADE)
842 return "Network Configuration Task"
844 def delete(self, *args, **kwargs):
846 return super(self.__class__, self).delete(*args, **kwargs)
848 def save(self, *args, **kwargs):
849 if len(ResourceQuery.filter(labid=self.resource_id)) != 1:
850 raise ValidationError("resource_id " + str(self.resource_id) + " does not refer to a single resource")
851 super().save(*args, **kwargs)
853 def get_resource(self):
854 return ResourceQuery.get(labid=self.resource_id)
857 class SnapshotRelation(TaskRelation):
858 snapshot = models.ForeignKey(Image, on_delete=models.CASCADE)
859 config = models.OneToOneField(SnapshotConfig, on_delete=models.CASCADE)
863 return "Snapshot Task"
866 return self.config.to_dict()
868 def delete(self, *args, **kwargs):
870 return super(self.__class__, self).delete(*args, **kwargs)
873 class JobFactory(object):
874 """This class creates all the API models (jobs, tasks, etc) needed to fulfill a booking."""
877 def reimageHost(cls, new_image, booking, host):
878 """Modify an existing job to reimage the given host."""
879 job = Job.objects.get(booking=booking)
880 # make hardware task new
881 hardware_relation = HostHardwareRelation.objects.get(host=host, job=job)
882 hardware_relation.config.set_image(new_image.lab_id)
883 hardware_relation.config.save()
884 hardware_relation.status = JobStatus.NEW
886 # re-apply networking after host is reset
887 net_relation = HostNetworkRelation.objects.get(host=host, job=job)
888 net_relation.status = JobStatus.NEW
890 # re-apply ssh access after host is reset
891 for relation in AccessRelation.objects.filter(job=job, config__access_type="ssh"):
892 relation.status = JobStatus.NEW
895 hardware_relation.save()
899 def makeSnapshotTask(cls, image, booking, host):
900 relation = SnapshotRelation()
901 job = Job.objects.get(booking=booking)
902 config = SnapshotConfig.objects.create(dashboard_id=image.id)
905 relation.config = config
906 relation.config.save()
907 relation.config = relation.config
908 relation.snapshot = image
912 config.set_host(host)
916 def makeCompleteJob(cls, booking):
917 """Create everything that is needed to fulfill the given booking."""
918 resources = booking.resource.get_resources()
921 job = Job.objects.get(booking=booking)
923 job = Job.objects.create(status=JobStatus.NEW, booking=booking)
924 cls.makeHardwareConfigs(
928 cls.makeNetworkConfigs(
936 all_users = list(booking.collaborators.all())
937 all_users.append(booking.owner)
938 cls.makeAccessConfig(
944 for user in all_users:
946 cls.makeAccessConfig(
952 "key": user.userprofile.ssh_public_key.open().read().decode(encoding="UTF-8"),
953 "hosts": [r.labid for r in resources]
960 def makeHardwareConfigs(cls, resources=[], job=Job()):
962 Create and save HardwareConfig.
964 Helper function to create the tasks related to
965 configuring the hardware
967 for res in resources:
968 hardware_config = None
970 hardware_config = HardwareConfig.objects.get(relation__resource_id=res.labid)
972 hardware_config = HardwareConfig()
974 relation = HostHardwareRelation()
975 relation.resource_id = res.labid
977 relation.config = hardware_config
978 relation.config.save()
979 relation.config = relation.config
982 hardware_config.set("image", "hostname", "power", "ipmi_create")
983 hardware_config.save()
986 def makeAccessConfig(cls, users, access_type, revoke=False, job=Job(), context=False):
988 Create and save AccessConfig.
990 Helper function to create the tasks related to
991 configuring the VPN, SSH, etc access for users
994 relation = AccessRelation()
996 config = AccessConfig()
997 config.access_type = access_type
1000 relation.config = config
1002 config.clear_delta()
1004 config.set_context(context)
1005 config.set_access_type(access_type)
1006 config.set_revoke(revoke)
1007 config.set_user(user)
1011 def makeNetworkConfigs(cls, resources=[], job=Job()):
1013 Create and save NetworkConfig.
1015 Helper function to create the tasks related to
1016 configuring the networking
1018 for res in resources:
1019 network_config = None
1021 network_config = NetworkConfig.objects.get(relation__host=res)
1023 network_config = NetworkConfig.objects.create()
1025 relation = HostNetworkRelation()
1026 relation.resource_id = res.labid
1028 network_config.save()
1029 relation.config = network_config
1031 network_config.clear_delta()
1033 # TODO: use get_interfaces() on resource
1034 for interface in res.interfaces.all():
1035 network_config.add_interface(interface)
1036 network_config.save()
1039 def make_bridge_config(cls, booking):
1040 if len(booking.resource.get_resources()) < 2:
1043 jumphost_config = ResourceOPNFVConfig.objects.filter(
1044 role__name__iexact="jumphost"
1046 jumphost = ResourceQuery.filter(
1047 bundle=booking.resource,
1048 config=jumphost_config.resource_config
1052 br_config = BridgeConfig.objects.create(opnfv_config=booking.opnfv_config)
1053 for iface in jumphost.interfaces.all():
1054 br_config.interfaces.add(iface)
1058 def makeSoftware(cls, booking=None, job=Job()):
1060 Create and save SoftwareConfig.
1062 Helper function to create the tasks related to
1063 configuring the desired software, e.g. an OPNFV deployment
1065 if not booking.opnfv_config:
1068 opnfv_api_config = OpnfvApiConfig.objects.create(
1069 opnfv_config=booking.opnfv_config,
1070 installer=booking.opnfv_config.installer.name,
1071 scenario=booking.opnfv_config.scenario.name,
1072 bridge_config=cls.make_bridge_config(booking)
1075 opnfv_api_config.set_xdf(booking, False)
1076 opnfv_api_config.save()
1078 for host in booking.resource.get_resources():
1079 opnfv_api_config.roles.add(host)
1080 software_config = SoftwareConfig.objects.create(opnfv=opnfv_api_config)
1081 software_relation = SoftwareRelation.objects.create(job=job, config=software_config)
1082 return software_relation
1085 JOB_TASK_CLASSLIST = [
1086 HostHardwareRelation,
1088 HostNetworkRelation,
1094 class JobTaskQuery(AbstractModelQuery):
1095 model_list = JOB_TASK_CLASSLIST