Single Template Selection For Compatible Dashboards 32/74232/3 liblaas-mvp
authorJustin Choquette <jchoquette@iol.unh.edu>
Mon, 23 Oct 2023 20:24:13 +0000 (16:24 -0400)
committerJustin Choquette <jchoquette@iol.unh.edu>
Wed, 25 Oct 2023 20:26:17 +0000 (16:26 -0400)
Change-Id: I0a795c2c49fdbe0427182a8789d622003997efcd
Signed-off-by: Justin Choquette <jchoquette@iol.unh.edu>
14 files changed:
config.env.sample
src/dashboard/views.py
src/laas_dashboard/settings.py
src/liblaas/endpoints.py
src/liblaas/views.py
src/resource_inventory/views.py
src/static/js/workflows/design-a-pod.js
src/templates/anuket/base.html [moved from src/templates/laas/base.html with 100% similarity]
src/templates/anuket/dashboard/landing.html [moved from src/templates/laas/dashboard/landing.html with 100% similarity]
src/templates/anuket/layout.html [moved from src/templates/laas/layout.html with 100% similarity]
src/templates/base/workflow/design_a_pod.html
src/templates/lfedge/base.html
src/templates/lfedge/dashboard/landing.html
src/workflow/views.py

index cf32c73..002cb7a 100644 (file)
@@ -63,7 +63,7 @@ JENKINS_URL=https://build.opnfv.org/ci
 BOOKING_EXPIRE_TIME=30
 BOOKING_MAXIMUM_NUMBER=10
 
-TEMPLATE_OVERRIDE_DIR=laas
+PROJECT=anuket
 
 LIBLAAS_BASE_URL=http://<address>:<port>/
 
index 2a8b43f..55be8fb 100644 (file)
@@ -20,6 +20,7 @@ from django.http import HttpResponse
 from account.models import Lab, UserProfile
 from booking.models import Booking
 from laas_dashboard import settings
+from laas_dashboard.settings import PROJECT
 from liblaas.utils import get_ipa_status
 
 from liblaas.views import flavor_list_flavors, flavor_list_hosts
@@ -38,15 +39,13 @@ def lab_detail_view(request, lab_name):
         user = request.user
 
     lab = get_object_or_404(Lab, name=lab_name)
-    origin = "anuket" if os.environ.get("TEMPLATE_OVERRIDE_DIR") == 'laas' else "lfedge"
-
-    flavors_list = flavor_list_flavors(origin)
-    host_list = flavor_list_hosts(origin)
+    flavors_list = flavor_list_flavors(PROJECT)
+    host_list = flavor_list_hosts(PROJECT)
 
     flavor_map = {}
     for flavor in flavors_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:
index a56265e..22f33ef 100644 (file)
@@ -87,15 +87,15 @@ if os.environ.get('EXPECT_HOST_FORWARDING') == 'True':
 
 ROOT_URLCONF = 'laas_dashboard.urls'
 
-TEMPLATE_OVERRIDE = os.environ.get("TEMPLATE_OVERRIDE_DIR", "")  # the user's custom template dir
+PROJECT = os.environ.get("PROJECT", "")  # the project for the current deployment (i.e. anuket or lfedge)
 TEMPLATE_DIRS = ["base"]  # where all the base templates are
 
 # If the user has a custom template directory,
 # We should search that first. Then we search the
 # root template directory so that we can extend the base
 # templates within the custom template dir.
-if TEMPLATE_OVERRIDE:
-    TEMPLATE_DIRS = [TEMPLATE_OVERRIDE, ""] + TEMPLATE_DIRS
+if PROJECT:
+    TEMPLATE_DIRS = [PROJECT, ""] + TEMPLATE_DIRS
 
 # all template dirs are relative to /project_root/templates/
 dirs = [os.path.join(BASE_DIR, "templates", d) for d in TEMPLATE_DIRS]
index 64e5126..55611f3 100644 (file)
@@ -18,6 +18,7 @@ from booking.models import Booking
 from account.models import Lab
 from django.utils import timezone
 from datetime import timedelta
+from laas_dashboard.settings import PROJECT
 
 def request_list_flavors(request) -> HttpResponse:
     data = json.loads(request.body.decode('utf-8'))
