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
13 from django.http import HttpResponse
18 from workflow.forms import ConfirmationForm
19 from api.models import JobFactory
20 from dashboard.exceptions import ResourceAvailabilityException, ModelValidationException
21 from resource_inventory.models import Image, GenericInterface
22 from resource_inventory.resource_manager import ResourceManager
23 from notifier.manager import NotificationHandler
24 from booking.models import Booking
27 class BookingAuthManager():
28 LFN_PROJECTS = ["opnfv"] # TODO
30 def parse_url(self, info_url):
32 will return the PTL in the INFO file on success, or None
35 parts = info_url.split("/")
36 if parts[0].find("http") > -1: # the url include http(s)://
38 if parts[-1] != "INFO.yaml":
40 if parts[0] not in ["github.com", "raw.githubusercontent.com"]:
42 if parts[1] not in self.LFN_PROJECTS:
44 # now to download and parse file
45 if parts[3] == "blob":
47 url = "https://" + "/".join(parts)
48 info_file = requests.get(url, timeout=15).text
49 info_parsed = yaml.load(info_file)
50 ptl = info_parsed.get('project_lead')
58 def booking_allowed(self, booking, repo):
60 This is the method that will have to change whenever the booking policy changes in the Infra
61 group / LFN. This is a nice isolation of that administration crap
62 currently checks if the booking uses multiple servers. if it does, then the owner must be a PTL,
63 which is checked using the provided info file
65 if len(booking.resource.template.getHosts()) < 2:
66 return True # if they only have one server, we dont care
67 if booking.owner.userprofile.booking_privledge:
68 return True # admin override for this user
69 if repo.BOOKING_INFO_FILE not in repo.el:
70 return False # INFO file not provided
71 ptl_info = self.parse_url(repo.BOOKING_INFO_FILE)
72 return ptl_info and ptl_info == booking.owner.userprofile.email_addr
75 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.SELECTED_GRESOURCE_BUNDLE, False)
126 if self.repo.BOOKING_MODELS not in self.repo.el:
128 vlan_manager = grb.lab.vlan_manager
129 if vlan_manager is None:
131 hosts = grb.getHosts()
133 for interface in host.generic_interfaces.all():
134 for vlan in interface.vlans.all():
136 if not vlan_manager.public_vlan_is_available(vlan.vlan_id):
139 if not vlan_manager.is_available(vlan.vlan_id):
140 return 1 # There is a problem with these vlans
143 def get_context(self):
144 context = super(Confirmation_Step, self).get_context()
145 context['form'] = ConfirmationForm()
146 context['confirmation_info'] = yaml.dump(
147 self.repo_get(self.repo.CONFIRMATION),
148 default_flow_style=False
150 context['vlan_warning'] = self.get_vlan_warning()
154 def flush_to_db(self):
155 errors = self.repo.make_models()
159 def post_render(self, request):
160 form = ConfirmationForm(request.POST)
162 data = form.cleaned_data['confirm']
163 context = self.get_context()
165 context["bypassed"] = "true"
166 errors = self.flush_to_db()
168 messages.add_message(request, messages.ERROR, "ERROR OCCURRED: " + errors)
170 messages.add_message(request, messages.SUCCESS, "Confirmed")
172 return HttpResponse('')
173 elif data == "False":
174 context["bypassed"] = "true"
175 messages.add_message(request, messages.SUCCESS, "Canceled")
176 return render(request, self.template, context)
181 if "vlan_input" in request.POST:
182 if request.POST.get("vlan_input") == "True":
183 self.translate_vlans()
184 return self.render(request)
187 def translate_vlans(self):
188 grb = self.repo_get(self.repo.SELECTED_GRESOURCE_BUNDLE, False)
191 vlan_manager = grb.lab.vlan_manager
192 if vlan_manager is None:
194 hosts = grb.getHosts()
196 for interface in host.generic_interfaces.all():
197 for vlan in interface.vlans.all():
199 if not vlan_manager.is_available(vlan.vlan_id):
200 vlan.vlan_id = vlan_manager.get_vlan()
203 if not vlan_manager.public_vlan_is_available(vlan.vlan_id):
204 pub_vlan = vlan_manager.get_public_vlan()
205 vlan.vlan_id = pub_vlan.vlan
219 RESOURCE_SELECT = "resource_select"
220 CONFIRMATION = "confirmation"
221 SELECTED_GRESOURCE_BUNDLE = "selected generic bundle pk"
222 SELECTED_CONFIG_BUNDLE = "selected config bundle pk"
223 GRESOURCE_BUNDLE_MODELS = "generic_resource_bundle_models"
224 GRESOURCE_BUNDLE_INFO = "generic_resource_bundle_info"
227 GRB_LAST_HOSTLIST = "grb_network_previous_hostlist"
228 BOOKING_FORMS = "booking_forms"
229 SWCONF_HOSTS = "swconf_hosts"
230 BOOKING_MODELS = "booking models"
231 CONFIG_MODELS = "configuration bundle models"
232 SESSION_USER = "session owner user account"
233 VALIDATED_MODEL_GRB = "valid grb config model instance in db"
234 VALIDATED_MODEL_CONFIG = "valid config model instance in db"
235 VALIDATED_MODEL_BOOKING = "valid booking model instance in db"
236 VLANS = "a list of vlans"
237 SNAPSHOT_MODELS = "the models for snapshotting"
238 SNAPSHOT_BOOKING_ID = "the booking id for snapshotting"
239 SNAPSHOT_NAME = "the name of the snapshot"
240 SNAPSHOT_DESC = "description of the snapshot"
241 BOOKING_INFO_FILE = "the INFO.yaml file for this user's booking"
243 #migratory elements of segmented workflow
244 #each of these is the end result of a different workflow.
245 HAS_RESULT = "whether or not workflow has a result"
246 RESULT_KEY = "key for target index that result will be put into in parent"
247 RESULT = "result object from workflow"
249 def get_child_defaults(self):
251 for key in [self.SELECTED_GRESOURCE_BUNDLE, self.SESSION_USER]:
252 return_tuples.append((key, self.el.get(key)))
255 def set_defaults(self, defaults):
256 for key, value in defaults:
259 def get(self, key, default, id):
260 self.add_get_history(key, id)
261 return self.el.get(key, default)
263 def put(self, key, val, id):
264 self.add_put_history(key, id)
267 def add_get_history(self, key, id):
268 self.add_history(key, id, self.get_history)
270 def add_put_history(self, key, id):
271 self.add_history(key, id, self.put_history)
273 def add_history(self, key, id, history):
274 if key not in history:
277 history[key].append(id)
279 def make_models(self):
280 if self.SNAPSHOT_MODELS in self.el:
281 errors = self.make_snapshot()
284 # if GRB WF, create it
285 if self.GRESOURCE_BUNDLE_MODELS in self.el:
286 errors = self.make_generic_resource_bundle()
290 self.el[self.HAS_RESULT] = True
291 self.el[self.RESULT_KEY] = self.SELECTED_GRESOURCE_BUNDLE
294 if self.CONFIG_MODELS in self.el:
295 errors = self.make_software_config_bundle()
299 self.el[self.HAS_RESULT] = True
300 self.el[self.RESULT_KEY] = self.SELECTED_CONFIG_BUNDLE
303 if self.BOOKING_MODELS in self.el:
304 errors = self.make_booking()
307 # create notification
308 booking = self.el[self.BOOKING_MODELS]['booking']
309 NotificationHandler.notify_new_booking(booking)
311 def make_snapshot(self):
312 owner = self.el[self.SESSION_USER]
313 models = self.el[self.SNAPSHOT_MODELS]
314 image = models.get('snapshot', Image())
315 booking_id = self.el.get(self.SNAPSHOT_BOOKING_ID)
317 return "SNAP, No booking ID provided"
318 booking = Booking.objects.get(pk=booking_id)
319 name = self.el.get(self.SNAPSHOT_NAME)
321 return "SNAP, no name provided"
322 host = models.get('host')
324 return "SNAP, no host provided"
325 description = self.el.get(self.SNAPSHOT_DESC, "")
326 image.from_lab = booking.lab
328 image.description = description
332 image.host_type = host.profile
335 def make_generic_resource_bundle(self):
336 owner = self.el[self.SESSION_USER]
337 if self.GRESOURCE_BUNDLE_MODELS in self.el:
338 models = self.el[self.GRESOURCE_BUNDLE_MODELS]
339 if 'hosts' in models:
340 hosts = models['hosts']
342 return "GRB has no hosts. CODE:0x0002"
343 if 'bundle' in models:
344 bundle = models['bundle']
346 return "GRB, no bundle in models. CODE:0x0003"
351 except Exception as e:
352 return "GRB, saving bundle generated exception: " + str(e) + " CODE:0x0004"
355 genericresource = host.resource
356 genericresource.bundle = bundle
357 genericresource.save()
358 host.resource = genericresource
360 except Exception as e:
361 return "GRB, saving hosts generated exception: " + str(e) + " CODE:0x0005"
363 if 'interfaces' in models:
364 for interface_set in models['interfaces'].values():
365 for interface in interface_set:
367 interface.host = interface.host
369 except Exception as e:
370 return "GRB, saving interface " + str(interface) + " failed. CODE:0x0019"
372 return "GRB, no interface set provided. CODE:0x001a"
374 if 'vlans' in models:
375 for resource_name, mapping in models['vlans'].items():
376 for profile_name, vlan_set in mapping.items():
377 interface = GenericInterface.objects.get(
378 profile__name=profile_name,
379 host__resource__name=resource_name,
380 host__resource__bundle=models['bundle']
382 for vlan in vlan_set:
385 interface.vlans.add(vlan)
386 except Exception as e:
387 return "GRB, saving vlan " + str(vlan) + " failed. Exception: " + str(e) + ". CODE:0x0017"
389 return "GRB, no vlan set provided. CODE:0x0018"
392 return "GRB no models given. CODE:0x0001"
394 self.el[self.RESULT] = bundle
397 def make_software_config_bundle(self):
398 models = self.el[self.CONFIG_MODELS]
399 if 'bundle' in models:
400 bundle = models['bundle']
401 bundle.bundle = bundle.bundle
404 except Exception as e:
405 return "SWC, saving bundle generated exception: " + str(e) + "CODE:0x0007"
408 return "SWC, no bundle in models. CODE:0x0006"
409 if 'host_configs' in models:
410 host_configs = models['host_configs']
411 for host_config in host_configs:
412 host_config.bundle = host_config.bundle
413 host_config.host = host_config.host
416 except Exception as e:
417 return "SWC, saving host configs generated exception: " + str(e) + "CODE:0x0009"
419 return "SWC, no host configs in models. CODE:0x0008"
420 if 'opnfv' in models:
421 opnfvconfig = models['opnfv']
422 opnfvconfig.bundle = opnfvconfig.bundle
423 if opnfvconfig.scenario not in opnfvconfig.installer.sup_scenarios.all():
424 return "SWC, scenario not supported by installer. CODE:0x000d"
427 except Exception as e:
428 return "SWC, saving opnfv config generated exception: " + str(e) + "CODE:0x000b"
432 self.el[self.RESULT] = bundle
435 def make_booking(self):
436 models = self.el[self.BOOKING_MODELS]
437 owner = self.el[self.SESSION_USER]
439 if self.SELECTED_GRESOURCE_BUNDLE in self.el:
440 selected_grb = self.el[self.SELECTED_GRESOURCE_BUNDLE]
442 return "BOOK, no selected resource. CODE:0x000e"
444 if not self.reserve_vlans(selected_grb):
445 return "BOOK, vlans not available"
447 if 'booking' in models:
448 booking = models['booking']
450 return "BOOK, no booking model exists. CODE:0x000f"
452 if not booking.start:
453 return "BOOK, booking has no start. CODE:0x0010"
455 return "BOOK, booking has no end. CODE:0x0011"
456 if booking.end <= booking.start:
457 return "BOOK, end before/same time as start. CODE:0x0012"
459 if 'collaborators' in models:
460 collaborators = models['collaborators']
462 return "BOOK, collaborators not defined. CODE:0x0013"
464 resource_bundle = ResourceManager.getInstance().convertResourceBundle(selected_grb, config=booking.config_bundle)
465 except ResourceAvailabilityException as e:
466 return "BOOK, requested resources are not available. Exception: " + str(e) + " CODE:0x0014"
467 except ModelValidationException as e:
468 return "Error encountered when saving bundle. " + str(e) + " CODE: 0x001b"
470 booking.resource = resource_bundle
471 booking.owner = owner
472 booking.config_bundle = booking.config_bundle
473 booking.lab = selected_grb.lab
475 is_allowed = BookingAuthManager().booking_allowed(booking, self)
477 return "BOOK, you are not allowed to book the requested resources"
481 except Exception as e:
482 return "BOOK, saving booking generated exception: " + str(e) + " CODE:0x0015"
484 for collaborator in collaborators:
485 booking.collaborators.add(collaborator)
488 JobFactory.makeCompleteJob(booking)
489 except Exception as e:
490 return "BOOK, serializing for api generated exception: " + str(e) + " CODE:0xFFFF"
494 except Exception as e:
495 return "BOOK, saving booking generated exception: " + str(e) + " CODE:0x0016"
497 def reserve_vlans(self, grb):
503 vlan_manager = grb.lab.vlan_manager
504 if vlan_manager is None:
506 for host in grb.getHosts():
507 for interface in host.generic_interfaces.all():
508 for vlan in interface.vlans.all():
512 vlans.append(vlan.vlan_id)
515 vlan_manager.reserve_vlans(vlans)
516 vlan_manager.reserve_public_vlan(public_vlan.vlan_id)
523 self.el[self.CONFIRMATION] = {}
524 self.el["active_step"] = 0
525 self.get_history = {}
526 self.put_history = {}