Removing project content and adding a note
[laas.git] / src / api / views.py
diff --git a/src/api/views.py b/src/api/views.py
deleted file mode 100644 (file)
index d5966ed..0000000
+++ /dev/null
@@ -1,756 +0,0 @@
-##############################################################################
-# Copyright (c) 2016 Max Breitenfeldt and others.
-# Copyright (c) 2018 Parker Berberian, 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
-##############################################################################
-
-import json
-import math
-import traceback
-import sys
-from datetime import timedelta
-
-from django.contrib.auth.decorators import login_required
-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
-from rest_framework import viewsets
-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 api.serializers.booking_serializer import BookingSerializer
-from api.serializers.old_serializers import UserSerializer
-from api.forms import DowntimeForm
-from account.models import UserProfile, Lab
-from booking.models import Booking
-from booking.quick_deployer import create_from_API
-from api.models import LabManagerTracker, get_task, Job, AutomationAPIManager, APILog, GeneratedCloudConfig
-from notifier.manager import NotificationHandler
-from analytics.models import ActiveVPNUser
-from resource_inventory.models import (
-    Image,
-    Opsys,
-    CloudInitFile,
-    ResourceQuery,
-    ResourceTemplate,
-)
-
-import yaml
-import uuid
-from deepmerge import Merger
-
-"""
-API views.
-
-All functions return a Json blob
-Most functions that deal with info from a specific lab (tasks, host info)
-requires the Lab auth token.
-    for example, curl -H auth-token:mylabsauthtoken url
-
-Most functions let you GET or POST to the same endpoint, and
-the correct thing will happen
-"""
-
-
-class BookingViewSet(viewsets.ModelViewSet):
-    queryset = Booking.objects.all()
-    serializer_class = BookingSerializer
-    filter_fields = ('resource', 'id')
-
-
-class UserViewSet(viewsets.ModelViewSet):
-    queryset = UserProfile.objects.all()
-    serializer_class = UserSerializer
-
-
-@method_decorator(login_required, name='dispatch')
-class GenerateTokenView(View):
-    def get(self, request, *args, **kwargs):
-        user = self.request.user
-        token, created = Token.objects.get_or_create(user=user)
-        if not created:
-            token.delete()
-            Token.objects.create(user=user)
-        return redirect('account:settings')
-
-
-def lab_inventory(request, lab_name=""):
-    lab_token = request.META.get('HTTP_AUTH_TOKEN')
-    lab_manager = LabManagerTracker.get(lab_name, lab_token)
-    return JsonResponse(lab_manager.get_inventory(), safe=False)
-
-
-@csrf_exempt
-def lab_host(request, lab_name="", host_id=""):
-    lab_token = request.META.get('HTTP_AUTH_TOKEN')
-    lab_manager = LabManagerTracker.get(lab_name, lab_token)
-    if request.method == "GET":
-        return JsonResponse(lab_manager.get_host(host_id), safe=False)
-    if request.method == "POST":
-        return JsonResponse(lab_manager.update_host(host_id, request.POST), safe=False)
-
-# API extension for Cobbler integration
-
-
-def all_images(request, lab_name=""):
-    a = []
-    for i in Image.objects.all():
-        a.append(i.serialize())
-    return JsonResponse(a, safe=False)
-
-
-def all_opsyss(request, lab_name=""):
-    a = []
-    for opsys in Opsys.objects.all():
-        a.append(opsys.serialize())
-
-    return JsonResponse(a, safe=False)
-
-
-@csrf_exempt
-def single_image(request, lab_name="", image_id=""):
-    lab_token = request.META.get('HTTP_AUTH_TOKEN')
-    lab_manager = LabManagerTracker.get(lab_name, lab_token)
-    img = lab_manager.get_image(image_id).first()
-
-    if request.method == "GET":
-        if not img:
-            return HttpResponse(status=404)
-        return JsonResponse(img.serialize(), safe=False)
-
-    if request.method == "POST":
-        # get POST data
-        data = json.loads(request.body.decode('utf-8'))
-        if img:
-            img.update(data)
-        else:
-            # append lab name and the ID from the URL
-            data['from_lab_id'] = lab_name
-            data['lab_id'] = image_id
-
-            # create and save a new Image object
-            img = Image.new_from_data(data)
-
-        img.save()
-
-        # indicate success in response
-        return HttpResponse(status=200)
-    return HttpResponse(status=405)
-
-
-@csrf_exempt
-def single_opsys(request, lab_name="", opsys_id=""):
-    lab_token = request.META.get('HTTP_AUTH_TOKEN')
-    lab_manager = LabManagerTracker.get(lab_name, lab_token)
-    opsys = lab_manager.get_opsys(opsys_id).first()
-
-    if request.method == "GET":
-        if not opsys:
-            return HttpResponse(status=404)
-        return JsonResponse(opsys.serialize(), safe=False)
-
-    if request.method == "POST":
-        data = json.loads(request.body.decode('utf-8'))
-        if opsys:
-            opsys.update(data)
-        else:
-            # only name, available, and obsolete are needed to create an Opsys
-            # other fields are derived from the URL parameters
-            data['from_lab_id'] = lab_name
-            data['lab_id'] = opsys_id
-            opsys = Opsys.new_from_data(data)
-
-        opsys.save()
-        return HttpResponse(status=200)
-    return HttpResponse(status=405)
-
-# end API extension
-
-
-def get_pdf(request, lab_name="", booking_id=""):
-    lab_token = request.META.get('HTTP_AUTH_TOKEN')
-    lab_manager = LabManagerTracker.get(lab_name, lab_token)
-    return HttpResponse(lab_manager.get_pdf(booking_id), content_type="text/plain")
-
-
-def get_idf(request, lab_name="", booking_id=""):
-    lab_token = request.META.get('HTTP_AUTH_TOKEN')
-    lab_manager = LabManagerTracker.get(lab_name, lab_token)
-    return HttpResponse(lab_manager.get_idf(booking_id), content_type="text/plain")
-
-
-def lab_status(request, lab_name=""):
-    lab_token = request.META.get('HTTP_AUTH_TOKEN')
-    lab_manager = LabManagerTracker.get(lab_name, lab_token)
-    if request.method == "POST":
-        return JsonResponse(lab_manager.set_status(request.POST), safe=False)
-    return JsonResponse(lab_manager.get_status(), safe=False)
-
-
-def lab_users(request, lab_name=""):
-    lab_token = request.META.get('HTTP_AUTH_TOKEN')
-    lab_manager = LabManagerTracker.get(lab_name, lab_token)
-    return HttpResponse(lab_manager.get_users(), content_type="text/plain")
-
-
-def lab_user(request, lab_name="", user_id=-1):
-    lab_token = request.META.get('HTTP_AUTH_TOKEN')
-    lab_manager = LabManagerTracker.get(lab_name, lab_token)
-    return HttpResponse(lab_manager.get_user(user_id), content_type="text/plain")
-
-
-@csrf_exempt
-def update_host_bmc(request, lab_name="", host_id=""):
-    lab_token = request.META.get('HTTP_AUTH_TOKEN')
-    lab_manager = LabManagerTracker.get(lab_name, lab_token)
-    if request.method == "POST":
-        # update / create RemoteInfo for host
-        return JsonResponse(
-            lab_manager.update_host_remote_info(request.POST, host_id),
-            safe=False
-        )
-
-
-def lab_profile(request, lab_name=""):
-    lab_token = request.META.get('HTTP_AUTH_TOKEN')
-    lab_manager = LabManagerTracker.get(lab_name, lab_token)
-    return JsonResponse(lab_manager.get_profile(), safe=False)
-
-
-@csrf_exempt
-def specific_task(request, lab_name="", job_id="", task_id=""):
-    lab_token = request.META.get('HTTP_AUTH_TOKEN')
-    LabManagerTracker.get(lab_name, lab_token)  # Authorize caller, but we dont need the result
-
-    if request.method == "POST":
-        task = get_task(task_id)
-        if 'status' in request.POST:
-            task.status = request.POST.get('status')
-        if 'message' in request.POST:
-            task.message = request.POST.get('message')
-        if 'lab_token' in request.POST:
-            task.lab_token = request.POST.get('lab_token')
-        task.save()
-        NotificationHandler.task_updated(task)
-        d = {}
-        d['task'] = task.config.get_delta()
-        m = {}
-        m['status'] = task.status
-        m['job'] = str(task.job)
-        m['message'] = task.message
-        d['meta'] = m
-        return JsonResponse(d, safe=False)
-    elif request.method == "GET":
-        return JsonResponse(get_task(task_id).config.get_delta())
-
-
-@csrf_exempt
-def specific_job(request, lab_name="", job_id=""):
-    lab_token = request.META.get('HTTP_AUTH_TOKEN')
-    lab_manager = LabManagerTracker.get(lab_name, lab_token)
-    if request.method == "POST":
-        return JsonResponse(lab_manager.update_job(job_id, request.POST), safe=False)
-    return JsonResponse(lab_manager.get_job(job_id), safe=False)
-
-
-@csrf_exempt
-def resource_ci_userdata(request, lab_name="", job_id="", resource_id="", file_id=0):
-    # lab_token = request.META.get('HTTP_AUTH_TOKEN')
-    # lab_manager = LabManagerTracker.get(lab_name, lab_token)
-
-    # job = lab_manager.get_job(job_id)
-    Job.objects.get(id=job_id)  # verify a valid job was given, even if we don't use it
-
-    cifile = None
-    try:
-        cifile = CloudInitFile.objects.get(id=file_id)
-    except ObjectDoesNotExist:
-        return HttpResponseNotFound("Could not find a matching resource by id " + str(resource_id))
-
-    text = cifile.text
-
-    prepended_text = "#cloud-config\n"
-    # mstrat = CloudInitFile.merge_strategy()
-    # prepended_text = prepended_text + yaml.dump({"merge_strategy": mstrat}) + "\n"
-    # print("in cloudinitfile create")
-    text = prepended_text + text
-    cloud_dict = {
-        "datasource": {
-            "None": {
-                "metadata": {
-                    "instance-id": str(uuid.uuid4())
-                },
-                "userdata_raw": text,
-            },
-        },
-        "datasource_list": ["None"],
-    }
-
-    return HttpResponse(yaml.dump(cloud_dict, width=float("inf")), status=200)
-
-
-@csrf_exempt
-def resource_ci_metadata(request, lab_name="", job_id="", resource_id="", file_id=0):
-    return HttpResponse("#cloud-config", status=200)
-
-
-@csrf_exempt
-def resource_ci_userdata_directory(request, lab_name="", job_id="", resource_id=""):
-    # files = [{"id": file.file_id, "priority": file.priority} for file in CloudInitFile.objects.filter(job__id=job_id, resource_id=resource_id).order_by("priority").all()]
-    resource = ResourceQuery.get(labid=resource_id, lab=Lab.objects.get(name=lab_name))
-    files = resource.config.cloud_init_files
-    files = [{"id": file.id, "priority": file.priority} for file in files.order_by("priority").all()]
-
-    d = {}
-
-    merge_failures = []
-
-    merger = Merger(
-        [
-            (list, ["append"]),
-            (dict, ["merge"]),
-        ],
-        ["override"],  # fallback
-        ["override"],  # if types conflict (shouldn't happen in CI, but handle case)
-    )
-
-    for f in resource.config.cloud_init_files.order_by("priority").all():
-        try:
-            other_dict = yaml.safe_load(f.text)
-            if not (type(d) is dict):
-                raise Exception("CI file was valid yaml but was not a dict")
-
-            merger.merge(d, other_dict)
-        except Exception as e:
-            # if fail to merge, then just skip
-            print("Failed to merge file in, as it had invalid content:", f.id)
-            print("File text was:")
-            print(f.text)
-            merge_failures.append({f.id: str(e)})
-
-    if len(merge_failures) > 0:
-        d['merge_failures'] = merge_failures
-
-    file = CloudInitFile.create(text=yaml.dump(d, width=float("inf")), priority=0)
-
-    return HttpResponse(json.dumps([{"id": file.id, "priority": file.priority}]), status=200)
-
-
-def new_jobs(request, lab_name=""):
-    lab_token = request.META.get('HTTP_AUTH_TOKEN')
-    lab_manager = LabManagerTracker.get(lab_name, lab_token)
-    return JsonResponse(lab_manager.get_new_jobs(), safe=False)
-
-
-def current_jobs(request, lab_name=""):
-    lab_token = request.META.get('HTTP_AUTH_TOKEN')
-    lab_manager = LabManagerTracker.get(lab_name, lab_token)
-    return JsonResponse(lab_manager.get_current_jobs(), safe=False)
-
-
-@csrf_exempt
-def analytics_job(request, lab_name=""):
-    """ returns all jobs with type booking"""
-    lab_token = request.META.get('HTTP_AUTH_TOKEN')
-    lab_manager = LabManagerTracker.get(lab_name, lab_token)
-    if request.method == "GET":
-        return JsonResponse(lab_manager.get_analytics_job(), safe=False)
-    if request.method == "POST":
-        users = json.loads(request.body.decode('utf-8'))['active_users']
-        try:
-            ActiveVPNUser.create(lab_name, users)
-        except ObjectDoesNotExist:
-            return JsonResponse('Lab does not exist!', safe=False)
-        return HttpResponse(status=200)
-    return HttpResponse(status=405)
-
-
-def lab_downtime(request, lab_name=""):
-    lab_token = request.META.get('HTTP_AUTH_TOKEN')
-    lab_manager = LabManagerTracker.get(lab_name, lab_token)
-    if request.method == "GET":
-        return JsonResponse(lab_manager.get_downtime_json())
-    if request.method == "POST":
-        return post_lab_downtime(request, lab_manager)
-    if request.method == "DELETE":
-        return delete_lab_downtime(lab_manager)
-    return HttpResponse(status=405)
-
-
-def post_lab_downtime(request, lab_manager):
-    current_downtime = lab_manager.get_downtime()
-    if current_downtime.exists():
-        return JsonResponse({"error": "Lab is already in downtime"}, status=422)
-    form = DowntimeForm(request.POST)
-    if form.is_valid():
-        return JsonResponse(lab_manager.create_downtime(form))
-    else:
-        return JsonResponse(form.errors.get_json_data(), status=400)
-
-
-def delete_lab_downtime(lab_manager):
-    current_downtime = lab_manager.get_downtime()
-    if current_downtime.exists():
-        dt = current_downtime.first()
-        dt.end = timezone.now()
-        dt.save()
-        return JsonResponse(lab_manager.get_downtime_json(), safe=False)
-    else:
-        return JsonResponse({"error": "Lab is not in downtime"}, status=422)
-
-
-def done_jobs(request, lab_name=""):
-    lab_token = request.META.get('HTTP_AUTH_TOKEN')
-    lab_manager = LabManagerTracker.get(lab_name, lab_token)
-    return JsonResponse(lab_manager.get_done_jobs(), safe=False)
-
-
-def auth_and_log(request, endpoint):
-    """
-    Function to authenticate an API user and log info
-    in the API log model. This is to keep record of
-    all calls to the dashboard
-    """
-    user_token = request.META.get('HTTP_AUTH_TOKEN')
-    response = None
-
-    if user_token is None:
-        return HttpResponse('Unauthorized', status=401)
-
-    try:
-        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:
-            response = HttpResponse('Unauthorized', status=401)
-
-    x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
-    if x_forwarded_for:
-        ip = x_forwarded_for.split(',')[0]
-    else:
-        ip = request.META.get('REMOTE_ADDR')
-
-    body = None
-
-    if request.method in ['POST', 'PUT']:
-        try:
-            body = json.loads(request.body.decode('utf-8')),
-        except Exception:
-            response = HttpResponse('Invalid Request Body', status=400)
-
-    APILog.objects.create(
-        user=token.user,
-        call_time=timezone.now(),
-        method=request.method,
-        endpoint=endpoint,
-        body=body,
-        ip_addr=ip
-    )
-
-    if response:
-        return response
-    else:
-        return token
-
-
-"""
-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)
-
-
-@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")
-
-
-@csrf_exempt
-def extend_booking(request, booking_id="", days=""):
-    token = auth_and_log(request, 'booking/{}/extendBooking/{}'.format(booking_id, days))
-
-    if isinstance(token, HttpResponse):
-        return token
-
-    booking = get_object_or_404(Booking, pk=booking_id, owner=token.user)
-
-    if booking.end < timezone.now():
-        return HttpResponse("This booking is already over, cannot extend")
-
-    if days > 30:
-        return HttpResponse("Cannot extend a booking longer than 30 days")
-
-    if booking.ext_count == 0:
-        return HttpResponse("Booking has already been extended 2 times, cannot extend again")
-
-    booking.end += timedelta(days=days)
-    booking.ext_count -= 1
-    booking.save()
-
-    return HttpResponse("Booking successfully extended")
-
-
-@csrf_exempt
-def make_booking(request):
-    token = auth_and_log(request, 'booking/makeBooking')
-
-    if isinstance(token, HttpResponse):
-        return token
-
-    try:
-        booking = create_from_API(request.body, token.user)
-
-    except Exception:
-        finalTrace = ''
-        exc_type, exc_value, exc_traceback = sys.exc_info()
-        for i in traceback.format_exception(exc_type, exc_value, exc_traceback):
-            finalTrace += '<br>' + i.strip()
-        return HttpResponse(finalTrace, status=400)
-
-    sbooking = AutomationAPIManager.serialize_booking(booking)
-    return JsonResponse(sbooking, safe=False)
-
-
-"""
-Resource Inventory API Views
-"""
-
-
-def available_templates(request):
-    token = auth_and_log(request, 'resource_inventory/availableTemplates')
-
-    if isinstance(token, HttpResponse):
-        return token
-
-    # get available templates
-    # mirrors MultipleSelectFilter Widget
-    avt = []
-    for lab in Lab.objects.all():
-        for template in ResourceTemplate.objects.filter(Q(owner=token.user) | Q(public=True), lab=lab, temporary=False):
-            available_resources = lab.get_available_resources()
-            required_resources = template.get_required_resources()
-            least_available = 100
-
-            for resource, count_required in required_resources.items():
-                try:
-                    curr_count = math.floor(available_resources[str(resource)] / count_required)
-                    if curr_count < least_available:
-                        least_available = curr_count
-                except KeyError:
-                    least_available = 0
-
-            if least_available > 0:
-                avt.append((template, least_available))
-
-    savt = [AutomationAPIManager.serialize_template(temp)
-            for temp in avt]
-
-    return JsonResponse(savt, safe=False)
-
-
-def images_for_template(request, template_id=""):
-    _ = auth_and_log(request, 'resource_inventory/{}/images'.format(template_id))
-
-    template = get_object_or_404(ResourceTemplate, pk=template_id)
-    images = [AutomationAPIManager.serialize_image(config.image)
-              for config in template.getConfigs()]
-    return JsonResponse(images, safe=False)
-
-
-"""
-User API Views
-"""
-
-
-def all_users(request):
-    token = auth_and_log(request, 'users')
-
-    if token is None:
-        return HttpResponse('Unauthorized', status=401)
-
-    users = [AutomationAPIManager.serialize_userprofile(up)
-             for up in UserProfile.objects.filter(public_user=True)]
-
-    return JsonResponse(users, safe=False)
-
-
-def create_ci_file(request):
-    token = auth_and_log(request, 'booking/makeCloudConfig')
-
-    if isinstance(token, HttpResponse):
-        return token
-
-    try:
-        cconf = request.body
-        d = yaml.load(cconf)
-        if not (type(d) is dict):
-            raise Exception()
-
-        cconf = CloudInitFile.create(text=cconf, priority=CloudInitFile.objects.count())
-
-        return JsonResponse({"id": cconf.id})
-    except Exception:
-        return JsonResponse({"error": "Provided config file was not valid yaml or was not a dict at the top level"})
-
-
-"""
-Lab API Views
-"""
-
-
-def list_labs(request):
-    lab_list = []
-    for lab in Lab.objects.all():
-        lab_info = {
-            'name': lab.name,
-            'username': lab.lab_user.username,
-            'status': lab.status,
-            'project': lab.project,
-            'description': lab.description,
-            'location': lab.location,
-            'info': lab.lab_info_link,
-            'email': lab.contact_email,
-            'phone': lab.contact_phone
-        }
-        lab_list.append(lab_info)
-
-    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)