Lab as a Service 2.0
[pharos-tools.git] / dashboard / src / workflow / sw_bundle_workflow.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.forms import formset_factory, modelformset_factory
12
13 from workflow.models import WorkflowStep
14 from workflow.forms import SoftwareConfigurationForm, HostSoftwareDefinitionForm
15 from workflow.booking_workflow import Resource_Select
16 from resource_inventory.models import *
17
18
19 #resource selection step is reused from Booking workflow
20
21 #TODO: change this: too hacky, just for presentation
22
23 class SWConf_Resource_Select(Resource_Select):
24     def __init__(self, *args, **kwargs):
25         super(SWConf_Resource_Select, self).__init__(*args, **kwargs)
26         self.repo_key = self.repo.SWCONF_SELECTED_GRB
27         self.confirm_key = "configuration"
28
29     def get_default_entry(self):
30         booking_grb = self.repo_get(self.repo.BOOKING_SELECTED_GRB)
31         if booking_grb:
32             return booking_grb
33         created_grb = self.repo_get(self.repo.GRESOURCE_BUNDLE_MODELS, {}).get("bundle", None)
34         return created_grb
35
36     def post_render(self, request):
37         response = super(SWConf_Resource_Select, self).post_render(request)
38         models = self.repo_get(self.repo.CONFIG_MODELS, {})
39         bundle = models.get("bundle", ConfigBundle(owner=self.repo_get(self.repo.SESSION_USER)))
40         bundle.bundle = self.repo_get(self.repo_key)  # super put grb here
41         models['bundle'] = bundle
42         self.repo_put(self.repo.CONFIG_MODELS, models)
43         return response
44
45 class Define_Software(WorkflowStep):
46     template = 'config_bundle/steps/define_software.html'
47     title = "Pick Software"
48     description = "Choose the opnfv and image of your machines"
49     short_title = "host config"
50
51     def create_hostformset(self, hostlist):
52         hosts_initial = []
53         host_configs = self.repo_get(self.repo.CONFIG_MODELS, {}).get("host_configs", False)
54         if host_configs:
55             for config in host_configs:
56                 host_initial = {'host_id': config.host.id, 'host_name': config.host.resource.name}
57                 host_initial['role'] = config.opnfvRole
58                 host_initial['image'] = config.image
59                 hosts_initial.append(host_initial)
60
61         else:
62             for host in hostlist:
63                 host_initial = {'host_id': host.id, 'host_name': host.resource.name}
64
65                 hosts_initial.append(host_initial)
66
67         HostFormset = formset_factory(HostSoftwareDefinitionForm, extra=0)
68         host_formset = HostFormset(initial=hosts_initial)
69
70         filter_data = {}
71         user = self.repo_get(self.repo.SESSION_USER)
72         i=0;
73         for host_data in hosts_initial:
74             host = GenericHost.objects.get(pk=host_data['host_id'])
75             excluded_images = Image.objects.exclude(owner=user).exclude(public=True)
76             excluded_images = excluded_images | Image.objects.exclude(host_type=host.profile)
77             lab = self.repo_get(self.repo.SWCONF_SELECTED_GRB).lab
78             excluded_images = excluded_images | Image.objects.exclude(from_lab=lab)
79             filter_data["id_form-" + str(i) + "-image"] = []
80             for image in excluded_images:
81                 filter_data["id_form-" + str(i) + "-image"].append(image.name)
82             i += 1
83
84         return host_formset, filter_data
85
86     def get_host_list(self, grb=None):
87         if grb is None:
88             grb = self.repo_get(self.repo.SWCONF_SELECTED_GRB, False)
89             if not grb:
90                 return []
91         if grb.id:
92             return GenericHost.objects.filter(resource__bundle=grb)
93         generic_hosts = self.repo_get(self.repo.GRESOURCE_BUNDLE_MODELS, {}).get("hosts", [])
94         return generic_hosts
95
96     def get_context(self):
97         context = super(Define_Software, self).get_context()
98
99         grb = self.repo_get(self.repo.SWCONF_SELECTED_GRB, False)
100
101         if grb:
102             context["grb"] = grb
103             formset, filter_data = self.create_hostformset(self.get_host_list(grb))
104             context["formset"] = formset
105             context["filter_data"] = filter_data
106         else:
107             context["error"] = "Please select a resource first"
108             self.metastep.set_invalid("Step requires information that is not yet provided by previous step")
109
110         return context
111
112     def post_render(self, request):
113         models = self.repo_get(self.repo.CONFIG_MODELS, {})
114         if "bundle" not in models:
115             models['bundle'] = ConfigBundle(owner=self.repo_get(self.repo.SESSION_USER))
116
117         confirm = self.repo_get(self.repo.CONFIRMATION, {})
118
119         HostFormset = formset_factory(HostSoftwareDefinitionForm, extra=0)
120         formset = HostFormset(request.POST)
121         hosts = self.get_host_list()
122         if formset.is_valid():
123             models['host_configs'] = []
124             i = 0
125             confirm_hosts = []
126             for form in formset:
127                 host = hosts[i]
128                 i += 1
129                 image = form.cleaned_data['image']
130                 # checks image compatability
131                 grb = self.repo_get(self.repo.SWCONF_SELECTED_GRB)
132                 lab = None
133                 if grb:
134                     lab = grb.lab
135                 try:
136                     owner = self.repo_get(self.repo.SESSION_USER)
137                     q = Image.objects.filter(owner=owner) | Image.objects.filter(public=True)
138                     q.filter(host_type=host.profile)
139                     q.filter(from_lab=lab)
140                     q.get(id=image.id)  # will throw exception if image is not in q
141                 except:
142                     self.metastep.set_invalid("Image " + image.name + " is not compatible with host " + host.resource.name)
143                 role = form.cleaned_data['role']
144                 bundle = models['bundle']
145                 hostConfig = HostConfiguration(
146                         host=host,
147                         image=image,
148                         bundle=bundle,
149                         opnfvRole=role
150                 )
151                 models['host_configs'].append(hostConfig)
152                 confirm_host = {"name": host.resource.name, "image": image.name, "role": role.name}
153                 confirm_hosts.append(confirm_host)
154
155             self.repo_put(self.repo.CONFIG_MODELS, models)
156             if "configuration" not in confirm:
157                 confirm['configuration'] = {}
158             confirm['configuration']['hosts'] = confirm_hosts
159             self.repo_put(self.repo.CONFIRMATION, confirm)
160             self.metastep.set_valid("Completed")
161         else:
162             self.metastep.set_invalid("Please complete all fields")
163
164         return self.render(request)
165
166 class Config_Software(WorkflowStep):
167     template = 'config_bundle/steps/config_software.html'
168     form = SoftwareConfigurationForm
169     context = {'workspace_form':form}
170     title = "Other Info"
171     description = "Give your software config a name, description, and other stuff"
172     short_title = "config info"
173
174     def get_context(self):
175         context = super(Config_Software, self).get_context()
176
177         initial = {}
178         models = self.repo_get(self.repo.CONFIG_MODELS, {})
179         bundle = models.get("bundle", False)
180         if bundle:
181             initial['name'] = bundle.name
182             initial['description'] = bundle.description
183         opnfv = models.get("opnfv", False)
184         if opnfv:
185             initial['installer'] = opnfv.installer
186             initial['scenario'] = opnfv.scenario
187         else:
188             initial['opnfv'] = False
189         supported = {}
190         for installer in Installer.objects.all():
191             supported[str(installer)] = []
192             for scenario in installer.sup_scenarios.all():
193                 supported[str(installer)].append(str(scenario))
194
195         context["form"] = SoftwareConfigurationForm(initial=initial)
196         context['supported'] = supported
197
198         return context
199
200     def post_render(self, request):
201         try:
202             models = self.repo_get(self.repo.CONFIG_MODELS, {})
203             if "bundle" not in models:
204                 models['bundle'] = ConfigBundle(owner=self.repo_get(self.repo.SESSION_USER))
205
206
207             confirm = self.repo_get(self.repo.CONFIRMATION, {})
208             if "configuration" not in confirm:
209                 confirm['configuration'] = {}
210
211             form = self.form(request.POST)
212             if form.is_valid():
213                 models['bundle'].name = form.cleaned_data['name']
214                 models['bundle'].description = form.cleaned_data['description']
215                 if form.cleaned_data['opnfv']:
216                     installer = form.cleaned_data['installer']
217                     scenario = form.cleaned_data['scenario']
218                     opnfv = OPNFVConfig(
219                             bundle=models['bundle'],
220                             installer=installer,
221                             scenario=scenario
222                         )
223                     models['opnfv'] = opnfv
224                     confirm['configuration']['installer'] = form.cleaned_data['installer'].name
225                     confirm['configuration']['scenario'] = form.cleaned_data['scenario'].name
226
227                 confirm['configuration']['name'] = form.cleaned_data['name']
228                 confirm['configuration']['description'] = form.cleaned_data['description']
229                 self.metastep.set_valid("Complete")
230             else:
231                 self.metastep.set_invalid("Please correct the errors shown below")
232
233             self.repo_put(self.repo.CONFIG_MODELS, models)
234             self.repo_put(self.repo.CONFIRMATION, confirm)
235
236         except Exception as e:
237             pass
238         return self.render(request)