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