@@ -90,7 +91,7 @@ def request_create_booking(request) -> HttpResponse:
         "project": data["metadata"]["project"],
         "length": int(data["metadata"]["length"])
     },
-    "origin": "anuket" if os.environ.get("TEMPLATE_OVERRIDE_DIR") == 'laas' else "lfedge" # todo - refactor
+    "origin": PROJECT
     }
 
     # Create booking in dashboard
index 5edc727..8eb1dd0 100644 (file)
@@ -74,20 +74,9 @@ def flavor_list_flavors(project: str) -> requests.Response:
         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}'
+    endpoint = f'flavor/hosts/{project}' #todo - support project in liblaas
     url = f'{base}{endpoint}'
     try:
         response = requests.get(url)
@@ -100,7 +89,7 @@ def flavor_list_hosts(project: str) -> requests.Response:
 
 # GET
 def template_list_templates(uid: str) -> requests.Response:
-    endpoint = f'template/list/{uid}'
+    endpoint = f'template/list/{uid}' # todo - templates need to be restricted by project
     url = f'{base}{endpoint}'
     try:
         response = requests.get(url)
@@ -122,7 +111,7 @@ def template_delete_template(template_id: str) -> requests.Response:
 
 #POST
 def template_make_template(template_blob: dict) -> requests.Response:
-    endpoint = f'template/create'
+    endpoint = f'template/create' # todo - needs to be restricted by project
     url = f'{base}{endpoint}'
     try:
         response = requests.post(url, data=json.dumps(template_blob), headers=post_headers)
index b9e3490..e23326b 100644 (file)
@@ -11,17 +11,16 @@ import json
 import os
 from django.shortcuts import render
 from django.http import HttpResponse
+from laas_dashboard.settings import PROJECT
 
 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":
         return HttpResponse(status=405)
 
-    host_list = flavor_list_hosts(origin)
-    flavor_list = flavor_list_flavors(origin)
+    host_list = flavor_list_hosts(PROJECT)
+    flavor_list = flavor_list_flavors(PROJECT)
 
     flavor_map = {}
     for flavor in flavor_list:
@@ -46,7 +45,7 @@ def profile_view(request, resource_id):
     if request.method != "GET":
         return HttpResponse(status=405)
 
-    flavor_list = flavor_list_flavors(origin)
+    flavor_list = flavor_list_flavors(PROJECT)
     selected_flavor = {}
     for flavor in flavor_list:
         if flavor["flavor_id"] == resource_id:
index efec093..3c8652e 100644 (file)
@@ -113,8 +113,8 @@ class DesignWorkflow extends Workflow {
           return;
       }
 
