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.contrib.auth.models import User
12 from django.db import models
13 from django.shortcuts import render
14 from django.contrib import messages
19 from workflow.forms import ConfirmationForm
20 from api.models import *
21 from dashboard.exceptions import *
22 from resource_inventory.models import *
23 from resource_inventory.resource_manager import ResourceManager
24 from notifier.manager import NotificationHandler
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')
55 except Exception as e:
59 def booking_allowed(self, booking, repo):
61 This is the method that will have to change whenever the booking policy changes in the Infra
62 group / LFN. This is a nice isolation of that administration crap
63 currently checks if the booking uses multiple servers. if it does, then the owner must be a PTL,
64 which is checked using the provided info file
66 if len(booking.resource.template.getHosts()) < 2:
67 return True #if they only have one server, we dont care
68 if booking.owner.userprofile.booking_privledge:
69 return True # admin override for this user
70 if repo.BOOKING_INFO_FILE not in repo.el:
71 return False # INFO file not provided
72 ptl_info = self.parse_url(repo.BOOKING_INFO_FILE)
73 return ptl_info and ptl_info == booking.owner.userprofile.email_addr
77 class WorkflowStep(object):
79 template = 'bad_request.html'
80 title = "Generic Step"
81 description = "You were led here by mistake"
85 def __init__(self, id, repo=None):
89 def get_context(self):
91 context['step_number'] = self.repo_get('steps')
92 context['active_step'] = self.repo_get('active_step')
93 context['render_correct'] = "true"
94 context['step_title'] = self.title
95 context['description'] = self.description
98 def render(self, request):
99 self.context = self.get_context()
100 return render(request, self.template, self.context)
102 def post_render(self, request):
103 return self.render(request)
105 def test_render(self, request):
106 if request.method == "POST":
107 return self.post_render(request)
108 return self.render(request)
110 def validate(self, request):
113 def repo_get(self, key, default=None):
114 return self.repo.get(key, default, self.id)
116 def repo_put(self, key, value):
117 return self.repo.put(key, value, self.id)
119 class Confirmation_Step(WorkflowStep):
120 template = 'workflow/confirm.html'
121 title = "Confirm Changes"
122 description = "Does this all look right?"
124 def get_vlan_warning(self):
125 grb = self.repo_get(self.repo.BOOKING_SELECTED_GRB, False)
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
144 def get_context(self):
145 context = super(Confirmation_Step, self).get_context()
146 context['form'] = ConfirmationForm()
147 context['confirmation_info'] = yaml.dump(
148 self.repo_get(self.repo.CONFIRMATION),
149 default_flow_style=False
151 context['vlan_warning'] = self.get_vlan_warning()
155 def flush_to_db(self):
156 errors = self.repo.make_models()
160 def post_render(self, request):
161 form = ConfirmationForm(request.POST)
163 data = form.cleaned_data['confirm']
164 context = self.get_context()
166 context["bypassed"] = "true"
167 errors = self.flush_to_db()
169 messages.add_message(request, messages.ERROR, "ERROR OCCURRED: " + errors)
170 return render(request, self.template, context)
171 messages.add_message(request, messages.SUCCESS, "Confirmed")
172 return render(request, self.template, context)
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.BOOKING_SELECTED_GRB, 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
218 RESOURCE_SELECT = "resource_select"
219 CONFIRMATION = "confirmation"
220 SELECTED_GRESOURCE_BUNDLE = "selected generic bundle pk"
221 GRESOURCE_BUNDLE_MODELS = "generic_resource_bundle_models"
222 GRESOURCE_BUNDLE_INFO = "generic_resource_bundle_info"
225 GRB_LAST_HOSTLIST = "grb_network_previous_hostlist"
226 BOOKING_FORMS = "booking_forms"
227 SWCONF_HOSTS = "swconf_hosts"
228 SWCONF_SELECTED_GRB = "swconf_selected_grb_pk"
229 BOOKING_SELECTED_GRB = "booking_selected_grb_pk"
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"
244 def get(self, key, default, id):
245 self.add_get_history(key, id)
246 return self.el.get(key, default)
248 def put(self,key,val, id):
249 self.add_put_history(key, id)
252 def add_get_history(self, key, id):
253 self.add_history(key, id, self.get_history)
255 def add_put_history(self, key, id):
256 self.add_history(key, id, self.put_history)
258 def add_history(self, key, id, history):
259 if not key in history:
262 history[key].append(id)
264 def make_models(self):
265 if self.SNAPSHOT_MODELS in self.el:
266 errors = self.make_snapshot()
269 #if GRB WF, create it
270 if self.GRESOURCE_BUNDLE_MODELS in self.el:
271 errors = self.make_generic_resource_bundle()
275 if self.CONFIG_MODELS in self.el:
276 errors = self.make_software_config_bundle()
280 if self.BOOKING_MODELS in self.el:
281 errors = self.make_booking()
284 # create notification
285 booking = self.el[self.BOOKING_MODELS]['booking']
286 NotificationHandler.notify_new_booking(booking)
289 def make_snapshot(self):
290 owner = self.el[self.SESSION_USER]
291 models = self.el[self.SNAPSHOT_MODELS]
292 image = models.get('snapshot', Image())
293 booking_id = self.el.get(self.SNAPSHOT_BOOKING_ID)
295 return "SNAP, No booking ID provided"
296 booking = Booking.objects.get(pk=booking_id)
297 name = self.el.get(self.SNAPSHOT_NAME)
299 return "SNAP, no name provided"
300 host = models.get('host')
302 return "SNAP, no host provided"
303 description = self.el.get(self.SNAPSHOT_DESC, "")
304 image.from_lab = booking.lab
306 image.description = description
310 image.host_type = host.profile
314 def make_generic_resource_bundle(self):
315 owner = self.el[self.SESSION_USER]
316 if self.GRESOURCE_BUNDLE_MODELS in self.el:
317 models = self.el[self.GRESOURCE_BUNDLE_MODELS]
318 if 'hosts' in models:
319 hosts = models['hosts']
321 return "GRB has no hosts. CODE:0x0002"
322 if 'bundle' in models:
323 bundle = models['bundle']
325 return "GRB, no bundle in models. CODE:0x0003"
330 except Exception as e:
331 return "GRB, saving bundle generated exception: " + str(e) + " CODE:0x0004"
334 genericresource = host.resource
335 genericresource.bundle = bundle
336 genericresource.save()
337 host.resource = genericresource
339 except Exception as e:
340 return "GRB, saving hosts generated exception: " + str(e) + " CODE:0x0005"
342 if 'interfaces' in models:
343 for interface_set in models['interfaces'].values():
344 for interface in interface_set:
346 interface.host = interface.host
348 except Exception as e:
349 return "GRB, saving interface " + str(interface) + " failed. CODE:0x0019"
351 return "GRB, no interface set provided. CODE:0x001a"
353 if 'vlans' in models:
354 for resource_name, mapping in models['vlans'].items():
355 for profile_name, vlan_set in mapping.items():
356 interface = GenericInterface.objects.get(
357 profile__name=profile_name,
358 host__resource__name=resource_name,
359 host__resource__bundle=models['bundle']
361 for vlan in vlan_set:
364 interface.vlans.add(vlan)
365 except Exception as e:
366 return "GRB, saving vlan " + str(vlan) + " failed. Exception: " + str(e) + ". CODE:0x0017"
368 return "GRB, no vlan set provided. CODE:0x0018"
372 return "GRB no models given. CODE:0x0001"
374 self.el[self.VALIDATED_MODEL_GRB] = bundle
378 def make_software_config_bundle(self):
379 owner = self.el[self.SESSION_USER]
380 models = self.el[self.CONFIG_MODELS]
381 if 'bundle' in models:
382 bundle = models['bundle']
383 bundle.bundle = bundle.bundle
386 except Exception as e:
387 return "SWC, saving bundle generated exception: " + str(e) + "CODE:0x0007"
390 return "SWC, no bundle in models. CODE:0x0006"
391 if 'host_configs' in models:
392 host_configs = models['host_configs']
393 for host_config in host_configs:
394 host_config.bundle = host_config.bundle
395 host_config.host = host_config.host
398 except Exception as e:
399 return "SWC, saving host configs generated exception: " + str(e) + "CODE:0x0009"
401 return "SWC, no host configs in models. CODE:0x0008"
402 if 'opnfv' in models:
403 opnfvconfig = models['opnfv']
404 opnfvconfig.bundle = opnfvconfig.bundle
405 if not opnfvconfig.scenario in opnfvconfig.installer.sup_scenarios.all():
406 return "SWC, scenario not supported by installer. CODE:0x000d"
409 except Exception as e:
410 return "SWC, saving opnfv config generated exception: " + str(e) + "CODE:0x000b"
414 self.el[self.VALIDATED_MODEL_CONFIG] = bundle
418 def make_booking(self):
419 models = self.el[self.BOOKING_MODELS]
420 owner = self.el[self.SESSION_USER]
422 if self.BOOKING_SELECTED_GRB in self.el:
423 selected_grb = self.el[self.BOOKING_SELECTED_GRB]
425 return "BOOK, no selected resource. CODE:0x000e"
427 if not self.reserve_vlans(selected_grb):
428 return "BOOK, vlans not available"
430 if 'booking' in models:
431 booking = models['booking']
433 return "BOOK, no booking model exists. CODE:0x000f"
435 if not booking.start:
436 return "BOOK, booking has no start. CODE:0x0010"
438 return "BOOK, booking has no end. CODE:0x0011"
439 if booking.end <= booking.start:
440 return "BOOK, end before/same time as start. CODE:0x0012"
442 if 'collaborators' in models:
443 collaborators = models['collaborators']
445 return "BOOK, collaborators not defined. CODE:0x0013"
447 resource_bundle = ResourceManager.getInstance().convertResourceBundle(selected_grb, config=booking.config_bundle)
448 except ResourceAvailabilityException as e:
449 return "BOOK, requested resources are not available. Exception: " + str(e) + " CODE:0x0014"
450 except ModelValidationException as e:
451 return "Error encountered when saving bundle. " + str(e) + " CODE: 0x001b"
453 booking.resource = resource_bundle
454 booking.owner = owner
455 booking.config_bundle = booking.config_bundle
456 booking.lab = selected_grb.lab
458 is_allowed = BookingAuthManager().booking_allowed(booking, self)
460 return "BOOK, you are not allowed to book the requested resources"
464 except Exception as e:
465 return "BOOK, saving booking generated exception: " + str(e) + " CODE:0x0015"
467 for collaborator in collaborators:
468 booking.collaborators.add(collaborator)
471 JobFactory.makeCompleteJob(booking)
472 except Exception as e:
473 return "BOOK, serializing for api generated exception: " + str(e) + " CODE:0xFFFF"
477 except Exception as e:
478 return "BOOK, saving booking generated exception: " + str(e) + " CODE:0x0016"
480 def reserve_vlans(self, grb):
486 vlan_manager = grb.lab.vlan_manager
487 if vlan_manager is None:
489 for host in grb.getHosts():
490 for interface in host.generic_interfaces.all():
491 for vlan in interface.vlans.all():
495 vlans.append(vlan.vlan_id)
498 vlan_manager.reserve_vlans(vlans)
499 vlan_manager.reserve_public_vlan(public_vlan.vlan_id)
501 except Exception as e:
507 self.el[self.CONFIRMATION] = {}
508 self.get_history = {}
509 self.put_history = {}