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.get_delta()
673 return self.format_delta(
674 self.hosthardwarerelation.get_resource().get_configuration(self.state),
675 self.hosthardwarerelation.lab_token)
678 class NetworkConfig(TaskConfig):
679 """Handles network configuration."""
681 interfaces = models.ManyToManyField(Interface)
682 delta = models.TextField()
686 hid = self.hostnetworkrelation.resource_id
688 for interface in self.interfaces.all():
689 d[hid][interface.mac_address] = []
690 if self.state != ConfigState.CLEAN:
691 for vlan in interface.config.all():
692 # TODO: should this come from the interface?
693 # e.g. will different interfaces for different resources need different configs?
694 d[hid][interface.mac_address].append({"vlan_id": vlan.vlan_id, "tagged": vlan.tagged})
699 return json.dumps(self.to_dict())
702 d = json.loads(self.to_json())
703 d['lab_token'] = self.hostnetworkrelation.lab_token
706 def clear_delta(self):
707 self.delta = json.dumps(self.to_dict())
710 def add_interface(self, interface):
711 self.interfaces.add(interface)
712 d = json.loads(self.delta)
713 hid = self.hostnetworkrelation.resource_id
716 d[hid][interface.mac_address] = []
717 for vlan in interface.config.all():
718 d[hid][interface.mac_address].append({"vlan_id": vlan.vlan_id, "tagged": vlan.tagged})
719 self.delta = json.dumps(d)
722 class SnapshotConfig(TaskConfig):
724 resource_id = models.CharField(max_length=200, default="default_id")
725 image = models.IntegerField(null=True)
726 dashboard_id = models.IntegerField()
727 delta = models.TextField(default="{}")
732 d['host'] = self.host.labid
734 d['image'] = self.image
735 d['dashboard_id'] = self.dashboard_id
739 return json.dumps(self.to_dict())
742 d = json.loads(self.to_json())
745 def clear_delta(self):
746 self.delta = json.dumps(self.to_dict())
749 def set_host(self, host):
751 d = json.loads(self.delta)
752 d['host'] = host.labid
753 self.delta = json.dumps(d)
755 def set_image(self, image):
757 d = json.loads(self.delta)
758 d['image'] = self.image
759 self.delta = json.dumps(d)
761 def clear_image(self):
763 d = json.loads(self.delta)
765 self.delta = json.dumps(d)
767 def set_dashboard_id(self, dash):
768 self.dashboard_id = dash
769 d = json.loads(self.delta)
770 d['dashboard_id'] = self.dashboard_id
771 self.delta = json.dumps(d)
773 def save(self, *args, **kwargs):
774 if len(ResourceQuery.filter(labid=self.resource_id)) != 1:
775 raise ValidationError("resource_id " + str(self.resource_id) + " does not refer to a single resource")
776 super().save(*args, **kwargs)
779 def get_task(task_id):
780 for taskclass in [AccessRelation, SoftwareRelation, HostHardwareRelation, HostNetworkRelation, SnapshotRelation]:
782 ret = taskclass.objects.get(task_id=task_id)
784 except taskclass.DoesNotExist:
786 from django.core.exceptions import ObjectDoesNotExist
787 raise ObjectDoesNotExist("Could not find matching TaskRelation instance")
791 return str(uuid.uuid4())
794 class TaskRelation(models.Model):
796 Relates a Job to a TaskConfig.
798 superclass that relates a Job to tasks anc maintains information
799 like status and messages from the lab
802 status = models.IntegerField(default=JobStatus.NEW)
803 job = models.ForeignKey(Job, on_delete=models.CASCADE)
804 config = models.OneToOneField(TaskConfig, on_delete=models.CASCADE)
805 task_id = models.CharField(default=get_task_uuid, max_length=37)
806 lab_token = models.CharField(default="null", max_length=50)
807 message = models.TextField(default="")
811 def delete(self, *args, **kwargs):
813 return super(self.__class__, self).delete(*args, **kwargs)
816 return "Generic Task"
822 class AccessRelation(TaskRelation):
823 config = models.OneToOneField(AccessConfig, on_delete=models.CASCADE)
829 def delete(self, *args, **kwargs):
831 return super(self.__class__, self).delete(*args, **kwargs)
834 class SoftwareRelation(TaskRelation):
835 config = models.OneToOneField(SoftwareConfig, on_delete=models.CASCADE)
839 return "Software Configuration Task"
841 def delete(self, *args, **kwargs):
843 return super(self.__class__, self).delete(*args, **kwargs)
846 class HostHardwareRelation(TaskRelation):
847 resource_id = models.CharField(max_length=200, default="default_id")
848 config = models.OneToOneField(HardwareConfig, on_delete=models.CASCADE)
852 return "Hardware Configuration Task"
855 return self.config.to_dict()
857 def delete(self, *args, **kwargs):
859 return super(self.__class__, self).delete(*args, **kwargs)
861 def save(self, *args, **kwargs):
862 if len(ResourceQuery.filter(labid=self.resource_id)) != 1:
863 raise ValidationError("resource_id " + str(self.resource_id) + " does not refer to a single resource")
864 super().save(*args, **kwargs)
866 def get_resource(self):
867 return ResourceQuery.get(labid=self.resource_id)
870 class HostNetworkRelation(TaskRelation):
871 resource_id = models.CharField(max_length=200, default="default_id")
872 config = models.OneToOneField(NetworkConfig, on_delete=models.CASCADE)
876 return "Network Configuration Task"
878 def delete(self, *args, **kwargs):
880 return super(self.__class__, self).delete(*args, **kwargs)
882 def save(self, *args, **kwargs):
883 if len(ResourceQuery.filter(labid=self.resource_id)) != 1:
884 raise ValidationError("resource_id " + str(self.resource_id) + " does not refer to a single resource")
885 super().save(*args, **kwargs)
887 def get_resource(self):
888 return ResourceQuery.get(labid=self.resource_id)
891 class SnapshotRelation(TaskRelation):
892 snapshot = models.ForeignKey(Image, on_delete=models.CASCADE)
893 config = models.OneToOneField(SnapshotConfig, on_delete=models.CASCADE)
897 return "Snapshot Task"
900 return self.config.to_dict()
902 def delete(self, *args, **kwargs):
904 return super(self.__class__, self).delete(*args, **kwargs)
907 class ActiveUsersRelation(TaskRelation):
908 config = models.OneToOneField(ActiveUsersConfig, on_delete=models.CASCADE)
909 job_key = "active users task"
912 return "Active Users Task"
915 class JobFactory(object):
916 """This class creates all the API models (jobs, tasks, etc) needed to fulfill a booking."""
919 def reimageHost(cls, new_image, booking, host):
920 """Modify an existing job to reimage the given host."""
921 job = Job.objects.get(booking=booking)
922 # make hardware task new
923 hardware_relation = HostHardwareRelation.objects.get(resource_id=host, job=job)
924 hardware_relation.config.image = new_image.lab_id
925 hardware_relation.config.save()
926 hardware_relation.status = JobStatus.NEW
928 # re-apply networking after host is reset
929 net_relation = HostNetworkRelation.objects.get(resource_id=host, job=job)
930 net_relation.status = JobStatus.NEW
932 # re-apply ssh access after host is reset
933 for relation in AccessRelation.objects.filter(job=job, config__access_type="ssh"):
934 relation.status = JobStatus.NEW
937 hardware_relation.save()
941 def makeSnapshotTask(cls, image, booking, host):
942 relation = SnapshotRelation()
943 job = Job.objects.get(booking=booking)
944 config = SnapshotConfig.objects.create(dashboard_id=image.id)
947 relation.config = config
948 relation.config.save()
949 relation.config = relation.config
950 relation.snapshot = image
954 config.set_host(host)
958 def makeActiveUsersTask(cls):
959 """ Append active users task to analytics job """
960 config = ActiveUsersConfig()
961 relation = ActiveUsersRelation()
962 job = Job.objects.get(job_type='DATA')
964 job.status = JobStatus.NEW
967 relation.config = config
968 relation.config.save()
969 relation.config = relation.config
974 def makeAnalyticsJob(cls, booking):
976 Create the analytics job
978 This will only run once since there will only be one analytics job.
979 All analytics tasks get appended to analytics job.
982 if len(Job.objects.filter(job_type='DATA')) > 0:
983 raise Exception("Cannot have more than one analytics job")
986 raise Exception("Booking is not marker for analytics job, has resoure")
989 job.booking = booking
990 job.job_type = 'DATA'
993 cls.makeActiveUsersTask()
996 def makeCompleteJob(cls, booking):
997 """Create everything that is needed to fulfill the given booking."""
998 resources = booking.resource.get_resources()
1001 job = Job.objects.get(booking=booking)
1003 job = Job.objects.create(status=JobStatus.NEW, booking=booking)
1004 cls.makeHardwareConfigs(
1005 resources=resources,
1008 cls.makeNetworkConfigs(
1009 resources=resources,
1016 all_users = list(booking.collaborators.all())
1017 all_users.append(booking.owner)
1018 cls.makeAccessConfig(
1024 for user in all_users:
1026 cls.makeAccessConfig(
1032 "key": user.userprofile.ssh_public_key.open().read().decode(encoding="UTF-8"),
1033 "hosts": [r.labid for r in resources]
1040 def makeHardwareConfigs(cls, resources=[], job=Job()):
1042 Create and save HardwareConfig.
1044 Helper function to create the tasks related to
1045 configuring the hardware
1047 for res in resources:
1048 hardware_config = None
1050 hardware_config = HardwareConfig.objects.get(relation__resource_id=res.labid)
1052 hardware_config = HardwareConfig()
1054 relation = HostHardwareRelation()
1055 relation.resource_id = res.labid
1057 relation.config = hardware_config
1058 relation.config.save()
1059 relation.config = relation.config
1062 hardware_config.set("id", "image", "hostname", "power", "ipmi_create")
1063 hardware_config.save()
1066 def makeAccessConfig(cls, users, access_type, revoke=False, job=Job(), context=False):
1068 Create and save AccessConfig.
1070 Helper function to create the tasks related to
1071 configuring the VPN, SSH, etc access for users
1074 relation = AccessRelation()
1076 config = AccessConfig()
1077 config.access_type = access_type
1080 relation.config = config
1082 config.clear_delta()
1084 config.set_context(context)
1085 config.set_access_type(access_type)
1086 config.set_revoke(revoke)
1087 config.set_user(user)
1091 def makeNetworkConfigs(cls, resources=[], job=Job()):
1093 Create and save NetworkConfig.
1095 Helper function to create the tasks related to
1096 configuring the networking
1098 for res in resources:
1099 network_config = None
1101 network_config = NetworkConfig.objects.get(relation__host=res)
1103 network_config = NetworkConfig.objects.create()
1105 relation = HostNetworkRelation()
1106 relation.resource_id = res.labid
1108 network_config.save()
1109 relation.config = network_config
1111 network_config.clear_delta()
1113 # TODO: use get_interfaces() on resource
1114 for interface in res.interfaces.all():
1115 network_config.add_interface(interface)
1116 network_config.save()
1119 def make_bridge_config(cls, booking):
1120 if len(booking.resource.get_resources()) < 2:
1123 jumphost_config = ResourceOPNFVConfig.objects.filter(
1124 role__name__iexact="jumphost"
1126 jumphost = ResourceQuery.filter(
1127 bundle=booking.resource,
1128 config=jumphost_config.resource_config
1132 br_config = BridgeConfig.objects.create(opnfv_config=booking.opnfv_config)
1133 for iface in jumphost.interfaces.all():
1134 br_config.interfaces.add(iface)
1138 def makeSoftware(cls, booking=None, job=Job()):
1140 Create and save SoftwareConfig.
1142 Helper function to create the tasks related to
1143 configuring the desired software, e.g. an OPNFV deployment
1145 if not booking.opnfv_config:
1148 opnfv_api_config = OpnfvApiConfig.objects.create(
1149 opnfv_config=booking.opnfv_config,
1150 installer=booking.opnfv_config.installer.name,
1151 scenario=booking.opnfv_config.scenario.name,
1152 bridge_config=cls.make_bridge_config(booking)
1155 opnfv_api_config.set_xdf(booking, False)
1156 opnfv_api_config.save()
1158 for host in booking.resource.get_resources():
1159 opnfv_api_config.roles.add(host)
1160 software_config = SoftwareConfig.objects.create(opnfv=opnfv_api_config)
1161 software_relation = SoftwareRelation.objects.create(job=job, config=software_config)
1162 return software_relation
1165 JOB_TASK_CLASSLIST = [
1166 HostHardwareRelation,
1168 HostNetworkRelation,
1175 class JobTaskQuery(AbstractModelQuery):
1176 model_list = JOB_TASK_CLASSLIST