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