diff options
Diffstat (limited to 'src/api')
-rw-r--r-- | src/api/models.py | 13 | ||||
-rw-r--r-- | src/api/urls.py | 18 | ||||
-rw-r--r-- | src/api/utils.py | 132 | ||||
-rw-r--r-- | src/api/views.py | 482 |
4 files changed, 1 insertions, 644 deletions
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 - ) |