1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
|
##############################################################################
# Copyright (c) 2018 Sawyer Bergeron, Parker Berberian, and others.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Apache License, Version 2.0
# which accompanies this distribution, and is available at
# http://www.apache.org/licenses/LICENSE-2.0
##############################################################################
from django.shortcuts import render
from django.contrib import messages
import yaml
import requests
from workflow.forms import ConfirmationForm
from api.models import JobFactory
from dashboard.exceptions import ResourceAvailabilityException, ModelValidationException
from resource_inventory.models import Image, GenericInterface
from resource_inventory.resource_manager import ResourceManager
from notifier.manager import NotificationHandler
from booking.models import Booking
class BookingAuthManager():
LFN_PROJECTS = ["opnfv"] # TODO
def parse_url(self, info_url):
"""
will return the PTL in the INFO file on success, or None
"""
try:
parts = info_url.split("/")
if parts[0].find("http") > -1: # the url include http(s)://
parts = parts[2:]
if parts[-1] != "INFO.yaml":
return None
if parts[0] not in ["github.com", "raw.githubusercontent.com"]:
return None
if parts[1] not in self.LFN_PROJECTS:
return None
# now to download and parse file
if parts[3] == "blob":
parts[3] = "raw"
url = "https://" + "/".join(parts)
info_file = requests.get(url, timeout=15).text
info_parsed = yaml.load(info_file)
ptl = info_parsed.get('project_lead')
if not ptl:
return None
return ptl
except Exception:
return None
def booking_allowed(self, booking, repo):
"""
This is the method that will have to change whenever the booking policy changes in the Infra
group / LFN. This is a nice isolation of that administration crap
currently checks if the booking uses multiple servers. if it does, then the owner must be a PTL,
which is checked using the provided info file
"""
if len(booking.resource.template.getHosts()) < 2:
return True # if they only have one server, we dont care
if booking.owner.userprofile.booking_privledge:
return True # admin override for this user
if repo.BOOKING_INFO_FILE not in repo.el:
return False # INFO file not provided
ptl_info = self.parse_url(repo.BOOKING_INFO_FILE)
return ptl_info and ptl_info == booking.owner.userprofile.email_addr
class WorkflowStep(object):
template = 'bad_request.html'
title = "Generic Step"
description = "You were led here by mistake"
short_title = "error"
metastep = None
def __init__(self, id, repo=None):
self.repo = repo
self.id = id
def get_context(self):
context = {}
context['step_number'] = self.repo_get('steps')
context['active_step'] = self.repo_get('active_step')
context['render_correct'] = "true"
context['step_title'] = self.title
context['description'] = self.description
return context
def render(self, request):
self.context = self.get_context()
return render(request, self.template, self.context)
def post_render(self, request):
return self.render(request)
def test_render(self, request):
if request.method == "POST":
return self.post_render(request)
return self.render(request)
def validate(self, request):
pass
def repo_get(self, key, default=None):
return self.repo.get(key, default, self.id)
def repo_put(self, key, value):
return self.repo.put(key, value, self.id)
class Confirmation_Step(WorkflowStep):
template = 'workflow/confirm.html'
title = "Confirm Changes"
description = "Does this all look right?"
def get_vlan_warning(self):
grb = self.repo_get(self.repo.BOOKING_SELECTED_GRB, False)
if not grb:
return 0
vlan_manager = grb.lab.vlan_manager
if vlan_manager is None:
return 0
hosts = grb.getHosts()
for host in hosts:
for interface in host.generic_interfaces.all():
for vlan in interface.vlans.all():
if vlan.public:
if not vlan_manager.public_vlan_is_available(vlan.vlan_id):
return 1
else:
if not vlan_manager.is_available(vlan.vlan_id):
return 1 # There is a problem with these vlans
return 0
def get_context(self):
context = super(Confirmation_Step, self).get_context()
context['form'] = ConfirmationForm()
context['confirmation_info'] = yaml.dump(
self.repo_get(self.repo.CONFIRMATION),
default_flow_style=False
).strip()
context['vlan_warning'] = self.get_vlan_warning()
return context
def flush_to_db(self):
errors = self.repo.make_models()
if errors:
return errors
def post_render(self, request):
form = ConfirmationForm(request.POST)
if form.is_valid():
data = form.cleaned_data['confirm']
context = self.get_context()
if data == "True":
context["bypassed"] = "true"
errors = self.flush_to_db()
if errors:
messages.add_message(request, messages.ERROR, "ERROR OCCURRED: " + errors)
return render(request, self.template, context)
messages.add_message(request, messages.SUCCESS, "Confirmed")
return render(request, self.template, context)
elif data == "False":
context["bypassed"] = "true"
messages.add_message(request, messages.SUCCESS, "Canceled")
return render(request, self.template, context)
else:
pass
else:
if "vlan_input" in request.POST:
if request.POST.get("vlan_input") == "True":
self.translate_vlans()
return self.render(request)
pass
def translate_vlans(self):
grb = self.repo_get(self.repo.BOOKING_SELECTED_GRB, False)
if not grb:
return 0
vlan_manager = grb.lab.vlan_manager
if vlan_manager is None:
return 0
hosts = grb.getHosts()
for host in hosts:
for interface in host.generic_interfaces.all():
for vlan in interface.vlans.all():
if not vlan.public:
if not vlan_manager.is_available(vlan.vlan_id):
vlan.vlan_id = vlan_manager.get_vlan()
vlan.save()
else:
if not vlan_manager.public_vlan_is_available(vlan.vlan_id):
pub_vlan = vlan_manager.get_public_vlan()
vlan.vlan_id = pub_vlan.vlan
vlan.save()
class Workflow():
steps = []
active_index = 0
class Repository():
EDIT = "editing"
MODELS = "models"
RESOURCE_SELECT = "resource_select"
CONFIRMATION = "confirmation"
SELECTED_GRESOURCE_BUNDLE = "selected generic bundle pk"
GRESOURCE_BUNDLE_MODELS = "generic_resource_bundle_models"
GRESOURCE_BUNDLE_INFO = "generic_resource_bundle_info"
BOOKING = "booking"
LAB = "lab"
GRB_LAST_HOSTLIST = "grb_network_previous_hostlist"
BOOKING_FORMS = "booking_forms"
SWCONF_HOSTS = "swconf_hosts"
SWCONF_SELECTED_GRB = "swconf_selected_grb_pk"
BOOKING_SELECTED_GRB = "booking_selected_grb_pk"
BOOKING_MODELS = "booking models"
CONFIG_MODELS = "configuration bundle models"
SESSION_USER = "session owner user account"
VALIDATED_MODEL_GRB = "valid grb config model instance in db"
VALIDATED_MODEL_CONFIG = "valid config model instance in db"
VALIDATED_MODEL_BOOKING = "valid booking model instance in db"
VLANS = "a list of vlans"
SNAPSHOT_MODELS = "the models for snapshotting"
SNAPSHOT_BOOKING_ID = "the booking id for snapshotting"
SNAPSHOT_NAME = "the name of the snapshot"
SNAPSHOT_DESC = "description of the snapshot"
BOOKING_INFO_FILE = "the INFO.yaml file for this user's booking"
def get(self, key, default, id):
self.add_get_history(key, id)
return self.el.get(key, default)
def put(self, key, val, id):
self.add_put_history(key, id)
self.el[key] = val
def add_get_history(self, key, id):
self.add_history(key, id, self.get_history)
def add_put_history(self, key, id):
self.add_history(key, id, self.put_history)
def add_history(self, key, id, history):
if key not in history:
history[key] = [id]
else:
history[key].append(id)
def make_models(self):
if self.SNAPSHOT_MODELS in self.el:
errors = self.make_snapshot()
if errors:
return errors
# if GRB WF, create it
if self.GRESOURCE_BUNDLE_MODELS in self.el:
errors = self.make_generic_resource_bundle()
if errors:
return errors
if self.CONFIG_MODELS in self.el:
errors = self.make_software_config_bundle()
if errors:
return errors
if self.BOOKING_MODELS in self.el:
errors = self.make_booking()
if errors:
return errors
# create notification
booking = self.el[self.BOOKING_MODELS]['booking']
NotificationHandler.notify_new_booking(booking)
def make_snapshot(self):
owner = self.el[self.SESSION_USER]
models = self.el[self.SNAPSHOT_MODELS]
image = models.get('snapshot', Image())
booking_id = self.el.get(self.SNAPSHOT_BOOKING_ID)
if not booking_id:
return "SNAP, No booking ID provided"
booking = Booking.objects.get(pk=booking_id)
name = self.el.get(self.SNAPSHOT_NAME)
if not name:
return "SNAP, no name provided"
host = models.get('host')
if not host:
return "SNAP, no host provided"
description = self.el.get(self.SNAPSHOT_DESC, "")
image.from_lab = booking.lab
image.name = name
image.description = description
image.public = False
image.lab_id = -1
image.owner = owner
image.host_type = host.profile
image.save()
def make_generic_resource_bundle(self):
owner = self.el[self.SESSION_USER]
if self.GRESOURCE_BUNDLE_MODELS in self.el:
models = self.el[self.GRESOURCE_BUNDLE_MODELS]
if 'hosts' in models:
hosts = models['hosts']
else:
return "GRB has no hosts. CODE:0x0002"
if 'bundle' in models:
bundle = models['bundle']
else:
return "GRB, no bundle in models. CODE:0x0003"
try:
bundle.owner = owner
bundle.save()
except Exception as e:
return "GRB, saving bundle generated exception: " + str(e) + " CODE:0x0004"
try:
for host in hosts:
genericresource = host.resource
genericresource.bundle = bundle
genericresource.save()
host.resource = genericresource
host.save()
except Exception as e:
return "GRB, saving hosts generated exception: " + str(e) + " CODE:0x0005"
if 'interfaces' in models:
for interface_set in models['interfaces'].values():
for interface in interface_set:
try:
interface.host = interface.host
interface.save()
except Exception as e:
return "GRB, saving interface " + str(interface) + " failed. CODE:0x0019"
else:
return "GRB, no interface set provided. CODE:0x001a"
if 'vlans' in models:
for resource_name, mapping in models['vlans'].items():
for profile_name, vlan_set in mapping.items():
interface = GenericInterface.objects.get(
profile__name=profile_name,
host__resource__name=resource_name,
host__resource__bundle=models['bundle']
)
for vlan in vlan_set:
try:
vlan.save()
interface.vlans.add(vlan)
except Exception as e:
return "GRB, saving vlan " + str(vlan) + " failed. Exception: " + str(e) + ". CODE:0x0017"
else:
return "GRB, no vlan set provided. CODE:0x0018"
else:
return "GRB no models given. CODE:0x0001"
self.el[self.VALIDATED_MODEL_GRB] = bundle
return False
def make_software_config_bundle(self):
models = self.el[self.CONFIG_MODELS]
if 'bundle' in models:
bundle = models['bundle']
bundle.bundle = bundle.bundle
try:
bundle.save()
except Exception as e:
return "SWC, saving bundle generated exception: " + str(e) + "CODE:0x0007"
else:
return "SWC, no bundle in models. CODE:0x0006"
if 'host_configs' in models:
host_configs = models['host_configs']
for host_config in host_configs:
host_config.bundle = host_config.bundle
host_config.host = host_config.host
try:
host_config.save()
except Exception as e:
return "SWC, saving host configs generated exception: " + str(e) + "CODE:0x0009"
else:
return "SWC, no host configs in models. CODE:0x0008"
if 'opnfv' in models:
opnfvconfig = models['opnfv']
opnfvconfig.bundle = opnfvconfig.bundle
if opnfvconfig.scenario not in opnfvconfig.installer.sup_scenarios.all():
return "SWC, scenario not supported by installer. CODE:0x000d"
try:
opnfvconfig.save()
except Exception as e:
return "SWC, saving opnfv config generated exception: " + str(e) + "CODE:0x000b"
else:
pass
self.el[self.VALIDATED_MODEL_CONFIG] = bundle
return False
def make_booking(self):
models = self.el[self.BOOKING_MODELS]
owner = self.el[self.SESSION_USER]
if self.BOOKING_SELECTED_GRB in self.el:
selected_grb = self.el[self.BOOKING_SELECTED_GRB]
else:
return "BOOK, no selected resource. CODE:0x000e"
if not self.reserve_vlans(selected_grb):
return "BOOK, vlans not available"
if 'booking' in models:
booking = models['booking']
else:
return "BOOK, no booking model exists. CODE:0x000f"
if not booking.start:
return "BOOK, booking has no start. CODE:0x0010"
if not booking.end:
return "BOOK, booking has no end. CODE:0x0011"
if booking.end <= booking.start:
return "BOOK, end before/same time as start. CODE:0x0012"
if 'collaborators' in models:
collaborators = models['collaborators']
else:
return "BOOK, collaborators not defined. CODE:0x0013"
try:
resource_bundle = ResourceManager.getInstance().convertResourceBundle(selected_grb, config=booking.config_bundle)
except ResourceAvailabilityException as e:
return "BOOK, requested resources are not available. Exception: " + str(e) + " CODE:0x0014"
except ModelValidationException as e:
return "Error encountered when saving bundle. " + str(e) + " CODE: 0x001b"
booking.resource = resource_bundle
booking.owner = owner
booking.config_bundle = booking.config_bundle
booking.lab = selected_grb.lab
is_allowed = BookingAuthManager().booking_allowed(booking, self)
if not is_allowed:
return "BOOK, you are not allowed to book the requested resources"
try:
booking.save()
except Exception as e:
return "BOOK, saving booking generated exception: " + str(e) + " CODE:0x0015"
for collaborator in collaborators:
booking.collaborators.add(collaborator)
try:
JobFactory.makeCompleteJob(booking)
except Exception as e:
return "BOOK, serializing for api generated exception: " + str(e) + " CODE:0xFFFF"
try:
booking.save()
except Exception as e:
return "BOOK, saving booking generated exception: " + str(e) + " CODE:0x0016"
def reserve_vlans(self, grb):
"""
True is success
"""
vlans = []
public_vlan = None
vlan_manager = grb.lab.vlan_manager
if vlan_manager is None:
return True
for host in grb.getHosts():
for interface in host.generic_interfaces.all():
for vlan in interface.vlans.all():
if vlan.public:
public_vlan = vlan
else:
vlans.append(vlan.vlan_id)
try:
vlan_manager.reserve_vlans(vlans)
vlan_manager.reserve_public_vlan(public_vlan.vlan_id)
return True
except Exception:
return False
def __init__(self):
self.el = {}
self.el[self.CONFIRMATION] = {}
self.get_history = {}
self.put_history = {}
|