-      if (this.templateBlob.host_list.length >= 8) {
-        showError("You may not add more than 8 hosts to a single pod.", -1)
+      if (max_hosts && this.templateBlob.host_list.length >= max_hosts) {
+        showError(`You may not add more than ${max_hosts} hosts to a single pod.`, -1)
         return;
       }
 
@@ -246,7 +246,7 @@ class DesignWorkflow extends Workflow {
       for (const [index, host] of this.resourceBuilder.user_configs.entries()) {
         const new_host = new HostConfigBlob(host);
         this.templateBlob.host_list.push(new_host);
-        this.labFlavors.get(host.flavor).available_count--      
+        this.labFlavors.get(host.flavor).available_count--
       }
 
       // Add networks
@@ -256,17 +256,17 @@ class DesignWorkflow extends Workflow {
         }
       }
 
-        // We are done
-        GUI.refreshHostStep(this.templateBlob.host_list, this.labFlavors, this.labImages);
-        GUI.refreshNetworkStep(this.templateBlob.networks);
-        GUI.refreshConnectionStep(this.templateBlob.host_list);
-        GUI.refreshPodSummaryHosts(this.templateBlob.host_list, this.labFlavors, this.labImages)
-        $('#resource_modal').modal('hide')
+      // We are done
+      GUI.refreshHostStep(this.templateBlob.host_list, this.labFlavors, this.labImages);
+      GUI.refreshNetworkStep(this.templateBlob.networks);
+      GUI.refreshConnectionStep(this.templateBlob.host_list);
+      GUI.refreshPodSummaryHosts(this.templateBlob.host_list, this.labFlavors, this.labImages)
+      $('#resource_modal').modal('hide')
     }
 
     /**
      * Takes a hostname, looks for the matching HostConfigBlob in the TemplateBlob, removes it from the list, and refreshes the appropriate views
-     * @param {String} hostname 
+     * @param {String} hostname
      */
     onclickDeleteHost(hostname) {
       this.goTo(steps.ADD_RESOURCES);
@@ -536,9 +536,9 @@ class DesignWorkflow extends Workflow {
         passed = false;
         message = "Please select a lab";
         step = steps.SELECT_LAB;
-      } else if (this.templateBlob.host_list.length < 1 || this.templateBlob.host_list.length > 8) {
+      } else if (this.templateBlob.host_list.length < 1 || (max_hosts && this.templateBlob.host_list.length > max_hosts)) {
         passed = false;
-        message = "Pods must contain 1 to 8 hosts";
+        message = "Pod contains invalid number of resources.";
         step = steps.ADD_RESOURCES;
       } else if (this.templateBlob.networks.length < 1) {
         passed = false;
@@ -817,7 +817,7 @@ class GUI {
 
     /**
      * Refreshes the step and creates a card for each host in the hostlist
-     * @param {List<HostConfigBlob>} hostlist 
+     * @param {List<HostConfigBlob>} hostlist
      */
     static refreshHostStep(hostlist, flavors, images) {
       const host_cards = document.getElementById('host_cards');
@@ -827,27 +827,41 @@ class GUI {
       }
 
       let span_class = ''
-      if (hostlist.length == 8) {
-        span_class = 'text-primary'
-      } else if (hostlist.length > 8) {
-        span_class = 'text-danger'
+      if (max_hosts) {
+        if (hostlist.length == max_hosts) {
+          span_class = 'text-primary'
+        } else if (hostlist.length > max_hosts) {
+          span_class = 'text-danger'
+        }
       }
+
       const plus_card = document.createElement("div");
       plus_card.classList.add("col-xl-3", "col-md-6", "col-12");
       plus_card.id = "add_resource_plus_card";
-      plus_card.innerHTML = `
-      <div class="card align-items-center border-0">
-      <span class="` + span_class + `" id="resource-count">` + hostlist.length + `/ 8</span>
-      <button class="btn btn-success add-button p-0" onclick="workflow.onclickAddResource()">+</button>
-      </div>
-      `
 
-      host_cards.appendChild(plus_card);
+      if (max_hosts) {
+        plus_card.innerHTML = `
+        <div class="card align-items-center border-0">
+        <span class="${span_class}" id="resource-count">${hostlist.length} / ${max_hosts}</span>
+        <button class="btn btn-success add-button p-0" onclick="workflow.onclickAddResource()">+</button>
+        </div>
+        `
+      } else {
+        plus_card.innerHTML = `
+        <div class="card align-items-center border-0">
+        <button class="btn btn-success add-button p-0" onclick="workflow.onclickAddResource()">+</button>
+        </div>
+        `
+      }
+
+      if (max_hosts || hostlist.length == 0) {
+        host_cards.appendChild(plus_card);
+      }
     }
 
     /**
      * Makes a host card element for a given host and returns a reference to the card
-     * @param {HostConfigBlob} host 
+     * @param {HostConfigBlob} host
      */
     static makeHostCard(host, flavors, images) {
       const new_card = document.createElement("div");
index c23e5a8..4804792 100644 (file)
@@ -7,8 +7,6 @@
 <script src="/static/js/workflows/design-a-pod.js"></script>
 {% endblock %}
 {% block content %}
-{% if dashboard == 'laas' %}
-
 <!-- Main Content -->
 <body>
 
         <div class="arrow arrow-up"></div>
       </button>
     </div>
-  
+
     <div id="next" class="row w-100 m-0">
       <button class="btn btn-workflow-nav stretched-link m-0 p-0 mb-3" onclick="workflow.goNext()" id="workflow-next">
         <div class="arrow arrow-down"></div>
       </button>
     </div>
-  
+
     <div class="scroll-container w-100 h-100 p-0">
 
       <!-- Select Lab -->
       <!-- Add Resources -->
       <div class="scroll-area pt-5 mx-5" id="add_resources">
         <h2 class="mt-4 mb-3">Add Resources<span class="text-danger">*</span></h2>
-        <p>Add up to 8 configurable resources to your pod, then use the navigation arrows to proceed to the next step.</p>
+        {% if constraints.max_hosts != "null" %}
+        <p>Add up to {{constraints.max_hosts}} configurable resources to your pod, then use the navigation arrows to proceed to the next step.</p>
+        {% else %}
+        <p>Select a resource bundle that you would like to configure. To change the selected resource bundle, remove all added resources.</p>
+        {% endif %}
         <div class="row card-deck align-items-center" id="host_cards">
           <div class="col-xl-3 col-md-6 col-12" id="add_resource_plus_card">
             <div class="card align-items-center border-0">
-              <span class="" id="resource-count">0 / 8</span>
+              {% if constraints.max_hosts != "null" %}
+              <span class="" id="resource-count">0 / {{constraints.max_hosts}}</span>
+              {% endif %}
               <button class="btn btn-success add-button p-0" onclick="workflow.onclickAddResource()">+</button>
             </div>
           </div>
         </div>
       </div>
 
-      <!-- Add Networks --> 
+      <!-- Add Networks -->
     <div class="scroll-area pt-5 mx-5" id="add_networks">
       <h2 class="mt-4 mb-3">Add Networks<span class="text-danger">*</span></h2>
       <p>Define networks to use in your pod. A network may be set as public or private.</p>
 
 <script>
   const user = "{{user}}"
+  const max_hosts = {{constraints.max_hosts}}
   const workflow = new DesignWorkflow();
   workflow.startWorkflow();
 </script>
-{% endif %}
 {% endblock %}
index 4413340..b8ed8c8 100644 (file)
@@ -17,8 +17,6 @@
     </a>
 </div>
 {% endblock logo %}
-{% block dropDown %}
-{% endblock dropDown %}
 {% block userDropDownText %}
 <a class="nav-link p-0 wtext p-2" data-toggle="dropdown" href="#">
     {% if request.user.username %}
index 9a776dc..f04ed4d 100644 (file)
@@ -1,23 +1,6 @@
 {% extends "base/dashboard/landing.html" %}
+
 {% block about_us %}
     <p>The Shared Community Lab at the IOL aims to help development and testing of LF Edge projects by hosting hardware and providing access to the community.</p>
     <p>To get started, you can request access to a pod at the right.</p>
 {% endblock about_us %}
-
-{% block btnGrp %}
-<style>
-.btnLFEdge {
-    color: #fff;
-    background-color: #0049b0;
-}
-.btnLFEdge:hover{
-    color: #fff;
-    background-color: #001776;
-}
-</style>
-<p>To get started, book a pod below:</p>
-<a class="btn btnLFEdge btn-lg d-flex flex-column justify-content-center align-content-center border text-white p-4" href="/booking/quick/">Book a Pod</a>
-{% endblock btnGrp %}
-
-{% block returningUsers %}
-{% endblock returningUsers %}
index a85ac09..947c177 100644 (file)
@@ -7,9 +7,8 @@
 # http://www.apache.org/licenses/LICENSE-2.0
 ##############################################################################
 
-import json
 from django.shortcuts import render, redirect
-from laas_dashboard.settings import TEMPLATE_OVERRIDE
+from laas_dashboard.settings import PROJECT
 from django.http import HttpResponse
 from liblaas.views import user_get_user
 from workflow.forms import BookingMetaForm
@@ -31,9 +30,10 @@ def design_a_pod_view(request):
     if (not profile or profile.ipa_username == None):
         return redirect("dashboard:index")
 
+    constraints = get_workflow_contraints(PROJECT)
     template = "workflow/design_a_pod.html"
     context = {
-        "dashboard": str(TEMPLATE_OVERRIDE)
+        "constraints": constraints,
     }
     return render(request, template, context)
 
@@ -51,7 +51,7 @@ def book_a_pod_view(request):
         return redirect("dashboard:index")
 
     vpn_user = user_get_user(profile.ipa_username)
-    
+
     # 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",
@@ -60,10 +60,23 @@ def book_a_pod_view(request):
 
     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)
 
+def get_workflow_contraints(project: str) -> dict:
+
+    if project == 'anuket':
+        return {
+            "max_hosts": 8,
+        }
+
+    if project == 'lfedge':
+        return {
+            "max_hosts": "null",
+        }
+
+    return {}
+