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_analytics_job(self):
253 """ Get analytics job with status new """
254 jobs = Job.objects.filter(
255 booking__lab=self.lab,
259 return self.serialize_jobs(jobs, status=JobStatus.NEW)
261 def get_job(self, jobid):
262 return Job.objects.get(pk=jobid).to_dict()
264 def update_job(self, jobid, data):
267 def serialize_jobs(self, jobs, status=JobStatus.NEW):
270 jsonized_job = job.get_delta(status)
271 if len(jsonized_job['payload']) < 1:
273 job_ser.append(jsonized_job)
277 def serialize_resources(self, resources):
278 # TODO: rewrite for Resource model
280 for res in resources:
283 'hostname': res.name,
284 'host_type': res.profile.name
286 for iface in res.get_interfaces():
287 r['interfaces'].append({
288 'mac': iface.mac_address,
289 'busaddr': iface.bus_address,
291 'switchport': {"switch_name": iface.switch_name, "port_name": iface.port_name}
295 def serialize_images(self, images):
301 "lab_id": image.lab_id,
302 "dashboard_id": image.id
307 def serialize_resource_profiles(self, profiles):
309 for profile in profiles:
312 "cores": profile.cpuprofile.first().cores,
313 "arch": profile.cpuprofile.first().architecture,
314 "cpus": profile.cpuprofile.first().cpus,
317 for disk in profile.storageprofile.all():
320 "type": disk.media_type,
324 p['description'] = profile.description
326 for iface in profile.interfaceprofile.all():
327 p['interfaces'].append(
329 "speed": iface.speed,
334 p['ram'] = {"amount": profile.ramprofile.first().amount}
335 p['name'] = profile.name
336 profile_ser.append(p)
340 class Job(models.Model):
342 A Job to be performed by the Lab.
344 The API uses Jobs and Tasks to communicate actions that need to be taken to the Lab
345 that is hosting a booking. A booking from a user has an associated Job which tells
346 the lab how to configure the hardware, networking, etc to fulfill the booking
348 This is the class that is serialized and put into the api
353 ('DATA', 'Analytics')
356 booking = models.OneToOneField(Booking, on_delete=models.CASCADE, null=True)
357 status = models.IntegerField(default=JobStatus.NEW)
358 complete = models.BooleanField(default=False)
359 job_type = models.CharField(
367 for relation in self.get_tasklist():
368 if relation.job_key not in d:
369 d[relation.job_key] = {}
370 d[relation.job_key][relation.task_id] = relation.config.to_dict()
372 return {"id": self.id, "payload": d}
374 def get_tasklist(self, status="all"):
376 return JobTaskQuery.filter(job=self, status=status)
377 return JobTaskQuery.filter(job=self)
379 def is_fulfilled(self):
381 If a job has been completed by the lab.
383 This method should return true if all of the job's tasks are done,
386 my_tasks = self.get_tasklist()
387 for task in my_tasks:
388 if task.status != JobStatus.DONE:
392 def get_delta(self, status):
394 for relation in self.get_tasklist(status=status):
395 if relation.job_key not in d:
396 d[relation.job_key] = {}
397 d[relation.job_key][relation.task_id] = relation.config.get_delta()
399 return {"id": self.id, "payload": d}
402 return json.dumps(self.to_dict())
405 class TaskConfig(models.Model):
406 state = models.IntegerField(default=ConfigState.NEW)
408 keys = set() # TODO: This needs to be an instance variable, not a class variable
409 delta_keys_list = models.CharField(max_length=200, default="[]")
412 def delta_keys(self):
413 return list(set(json.loads(self.delta_keys_list)))
416 def delta_keys(self, keylist):
417 self.delta_keys_list = json.dumps(keylist)
420 raise NotImplementedError
423 raise NotImplementedError
425 def format_delta(self, config, token):
426 delta = {k: config[k] for k in self.delta_keys}
427 delta['lab_token'] = token
431 return json.dumps(self.to_dict())
433 def clear_delta(self):
436 def set(self, *args):
437 dkeys = self.delta_keys
441 self.delta_keys = dkeys
444 class BridgeConfig(models.Model):
445 """Displays mapping between jumphost interfaces and bridges."""
447 interfaces = models.ManyToManyField(Interface)
448 opnfv_config = models.ForeignKey(OPNFVConfig, on_delete=models.CASCADE)
452 hid = ResourceQuery.get(interface__pk=self.interfaces.first().pk).labid
454 for interface in self.interfaces.all():
455 d[hid][interface.mac_address] = []
456 for vlan in interface.config.all():
457 network_role = self.opnfv_model.networks().filter(network=vlan.network)
458 bridge = IDFTemplater.bridge_names[network_role.name]
460 "vlan_id": vlan.vlan_id,
461 "tagged": vlan.tagged,
464 d[hid][interface.mac_address].append(br_config)
468 return json.dumps(self.to_dict())
471 class ActiveUsersConfig(models.Model):
473 Task for getting active VPN users
475 StackStorm needs no information to run this job
476 so this task is very bare, but neccessary to fit
477 job creation convention.
480 def clear_delta(self):
484 return json.loads(self.to_json())
487 return json.dumps(self.to_dict())
493 class OpnfvApiConfig(models.Model):
495 installer = models.CharField(max_length=200)
496 scenario = models.CharField(max_length=300)
497 roles = models.ManyToManyField(ResourceOPNFVConfig)
498 # pdf and idf are url endpoints, not the actual file
499 pdf = models.CharField(max_length=100)
500 idf = models.CharField(max_length=100)
501 bridge_config = models.OneToOneField(BridgeConfig, on_delete=models.CASCADE, null=True)
502 delta = models.TextField()
503 opnfv_config = models.ForeignKey(OPNFVConfig, null=True, on_delete=models.SET_NULL)
507 if not self.opnfv_config:
510 d['installer'] = self.installer
512 d['scenario'] = self.scenario
517 if self.bridge_config:
518 d['bridged_interfaces'] = self.bridge_config.to_dict()
520 hosts = self.roles.all()
525 host.labid: self.opnfv_config.host_opnfv_config.get(
526 host_config__pk=host.config.pk
533 return json.dumps(self.to_dict())
535 def set_installer(self, installer):
536 self.installer = installer
537 d = json.loads(self.delta)
538 d['installer'] = installer
539 self.delta = json.dumps(d)
541 def set_scenario(self, scenario):
542 self.scenario = scenario
543 d = json.loads(self.delta)
544 d['scenario'] = scenario
545 self.delta = json.dumps(d)
547 def set_xdf(self, booking, update_delta=True):
548 kwargs = {'lab_name': booking.lab.name, 'booking_id': booking.id}
549 self.pdf = reverse('get-pdf', kwargs=kwargs)
550 self.idf = reverse('get-idf', kwargs=kwargs)
552 d = json.loads(self.delta)
555 self.delta = json.dumps(d)
557 def add_role(self, host):
559 d = json.loads(self.delta)
562 d['roles'].append({host.labid: host.config.opnfvRole.name})
563 self.delta = json.dumps(d)
565 def clear_delta(self):
569 return json.loads(self.to_json())
572 class AccessConfig(TaskConfig):
573 access_type = models.CharField(max_length=50)
574 user = models.ForeignKey(User, on_delete=models.CASCADE)
575 revoke = models.BooleanField(default=False)
576 context = models.TextField(default="")
577 delta = models.TextField(default="{}")
581 d['access_type'] = self.access_type
582 d['user'] = self.user.id
583 d['revoke'] = self.revoke
585 d['context'] = json.loads(self.context)
591 d = json.loads(self.to_json())
592 d["lab_token"] = self.accessrelation.lab_token
597 return json.dumps(self.to_dict())
599 def clear_delta(self):
601 d["lab_token"] = self.accessrelation.lab_token
602 self.delta = json.dumps(d)
604 def set_access_type(self, access_type):
605 self.access_type = access_type
606 d = json.loads(self.delta)
607 d['access_type'] = access_type
608 self.delta = json.dumps(d)
610 def set_user(self, user):
612 d = json.loads(self.delta)
613 d['user'] = self.user.id
614 self.delta = json.dumps(d)
616 def set_revoke(self, revoke):
618 d = json.loads(self.delta)
620 self.delta = json.dumps(d)
622 def set_context(self, context):
623 self.context = json.dumps(context)
624 d = json.loads(self.delta)
625 d['context'] = context
626 self.delta = json.dumps(d)
629 class SoftwareConfig(TaskConfig):
630 """Handles software installations, such as OPNFV or ONAP."""
632 opnfv = models.ForeignKey(OpnfvApiConfig, on_delete=models.CASCADE)
637 d['opnfv'] = self.opnfv.to_dict()
639 d["lab_token"] = self.softwarerelation.lab_token
640 self.delta = json.dumps(d)
646 d['opnfv'] = self.opnfv.get_delta()
647 d['lab_token'] = self.softwarerelation.lab_token
651 def clear_delta(self):
652 self.opnfv.clear_delta()
655 return json.dumps(self.to_dict())
658 class HardwareConfig(TaskConfig):
659 """Describes the desired configuration of the hardware."""
661 image = models.CharField(max_length=100, default="defimage")
662 power = models.CharField(max_length=100, default="off")
663 hostname = models.CharField(max_length=100, default="hostname")
664 ipmi_create = models.BooleanField(default=False)
665 delta = models.TextField()
667 keys = set(["id", "image", "power", "hostname", "ipmi_create"])
670 return self.format_delta(
671 self.hosthardwarerelation.get_resource().get_configuration(self.state),
672 self.hosthardwarerelation.lab_token)
675 class NetworkConfig(TaskConfig):
676 """Handles network configuration."""
678 interfaces = models.ManyToManyField(Interface)
679 delta = models.TextField()
683 hid = self.hostnetworkrelation.resource_id
685 for interface in self.interfaces.all():
686 d[hid][interface.mac_address] = []
687 if self.state != ConfigState.CLEAN:
688 for vlan in interface.config.all():
689 # TODO: should this come from the interface?
690 # e.g. will different interfaces for different resources need different configs?
691 d[hid][interface.mac_address].append({"vlan_id": vlan.vlan_id, "tagged": vlan.tagged})
696 return json.dumps(self.to_dict())
699 d = json.loads(self.to_json())
700 d['lab_token'] = self.hostnetworkrelation.lab_token
703 def clear_delta(self):
704 self.delta = json.dumps(self.to_dict())
707 def add_interface(self, interface):
708 self.interfaces.add(interface)
709 d = json.loads(self.delta)
710 hid = self.hostnetworkrelation.resource_id
713 d[hid][interface.mac_address] = []
714 for vlan in interface.config.all():
715 d[hid][interface.mac_address].append({"vlan_id": vlan.vlan_id, "tagged": vlan.tagged})
716 self.delta = json.dumps(d)
719 class SnapshotConfig(TaskConfig):
721 resource_id = models.CharField(max_length=200, default="default_id")
722 image = models.IntegerField(null=True)
723 dashboard_id = models.IntegerField()
724 delta = models.TextField(default="{}")
729 d['host'] = self.host.labid
731 d['image'] = self.image
732 d['dashboard_id'] = self.dashboard_id
736 return json.dumps(self.to_dict())
739 d = json.loads(self.to_json())
742 def clear_delta(self):
743 self.delta = json.dumps(self.to_dict())
746 def set_host(self, host):
748 d = json.loads(self.delta)
749 d['host'] = host.labid
750 self.delta = json.dumps(d)
752 def set_image(self, image):
754 d = json.loads(self.delta)
755 d['image'] = self.image
756 self.delta = json.dumps(d)
758 def clear_image(self):
760 d = json.loads(self.delta)
762 self.delta = json.dumps(d)
764 def set_dashboard_id(self, dash):
765 self.dashboard_id = dash
766 d = json.loads(self.delta)
767 d['dashboard_id'] = self.dashboard_id
768 self.delta = json.dumps(d)
770 def save(self, *args, **kwargs):
771 if len(ResourceQuery.filter(labid=self.resource_id)) != 1:
772 raise ValidationError("resource_id " + str(self.resource_id) + " does not refer to a single resource")
773 super().save(*args, **kwargs)
776 def get_task(task_id):
777 for taskclass in [AccessRelation, SoftwareRelation, HostHardwareRelation, HostNetworkRelation, SnapshotRelation]:
779 ret = taskclass.objects.get(task_id=task_id)
781 except taskclass.DoesNotExist:
783 from django.core.exceptions import ObjectDoesNotExist
784 raise ObjectDoesNotExist("Could not find matching TaskRelation instance")
788 return str(uuid.uuid4())
791 class TaskRelation(models.Model):
793 Relates a Job to a TaskConfig.
795 superclass that relates a Job to tasks anc maintains information
796 like status and messages from the lab
799 status = models.IntegerField(default=JobStatus.NEW)
800 job = models.ForeignKey(Job, on_delete=models.CASCADE)
801 config = models.OneToOneField(TaskConfig, on_delete=models.CASCADE)
802 task_id = models.CharField(default=get_task_uuid, max_length=37)
803 lab_token = models.CharField(default="null", max_length=50)
804 message = models.TextField(default="")
808 def delete(self, *args, **kwargs):
810 return super(self.__class__, self).delete(*args, **kwargs)
813 return "Generic Task"
819 class AccessRelation(TaskRelation):
820 config = models.OneToOneField(AccessConfig, on_delete=models.CASCADE)
826 def delete(self, *args, **kwargs):
828 return super(self.__class__, self).delete(*args, **kwargs)
831 class SoftwareRelation(TaskRelation):
832 config = models.OneToOneField(SoftwareConfig, on_delete=models.CASCADE)
836 return "Software Configuration Task"
838 def delete(self, *args, **kwargs):
840 return super(self.__class__, self).delete(*args, **kwargs)
843 class HostHardwareRelation(TaskRelation):
844 resource_id = models.CharField(max_length=200, default="default_id")
845 config = models.OneToOneField(HardwareConfig, on_delete=models.CASCADE)
849 return "Hardware Configuration Task"
852 return self.config.to_dict()
854 def delete(self, *args, **kwargs):
856 return super(self.__class__, self).delete(*args, **kwargs)
858 def save(self, *args, **kwargs):
859 if len(ResourceQuery.filter(labid=self.resource_id)) != 1:
860 raise ValidationError("resource_id " + str(self.resource_id) + " does not refer to a single resource")
861 super().save(*args, **kwargs)
863 def get_resource(self):
864 return ResourceQuery.get(labid=self.resource_id)
867 class HostNetworkRelation(TaskRelation):
868 resource_id = models.CharField(max_length=200, default="default_id")
869 config = models.OneToOneField(NetworkConfig, on_delete=models.CASCADE)
873 return "Network Configuration Task"
875 def delete(self, *args, **kwargs):
877 return super(self.__class__, self).delete(*args, **kwargs)
879 def save(self, *args, **kwargs):
880 if len(ResourceQuery.filter(labid=self.resource_id)) != 1:
881 raise ValidationError("resource_id " + str(self.resource_id) + " does not refer to a single resource")
882 super().save(*args, **kwargs)
884 def get_resource(self):
885 return ResourceQuery.get(labid=self.resource_id)
888 class SnapshotRelation(TaskRelation):
889 snapshot = models.ForeignKey(Image, on_delete=models.CASCADE)
890 config = models.OneToOneField(SnapshotConfig, on_delete=models.CASCADE)
894 return "Snapshot Task"
897 return self.config.to_dict()
899 def delete(self, *args, **kwargs):
901 return super(self.__class__, self).delete(*args, **kwargs)
904 class ActiveUsersRelation(TaskRelation):
905 config = models.OneToOneField(ActiveUsersConfig, on_delete=models.CASCADE)
906 job_key = "active users task"
909 return "Active Users Task"
912 class JobFactory(object):
913 """This class creates all the API models (jobs, tasks, etc) needed to fulfill a booking."""
916 def reimageHost(cls, new_image, booking, host):
917 """Modify an existing job to reimage the given host."""
918 job = Job.objects.get(booking=booking)
919 # make hardware task new
920 hardware_relation = HostHardwareRelation.objects.get(resource_id=host, job=job)
921 hardware_relation.config.image = new_image.lab_id
922 hardware_relation.config.save()
923 hardware_relation.status = JobStatus.NEW
925 # re-apply networking after host is reset
926 net_relation = HostNetworkRelation.objects.get(resource_id=host, job=job)
927 net_relation.status = JobStatus.NEW
929 # re-apply ssh access after host is reset
930 for relation in AccessRelation.objects.filter(job=job, config__access_type="ssh"):
931 relation.status = JobStatus.NEW
934 hardware_relation.save()
938 def makeSnapshotTask(cls, image, booking, host):
939 relation = SnapshotRelation()
940 job = Job.objects.get(booking=booking)
941 config = SnapshotConfig.objects.create(dashboard_id=image.id)
944 relation.config = config
945 relation.config.save()
946 relation.config = relation.config
947 relation.snapshot = image
951 config.set_host(host)
955 def makeActiveUsersTask(cls):
956 """ Append active users task to analytics job """
957 config = ActiveUsersConfig()
958 relation = ActiveUsersRelation()
959 job = Job.objects.get(job_type='DATA')
961 job.status = JobStatus.NEW
964 relation.config = config
965 relation.config.save()
966 relation.config = relation.config
971 def makeAnalyticsJob(cls, booking):
973 Create the analytics job
975 This will only run once since there will only be one analytics job.
976 All analytics tasks get appended to analytics job.
979 if len(Job.objects.filter(job_type='DATA')) > 0:
980 raise Exception("Cannot have more than one analytics job")
983 raise Exception("Booking is not marker for analytics job, has resoure")
986 job.booking = booking
987 job.job_type = 'DATA'
990 cls.makeActiveUsersTask()
993 def makeCompleteJob(cls, booking):
994 """Create everything that is needed to fulfill the given booking."""
995 resources = booking.resource.get_resources()
998 job = Job.objects.get(booking=booking)
1000 job = Job.objects.create(status=JobStatus.NEW, booking=booking)
1001 cls.makeHardwareConfigs(
1002 resources=resources,
1005 cls.makeNetworkConfigs(
1006 resources=resources,
1013 all_users = list(booking.collaborators.all())
1014 all_users.append(booking.owner)
1015 cls.makeAccessConfig(
1021 for user in all_users:
1023 cls.makeAccessConfig(
1029 "key": user.userprofile.ssh_public_key.open().read().decode(encoding="UTF-8"),
1030 "hosts": [r.labid for r in resources]
1037 def makeHardwareConfigs(cls, resources=[], job=Job()):
1039 Create and save HardwareConfig.
1041 Helper function to create the tasks related to
1042 configuring the hardware
1044 for res in resources:
1045 hardware_config = None
1047 hardware_config = HardwareConfig.objects.get(relation__resource_id=res.labid)
1049 hardware_config = HardwareConfig()
1051 relation = HostHardwareRelation()
1052 relation.resource_id = res.labid
1054 relation.config = hardware_config
1055 relation.config.save()
1056 relation.config = relation.config
1059 hardware_config.set("id", "image", "hostname", "power", "ipmi_create")
1060 hardware_config.save()
1063 def makeAccessConfig(cls, users, access_type, revoke=False, job=Job(), context=False):
1065 Create and save AccessConfig.
1067 Helper function to create the tasks related to
1068 configuring the VPN, SSH, etc access for users
1071 relation = AccessRelation()
1073 config = AccessConfig()
1074 config.access_type = access_type
1077 relation.config = config
1079 config.clear_delta()
1081 config.set_context(context)
1082 config.set_access_type(access_type)
1083 config.set_revoke(revoke)
1084 config.set_user(user)
1088 def makeNetworkConfigs(cls, resources=[], job=Job()):
1090 Create and save NetworkConfig.
1092 Helper function to create the tasks related to
1093 configuring the networking
1095 for res in resources:
1096 network_config = None
1098 network_config = NetworkConfig.objects.get(relation__host=res)
1100 network_config = NetworkConfig.objects.create()
1102 relation = HostNetworkRelation()
1103 relation.resource_id = res.labid
1105 network_config.save()
1106 relation.config = network_config
1108 network_config.clear_delta()
1110 # TODO: use get_interfaces() on resource
1111 for interface in res.interfaces.all():
1112 network_config.add_interface(interface)
1113 network_config.save()
1116 def make_bridge_config(cls, booking):
1117 if len(booking.resource.get_resources()) < 2:
1120 jumphost_config = ResourceOPNFVConfig.objects.filter(
1121 role__name__iexact="jumphost"
1123 jumphost = ResourceQuery.filter(
1124 bundle=booking.resource,
1125 config=jumphost_config.resource_config
1129 br_config = BridgeConfig.objects.create(opnfv_config=booking.opnfv_config)
1130 for iface in jumphost.interfaces.all():
1131 br_config.interfaces.add(iface)
1135 def makeSoftware(cls, booking=None, job=Job()):
1137 Create and save SoftwareConfig.
1139 Helper function to create the tasks related to
1140 configuring the desired software, e.g. an OPNFV deployment
1142 if not booking.opnfv_config:
1145 opnfv_api_config = OpnfvApiConfig.objects.create(
1146 opnfv_config=booking.opnfv_config,
1147 installer=booking.opnfv_config.installer.name,
1148 scenario=booking.opnfv_config.scenario.name,
1149 bridge_config=cls.make_bridge_config(booking)
1152 opnfv_api_config.set_xdf(booking, False)
1153 opnfv_api_config.save()
1155 for host in booking.resource.get_resources():
1156 opnfv_api_config.roles.add(host)
1157 software_config = SoftwareConfig.objects.create(opnfv=opnfv_api_config)
1158 software_relation = SoftwareRelation.objects.create(job=job, config=software_config)
1159 return software_relation
1162 JOB_TASK_CLASSLIST = [
1163 HostHardwareRelation,
1165 HostNetworkRelation,
1172 class JobTaskQuery(AbstractModelQuery):
1173 model_list = JOB_TASK_CLASSLIST