Laas Dashboard Front End Improvements
[laas.git] / src / workflow / workflow_manager.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.http import JsonResponse
12 from django.http.request import QueryDict
13 from django.urls import reverse
14
15 from booking.models import Booking
16 from workflow.workflow_factory import WorkflowFactory
17 from workflow.models import Repository
18 from resource_inventory.models import (
19     ResourceTemplate,
20     ResourceConfiguration,
21     OPNFVConfig
22 )
23 from workflow.forms import ManagerForm
24
25 import logging
26 logger = logging.getLogger(__name__)
27
28
29 class SessionManager():
30     def active_workflow(self):
31         return self.workflows[-1]
32
33     def __init__(self, request=None):
34         self.workflows = []
35         self.owner = request.user
36         self.factory = WorkflowFactory()
37         self.result = None
38
39     def set_step_statuses(self, superclass_type, desired_enabled=True):
40         workflow = self.active_workflow()
41         steps = workflow.steps
42         for step in steps:
43             if isinstance(step, superclass_type):
44                 if desired_enabled:
45                     step.enable()
46                 else:
47                     step.disable()
48
49     def add_workflow(self, workflow_type=None, **kwargs):
50         repo = Repository()
51         if (len(self.workflows) >= 1):
52             defaults = self.workflows[-1].repository.get_child_defaults()
53             repo.set_defaults(defaults)
54             repo.el[repo.HAS_RESULT] = False
55         repo.el[repo.SESSION_USER] = self.owner
56         repo.el[repo.SESSION_MANAGER] = self
57         self.workflows.append(
58             self.factory.create_workflow(
59                 workflow_type=workflow_type,
60                 repo=repo
61             )
62         )
63
64     def get_redirect(self):
65         if isinstance(self.result, Booking):
66             return reverse('booking:booking_detail', kwargs={'booking_id': self.result.id})
67         return "/"
68
69     def pop_workflow(self, discard=False):
70         multiple_wfs = len(self.workflows) > 1
71         if multiple_wfs:
72             if self.workflows[-1].repository.el[Repository.RESULT]:  # move result
73                 key = self.workflows[-1].repository.el[Repository.RESULT_KEY]
74                 result = self.workflows[-1].repository.el[Repository.RESULT]
75                 self.workflows[-2].repository.el[key] = result
76         prev_workflow = self.workflows.pop()
77         if self.workflows:
78             current_repo = self.workflows[-1].repository
79         else:
80             current_repo = prev_workflow.repository
81         self.result = current_repo.el[current_repo.RESULT]
82         if discard:
83             current_repo.cancel()
84         return multiple_wfs, self.result
85
86     def status(self, request):
87         return {
88             "steps": [step.to_json() for step in self.active_workflow().steps],
89             "active": self.active_workflow().repository.el['active_step'],
90             "workflow_count": len(self.workflows)
91         }
92
93     def handle_post(self, request):
94         form = ManagerForm(request.POST)
95         if form.is_valid():
96             self.get_active_step().post(
97                 QueryDict(form.cleaned_data['step_form']),
98                 user=request.user
99             )
100             # change step
101             if form.cleaned_data['step'] == 'prev':
102                 self.go_prev()
103             if form.cleaned_data['step'] == 'next':
104                 self.go_next()
105         else:
106             pass  # Exception?
107
108     def handle_request(self, request):
109         if request.method == 'POST':
110             self.handle_post(request)
111         return self.render(request)
112
113     def render(self, request, **kwargs):
114         if self.workflows:
115             return JsonResponse({
116                 "meta": self.status(request),
117                 "content": self.get_active_step().render_to_string(request),
118             })
119         else:
120             return JsonResponse({
121                 "redirect": self.get_redirect()
122             })
123
124     def post_render(self, request):
125         return self.active_workflow().steps[self.active_workflow().active_index].post_render(request)
126
127     def get_active_step(self):
128         return self.active_workflow().steps[self.active_workflow().active_index]
129
130     def go_next(self, **kwargs):
131         # need to verify current step is valid to allow this
132         if self.get_active_step().valid < 200:
133             return
134         next_step = self.active_workflow().active_index + 1
135         if next_step >= len(self.active_workflow().steps):
136             raise Exception("Out of bounds request for step")
137         while not self.active_workflow().steps[next_step].enabled:
138             next_step += 1
139         self.active_workflow().repository.el['active_step'] = next_step
140         self.active_workflow().active_index = next_step
141
142     def go_prev(self, **kwargs):
143         prev_step = self.active_workflow().active_index - 1
144         if prev_step < 0:
145             raise Exception("Out of bounds request for step")
146         while not self.active_workflow().steps[prev_step].enabled:
147             prev_step -= 1
148         self.active_workflow().repository.el['active_step'] = prev_step
149         self.active_workflow().active_index = prev_step
150
151     def prefill_repo(self, target_id, workflow_type):
152         self.repository.el[self.repository.EDIT] = True
153         edit_object = None
154         if workflow_type == 0:
155             edit_object = Booking.objects.get(pk=target_id)
156             self.prefill_booking(edit_object)
157         elif workflow_type == 1:
158             edit_object = ResourceTemplate.objects.get(pk=target_id)
159             self.prefill_resource(edit_object)
160         elif workflow_type == 2:
161             edit_object = ResourceTemplate.objects.get(pk=target_id)
162             self.prefill_config(edit_object)
163
164     def prefill_booking(self, booking):
165         models = self.make_booking_models(booking)
166         confirmation = self.make_booking_confirm(booking)
167         self.active_workflow().repository.el[self.active_workflow().repository.BOOKING_MODELS] = models
168         self.active_workflow().repository.el[self.active_workflow().repository.CONFIRMATION] = confirmation
169         self.active_workflow().repository.el[self.active_workflow().repository.RESOURCE_TEMPLATE_MODELS] = self.make_grb_models(booking.resource.template)
170         self.active_workflow().repository.el[self.active_workflow().repository.SELECTED_RESOURCE_TEMPLATE] = self.make_grb_models(booking.resource.template)['bundle']
171         self.active_workflow().repository.el[self.active_workflow().repository.CONFIG_MODELS] = self.make_config_models(booking.config_bundle)
172
173     def prefill_resource(self, resource):
174         models = self.make_grb_models(resource)
175         confirm = self.make_grb_confirm(resource)
176         self.active_workflow().repository.el[self.active_workflow().repository.RESOURCE_TEMPLATE_MODELS] = models
177         self.active_workflow().repository.el[self.active_workflow().repository.CONFIRMATION] = confirm
178
179     def prefill_config(self, config):
180         models = self.make_config_models(config)
181         confirm = self.make_config_confirm(config)
182         self.active_workflow().repository.el[self.active_workflow().repository.CONFIG_MODELS] = models
183         self.active_workflow().repository.el[self.active_workflow().repository.CONFIRMATION] = confirm
184         grb_models = self.make_grb_models(config.bundle)
185         self.active_workflow().repository.el[self.active_workflow().repository.RESOURCE_TEMPLATE_MODELS] = grb_models
186
187     def make_grb_models(self, resource):
188         models = self.active_workflow().repository.el.get(self.active_workflow().repository.RESOURCE_TEMPLATE_MODELS, {})
189         models['hosts'] = []
190         models['bundle'] = resource
191         models['interfaces'] = {}
192         models['vlans'] = {}
193         for host in resource.getResources():
194             models['hosts'].append(host)
195             models['interfaces'][host.resource.name] = []
196             models['vlans'][host.resource.name] = {}
197             for interface in host.generic_interfaces.all():
198                 models['interfaces'][host.resource.name].append(interface)
199                 models['vlans'][host.resource.name][interface.profile.name] = []
200                 for vlan in interface.vlans.all():
201                     models['vlans'][host.resource.name][interface.profile.name].append(vlan)
202         return models
203
204     def make_grb_confirm(self, resource):
205         confirm = self.active_workflow().repository.el.get(self.active_workflow().repository.CONFIRMATION, {})
206         confirm['resource'] = {}
207         confirm['resource']['hosts'] = []
208         confirm['resource']['lab'] = resource.lab.lab_user.username
209         for host in resource.getResources():
210             confirm['resource']['hosts'].append({"name": host.resource.name, "profile": host.profile.name})
211         return confirm
212
213     def make_config_models(self, config):
214         models = self.active_workflow().repository.el.get(self.active_workflow().repository.CONFIG_MODELS, {})
215         models['bundle'] = config
216         models['host_configs'] = []
217         for host_conf in ResourceConfiguration.objects.filter(bundle=config):
218             models['host_configs'].append(host_conf)
219         models['opnfv'] = OPNFVConfig.objects.filter(bundle=config).last()
220         return models
221
222     def make_config_confirm(self, config):
223         confirm = self.active_workflow().repository.el.get(self.active_workflow().repository.CONFIRMATION, {})
224         confirm['configuration'] = {}
225         confirm['configuration']['hosts'] = []
226         confirm['configuration']['name'] = config.name
227         confirm['configuration']['description'] = config.description
228         opnfv = OPNFVConfig.objects.filter(bundle=config).last()
229         confirm['configuration']['installer'] = opnfv.installer.name
230         confirm['configuration']['scenario'] = opnfv.scenario.name
231         for host_conf in ResourceConfiguration.objects.filter(bundle=config):
232             h = {"name": host_conf.host.resource.name, "image": host_conf.image.name, "role": host_conf.opnfvRole.name}
233             confirm['configuration']['hosts'].append(h)
234         return confirm
235
236     def make_booking_models(self, booking):
237         models = self.active_workflow().repository.el.get(self.active_workflow().repository.BOOKING_MODELS, {})
238         models['booking'] = booking
239         models['collaborators'] = []
240         for user in booking.collaborators.all():
241             models['collaborators'].append(user)
242         return models
243
244     def make_booking_confirm(self, booking):
245         confirm = self.active_workflow().repository.el.get(self.active_workflow().repository.CONFIRMATION, {})
246         confirm['booking'] = {}
247         confirm['booking']['length'] = (booking.end - booking.start).days
248         confirm['booking']['project'] = booking.project
249         confirm['booking']['purpose'] = booking.purpose
250         confirm['booking']['resource name'] = booking.resource.template.name
251         confirm['booking']['configuration name'] = booking.config_bundle.name
252         confirm['booking']['collaborators'] = []
253         for user in booking.collaborators.all():
254             confirm['booking']['collaborators'].append(user.username)
255         return confirm
256
257
258 class ManagerTracker():
259     instance = None
260
261     managers = {}
262
263     def __init__(self):
264         pass
265
266     @staticmethod
267     def getInstance():
268         if ManagerTracker.instance is None:
269             ManagerTracker.instance = ManagerTracker()
270         return ManagerTracker.instance