Lab as a Service 2.0
[laas.git] / src / workflow / booking_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 from django.contrib import messages
11 from django.shortcuts import render
12 from django.contrib.auth.models import User
13 from django.utils import timezone
14
15 import json
16 from datetime import timedelta
17
18 from account.models import UserProfile
19 from booking.models import Booking
20 from workflow.models import WorkflowStep
21 from workflow.forms import ResourceSelectorForm, SWConfigSelectorForm, BookingMetaForm, ConfirmationForm
22 from resource_inventory.models import GenericResourceBundle, ResourceBundle, ConfigBundle
23
24 class Resource_Select(WorkflowStep):
25     template = 'booking/steps/resource_select.html'
26     title = "Select Resource"
27     description = "Select a resource template to use for your deployment"
28     short_title = "pod select"
29
30     def __init__(self, *args, **kwargs):
31         super(Resource_Select, self).__init__(*args, **kwargs)
32         self.repo_key = self.repo.SELECTED_GRESOURCE_BUNDLE
33         self.repo_check_key = False
34         self.confirm_key = "booking"
35
36     def get_default_entry(self):
37         return None
38
39     def get_context(self):
40         context = super(Resource_Select, self).get_context()
41         default = []
42         chosen_bundle = None
43         default_bundle = self.get_default_entry()
44         if default_bundle:
45             context['disabled'] = True
46             chosen_bundle = default_bundle
47             if chosen_bundle.id:
48                 default.append(chosen_bundle.id)
49             else:
50                 default.append("repo bundle")
51         else:
52             chosen_bundle = self.repo_get(self.repo_key, False)
53             if chosen_bundle:
54                 if chosen_bundle.id:
55                     default.append(chosen_bundle.id)
56                 else:
57                     default.append("repo bundle")
58
59         bundle = default_bundle
60         if not bundle:
61             bundle = chosen_bundle
62         edit = self.repo_get(self.repo.EDIT, False)
63         user = self.repo_get(self.repo.SESSION_USER)
64         context['form'] = ResourceSelectorForm(
65                 data={"user": user},
66                 chosen_resource=default,
67                 bundle=bundle,
68                 edit=edit
69                 )
70         return context
71
72     def  post_render(self, request):
73         form = ResourceSelectorForm(request.POST)
74         context = self.get_context()
75         if form.is_valid():
76             data = form.cleaned_data['generic_resource_bundle']
77             irint(str(data['user']))
78             data = data[2:-2]
79             if not data:
80                 self.metastep.set_invalid("Please select a valid bundle")
81                 return render(request, self.template, context)
82             selected_bundle = json.loads(data)
83             selected_id = selected_bundle[0]['id']
84             gresource_bundle = None
85             try:
86                 selected_id = int(selected_id)
87                 gresource_bundle = GenericResourceBundle.objects.get(id=selected_id)
88             except ValueError:
89                 # we want the bundle in the repo
90                 gresource_bundle = self.repo_get(self.repo.GRESOURCE_BUNDLE_MODELS,{}).get("bundle", GenericResourceBundle())
91             self.repo_put(
92                     self.repo_key,
93                     gresource_bundle
94                     )
95             confirm = self.repo_get(self.repo.CONFIRMATION)
96             if self.confirm_key not in confirm:
97                 confirm[self.confirm_key] = {}
98             confirm[self.confirm_key]["resource name"] = gresource_bundle.name
99             self.repo_put(self.repo.CONFIRMATION, confirm)
100             messages.add_message(request, messages.SUCCESS, 'Form Validated Successfully', fail_silently=True)
101             self.metastep.set_valid("Step Completed")
102             return render(request, self.template, context)
103         else:
104             messages.add_message(request, messages.ERROR, "Form Didn't Validate", fail_silently=True)
105             self.metastep.set_invalid("Please complete the fields highlighted in red to continue")
106             return render(request, self.template, context)
107
108 class Booking_Resource_Select(Resource_Select):
109
110     def __init__(self, *args, **kwargs):
111         super(Booking_Resource_Select, self).__init__(*args, **kwargs)
112         self.repo_key = self.repo.BOOKING_SELECTED_GRB
113         self.confirm_key = "booking"
114
115     def get_default_entry(self):
116         default = self.repo_get(self.repo.GRESOURCE_BUNDLE_MODELS, {}).get("bundle")
117         mine = self.repo_get(self.repo_key)
118         if mine:
119             return None
120         try:
121             config_bundle = self.repo_get(self.repo.BOOKING_MODELS)['booking'].config_bundle
122             if default:
123                 return default # select created grb, even if preselected config bundle
124             return config_bundle.bundle
125         except:
126             pass
127         return default
128
129     def get_context(self):
130         context = super(Booking_Resource_Select, self).get_context()
131         return context
132
133     def post_render(self, request):
134         response = super(Booking_Resource_Select, self).post_render(request)
135         models = self.repo_get(self.repo.BOOKING_MODELS, {})
136         if "booking" not in models:
137             models['booking'] = Booking()
138         booking = models['booking']
139         resource = self.repo_get(self.repo_key, False)
140         if resource:
141             try:
142                 booking.resource.template = resource
143             except:
144                 booking.resource = ResourceBundle(template=resource)
145         models['booking'] = booking
146         self.repo_put(self.repo.BOOKING_MODELS, models)
147         return response
148
149 class SWConfig_Select(WorkflowStep):
150     template = 'booking/steps/swconfig_select.html'
151     title = "Select Software Configuration"
152     description = "Choose the software and related configurations you want to have used for your deployment"
153     short_title = "pod config"
154
155     def post_render(self, request):
156         form = SWConfigSelectorForm(request.POST)
157         if form.is_valid():
158
159             bundle_json = form.cleaned_data['software_bundle']
160             bundle_json = bundle_json[2:-2]  # Stupid django string bug
161             if not bundle_json:
162                 self.metastep.set_invalid("Please select a valid config")
163                 return self.render(request)
164             bundle_json = json.loads(bundle_json)
165             bundle = None
166             try:
167                 id = int(bundle_json[0]['id'])
168                 bundle = ConfigBundle.objects.get(id=id)
169             except ValueError:
170                 bundle = self.repo_get(self.repo.CONFIG_MODELS).get("bundle")
171
172             models = self.repo_get(self.repo.BOOKING_MODELS, {})
173             if "booking" not in models:
174                 models['booking'] = Booking()
175             models['booking'].config_bundle = bundle
176             self.repo_put(self.repo.BOOKING_MODELS, models)
177             confirm = self.repo_get(self.repo.CONFIRMATION)
178             if "booking" not in confirm:
179                 confirm['booking'] = {}
180             confirm['booking']["configuration name"] = bundle.name
181             self.repo_put(self.repo.CONFIRMATION, confirm)
182             self.metastep.set_valid("Step Completed")
183             messages.add_message(request, messages.SUCCESS, 'Form Validated Successfully', fail_silently=True)
184         else:
185             self.metastep.set_invalid("Please select or create a valid config")
186             messages.add_message(request, messages.ERROR, "Form Didn't Validate", fail_silently=True)
187
188         return self.render(request)
189
190
191     def get_context(self):
192         context = super(SWConfig_Select, self).get_context()
193         default = []
194         bundle = None
195         chosen_bundle = None
196         created_bundle = self.repo_get(self.repo.CONFIG_MODELS, {}).get("bundle", False)
197         booking = self.repo_get(self.repo.BOOKING_MODELS, {}).get("booking", False)
198         try:
199             chosen_bundle = booking.config_bundle
200             default.append(chosen_bundle.id)
201             bundle=chosen_bundle
202         except:
203             if created_bundle:
204                 default.append("repo bundle")
205                 bundle = created_bundle
206                 context['disabled'] = True
207         edit = self.repo_get(self.repo.EDIT, False)
208         grb = self.repo_get(self.repo.BOOKING_SELECTED_GRB)
209         context['form'] = SWConfigSelectorForm(chosen_software=default, bundle=bundle, edit=edit, resource=grb)
210         return context
211
212 class Booking_Meta(WorkflowStep):
213     template = 'booking/steps/booking_meta.html'
214     title = "Extra Info"
215     description = "Tell us how long you want your booking, what it is for, and who else should have access to it"
216     short_title = "booking info"
217
218     def get_context(self):
219         context = super(Booking_Meta, self).get_context()
220         initial = {}
221         default = []
222         try:
223             models = self.repo_get(self.repo.BOOKING_MODELS, {})
224             booking = models.get("booking")
225             if booking:
226                 initial['purpose'] = booking.purpose
227                 initial['project'] = booking.project
228                 initial['length'] = (booking.end - booking.start).days
229             info = self.repo_get(self.repo.BOOKING_INFO_FILE, False)
230             if info:
231                 initial['info_file'] = info
232             users = models.get("collaborators", [])
233             for user in users:
234                 default.append(user.id)
235         except Exception as e:
236             pass
237
238         default_user = self.repo_get(self.repo.SESSION_USER)
239         if default_user is None:
240             # TODO: error
241             default_user = "you"
242         else:
243             default_user = default_user.username
244
245         context['form'] = BookingMetaForm(initial=initial, chosen_users=default, default_user=default_user)
246         return context
247
248     def post_render(self, request):
249         form = BookingMetaForm(data=request.POST)
250         context = self.get_context()
251
252         forms = self.repo_get(self.repo.BOOKING_FORMS, {})
253
254         forms["meta_form"] = form
255         self.repo_put(self.repo.BOOKING_FORMS, forms)
256
257         if form.is_valid():
258             models = self.repo_get(self.repo.BOOKING_MODELS, {})
259             if "booking" not in models:
260                 models['booking'] = Booking()
261             models['collaborators'] = []
262             confirm = self.repo_get(self.repo.CONFIRMATION)
263             if "booking" not in confirm:
264                 confirm['booking'] = {}
265
266             models['booking'].start = timezone.now()
267             models['booking'].end = timezone.now() + timedelta(days=int(form.cleaned_data['length']))
268             models['booking'].purpose = form.cleaned_data['purpose']
269             models['booking'].project = form.cleaned_data['project']
270             for key in ['length', 'project', 'purpose']:
271                 confirm['booking'][key] = form.cleaned_data[key]
272
273             user_data = form.cleaned_data['users']
274             confirm['booking']['collaborators'] = []
275             user_data = user_data[2:-2] #fixes malformed string from querydict
276             if user_data:
277                 form_users = json.loads(user_data)
278                 for user_json in form_users:
279                     user = User.objects.get(pk=user_json['id'])
280                     models['collaborators'].append(user)
281                     confirm['booking']['collaborators'].append(user.username)
282
283             info_file = form.cleaned_data.get("info_file", False)
284             if info_file:
285                 self.repo_put(self.repo.BOOKING_INFO_FILE, info_file)
286
287             self.repo_put(self.repo.BOOKING_MODELS, models)
288             self.repo_put(self.repo.CONFIRMATION, confirm)
289             messages.add_message(request, messages.SUCCESS, 'Form Validated', fail_silently=True)
290             self.metastep.set_valid("Step Completed")
291         else:
292             messages.add_message(request, messages.ERROR, "Form didn't validate", fail_silently=True)
293             self.metastep.set_invalid("Please complete the fields highlighted in red to continue")
294             context['form'] = form  # TODO: store this form
295         return render(request, self.template, context)