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