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.contrib.postgres.fields import JSONField
16 from django.http import HttpResponseNotFound
17 from django.urls import reverse
18 from django.utils import timezone
23 from booking.models import Booking
24 from resource_inventory.models import (
35 from resource_inventory.idf_templater import IDFTemplater
36 from resource_inventory.pdf_templater import PDFTemplater
37 from account.models import Downtime, UserProfile
38 from dashboard.utils import AbstractModelQuery
43 A poor man's enum for a job's status.
45 A job is NEW if it has not been started or recognized by the Lab
46 A job is CURRENT if it has been started by the lab but it is not yet completed
47 a job is DONE if all the tasks are complete and the booking is ready to use
56 class LabManagerTracker:
59 def get(cls, lab_name, token):
63 Takes in a lab name (from a url path)
64 returns a lab manager instance for that lab, if it exists
65 Also checks that the given API token is correct
68 lab = Lab.objects.get(name=lab_name)
70 raise PermissionDenied("Lab not found")
71 if lab.api_token == token:
72 return LabManager(lab)
73 raise PermissionDenied("Lab not authorized")
78 Handles all lab REST calls.
80 handles jobs, inventory, status, etc
81 may need to create helper classes
84 def __init__(self, lab):
87 def get_downtime(self):
88 return Downtime.objects.filter(start__lt=timezone.now(), end__gt=timezone.now(), lab=self.lab)
90 def get_downtime_json(self):
91 downtime = self.get_downtime().first() # should only be one item in queryset
95 "start": downtime.start,
97 "description": downtime.description
99 return {"is_down": False}
101 def create_downtime(self, form):
103 Create a downtime event.
105 Takes in a dictionary that describes the model.
107 "start": utc timestamp
109 "description": human text (optional)
111 For timestamp structure, https://docs.djangoproject.com/en/2.2/ref/forms/fields/#datetimefield
113 Downtime.objects.create(
114 start=form.cleaned_data['start'],
115 end=form.cleaned_data['end'],
116 description=form.cleaned_data['description'],
119 return self.get_downtime_json()
121 def update_host_remote_info(self, data, res_id):
122 resource = ResourceQuery.filter(labid=res_id, lab=self.lab)
123 if len(resource) != 1:
124 return HttpResponseNotFound("Could not find single host with id " + str(res_id))
125 resource = resource[0]
128 info['address'] = data['address']
129 info['mac_address'] = data['mac_address']
130 info['password'] = data['password']
131 info['user'] = data['user']
132 info['type'] = data['type']
133 info['versions'] = json.dumps(data['versions'])
134 except Exception as e:
135 return {"error": "invalid arguement: " + str(e)}
136 remote_info = resource.remote_management
137 if "default" in remote_info.mac_address:
138 remote_info = RemoteInfo()
139 remote_info.address = info['address']
140 remote_info.mac_address = info['mac_address']
141 remote_info.password = info['password']
142 remote_info.user = info['user']
143 remote_info.type = info['type']
144 remote_info.versions = info['versions']
146 resource.remote_management = remote_info
148 booking = Booking.objects.get(resource=resource.bundle)
149 self.update_xdf(booking)
150 return {"status": "success"}
152 def update_xdf(self, booking):
153 booking.pdf = PDFTemplater.makePDF(booking)
154 booking.idf = IDFTemplater().makeIDF(booking)
157 def get_pdf(self, booking_id):
158 booking = get_object_or_404(Booking, pk=booking_id, lab=self.lab)
161 def get_idf(self, booking_id):
162 booking = get_object_or_404(Booking, pk=booking_id, lab=self.lab)
165 def get_profile(self):
167 prof['name'] = self.lab.name
169 "phone": self.lab.contact_phone,
170 "email": self.lab.contact_email
172 prof['host_count'] = [{
173 "type": profile.name,
174 "count": len(profile.get_resources(lab=self.lab))}
175 for profile in ResourceProfile.objects.filter(labs=self.lab)]
178 def format_user(self, userprofile):
180 "id": userprofile.user.id,
181 "username": userprofile.user.username,
182 "email": userprofile.email_addr,
183 "first_name": userprofile.user.first_name,
184 "last_name": userprofile.user.last_name,
185 "company": userprofile.company
189 userlist = [self.format_user(profile) for profile in UserProfile.objects.select_related("user").all()]
191 return json.dumps({"users": userlist})
193 def get_user(self, user_id):
194 user = User.objects.get(pk=user_id)
196 profile = get_object_or_404(UserProfile, user=user)
198 return json.dumps(self.format_user(profile))
200 def get_inventory(self):
202 resources = ResourceQuery.filter(lab=self.lab)
203 images = Image.objects.filter(from_lab=self.lab)
204 profiles = ResourceProfile.objects.filter(labs=self.lab)
205 inventory['resources'] = self.serialize_resources(resources)
206 inventory['images'] = self.serialize_images(images)
207 inventory['host_types'] = self.serialize_host_profiles(profiles)
210 def get_host(self, hostname):
211 resource = ResourceQuery.filter(labid=hostname, lab=self.lab)
212 if len(resource) != 1:
213 return HttpResponseNotFound("Could not find single host with id " + str(hostname))
214 resource = resource[0]
216 "booked": resource.booked,
217 "working": resource.working,
218 "type": resource.profile.name
221 def update_host(self, hostname, data):
222 resource = ResourceQuery.filter(labid=hostname, lab=self.lab)
223 if len(resource) != 1:
224 return HttpResponseNotFound("Could not find single host with id " + str(hostname))
225 resource = resource[0]
226 if "working" in data:
227 working = data['working'] == "true"
228 resource.working = working
230 return self.get_host(hostname)
232 def get_status(self):
233 return {"status": self.lab.status}
235 def set_status(self, payload):
238 def get_current_jobs(self):
239 jobs = Job.objects.filter(booking__lab=self.lab)
241 return self.serialize_jobs(jobs, status=JobStatus.CURRENT)
243 def get_new_jobs(self):
244 jobs = Job.objects.filter(booking__lab=self.lab)
246 return self.serialize_jobs(jobs, status=JobStatus.NEW)
248 def get_done_jobs(self):
249 jobs = Job.objects.filter(booking__lab=self.lab)
251 return self.serialize_jobs(jobs, status=JobStatus.DONE)
253 def get_analytics_job(self):
254 """ Get analytics job with status new """
255 jobs = Job.objects.filter(
256 booking__lab=self.lab,
260 return self.serialize_jobs(jobs, status=JobStatus.NEW)
262 def get_job(self, jobid):
263 return Job.objects.get(pk=jobid).to_dict()
265 def update_job(self, jobid, data):
268 def serialize_jobs(self, jobs, status=JobStatus.NEW):
271 jsonized_job = job.get_delta(status)
272 if len(jsonized_job['payload']) < 1:
274 job_ser.append(jsonized_job)
278 def serialize_resources(self, resources):
279 # TODO: rewrite for Resource model
281 for res in resources:
284 'hostname': res.name,
285 'host_type': res.profile.name
287 for iface in res.get_interfaces():
288 r['interfaces'].append({
289 'mac': iface.mac_address,
290 'busaddr': iface.bus_address,
292 'switchport': {"switch_name": iface.switch_name, "port_name": iface.port_name}
296 def serialize_images(self, images):
302 "lab_id": image.lab_id,
303 "dashboard_id": image.id
308 def serialize_resource_profiles(self, profiles):
310 for profile in profiles:
313 "cores": profile.cpuprofile.first().cores,
314 "arch": profile.cpuprofile.first().architecture,
315 "cpus": profile.cpuprofile.first().cpus,
318 for disk in profile.storageprofile.all():
321 "type": disk.media_type,
325 p['description'] = profile.description
327 for iface in profile.interfaceprofile.all():
328 p['interfaces'].append(
330 "speed": iface.speed,
335 p['ram'] = {"amount": profile.ramprofile.first().amount}
336 p['name'] = profile.name
337 profile_ser.append(p)
341 class APILog(models.Model):
342 user = models.ForeignKey(User, on_delete=models.PROTECT)
343 call_time = models.DateTimeField(auto_now=True)
344 method = models.CharField(null=True, max_length=6)
345 endpoint = models.CharField(null=True, max_length=300)
346 ip_addr = models.GenericIPAddressField(protocol="both", null=True, unpack_ipv4=False)
347 body = JSONField(null=True)
350 return "Call to {} at {} by {}".format(
357 class AutomationAPIManager:
359 def serialize_booking(booking):
361 sbook['id'] = booking.pk
362 sbook['owner'] = booking.owner.username
363 sbook['collaborators'] = [user.username for user in booking.collaborators.all()]
364 sbook['start'] = booking.start
365 sbook['end'] = booking.end
366 sbook['lab'] = AutomationAPIManager.serialize_lab(booking.lab)
367 sbook['purpose'] = booking.purpose
368 sbook['resourceBundle'] = AutomationAPIManager.serialize_bundle(booking.resource)
372 def serialize_lab(lab):
375 slab['name'] = lab.name
379 def serialize_bundle(bundle):
381 sbundle['id'] = bundle.pk
382 sbundle['resources'] = [
383 AutomationAPIManager.serialize_server(server)
384 for server in bundle.get_resources()]
388 def serialize_server(server):
390 sserver['id'] = server.pk
391 sserver['name'] = server.name
395 def serialize_resource_profile(profile):
397 sprofile['id'] = profile.pk
398 sprofile['name'] = profile.name
402 def serialize_template(rec_temp_and_count):
403 template = rec_temp_and_count[0]
404 count = rec_temp_and_count[1]
407 stemplate['id'] = template.pk
408 stemplate['name'] = template.name
409 stemplate['count_available'] = count
410 stemplate['resourceProfiles'] = [
411 AutomationAPIManager.serialize_resource_profile(config.profile)
412 for config in template.getConfigs()
417 def serialize_image(image):
419 simage['id'] = image.pk
420 simage['name'] = image.name
424 def serialize_userprofile(up):
427 sup['username'] = up.user.username
431 class Job(models.Model):
433 A Job to be performed by the Lab.
435 The API uses Jobs and Tasks to communicate actions that need to be taken to the Lab
436 that is hosting a booking. A booking from a user has an associated Job which tells
437 the lab how to configure the hardware, networking, etc to fulfill the booking
439 This is the class that is serialized and put into the api
444 ('DATA', 'Analytics')
447 booking = models.OneToOneField(Booking, on_delete=models.CASCADE, null=True)
448 status = models.IntegerField(default=JobStatus.NEW)
449 complete = models.BooleanField(default=False)
450 job_type = models.CharField(
458 for relation in self.get_tasklist():
459 if relation.job_key not in d:
460 d[relation.job_key] = {}
461 d[relation.job_key][relation.task_id] = relation.config.to_dict()
463 return {"id": self.id, "payload": d}
465 def get_tasklist(self, status="all"):
467 return JobTaskQuery.filter(job=self, status=status)
468 return JobTaskQuery.filter(job=self)
470 def is_fulfilled(self):
472 If a job has been completed by the lab.
474 This method should return true if all of the job's tasks are done,
477 my_tasks = self.get_tasklist()
478 for task in my_tasks:
479 if task.status != JobStatus.DONE:
483 def get_delta(self, status):
485 for relation in self.get_tasklist(status=status):
486 if relation.job_key not in d:
487 d[relation.job_key] = {}
488 d[relation.job_key][relation.task_id] = relation.config.get_delta()
490 return {"id": self.id, "payload": d}
493 return json.dumps(self.to_dict())
496 class TaskConfig(models.Model):
497 state = models.IntegerField(default=ConfigState.NEW)
499 keys = set() # TODO: This needs to be an instance variable, not a class variable
500 delta_keys_list = models.CharField(max_length=200, default="[]")
503 def delta_keys(self):
504 return list(set(json.loads(self.delta_keys_list)))
507 def delta_keys(self, keylist):
508 self.delta_keys_list = json.dumps(keylist)
511 raise NotImplementedError
514 raise NotImplementedError
516 def format_delta(self, config, token):
517 delta = {k: config[k] for k in self.delta_keys}
518 delta['lab_token'] = token
522 return json.dumps(self.to_dict())
524 def clear_delta(self):
527 def set(self, *args):
528 dkeys = self.delta_keys
532 self.delta_keys = dkeys
535 class BridgeConfig(models.Model):
536 """Displays mapping between jumphost interfaces and bridges."""
538 interfaces = models.ManyToManyField(Interface)
539 opnfv_config = models.ForeignKey(OPNFVConfig, on_delete=models.CASCADE)
543 hid = ResourceQuery.get(interface__pk=self.interfaces.first().pk).labid
545 for interface in self.interfaces.all():
546 d[hid][interface.mac_address] = []
547 for vlan in interface.config.all():
548 network_role = self.opnfv_model.networks().filter(network=vlan.network)
549 bridge = IDFTemplater.bridge_names[network_role.name]
551 "vlan_id": vlan.vlan_id,
552 "tagged": vlan.tagged,
555 d[hid][interface.mac_address].append(br_config)
559 return json.dumps(self.to_dict())
562 class ActiveUsersConfig(models.Model):
564 Task for getting active VPN users
566 StackStorm needs no information to run this job
567 so this task is very bare, but neccessary to fit
568 job creation convention.
571 def clear_delta(self):
575 return json.loads(self.to_json())
578 return json.dumps(self.to_dict())
584 class OpnfvApiConfig(models.Model):
586 installer = models.CharField(max_length=200)
587 scenario = models.CharField(max_length=300)
588 roles = models.ManyToManyField(ResourceOPNFVConfig)
589 # pdf and idf are url endpoints, not the actual file
590 pdf = models.CharField(max_length=100)
591 idf = models.CharField(max_length=100)
592 bridge_config = models.OneToOneField(BridgeConfig, on_delete=models.CASCADE, null=True)
593 delta = models.TextField()
594 opnfv_config = models.ForeignKey(OPNFVConfig, null=True, on_delete=models.SET_NULL)
598 if not self.opnfv_config:
601 d['installer'] = self.installer
603 d['scenario'] = self.scenario
608 if self.bridge_config:
609 d['bridged_interfaces'] = self.bridge_config.to_dict()
611 hosts = self.roles.all()
616 host.labid: self.opnfv_config.host_opnfv_config.get(
617 host_config__pk=host.config.pk
624 return json.dumps(self.to_dict())
626 def set_installer(self, installer):
627 self.installer = installer
628 d = json.loads(self.delta)
629 d['installer'] = installer
630 self.delta = json.dumps(d)
632 def set_scenario(self, scenario):
633 self.scenario = scenario
634 d = json.loads(self.delta)
635 d['scenario'] = scenario
636 self.delta = json.dumps(d)
638 def set_xdf(self, booking, update_delta=True):
639 kwargs = {'lab_name': booking.lab.name, 'booking_id': booking.id}
640 self.pdf = reverse('get-pdf', kwargs=kwargs)
641 self.idf = reverse('get-idf', kwargs=kwargs)
643 d = json.loads(self.delta)
646 self.delta = json.dumps(d)
648 def add_role(self, host):
650 d = json.loads(self.delta)
653 d['roles'].append({host.labid: host.config.opnfvRole.name})
654 self.delta = json.dumps(d)
656 def clear_delta(self):
660 return json.loads(self.to_json())
663 class AccessConfig(TaskConfig):
664 access_type = models.CharField(max_length=50)
665 user = models.ForeignKey(User, on_delete=models.CASCADE)
666 revoke = models.BooleanField(default=False)
667 context = models.TextField(default="")
668 delta = models.TextField(default="{}")
672 d['access_type'] = self.access_type
673 d['user'] = self.user.id
674 d['revoke'] = self.revoke
676 d['context'] = json.loads(self.context)
682 d = json.loads(self.to_json())
683 d["lab_token"] = self.accessrelation.lab_token
688 return json.dumps(self.to_dict())
690 def clear_delta(self):
692 d["lab_token"] = self.accessrelation.lab_token
693 self.delta = json.dumps(d)
695 def set_access_type(self, access_type):
696 self.access_type = access_type
697 d = json.loads(self.delta)
698 d['access_type'] = access_type
699 self.delta = json.dumps(d)
701 def set_user(self, user):
703 d = json.loads(self.delta)
704 d['user'] = self.user.id
705 self.delta = json.dumps(d)
707 def set_revoke(self, revoke):
709 d = json.loads(self.delta)
711 self.delta = json.dumps(d)
713 def set_context(self, context):
714 self.context = json.dumps(context)
715 d = json.loads(self.delta)
716 d['context'] = context
717 self.delta = json.dumps(d)
720 class SoftwareConfig(TaskConfig):
721 """Handles software installations, such as OPNFV or ONAP."""
723 opnfv = models.ForeignKey(OpnfvApiConfig, on_delete=models.CASCADE)
728 d['opnfv'] = self.opnfv.to_dict()
730 d["lab_token"] = self.softwarerelation.lab_token
731 self.delta = json.dumps(d)
737 d['opnfv'] = self.opnfv.get_delta()
738 d['lab_token'] = self.softwarerelation.lab_token
742 def clear_delta(self):
743 self.opnfv.clear_delta()
746 return json.dumps(self.to_dict())
749 class HardwareConfig(TaskConfig):
750 """Describes the desired configuration of the hardware."""
752 image = models.CharField(max_length=100, default="defimage")
753 power = models.CharField(max_length=100, default="off")
754 hostname = models.CharField(max_length=100, default="hostname")
755 ipmi_create = models.BooleanField(default=False)
756 delta = models.TextField()
758 keys = set(["id", "image", "power", "hostname", "ipmi_create"])
761 return self.get_delta()
764 return self.format_delta(
765 self.hosthardwarerelation.get_resource().get_configuration(self.state),
766 self.hosthardwarerelation.lab_token)
769 class NetworkConfig(TaskConfig):
770 """Handles network configuration."""
772 interfaces = models.ManyToManyField(Interface)
773 delta = models.TextField()
777 hid = self.hostnetworkrelation.resource_id
779 for interface in self.interfaces.all():
780 d[hid][interface.mac_address] = []
781 if self.state != ConfigState.CLEAN:
782 for vlan in interface.config.all():
783 # TODO: should this come from the interface?
784 # e.g. will different interfaces for different resources need different configs?
785 d[hid][interface.mac_address].append({"vlan_id": vlan.vlan_id, "tagged": vlan.tagged})
790 return json.dumps(self.to_dict())
793 d = json.loads(self.to_json())
794 d['lab_token'] = self.hostnetworkrelation.lab_token
797 def clear_delta(self):
798 self.delta = json.dumps(self.to_dict())
801 def add_interface(self, interface):
802 self.interfaces.add(interface)
803 d = json.loads(self.delta)
804 hid = self.hostnetworkrelation.resource_id
807 d[hid][interface.mac_address] = []
808 for vlan in interface.config.all():
809 d[hid][interface.mac_address].append({"vlan_id": vlan.vlan_id, "tagged": vlan.tagged})
810 self.delta = json.dumps(d)
813 class SnapshotConfig(TaskConfig):
815 resource_id = models.CharField(max_length=200, default="default_id")
816 image = models.IntegerField(null=True)
817 dashboard_id = models.IntegerField()
818 delta = models.TextField(default="{}")
823 d['host'] = self.host.labid
825 d['image'] = self.image
826 d['dashboard_id'] = self.dashboard_id
830 return json.dumps(self.to_dict())
833 d = json.loads(self.to_json())
836 def clear_delta(self):
837 self.delta = json.dumps(self.to_dict())
840 def set_host(self, host):
842 d = json.loads(self.delta)
843 d['host'] = host.labid
844 self.delta = json.dumps(d)
846 def set_image(self, image):
848 d = json.loads(self.delta)
849 d['image'] = self.image
850 self.delta = json.dumps(d)
852 def clear_image(self):
854 d = json.loads(self.delta)
856 self.delta = json.dumps(d)
858 def set_dashboard_id(self, dash):
859 self.dashboard_id = dash
860 d = json.loads(self.delta)
861 d['dashboard_id'] = self.dashboard_id
862 self.delta = json.dumps(d)
864 def save(self, *args, **kwargs):
865 if len(ResourceQuery.filter(labid=self.resource_id)) != 1:
866 raise ValidationError("resource_id " + str(self.resource_id) + " does not refer to a single resource")
867 super().save(*args, **kwargs)
870 def get_task(task_id):
871 for taskclass in [AccessRelation, SoftwareRelation, HostHardwareRelation, HostNetworkRelation, SnapshotRelation]:
873 ret = taskclass.objects.get(task_id=task_id)
875 except taskclass.DoesNotExist:
877 from django.core.exceptions import ObjectDoesNotExist
878 raise ObjectDoesNotExist("Could not find matching TaskRelation instance")
882 return str(uuid.uuid4())
885 class TaskRelation(models.Model):
887 Relates a Job to a TaskConfig.
889 superclass that relates a Job to tasks anc maintains information
890 like status and messages from the lab
893 status = models.IntegerField(default=JobStatus.NEW)
894 job = models.ForeignKey(Job, on_delete=models.CASCADE)
895 config = models.OneToOneField(TaskConfig, on_delete=models.CASCADE)
896 task_id = models.CharField(default=get_task_uuid, max_length=37)
897 lab_token = models.CharField(default="null", max_length=50)
898 message = models.TextField(default="")
902 def delete(self, *args, **kwargs):
904 return super(self.__class__, self).delete(*args, **kwargs)
907 return "Generic Task"
913 class AccessRelation(TaskRelation):
914 config = models.OneToOneField(AccessConfig, on_delete=models.CASCADE)
920 def delete(self, *args, **kwargs):
922 return super(self.__class__, self).delete(*args, **kwargs)
925 class SoftwareRelation(TaskRelation):
926 config = models.OneToOneField(SoftwareConfig, on_delete=models.CASCADE)
930 return "Software Configuration Task"
932 def delete(self, *args, **kwargs):
934 return super(self.__class__, self).delete(*args, **kwargs)
937 class HostHardwareRelation(TaskRelation):
938 resource_id = models.CharField(max_length=200, default="default_id")
939 config = models.OneToOneField(HardwareConfig, on_delete=models.CASCADE)
943 return "Hardware Configuration Task"
946 return self.config.to_dict()
948 def delete(self, *args, **kwargs):
950 return super(self.__class__, self).delete(*args, **kwargs)
952 def save(self, *args, **kwargs):
953 if len(ResourceQuery.filter(labid=self.resource_id)) != 1:
954 raise ValidationError("resource_id " + str(self.resource_id) + " does not refer to a single resource")
955 super().save(*args, **kwargs)
957 def get_resource(self):
958 return ResourceQuery.get(labid=self.resource_id)
961 class HostNetworkRelation(TaskRelation):
962 resource_id = models.CharField(max_length=200, default="default_id")
963 config = models.OneToOneField(NetworkConfig, on_delete=models.CASCADE)
967 return "Network Configuration Task"
969 def delete(self, *args, **kwargs):
971 return super(self.__class__, self).delete(*args, **kwargs)
973 def save(self, *args, **kwargs):
974 if len(ResourceQuery.filter(labid=self.resource_id)) != 1:
975 raise ValidationError("resource_id " + str(self.resource_id) + " does not refer to a single resource")
976 super().save(*args, **kwargs)
978 def get_resource(self):
979 return ResourceQuery.get(labid=self.resource_id)
982 class SnapshotRelation(TaskRelation):
983 snapshot = models.ForeignKey(Image, on_delete=models.CASCADE)
984 config = models.OneToOneField(SnapshotConfig, on_delete=models.CASCADE)
988 return "Snapshot Task"
991 return self.config.to_dict()
993 def delete(self, *args, **kwargs):
995 return super(self.__class__, self).delete(*args, **kwargs)
998 class ActiveUsersRelation(TaskRelation):
999 config = models.OneToOneField(ActiveUsersConfig, on_delete=models.CASCADE)
1000 job_key = "active users task"
1003 return "Active Users Task"
1006 class JobFactory(object):
1007 """This class creates all the API models (jobs, tasks, etc) needed to fulfill a booking."""
1010 def reimageHost(cls, new_image, booking, host):
1011 """Modify an existing job to reimage the given host."""
1012 job = Job.objects.get(booking=booking)
1013 # make hardware task new
1014 hardware_relation = HostHardwareRelation.objects.get(resource_id=host, job=job)
1015 hardware_relation.config.image = new_image.lab_id
1016 hardware_relation.config.save()
1017 hardware_relation.status = JobStatus.NEW
1019 # re-apply networking after host is reset
1020 net_relation = HostNetworkRelation.objects.get(resource_id=host, job=job)
1021 net_relation.status = JobStatus.NEW
1023 # re-apply ssh access after host is reset
1024 for relation in AccessRelation.objects.filter(job=job, config__access_type="ssh"):
1025 relation.status = JobStatus.NEW
1028 hardware_relation.save()
1032 def makeSnapshotTask(cls, image, booking, host):
1033 relation = SnapshotRelation()
1034 job = Job.objects.get(booking=booking)
1035 config = SnapshotConfig.objects.create(dashboard_id=image.id)
1038 relation.config = config
1039 relation.config.save()
1040 relation.config = relation.config
1041 relation.snapshot = image
1044 config.clear_delta()
1045 config.set_host(host)
1049 def makeActiveUsersTask(cls):
1050 """ Append active users task to analytics job """
1051 config = ActiveUsersConfig()
1052 relation = ActiveUsersRelation()
1053 job = Job.objects.get(job_type='DATA')
1055 job.status = JobStatus.NEW
1058 relation.config = config
1059 relation.config.save()
1060 relation.config = relation.config
1065 def makeAnalyticsJob(cls, booking):
1067 Create the analytics job
1069 This will only run once since there will only be one analytics job.
1070 All analytics tasks get appended to analytics job.
1073 if len(Job.objects.filter(job_type='DATA')) > 0:
1074 raise Exception("Cannot have more than one analytics job")
1076 if booking.resource:
1077 raise Exception("Booking is not marker for analytics job, has resoure")
1080 job.booking = booking
1081 job.job_type = 'DATA'
1084 cls.makeActiveUsersTask()
1087 def makeCompleteJob(cls, booking):
1088 """Create everything that is needed to fulfill the given booking."""
1089 resources = booking.resource.get_resources()
1092 job = Job.objects.get(booking=booking)
1094 job = Job.objects.create(status=JobStatus.NEW, booking=booking)
1095 cls.makeHardwareConfigs(
1096 resources=resources,
1099 cls.makeNetworkConfigs(
1100 resources=resources,
1107 all_users = list(booking.collaborators.all())
1108 all_users.append(booking.owner)
1109 cls.makeAccessConfig(
1115 for user in all_users:
1117 cls.makeAccessConfig(
1123 "key": user.userprofile.ssh_public_key.open().read().decode(encoding="UTF-8"),
1124 "hosts": [r.labid for r in resources]
1131 def makeHardwareConfigs(cls, resources=[], job=Job()):
1133 Create and save HardwareConfig.
1135 Helper function to create the tasks related to
1136 configuring the hardware
1138 for res in resources:
1139 hardware_config = None
1141 hardware_config = HardwareConfig.objects.get(relation__resource_id=res.labid)
1143 hardware_config = HardwareConfig()
1145 relation = HostHardwareRelation()
1146 relation.resource_id = res.labid
1148 relation.config = hardware_config
1149 relation.config.save()
1150 relation.config = relation.config
1153 hardware_config.set("id", "image", "hostname", "power", "ipmi_create")
1154 hardware_config.save()
1157 def makeAccessConfig(cls, users, access_type, revoke=False, job=Job(), context=False):
1159 Create and save AccessConfig.
1161 Helper function to create the tasks related to
1162 configuring the VPN, SSH, etc access for users
1165 relation = AccessRelation()
1167 config = AccessConfig()
1168 config.access_type = access_type
1171 relation.config = config
1173 config.clear_delta()
1175 config.set_context(context)
1176 config.set_access_type(access_type)
1177 config.set_revoke(revoke)
1178 config.set_user(user)
1182 def makeNetworkConfigs(cls, resources=[], job=Job()):
1184 Create and save NetworkConfig.
1186 Helper function to create the tasks related to
1187 configuring the networking
1189 for res in resources:
1190 network_config = None
1192 network_config = NetworkConfig.objects.get(relation__host=res)
1194 network_config = NetworkConfig.objects.create()
1196 relation = HostNetworkRelation()
1197 relation.resource_id = res.labid
1199 network_config.save()
1200 relation.config = network_config
1202 network_config.clear_delta()
1204 # TODO: use get_interfaces() on resource
1205 for interface in res.interfaces.all():
1206 network_config.add_interface(interface)
1207 network_config.save()
1210 def make_bridge_config(cls, booking):
1211 if len(booking.resource.get_resources()) < 2:
1214 jumphost_config = ResourceOPNFVConfig.objects.filter(
1215 role__name__iexact="jumphost"
1217 jumphost = ResourceQuery.filter(
1218 bundle=booking.resource,
1219 config=jumphost_config.resource_config
1223 br_config = BridgeConfig.objects.create(opnfv_config=booking.opnfv_config)
1224 for iface in jumphost.interfaces.all():
1225 br_config.interfaces.add(iface)
1229 def makeSoftware(cls, booking=None, job=Job()):
1231 Create and save SoftwareConfig.
1233 Helper function to create the tasks related to
1234 configuring the desired software, e.g. an OPNFV deployment
1236 if not booking.opnfv_config:
1239 opnfv_api_config = OpnfvApiConfig.objects.create(
1240 opnfv_config=booking.opnfv_config,
1241 installer=booking.opnfv_config.installer.name,
1242 scenario=booking.opnfv_config.scenario.name,
1243 bridge_config=cls.make_bridge_config(booking)
1246 opnfv_api_config.set_xdf(booking, False)
1247 opnfv_api_config.save()
1249 for host in booking.resource.get_resources():
1250 opnfv_api_config.roles.add(host)
1251 software_config = SoftwareConfig.objects.create(opnfv=opnfv_api_config)
1252 software_relation = SoftwareRelation.objects.create(job=job, config=software_config)
1253 return software_relation
1256 JOB_TASK_CLASSLIST = [
1257 HostHardwareRelation,
1259 HostNetworkRelation,
1266 class JobTaskQuery(AbstractModelQuery):
1267 model_list = JOB_TASK_CLASSLIST