Requires a Jumphost per POD
[pharos-tools.git] / dashboard / 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
13 import random
14
15 from booking.models import Booking
16 from workflow.workflow_factory import WorkflowFactory, MetaStep, MetaRelation
17 from workflow.models import Repository, Confirmation_Step
18 from resource_inventory.models import (
19     GenericResourceBundle,
20     ConfigBundle,
21     HostConfiguration,
22     OPNFVConfig
23 )
24
25 import logging
26 logger = logging.getLogger(__name__)
27
28
29 class SessionManager():
30
31     def __init__(self, request=None):
32         self.repository = Repository()
33         self.repository.el[self.repository.SESSION_USER] = request.user
34         self.repository.el['active_step'] = 0
35         self.steps = []
36         self.factory = WorkflowFactory()
37         c_step = WorkflowFactory().make_step(Confirmation_Step, self.repository)
38         self.steps.append(c_step)
39         metaconfirm = MetaStep()
40         metaconfirm.index = 0
41         metaconfirm.short_title = "confirm"
42         self.repository.el['steps'] = 1
43         self.metaworkflow = None
44         self.metaworkflows = []
45         self.metarelations = []
46         self.relationreverselookup = {}
47         self.initialized = False
48         self.active_index = 0
49         self.step_meta = [metaconfirm]
50         self.relation_depth = 0
51
52     def add_workflow(self, workflow_type=None, target_id=None, **kwargs):
53         if target_id is not None:
54             self.prefill_repo(target_id, workflow_type)
55         factory_steps, meta_info = self.factory.conjure(workflow_type=workflow_type, repo=self.repository)
56         offset = len(meta_info)
57         for relation in self.metarelations:
58             if relation.depth > self.relation_depth:
59                 self.relation_depth = relation.depth
60             if relation.parent >= self.repository.el['active_step']:
61                 relation.parent += offset
62                 for i in range(0, len(relation.children)):
63                     if relation.children[i] >= self.repository.el['active_step']:
64                         relation.children[i] += offset
65         self.step_meta[self.active_index:self.active_index] = meta_info
66         self.steps[self.active_index:self.active_index] = factory_steps
67
68         if self.initialized:
69             relation = MetaRelation()
70             relation.parent = self.repository.el['active_step'] + offset
71             relation.depth = self.relationreverselookup[self.step_meta[relation.parent]].depth + 1
72             if relation.depth > self.relation_depth:
73                 self.relation_depth = relation.depth
74             for i in range(self.repository.el['active_step'], offset + self.repository.el['active_step']):
75                 relation.children.append(i)
76                 self.relationreverselookup[self.step_meta[i]] = relation
77             relation.color = "#%06x" % random.randint(0, 0xFFFFFF)
78             self.metarelations.append(relation)
79         else:
80             relation = MetaRelation()
81             relation.depth = 0
82             relation.parent = 500000000000
83             for i in range(0, len(self.step_meta)):
84                 relation.children.append(i)
85                 self.relationreverselookup[self.step_meta[i]] = relation
86             self.metarelations.append(relation)
87             self.initialized = True
88
89     def status(self, request):
90         try:
91             steps = []
92             for step in self.step_meta:
93                 steps.append(step.to_json())
94             parents = {}
95             children = {}
96             responsejson = {}
97             responsejson["steps"] = steps
98             responsejson["active"] = self.repository.el['active_step']
99             responsejson["relations"] = []
100             i = 0
101             for relation in self.metarelations:
102                 responsejson["relations"].append(relation.to_json())
103                 children[relation.parent] = i
104                 for child in relation.children:
105                     parents[child] = i
106                 i += 1
107             responsejson['max_depth'] = self.relation_depth
108             responsejson['parents'] = parents
109             responsejson['children'] = children
110             return JsonResponse(responsejson, safe=False)
111         except Exception:
112             pass
113
114     def render(self, request, **kwargs):
115         # filter out when a step needs to handle post/form data
116         # if 'workflow' in post data, this post request was meant for me, not step
117         if request.method == 'POST' and request.POST.get('workflow', None) is None:
118             return self.steps[self.active_index].post_render(request)
119         return self.steps[self.active_index].render(request)
120
121     def post_render(self, request):
122         return self.steps[self.active_index].post_render(request)
123
124     def goto(self, num, **kwargs):
125         self.repository.el['active_step'] = int(num)
126         self.active_index = int(num)
127         # TODO: change to include some checking
128
129     def prefill_repo(self, target_id, workflow_type):
130         self.repository.el[self.repository.EDIT] = True
131         edit_object = None
132         if workflow_type == 0:
133             edit_object = Booking.objects.get(pk=target_id)
134             self.prefill_booking(edit_object)
135         elif workflow_type == 1:
136             edit_object = GenericResourceBundle.objects.get(pk=target_id)
137             self.prefill_resource(edit_object)
138         elif workflow_type == 2:
139             edit_object = ConfigBundle.objects.get(pk=target_id)
140             self.prefill_config(edit_object)
141
142     def prefill_booking(self, booking):
143         models = self.make_booking_models(booking)
144         confirmation = self.make_booking_confirm(booking)
145         self.repository.el[self.repository.BOOKING_MODELS] = models
146         self.repository.el[self.repository.CONFIRMATION] = confirmation
147         self.repository.el[self.repository.GRESOURCE_BUNDLE_MODELS] = self.make_grb_models(booking.resource.template)
148         self.repository.el[self.repository.BOOKING_SELECTED_GRB] = self.make_grb_models(booking.resource.template)['bundle']
149         self.repository.el[self.repository.CONFIG_MODELS] = self.make_config_models(booking.config_bundle)
150
151     def prefill_resource(self, resource):
152         models = self.make_grb_models(resource)
153         confirm = self.make_grb_confirm(resource)
154         self.repository.el[self.repository.GRESOURCE_BUNDLE_MODELS] = models
155         self.repository.el[self.repository.CONFIRMATION] = confirm
156
157     def prefill_config(self, config):
158         models = self.make_config_models(config)
159         confirm = self.make_config_confirm(config)
160         self.repository.el[self.repository.CONFIG_MODELS] = models
161         self.repository.el[self.repository.CONFIRMATION] = confirm
162         grb_models = self.make_grb_models(config.bundle)
163         self.repository.el[self.repository.GRESOURCE_BUNDLE_MODELS] = grb_models
164         self.repository.el[self.repository.SWCONF_SELECTED_GRB] = config.bundle
165
166     def make_grb_models(self, resource):
167         models = self.repository.el.get(self.repository.GRESOURCE_BUNDLE_MODELS, {})
168         models['hosts'] = []
169         models['bundle'] = resource
170         models['interfaces'] = {}
171         models['vlans'] = {}
172         for host in resource.getHosts():
173             models['hosts'].append(host)
174             models['interfaces'][host.resource.name] = []
175             models['vlans'][host.resource.name] = {}
176             for interface in host.generic_interfaces.all():
177                 models['interfaces'][host.resource.name].append(interface)
178                 models['vlans'][host.resource.name][interface.profile.name] = []
179                 for vlan in interface.vlans.all():
180                     models['vlans'][host.resource.name][interface.profile.name].append(vlan)
181         return models
182
183     def make_grb_confirm(self, resource):
184         confirm = self.repository.el.get(self.repository.CONFIRMATION, {})
185         confirm['resource'] = {}
186         confirm['resource']['hosts'] = []
187         confirm['resource']['lab'] = resource.lab.lab_user.username
188         for host in resource.getHosts():
189             confirm['resource']['hosts'].append({"name": host.resource.name, "profile": host.profile.name})
190         return confirm
191
192     def make_config_models(self, config):
193         models = self.repository.el.get(self.repository.CONFIG_MODELS, {})
194         models['bundle'] = config
195         models['host_configs'] = []
196         for host_conf in HostConfiguration.objects.filter(bundle=config):
197             models['host_configs'].append(host_conf)
198         models['opnfv'] = OPNFVConfig.objects.filter(bundle=config).last()
199         return models
200
201     def make_config_confirm(self, config):
202         confirm = self.repository.el.get(self.repository.CONFIRMATION, {})
203         confirm['configuration'] = {}
204         confirm['configuration']['hosts'] = []
205         confirm['configuration']['name'] = config.name
206         confirm['configuration']['description'] = config.description
207         opnfv = OPNFVConfig.objects.filter(bundle=config).last()
208         confirm['configuration']['installer'] = opnfv.installer.name
209         confirm['configuration']['scenario'] = opnfv.scenario.name
210         for host_conf in HostConfiguration.objects.filter(bundle=config):
211             h = {"name": host_conf.host.resource.name, "image": host_conf.image.name, "role": host_conf.opnfvRole.name}
212             confirm['configuration']['hosts'].append(h)
213         return confirm
214
215     def make_booking_models(self, booking):
216         models = self.repository.el.get(self.repository.BOOKING_MODELS, {})
217         models['booking'] = booking
218         models['collaborators'] = []
219         for user in booking.collaborators.all():
220             models['collaborators'].append(user)
221         return models
222
223     def make_booking_confirm(self, booking):
224         confirm = self.repository.el.get(self.repository.CONFIRMATION, {})
225         confirm['booking'] = {}
226         confirm['booking']['length'] = (booking.end - booking.start).days
227         confirm['booking']['project'] = booking.project
228         confirm['booking']['purpose'] = booking.purpose
229         confirm['booking']['resource name'] = booking.resource.template.name
230         confirm['booking']['configuration name'] = booking.config_bundle.name
231         confirm['booking']['collaborators'] = []
232         for user in booking.collaborators.all():
233             confirm['booking']['collaborators'].append(user.username)
234         return confirm
235
236
237 class ManagerTracker():
238     instance = None
239
240     managers = {}
241
242     def __init__(self):
243         pass
244
245     @staticmethod
246     def getInstance():
247         if ManagerTracker.instance is None:
248             ManagerTracker.instance = ManagerTracker()
249         return ManagerTracker.instance