aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJustin Choquette <jchoquette@iol.unh.edu>2023-09-27 17:03:38 -0400
committerJustin Choquette <jchoquette@iol.unh.edu>2023-10-19 18:10:17 -0400
commitaff53e072502d63d8002d9c83213ce7f9d12c352 (patch)
tree3a30adca1fe7c958ddc092dbf7b9fa24259dd923 /src
parent1947d40115c7b13f8617ea92078a6f910d6bc799 (diff)
user subsystem clean up
Change-Id: Ia59bb7c1e4412693f55cdcaf9607bcb4158850ae Signed-off-by: Justin Choquette <jchoquette@iol.unh.edu>
Diffstat (limited to 'src')
-rw-r--r--src/account/urls.py4
-rw-r--r--src/account/views.py201
-rw-r--r--src/api/models.py13
-rw-r--r--src/api/urls.py18
-rw-r--r--src/api/utils.py132
-rw-r--r--src/api/views.py482
-rw-r--r--src/booking/urls.py4
-rw-r--r--src/booking/views.py84
-rw-r--r--src/dashboard/forms.py46
-rw-r--r--src/dashboard/tasks.py5
-rw-r--r--src/dashboard/views.py84
-rw-r--r--src/laas_dashboard/settings.py1
-rw-r--r--src/laas_dashboard/urls.py1
-rw-r--r--src/liblaas/__init__.py8
-rw-r--r--src/liblaas/endpoints.py254
-rw-r--r--src/liblaas/tests.py65
-rw-r--r--src/liblaas/urls.py24
-rw-r--r--src/liblaas/utils.py63
-rw-r--r--src/liblaas/views.py205
-rw-r--r--src/resource_inventory/views.py73
-rw-r--r--src/static/js/workflows/book-a-pod.js9
-rw-r--r--src/static/js/workflows/workflow.js128
-rw-r--r--src/templates/base/account/booking_list.html27
-rw-r--r--src/templates/base/account/resource_list.html24
-rw-r--r--src/templates/base/account/settings.html181
-rw-r--r--src/templates/base/booking/booking_detail.html28
-rw-r--r--src/templates/base/dashboard/landing.html89
-rw-r--r--src/templates/base/workflow/book_a_pod.html67
-rw-r--r--src/workflow/views.py85
29 files changed, 1237 insertions, 1168 deletions
diff --git a/src/account/urls.py b/src/account/urls.py
index 35ef43b..6865ded 100644
--- a/src/account/urls.py
+++ b/src/account/urls.py
@@ -34,8 +34,6 @@ from account.views import (
account_resource_view,
account_booking_view,
account_detail_view,
- template_delete_view,
- booking_cancel_view,
account_settings_view
)
@@ -46,8 +44,6 @@ urlpatterns = [
url(r'^login/$', OIDCLoginView.as_view(), name='login'),
url(r'^logout/$', LogoutView.as_view(), name='logout'),
url(r'^my/resources/$', account_resource_view, name='my-resources'),
- path('my/resources/delete/<int:resource_id>', template_delete_view),
url(r'^my/bookings/$', account_booking_view, name='my-bookings'),
- path('my/bookings/cancel/<int:booking_id>', booking_cancel_view),
url(r'^my/$', account_detail_view, name='my-account'),
]
diff --git a/src/account/views.py b/src/account/views.py
index a392349..cae2695 100644
--- a/src/account/views.py
+++ b/src/account/views.py
@@ -9,67 +9,72 @@
##############################################################################
-import json
import os
-
+import pytz
+import json
from django.utils import timezone
-from django.contrib import messages
from django.contrib.auth import logout
-from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.models import User
from django.urls import reverse
from django.http import HttpResponse
from django.shortcuts import get_object_or_404, redirect, render
-from django.utils.decorators import method_decorator
-from django.views.generic import RedirectView, TemplateView, UpdateView
+from django.views.generic import RedirectView
from django.shortcuts import render
-from api.utils import ipa_set_ssh, ipa_query_user, ipa_set_company
-from dashboard.forms import SetCompanyForm, SetSSHForm
-from rest_framework.authtoken.models import Token
from mozilla_django_oidc.auth import OIDCAuthenticationBackend
-
-from account.forms import AccountPreferencesForm
from account.models import UserProfile
from booking.models import Booking
-from api.views import delete_template, liblaas_templates
from workflow.views import login
+from liblaas.views import user_get_user,user_set_company, user_set_ssh, \
+template_list_templates, template_delete_template, booking_end_booking
+
def account_settings_view(request):
if request.method == "GET":
if not request.user.is_authenticated:
return login(request)
+
profile = UserProfile.objects.get(user=request.user)
- if (not profile or profile.ipa_username == "" or profile.ipa_username == None):
- return redirect("dashboard:index")
- ipa_user = ipa_query_user(profile.ipa_username)
+
+ ipa_user = user_get_user(profile.ipa_username)
template = "account/settings.html"
+
context = {
- "preference_form": AccountPreferencesForm(instance=profile),
- "company_form": SetCompanyForm(initial={'company': ipa_user['ou']}),
+ "timezone": profile.timezone,
+ "timezones": [(x) for x in pytz.common_timezones],
+ "public": profile.public_user,
+ "company": ipa_user["ou"] if 'ou' in ipa_user else "",
"existing_keys": ipa_user['ipasshpubkey'] if 'ipasshpubkey' in ipa_user else [],
"ipa_username": profile.ipa_username
}
- return render(request, template, context)
- if request.method == 'POST':
- data = request.POST
+ return render(request, template, context)
- print("data is", data)
- # User profile
- profile = UserProfile.objects.get(user=request.user)
- profile.public_user = "public_user" in data
+ if request.method == "POST":
+ return account_update_settings(request)
+
+ return HttpResponse(status=405)
+
+def account_update_settings(request):
+ if request.method != "POST":
+ return HttpResponse(status=405)
+
+ data = json.loads(request.body.decode('utf-8'))
+ profile = UserProfile.objects.get(user=request.user)
+ profile.public_user = data["public_user"]
+
+ if data["timezone"] in pytz.common_timezones:
profile.timezone = data["timezone"]
- profile.save()
+ r1 = user_set_company(profile.ipa_username, data["company"])
+ r2 = user_set_ssh(profile, data["keys"])
- # IPA
- ipa_set_company(profile, data['company'])
- ipa_set_ssh(profile, data['ssh_key_list'].split(","))
+ if (r1 and r2):
+ profile.save()
+ return HttpResponse(status=200)
- return redirect("account:settings")
- return HttpResponse(status=405)
+ return HttpResponse(status=500)
class MyOIDCAB(OIDCAuthenticationBackend):
def filter_users_by_claims(self, claims):
@@ -132,20 +137,19 @@ def account_detail_view(request):
def account_resource_view(request):
"""
Display a user's resources.
-
- gathers a users genericResoureBundles and
- turns them into displayable objects
"""
- if not request.user.is_authenticated:
- return render(request, "dashboard/login.html", {'title': 'Authentication Required'})
- template = "account/resource_list.html"
if request.method == "GET":
+
+ if not request.user.is_authenticated:
+ return render(request, "dashboard/login.html", {'title': 'Authentication Required'})
+ template = "account/resource_list.html"
+
profile = UserProfile.objects.get(user=request.user)
- if (not profile or profile.ipa_username == "" or profile.ipa_username == None):
+ if (not profile or profile.ipa_username == None):
return redirect("dashboard:index")
- r = liblaas_templates(request)
- usable_templates = r.json()
+
+ usable_templates = template_list_templates(profile.ipa_username)
user_templates = [ t for t in usable_templates if t["owner"] == profile.ipa_username]
context = {
"templates": user_templates,
@@ -154,59 +158,84 @@ def account_resource_view(request):
return render(request, template, context=context)
if request.method == "POST":
- return delete_template(request)
+ return account_delete_resource(request)
return HttpResponse(status_code=405)
+
+def account_delete_resource(request):
+ if request.method != "POST":
+ return HttpResponse(status=405)
+
+ data = json.loads(request.body.decode('utf-8'))
+ template_id = data["template_id"]
+
+ if not canDeleteTemplate(template_id, UserProfile.objects.get(user=request.user).ipa_username):
+ return HttpResponse(status=401)
+
+ response = template_delete_template(template_id)
+ if (response.status_code == 200):
+ return HttpResponse(status=200)
+
+
+ return HttpResponse(status=500)
+
+def canDeleteTemplate(template_id, ipa_username):
+ usable_templates = template_list_templates(ipa_username)
+ for t in usable_templates:
+ if (t['id'] == template_id and t['owner'] == ipa_username):
+ return True
+
+ return False
def account_booking_view(request):
- if not request.user.is_authenticated:
- return render(request, "dashboard/login.html", {'title': 'Authentication Required'})
- profile = UserProfile.objects.get(user=request.user)
- if (not profile or profile.ipa_username == "" or profile.ipa_username == None):
- return redirect("dashboard:index")
- template = "account/booking_list.html"
- bookings = list(Booking.objects.filter(owner=request.user, end__gt=timezone.now()).order_by("-start"))
- my_old_bookings = Booking.objects.filter(owner=request.user, end__lt=timezone.now()).order_by("-start")
- collab_old_bookings = request.user.collaborators.filter(end__lt=timezone.now()).order_by("-start")
- expired_bookings = list(my_old_bookings.union(collab_old_bookings))
- collab_bookings = list(request.user.collaborators.filter(end__gt=timezone.now()).order_by("-start"))
- context = {
- "title": "My Bookings",
- "bookings": bookings,
- "collab_bookings": collab_bookings,
- "expired_bookings": expired_bookings
- }
- return render(request, template, context=context)
-
-
-
-def template_delete_view(request, resource_id=None):
- # if not request.user.is_authenticated:
- # return HttpResponse(status=403)
- # template = get_object_or_404(ResourceTemplate, pk=resource_id)
- # if not request.user.id == template.owner.id:
- # return HttpResponse(status=403)
- # if Booking.objects.filter(resource__template=template, end__gt=timezone.now()).exists():
- # return HttpResponse(status=403)
- # template.public = False
- # template.temporary = True
- # template.save()
- # return HttpResponse(status=200)
- return HttpResponse(status=404) # todo - LL Integration
-
-
-def booking_cancel_view(request, booking_id=None):
- if not request.user.is_authenticated:
- return HttpResponse('no') # 403?
- booking = get_object_or_404(Booking, pk=booking_id)
- if not request.user.id == booking.owner.id:
- return HttpResponse('no') # 403?
-
- if booking.end < timezone.now(): # booking already over
- return HttpResponse('')
+ if request.method == 'GET':
+ if not request.user.is_authenticated:
+ return render(request, "dashboard/login.html", {'title': 'Authentication Required'})
+
+ profile = UserProfile.objects.get(user=request.user)
+ if (not profile or profile.ipa_username == None):
+ return redirect("dashboard:index")
+
+ template = "account/booking_list.html"
+ bookings = list(Booking.objects.filter(owner=request.user, end__gt=timezone.now()).order_by("-start"))
+ my_old_bookings = Booking.objects.filter(owner=request.user, end__lt=timezone.now()).order_by("-start")
+ collab_old_bookings = request.user.collaborators.filter(end__lt=timezone.now()).order_by("-start")
+ expired_bookings = list(my_old_bookings.union(collab_old_bookings))
+ collab_bookings = list(request.user.collaborators.filter(end__gt=timezone.now()).order_by("-start"))
+ context = {
+ "title": "My Bookings",
+ "bookings": bookings,
+ "collab_bookings": collab_bookings,
+ "expired_bookings": expired_bookings
+ }
+ return render(request, template, context=context)
+
+ if request.method == 'POST':
+ return account_cancel_booking(request)
+
+ return HttpResponse(status=405)
+def account_cancel_booking(request):
+ if request.method != "POST":
+ return HttpResponse(status=405)
+
+ data = json.loads(request.body.decode('utf-8'))
+ booking_id = data["booking_id"]
+ booking = Booking.objects.get(id=booking_id)
+ if not (booking.owner == request.user):
+ return HttpResponse(401)
+
+ # LibLaaS
+ response = booking_end_booking(booking.aggregateId)
+
+ # Dashboard
booking.end = timezone.now()
booking.save()
- return HttpResponse('')
+
+ if (response.status_code == 200):
+ return HttpResponse(status=200)
+
+ return HttpResponse(status=500)
+
diff --git a/src/api/models.py b/src/api/models.py
index ca33ed8..86d2560 100644
--- a/src/api/models.py
+++ b/src/api/models.py
@@ -10,23 +10,12 @@
from django.contrib.auth.models import User
from django.db import models
-from django.core.exceptions import PermissionDenied, ValidationError
+from django.core.exceptions import PermissionDenied
from django.shortcuts import get_object_or_404
from django.contrib.postgres.fields import JSONField
-from django.http import HttpResponseNotFound
-from django.urls import reverse
from django.utils import timezone
-
import json
-import uuid
-import yaml
-import re
-
-from booking.models import Booking
from account.models import Downtime, UserProfile
-from dashboard.utils import AbstractModelQuery
-
-
class LabManagerTracker:
diff --git a/src/api/urls.py b/src/api/urls.py
index 755b61f..a5254ce 100644
--- a/src/api/urls.py
+++ b/src/api/urls.py
@@ -35,18 +35,9 @@ from api.views import (
lab_users,
lab_user,
GenerateTokenView,
- user_bookings,
- specific_booking,
extend_booking,
- make_booking,
list_labs,
all_users,
- booking_details,
- ipa_create_account,
- ipa_confirm_account,
- ipa_set_company_from_workflow,
- ipa_add_ssh_from_workflow,
- ipa_conflict_account
)
urlpatterns = [
@@ -56,19 +47,10 @@ urlpatterns = [
path('labs/<slug:lab_name>/users', lab_users),
path('labs/<slug:lab_name>/users/<int:user_id>', lab_user),
- path('booking', user_bookings),
- path('booking/<int:booking_id>', specific_booking),
path('booking/<int:booking_id>/extendBooking/<int:days>', extend_booking),
- path('booking/makeBooking', make_booking),
- path('booking/<int:booking_id>/details', booking_details),
path('users', all_users),
path('labs', list_labs),
- path('ipa/create', ipa_create_account),
- path('ipa/confirm', ipa_confirm_account),
- path('ipa/conflict', ipa_conflict_account),
- path('ipa/workflow-company', ipa_set_company_from_workflow),
- path('ipa/workflow-ssh', ipa_add_ssh_from_workflow),
url(r'^token$', GenerateTokenView.as_view(), name='generate_token'),
]
diff --git a/src/api/utils.py b/src/api/utils.py
deleted file mode 100644
index 9a9c260..0000000
--- a/src/api/utils.py
+++ /dev/null
@@ -1,132 +0,0 @@
-# These functions are called from views and perform the actual request to LibLaaS
-
-import json
-from django.http.response import JsonResponse, HttpResponse
-import requests
-import os
-
-from dashboard.forms import *
-liblaas_base_url = os.environ.get("LIBLAAS_BASE_URL")
-
-# IPA Stuff
-def ipa_query_user(ipa_username):
- url = liblaas_base_url + "user/" + ipa_username
- print("Getting ipa user for", ipa_username, url)
- try:
- response = requests.get(url)
- data = response.json()
- print("ipa user is", data)
- return data # Expects a dict
- except Exception as e:
- print(e)
- return None
-
-# Queries for an IPA user using dashboard username
-# Returns a result
-def get_ipa_migration_form(user, profile):
- # ipa_user = ipa_query_user(str(dashboard_user))
- # if (ipa_user and ipa_user.mail is )
- # pass
- dashboard_username = str(user)
- dashboard_email = profile.email_addr
- first_name = user.first_name
- last_name = user.last_name
-
- ipa_user = ipa_query_user(dashboard_username)
- print("Attempting auto migration with", dashboard_username, dashboard_email, ipa_user)
- if (ipa_user):
- if (dashboard_email == ipa_user["mail"]):
- # User is found and email match
- print("User is found and email match")
- return {
- "form": ReadOnlyIPAAccountForm(initial={'ipa_username': ipa_user['uid'],'first_name': ipa_user["givenname"], 'last_name': ipa_user["sn"], 'email': ipa_user["mail"], 'company': ipa_user["ou"]}),
- "message": "We have located the following IPA account matching your username and email. Please confirm to link your account. You may change these details at any time.",
- "action": "api/ipa/confirm",
- "button": "Link"
- }
-
- else:
- # User is found and emails don't match
- print("User is found and emails don't match")
- return {
- "form": ConflictIPAAcountForm(initial={'first_name': first_name, 'last_name': last_name, 'email': dashboard_email}),
- "message": "Our records indicate that you do not currently have an account in our IPA system, or your emails do not match. Please enter the following details to enroll your account.",
- "action": "/",
- "button": "Submit"
- }
- else:
- # User is not found
- print("User is not found")
- return {
- "form": NewIPAAccountForm(initial={'first_name': first_name, 'last_name': last_name, 'email': dashboard_email}),
- "message": "Our records indicate that you do not currently have an account in our IPA system, or your usernames do not match. Please enter the following details to enroll your account.",
- "action": "api/ipa/create",
- "button": "Submit"
- }
-
-# Removes leading and trailing white space from a list of ssh keys and returns the cleaned list
-def clean_ssh_keys(ssh_key_list):
- cleaned = []
- for key in ssh_key_list:
- cleaned.append(key.strip())
- return cleaned
-
-# Take a list of strings, sends it to liblaas, replacing the IPA keys with the new keys
-def ipa_set_ssh(user_profile, ssh_key_list):
- url = liblaas_base_url + "user/" + user_profile.ipa_username + "/ssh"
- ssh_key_list = clean_ssh_keys(ssh_key_list)
- try:
- requests.post(url, data=json.dumps(ssh_key_list), headers={'Content-Type': 'application/json'})
- return HttpResponse(status=200)
- except Exception as e:
- print(e)
- return HttpResponse(status=500)
-
-def ipa_set_company(user_profile, company_name):
- url = liblaas_base_url + "user/" + user_profile.ipa_username + "/company"
- print("Setting company with URL", url)
- try:
- requests.post(url, data=json.dumps(company_name), headers={'Content-Type': 'application/json'})
- return HttpResponse(status=200)
- except Exception as e:
- print(e)
- return HttpResponse(status=500)
-
-def get_booking_prereqs_validator(user_profile):
- ipa_user = None
- if (user_profile.ipa_username != None and user_profile.ipa_username != ""):
- ipa_user = ipa_query_user(user_profile.ipa_username)
-
- if ipa_user == None:
- print("No user")
- return {
- "form": None,
- "exists": "false",
- "action": "no user",
- "result": 0
- }
-
- if (not "ou" in ipa_user) or (ipa_user["ou"] == ""):
- print("Missing company")
- return {
- "form": SetCompanyForm(),
- "exists": "true",
- "action": "/api/ipa/workflow-company",
- "result": 1
- }
-
- if (not "ipasshpubkey" in ipa_user) or (ipa_user["ipasshpubkey"] == []):
- print("Missing SSH key")
- return {
- "form": SetSSHForm(),
- "exists": "true",
- "action": "/api/ipa/workflow-ssh",
- "result": 2,
- }
-
- return {
- "form": None,
- "exists": "false",
- "action": "",
- "result": -1
- } \ No newline at end of file
diff --git a/src/api/views.py b/src/api/views.py
index fab78ee..d4fa243 100644
--- a/src/api/views.py
+++ b/src/api/views.py
@@ -9,10 +9,6 @@
##############################################################################
import json
-import math
-import os
-import traceback
-import sys
from datetime import timedelta
from django.contrib.auth.decorators import login_required
@@ -20,25 +16,14 @@ from django.shortcuts import redirect, get_object_or_404
from django.utils.decorators import method_decorator
from django.utils import timezone
from django.views import View
-from django.http import HttpResponseNotFound
from django.http.response import JsonResponse, HttpResponse
-import requests
-from api.utils import ipa_query_user, ipa_set_ssh, ipa_set_company
from rest_framework.authtoken.models import Token
from django.views.decorators.csrf import csrf_exempt
-from django.core.exceptions import ObjectDoesNotExist
-from django.db.models import Q
-from django.contrib.auth.models import User
from api.forms import DowntimeForm
from account.models import UserProfile, Lab
from booking.models import Booking
from api.models import LabManagerTracker,AutomationAPIManager, APILog
-from api.utils import get_booking_prereqs_validator
-
-import yaml
-import uuid
-from deepmerge import Merger
"""
API views.
@@ -52,7 +37,6 @@ Most functions let you GET or POST to the same endpoint, and
the correct thing will happen
"""
-liblaas_base_url = os.environ.get('LIBLAAS_BASE_URL')
@method_decorator(login_required, name='dispatch')
class GenerateTokenView(View):
def get(self, request, *args, **kwargs):
@@ -141,7 +125,6 @@ def auth_and_log(request, endpoint):
token = Token.objects.get(key=user_token)
except Token.DoesNotExist:
token = None
- # Added logic to detect malformed token
if len(str(user_token)) != 40:
response = HttpResponse('Malformed Token', status=401)
else:
@@ -181,44 +164,6 @@ Booking API Views
"""
-def user_bookings(request):
- # token = auth_and_log(request, 'booking')
-
- # if isinstance(token, HttpResponse):
- # return token
-
- # bookings = Booking.objects.filter(owner=token.user, end__gte=timezone.now())
- # output = [AutomationAPIManager.serialize_booking(booking)
- # for booking in bookings]
- # return JsonResponse(output, safe=False)
- # todo - LL Integration
- return HttpResponse(status=404)
-
-
-@csrf_exempt
-def specific_booking(request, booking_id=""):
- # token = auth_and_log(request, 'booking/{}'.format(booking_id))
-
- # if isinstance(token, HttpResponse):
- # return token
-
- # booking = get_object_or_404(Booking, pk=booking_id, owner=token.user)
- # if request.method == "GET":
- # sbooking = AutomationAPIManager.serialize_booking(booking)
- # return JsonResponse(sbooking, safe=False)
-
- # if request.method == "DELETE":
-
- # if booking.end < timezone.now():
- # return HttpResponse("Booking already over", status=400)
-
- # booking.end = timezone.now()
- # booking.save()
- # return HttpResponse("Booking successfully cancelled")
- # todo - LL Integration
- return HttpResponse(status=404)
-
-
@csrf_exempt
def extend_booking(request, booking_id="", days=""):
token = auth_and_log(request, 'booking/{}/extendBooking/{}'.format(booking_id, days))
@@ -243,113 +188,6 @@ def extend_booking(request, booking_id="", days=""):
return HttpResponse("Booking successfully extended")
-
-@csrf_exempt
-def make_booking(request):
- print("received call to make_booking")
- data = json.loads(request.body)
- print("incoming data is ", data)
-
- # todo - test this
- ipa_users = []
- ipa_users.append(UserProfile.objects.get(user=request.user).ipa_username) # add owner's ipa username to list of allowed users to be sent to liblaas
-
- for user in data["allowed_users"]:
- collab_profile = UserProfile.objects.get(user=User.objects.get(username=user))
- prereq_validator = get_booking_prereqs_validator(collab_profile)
-
- if prereq_validator["result"] == -1:
- # All good
- ipa_users.append(collab_profile.ipa_username)
- else:
- message = "There is an issue with one of your chosen collaborators."
- if prereq_validator["result"] == 0:
- # No IPA username
- message = str(collab_profile) + " has not linked their IPA account yet. Please ask them to log into the LaaS dashboard, or remove them from the booking to continue."
- elif prereq_validator["result"] == 1:
- # No Company
- message = str(collab_profile) + " has not set their company yet. Please ask them to log into the LaaS dashboard, go to the settings page and add it. Otherwise, remove them from the booking to continue."
- elif prereq_validator["result"] == 2:
- # No SSH
- message = str(collab_profile) + " has not added an SSH public key yet. Please ask them to log into the LaaS dashboard, go to the settings page and add it. Otherwise, remove them from the booking to continue."
- return JsonResponse(
- data={"message": message, "error": True},
- status=200,
- safe=False
- )
-
- bookingBlob = {
- "template_id": data["template_id"],
- "allowed_users": ipa_users,
- "global_cifile": data["global_cifile"],
- "metadata": {
- "booking_id": None, # fill in after creating django object
- "owner": UserProfile.objects.get(user=request.user).ipa_username,
- "lab": "UNH_IOL",
- "purpose": data["metadata"]["purpose"],
- "project": data["metadata"]["project"],
- "length": int(data["metadata"]["length"])
- },
- "origin": "anuket" if os.environ.get("TEMPLATE_OVERRIDE_DIR") == 'laas' else "lfedge"
- }
-
- print("allowed users are ", bookingBlob["allowed_users"])
- try:
- booking = Booking.objects.create(
- purpose=bookingBlob["metadata"]["purpose"],
- project=bookingBlob["metadata"]['project'],
- lab=Lab.objects.get(name='UNH_IOL'),
- owner=request.user,
- start=timezone.now(),
- end=timezone.now() + timedelta(days=int(bookingBlob["metadata"]['length'])),
- )
- print("successfully created booking object with id ", booking.id)
-
- # Now add collabs
- for c in list(data["allowed_users"]):
- booking.collaborators.add(User.objects.get(username=c))
- print("successfully added collabs")
-
- # Now create it in liblaas
- bookingBlob["metadata"]["booking_id"] = str(booking.id)
- liblaas_endpoint = liblaas_base_url + 'booking/create'
- liblaas_response = requests.post(liblaas_endpoint, data=json.dumps(bookingBlob), headers={'Content-Type': 'application/json'})
- print("response from liblaas is", vars(liblaas_response))
- if liblaas_response.status_code != 200:
- print("received non success from liblaas, deleting booking from dashboard")
- booking.delete()
- return JsonResponse(
- data={},
- status=500,
- safe=False
- )
- aggregateId = json.loads(liblaas_response.content)
- print("successfully created aggregate in liblaas")
-
- # Now update the agg_id
- booking.aggregateId = aggregateId
- booking.save()
- print("sucessfully updated aggreagateId in booking object")
-
- return JsonResponse(
- data = {"bookingId": booking.id},
- status=200,
- safe=False
- )
- except Exception as error:
- print(error)
- return JsonResponse(
- data={},
- status=500,
- safe=False
- )
-
-
-"""
-Resource Inventory API Views
-"""
-# todo - LL Integration
-
"""
User API Views
"""
@@ -390,323 +228,3 @@ def list_labs(request):
return JsonResponse(lab_list, safe=False)
-
-"""
-Booking Details API Views
-"""
-
-
-def booking_details(request, booking_id=""):
- # token = auth_and_log(request, 'booking/{}/details'.format(booking_id))
-
- # if isinstance(token, HttpResponse):
- # return token
-
- # booking = get_object_or_404(Booking, pk=booking_id, owner=token.user)
-
- # # overview
- # overview = {
- # 'username': GeneratedCloudConfig._normalize_username(None, str(token.user)),
- # 'purpose': booking.purpose,
- # 'project': booking.project,
- # 'start_time': booking.start,
- # 'end_time': booking.end,
- # 'pod_definitions': booking.resource.template,
- # 'lab': booking.lab
- # }
-
- # # deployment progress
- # task_list = []
- # for task in booking.job.get_tasklist():
- # task_info = {
- # 'name': str(task),
- # 'status': 'DONE',
- # 'lab_response': 'No response provided (yet)'
- # }
- # if task.status < 100:
- # task_info['status'] = 'PENDING'
- # elif task.status < 200:
- # task_info['status'] = 'IN PROGRESS'
-
- # if task.message:
- # if task.type_str == "Access Task" and request.user.id != task.config.user.id:
- # task_info['lab_response'] = '--secret--'
- # else:
- # task_info['lab_response'] = str(task.message)
- # task_list.append(task_info)
-
- # # pods
- # pod_list = []
- # for host in booking.resource.get_resources():
- # pod_info = {
- # 'hostname': host.config.name,
- # 'machine': host.name,
- # 'role': '',
- # 'is_headnode': host.config.is_head_node,
- # 'image': host.config.image,
- # 'ram': {'amount': str(host.profile.ramprofile.first().amount) + 'G', 'channels': host.profile.ramprofile.first().channels},
- # 'cpu': {'arch': host.profile.cpuprofile.first().architecture, 'cores': host.profile.cpuprofile.first().cores, 'sockets': host.profile.cpuprofile.first().cpus},
- # 'disk': {'size': str(host.profile.storageprofile.first().size) + 'GiB', 'type': host.profile.storageprofile.first().media_type, 'mount_point': host.profile.storageprofile.first().name},
- # 'interfaces': [],
- # }
- # try:
- # pod_info['role'] = host.template.opnfvRole
- # except Exception:
- # pass
- # for intprof in host.profile.interfaceprofile.all():
- # int_info = {
- # 'name': intprof.name,
- # 'speed': intprof.speed
- # }
- # pod_info['interfaces'].append(int_info)
- # pod_list.append(pod_info)
-
- # # diagnostic info
- # diagnostic_info = {
- # 'job_id': booking.job.id,
- # 'ci_files': '',
- # 'pods': []
- # }
- # for host in booking.resource.get_resources():
- # pod = {
- # 'host': host.name,
- # 'configs': [],
-
- # }
- # for ci_file in host.config.cloud_init_files.all():
- # ci_info = {
- # 'id': ci_file.id,
- # 'text': ci_file.text
- # }
- # pod['configs'].append(ci_info)
- # diagnostic_info['pods'].append(pod)
-
- # details = {
- # 'overview': overview,
- # 'deployment_progress': task_list,
- # 'pods': pod_list,
- # 'diagnostic_info': diagnostic_info,
- # 'pdf': booking.pdf
- # }
- # return JsonResponse(str(details), safe=False)
- # todo - LL Integration
- return HttpResponse(status=404)
-
-
-""" Forwards a request to the LibLaaS API from a workflow """
-def liblaas_request(request) -> JsonResponse:
- print("handing liblaas request... ", request.method)
- print(request.body)
- if request.method != 'POST':
- return JsonResponse({"error" : "405 Method not allowed"})
-
- post_data = json.loads(request.body)
- print("post data is " + str(post_data))
- http_method = post_data["method"]
- liblaas_endpoint = post_data["endpoint"]
- payload = post_data["workflow_data"]
- # Fill in actual username
- liblaas_endpoint = liblaas_endpoint.replace("[username]", UserProfile.objects.get(user=request.user).ipa_username)
- liblaas_endpoint = liblaas_base_url + liblaas_endpoint
- print("processed endpoint is ", liblaas_endpoint)
-
- if (http_method == "GET"):
- response = requests.get(liblaas_endpoint, data=json.dumps(payload))
- elif (http_method == "POST"):
- response = requests.post(liblaas_endpoint, data=json.dumps(payload), headers={'Content-Type': 'application/json'})
- elif (http_method == "DELETE"):
- response = requests.delete(liblaas_endpoint, data=json.dumps(payload))
- elif (http_method == "PUT"):
- response = requests.put(liblaas_endpoint, data=json.dumps(payload))
- else:
- return JsonResponse(
- data={},
- status=405,
- safe=False
- )
- try:
- return JsonResponse(
- data=json.loads(response.content.decode('utf8')),
- status=200,
- safe=False
- )
- except Exception as e:
- print("fail")
- print(e)
- return JsonResponse(
- data = {},
- status=500,
- safe=False
- )
-
-def liblaas_templates(request):
- liblaas_url = liblaas_base_url + "template/list/" + UserProfile.objects.get(user=request.user).ipa_username
- print("api call to " + liblaas_url)
- return requests.get(liblaas_url)
-
-def delete_template(request):
- endpoint = json.loads(request.body)["endpoint"]
- liblaas_url = liblaas_base_url + endpoint
- print("api call to ", liblaas_url)
- try:
- response = requests.delete(liblaas_url)
- return JsonResponse(
- data={},
- status=response.status_code,
- safe=False
- )
- except:
- return JsonResponse(
- data={},
- status=500,
- safe=False
- )
-
-def booking_status(request, booking_id):
- print("booking id is", booking_id)
- statuses = get_booking_status(Booking.objects.get(id=booking_id))
- return HttpResponse(json.dumps(statuses))
-
-def get_booking_status(bookingObject):
- liblaas_url = liblaas_base_url + "booking/" + bookingObject.aggregateId + "/status"
- print("Getting booking status at: ", liblaas_url)
- response = requests.get(liblaas_url)
- try:
- return json.loads(response.content)
- except:
- print("failed to get status")
- return {}
-
-def liblaas_end_booking(aggregateId):
- liblaas_url = liblaas_base_url + "booking/" + str(aggregateId) + "/end"
- print("Ending booking at ", liblaas_url)
- response = requests.delete(liblaas_url)
- try:
- return response
- except:
- print("failed to end booking")
- return HttpResponse(status=500)
-
-def ipa_create_account(request):
- # Called when no username was found
- # Only allow if user does not have a linked ipa account
- profile = UserProfile.objects.get(user=request.user)
- if (profile.ipa_username):
- return HttpResponse(status=401)
-
- post_data = request.POST
- user = {
- 'uid': str(request.user),
- 'givenname': post_data['first_name'],
- 'sn': post_data['last_name'],
- 'cn': post_data['first_name'] + " " + post_data['last_name'],
- 'mail': post_data['email'],
- 'ou': post_data['company'],
- 'random': True
- }
-
- try:
- response = requests.post(liblaas_base_url + "user/create", data=json.dumps(user), headers={'Content-Type': 'application/json'})
- profile.ipa_username = user['uid']
- print("Saving ipa username", profile.ipa_username)
- profile.save()
- return redirect("dashboard:index")
- except Exception as e:
- print(e)
- return redirect("dashboard:index")
-
-def ipa_confirm_account(request):
- # Called when username was found and email matches
- profile = UserProfile.objects.get(user=request.user)
- if (profile.ipa_username):
- return HttpResponse(status=401)
-
- profile.ipa_username = str(request.user)
- print("Saving ipa username", profile.ipa_username)
- profile.save()
- return redirect("dashboard:index")
-
-def ipa_conflict_account(request):
- # Called when username was found but emails do not match
- # Need to ask user to input alternate username
- # To verify username is not taken, call query_username and accept if returns None
- print("ipa conflict account")
- profile = UserProfile.objects.get(user=request.user)
- print("profile is", profile)
- if (profile.ipa_username):
- return HttpResponse(status=401)
-
- post_data = request.POST
- user = {
- 'uid': post_data['ipa_username'],
- 'givenname': post_data['first_name'],
- 'sn': post_data['last_name'],
- 'cn': post_data['first_name'] + " " + post_data['last_name'],
- 'mail': post_data['email'],
- 'ou': post_data['company'],
- 'random': True,
- }
-
- try:
- response = requests.post(liblaas_base_url + "user/create", data=json.dumps(user), headers={'Content-Type': 'application/json'})
- profile.ipa_username = user['uid']
- print("Saving ipa username", profile.ipa_username)
- profile.save()
- return redirect("dashboard:index")
- except Exception as e:
- print(e)
- return redirect("dashboard:index")
-
-def ipa_set_company_from_workflow(request):
- profile = UserProfile.objects.get(user=request.user)
- ipa_set_company(profile, request.POST["company"])
- return redirect("workflow:book_a_pod")
-
-def ipa_add_ssh_from_workflow(request):
- profile = UserProfile.objects.get(user=request.user)
- key_as_list = []
- key_as_list.append(request.POST["ssh_public_key"])
- ipa_set_ssh(profile, key_as_list)
- return redirect("workflow:book_a_pod")
-
-def list_hosts(request):
- if request.method != "GET":
- return HttpResponse(status=405)
-
- dashboard = 'lfedge' if liblaas_base_url == 'lfedge' else 'anuket'
-
- liblaas_url = os.environ.get('LIBLAAS_BASE_URL') + "flavor/hosts/" + dashboard
- print("Listing hosts at ", liblaas_url)
- response = requests.get(liblaas_url)
- try:
- return JsonResponse(
- data = json.loads(response.content),
- status=200,
- safe=False
- )
- except Exception as e:
- print("Failed to list hosts!", e)
- return JsonResponse(
- data = {},
- status=500,
- safe=False
- )
-
-def list_flavors(request):
- if (request.method) != "GET":
- return HttpResponse(status=405)
-
- response = requests.get(liblaas_base_url + "flavor")
- try:
- return JsonResponse(
- data = json.loads(response.content),
- status=200,
- safe=False
- )
- except Exception as e:
- print("Failed to list flavors!", e)
- return JsonResponse(
- data = {},
- status=500,
- safe=False
- )
diff --git a/src/booking/urls.py b/src/booking/urls.py
index c6b9d40..9784fc5 100644
--- a/src/booking/urls.py
+++ b/src/booking/urls.py
@@ -26,7 +26,6 @@ Including another URLconf
2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls'))
"""
from django.conf.urls import url
-from django.urls import path
from booking.views import (
booking_detail_view,
@@ -35,12 +34,9 @@ from booking.views import (
BookingListView,
)
-from api.views import booking_status
-
app_name = 'booking'
urlpatterns = [
url(r'^detail/(?P<booking_id>[0-9]+)/$', booking_detail_view, name='detail'),
- url(r'^detail/(?P<booking_id>[0-9]+)/status$', booking_status, name='detail'),
url(r'^(?P<booking_id>[0-9]+)/$', booking_detail_view, name='booking_detail'),
url(r'^delete/$', BookingDeleteView.as_view(), name='delete_prefix'),
url(r'^delete/(?P<booking_id>[0-9]+)/$', BookingDeleteView.as_view(), name='delete'),
diff --git a/src/booking/views.py b/src/booking/views.py
index df446d5..4fdd1db 100644
--- a/src/booking/views.py
+++ b/src/booking/views.py
@@ -9,18 +9,17 @@
##############################################################################
from django.contrib import messages, admin
+import json
+from django.contrib import messages
from django.shortcuts import get_object_or_404
-from django.http import JsonResponse, HttpResponse
from django.utils import timezone
-from django.views import View
from django.views.generic import TemplateView
from django.shortcuts import redirect, render
-from django.db.models import Q
-from django.urls import reverse
from account.models import Downtime, Lab
-from api.views import get_booking_status
from booking.models import Booking
+from liblaas.views import booking_booking_status
+from django.http import HttpResponse, JsonResponse
class BookingView(TemplateView):
template_name = "booking/booking_detail.html"
@@ -71,30 +70,53 @@ class BookingListView(TemplateView):
def booking_detail_view(request, booking_id):
- user = None
- if request.user.is_authenticated:
- user = request.user
- else:
- return render(request, "dashboard/login.html", {'title': 'Authentication Required'})
+ if request.method == 'GET':
+ user = None
+ if request.user.is_authenticated:
+ user = request.user
+ else:
+ return render(request, "dashboard/login.html", {'title': 'Authentication Required'})
+
+ booking = get_object_or_404(Booking, id=booking_id)
+ statuses = []
+ if booking.aggregateId:
+ statuses = booking_booking_status(booking.aggregateId)
+ allowed_users = set(list(booking.collaborators.all()))
+ if (request.user.is_superuser):
+ allowed_users.add(request.user)
+ allowed_users.add(booking.owner)
+ if user not in allowed_users:
+ return render(request, "dashboard/login.html", {'title': 'This page is private'})
+
+ context = {
+ 'title': 'Booking Details',
+ 'booking': booking,
+ 'status': statuses,
+ 'collab_string': ', '.join(map(str, booking.collaborators.all()))
+ }
+
+ return render(
+ request,
+ "booking/booking_detail.html",
+ context
+ )
+
+ if request.method == 'POST':
+ return update_booking_status(request)
+
+ return HttpResponse(status=405)
+
+def update_booking_status(request):
+ if request.method != "POST":
+ return HttpResponse(status=405)
+
+ data = json.loads(request.body.decode('utf-8'))
+ agg_id = data["agg_id"]
+
+ response = booking_booking_status(agg_id)
+
+ if (response.status_code == 200):
+ return JsonResponse(status=200, data=response)
+
+ return HttpResponse(status=500)
- booking = get_object_or_404(Booking, id=booking_id)
- statuses = get_booking_status(booking)
- allowed_users = set(list(booking.collaborators.all()))
- if (request.user.is_superuser):
- allowed_users.add(request.user)
- allowed_users.add(booking.owner)
- if user not in allowed_users:
- return render(request, "dashboard/login.html", {'title': 'This page is private'})
- context = {
- 'title': 'Booking Details',
- 'booking': booking,
- 'status': statuses,
- 'collab_string': ', '.join(map(str, booking.collaborators.all())),
- 'contact_email': booking.lab.contact_email
- }
-
- return render(
- request,
- "booking/booking_detail.html",
- context
- )
diff --git a/src/dashboard/forms.py b/src/dashboard/forms.py
deleted file mode 100644
index dd87ec6..0000000
--- a/src/dashboard/forms.py
+++ /dev/null
@@ -1,46 +0,0 @@
-from django import forms
-
-class NewIPAAccountForm(forms.Form):
-
- # First name
- first_name = forms.CharField(widget=forms.TextInput(attrs={'placeholder': 'First Name', 'style': 'width: 300px;', 'class': 'form-control'}))
- # Last name
- last_name = forms.CharField(widget=forms.TextInput(attrs={'placeholder': 'Last Name', 'style': 'width: 300px;', 'class': 'form-control'}))
- # Company
- company = forms.CharField(widget=forms.TextInput(attrs={'placeholder': 'Company', 'style': 'width: 300px;', 'class': 'form-control'}))
- # Email
- email = forms.CharField(widget=forms.TextInput(attrs={'placeholder': 'Email', 'style': 'width: 300px;', 'class': 'form-control', "readonly": True}))
-
-class ReadOnlyIPAAccountForm(forms.Form):
- # IPA Username
- ipa_username = forms.CharField(required=False, widget=forms.TextInput(attrs={'placeholder': 'IPA Username', 'style': 'width: 300px;', 'class': 'form-control', "readonly": True}))
- # First name
- first_name = forms.CharField(required=False, widget=forms.TextInput(attrs={'placeholder': 'First Name', 'style': 'width: 300px;', 'class': 'form-control', "readonly": True}))
- # Last name
- last_name = forms.CharField(required=False, widget=forms.TextInput(attrs={'placeholder': 'Last Name', 'style': 'width: 300px;', 'class': 'form-control', "readonly": True}))
- # Company
- company = forms.CharField(required=False, widget=forms.TextInput(attrs={'placeholder': 'Company', 'style': 'width: 300px;', 'class': 'form-control', "readonly": True}))
- # Email
- email = forms.CharField(required=False, widget=forms.TextInput(attrs={'placeholder': 'Email', 'style': 'width: 300px;', 'class': 'form-control', "readonly": True}))
-
-class ConflictIPAAcountForm(forms.Form):
- # IPA Username
- ipa_username = forms.CharField(widget=forms.TextInput(attrs={'placeholder': 'IPA Username', 'style': 'width: 300px;', 'class': 'form-control'}))
- # First name
- first_name = forms.CharField(widget=forms.TextInput(attrs={'placeholder': 'First Name', 'style': 'width: 300px;', 'class': 'form-control'}))
- # Last name
- last_name = forms.CharField(widget=forms.TextInput(attrs={'placeholder': 'Last Name', 'style': 'width: 300px;', 'class': 'form-control'}))
- # Company
- company = forms.CharField(widget=forms.TextInput(attrs={'placeholder': 'Company', 'style': 'width: 300px;', 'class': 'form-control'}))
- # Email
- email = forms.CharField(widget=forms.TextInput(attrs={'placeholder': 'Email', 'style': 'width: 300px;', 'class': 'form-control', "readonly": True}))
-
-class SetCompanyForm(forms.Form):
- # Company
- company = forms.CharField(required=True, widget=forms.TextInput(attrs={'placeholder': 'Company', 'style': 'width: 300px;', 'class': 'form-control'}))
-
-class SetSSHForm(forms.Form):
- # SSH key
- ssh_public_key = forms.CharField(required=True, widget=forms.Textarea(attrs={'placeholder': 'SSH Public Key', 'style': 'width: 300px;', 'class': 'form-control'}))
-
-
diff --git a/src/dashboard/tasks.py b/src/dashboard/tasks.py
index 281db92..56356d5 100644
--- a/src/dashboard/tasks.py
+++ b/src/dashboard/tasks.py
@@ -12,9 +12,8 @@
from booking.models import Booking
from celery import shared_task
from django.utils import timezone
-from api.views import liblaas_end_booking
+from liblaas.views import booking_end_booking
-# todo - make a task to check for expired bookings
@shared_task
def end_expired_bookings():
cleanup_set = Booking.objects.filter(end__lte=timezone.now(), ).filter(complete=False)
@@ -22,7 +21,7 @@ def end_expired_bookings():
booking.complete = True
if (booking.aggregateId):
print("ending booking " + str(booking.id) + " with agg id: ", booking.aggregateId)
- liblaas_end_booking(booking.aggregateId)
+ booking_end_booking(booking.aggregateId)
else:
print("booking " + str(booking.id) + " has no agg id")
booking.save()
diff --git a/src/dashboard/views.py b/src/dashboard/views.py
index 909b695..2a8b43f 100644
--- a/src/dashboard/views.py
+++ b/src/dashboard/views.py
@@ -8,22 +8,21 @@
# http://www.apache.org/licenses/LICENSE-2.0
##############################################################################
-import json
+import os
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 django.http import HttpResponse
from account.models import Lab, UserProfile
-from api.utils import get_ipa_migration_form, ipa_query_user
-from api.views import ipa_conflict_account
from booking.models import Booking
-from dashboard.forms import *
-from api.views import list_flavors, list_hosts
-
from laas_dashboard import settings
+from liblaas.utils import get_ipa_status
+
+from liblaas.views import flavor_list_flavors, flavor_list_hosts
def lab_list_view(request):
@@ -34,14 +33,16 @@ def lab_list_view(request):
def lab_detail_view(request, lab_name):
- # todo - LL Integration
user = None
if request.user.is_authenticated:
user = request.user
lab = get_object_or_404(Lab, name=lab_name)
- flavors_list = json.loads(list_flavors(request).content)
- host_list = json.loads(list_hosts(request).content)
+ origin = "anuket" if os.environ.get("TEMPLATE_OVERRIDE_DIR") == 'laas' else "lfedge"
+
+ flavors_list = flavor_list_flavors(origin)
+ host_list = flavor_list_hosts(origin)
+
flavor_map = {}
for flavor in flavors_list:
flavor_map[flavor['flavor_id']] = flavor['name']
@@ -79,55 +80,42 @@ def host_profile_detail_view(request):
def landing_view(request):
user = request.user
- ipa_migrator = {
- "exists": "false" # Jinja moment
- }
+ ipa_status = "n/a"
+ profile = {}
+
if not user.is_anonymous:
bookings = Booking.objects.filter(
Q(owner=user) | Q(collaborators=user),
end__gte=datetime.now(pytz.utc)
)
- profile = UserProfile.objects.get(user=user)
- if (not profile.ipa_username):
- ipa_migrator = get_ipa_migration_form(user, profile)
- ipa_migrator["exists"] = "true"
-
+ # new, link, conflict, n/a
+ ipa_status = get_ipa_status(user)
+ up = UserProfile.objects.get(user=user)
+ profile["email"] = up.email_addr
+
+ # Link by default, no need for modal
+ if ipa_status == "link":
+ up.ipa_username = str(user)
+ up.save()
else:
bookings = None
- print("IPA migrator is", ipa_migrator)
LFID = True if settings.AUTH_SETTING == 'LFID' else False
- if request.method == "GET":
- return render(
- request,
- 'dashboard/landing.html',
- {
- 'title': "Welcome to the Lab as a Service Dashboard",
- 'bookings': bookings,
- 'LFID': LFID,
- 'ipa_migrator': ipa_migrator,
- }
- )
-
- # Using this for the special case in the ipa_migrator
- if request.method == 'POST':
- existing_profile = ipa_query_user(request.POST['ipa_username'])
- print("exists already?", existing_profile != None)
- if (existing_profile != None):
- return render(
- request,
- 'dashboard/landing.html',
- {
- 'title': "Welcome to the Lab as a Service Dashboard",
- 'bookings': bookings,
- 'LFID': LFID,
- 'ipa_migrator': ipa_migrator,
- 'error': "Username is already taken"
- }
- )
- else:
- return ipa_conflict_account(request)
+ if request.method != "GET":
+ return HttpResponse(status_code=405)
+
+ return render(
+ request,
+ 'dashboard/landing.html',
+ {
+ 'title': "Welcome to the Lab as a Service Dashboard",
+ 'bookings': bookings,
+ 'LFID': LFID,
+ 'ipa_status': ipa_status,
+ 'profile': profile
+ }
+ )
class LandingView(TemplateView):
template_name = "dashboard/landing.html"
diff --git a/src/laas_dashboard/settings.py b/src/laas_dashboard/settings.py
index 66de35d..a56265e 100644
--- a/src/laas_dashboard/settings.py
+++ b/src/laas_dashboard/settings.py
@@ -29,6 +29,7 @@ INSTALLED_APPS = [
'workflow',
'api',
'analytics',
+ 'liblaas',
'django.contrib.admin',
'django.contrib.auth',
'mozilla_django_oidc', # needs to be defined after auth
diff --git a/src/laas_dashboard/urls.py b/src/laas_dashboard/urls.py
index a7b29fb..22452cb 100644
--- a/src/laas_dashboard/urls.py
+++ b/src/laas_dashboard/urls.py
@@ -42,6 +42,7 @@ urlpatterns = [
url(r'^api/', include('api.urls')),
url(r'^oidc/', include('mozilla_django_oidc.urls')),
url(r'^resource/', include('resource_inventory.urls', namespace='resource')),
+ url(r'^liblaas/', include('liblaas.urls', namespace='liblaas'))
]
if settings.DEBUG is True:
diff --git a/src/liblaas/__init__.py b/src/liblaas/__init__.py
new file mode 100644
index 0000000..46e0bd7
--- /dev/null
+++ b/src/liblaas/__init__.py
@@ -0,0 +1,8 @@
+##############################################################################
+# Copyright (c) 2018 Sawyer Bergeron 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
+############################################################################## \ No newline at end of file
diff --git a/src/liblaas/endpoints.py b/src/liblaas/endpoints.py
new file mode 100644
index 0000000..64e5126
--- /dev/null
+++ b/src/liblaas/endpoints.py
@@ -0,0 +1,254 @@
+##############################################################################
+# Copyright (c) 2018 Sawyer Bergeron 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
+##############################################################################
+
+# HTTP Requests from the user will need to be processed here first, before the appropriate liblaas endpoint is called
+
+from account.models import UserProfile
+from liblaas.views import *
+from liblaas.utils import validate_collaborators
+from django.http import JsonResponse, HttpResponse
+from django.contrib.auth.models import User
+from booking.models import Booking
+from account.models import Lab
+from django.utils import timezone
+from datetime import timedelta
+
+def request_list_flavors(request) -> HttpResponse:
+ data = json.loads(request.body.decode('utf-8'))
+ if not "project" in data:
+ return HttpResponse(status=422)
+
+ if not request.user.is_authenticated:
+ return HttpResponse(status=401)
+
+ response = flavor_list_flavors(data["project"])
+ return JsonResponse(status=200, data={"flavors_list": response})
+
+
+def request_list_template(request) -> HttpResponse:
+ if not request.user.is_authenticated:
+ return HttpResponse(status=401)
+
+ uid = UserProfile.objects.get(user=request.user)
+ uid = uid.ipa_username
+
+ response = template_list_templates(uid)
+ return JsonResponse(status=200, data={"templates_list": response})
+
+def request_create_template(request) -> HttpResponse:
+ data = json.loads(request.body.decode('utf-8'))
+ if not "template_blob" in data:
+ return HttpResponse(status=422)
+
+ if not request.user.is_authenticated:
+ return HttpResponse(status=401)
+
+ response = template_make_template(data["template_blob"])
+ return JsonResponse(status=200, data={"uuid": response})
+
+def request_create_booking(request) -> HttpResponse:
+ # Validate request
+ data = json.loads(request.body.decode('utf-8'))
+ if not "booking_blob" in data:
+ return HttpResponse(status=422)
+
+ if not request.user.is_authenticated:
+ return HttpResponse(status=401)
+
+ data = data["booking_blob"]
+
+ # Validate and build collaborators list
+ valid = validate_collaborators(data["allowed_users"])
+ if (not valid["valid"]):
+ return JsonResponse(
+ data={"message": valid["message"], "error": True},
+ status=200,
+ safe=False
+ )
+ ipa_users = []
+ ipa_users.append(UserProfile.objects.get(user=request.user).ipa_username)
+ for user in data["allowed_users"]:
+ collab_profile = UserProfile.objects.get(user=User.objects.get(username=user))
+ ipa_users.append(collab_profile.ipa_username)
+
+ # Reformat post data
+ bookingBlob = {
+ "template_id": data["template_id"],
+ "allowed_users": ipa_users,
+ "global_cifile": data["global_cifile"],
+ "metadata": {
+ "booking_id": None, # fill in after creating django object
+ "owner": UserProfile.objects.get(user=request.user).ipa_username,
+ "lab": "UNH_IOL",
+ "purpose": data["metadata"]["purpose"],
+ "project": data["metadata"]["project"],
+ "length": int(data["metadata"]["length"])
+ },
+ "origin": "anuket" if os.environ.get("TEMPLATE_OVERRIDE_DIR") == 'laas' else "lfedge" # todo - refactor
+ }
+
+ # Create booking in dashboard
+ booking = Booking.objects.create(
+ purpose=bookingBlob["metadata"]["purpose"],
+ project=bookingBlob["metadata"]['project'],
+ lab=Lab.objects.get(name='UNH_IOL'),
+ owner=request.user,
+ start=timezone.now(),
+ end=timezone.now() + timedelta(days=int(bookingBlob["metadata"]['length'])),
+ )
+
+ for c in list(data["allowed_users"]):
+ booking.collaborators.add(User.objects.get(username=c))
+
+ # Create booking in liblaas
+ bookingBlob["metadata"]["booking_id"] = str(booking.id)
+ liblaas_response = booking_create_booking(bookingBlob)
+ if liblaas_response.status_code != 200:
+ print("received non success from liblaas, deleting booking from dashboard")
+ booking.delete()
+ return JsonResponse(
+ data={},
+ status=500,
+ safe=False
+ )
+
+ aggregateId = liblaas_response
+ booking.aggregateId = aggregateId
+ booking.save()
+
+ print(f"Created new booking with id {booking.id} and aggregate {aggregateId}")
+
+ return JsonResponse(
+ data = {"bookingId": booking.id},
+ status=200,
+ safe=False
+ )
+
+def request_migrate_new(request) -> HttpResponse:
+ user = request.user
+ profile = UserProfile.objects.get(user=user)
+
+ data = json.loads(request.body.decode('utf-8'))
+ fn = data["firstname"].strip()
+ ln = data["lastname"].strip()
+ company = data["company"].strip()
+
+ if (fn == "" or ln == "" or company == ""):
+ return JsonResponse(
+ data = {"message": "Please fill out all fields."},
+ status=406
+ )
+
+ user_blob = {
+ "uid": str(user), # For this case, the username is not taken, so the dashboard username will be used
+ "givenname": str(fn),
+ "sn": str(ln),
+ "ou": str(company),
+ "mail": str(profile.email_addr),
+ 'random': True
+ }
+
+ vpn_username = user_create_user(user_blob)
+
+ if (vpn_username):
+ profile.ipa_username = vpn_username
+ profile.save()
+ return HttpResponse(status=200)
+
+ return JsonResponse(
+ data = {"message": "Unable to create account. Please try again."},
+ status=406
+ )
+
+
+
+def request_migrate_conflict(request) -> HttpResponse:
+ user = request.user
+ profile = UserProfile.objects.get(user=user)
+
+ data = json.loads(request.body.decode('utf-8'))
+ fn = data["firstname"].strip()
+ ln = data["lastname"].strip()
+ company = data["company"].strip()
+ username = data["username"].strip()
+
+ if (fn == "" or ln == "" or company == "" or username == ""):
+ return JsonResponse(
+ data = {"message": "Please fill out all fields."},
+ status=406
+ )
+
+ # Check if username is taken by querying for that user
+ existing_user = user_get_user(username)
+ if (existing_user):
+ return JsonResponse(
+ data = {"message": "Username is already taken. Please try again."},
+ status=406
+ )
+
+ # Create and link account
+ user_blob = {
+ "uid": str(username),
+ "givenname": str(fn),
+ "sn": str(ln),
+ "ou": str(company),
+ "mail": str(profile.email_addr),
+ 'random': True
+ }
+
+ vpn_username = user_create_user(user_blob)
+
+ if (vpn_username):
+ profile.ipa_username = vpn_username
+ profile.save()
+ return HttpResponse(status=200)
+
+ return JsonResponse(
+ data = {"message": "Unable to create account. Please try again."},
+ status=406
+ )
+
+def request_set_company(request):
+ data = json.loads(request.body.decode('utf-8'))
+ if not "data" in data:
+ return HttpResponse(status=422)
+
+ if not request.user.is_authenticated:
+ return HttpResponse(status=401)
+
+ up = UserProfile.objects.get(user=request.user)
+ success = user_set_company(up.ipa_username, data["data"].strip())
+
+ if (success):
+ return HttpResponse(status=200)
+
+ return JsonResponse(
+ data = {"message": "Unable to update details. Please check your input and try again."},
+ status=406
+ )
+
+def request_set_ssh(request):
+ data = json.loads(request.body.decode('utf-8'))
+ if not "data" in data:
+ return HttpResponse(status=422)
+
+ if not request.user.is_authenticated:
+ return HttpResponse(status=401)
+ up = UserProfile.objects.get(user=request.user)
+ success = user_set_ssh(up.ipa_username, data["data"])
+
+ if (success):
+ return HttpResponse(status=200)
+
+ # API does not provide a way to verify keys were successfully added, so it is unlikely that this case will ever be hit
+ return JsonResponse(
+ data = {"message": "Unable to add SSH key. Please verify that the key is valid and try again."},
+ status=406
+ )
+
diff --git a/src/liblaas/tests.py b/src/liblaas/tests.py
new file mode 100644
index 0000000..5ec7f15
--- /dev/null
+++ b/src/liblaas/tests.py
@@ -0,0 +1,65 @@
+##############################################################################
+# Copyright (c) 2018 Sawyer Bergeron 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.test import TestCase
+from liblaas.views import liblaas_docs, user_get_user
+
+class LibLaaSTests(TestCase):
+ def test_can_reach_liblaas(self):
+ response = liblaas_docs()
+ self.assertEqual(response.status_code, 200)
+
+class BookingTests(TestCase):
+ def test_create_booking_succeeds_with_valid_blob(self):
+ pass
+
+ def test_create_booking_fails_with_invalid_blob(self):
+ pass
+
+ def test_booking_status_succeeds_on_valid_booking_id(self):
+ pass
+
+ def test_end_booking_succeeds_on_valid_agg_id(self):
+ pass
+
+class FlavorTests(TestCase):
+ def test_list_flavors_succeeds(self):
+ pass
+
+ def test_get_flavor_by_id_succeeds(self):
+ pass
+
+ def test_list_hosts_succeeds(self):
+ pass
+
+class TemplateTests(TestCase):
+ def test_list_templates_succeeds(self):
+ pass
+
+ def test_make_template_succeeds(self):
+ pass
+
+ def test_delete_template_succeeds(self):
+ pass
+
+class UserTests(TestCase):
+ def test_get_user_succeeds(self):
+ pass
+
+ def test_create_user_succeeds(self):
+ pass
+
+ def test_set_ssh_succeeds(self):
+ pass
+
+ def test_set_company_succeeds(self):
+ pass
+
+ def test_set_email_succeeds(self):
+ pass
diff --git a/src/liblaas/urls.py b/src/liblaas/urls.py
new file mode 100644
index 0000000..6deb9cf
--- /dev/null
+++ b/src/liblaas/urls.py
@@ -0,0 +1,24 @@
+##############################################################################
+# Copyright (c) 2018 Sawyer Bergeron 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.conf.urls import url
+
+from liblaas.endpoints import *
+
+app_name = 'liblaas'
+urlpatterns = [
+ url(r'^flavor/$', request_list_flavors, name='flavor'),
+ url(r'^template/$', request_list_template, name='template'),
+ url(r'^template/create/$', request_create_template, name='template_create'),
+ url(r'^booking/create/$', request_create_booking, name='booking_create'),
+ url(r'^migrate/new/$', request_migrate_new, name='migrate_new'),
+ url(r'^migrate/conflict/$', request_migrate_conflict, name='migrate_conflict'),
+ url(r'^ipa/ssh/$', request_set_ssh, name='set_ssh'),
+ url(r'^ipa/company/$', request_set_company, name='set_company'),
+] \ No newline at end of file
diff --git a/src/liblaas/utils.py b/src/liblaas/utils.py
new file mode 100644
index 0000000..c3b66a7
--- /dev/null
+++ b/src/liblaas/utils.py
@@ -0,0 +1,63 @@
+##############################################################################
+# Copyright (c) 2018 Sawyer Bergeron 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.contrib.auth.models import User
+from account.models import UserProfile
+from liblaas.views import user_get_user
+
+def validate_collaborators(collab_list: list[str]) -> dict:
+ result = {"message": "n/a", "valid": False}
+
+ for user in collab_list:
+ collab_profile = UserProfile.objects.get(user=User.objects.get(username=user))
+ ipa_username = collab_profile.ipa_username
+ if ipa_username == None:
+ result["message"] = f"{str(collab_profile)} has not linked their IPA account yet. Please ask them to log into the LaaS dashboard, or remove them from the booking to continue."
+ return result
+
+ ipa_account = user_get_user(ipa_username)
+ print(ipa_account)
+
+ if not "ou" in ipa_account or ipa_account["ou"] == "":
+ result["message"] = f"{str(collab_profile)} has not set their company yet. Please ask them to log into the LaaS dashboard, go to the settings page and add it. Otherwise, remove them from the booking to continue."
+ return result
+
+ if not "ipasshpubkey" in ipa_account:
+ result["message"] = f"{str(collab_profile)} has not added an SSH public key yet. Please ask them to log into the LaaS dashboard, go to the settings page and add it. Otherwise, remove them from the booking to continue."
+ return result
+
+ result["valid"] = True
+
+ return result
+
+# Returns whether the user has linked their ipa account. If not, determines how it needs to be linked.
+def get_ipa_status(dashboard_user: User) -> str:
+ profile = UserProfile.objects.get(user=dashboard_user)
+ if profile == None:
+ return "n/a"
+
+ # Already linked, no need to continue
+ if profile.ipa_username != None:
+ return "n/a"
+
+ # Basic info
+ dashboard_username = str(dashboard_user)
+ dashboard_email = profile.email_addr
+ ipa_user = user_get_user(dashboard_username)
+
+ # New user case
+ if ipa_user == None:
+ return "new"
+
+ # Link case
+ if ipa_user["mail"] == dashboard_email:
+ return "link"
+
+ # Conflict case
+ return "conflict"
diff --git a/src/liblaas/views.py b/src/liblaas/views.py
new file mode 100644
index 0000000..5edc727
--- /dev/null
+++ b/src/liblaas/views.py
@@ -0,0 +1,205 @@
+##############################################################################
+# Copyright (c) 2018 Sawyer Bergeron 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
+##############################################################################
+
+# Unauthenticated requests to liblaas. If a call makes it to here, it is assumed to be authenticated
+
+import os
+import requests
+import json
+
+base = os.environ.get("LIBLAAS_BASE_URL")
+post_headers = {'Content-Type': 'application/json'}
+
+def liblaas_docs():
+ endpoint = f'docs'
+ url = f'{base}{endpoint}'
+ try:
+ response = requests.get(url)
+ return response.json()
+ except:
+ print(f"Error at {url}")
+ return None
+
+### BOOKING
+
+# DELETE
+def booking_end_booking(agg_id: str) -> requests.Response:
+ endpoint = f'booking/{agg_id}/end'
+ url = f'{base}{endpoint}'
+ try:
+ response = requests.delete(url)
+ return response.json()
+ except:
+ print(f"Error at {url}")
+ return None
+
+# GET
+def booking_booking_status(agg_id: str) -> requests.Response:
+ endpoint = f'booking/{agg_id}/status'
+ url = f'{base}{endpoint}'
+ try:
+ response = requests.get(url)
+ return response.json()
+ except:
+ print(f"Error at {url}")
+ return None
+
+# POST
+def booking_create_booking(booking_blob: dict) -> requests.Response:
+ endpoint = f'booking/create'
+ url = f'{base}{endpoint}'
+ try:
+ response = requests.post(url, data=json.dumps(booking_blob), headers=post_headers)
+ return response.json()
+ except:
+ print(f"Error at {url}")
+ return None
+
+### FLAVOR
+
+# GET
+def flavor_list_flavors(project: str) -> requests.Response:
+ endpoint = f'flavor' #todo - add project to url once liblaas supports it
+ url = f'{base}{endpoint}'
+ try:
+ response = requests.get(url)
+ return response.json()
+ except:
+ print(f"Error at {url}")
+ return None
+
+# GET
+def flavor_get_flavor_by_id(flavor_id: str) -> requests.Response:
+ endpoint = f'flavor/name/{flavor_id}/'
+ url = f'{base}{endpoint}'
+ try:
+ response = requests.get(url)
+ return response.json()
+ except:
+ print(f"Error at {url}")
+ return None
+
+# GET
+def flavor_list_hosts(project: str) -> requests.Response:
+ endpoint = f'flavor/hosts/{project}'
+ url = f'{base}{endpoint}'
+ try:
+ response = requests.get(url)
+ return response.json()
+ except:
+ print(f"Error at {url}")
+ return None
+
+### TEMPLATE
+
+# GET
+def template_list_templates(uid: str) -> requests.Response:
+ endpoint = f'template/list/{uid}'
+ url = f'{base}{endpoint}'
+ try:
+ response = requests.get(url)
+ return response.json()
+ except:
+ print(f"Error at {url}")
+ return None
+
+# DELETE
+def template_delete_template(template_id: str) -> requests.Response:
+ endpoint = f'template/{template_id}'
+ url = f'{base}{endpoint}'
+ try:
+ response = requests.delete(url)
+ return response.json()
+ except:
+ print(f"Error at {url}")
+ return None
+
+#POST
+def template_make_template(template_blob: dict) -> requests.Response:
+ endpoint = f'template/create'
+ url = f'{base}{endpoint}'
+ try:
+ response = requests.post(url, data=json.dumps(template_blob), headers=post_headers)
+ return response.json()
+ except:
+ print(f"Error at {url}")
+ return None
+
+### USER
+
+# GET
+def user_get_user(uid: str) -> requests.Response:
+ endpoint = f'user/{uid}'
+ url = f'{base}{endpoint}'
+ try:
+ response = requests.get(url)
+ return response.json()
+ except:
+ print(f"Error at {url}")
+ return None
+
+# POST
+def user_create_user(user_blob: dict) -> requests.Response:
+ endpoint = f'user/create'
+ url = f'{base}{endpoint}'
+ try:
+ response = requests.post(url, data=json.dumps(user_blob), headers=post_headers)
+ # LibLaaS is not returning anything here regardless, so we need some way of determining if it was successful
+ # return response.json()
+
+ if response.status_code == 200:
+ return user_blob["uid"]
+
+ return response.json()
+ except:
+ print(f"Error at {url}")
+ return None
+
+# POST
+def user_set_ssh(uid: str, keys: list) -> requests.Response:
+ endpoint = f'user/{uid}/ssh'
+ url = f'{base}{endpoint}'
+ try:
+ response = requests.post(url, data=json.dumps(clean_ssh_keys(keys)), headers=post_headers)
+ # return response.json()
+ return response.status_code == 200
+ except:
+ print(f"Error at {url}")
+ return None
+
+# POST
+def user_set_company(uid: str, company: str):
+ endpoint = f'user/{uid}/company'
+ url = f'{base}{endpoint}'
+ try:
+ response = requests.post(url, data=json.dumps(company), headers=post_headers)
+ # return response.json()
+ return response.status_code == 200
+ except:
+ print(f"Error at {url}")
+ return None
+
+# POST
+def user_set_email(uid: str, email: str):
+ endpoint = f'user/{uid}/email'
+ url = f'{base}{endpoint}'
+ try:
+ response = requests.post(url, data=json.dumps(email), headers=post_headers)
+ # return response.json()
+ return response.status_code == 200
+ except:
+ print(f"Error at {url}")
+ return None
+
+# utils
+def clean_ssh_keys(ssh_key_list):
+ cleaned = []
+ for key in ssh_key_list:
+ cleaned.append(key.strip())
+ return cleaned \ No newline at end of file
diff --git a/src/resource_inventory/views.py b/src/resource_inventory/views.py
index 837cc73..b9e3490 100644
--- a/src/resource_inventory/views.py
+++ b/src/resource_inventory/views.py
@@ -8,48 +8,53 @@
##############################################################################
import json
+import os
from django.shortcuts import render
from django.http import HttpResponse
-from api.views import list_hosts, list_flavors
+from liblaas.views import flavor_list_hosts, flavor_list_flavors
+
+origin = "anuket" if os.environ.get("TEMPLATE_OVERRIDE_DIR") == 'laas' else "lfedge"
def host_list_view(request):
- if request.method == "GET":
- host_list = json.loads(list_hosts(request).content)
- flavor_list = json.loads(list_flavors(request).content)
- flavor_map = {}
- for flavor in flavor_list:
- flavor_map[flavor['flavor_id']] = flavor['name']
+ if request.method != "GET":
+ return HttpResponse(status=405)
+
+ host_list = flavor_list_hosts(origin)
+ flavor_list = flavor_list_flavors(origin)
+
+ flavor_map = {}
+ for flavor in flavor_list:
+ flavor_map[flavor['flavor_id']] = flavor['name']
- # Apparently Django Templating lacks many features that regular Jinja offers, so I need to get creative
- for host in host_list:
- id = host["flavor"]
- name = flavor_map[id]
- host["flavor"] = {"id": id, "name": name}
+ # Apparently Django Templating lacks many features that regular Jinja offers, so I need to get creative
+ for host in host_list:
+ id = host["flavor"]
+ name = flavor_map[id]
+ host["flavor"] = {"id": id, "name": name}
- template = "resource/hosts.html"
- context = {
- "hosts": host_list,
- "flavor_map": flavor_map
- }
- return render(request, template, context)
+ template = "resource/hosts.html"
+ context = {
+ "hosts": host_list,
+ "flavor_map": flavor_map
+ }
+ return render(request, template, context)
- return HttpResponse(status=405)
def profile_view(request, resource_id):
- if request.method == "GET":
- flavor_list = json.loads(list_flavors(request).content)
- selected_flavor = {}
- for flavor in flavor_list:
- if flavor["flavor_id"] == resource_id:
- selected_flavor = flavor
- break
-
- template = "resource/hostprofile_detail.html"
- context = {
- "flavor": selected_flavor
- }
- return render(request, template, context)
-
- return HttpResponse(status=405) \ No newline at end of file
+ if request.method != "GET":
+ return HttpResponse(status=405)
+
+ flavor_list = flavor_list_flavors(origin)
+ selected_flavor = {}
+ for flavor in flavor_list:
+ if flavor["flavor_id"] == resource_id:
+ selected_flavor = flavor
+ break
+
+ template = "resource/hostprofile_detail.html"
+ context = {
+ "flavor": selected_flavor
+ }
+ return render(request, template, context)
diff --git a/src/static/js/workflows/book-a-pod.js b/src/static/js/workflows/book-a-pod.js
index 2396fdb..60a5370 100644
--- a/src/static/js/workflows/book-a-pod.js
+++ b/src/static/js/workflows/book-a-pod.js
@@ -247,13 +247,14 @@ const steps = {
if (!response) {
showError("The selected resources for this booking are unavailable at this time. Please select a different resource or try again later.", -1)
}
- if (response.bookingId) {
+ const r = JSON.parse(response)
+ if (r.bookingId) {
showError("The booking has been successfully created.", -1)
- window.location.href = "../../booking/detail/" + response.bookingId + "/"; // todo
+ window.location.href = "../../booking/detail/" + r.bookingId + "/";
return;
} else {
- if (response.error == true) {
- showError(response.message, -1)
+ if (r.error == true) {
+ showError(r.message, -1)
} else {
showError("The booking could not be created at this time.", -1)
}
diff --git a/src/static/js/workflows/workflow.js b/src/static/js/workflows/workflow.js
index 97892a2..02f79a5 100644
--- a/src/static/js/workflows/workflow.js
+++ b/src/static/js/workflows/workflow.js
@@ -1,26 +1,15 @@
/*
Defines a common interface for creating workflows
-Functions as the "view" part of MVC, or the "controller" part. Not really sure tbh
+Functions as the "view" part of MVC, or the "controller" part.
*/
-
-const HTTP = {
- GET: "GET",
- POST: "POST",
- DELETE: "DELETE",
- PUT: "PUT"
-}
-
const endpoint = {
LABS: "todo", // Not implemented
FLAVORS: "flavor/",
IMAGES: "images/",
- TEMPLATES: "template/list/[username]",
- SAVE_DESIGN_WORKFLOW: "todo", // Post MVP
- SAVE_BOOKING_WORKFLOW: "todo", // Post MVP
- MAKE_TEMPLATE: "template/create",
- DELETE_TEMPLATE: "template",
- MAKE_BOOKING: "booking/create",
+ TEMPLATES: "template/",
+ MAKE_TEMPLATE: "template/create/",
+ MAKE_BOOKING: "booking/create/",
}
/** Functions as a namespace for static methods that post to the dashboard, then send an HttpRequest to LibLaas, then receive the response */
@@ -28,25 +17,20 @@ class LibLaaSAPI {
/** POSTs to dashboard, which then auths and logs the requests, makes the request to LibLaaS, and passes the result back to here.
Treat this as a private function. Only use the async functions when outside of this class */
- static makeRequest(method, endpoint, workflow_data) {
+ static makeRequest(endpoint, json_data) {
const token = document.getElementsByName('csrfmiddlewaretoken')[0].value
+
return new Promise((resolve, reject) => {// -> HttpResponse
$.ajax(
{
- crossDomain: true, // might need to change this back to true
- method: "POST",
- contentType: "application/json; charset=utf-8",
- dataType : 'json',
+ url: '../../liblaas/' + endpoint,
+ type: 'post',
+ data: JSON.stringify(json_data),
headers: {
- 'X-CSRFToken': token
+ 'X-CSRFToken': token,
+ 'Content-Type': 'application/json'
},
- data: JSON.stringify(
- {
- "method": method,
- "endpoint": endpoint,
- "workflow_data": workflow_data
- }
- ),
+ dataType: 'text',
timeout: 10000,
success: (response) => {
resolve(response);
@@ -60,85 +44,44 @@ class LibLaaSAPI {
}
static async getLabs() { // -> List<LabBlob>
- // return this.makeRequest(HTTP.GET, endpoint.LABS, {});
let jsonObject = JSON.parse('{"name": "UNH_IOL","description": "University of New Hampshire InterOperability Lab","location": "NH","status": 0}');
return [new LabBlob(jsonObject)];
}
- static async getLabFlavors(lab_name) { // -> List<FlavorBlob>
- const data = await this.handleResponse(this.makeRequest(HTTP.GET, endpoint.FLAVORS, {"lab_name": lab_name}));
+ static async getLabFlavors(project) { // -> List<FlavorBlob>
+ const response = await this.handleResponse(this.makeRequest(endpoint.FLAVORS, {"project": project}));
let flavors = [];
+ let data = JSON.parse(response)
if (data) {
- for (const d of data) {
+ for (const d of data.flavors_list) {
flavors.push(new FlavorBlob(d))
}
} else {
apiError("flavors")
}
return flavors;
- // let jsonObject = JSON.parse('{"flavor_id": "aaa-bbb-ccc", "name": "HPE Gen 9", "description": "placeholder", "interfaces": ["ens1", "ens2", "ens3"]}')
- // return [new FlavorBlob(jsonObject)];
}
- static async getImagesForFlavor(flavor_id) {
- let full_endpoint = endpoint.FLAVORS + flavor_id + '/[username]/' + endpoint.IMAGES;
- const data = await this.handleResponse(this.makeRequest(HTTP.GET, full_endpoint, {}));
- let images = []
-
- if (data) {
- for (const d of data) {
- images.push(new ImageBlob(d));
- }
- } else {
- apiError("images")
- }
-
- return images;
- // let jsonObject = JSON.parse('{"image_id": "111-222-333", "name": "Arch Linux"}')
- // let jsonObject2 = JSON.parse('{"image_id": "444-555-666", "name": "Oracle Linux"}')
- // return [new ImageBlob(jsonObject), new ImageBlob(jsonObject2)];
- }
-
- /** Doesn't need to be passed a username because django will pull this from the request */
static async getTemplatesForUser() { // -> List<TemplateBlob>
- const data = await this.handleResponse(this.makeRequest(HTTP.GET, endpoint.TEMPLATES, {}))
+ const response = await this.handleResponse(this.makeRequest(endpoint.TEMPLATES, {}))
let templates = []
-
+ let data = JSON.parse(response)
if (data)
- for (const d of data) {
+ for (const d of data.templates_list) {
templates.push(new TemplateBlob(d))
} else {
apiError("templates")
}
return templates;
- // let jsonObject = JSON.parse('{"id": "12345", "owner":"jchoquette", "lab_name":"UNH_IOL","pod_name":"test pod","pod_desc":"for e2e testing","public":false,"host_list":[{"hostname":"test-node","flavor":"1ca6169c-a857-43c6-80b7-09b608c0daec","image":"3fc3833e-7b8b-4748-ab44-eacec8d14f8b","cifile":[],"bondgroups":[{"connections":[{"tagged":true,"connects_to":"public"}],"ifaces":[{"name":"eno49","speed":{"value":10000,"unit":"BitsPerSecond"},"cardtype":"Unknown"}]}]}],"networks":[{"name":"public","public":true}]}')
- // let jsonObject2 = JSON.parse('{"id":6789,"owner":"jchoquette","lab_name":"UNH_IOL","pod_name":"Other Host","pod_desc":"Default Template","public":false,"host_list":[{"cifile":["some ci data goes here"],"hostname":"node","flavor":"aaa-bbb-ccc","image":"111-222-333", "bondgroups":[{"connections": [{"tagged": false, "connects_to": "private"}], "ifaces": [{"name": "ens2"}]}]}],"networks":[{"name": "private", "public": false}]}');
-
- return [new TemplateBlob(jsonObject)];
- }
-
- static async saveDesignWorkflow(templateBlob) { // -> bool
- templateBlob.owner = user;
- return await this.handleResponse(this.makeRequest(HTTP.PUT, endpoint.SAVE_DESIGN_WORKFLOW))
- }
-
- static async saveBookingWorkflow(bookingBlob) { // -> bool
- bookingBlob.owner = user;
- return await this.handleResponse(this.makeRequest(HTTP.PUT, endpoint.SAVE_BOOKING_WORKFLOW, bookingBlob));
}
static async makeTemplate(templateBlob) { // -> UUID or null
- templateBlob.owner = user;
- return await this.handleResponse(this.makeRequest(HTTP.POST, endpoint.MAKE_TEMPLATE, templateBlob));
+ templateBlob.owner = user; // todo - remove this and handle this in django
+ return await this.handleResponse(this.makeRequest(endpoint.MAKE_TEMPLATE, {"template_blob": templateBlob}));
}
- static async deleteTemplate(template_id) { // -> UUID or null
- return await this.handleResponse(this.makeRequest(HTTP.DELETE, endpoint.DELETE_TEMPLATE + "/" + template_id, {}));
- }
-
- /** PUT to the dashboard with the bookingBlob. Dashboard will fill in lab and owner, make the django model, then hit liblaas, then come back and fill in the agg_id */
static async makeBooking(bookingBlob) {
- return await this.handleResponse(this.createDashboardBooking(bookingBlob));
+ return await this.handleResponse(this.makeRequest(endpoint.MAKE_BOOKING, {"booking_blob": bookingBlob}));
}
/** Wraps a call in a try / catch, processes the result, and returns the response or null if it failed */
@@ -151,33 +94,6 @@ class LibLaaSAPI {
return null;
}
}
-
- /** Uses PUT instead of POST to tell the dashboard that we want to create a dashboard booking instead of a liblaas request */
- static createDashboardBooking(bookingBlob) {
- const token = document.getElementsByName('csrfmiddlewaretoken')[0].value
- return new Promise((resolve, reject) => { // -> HttpResponse
- $.ajax(
- {
- crossDomain: false,
- method: "PUT",
- contentType: "application/json; charset=utf-8",
- dataType : 'json',
- headers: {
- 'X-CSRFToken': token
- },
- data: JSON.stringify(
- bookingBlob),
- timeout: 10000,
- success: (response) => {
- resolve(response);
- },
- error: (response) => {
- reject(response);
- }
- }
- )
- })
- }
}
diff --git a/src/templates/base/account/booking_list.html b/src/templates/base/account/booking_list.html
index 2af3915..c6702c9 100644
--- a/src/templates/base/account/booking_list.html
+++ b/src/templates/base/account/booking_list.html
@@ -97,16 +97,23 @@
}
async function submit_cancel_form() {
- var ajaxForm = $("#booking_cancel_form");
- var formData = ajaxForm.serialize();
- req = new XMLHttpRequest();
- var url = "cancel/" + current_booking_id;
- req.open("POST", url, true);
- req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
- req.onerror = function() { alert("problem submitting form"); }
- req.send(formData);
- await new Promise(r => setTimeout(r, 500)); // Quickest solution I could come up with to give liblaas time to mark it as deleted until we do an api rework
- location.reload();
+ let data = {"booking_id": current_booking_id}
+ $.ajax({
+ url: '',
+ type: 'post',
+ data: JSON.stringify(data),
+ headers: {
+ 'X-CSRFToken': document.getElementsByName('csrfmiddlewaretoken')[0].value,
+ 'Content-Type': 'application/json'
+ },
+ dataType: 'text',
+ })
+ .done(function() {
+ location.reload()
+ })
+ .fail(function() {
+ alert("Unable to cancel booking.")
+ })
}
</script>
diff --git a/src/templates/base/account/resource_list.html b/src/templates/base/account/resource_list.html
index c16fd07..c44e974 100644
--- a/src/templates/base/account/resource_list.html
+++ b/src/templates/base/account/resource_list.html
@@ -1,6 +1,5 @@
{% extends "base.html" %}
{% block extrahead %}
-<script src="/static/js/workflows/workflow.js"></script>
{% endblock %}
{% block content %}
<div class="row">
@@ -42,12 +41,25 @@
}
async function submit_delete_form() {
- if(LibLaaSAPI.deleteTemplate(current_resource_id)) {
- location.reload();
- } else {
- alert('Unable to delete template.');
- }
+ let data = {"template_id": current_resource_id}
+ $.ajax({
+ url: '',
+ type: 'post',
+ data: JSON.stringify(data),
+ headers: {
+ 'X-CSRFToken': document.getElementsByName('csrfmiddlewaretoken')[0].value,
+ 'Content-Type': 'application/json'
+ },
+ dataType: 'text',
+ })
+ .done(function() {
+ location.reload()
+ })
+ .fail(function() {
+ alert("Unable to delete resource.")
+ })
}
+
</script>
<div class="modal fade" id="resModal" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog" role="document">
diff --git a/src/templates/base/account/settings.html b/src/templates/base/account/settings.html
index 611ae38..2813beb 100644
--- a/src/templates/base/account/settings.html
+++ b/src/templates/base/account/settings.html
@@ -3,88 +3,143 @@
{% load bootstrap4 %}
{% block content %}
<h1>Settings</h1>
- <form action="/accounts/settings/" method="post">
+<form>
+ <div class="form-group">
{% csrf_token %}
<input id="hidden_key_list" type="hidden" name="ssh_key_list" value="">
- <div class="form-group">
- <label>Your IPA Username</label>
- <input type="text" class="form-control" disabled="true" style="width: 300px;" value="{{ ipa_username }}">
+ <label for="form_username">VPN Username</label>
+ <input value="{{ipa_username}}" type="text" class="form-control col-lg-1" id="form_username" disabled="">
<p>To change your password and access advanced account management, please go <a href="http://os-passwd.iol.unh.edu">here</a></p>
- {{ company_form }}
- {{ preference_form }}
- <br>
- <label>SSH Keys:</label>
- <ul class="list-group" id="key_ul">
- {% for key in existing_keys %}
- <li class="card w-25 mb-1">
- <div class="card-body" style="height: 150px; overflow-y: auto;">
- {{key}}
- </div>
- <div class="card-footer d-flex flex-row-reverse">
- <div class="btn btn-danger" onclick="remove_key('{{key}}', this.parentNode.parentNode)">Delete</div>
- </div>
- </li>
+
+ <label for="form_company">Company</label>
+ <input value="{{company}}" type="text" class="form-control col-lg-1" id="form_company">
+
+ <label for="form_timezone">Timezone</label>
+ <select class="form-control col-lg-1" id="form_timezone">
+ {% for tz in timezones %}
+ <option {% if tz == timezone %} selected {% endif %}>{{tz}}</option>
{% endfor %}
- </ul>
+ </select>
+
+ <label for="Nothing" class="m-0">Public User</label>
+ <div class="custom-control custom-switch">
+ <input type="checkbox" class="custom-control-input" id="form_public" {% if public %} checked="" {% endif %}>
+ <label class="custom-control-label" for="form_public"></label>
+ </div>
+ </div>
+</form>
+
+<div class="form-group">
+ <h2>SSH Keys</h2>
+ <ul class="list-group" id="key_ul">
+ {% for key in existing_keys %}
+ <li class="card w-25 mb-1">
+ <div class="card-body" style="height: 150px; overflow-y: auto;">
+ {{key}}
+ </div>
+ <div class="card-footer d-flex flex-row-reverse">
+ <div class="btn btn-danger" onclick="remove_key('{{key}}', this.parentNode.parentNode)">Delete</div>
+ </div>
+ </li>
+ {% endfor %}
+ </ul>
+
+ <ul class="list-group">
<li class="card w-25 text-truncate mb-1">
<div class="card-body">
- <textarea id="new_key_area" placeholder="New SSH Public Key" class="form-control" id="new_key_input"></textarea> </div>
+ <textarea id="new_key_area" placeholder="New SSH Public Key" class="form-control"
+ id="new_key_input"></textarea>
+ </div>
<div class="card-footer d-flex flex-row-reverse">
- <div class="btn btn-success" onclick="add_key(this.parentNode.parentNode.childNodes[1].childNodes[1].value)">Add</div>
+ <div class="btn btn-success"
+ onclick="add_key(this.parentNode.parentNode.childNodes[1].childNodes[1].value)">Add</div>
</div>
</li>
- <input class="btn btn-success mt-5" style="width: 100px" name="submitButton" type="submit" value="Save">
- </div>
- </form>
+ </ul>
+
+ <button class="btn btn-success" onclick=submit_changes()>Save</button>
+</div>
<script>
-let key_list = []
-$(window).on('load', function() {
- document.getElementById('new_key_area').value = "";
- {% for key in existing_keys %}
- key_list.push('{{key}}')
- {% endfor %}
- update_json_list()
-});
-
-
-function remove_key(target_key, node) {
- key_list = key_list.filter(key => key != target_key);
- node.setAttribute("hidden", "true");
- update_json_list()
-}
-
-function add_key(key_string) {
- console.log(key_string)
- if (key_list.indexOf(key_string) != -1) {
- alert('This key has already been added');
- return;
+ let key_list = []
+ $(window).on('load', function () {
+ document.getElementById('new_key_area').value = "";
+ {% for key in existing_keys %}
+ key_list.push('{{key}}')
+ {% endfor %}
+ update_json_list()
+ });
+
+
+ function remove_key(target_key, node) {
+ key_list = key_list.filter(key => key != target_key);
+ node.setAttribute("hidden", "true");
+ update_json_list()
+ }
+
+ function add_key(key_string) {
+ console.log(key_string)
+ if (key_list.indexOf(key_string) != -1) {
+ alert('This key has already been added');
+ return;
+ }
+ key_list.push(key_string)
+ create_key_card(key_string)
+ update_json_list()
}
- key_list.push(key_string)
- create_key_card(key_string)
- update_json_list()
-}
-
-function create_key_card(key_string) {
- const elem = document.createElement('li');
- elem.classList.add('card', 'w-25', 'mb-1');
- elem.innerHTML = `
+
+ function create_key_card(key_string) {
+ const elem = document.createElement('li');
+ elem.classList.add('card', 'w-25', 'mb-1');
+ elem.innerHTML = `
<div class="card-body" style="height: 150px; overflow-y: auto;">
` + key_string + `
</div>
<div class="card-footer d-flex flex-row-reverse">
- <div class="btn btn-danger" onclick="remove_key('` + key_string +`', this.parentNode.parentNode)">Delete</div>
+ <div class="btn btn-danger" onclick="remove_key('` + key_string + `', this.parentNode.parentNode)">Delete</div>
</div>
`
- document.getElementById('key_ul').appendChild(elem);
- document.getElementById('new_key_area').value = "";
+ document.getElementById('key_ul').appendChild(elem);
+ document.getElementById('new_key_area').value = "";
-}
+ }
-function update_json_list() {
- document.getElementById("hidden_key_list").value = key_list.toString()
-}
-</script>
-{% endblock content %}
+ function update_json_list() {
+ document.getElementById("hidden_key_list").value = key_list.toString()
+ }
+ function collect_form_data() {
+ data = {
+ "public_user": document.getElementById("form_public").checked,
+ "timezone": document.getElementById("form_timezone").value,
+ "company": document.getElementById("form_company").value,
+ "keys": key_list
+ }
+ console.log(data)
+ return data
+ }
+
+ async function submit_changes() {
+
+ let data = collect_form_data();
+ $.ajax({
+ url: '',
+ type: 'post',
+ data: JSON.stringify(data),
+ headers: {
+ 'X-CSRFToken': document.getElementsByName('csrfmiddlewaretoken')[0].value,
+ 'Content-Type': 'application/json'
+ },
+ dataType: 'text',
+ })
+ .done(function() {
+ location.reload()
+ })
+ .fail(function() {
+ alert("There was an issue saving your changes.")
+ })
+ }
+
+</script>
+{% endblock content %} \ No newline at end of file
diff --git a/src/templates/base/booking/booking_detail.html b/src/templates/base/booking/booking_detail.html
index bcf554b..0ae6530 100644
--- a/src/templates/base/booking/booking_detail.html
+++ b/src/templates/base/booking/booking_detail.html
@@ -169,28 +169,32 @@ code {
</div>
<script>
+ const agg_id = "{{booking.aggregateId}}"
setInterval(function () {
fetchBookingStatus();
}, 5000);
async function fetchBookingStatus() {
- req = new XMLHttpRequest();
- var url = "status";
- req.open("GET", url, true);
- req.onerror = function () { console.log("failed to get status") }
- req.onreadystatechange = function () {
- if (req.readyState === 4) {
- let status = JSON.parse(req.responseText)
- updateStatuses(status)
- }
- }
- req.send();
+ data = {"agg_id": agg_id}
+ $.ajax({
+ url: '',
+ type: 'post',
+ data: JSON.stringify(data),
+ headers: {
+ 'X-CSRFToken': document.getElementsByName('csrfmiddlewaretoken')[0].value,
+ 'Content-Type': 'application/json'
+ },
+ dataType: 'text',
+ })
+ .done(function(response) {
+ updateStatuses(response)
+ })
}
async function updateStatuses(status) {
-
const instances = status.instances;
+ if (!instances) return;
Object.keys(instances).forEach((aggId) => {
const instance = instances[aggId]
diff --git a/src/templates/base/dashboard/landing.html b/src/templates/base/dashboard/landing.html
index 7f97e4f..97cc508 100644
--- a/src/templates/base/dashboard/landing.html
+++ b/src/templates/base/dashboard/landing.html
@@ -63,37 +63,98 @@
</div>
</div>
-<!-- IPA Modal -->
+ <!-- Link Modal -->
<div class="modal fade" id="ipa-modal" tabindex="-1">
- <div class="modal-dialog modal-xl">
- <div class="modal-content">
+<div class="modal-dialog modal-xl">
+ <div class="modal-content">
<div class="modal-header">
- <h5 class="modal-title">Welcome to LaaS 3.0</h5>
+ <h5 class="modal-title">Welcome to LaaS 3.0</h5>
</div>
- <div class="modal-body" id="add_resource_modal_body">
+ <div class="modal-body">
<p>We have made large scale improvements to the dashboard and our host provisioning service to improve your user experience.</p>
- <p>{{ ipa_migrator.message }}</p>
- <form action="{{ipa_migrator.action}}" method="post">
+ <p id="form-message">
+ {% if ipa_status == "new" %}
+ Our records indicate that you do not currently have an account in our IPA system, or your usernames do not match. Please enter the following details to enroll your account.
+ {% elif ipa_status == "conflict" %}
+ Our records indicate that you do not currently have an account in our IPA system, or your emails do not match. Please enter the following details to enroll your account.
+ {% endif %}
+ </p>
+ <form>
{% csrf_token %}
- <p class="text-danger">{{error}}</p>
- {{ ipa_migrator.form }}
+
+ <p class="text-danger" id="error-msg"></p>
<div class="form-group">
- <input class="btn btn-success" name="submitButton" type="submit" value="{{ipa_migrator.button}}">
+
+ <label for="firstname">First Name:</label>
+ <input type="text" class="form-control" name="firstname" id="firstname"style="width: 300px;" placeholder="First Name">
+
+ <label for="lastname">Last Name:</label>
+ <input type="text" class="form-control" name="lastname" id="lastname" style="width: 300px;" placeholder="Last Name">
+
+ <label for="company">Company:</label>
+ <input type="text" class="form-control" name="company" id="company" style="width: 300px;" placeholder="Company">
+
+
+ <label for="username" {% if ipa_status != "conflict" %} hidden {% endif %}>New VPN Username:</label>
+ <input type="text" class="form-control" name="username" id="username" style="width: 300px;" placeholder="New VPN Username" {% if ipa_status != "conflict" %} hidden {% endif %}>
+
+ <label for="email">Email:</label>
+ <input type="text" class="form-control" name="email" id="email" style="width: 300px;" value="{{profile.email}}" disabled>
+
</div>
</form>
+
+ <button class="btn btn-success" onclick="submit_form()">Submit</button>
</div>
- </div>
</div>
- </div>
+</div>
+</div>
<script>
+ function collect_form_data() {
+ data = {
+ "firstname": document.getElementById("firstname").value,
+ "lastname": document.getElementById("lastname").value,
+ "company": document.getElementById("company").value,
+ "username": document.getElementById("username").value // Only present in conflict form
+ // Do not collect email, grab this from the UserProfile in django. Prevents hijacking of accounts.
+ };
+
+ return data;
+
+ }
+
+ function submit_form() {
+ const data = collect_form_data();
+
+ $.ajax({
+ url: '/liblaas/migrate/{{ipa_status}}/',
+ type: 'post',
+ data: JSON.stringify(data),
+ headers: {
+ 'X-CSRFToken': document.getElementsByName('csrfmiddlewaretoken')[0].value,
+ 'Content-Type': 'application/json'
+ },
+ dataType: 'text',
+ success: (response) => {
+ console.log("successful response is")
+ location.reload();
+ },
+ error: (response) => {
+ const r = JSON.parse(response.responseText)
+ document.getElementById("error-msg").innerText = r.message;
+ }
+ })
+ }
+
$(window).on('load', function() {
- if ({{ipa_migrator.exists}}) {
+
+ if ("{{ipa_status}}" == "new" || "{{ipa_status}}" == "conflict") {
$('#ipa-modal').modal({backdrop: 'static', keyboard: false});
$('#ipa-modal').modal('show');
}
- });
+});
</script>
{% endblock content %}
diff --git a/src/templates/base/workflow/book_a_pod.html b/src/templates/base/workflow/book_a_pod.html
index 5c1a253..6c79bfe 100644
--- a/src/templates/base/workflow/book_a_pod.html
+++ b/src/templates/base/workflow/book_a_pod.html
@@ -114,14 +114,21 @@
</div>
<div class="modal-body" id="add_resource_modal_body">
<p>Please update your information before creating a booking.</p>
- <form action="{{prereq_validator.action}}" method="post">
- {% csrf_token %}
- {{ prereq_validator.form }}
- <div class="form-group">
- <input class="btn btn-success" name="submitButton" type="submit" value="Save">
- </div>
+ <p id="error-msg" class="text-danger"></p>
+ <form>
+ {% csrf_token %}
+ <div class="form-group">
+ {% if prereqs.company == "false" %}
+ <label for="prereq_form">Company:</label>
+ <input type="text" class="form-control" style="width: 300px;" placeholder="Company" id="prereq_form" name="company">
+ {% elif prereqs.keys == "false" %}
+ <label for="prereq_form">SSH Public Key:</label>
+ <textarea id="prereq_form" placeholder="SSH Public Key" class="form-control" style="width: 500px;" id="prereq_form" name="ssh"></textarea>
+ {% endif %}
+ </div>
</form>
- </div>
+ <button class="btn btn-success" onclick="submit_form({% if prereqs.company == 'false' %} 'ipa/company/'{% elif prereqs.keys == 'false' %} 'ipa/ssh/' {% endif %})">Save</button>
+ </div>
</div>
</div>
</div>
@@ -145,10 +152,52 @@
</body>
<script>
+ function collect_form_data() {
+
+ const form = document.getElementById("prereq_form");
+
+ const data = {};
+ if (form.name == "company") {
+ data["data"] = form.value;
+ } else if (form.name == "ssh") {
+ data["data"] = [];
+ data["data"].push(form.value)
+ }
+
+ return data;
+
+ }
+
+ function submit_form(endpoint) {
+ const data = collect_form_data();
+
+ $.ajax({
+ url: '../../liblaas/' + endpoint,
+ type: 'post',
+ data: JSON.stringify(data),
+ headers: {
+ 'X-CSRFToken': document.getElementsByName('csrfmiddlewaretoken')[0].value,
+ 'Content-Type': 'application/json'
+ },
+ dataType: 'text',
+ success: (response) => {
+ location.reload();
+ },
+ error: (response) => {
+ const r = JSON.parse(response.responseText)
+ document.getElementById("error-msg").innerText = r.message;
+ }
+ })
+ }
+
+
+
+
let user;
let workflow;
$(window).on('load', function() {
- if ({{prereq_validator.exists}}) {
+
+ if (!{{prereqs.company}} || !{{prereqs.keys}}) {
$('#ipa-modal').modal({backdrop: 'static', keyboard: false});
$('#ipa-modal').modal('show');
} else {
@@ -157,7 +206,5 @@
workflow.startWorkflow();
}
});
-
-
</script>
{% endblock %}
diff --git a/src/workflow/views.py b/src/workflow/views.py
index c634b38..a85ac09 100644
--- a/src/workflow/views.py
+++ b/src/workflow/views.py
@@ -11,60 +11,59 @@ import json
from django.shortcuts import render, redirect
from laas_dashboard.settings import TEMPLATE_OVERRIDE
from django.http import HttpResponse
-from django.http.response import JsonResponse
+from liblaas.views import user_get_user
from workflow.forms import BookingMetaForm
-from api.views import liblaas_request, make_booking
-from api.utils import get_booking_prereqs_validator
from account.models import UserProfile
-def no_workflow(request):
- return render(request, 'workflow/no_workflow.html', {'title': "Not Found"}, status=404)
-
-
def login(request):
return render(request, "dashboard/login.html", {'title': 'Authentication Required'})
def design_a_pod_view(request):
- if request.method == "GET":
- if not request.user.is_authenticated:
- return login(request)
- prereq_validator = get_booking_prereqs_validator(UserProfile.objects.get(user=request.user))
- if (prereq_validator["action"] == "no user"):
- return redirect("dashboard:index")
- template = "workflow/design_a_pod.html"
- context = {
- "dashboard": str(TEMPLATE_OVERRIDE)
- }
- return render(request, template, context)
-
- if request.method == "POST":
- print("forwarding request to liblaas...")
- return liblaas_request(request)
+ if request.method != "GET":
+ return HttpResponse(status=405)
+
+ if not request.user.is_authenticated:
+ return login(request)
+
+ profile = UserProfile.objects.get(user=request.user)
+
+ if (not profile or profile.ipa_username == None):
+ return redirect("dashboard:index")
+
+ template = "workflow/design_a_pod.html"
+ context = {
+ "dashboard": str(TEMPLATE_OVERRIDE)
+ }
+ return render(request, template, context)
- return HttpResponse(status=405)
def book_a_pod_view(request):
- if request.method == "GET":
- if not request.user.is_authenticated:
- return login(request)
- prereq_validator = get_booking_prereqs_validator(UserProfile.objects.get(user=request.user))
- if (prereq_validator["action"] == "no user"):
- return redirect("dashboard:index")
- template = "workflow/book_a_pod.html"
- context = {
- "dashboard": str(TEMPLATE_OVERRIDE),
- "form": BookingMetaForm(initial={}, user_initial=[], owner=request.user),
- "prereq_validator": prereq_validator
- }
- return render(request, template, context)
+ if request.method != "GET":
+ return HttpResponse(status=405)
+
+ if not request.user.is_authenticated:
+ return login(request)
+
+ profile = UserProfile.objects.get(user=request.user)
+
+ if (not profile or profile.ipa_username == None):
+ return redirect("dashboard:index")
+
+ vpn_user = user_get_user(profile.ipa_username)
- if request.method == "POST":
- print("forwarding request to liblaas...")
- return liblaas_request(request)
+ # These booleans need to be represented as strings, due to the way jinja interprets them
+ prereqs = {
+ "company": "true" if ("ou" in vpn_user and vpn_user["ou"] != "") else "false",
+ "keys": "true" if ("ipasshpubkey" in vpn_user) and (len(vpn_user["ipasshpubkey"]) > 0) else "false"
+ }
+
+ template = "workflow/book_a_pod.html"
+ context = {
+ "dashboard": str(TEMPLATE_OVERRIDE),
+ "form": BookingMetaForm(initial={}, user_initial=[], owner=request.user),
+ "prereqs": prereqs
+ }
+ return render(request, template, context)
- # Using PUT to signal that we do not want to talk to liblaas
- if request.method == "PUT":
- return make_booking(request)
- return HttpResponse(status=405)