4ce8c3e52cb165fc454ddc22551c5cf0fceacd93
[pharos-tools.git] / dashboard / src / api / models.py
1 ##############################################################################
2 # Copyright (c) 2018 Sawyer Bergeron, Parker Berberian, and others.
3 #
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 ##############################################################################
9
10
11 from django.contrib.auth.models import User
12 from django.db import models
13 from django.core.exceptions import PermissionDenied
14 from django.shortcuts import get_object_or_404
15
16 import json
17 import uuid
18
19 from booking.models import Booking
20 from resource_inventory.models import (
21     Lab,
22     HostProfile,
23     Host,
24     Image,
25     Interface,
26     RemoteInfo
27 )
28 from resource_inventory.idf_templater import IDFTemplater
29 from resource_inventory.pdf_templater import PDFTemplater
30
31
32 class JobStatus(object):
33     NEW = 0
34     CURRENT = 100
35     DONE = 200
36     ERROR = 300
37
38
39 class LabManagerTracker(object):
40
41     @classmethod
42     def get(cls, lab_name, token):
43         """
44         Takes in a lab name (from a url path)
45         returns a lab manager instance for that lab, if it exists
46         """
47         try:
48             lab = Lab.objects.get(name=lab_name)
49         except Exception:
50             raise PermissionDenied("Lab not found")
51         if lab.api_token == token:
52             return LabManager(lab)
53         raise PermissionDenied("Lab not authorized")
54
55
56 class LabManager(object):
57     """
58     This is the class that will ultimately handle all REST calls to
59     lab endpoints.
60     handles jobs, inventory, status, etc
61     may need to create helper classes
62     """
63
64     def __init__(self, lab):
65         self.lab = lab
66
67     def update_host_remote_info(self, data, host_id):
68         host = get_object_or_404(Host, labid=host_id, lab=self.lab)
69         info = {}
70         try:
71             info['address'] = data['address']
72             info['mac_address'] = data['mac_address']
73             info['password'] = data['password']
74             info['user'] = data['user']
75             info['type'] = data['type']
76             info['versions'] = json.dumps(data['versions'])
77         except Exception as e:
78             return {"error": "invalid arguement: " + str(e)}
79         remote_info = host.remote_management
80         if "default" in remote_info.mac_address:
81             remote_info = RemoteInfo()
82         remote_info.address = info['address']
83         remote_info.mac_address = info['mac_address']
84         remote_info.password = info['password']
85         remote_info.user = info['user']
86         remote_info.type = info['type']
87         remote_info.versions = info['versions']
88         remote_info.save()
89         host.remote_management = remote_info
90         host.save()
91         booking = Booking.objects.get(resource=host.bundle)
92         self.update_xdf(booking)
93         return {"status": "success"}
94
95     def update_xdf(self, booking):
96         booking.pdf = PDFTemplater.makePDF(booking.resource)
97         booking.idf = IDFTemplater().makeIDF(booking)
98         booking.save()
99
100     def get_profile(self):
101         prof = {}
102         prof['name'] = self.lab.name
103         prof['contact'] = {
104             "phone": self.lab.contact_phone,
105             "email": self.lab.contact_email
106         }
107         prof['host_count'] = []
108         for host in HostProfile.objects.filter(labs=self.lab):
109             count = Host.objects.filter(profile=host, lab=self.lab).count()
110             prof['host_count'].append(
111                 {
112                     "type": host.name,
113                     "count": count
114                 }
115             )
116         return prof
117
118     def get_inventory(self):
119         inventory = {}
120         hosts = Host.objects.filter(lab=self.lab)
121         images = Image.objects.filter(from_lab=self.lab)
122         profiles = HostProfile.objects.filter(labs=self.lab)
123         inventory['hosts'] = self.serialize_hosts(hosts)
124         inventory['images'] = self.serialize_images(images)
125         inventory['host_types'] = self.serialize_host_profiles(profiles)
126         return inventory
127
128     def get_host(self, hostname):
129         host = get_object_or_404(Host, labid=hostname, lab=self.lab)
130         return {
131             "booked": host.booked,
132             "working": host.working,
133             "type": host.profile.name
134         }
135
136     def update_host(self, hostname, data):
137         host = get_object_or_404(Host, labid=hostname, lab=self.lab)
138         if "working" in data:
139             working = data['working'] == "true"
140             host.working = working
141         host.save()
142         return self.get_host(hostname)
143
144     def get_status(self):
145         return {"status": self.lab.status}
146
147     def set_status(self, payload):
148         {}
149
150     def get_current_jobs(self):
151         jobs = Job.objects.filter(booking__lab=self.lab)
152
153         return self.serialize_jobs(jobs, status=JobStatus.CURRENT)
154
155     def get_new_jobs(self):
156         jobs = Job.objects.filter(booking__lab=self.lab)
157
158         return self.serialize_jobs(jobs, status=JobStatus.NEW)
159
160     def get_done_jobs(self):
161         jobs = Job.objects.filter(booking__lab=self.lab)
162
163         return self.serialize_jobs(jobs, status=JobStatus.DONE)
164
165     def get_job(self, jobid):
166         return Job.objects.get(pk=jobid).to_dict()
167
168     def update_job(self, jobid, data):
169         {}
170
171     def serialize_jobs(self, jobs, status=JobStatus.NEW):
172         job_ser = []
173         for job in jobs:
174             jsonized_job = job.get_delta(status)
175             if len(jsonized_job['payload']) < 1:
176                 continue
177             job_ser.append(jsonized_job)
178
179         return job_ser
180
181     def serialize_hosts(self, hosts):
182         host_ser = []
183         for host in hosts:
184             h = {}
185             h['interfaces'] = []
186             h['hostname'] = host.name
187             h['host_type'] = host.profile.name
188             for iface in host.interfaces.all():
189                 eth = {}
190                 eth['mac'] = iface.mac_address
191                 eth['busaddr'] = iface.bus_address
192                 eth['name'] = iface.name
193                 eth['switchport'] = {"switch_name": iface.switch_name, "port_name": iface.port_name}
194                 h['interfaces'].append(eth)
195         return host_ser
196
197     def serialize_images(self, images):
198         images_ser = []
199         for image in images:
200             images_ser.append(
201                 {
202                     "name": image.name,
203                     "lab_id": image.lab_id,
204                     "dashboard_id": image.id
205                 }
206             )
207         return images_ser
208
209     def serialize_host_profiles(self, profiles):
210         profile_ser = []
211         for profile in profiles:
212             p = {}
213             p['cpu'] = {
214                 "cores": profile.cpuprofile.first().cores,
215                 "arch": profile.cpuprofile.first().architecture,
216                 "cpus": profile.cpuprofile.first().cpus,
217             }
218             p['disks'] = []
219             for disk in profile.storageprofile.all():
220                 d = {
221                     "size": disk.size,
222                     "type": disk.media_type,
223                     "name": disk.name
224                 }
225                 p['disks'].append(d)
226             p['description'] = profile.description
227             p['interfaces'] = []
228             for iface in profile.interfaceprofile.all():
229                 p['interfaces'].append(
230                     {
231                         "speed": iface.speed,
232                         "name": iface.name
233                     }
234                 )
235
236             p['ram'] = {"amount": profile.ramprofile.first().amount}
237             p['name'] = profile.name
238             profile_ser.append(p)
239         return profile_ser
240
241
242 class Job(models.Model):
243     """
244     This is the class that is serialized and put into the api
245     """
246     booking = models.OneToOneField(Booking, on_delete=models.CASCADE, null=True)
247     status = models.IntegerField(default=JobStatus.NEW)
248     complete = models.BooleanField(default=False)
249
250     def to_dict(self):
251         d = {}
252         j = {}
253         j['id'] = self.id
254         for relation in AccessRelation.objects.filter(job=self):
255             if 'access' not in d:
256                 d['access'] = {}
257             d['access'][relation.task_id] = relation.config.to_dict()
258         for relation in SoftwareRelation.objects.filter(job=self):
259             if 'software' not in d:
260                 d['software'] = {}
261             d['software'][relation.task_id] = relation.config.to_dict()
262         for relation in HostHardwareRelation.objects.filter(job=self):
263             if 'hardware' not in d:
264                 d['hardware'] = {}
265             d['hardware'][relation.task_id] = relation.config.to_dict()
266         for relation in HostNetworkRelation.objects.filter(job=self):
267             if 'network' not in d:
268                 d['network'] = {}
269             d['network'][relation.task_id] = relation.config.to_dict()
270         for relation in SnapshotRelation.objects.filter(job=self):
271             if 'snapshot' not in d:
272                 d['snapshot'] = {}
273             d['snapshot'][relation.task_id] = relation.config.to_dict()
274
275         j['payload'] = d
276
277         return j
278
279     def get_tasklist(self, status="all"):
280         tasklist = []
281         clist = [
282             HostHardwareRelation,
283             AccessRelation,
284             HostNetworkRelation,
285             SoftwareRelation,
286             SnapshotRelation
287         ]
288         if status == "all":
289             for cls in clist:
290                 tasklist += list(cls.objects.filter(job=self))
291         else:
292             for cls in clist:
293                 tasklist += list(cls.objects.filter(job=self).filter(status=status))
294         return tasklist
295
296     def is_fulfilled(self):
297         """
298         This method should return true if all of the job's tasks are done,
299         and false otherwise
300         """
301         my_tasks = self.get_tasklist()
302         for task in my_tasks:
303             if task.status != JobStatus.DONE:
304                 return False
305         return True
306
307     def get_delta(self, status):
308         d = {}
309         j = {}
310         j['id'] = self.id
311         for relation in AccessRelation.objects.filter(job=self).filter(status=status):
312             if 'access' not in d:
313                 d['access'] = {}
314             d['access'][relation.task_id] = relation.config.get_delta()
315         for relation in SoftwareRelation.objects.filter(job=self).filter(status=status):
316             if 'software' not in d:
317                 d['software'] = {}
318             d['software'][relation.task_id] = relation.config.get_delta()
319         for relation in HostHardwareRelation.objects.filter(job=self).filter(status=status):
320             if 'hardware' not in d:
321                 d['hardware'] = {}
322             d['hardware'][relation.task_id] = relation.config.get_delta()
323         for relation in HostNetworkRelation.objects.filter(job=self).filter(status=status):
324             if 'network' not in d:
325                 d['network'] = {}
326             d['network'][relation.task_id] = relation.config.get_delta()
327         for relation in SnapshotRelation.objects.filter(job=self).filter(status=status):
328             if 'snapshot' not in d:
329                 d['snapshot'] = {}
330             d['snapshot'][relation.task_id] = relation.config.get_delta()
331
332         j['payload'] = d
333         return j
334
335     def to_json(self):
336         return json.dumps(self.to_dict())
337
338
339 class TaskConfig(models.Model):
340     def to_dict(self):
341         pass
342
343     def get_delta(self):
344         pass
345
346     def to_json(self):
347         return json.dumps(self.to_dict())
348
349     def clear_delta(self):
350         self.delta = '{}'
351
352
353 class OpnfvApiConfig(models.Model):
354
355     installer = models.CharField(max_length=200)
356     scenario = models.CharField(max_length=300)
357     roles = models.ManyToManyField(Host)
358     delta = models.TextField()
359
360     def to_dict(self):
361         d = {}
362         if self.installer:
363             d['installer'] = self.installer
364         if self.scenario:
365             d['scenario'] = self.scenario
366
367         hosts = self.roles.all()
368         if hosts.exists():
369             d['roles'] = []
370         for host in self.roles.all():
371             d['roles'].append({host.labid: host.config.opnfvRole.name})
372
373         return d
374
375     def to_json(self):
376         return json.dumps(self.to_dict())
377
378     def set_installer(self, installer):
379         self.installer = installer
380         d = json.loads(self.delta)
381         d['installer'] = installer
382         self.delta = json.dumps(d)
383
384     def set_scenario(self, scenario):
385         self.scenario = scenario
386         d = json.loads(self.delta)
387         d['scenario'] = scenario
388         self.delta = json.dumps(d)
389
390     def add_role(self, host):
391         self.roles.add(host)
392         d = json.loads(self.delta)
393         if 'role' not in d:
394             d['role'] = []
395         d['roles'].append({host.labid: host.config.opnfvRole.name})
396         self.delta = json.dumps(d)
397
398     def clear_delta(self):
399         self.delta = '{}'
400
401     def get_delta(self):
402         if not self.delta:
403             self.delta = self.to_json()
404             self.save()
405         return json.loads(self.delta)
406
407
408 class AccessConfig(TaskConfig):
409     access_type = models.CharField(max_length=50)
410     user = models.ForeignKey(User, on_delete=models.CASCADE)
411     revoke = models.BooleanField(default=False)
412     context = models.TextField(default="")
413     delta = models.TextField(default="{}")
414
415     def to_dict(self):
416         d = {}
417         d['access_type'] = self.access_type
418         d['user'] = self.user.id
419         d['revoke'] = self.revoke
420         try:
421             d['context'] = json.loads(self.context)
422         except Exception:
423             pass
424         return d
425
426     def get_delta(self):
427         if not self.delta:
428             self.delta = self.to_json()
429             self.save()
430         d = json.loads(self.delta)
431         d["lab_token"] = self.accessrelation.lab_token
432
433         return d
434
435     def to_json(self):
436         return json.dumps(self.to_dict())
437
438     def clear_delta(self):
439         d = {}
440         d["lab_token"] = self.accessrelation.lab_token
441         self.delta = json.dumps(d)
442
443     def set_access_type(self, access_type):
444         self.access_type = access_type
445         d = json.loads(self.delta)
446         d['access_type'] = access_type
447         self.delta = json.dumps(d)
448
449     def set_user(self, user):
450         self.user = user
451         d = json.loads(self.delta)
452         d['user'] = self.user.id
453         self.delta = json.dumps(d)
454
455     def set_revoke(self, revoke):
456         self.revoke = revoke
457         d = json.loads(self.delta)
458         d['revoke'] = revoke
459         self.delta = json.dumps(d)
460
461     def set_context(self, context):
462         self.context = json.dumps(context)
463         d = json.loads(self.delta)
464         d['context'] = context
465         self.delta = json.dumps(d)
466
467
468 class SoftwareConfig(TaskConfig):
469     """
470     handled opnfv installations, etc
471     """
472     opnfv = models.ForeignKey(OpnfvApiConfig, on_delete=models.CASCADE)
473
474     def to_dict(self):
475         d = {}
476         if self.opnfv:
477             d['opnfv'] = self.opnfv.to_dict()
478
479         d["lab_token"] = self.softwarerelation.lab_token
480         self.delta = json.dumps(d)
481
482         return d
483
484     def get_delta(self):
485         d = {}
486         d['opnfv'] = self.opnfv.get_delta()
487         d['lab_token'] = self.softwarerelation.lab_token
488
489         return d
490
491     def clear_delta(self):
492         self.opnfv.clear_delta()
493
494     def to_json(self):
495         return json.dumps(self.to_dict())
496
497
498 class HardwareConfig(TaskConfig):
499     """
500     handles imaging, user accounts, etc
501     """
502     image = models.CharField(max_length=100, default="defimage")
503     power = models.CharField(max_length=100, default="off")
504     hostname = models.CharField(max_length=100, default="hostname")
505     ipmi_create = models.BooleanField(default=False)
506     delta = models.TextField()
507
508     def to_dict(self):
509         d = {}
510         d['image'] = self.image
511         d['power'] = self.power
512         d['hostname'] = self.hostname
513         d['ipmi_create'] = str(self.ipmi_create)
514         d['id'] = self.hosthardwarerelation.host.labid
515         return d
516
517     def to_json(self):
518         return json.dumps(self.to_dict())
519
520     def get_delta(self):
521         if not self.delta:
522             self.delta = self.to_json()
523             self.save()
524         d = json.loads(self.delta)
525         d['lab_token'] = self.hosthardwarerelation.lab_token
526         return d
527
528     def clear_delta(self):
529         d = {}
530         d["id"] = self.hosthardwarerelation.host.labid
531         d["lab_token"] = self.hosthardwarerelation.lab_token
532         self.delta = json.dumps(d)
533
534     def set_image(self, image):
535         self.image = image
536         d = json.loads(self.delta)
537         d['image'] = self.image
538         self.delta = json.dumps(d)
539
540     def set_power(self, power):
541         self.power = power
542         d = json.loads(self.delta)
543         d['power'] = power
544         self.delta = json.dumps(d)
545
546     def set_hostname(self, hostname):
547         self.hostname = hostname
548         d = json.loads(self.delta)
549         d['hostname'] = hostname
550         self.delta = json.dumps(d)
551
552     def set_ipmi_create(self, ipmi_create):
553         self.ipmi_create = ipmi_create
554         d = json.loads(self.delta)
555         d['ipmi_create'] = ipmi_create
556         self.delta = json.dumps(d)
557
558
559 class NetworkConfig(TaskConfig):
560     """
561     handles network configuration
562     """
563     interfaces = models.ManyToManyField(Interface)
564     delta = models.TextField()
565
566     def to_dict(self):
567         d = {}
568         hid = self.hostnetworkrelation.host.labid
569         d[hid] = {}
570         for interface in self.interfaces.all():
571             d[hid][interface.mac_address] = []
572             for vlan in interface.config.all():
573                 d[hid][interface.mac_address].append({"vlan_id": vlan.vlan_id, "tagged": vlan.tagged})
574
575         return d
576
577     def to_json(self):
578         return json.dumps(self.to_dict())
579
580     def get_delta(self):
581         if not self.delta:
582             self.delta = self.to_json()
583             self.save()
584         d = json.loads(self.delta)
585         d['lab_token'] = self.hostnetworkrelation.lab_token
586         return d
587
588     def clear_delta(self):
589         self.delta = json.dumps(self.to_dict())
590         self.save()
591
592     def add_interface(self, interface):
593         self.interfaces.add(interface)
594         d = json.loads(self.delta)
595         hid = self.hostnetworkrelation.host.labid
596         if hid not in d:
597             d[hid] = {}
598         d[hid][interface.mac_address] = []
599         for vlan in interface.config.all():
600             d[hid][interface.mac_address].append({"vlan_id": vlan.vlan_id, "tagged": vlan.tagged})
601         self.delta = json.dumps(d)
602
603
604 class SnapshotConfig(TaskConfig):
605
606     host = models.ForeignKey(Host, null=True, on_delete=models.DO_NOTHING)
607     image = models.IntegerField(null=True)
608     dashboard_id = models.IntegerField()
609     delta = models.TextField(default="{}")
610
611     def to_dict(self):
612         d = {}
613         if self.host:
614             d['host'] = self.host.labid
615         if self.image:
616             d['image'] = self.image
617         d['dashboard_id'] = self.dashboard_id
618         return d
619
620     def to_json(self):
621         return json.dumps(self.to_dict())
622
623     def get_delta(self):
624         if not self.delta:
625             self.delta = self.to_json()
626             self.save()
627         d = json.loads(self.delta)
628         return d
629
630     def clear_delta(self):
631         self.delta = json.dumps(self.to_dict())
632         self.save()
633
634     def set_host(self, host):
635         self.host = host
636         d = json.loads(self.delta)
637         d['host'] = host.labid
638         self.delta = json.dumps(d)
639
640     def set_image(self, image):
641         self.image = image
642         d = json.loads(self.delta)
643         d['image'] = self.image
644         self.delta = json.dumps(d)
645
646     def clear_image(self):
647         self.image = None
648         d = json.loads(self.delta)
649         d.pop("image", None)
650         self.delta = json.dumps(d)
651
652     def set_dashboard_id(self, dash):
653         self.dashboard_id = dash
654         d = json.loads(self.delta)
655         d['dashboard_id'] = self.dashboard_id
656         self.delta = json.dumps(d)
657
658
659 def get_task(task_id):
660     for taskclass in [AccessRelation, SoftwareRelation, HostHardwareRelation, HostNetworkRelation, SnapshotRelation]:
661         try:
662             ret = taskclass.objects.get(task_id=task_id)
663             return ret
664         except taskclass.DoesNotExist:
665             pass
666     from django.core.exceptions import ObjectDoesNotExist
667     raise ObjectDoesNotExist("Could not find matching TaskRelation instance")
668
669
670 def get_task_uuid():
671     return str(uuid.uuid4())
672
673
674 class TaskRelation(models.Model):
675     status = models.IntegerField(default=JobStatus.NEW)
676     job = models.ForeignKey(Job, on_delete=models.CASCADE)
677     config = models.OneToOneField(TaskConfig, on_delete=models.CASCADE)
678     task_id = models.CharField(default=get_task_uuid, max_length=37)
679     lab_token = models.CharField(default="null", max_length=50)
680     message = models.TextField(default="")
681
682     def delete(self, *args, **kwargs):
683         self.config.delete()
684         return super(self.__class__, self).delete(*args, **kwargs)
685
686     def type_str(self):
687         return "Generic Task"
688
689     class Meta:
690         abstract = True
691
692
693 class AccessRelation(TaskRelation):
694     config = models.OneToOneField(AccessConfig, on_delete=models.CASCADE)
695
696     def type_str(self):
697         return "Access Task"
698
699     def delete(self, *args, **kwargs):
700         self.config.delete()
701         return super(self.__class__, self).delete(*args, **kwargs)
702
703
704 class SoftwareRelation(TaskRelation):
705     config = models.OneToOneField(SoftwareConfig, on_delete=models.CASCADE)
706
707     def type_str(self):
708         return "Software Configuration Task"
709
710     def delete(self, *args, **kwargs):
711         self.config.delete()
712         return super(self.__class__, self).delete(*args, **kwargs)
713
714
715 class HostHardwareRelation(TaskRelation):
716     host = models.ForeignKey(Host, on_delete=models.CASCADE)
717     config = models.OneToOneField(HardwareConfig, on_delete=models.CASCADE)
718
719     def type_str(self):
720         return "Hardware Configuration Task"
721
722     def get_delta(self):
723         return self.config.to_dict()
724
725     def delete(self, *args, **kwargs):
726         self.config.delete()
727         return super(self.__class__, self).delete(*args, **kwargs)
728
729
730 class HostNetworkRelation(TaskRelation):
731     host = models.ForeignKey(Host, on_delete=models.CASCADE)
732     config = models.OneToOneField(NetworkConfig, on_delete=models.CASCADE)
733
734     def type_str(self):
735         return "Network Configuration Task"
736
737     def delete(self, *args, **kwargs):
738         self.config.delete()
739         return super(self.__class__, self).delete(*args, **kwargs)
740
741
742 class SnapshotRelation(TaskRelation):
743     snapshot = models.ForeignKey(Image, on_delete=models.CASCADE)
744     config = models.OneToOneField(SnapshotConfig, on_delete=models.CASCADE)
745
746     def type_str(self):
747         return "Snapshot Task"
748
749     def get_delta(self):
750         return self.config.to_dict()
751
752     def delete(self, *args, **kwargs):
753         self.config.delete()
754         return super(self.__class__, self).delete(*args, **kwargs)
755
756
757 class JobFactory(object):
758
759     @classmethod
760     def reimageHost(cls, new_image, booking, host):
761         """
762         This method will make all necessary changes to make a lab
763         reimage a host.
764         """
765         job = Job.objects.get(booking=booking)
766         # make hardware task new
767         hardware_relation = HostHardwareRelation.objects.get(host=host, job=job)
768         hardware_relation.config.set_image(new_image.lab_id)
769         hardware_relation.config.save()
770         hardware_relation.status = JobStatus.NEW
771
772         # re-apply networking after host is reset
773         net_relation = HostNetworkRelation.objects.get(host=host, job=job)
774         net_relation.status = JobStatus.NEW
775
776         # re-apply ssh access after host is reset
777         ssh_relation = AccessRelation.objects.get(job=job, config__access_type="ssh")
778         ssh_relation.status = JobStatus.NEW
779
780         # save them all at once to reduce the chance
781         # of a lab polling and only seeing partial change
782         hardware_relation.save()
783         net_relation.save()
784         ssh_relation.save()
785
786     @classmethod
787     def makeSnapshotTask(cls, image, booking, host):
788         relation = SnapshotRelation()
789         job = Job.objects.get(booking=booking)
790         config = SnapshotConfig.objects.create(dashboard_id=image.id)
791
792         relation.job = job
793         relation.config = config
794         relation.config.save()
795         relation.config = relation.config
796         relation.snapshot = image
797         relation.save()
798
799         config.clear_delta()
800         config.set_host(host)
801         config.save()
802
803     @classmethod
804     def makeCompleteJob(cls, booking):
805         hosts = Host.objects.filter(bundle=booking.resource)
806         job = None
807         try:
808             job = Job.objects.get(booking=booking)
809         except Exception:
810             job = Job.objects.create(status=JobStatus.NEW, booking=booking)
811         cls.makeHardwareConfigs(
812             hosts=hosts,
813             job=job
814         )
815         cls.makeNetworkConfigs(
816             hosts=hosts,
817             job=job
818         )
819         cls.makeSoftware(
820             hosts=hosts,
821             job=job
822         )
823         all_users = list(booking.collaborators.all())
824         all_users.append(booking.owner)
825         cls.makeAccessConfig(
826             users=all_users,
827             access_type="vpn",
828             revoke=False,
829             job=job
830         )
831         for user in all_users:
832             try:
833                 cls.makeAccessConfig(
834                     users=[user],
835                     access_type="ssh",
836                     revoke=False,
837                     job=job,
838                     context={
839                         "key": user.userprofile.ssh_public_key.open().read().decode(encoding="UTF-8"),
840                         "hosts": [host.labid for host in hosts]
841                     }
842                 )
843             except Exception:
844                 continue
845
846     @classmethod
847     def makeHardwareConfigs(cls, hosts=[], job=Job()):
848         for host in hosts:
849             hardware_config = None
850             try:
851                 hardware_config = HardwareConfig.objects.get(relation__host=host)
852             except Exception:
853                 hardware_config = HardwareConfig()
854
855             relation = HostHardwareRelation()
856             relation.host = host
857             relation.job = job
858             relation.config = hardware_config
859             relation.config.save()
860             relation.config = relation.config
861             relation.save()
862
863             hardware_config.clear_delta()
864             hardware_config.set_image(host.config.image.lab_id)
865             hardware_config.set_hostname(host.template.resource.name)
866             hardware_config.set_power("on")
867             hardware_config.set_ipmi_create(True)
868             hardware_config.save()
869
870     @classmethod
871     def makeAccessConfig(cls, users, access_type, revoke=False, job=Job(), context=False):
872         for user in users:
873             relation = AccessRelation()
874             relation.job = job
875             config = AccessConfig()
876             config.access_type = access_type
877             config.user = user
878             config.save()
879             relation.config = config
880             relation.save()
881             config.clear_delta()
882             if context:
883                 config.set_context(context)
884             config.set_access_type(access_type)
885             config.set_revoke(revoke)
886             config.set_user(user)
887             config.save()
888
889     @classmethod
890     def makeNetworkConfigs(cls, hosts=[], job=Job()):
891         for host in hosts:
892             network_config = None
893             try:
894                 network_config = NetworkConfig.objects.get(relation__host=host)
895             except Exception:
896                 network_config = NetworkConfig.objects.create()
897
898             relation = HostNetworkRelation()
899             relation.host = host
900             relation.job = job
901             network_config.save()
902             relation.config = network_config
903             relation.save()
904             network_config.clear_delta()
905
906             for interface in host.interfaces.all():
907                 network_config.add_interface(interface)
908             network_config.save()
909
910     @classmethod
911     def makeSoftware(cls, hosts=[], job=Job()):
912         def init_config(host):
913             opnfv_config = OpnfvApiConfig()
914             if host is not None:
915                 opnfv = host.config.bundle.opnfv_config.first()
916                 opnfv_config.installer = opnfv.installer.name
917                 opnfv_config.scenario = opnfv.scenario.name
918             opnfv_config.save()
919             return opnfv_config
920
921         try:
922             host = None
923             if len(hosts) > 0:
924                 host = hosts[0]
925             opnfv_config = init_config(host)
926
927             for host in hosts:
928                 opnfv_config.roles.add(host)
929             software_config = SoftwareConfig.objects.create(opnfv=opnfv_config)
930             software_config.save()
931             software_relation = SoftwareRelation.objects.create(job=job, config=software_config)
932             software_relation.save()
933             return software_relation
934         except Exception:
935             return None