aboutsummaryrefslogtreecommitdiffstats
path: root/src/booking
diff options
context:
space:
mode:
Diffstat (limited to 'src/booking')
-rw-r--r--src/booking/forms.py1
-rw-r--r--src/booking/lib.py4
-rw-r--r--src/booking/migrations/0009_booking_complete.py18
-rw-r--r--src/booking/models.py2
-rw-r--r--src/booking/quick_deployer.py147
-rw-r--r--src/booking/stats.py2
-rw-r--r--src/booking/urls.py2
-rw-r--r--src/booking/views.py7
8 files changed, 122 insertions, 61 deletions
diff --git a/src/booking/forms.py b/src/booking/forms.py
index cbc3407..ff829b2 100644
--- a/src/booking/forms.py
+++ b/src/booking/forms.py
@@ -22,6 +22,7 @@ class QuickBookingForm(forms.Form):
purpose = forms.CharField(max_length=1000)
project = forms.CharField(max_length=400)
hostname = forms.CharField(required=False, max_length=400)
+ global_cloud_config = forms.CharField(widget=forms.Textarea, required=False)
installer = forms.ModelChoiceField(queryset=Installer.objects.all(), required=False)
scenario = forms.ModelChoiceField(queryset=Scenario.objects.all(), required=False)
diff --git a/src/booking/lib.py b/src/booking/lib.py
index 7a4c261..8c87979 100644
--- a/src/booking/lib.py
+++ b/src/booking/lib.py
@@ -28,9 +28,9 @@ def get_user_items(exclude=None):
for up in qs:
item = {
'id': up.id,
- 'expanded_name': up.full_name,
+ 'expanded_name': up.full_name if up.full_name else up.user.username,
'small_name': up.user.username,
- 'string': up.email_addr
+ 'string': up.email_addr if up.email_addr else up.user.username,
}
items[up.id] = item
return items
diff --git a/src/booking/migrations/0009_booking_complete.py b/src/booking/migrations/0009_booking_complete.py
new file mode 100644
index 0000000..e291a83
--- /dev/null
+++ b/src/booking/migrations/0009_booking_complete.py
@@ -0,0 +1,18 @@
+# Generated by Django 2.2 on 2021-09-07 15:14
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('booking', '0008_auto_20201109_1947'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='booking',
+ name='complete',
+ field=models.BooleanField(default=False),
+ ),
+ ]
diff --git a/src/booking/models.py b/src/booking/models.py
index cfdf7bc..966f1c2 100644
--- a/src/booking/models.py
+++ b/src/booking/models.py
@@ -39,6 +39,8 @@ class Booking(models.Model):
pdf = models.TextField(blank=True, default="")
idf = models.TextField(blank=True, default="")
+ complete = models.BooleanField(default=False)
+
class Meta:
db_table = 'booking'
diff --git a/src/booking/quick_deployer.py b/src/booking/quick_deployer.py
index 0a3bfc6..4b85d76 100644
--- a/src/booking/quick_deployer.py
+++ b/src/booking/quick_deployer.py
@@ -9,15 +9,16 @@
import json
+import yaml
from django.db.models import Q
+from django.db import transaction
from datetime import timedelta
from django.utils import timezone
from django.core.exceptions import ValidationError
-from account.models import Lab
+from account.models import Lab, UserProfile
from resource_inventory.models import (
ResourceTemplate,
- Installer,
Image,
OPNFVRole,
OPNFVConfig,
@@ -26,6 +27,7 @@ from resource_inventory.models import (
NetworkConnection,
InterfaceConfiguration,
Network,
+ CloudInitFile,
)
from resource_inventory.resource_manager import ResourceManager
from resource_inventory.pdf_templater import PDFTemplater
@@ -60,7 +62,7 @@ def parse_resource_field(resource_json):
return lab, template
-def update_template(old_template, image, hostname, user):
+def update_template(old_template, image, hostname, user, global_cloud_config=None):
"""
Duplicate a template to the users account and update configured fields.
@@ -80,6 +82,8 @@ def update_template(old_template, image, hostname, user):
description=old_template.description,
public=False,
temporary=True,
+ private_vlan_pool=old_template.private_vlan_pool,
+ public_vlan_pool=old_template.public_vlan_pool,
copy_of=old_template
)
@@ -112,9 +116,17 @@ def update_template(old_template, image, hostname, user):
image=image_to_set,
template=template,
is_head_node=old_config.is_head_node,
- name=hostname if len(old_template.getConfigs()) == 1 else old_config.name
+ name=hostname if len(old_template.getConfigs()) == 1 else old_config.name,
+ # cloud_init_files=old_config.cloud_init_files.set()
)
+ for file in old_config.cloud_init_files.all():
+ config.cloud_init_files.add(file)
+
+ if global_cloud_config:
+ config.cloud_init_files.add(global_cloud_config)
+ config.save()
+
for old_iface_config in old_config.interface_configs.all():
iface_config = InterfaceConfiguration.objects.create(
profile=old_iface_config.profile,
@@ -167,28 +179,19 @@ def generate_resource_bundle(template):
return resource_bundle
-def check_invariants(request, **kwargs):
+def check_invariants(**kwargs):
# TODO: This should really happen in the BookingForm validation methods
- installer = kwargs['installer']
image = kwargs['image']
- scenario = kwargs['scenario']
lab = kwargs['lab']
length = kwargs['length']
# check that image os is compatible with installer
if image:
- if installer or scenario:
- if installer in image.os.sup_installers.all():
- # if installer not here, we can omit that and not check for scenario
- if not scenario:
- raise ValidationError("An OPNFV Installer needs a scenario to be chosen to work properly")
- if scenario not in installer.sup_scenarios.all():
- raise ValidationError("The chosen installer does not support the chosen scenario")
if image.from_lab != lab:
raise ValidationError("The chosen image is not available at the chosen hosting lab")
# TODO
# if image.host_type != host_profile:
# raise ValidationError("The chosen image is not available for the chosen host type")
- if not image.public and image.owner != request.user:
+ if not image.public and image.owner != kwargs['owner']:
raise ValidationError("You are not the owner of the chosen private image")
if length < 1 or length > 21:
raise BookingLengthException("Booking must be between 1 and 21 days long")
@@ -196,62 +199,105 @@ def check_invariants(request, **kwargs):
def create_from_form(form, request):
"""
- Create a Booking from the user's form.
-
- Large, nasty method to create a booking or return a useful error
- based on the form from the frontend
+ Parse data from QuickBookingForm to create booking
"""
resource_field = form.cleaned_data['filter_field']
- purpose_field = form.cleaned_data['purpose']
- project_field = form.cleaned_data['project']
- users_field = form.cleaned_data['users']
+ # users_field = form.cleaned_data['users']
hostname = 'opnfv_host' if not form.cleaned_data['hostname'] else form.cleaned_data['hostname']
- length = form.cleaned_data['length']
- image = form.cleaned_data['image']
- scenario = form.cleaned_data['scenario']
- installer = form.cleaned_data['installer']
+ global_cloud_config = None if not form.cleaned_data['global_cloud_config'] else form.cleaned_data['global_cloud_config']
+
+ if global_cloud_config:
+ form.cleaned_data['global_cloud_config'] = create_ci_file(global_cloud_config)
+
+ # image = form.cleaned_data['image']
+ # scenario = form.cleaned_data['scenario']
+ # installer = form.cleaned_data['installer']
lab, resource_template = parse_resource_field(resource_field)
data = form.cleaned_data
+ data['hostname'] = hostname
data['lab'] = lab
data['resource_template'] = resource_template
- check_invariants(request, **data)
+ data['owner'] = request.user
+
+ return _create_booking(data)
+
+
+def create_from_API(body, user):
+ """
+ Parse data from Automation API to create booking
+ """
+ booking_info = json.loads(body.decode('utf-8'))
+
+ data = {}
+ data['purpose'] = booking_info['purpose']
+ data['project'] = booking_info['project']
+ data['users'] = [UserProfile.objects.get(user__username=username)
+ for username in booking_info['collaborators']]
+ data['hostname'] = booking_info['hostname']
+ data['length'] = booking_info['length']
+ data['installer'] = None
+ data['scenario'] = None
+
+ data['image'] = Image.objects.get(pk=booking_info['imageLabID'])
+
+ data['resource_template'] = ResourceTemplate.objects.get(pk=booking_info['templateID'])
+ data['lab'] = data['resource_template'].lab
+ data['owner'] = user
+
+ if 'global_cloud_config' in data.keys():
+ data['global_cloud_config'] = CloudInitFile.objects.get(id=data['global_cloud_config'])
+
+ return _create_booking(data)
+
+
+def create_ci_file(data: str) -> CloudInitFile:
+ try:
+ d = yaml.load(data)
+ if not (type(d) is dict):
+ raise Exception("CI file was valid yaml but was not a dict")
+ except Exception:
+ raise ValidationError("The provided Cloud Config is not valid yaml, please refer to the Cloud Init documentation for expected structure")
+ print("about to create global cloud config")
+ config = CloudInitFile.create(text=data, priority=CloudInitFile.objects.count())
+ print("made global cloud config")
+
+ return config
+
+
+@transaction.atomic
+def _create_booking(data):
+ check_invariants(**data)
# check booking privileges
# TODO: use the canonical booking_allowed method because now template might have multiple
# machines
- if Booking.objects.filter(owner=request.user, end__gt=timezone.now()).count() >= 3 and not request.user.userprofile.booking_privledge:
+ if Booking.objects.filter(owner=data['owner'], end__gt=timezone.now()).count() >= 3 and not data['owner'].userprofile.booking_privledge:
raise PermissionError("You do not have permission to have more than 3 bookings at a time.")
- ResourceManager.getInstance().templateIsReservable(resource_template)
+ ResourceManager.getInstance().templateIsReservable(data['resource_template'])
- resource_template = update_template(resource_template, image, hostname, request.user)
-
- # if no installer provided, just create blank host
- opnfv_config = None
- if installer:
- hconf = resource_template.getConfigs()[0]
- opnfv_config = generate_opnfvconfig(scenario, installer, resource_template)
- generate_hostopnfv(hconf, opnfv_config)
+ resource_template = update_template(data['resource_template'], data['image'], data['hostname'], data['owner'], global_cloud_config=data['global_cloud_config'])
# generate resource bundle
resource_bundle = generate_resource_bundle(resource_template)
# generate booking
booking = Booking.objects.create(
- purpose=purpose_field,
- project=project_field,
- lab=lab,
- owner=request.user,
+ purpose=data['purpose'],
+ project=data['project'],
+ lab=data['lab'],
+ owner=data['owner'],
start=timezone.now(),
- end=timezone.now() + timedelta(days=int(length)),
+ end=timezone.now() + timedelta(days=int(data['length'])),
resource=resource_bundle,
- opnfv_config=opnfv_config
+ opnfv_config=None
)
+
booking.pdf = PDFTemplater.makePDF(booking)
- for collaborator in users_field: # list of UserProfiles
+ for collaborator in data['users']: # list of Users (not UserProfile)
booking.collaborators.add(collaborator.user)
booking.save()
@@ -272,23 +318,14 @@ def drop_filter(user):
that installer is supported on that image
"""
installer_filter = {}
- for image in Image.objects.all():
- installer_filter[image.id] = {}
- for installer in image.os.sup_installers.all():
- installer_filter[image.id][installer.id] = 1
-
scenario_filter = {}
- for installer in Installer.objects.all():
- scenario_filter[installer.id] = {}
- for scenario in installer.sup_scenarios.all():
- scenario_filter[installer.id][scenario.id] = 1
images = Image.objects.filter(Q(public=True) | Q(owner=user))
image_filter = {}
for image in images:
image_filter[image.id] = {
'lab': 'lab_' + str(image.from_lab.lab_user.id),
- 'host_profile': str(image.host_type.id),
+ 'architecture': str(image.architecture),
'name': image.name
}
@@ -296,7 +333,7 @@ def drop_filter(user):
templates = ResourceTemplate.objects.filter(Q(public=True) | Q(owner=user))
for rt in templates:
profiles = [conf.profile for conf in rt.getConfigs()]
- resource_filter["resource_" + str(rt.id)] = [str(p.id) for p in profiles]
+ resource_filter["resource_" + str(rt.id)] = [str(p.architecture) for p in profiles]
return {
'installer_filter': json.dumps(installer_filter),
diff --git a/src/booking/stats.py b/src/booking/stats.py
index 626ed79..70f91fa 100644
--- a/src/booking/stats.py
+++ b/src/booking/stats.py
@@ -104,5 +104,5 @@ class StatisticsManager(object):
"user": [x, users],
"utils": [in_use, not_in_use, maintenance],
"projects": [project_keys, project_counts],
- "colors": anuket_colors if os.environ['TEMPLATE_OVERRIDE_DIR'] == 'laas' else lfedge_colors
+ "colors": anuket_colors if os.environ.get('TEMPLATE_OVERRIDE_DIR') == 'laas' else lfedge_colors
}
diff --git a/src/booking/urls.py b/src/booking/urls.py
index cdf18ae..0b60351 100644
--- a/src/booking/urls.py
+++ b/src/booking/urls.py
@@ -38,7 +38,7 @@ from booking.views import (
booking_modify_image
)
-app_name = "booking"
+app_name = 'booking'
urlpatterns = [
url(r'^detail/(?P<booking_id>[0-9]+)/$', booking_detail_view, name='detail'),
url(r'^(?P<booking_id>[0-9]+)/$', booking_detail_view, name='booking_detail'),
diff --git a/src/booking/views.py b/src/booking/views.py
index 2b910e7..940428b 100644
--- a/src/booking/views.py
+++ b/src/booking/views.py
@@ -28,6 +28,7 @@ from api.models import JobFactory
from workflow.views import login
from booking.forms import QuickBookingForm
from booking.quick_deployer import create_from_form, drop_filter
+import traceback
def quick_create_clear_fields(request):
@@ -62,6 +63,9 @@ def quick_create(request):
"Check Account->My Bookings for the status of your new booking")
return redirect(reverse('booking:booking_detail', kwargs={'booking_id': booking.id}))
except Exception as e:
+ print("Error occurred while handling quick deployment:")
+ traceback.print_exc()
+ print(str(e))
messages.error(request, "Whoops, an error occurred: " + str(e))
context.update(drop_filter(request.user))
return render(request, 'booking/quick_deploy.html', context)
@@ -127,7 +131,6 @@ class ResourceBookingsJSON(View):
'start',
'end',
'purpose',
- 'jira_issue_status',
'config_bundle__name'
)
return JsonResponse({'bookings': list(bookings)})
@@ -138,7 +141,7 @@ def build_image_mapping(lab, user):
for profile in ResourceProfile.objects.filter(labs=lab):
images = Image.objects.filter(
from_lab=lab,
- host_type=profile
+ architecture=profile.architecture
).filter(
Q(public=True) | Q(owner=user)
)