diff options
-rw-r--r-- | src/account/models.py | 16 | ||||
-rw-r--r-- | src/booking/views.py | 11 | ||||
-rw-r--r-- | src/dashboard/admin_utils.py | 111 | ||||
-rw-r--r-- | src/dashboard/views.py | 16 | ||||
-rw-r--r-- | src/resource_inventory/models.py | 18 | ||||
-rw-r--r-- | src/resource_inventory/pdf_templater.py | 6 | ||||
-rw-r--r-- | src/templates/akraino/booking/quick_deploy.html | 3 | ||||
-rw-r--r-- | src/templates/base/booking/booking_detail.html | 3 | ||||
-rw-r--r-- | src/templates/base/booking/quick_deploy.html | 5 | ||||
-rw-r--r-- | src/templates/base/dashboard/landing.html | 17 | ||||
-rw-r--r-- | src/templates/base/workflow/viewport-base.html | 5 | ||||
-rw-r--r-- | src/templates/laas/dashboard/landing.html | 2 | ||||
-rw-r--r-- | src/workflow/views.py | 8 |
13 files changed, 192 insertions, 29 deletions
diff --git a/src/account/models.py b/src/account/models.py index 2c133bb..2d0293f 100644 --- a/src/account/models.py +++ b/src/account/models.py @@ -11,8 +11,6 @@ from django.contrib.auth.models import User from django.db import models from django.apps import apps -from django.core.exceptions import ValidationError -import re import json import random @@ -59,20 +57,6 @@ class UserProfile(models.Model): class Meta: db_table = 'user_profile' - def clean(self, *args, **kwargs): - company = self.company - regex = r'[a-z\_\-\.\$]*' - pattern = re.compile(regex) - - if not pattern.fullmatch(company): - raise ValidationError('Company may only include lowercase letters, _, -, . and $') - - super().clean(*args, **kwargs) - - def save(self, *args, **kwargs): - self.full_clean() - super().save(*args, **kwargs) - def __str__(self): return self.user.username diff --git a/src/booking/views.py b/src/booking/views.py index bd57812..c41a7d6 100644 --- a/src/booking/views.py +++ b/src/booking/views.py @@ -19,7 +19,7 @@ from django.db.models import Q from django.urls import reverse from resource_inventory.models import ResourceBundle, ResourceProfile, Image, ResourceQuery -from account.models import Downtime +from account.models import Downtime, Lab from booking.models import Booking from booking.stats import StatisticsManager from booking.forms import HostReImageForm @@ -44,6 +44,7 @@ def quick_create(request): context['form'] = QuickBookingForm(lab_data=attrs, default_user=request.user.username, user=request.user) context['lab_profile_map'] = {} context.update(drop_filter(request.user)) + context['contact_email'] = Lab.objects.filter(name="UNH_IOL").first().contact_email return render(request, 'booking/quick_deploy.html', context) if request.method == 'POST': @@ -74,9 +75,15 @@ class BookingView(TemplateView): def get_context_data(self, **kwargs): booking = get_object_or_404(Booking, id=self.kwargs['booking_id']) title = 'Booking Details' + contact = Lab.objects.filter(name="UNH_IOL").first().contact_email downtime = Downtime.objects.filter(lab=booking.lab, start__lt=timezone.now, end__gt=timezone.now()).first() context = super(BookingView, self).get_context_data(**kwargs) - context.update({'title': title, 'booking': booking, 'downtime': downtime}) + context.update({ + 'title': title, + 'booking': booking, + 'downtime': downtime, + 'contact_email': contact + }) return context diff --git a/src/dashboard/admin_utils.py b/src/dashboard/admin_utils.py new file mode 100644 index 0000000..367c613 --- /dev/null +++ b/src/dashboard/admin_utils.py @@ -0,0 +1,111 @@ +from resource_inventory.models import ( + ResourceTemplate, + Image, + Server, + ResourceBundle, +) + +from django.contrib.auth.models import User + +from account.models import Lab + +from resource_inventory.resource_manager import ResourceManager +from resource_inventory.pdf_templater import PDFTemplater + +from booking.quick_deployer import update_template + +from datetime import timedelta + +from django.utils import timezone + +from booking.models import Booking +from notifier.manager import NotificationHandler +from api.models import JobFactory + + +""" +creates a quick booking using the given host +""" + + +def book_host(owner_username, host_labid, lab_username, hostname, image_id, template_name, length_days=21, collaborator_usernames=[], purpose="internal", project="LaaS"): + lab = Lab.objects.get(lab_user__username=lab_username) + host = Server.objects.filter(lab=lab).get(labid=host_labid) + if host.booked: + print("Can't book host, already marked as booked") + return + else: + host.booked = True + host.save() + + template = ResourceTemplate.objects.filter(public=True).get(name=template_name) + image = Image.objects.get(id=image_id) + + owner = User.objects.get(username=owner_username) + + new_template = update_template(template, image, hostname, owner) + + rmanager = ResourceManager.getInstance() + + vlan_map = rmanager.get_vlans(new_template) + + # only a single host so can reuse var for iter here + resource_bundle = ResourceBundle.objects.create(template=new_template) + res_configs = new_template.getConfigs() + + for config in res_configs: + try: + host.bundle = resource_bundle + host.config = config + rmanager.configureNetworking(resource_bundle, host, vlan_map) + host.save() + except Exception: + host.booked = False + host.save() + print("Failed to book host due to error configuring it") + return + + new_template.save() + + booking = Booking.objects.create( + purpose=purpose, + project=project, + lab=lab, + owner=owner, + start=timezone.now(), + end=timezone.now() + timedelta(days=int(length_days)), + resource=resource_bundle, + opnfv_config=None + ) + + booking.pdf = PDFTemplater.makePDF(booking) + + booking.save() + + for collaborator_username in collaborator_usernames: + try: + user = User.objects.get(username=collaborator_username) + booking.collaborators.add(user) + except Exception: + print("couldn't add user with username ", collaborator_username) + + booking.save() + + JobFactory.makeCompleteJob(booking) + NotificationHandler.notify_new_booking(booking) + + +def mark_working(host_labid, lab_username, working=True): + lab = Lab.objects.get(lab_user__username=lab_username) + server = Server.objects.filter(lab=lab).get(labid=host_labid) + print("changing server working status from ", server.working, "to", working) + server.working = working + server.save() + + +def mark_booked(host_labid, lab_username, booked=True): + lab = Lab.objects.get(lab_user__username=lab_username) + server = Server.objects.filter(lab=lab).get(labid=host_labid) + print("changing server booked status from ", server.booked, "to", booked) + server.booked = booked + server.save() diff --git a/src/dashboard/views.py b/src/dashboard/views.py index 2ace2d4..f9a908c 100644 --- a/src/dashboard/views.py +++ b/src/dashboard/views.py @@ -12,8 +12,12 @@ from django.shortcuts import get_object_or_404 from django.views.generic import TemplateView from django.shortcuts import render +from django.db.models import Q +from datetime import datetime +import pytz from account.models import Lab +from booking.models import Booking from resource_inventory.models import Image, ResourceProfile, ResourceQuery from workflow.workflow_manager import ManagerTracker @@ -65,12 +69,22 @@ def host_profile_detail_view(request): def landing_view(request): manager = ManagerTracker.managers.get(request.session.get('manager_session')) + user = request.user + if not user.is_anonymous: + bookings = Booking.objects.filter( + Q(owner=user) | Q(collaborators=user), + end__gte=datetime.now(pytz.utc) + ) + else: + bookings = None + return render( request, 'dashboard/landing.html', { 'manager': manager is not None, - 'title': "Welcome to the Lab as a Service Dashboard" + 'title': "Welcome to the Lab as a Service Dashboard", + 'bookings': bookings } ) diff --git a/src/resource_inventory/models.py b/src/resource_inventory/models.py index c918513..01da8bb 100644 --- a/src/resource_inventory/models.py +++ b/src/resource_inventory/models.py @@ -12,6 +12,7 @@ from django.contrib.auth.models import User from django.core.exceptions import ValidationError from django.db import models from django.db.models import Q +import traceback import re from collections import Counter @@ -201,10 +202,20 @@ class ResourceBundle(models.Model): def release(self): for pn in PhysicalNetwork.objects.filter(bundle=self).all(): - pn.release() + try: + pn.release() + except Exception as e: + print("Exception occurred while trying to release resource ", pn.vlan_id) + print(e) + traceback.print_exc() for resource in self.get_resources(): - resource.release() + try: + resource.release() + except Exception as e: + print("Exception occurred while trying to release resource ", resource) + print(e) + traceback.print_exc() def get_template_name(self): if not self.template: @@ -343,6 +354,7 @@ class Server(Resource): return list(self.interfaces.all().order_by('bus_address')) def release(self): + self.bundle = None self.booked = False self.save() @@ -408,7 +420,7 @@ class Network(models.Model): class PhysicalNetwork(models.Model): vlan_id = models.IntegerField() generic_network = models.ForeignKey(Network, on_delete=models.CASCADE) - bundle = models.ForeignKey(ResourceBundle, null=True, on_delete=models.CASCADE) + bundle = models.ForeignKey(ResourceBundle, null=True, blank=True, on_delete=models.CASCADE) def get_configuration(self, state): """ diff --git a/src/resource_inventory/pdf_templater.py b/src/resource_inventory/pdf_templater.py index 86d84aa..c4b22fe 100644 --- a/src/resource_inventory/pdf_templater.py +++ b/src/resource_inventory/pdf_templater.py @@ -22,7 +22,11 @@ class PDFTemplater: template = "dashboard/pdf.yaml" info = {} info['details'] = cls.get_pdf_details(booking.resource) - info['jumphost'] = cls.get_pdf_jumphost(booking) + try: + info['jumphost'] = cls.get_pdf_jumphost(booking) + except Exception: + # filling in jumphost info can be optional in some cases, this shouldn't be a hard error + info['jumphost'] = {} info['nodes'] = cls.get_pdf_nodes(booking) return render_to_string(template, context=info) diff --git a/src/templates/akraino/booking/quick_deploy.html b/src/templates/akraino/booking/quick_deploy.html index af9b3d3..c3e519f 100644 --- a/src/templates/akraino/booking/quick_deploy.html +++ b/src/templates/akraino/booking/quick_deploy.html @@ -6,7 +6,8 @@ Please select a host type you wish to book. Only available types are shown. More information can be found here: - <a href="https://wiki.akraino.org/display/AK/Shared+Community+Lab">Akraino Wiki</a> + <a href="https://wiki.akraino.org/display/AK/Shared+Community+Lab">Akraino Wiki</a>. + If something isn't working right, let us know <a href="mailto:{{contact_email}}"> here! </a> </p> {% endblock form-text %} {% block collab %} diff --git a/src/templates/base/booking/booking_detail.html b/src/templates/base/booking/booking_detail.html index 4b70f69..24a654c 100644 --- a/src/templates/base/booking/booking_detail.html +++ b/src/templates/base/booking/booking_detail.html @@ -159,7 +159,8 @@ <div class="card mb-3"> <div class="card-header d-flex"> <h4 class="d-inline">Deployment Progress</h4> - <p>These are the different tasks that have to be completed before your deployment is ready</p> + <p>These are the different tasks that have to be completed before your deployment is ready. + If this is taking a really long time, let us know <a href="mailto:{{contact_email}}">here!</a></p> <button data-toggle="collapse" data-target="#panel_tasks" class="btn btn-outline-secondary ml-auto">Expand</button> </div> <div class="collapse show" id="panel_tasks"> diff --git a/src/templates/base/booking/quick_deploy.html b/src/templates/base/booking/quick_deploy.html index e4b9431..c954073 100644 --- a/src/templates/base/booking/quick_deploy.html +++ b/src/templates/base/booking/quick_deploy.html @@ -9,7 +9,10 @@ <div class="row mx-0 px-0"> <div class="col-12 mx-0 px-0 mt-2"> {% block form-text %} - <p class="my-0">Please select a host type you wish to book. Only available types are shown.</p> + <p class="my-0"> + Please select a host type you wish to book. Only available types are shown. + If something isn't working right, let us know <a href="mailto:{{contact_email}}"> here! </a> + </p> {% endblock form-text %} {% bootstrap_field form.filter_field show_label=False %} </div> diff --git a/src/templates/base/dashboard/landing.html b/src/templates/base/dashboard/landing.html index ed50638..ecb12c6 100644 --- a/src/templates/base/dashboard/landing.html +++ b/src/templates/base/dashboard/landing.html @@ -16,12 +16,27 @@ {% csrf_token %} <div class="row"> - <!-- About us --> <div class="col-12 col-lg-6 mb-4"> + <!-- About us --> <h2 class="border-bottom">About Us</h2> {% block about_us %} <p>Here is some information about us!</p> {% endblock about_us %} + {% block welcome_back %} + {% if user.is_authenticated %} + <h2 class="border-bottom">Welcome Back!</h2> + {% if bookings %} + <h5> These are your current bookings: </h5> + <ul style="list-style: none;"> + {% for book in bookings %} + <li><a href="/booking/detail/{{ book.id }}/">{{ book.purpose }}</a></li> + {% endfor %} + </ul> + {% else %} + <h5> You have no current bookings <h5> + {% endif %} + {% endif %} + {% endblock welcome_back %} </div> <!-- Get started --> diff --git a/src/templates/base/workflow/viewport-base.html b/src/templates/base/workflow/viewport-base.html index d08145c..d9648c2 100644 --- a/src/templates/base/workflow/viewport-base.html +++ b/src/templates/base/workflow/viewport-base.html @@ -29,6 +29,11 @@ </nav> </div> </div> + <div class=”row”> + <div class=”col-xs-6 col-md-4”> + Is something not working right? Let us know <a href="mailto::{{contact_email}}"> here! </a> + </div> +</div> <!-- Top header --> <div class="row"> <div class="col"> diff --git a/src/templates/laas/dashboard/landing.html b/src/templates/laas/dashboard/landing.html index a8e0ff8..fc6b3e3 100644 --- a/src/templates/laas/dashboard/landing.html +++ b/src/templates/laas/dashboard/landing.html @@ -9,4 +9,4 @@ book a whole block of servers with customized layer2 networks (e.g. a Pharos Pod). Read more here: <a href="https://wiki.opnfv.org/x/HAE-Ag" target="_blank">LaaS Wiki</a></p> -{% endblock about_us %} +{% endblock %}
\ No newline at end of file diff --git a/src/workflow/views.py b/src/workflow/views.py index 9666d72..fb311b7 100644 --- a/src/workflow/views.py +++ b/src/workflow/views.py @@ -10,6 +10,7 @@ from django.http import HttpResponse from django.shortcuts import render +from account.models import Lab import uuid @@ -73,7 +74,12 @@ def viewport_view(request): if request.method != 'GET': return HttpResponse(status=405) - return render(request, 'workflow/viewport-base.html') + + context = { + 'contact_email': Lab.objects.get(name="UNH_IOL").contact_email + } + + return render(request, 'workflow/viewport-base.html', context) def create_workflow(request): |