1 ##############################################################################
2 # Copyright (c) 2018 Sawyer Bergeron, Parker Berberian, and others.
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 ##############################################################################
11 from django.shortcuts import render
12 from django.contrib import messages
17 from workflow.forms import ConfirmationForm
18 from api.models import JobFactory
19 from dashboard.exceptions import ResourceAvailabilityException, ModelValidationException
20 from resource_inventory.models import Image, GenericInterface
21 from resource_inventory.resource_manager import ResourceManager
22 from notifier.manager import NotificationHandler
23 from booking.models import Booking
26 class BookingAuthManager():
27 LFN_PROJECTS = ["opnfv"] # TODO
29 def parse_url(self, info_url):
31 will return the PTL in the INFO file on success, or None
34 parts = info_url.split("/")
35 if parts[0].find("http") > -1: # the url include http(s)://
37 if parts[-1] != "INFO.yaml":
39 if parts[0] not in ["github.com", "raw.githubusercontent.com"]:
41 if parts[1] not in self.LFN_PROJECTS:
43 # now to download and parse file
44 if parts[3] == "blob":
46 url = "https://" + "/".join(parts)
47 info_file = requests.get(url, timeout=15).text
48 info_parsed = yaml.load(info_file)
49 ptl = info_parsed.get('project_lead')
57 def booking_allowed(self, booking, repo):
59 This is the method that will have to change whenever the booking policy changes in the Infra
60 group / LFN. This is a nice isolation of that administration crap
61 currently checks if the booking uses multiple servers. if it does, then the owner must be a PTL,
62 which is checked using the provided info file
64 if len(booking.resource.template.getHosts()) < 2:
65 return True # if they only have one server, we dont care
66 if booking.owner.userprofile.booking_privledge:
67 return True # admin override for this user
68 if repo.BOOKING_INFO_FILE not in repo.el:
69 return False # INFO file not provided
70 ptl_info = self.parse_url(repo.BOOKING_INFO_FILE)
71 return ptl_info and ptl_info == booking.owner.userprofile.email_addr
74 class WorkflowStep(object):
76 template = 'bad_request.html'
77 title = "Generic Step"
78 description = "You were led here by mistake"
82 def __init__(self, id, repo=None):
86 def get_context(self):
88 context['step_number'] = self.repo_get('steps')
89 context['active_step'] = self.repo_get('active_step')
90 context['render_correct'] = "true"
91 context['step_title'] = self.title
92 context['description'] = self.description
95 def render(self, request):
96 self.context = self.get_context()
97 return render(request, self.template, self.context)
99 def post_render(self, request):
100 return self.render(request)
102 def test_render(self, request):
103 if request.method == "POST":
104 return self.post_render(request)
105 return self.render(request)
107 def validate(self, request):
110 def repo_get(self, key, default=None):
111 return self.repo.get(key, default, self.id)
113 def repo_put(self, key, value):
114 return self.repo.put(key, value, self.id)
117 class Confirmation_Step(WorkflowStep):
118 template = 'workflow/confirm.html'
119 title = "Confirm Changes"
120 description = "Does this all look right?"
122 def get_vlan_warning(self):
123 grb = self.repo_get(self.repo.BOOKING_SELECTED_GRB, False)
126 vlan_manager = grb.lab.vlan_manager
127 if vlan_manager is None:
129 hosts = grb.getHosts()
131 for interface in host.generic_interfaces.all():
132 for vlan in interface.vlans.all():
134 if not vlan_manager.public_vlan_is_available(vlan.vlan_id):
137 if not vlan_manager.is_available(vlan.vlan_id):
138 return 1 # There is a problem with these vlans
141 def get_context(self):
142 context = super(Confirmation_Step, self).get_context()
143 context['form'] = ConfirmationForm()
144 context['confirmation_info'] = yaml.dump(
145 self.repo_get(self.repo.CONFIRMATION),
146 default_flow_style=False
148 context['vlan_warning'] = self.get_vlan_warning()
152 def flush_to_db(self):
153 errors = self.repo.make_models()
157 def post_render(self, request):
158 form = ConfirmationForm(request.POST)
160 data = form.cleaned_data['confirm']
161 context = self.get_context()
163 context["bypassed"] = "true"
164 errors = self.flush_to_db()
166 messages.add_message(request, messages.ERROR, "ERROR OCCURRED: " + errors)
167 return render(request, self.template, context)
168 messages.add_message(request, messages.SUCCESS, "Confirmed")
169 return render(request, self.template, context)
170 elif data == "False":
171 context["bypassed"] = "true"
172 messages.add_message(request, messages.SUCCESS, "Canceled")
173 return render(request, self.template, context)
178 if "vlan_input" in request.POST:
179 if request.POST.get("vlan_input") == "True":
180 self.translate_vlans()
181 return self.render(request)
184 def translate_vlans(self):
185 grb = self.repo_get(self.repo.BOOKING_SELECTED_GRB, False)
188 vlan_manager = grb.lab.vlan_manager
189 if vlan_manager is None:
191 hosts = grb.getHosts()
193 for interface in host.generic_interfaces.all():
194 for vlan in interface.vlans.all():
196 if not vlan_manager.is_available(vlan.vlan_id):
197 vlan.vlan_id = vlan_manager.get_vlan()
200 if not vlan_manager.public_vlan_is_available(vlan.vlan_id):
201 pub_vlan = vlan_manager.get_public_vlan()
202 vlan.vlan_id = pub_vlan.vlan
216 RESOURCE_SELECT = "resource_select"
217 CONFIRMATION = "confirmation"
218 SELECTED_GRESOURCE_BUNDLE = "selected generic bundle pk"
219 GRESOURCE_BUNDLE_MODELS = "generic_resource_bundle_models"
220 GRESOURCE_BUNDLE_INFO = "generic_resource_bundle_info"
223 GRB_LAST_HOSTLIST = "grb_network_previous_hostlist"
224 BOOKING_FORMS = "booking_forms"
225 SWCONF_HOSTS = "swconf_hosts"
226 SWCONF_SELECTED_GRB = "swconf_selected_grb_pk"
227 BOOKING_SELECTED_GRB = "booking_selected_grb_pk"
228 BOOKING_MODELS = "booking models"
229 CONFIG_MODELS = "configuration bundle models"
230 SESSION_USER = "session owner user account"
231 VALIDATED_MODEL_GRB = "valid grb config model instance in db"
232 VALIDATED_MODEL_CONFIG = "valid config model instance in db"
233 VALIDATED_MODEL_BOOKING = "valid booking model instance in db"
234 VLANS = "a list of vlans"
235 SNAPSHOT_MODELS = "the models for snapshotting"
236 SNAPSHOT_BOOKING_ID = "the booking id for snapshotting"
237 SNAPSHOT_NAME = "the name of the snapshot"
238 SNAPSHOT_DESC = "description of the snapshot"
239 BOOKING_INFO_FILE = "the INFO.yaml file for this user's booking"
241 def get(self, key, default, id):
242 self.add_get_history(key, id)
243 return self.el.get(key, default)
245 def put(self, key, val, id):
246 self.add_put_history(key, id)
249 def add_get_history(self, key, id):
250 self.add_history(key, id, self.get_history)
252 def add_put_history(self, key, id):
253 self.add_history(key, id, self.put_history)
255 def add_history(self, key, id, history):
256 if key not in history:
259 history[key].append(id)
261 def make_models(self):
262 if self.SNAPSHOT_MODELS in self.el:
263 errors = self.make_snapshot()
266 # if GRB WF, create it
267 if self.GRESOURCE_BUNDLE_MODELS in self.el:
268 errors = self.make_generic_resource_bundle()
272 if self.CONFIG_MODELS in self.el:
273 errors = self.make_software_config_bundle()
277 if self.BOOKING_MODELS in self.el:
278 errors = self.make_booking()
281 # create notification
282 booking = self.el[self.BOOKING_MODELS]['booking']
283 NotificationHandler.notify_new_booking(booking)
285 def make_snapshot(self):
286 owner = self.el[self.SESSION_USER]
287 models = self.el[self.SNAPSHOT_MODELS]
288 image = models.get('snapshot', Image())
289 booking_id = self.el.get(self.SNAPSHOT_BOOKING_ID)
291 return "SNAP, No booking ID provided"
292 booking = Booking.objects.get(pk=booking_id)
293 name = self.el.get(self.SNAPSHOT_NAME)
295 return "SNAP, no name provided"
296 host = models.get('host')
298 return "SNAP, no host provided"
299 description = self.el.get(self.SNAPSHOT_DESC, "")
300 image.from_lab = booking.lab
302 image.description = description
306 image.host_type = host.profile
309 def make_generic_resource_bundle(self):
310 owner = self.el[self.SESSION_USER]
311 if self.GRESOURCE_BUNDLE_MODELS in self.el:
312 models = self.el[self.GRESOURCE_BUNDLE_MODELS]
313 if 'hosts' in models:
314 hosts = models['hosts']
316 return "GRB has no hosts. CODE:0x0002"
317 if 'bundle' in models:
318 bundle = models['bundle']
320 return "GRB, no bundle in models. CODE:0x0003"
325 except Exception as e:
326 return "GRB, saving bundle generated exception: " + str(e) + " CODE:0x0004"
329 genericresource = host.resource
330 genericresource.bundle = bundle
331 genericresource.save()
332 host.resource = genericresource
334 except Exception as e:
335 return "GRB, saving hosts generated exception: " + str(e) + " CODE:0x0005"
337 if 'interfaces' in models:
338 for interface_set in models['interfaces'].values():
339 for interface in interface_set:
341 interface.host = interface.host
343 except Exception as e:
344 return "GRB, saving interface " + str(interface) + " failed. CODE:0x0019"
346 return "GRB, no interface set provided. CODE:0x001a"
348 if 'vlans' in models:
349 for resource_name, mapping in models['vlans'].items():
350 for profile_name, vlan_set in mapping.items():
351 interface = GenericInterface.objects.get(
352 profile__name=profile_name,
353 host__resource__name=resource_name,
354 host__resource__bundle=models['bundle']
356 for vlan in vlan_set:
359 interface.vlans.add(vlan)
360 except Exception as e:
361 return "GRB, saving vlan " + str(vlan) + " failed. Exception: " + str(e) + ". CODE:0x0017"
363 return "GRB, no vlan set provided. CODE:0x0018"
366 return "GRB no models given. CODE:0x0001"
368 self.el[self.VALIDATED_MODEL_GRB] = bundle
371 def make_software_config_bundle(self):
372 models = self.el[self.CONFIG_MODELS]
373 if 'bundle' in models:
374 bundle = models['bundle']
375 bundle.bundle = bundle.bundle
378 except Exception as e:
379 return "SWC, saving bundle generated exception: " + str(e) + "CODE:0x0007"
382 return "SWC, no bundle in models. CODE:0x0006"
383 if 'host_configs' in models:
384 host_configs = models['host_configs']
385 for host_config in host_configs:
386 host_config.bundle = host_config.bundle
387 host_config.host = host_config.host
390 except Exception as e:
391 return "SWC, saving host configs generated exception: " + str(e) + "CODE:0x0009"
393 return "SWC, no host configs in models. CODE:0x0008"
394 if 'opnfv' in models:
395 opnfvconfig = models['opnfv']
396 opnfvconfig.bundle = opnfvconfig.bundle
397 if opnfvconfig.scenario not in opnfvconfig.installer.sup_scenarios.all():
398 return "SWC, scenario not supported by installer. CODE:0x000d"
401 except Exception as e:
402 return "SWC, saving opnfv config generated exception: " + str(e) + "CODE:0x000b"
406 self.el[self.VALIDATED_MODEL_CONFIG] = bundle
409 def make_booking(self):
410 models = self.el[self.BOOKING_MODELS]
411 owner = self.el[self.SESSION_USER]
413 if self.BOOKING_SELECTED_GRB in self.el:
414 selected_grb = self.el[self.BOOKING_SELECTED_GRB]
416 return "BOOK, no selected resource. CODE:0x000e"
418 if not self.reserve_vlans(selected_grb):
419 return "BOOK, vlans not available"
421 if 'booking' in models:
422 booking = models['booking']
424 return "BOOK, no booking model exists. CODE:0x000f"
426 if not booking.start:
427 return "BOOK, booking has no start. CODE:0x0010"
429 return "BOOK, booking has no end. CODE:0x0011"
430 if booking.end <= booking.start:
431 return "BOOK, end before/same time as start. CODE:0x0012"
433 if 'collaborators' in models:
434 collaborators = models['collaborators']
436 return "BOOK, collaborators not defined. CODE:0x0013"
438 resource_bundle = ResourceManager.getInstance().convertResourceBundle(selected_grb, config=booking.config_bundle)
439 except ResourceAvailabilityException as e:
440 return "BOOK, requested resources are not available. Exception: " + str(e) + " CODE:0x0014"
441 except ModelValidationException as e:
442 return "Error encountered when saving bundle. " + str(e) + " CODE: 0x001b"
444 booking.resource = resource_bundle
445 booking.owner = owner
446 booking.config_bundle = booking.config_bundle
447 booking.lab = selected_grb.lab
449 is_allowed = BookingAuthManager().booking_allowed(booking, self)
451 return "BOOK, you are not allowed to book the requested resources"
455 except Exception as e:
456 return "BOOK, saving booking generated exception: " + str(e) + " CODE:0x0015"
458 for collaborator in collaborators:
459 booking.collaborators.add(collaborator)
462 JobFactory.makeCompleteJob(booking)
463 except Exception as e:
464 return "BOOK, serializing for api generated exception: " + str(e) + " CODE:0xFFFF"
468 except Exception as e:
469 return "BOOK, saving booking generated exception: " + str(e) + " CODE:0x0016"
471 def reserve_vlans(self, grb):
477 vlan_manager = grb.lab.vlan_manager
478 if vlan_manager is None:
480 for host in grb.getHosts():
481 for interface in host.generic_interfaces.all():
482 for vlan in interface.vlans.all():
486 vlans.append(vlan.vlan_id)
489 vlan_manager.reserve_vlans(vlans)
490 vlan_manager.reserve_public_vlan(public_vlan.vlan_id)
497 self.el[self.CONFIRMATION] = {}
498 self.get_history = {}
499 self.put_history = {}