aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/api/views.py2
-rw-r--r--src/booking/views.py2
-rw-r--r--src/static/js/workflows/book-a-pod.js32
-rw-r--r--src/static/js/workflows/design-a-pod.js42
-rw-r--r--src/static/js/workflows/workflow.js41
-rw-r--r--src/templates/base/booking/booking_detail.html146
-rw-r--r--src/templates/base/dashboard/lab_detail.html6
-rw-r--r--src/templates/base/dashboard/landing.html10
-rw-r--r--src/templates/base/workflow/book_a_pod.html18
-rw-r--r--src/templates/base/workflow/design_a_pod.html28
10 files changed, 199 insertions, 128 deletions
diff --git a/src/api/views.py b/src/api/views.py
index dbe00e8..fab78ee 100644
--- a/src/api/views.py
+++ b/src/api/views.py
@@ -575,7 +575,7 @@ def get_booking_status(bookingObject):
return json.loads(response.content)
except:
print("failed to get status")
- return []
+ return {}
def liblaas_end_booking(aggregateId):
liblaas_url = liblaas_base_url + "booking/" + str(aggregateId) + "/end"
diff --git a/src/booking/views.py b/src/booking/views.py
index 25cac43..c99e2c2 100644
--- a/src/booking/views.py
+++ b/src/booking/views.py
@@ -87,7 +87,7 @@ def booking_detail_view(request, booking_id):
context = {
'title': 'Booking Details',
'booking': booking,
- 'statuses': statuses,
+ 'status': statuses,
'collab_string': ', '.join(map(str, booking.collaborators.all()))
}
diff --git a/src/static/js/workflows/book-a-pod.js b/src/static/js/workflows/book-a-pod.js
index ddea556..2396fdb 100644
--- a/src/static/js/workflows/book-a-pod.js
+++ b/src/static/js/workflows/book-a-pod.js
@@ -64,7 +64,7 @@ const steps = {
}
onclickSelectTemplate(templateCard, templateId) {
- this.step = steps.SELECT_TEMPLATE
+ this.goTo(steps.SELECT_TEMPLATE)
const oldHighlight = document.querySelector("#default_templates_list .selected_node")
if (oldHighlight) {
GUI.unhighlightCard(oldHighlight)
@@ -90,12 +90,6 @@ const steps = {
return[passed, message]
}
- if (!(project.match(/^[a-z0-9~@#$^*()_+=[\]{}|,.?': -]+$/i))) {
- passed = false;
- message = "Project field contains invalid characters"
- return[passed, message]
- }
-
return [passed, message]
}
@@ -109,12 +103,6 @@ const steps = {
return[passed, message]
}
- if (!(purpose.match(/^[a-z0-9~@#$^*()_+=[\]{}|,.?': -]+$/i))) {
- passed = false;
- message = "Purpose field contains invalid characters"
- return[passed, message]
- }
-
return [passed, message]
}
@@ -129,7 +117,7 @@ const steps = {
}
onFocusInCIFile() {
- this.step = steps.CLOUD_INIT
+ workflow.goTo(steps.CLOUD_INIT)
const ci_textarea = document.getElementById('ci-textarea')
GUI.unhighlightError(ci_textarea)
}
@@ -148,7 +136,7 @@ const steps = {
}
onFocusInPurpose() {
- this.step = steps.BOOKING_DETAILS
+ workflow.goTo(steps.BOOKING_DETAILS)
const input = document.getElementById('input_purpose');
GUI.hideDetailsError()
GUI.unhighlightError(input)
@@ -168,14 +156,14 @@ const steps = {
}
onFocusInProject() {
- this.step = steps.BOOKING_DETAILS
+ workflow.goTo(steps.BOOKING_DETAILS)
const input = document.getElementById('input_project');
GUI.hideDetailsError()
GUI.unhighlightError(input)
}
onchangeDays() {
- this.step = steps.BOOKING_DETAILS
+ workflow.goTo(steps.BOOKING_DETAILS)
const counter = document.getElementById("booking_details_day_counter")
const input = document.getElementById('input_length')
workflow.bookingBlob.metadata.length = input.value
@@ -184,8 +172,7 @@ const steps = {
}
add_collaborator(username) {
- this.step = steps.ADD_COLLABS;
-
+ workflow.goTo(steps.ADD_COLLABS)
for (const c of this.bookingBlob.allowed_users) {
if (c == username) {
return;
@@ -198,8 +185,7 @@ const steps = {
remove_collaborator(username) {
// Removes collab from collaborators list and updates summary
- this.step = steps.ADD_COLLABS
-
+ this.goTo(steps.ADD_COLLABS)
const temp = [];
for (const c of this.bookingBlob.allowed_users) {
@@ -340,9 +326,9 @@ class GUI {
let disabled = !isAvailable ? 'disabled = "true"' : '';
const col = document.createElement('div');
- col.classList.add('col-3', 'my-1');
+ col.classList.add('col-12', 'col-md-6', 'col-xl-3', 'my-3', 'd-flex', 'flex-grow-1');
col.innerHTML= `
- <div class="card">
+ <div class="card flex-grow-1">
<div class="card-header">
<p class="h5 font-weight-bold mt-2">` + templateBlob.pod_name + `</p>
</div>
diff --git a/src/static/js/workflows/design-a-pod.js b/src/static/js/workflows/design-a-pod.js
index 2083f7d..efec093 100644
--- a/src/static/js/workflows/design-a-pod.js
+++ b/src/static/js/workflows/design-a-pod.js
@@ -68,7 +68,7 @@ class DesignWorkflow extends Workflow {
/** Takes an HTML element */
async onclickSelectLab(lab_card) {
- this.step = steps.SELECT_LAB;
+ this.goTo(steps.SELECT_LAB)
if (this.templateBlob.lab_name == null) { // Lab has not been selected yet
this.templateBlob.lab_name = lab_card.id;
@@ -106,7 +106,7 @@ class DesignWorkflow extends Workflow {
// Generate template cards
// Show modal
- this.step = steps.ADD_RESOURCES;
+ this.goTo(steps.ADD_RESOURCES)
if (this.templateBlob.lab_name == null) {
showError("Please select a lab before adding resources.", steps.SELECT_LAB);
@@ -269,7 +269,7 @@ class DesignWorkflow extends Workflow {
* @param {String} hostname
*/
onclickDeleteHost(hostname) {
- this.step = steps.ADD_RESOURCES;
+ this.goTo(steps.ADD_RESOURCES);
for (let existing_host of this.templateBlob.host_list) {
if (hostname == existing_host.hostname) {
this.removeHostFromTemplateBlob(existing_host);
@@ -294,8 +294,7 @@ class DesignWorkflow extends Workflow {
// Prerequisite step checks
// GUI stuff
- this.step = steps.ADD_NETWORKS;
-
+ this.goTo(steps.ADD_NETWORKS)
if (this.templateBlob.lab_name == null) {
showError("Please select a lab before adding networks.", steps.SELECT_LAB);
return;
@@ -311,7 +310,7 @@ class DesignWorkflow extends Workflow {
/** onclick handler for the adding_network_confirm button */
onclickConfirmNetwork() {
- this.step = steps.ADD_NETWORKS;
+ this.goTo(steps.ADD_NETWORKS)
// Add the network
// call the GUI to make the card (refresh the whole view to make it easier)
@@ -364,7 +363,7 @@ class DesignWorkflow extends Workflow {
* Takes a network name as a parameter.
*/
onclickDeleteNetwork(network_name) {
- this.step = steps.ADD_NETWORKS;
+ this.goTo(steps.ADD_NETWORKS)
for (let existing_network of this.templateBlob.networks) {
if (network_name == existing_network.name) {
@@ -402,7 +401,7 @@ class DesignWorkflow extends Workflow {
}
onclickConfigureConnection(hostname) {
- this.step = steps.CONFIGURE_CONNECTIONS;
+ this.goTo(steps.CONFIGURE_CONNECTIONS)
const host = this.templateBlob.findHost(hostname);
if (!host) {
@@ -450,7 +449,7 @@ class DesignWorkflow extends Workflow {
});
pod_name_input.addEventListener('focusin', (event)=> {
- this.step = steps.POD_DETAILS;
+ this.goTo(steps.POD_DETAILS)
GUI.unhighlightError(pod_name_input);
GUI.hidePodDetailsError();
});
@@ -460,7 +459,7 @@ class DesignWorkflow extends Workflow {
});
pod_desc_input.addEventListener('focusin', (event)=> {
- this.step = steps.POD_DETAILS;
+ this.goTo(steps.POD_DETAILS)
GUI.unhighlightError(pod_desc_input);
GUI.hidePodDetailsError();
});
@@ -515,9 +514,6 @@ class DesignWorkflow extends Workflow {
else if (input.length > maxCharCount) {
message = form_name + ' cannot exceed ' + maxCharCount + ' characters.';
result = false;
- } else if (!(input.match(/^[a-z0-9~@#$^*()_+=[\]{}|,.?': -!]+$/i))) {
- message = form_name + ' contains invalid characters.';
- result = false;
}
return [result, message]
@@ -557,7 +553,7 @@ class DesignWorkflow extends Workflow {
}
async onclickSubmitTemplate() {
- this.step = steps.POD_SUMMARY;
+ this.goTo(steps.POD_SUMMARY)
const simpleValidation = this.simpleStepValidation();
if (!simpleValidation[0]) {
showError(simpleValidation[1], simpleValidation[2])
@@ -632,8 +628,10 @@ class GUI {
*/
static refreshAddHostModal(template_list, flavor_map) {
document.getElementById('add_resource_modal_body').innerHTML = `
- <h2>Resource</h2>
- <div id="template-cards" class="row align-items-center justify-content-start">
+ <p>Select a resource, then configure the image, hostname and cloud-init (optional).</p>
+ <p>For multi-node resources, select a tab to modify each individual node.</p>
+ <h2>Resource<span class="text-danger">*</span></h2>
+ <div id="template-cards" class="row flex-grow-1">
</div>
<div id="template-config-section">
@@ -642,11 +640,11 @@ class GUI {
</ul>
<!-- tabs -->
<div id="resource_config_section" hidden="true">
- <h2>Image</h2>
+ <h2>Image<span class="text-danger">*</span></h2>
<div id="image-cards" class="row justify-content-start align-items-center">
</div>
<div class="form-group">
- <h2>Hostname</h2>
+ <h2>Hostname<span class="text-danger">*</span></h2>
<input type="text" class="form-control" id="hostname-input" placeholder="Enter Hostname">
<h2>Cloud Init</h2>
<div class="d-flex justify-content-center align-items-center">
@@ -695,11 +693,11 @@ class GUI {
let color = available_count > 0 ? 'text-success' : 'text-danger';
// let disabled = available_count == 0 ? 'disabled = "true"' : '';
const col = document.createElement('div');
- col.classList.add('col-12', 'col-md-6', 'col-xl-3', 'my-3');
+ col.classList.add('col-12', 'col-md-6', 'col-xl-3', 'my-3', 'd-flex', 'flex-grow-1');
col.innerHTML= `
- <div class="card" id="card-" ` + templateBlob.id + `>
+ <div class="card flex-grow-1" id="card-" ` + templateBlob.id + `>
<div class="card-header">
- <p class="h5 font-weight-bold mt-2">` + templateBlob.pod_name + `</p>
+ <p class="h5 font-weight-bold">` + templateBlob.pod_name + `</p>
</div>
<div class="card-body">
<p class="grid-item-description">` + templateBlob.pod_desc +`</p>
@@ -999,7 +997,7 @@ class GUI {
outer_block.appendChild(inner_block)
for (const c of bg.connections) {
const connection_li = document.createElement('li');
- connection_li.innerText = c.connects_to + `: ` + c.tagged;
+ connection_li.innerText = c.connects_to + `: ` + (c.tagged == true ? "tagged" : "untagged");
inner_block.appendChild(connection_li);
}
bondgroup_list.appendChild(outer_block)
diff --git a/src/static/js/workflows/workflow.js b/src/static/js/workflows/workflow.js
index 97bf8f4..97892a2 100644
--- a/src/static/js/workflows/workflow.js
+++ b/src/static/js/workflows/workflow.js
@@ -196,45 +196,28 @@ class Workflow {
* Enables the next button if the step is less than sections.length after executing
*/
goPrev() {
-
- if (workflow.step <= 0) {
- return;
- }
-
- this.step--;
-
- document.getElementById(this.sections[this.step]).scrollIntoView({behavior: 'smooth'});
-
- if (this.step == 0) {
- document.getElementById('prev').setAttribute('disabled', '');
- } else if (this.step == this.sections.length - 2) {
- document.getElementById('next').removeAttribute('disabled');
- }
+ this.goTo(this.step -1);
}
goNext() {
- if (this.step >= this.sections.length - 1 ) {
- return;
- }
-
- this.step++;
- document.getElementById(this.sections[this.step]).scrollIntoView({behavior: 'smooth'});
-
- if (this.step == this.sections.length - 1) {
- document.getElementById('next').setAttribute('disabled', '');
- } else if (this.step == 1) {
- document.getElementById('prev').removeAttribute('disabled');
- }
+ this.goTo(this.step + 1);
}
goTo(step_number) {
if (step_number < 0) return;
this.step = step_number
+ document.getElementById('workflow-next').removeAttribute('hidden');
+ document.getElementById('workflow-prev').removeAttribute('hidden');
+
document.getElementById(this.sections[this.step]).scrollIntoView({behavior: 'smooth'});
+
+ if (this.step == 0) {
+ document.getElementById('workflow-prev').setAttribute('hidden', '');
+
+ }
+
if (this.step == this.sections.length - 1) {
- document.getElementById('next').setAttribute('disabled', '');
- } else if (this.step == 1) {
- document.getElementById('prev').removeAttribute('disabled');
+ document.getElementById('workflow-next').setAttribute('hidden', '');
}
}
diff --git a/src/templates/base/booking/booking_detail.html b/src/templates/base/booking/booking_detail.html
index 33b0486..bcf554b 100644
--- a/src/templates/base/booking/booking_detail.html
+++ b/src/templates/base/booking/booking_detail.html
@@ -53,6 +53,14 @@ code {
<td>Lab Deployed At</td>
<td>{{ booking.lab }}</td>
</tr>
+ <tr>
+ <td>IPMI Username</td>
+ <td><code id="ipmi-username">{{ status.config.ipmi_username }}</code></td>
+ </tr>
+ <tr>
+ <td>IPMI Password</td>
+ <td><code id="ipmi-password">{{ status.config.ipmi_password }}</code></td>
+ </tr>
</table>
</div>
</div>
@@ -61,35 +69,80 @@ code {
<div class="card mb-3">
<div class="card-header d-flex">
<h4 class="d-inline">Deployment Progress</h4>
- <p class="mx-3">Your resources are being prepared. If this is taking a really long time, please contact us <a href="mailto:{{contact_email}}">here!</a></p>
- <!-- <button data-toggle="collapse" data-target="#panel_tasks" class="btn btn-outline-secondary ml-auto">Expand</button> -->
+ <p class="mx-3">Your resources are being prepared. If this is taking a really long time, please contact
+ us <a href="mailto:{{contact_email}}">here!</a></p>
</div>
<div class="collapse show" id="panel_tasks">
<table class="table m-0">
<tr>
<th></th>
- <th>Resource</th>
+ <th>Name</th>
+
+ <th>Assigned Host</th>
<th>Status</th>
</tr>
- {% for host in statuses %}
+ {% with status.instances as instances %}
+
+ {% for id, inst in instances.items %}
<tr>
<td>
- {% if 'Success' in host.status %}
- <div class="rounded-circle bg-success square-20"></div>
- {% elif 'Fail' in host.status %}
- <div class="rounded-circle bg-danger square-20"></div>
+ <!-- icon -->
+ {% if inst.logs|length > 0 %}
+ {% with inst.logs|last as lastelem %}
+ {% if 'Success' in lastelem.status %}
+ <div id="icon-{{id}}" class="rounded-circle bg-success square-20"></div>
+ {% elif 'Fail' in lastelem.status %}
+ <div id="icon-{{id}}" class="rounded-circle bg-danger square-20"></div>
{% else %}
- <div class="spinner-border text-primary square-20"></div>
+ <div id="icon-{{id}}" class="spinner-border text-primary square-20"></div>
+ {% endif %}
+ {% endwith %}
{% endif %}
</td>
- <td>
- {{ host.hostname }}
+ <td id="alias-{{id}}">
+ <!-- Hostname -->
+ {{inst.host_alias}}
+ </td>
+ <td id="host-{{id}}">
+ <!-- Actual Host -->
+ {{inst.assigned_host}}
</td>
- <td id="{{host.instance_id}}">
- {{ host.status }}
+ <td>
+ <!-- Logs -->
+ {% if inst.logs|length > 0 %}
+ {% with inst.logs|last as lastelem %}
+ <span id="status-{{id}}">
+ {{lastelem.status}}
+ </span>
+ <button class="btn-secondary btn float-right" data-target="#collapse-{{id}}"
+ data-toggle="collapse">Show Additional Logs</button>
+ {% endwith %}
+
+ {% endif %}
+ <p>
+ <div class="card collapse mt-3" id="collapse-{{id}}">
+ <div class="card-header">Additional Logs</div>
+ <div class="card-body">
+ <table id="logs-{{id}}">
+ {% for log in inst.logs %}
+ <tr>
+ <td>
+ {{log.status}}
+ </td>
+
+ <td>
+ {{log.time}}
+ </td>
+ </tr>
+ {% endfor %}
+ </table>
+ </div>
+ </div>
+ </p>
</td>
</tr>
{% endfor %}
+ {% endwith %}
</table>
</div>
</div>
@@ -116,30 +169,67 @@ code {
</div>
<script>
-setInterval(function(){
- fetchBookingStatus();
-}, 5000);
+ setInterval(function () {
+ fetchBookingStatus();
+ }, 5000);
-async function fetchBookingStatus() {
+ async function fetchBookingStatus() {
req = new XMLHttpRequest();
var url = "status";
req.open("GET", url, true);
- req.onerror = function() { alert("oops"); }
- req.onreadystatechange = function() {
- if(req.readyState === 4) {
- statuses = JSON.parse(req.responseText)
- updateStatuses(statuses)
+ 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();
-}
+ }
+
+ async function updateStatuses(status) {
+
+ const instances = status.instances;
+
+ Object.keys(instances).forEach((aggId) => {
+ const instance = instances[aggId]
+ const status = instance.logs.pop()
-async function updateStatuses(statuses) {
- for (const s of statuses) {
- document.getElementById(s.instance_id).innerText = s.status
+ let icon_class = "spinner-border text-primary square-20"
+ if (status.status.includes('Success')) {
+ icon_class = "rounded-circle bg-success square-20"
+ } else if (status.status.includes('Fail')) {
+ icon_class = "rounded-circle bg-danger square-20"
+ }
+
+ // icon
+ document.getElementById("icon-" + aggId).className = icon_class;
+ // host alias
+ document.getElementById("alias-" + aggId).innerText = instance.host_alias;
+ // assigned host
+ document.getElementById("host-" + aggId).innerText = instance.assigned_host;
+ // status
+ document.getElementById("status-" + aggId).innerText = status.status;
+ // logs
+ const log_table = document.getElementById("logs-" + aggId);
+ log_table.innerHTML = "";
+ for (const log of instance.logs) {
+ const tr = document.createElement('tr');
+ const td_status = document.createElement('td')
+ td_status.innerText = log.status;
+ const td_time = document.createElement('td')
+ td_time.innerText = log.time;
+ tr.appendChild(td_status)
+ tr.appendChild(td_time)
+ log_table.appendChild(tr)
+ }
+ })
+
+ // IPMI
+ document.getElementById("ipmi-username").innerText = status.config.ipmi_username;
+ document.getElementById("ipmi-password").innerText = status.config.ipmi_password;
}
-}
</script>
-{% endblock content %}
+{% endblock content %} \ No newline at end of file
diff --git a/src/templates/base/dashboard/lab_detail.html b/src/templates/base/dashboard/lab_detail.html
index cd096f6..1c15496 100644
--- a/src/templates/base/dashboard/lab_detail.html
+++ b/src/templates/base/dashboard/lab_detail.html
@@ -57,7 +57,9 @@
</div>
</div>
</div>
- <div class="card my-3">
+
+ <!-- Needs to stay commented until we can filter profiles by project. Currently this is showing Anuket and LFEdge profiles. -->
+ <!-- <div class="card my-3">
<div class="card-header d-flex">
<h4 class="d-inline-block">Host Profiles</h4>
</div>
@@ -74,7 +76,7 @@
</table>
</div>
</div>
- </div>
+ </div> -->
<div class="card my-3">
<div class="card-header d-flex">
diff --git a/src/templates/base/dashboard/landing.html b/src/templates/base/dashboard/landing.html
index 960ad39..7f97e4f 100644
--- a/src/templates/base/dashboard/landing.html
+++ b/src/templates/base/dashboard/landing.html
@@ -49,13 +49,15 @@
{% endif %}
{% else %}
{% block btnGrp %}
- <p>To get started, book a server below:</p>
- <a class="btn btn-primary btn-lg d-flex flex-column justify-content-center align-content-center border p-4 btnAnuket" href="{% url 'workflow:book_a_pod' %}" >
- Book a Pod
- </a>
+ <p>Select 'Design a Pod' to create a custom resource group.</p>
+ <p>Select 'Book a Pod' to reserve a custom pod or a single resource.</p>
<a class="btn btn-primary btn-lg d-flex flex-column justify-content-center align-content-center border p-4 btnAnuket" href="{% url 'workflow:design_a_pod' %}" >
Design a Pod
</a>
+ <a class="btn btn-primary btn-lg d-flex flex-column justify-content-center align-content-center border p-4 btnAnuket" href="{% url 'workflow:book_a_pod' %}" >
+ Book a Pod
+ </a>
+
{% endblock btnGrp %}
{% endif %}
</div>
diff --git a/src/templates/base/workflow/book_a_pod.html b/src/templates/base/workflow/book_a_pod.html
index 8a0fb47..5c1a253 100644
--- a/src/templates/base/workflow/book_a_pod.html
+++ b/src/templates/base/workflow/book_a_pod.html
@@ -10,7 +10,7 @@
<body>
<div class="workflow-container">
<div id="prev" class="row w-100 m-0">
- <button class="btn btn-workflow-nav stretched-link m-0 p-0 mt-3" onclick="workflow.goPrev()" id="workflow-prev">
+ <button class="btn btn-workflow-nav stretched-link m-0 p-0 mt-3" hidden="true" onclick="workflow.goPrev()" id="workflow-prev">
<div class="arrow arrow-up"></div>
</button>
</div>
@@ -25,21 +25,14 @@
<div class="scroll-area pt-5 mx-5" id="select_template">
<h1 class="mt-4"><u>Book a Pod</u></h1>
<h2 class="mt-4 mb-3">Select Host Or Template<span class="text-danger">*</span></h2>
- <div class="card-deck align-items-center">
-
- <div class="col-12" id="template_list">
-
- <div class="my-5" id="select_template_tab_content">
- <ul id="default_templates_list" class="p-0 m-0 row">
- </ul>
- </div>
-
+ <p>Select the resource bundle that you would like to reserve. Then use the navigation arrows or scroll to advance the workflow. Configure your own resource <a href="{% url 'workflow:design_a_pod' %}">here</a>.</p>
+ <div id="default_templates_list" class="row flex-grow-1">
</div>
- </div>
</div>
<div class="scroll-area pt-5 mx-5" id="cloud_init">
<h2 class="mt-4 mb-3">Global Cloud Init Override</h2>
+ <p>Add a custom cloud init configuration to apply to all hosts in your booking (optional).</p>
<div class="d-flex align-items-center">
<textarea name="ci-textarea" id="ci-textarea" rows="15" class="w-50"></textarea>
</div>
@@ -47,6 +40,7 @@
<div class="scroll-area pt-5 mx-5" id="booking_details">
<h2 class="mt-4 mb-3">Booking Details<span class="text-danger">*</span></h2>
+ <p>Enter the project and purpose for your booking, as well as the duration (up to 21 days).</p>
<div class="form-group mb-0">
<div class="row align-items-center my-4">
<div class="col-xl-6 col-md-8 col-11">
@@ -67,6 +61,7 @@
</div>
</div>
<h2 class="mt-4 mb-3">Add Collaborators:</h2>
+ <p>Give SSH and booking overview access to other users. Collaborators must mark their profiles as public and have a linked IPA account.</p>
<div class="row">
<div class="col-xl-6 col-md-8 col-11 p-0 border border-dark">
{% bootstrap_field form.users label="Collaborators" %}
@@ -76,6 +71,7 @@
<div class="scroll-area pt-5 mx-5" id="booking_summary">
<h2 class="mt-4 mb-3">Booking Summary</h2>
+ <p>Review your booking information and click 'Book' to reserve your resources.</p>
<div class="row align-items-center">
<div class="card col-xl-6 col-md-8 col-11 border-0">
<ul class="list-group">
diff --git a/src/templates/base/workflow/design_a_pod.html b/src/templates/base/workflow/design_a_pod.html
index 32bd332..c23e5a8 100644
--- a/src/templates/base/workflow/design_a_pod.html
+++ b/src/templates/base/workflow/design_a_pod.html
@@ -14,7 +14,7 @@
<div class="workflow-container">
<div id="prev" class="row w-100 m-0">
- <button class="btn btn-workflow-nav stretched-link m-0 p-0 mt-3" onclick="workflow.goPrev()" id="workflow-prev">
+ <button class="btn btn-workflow-nav stretched-link m-0 p-0 mt-3" hidden="true" onclick="workflow.goPrev()" id="workflow-prev">
<div class="arrow arrow-up"></div>
</button>
</div>
@@ -30,7 +30,8 @@
<!-- Select Lab -->
<div class="scroll-area pt-5 mx-5" id="select_lab">
<!-- Ideally the 'Design a Pod' header would be anchored to the top of the page below the arrow -->
- <h1 class="mt-4"><u>Design a Pod</u></h1>
+ <h1 class="mt-4"><u>Design a Pod</u></h1>
+ <p>To get started, select a lab. Then use the navigation arrows or scroll to advance through the workflow.</p>
<h2 class="mt-4 mb-3">Select a Lab<span class="text-danger">*</span></h2>
<div class="row card-deck" id="lab_cards">
</div>
@@ -39,6 +40,7 @@
<!-- 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>
<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">
@@ -52,6 +54,7 @@
<!-- 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>
<div class="row card-deck align-items-center" id="network_card_deck">
<div class="col-xl-3 col-md-6 col-12" id="add_network_plus_card">
<div class="card align-items-center border-0">
@@ -65,6 +68,7 @@
<!-- Configure Connections-->
<div class="scroll-area pt-5 mx-5" id="configure_connections">
<h2 class="mt-4 mb-3">Configure Connections<span class="text-danger">*</span></h2>
+ <p>Configure the connections for each host in your pod to your defined networks.</p>
<div class="row card-deck align-items-center" id="connection_cards">
</div>
</div>
@@ -72,6 +76,7 @@
<!-- Pod Details-->
<div class="scroll-area pt-5 mx-5" id="pod_details">
<h2 class="mt-4 mb-3">Pod Details<span class="text-danger">*</span></h2>
+ <p>Add a pod name and description to refer to later.</p>
<div class="form-group">
<div class="row align-items-center my-4">
<div class="col-xl-6 col-md-8 col-11">
@@ -102,7 +107,8 @@
<!-- Pod Summary-->
<div class="scroll-area pt-5 mx-5" id="pod_summary">
- <h2 class="mt-4 mb-3">Pod Summary:</h2>
+ <h2 class="mt-4 mb-3">Pod Summary</h2>
+ <p>Confirm the specifications below, and select 'create' to save this pod. Otherwise, navigate upwards and modify it as needed.</p>
<div class="row align-items-center">
<div class="col-xl-6 col-md-8 col-11">
<div class="card border-0">
@@ -143,8 +149,10 @@
<button class="close" data-dismiss="modal"><span aria-hidden="true">&times;</span></button>
</div>
<div class="modal-body" id="add_resource_modal_body">
- <h2>Resource</h2>
- <div id="template-cards" class="row align-items-center justify-content-start">
+ <p>Select a resource, then configure the image, hostname and cloud-init (optional).</p>
+ <p>For multi-node resources, select a tab to modify each individual node.</p>
+ <h2>Resource<span class="text-danger">*</span></h2>
+ <div id="template-cards" class="row flex-grow-1">
</div>
<div id="template-config-section">
@@ -153,11 +161,11 @@
</ul>
<!-- tabs -->
<div id="resource_config_section">
- <h2>Image</h2>
+ <h2>Image<span class="text-danger">*</span></h2>
<div id="image-cards" class="row justify-content-start align-items-center">
</div>
<div class="form-group">
- <h2>Hostname</h2>
+ <h2>Hostname<span class="text-danger">*</span></h2>
<input type="text" class="form-control" id="hostname-input" placeholder="Enter Hostname">
<h2>Cloud Init</h2>
<div class="d-flex justify-content-center align-items-center">
@@ -184,6 +192,12 @@
<h5 class="modal-title">Configure Connections</h5>
<button class="close" data-dismiss="modal"><span aria-hidden="true">&times;</span></button>
</div>
+ <ul>
+ <li>Select an interface and a frame type to add a connection to a network.</li>
+ <li>An interface may send tagged or untagged frames on a single network, but not both.</li>
+ <li>Each interface may only be untagged on one network.</li>
+ <li>Reselect a connection to remove it.</li>
+ </ul>
<div class="modal-body text-center">
<ul class="nav nav-tabs" role="tablist" id="configure-connections-tablist">
</ul>