diff options
author | Parker Berberian <pberberian@iol.unh.edu> | 2018-10-10 16:06:47 -0400 |
---|---|---|
committer | Parker Berberian <pberberian@iol.unh.edu> | 2018-10-15 13:16:11 -0400 |
commit | 1f3a770d2547848590f39e9d9b9bdffeb94eec14 (patch) | |
tree | 97222e5facd1a242d951c38482315057b5790d51 /src/templates/booking | |
parent | 6d4019e59eda897384e9c00d1daf8b2ce87d128f (diff) |
Lab as a Service 2.0
See changes here:
https://wiki.opnfv.org/display/INF/Pharos+Laas
Change-Id: I59ada5f98e70a28d7f8c14eab3239597e236ca26
Signed-off-by: Sawyer Bergeron <sbergeron@iol.unh.edu>
Signed-off-by: Parker Berberian <pberberian@iol.unh.edu>
Diffstat (limited to 'src/templates/booking')
-rw-r--r-- | src/templates/booking/booking_calendar.html | 65 | ||||
-rw-r--r-- | src/templates/booking/booking_detail.html | 361 | ||||
-rw-r--r-- | src/templates/booking/booking_grid_item.html | 11 | ||||
-rw-r--r-- | src/templates/booking/booking_list.html | 6 | ||||
-rw-r--r-- | src/templates/booking/stats.html | 60 | ||||
-rw-r--r-- | src/templates/booking/steps/booking_confirm.html | 25 | ||||
-rw-r--r-- | src/templates/booking/steps/booking_meta.html | 66 | ||||
-rw-r--r-- | src/templates/booking/steps/resource_select.html | 73 | ||||
-rw-r--r-- | src/templates/booking/steps/swconfig_select.html | 73 |
9 files changed, 667 insertions, 73 deletions
diff --git a/src/templates/booking/booking_calendar.html b/src/templates/booking/booking_calendar.html index b60db3c..349cb0a 100644 --- a/src/templates/booking/booking_calendar.html +++ b/src/templates/booking/booking_calendar.html @@ -4,6 +4,8 @@ {% load bootstrap3 %} {% block extrahead %} + {{ block.super }} + <link href="{% static "bower_components/fullcalendar/dist/fullcalendar.css" %}" rel='stylesheet'/> <link href="{% static "bower_components/eonasdan-bootstrap-datetimepicker/build/css/bootstrap-datetimepicker.min.css" %}" @@ -84,6 +86,7 @@ hidediv.parentNode.insertBefore(par,hidediv.nextSibling); } </script> + {% bootstrap_field form.opsys %} {% bootstrap_field form.purpose %} {% bootstrap_field form.installer %} {% bootstrap_field form.scenario %} @@ -95,6 +98,15 @@ {% endbuttons %} </form> </div> + <script type="text/javascript"> + //Check if current view is an edit or a standard booking view + if(window.location.href.includes('edit')) { + var element = document.getElementById("bookingform"); + element.parentNode.removeChild(element); } + else { + var element = document.getElementById("bookingeditform"); + element.parentNode.removeChild(element); } + </script> {% else %} <p>Please <a href="{% url 'account:login' %}"> @@ -132,8 +144,61 @@ var booking_detail_prefix = "{% url 'booking:detail_prefix' %}"; var booking_delete_prefix = "{% url 'booking:delete_prefix' %}"; var user_timezone = "{{ request.user.userprofile.timezone }}" + {% autoescape off %} + var sup_installer_dict = {{ installer_filter }} + var sup_scenario_dict = {{ scenario_filter }} + {% endautoescape %} + </script> + <script type="text/javascript"> + $(document).ready(function () { + $("#id_installer").children().hide(); + $("#id_scenario").children().hide(); + }); + + function installerHider() { + dropFilter("id_installer", sup_installer_dict, "id_opsys"); + scenarioHider(); + } + document.getElementById('id_opsys').addEventListener('change', installerHider); + + function scenarioHider() { + dropFilter("id_scenario", sup_scenario_dict, "id_installer"); + } + document.getElementById('id_installer').addEventListener('change', scenarioHider); + function dropFilter(target, target_filter, master) { + ob = document.getElementById(target); + + for(var i=0; i<ob.options.length; i++) { + if ( ob.options[i].text == '---------' ) { + ob.selectedIndex = i; + } + } + + targ_id = "#" + target; + $(targ_id).children().hide(); + var drop = document.getElementById(master); + var opts = target_filter[drop.options[drop.selectedIndex].value]; + if (!opts) { + opts = {}; + } + var emptyMap = {} + + var map = Object.create(null); + for (var i = 0; i < opts.length; i++) { + var j = opts[i]; + map[j] = true; + } + + for (var i = 0; i < document.getElementById(target).childNodes.length; i++) { + if (document.getElementById(target).childNodes[i].value in opts && !(document.getElementById(target).childNodes[i].value in emptyMap) ) { + document.getElementById(target).childNodes[i].style.display = "inherit"; + } + } + } + + </script> <script src={% static "bower_components/moment/moment.js" %}></script> <script src={% static "bower_components/fullcalendar/dist/fullcalendar.js" %}></script> <script type="text/javascript" diff --git a/src/templates/booking/booking_detail.html b/src/templates/booking/booking_detail.html index 4628a62..126aaab 100644 --- a/src/templates/booking/booking_detail.html +++ b/src/templates/booking/booking_detail.html @@ -1,74 +1,293 @@ -{% load jira_filters %} - -<script type="text/javascript"> - function deleteClick(id) { - var booking_delete_url = booking_delete_prefix + id - $.ajax({ - url: booking_delete_url, - type: 'get', - success: function (data) { - $('#booking_delete_content').html(data); - }, - failure: function (data) { - alert('Error finding that booking'); - } - }); - $('#booking_delete_modal').modal('show'); - } -</script> - -<p> - <b>Resource: </b> - <a href="{{ booking.resource.url }}"> - {{ booking.resource.name }} - </a> -</p> -<p> - <b>User: </b> {{ booking.user.username }} -</p> -<p> - <b>Start: </b> {{ booking.start }} -</p> -<p> - <b>End: </b> {{ booking.end }} -</p> -<p> - <b>Purpose: </b> {{ booking.purpose }} -</p> -<p> - <b>Operating System: </b> {{ booking.opsys }} -</p> -<p> - <b>Installer: </b> {{ booking.installer }} -</p> -<p> - <b>Scenario: </b> {{ booking.scenario }} -</p> -<p> - <b>Extensions Remaining: </b> {{ booking.ext_count }} -</p> - -{% if user.is_authenticated %} -{% if user.get_username == booking.user.username %} -<p> - <a href="{% url 'booking:edit' booking_id=booking.id resource_id=booking.resource.id %}" class="btn btn btn-success"> - Edit Booking - </a> - <a href="javascript:deleteClick({{ booking.id }})" class="btn btn btn-danger"> - Delete Booking - </a> - <div id="booking_delete_modal" class="modal fade" role="dialog"> - <div class="modal-dialog"> - <div class="modal-content"> - <div class="modal-header"> - <button type="button" class="close" data-dismiss="modal">×</button> - <h4 class="modal-title">Delete Booking</h4> - </div> - <div class="modal-body" id="booking_delete_content"> - </div> +{% extends "base.html" %} +{% load staticfiles %} + +{% block extrahead %} + {{block.super}} + <script src="https://cdn.rawgit.com/google/code-prettify/master/loader/run_prettify.js?lang=yaml"></script> +<script src="https://unpkg.com/masonry-layout@4/dist/masonry.pkgd.min.js"></script> +{% endblock %} + +{% block content %} +<div class="container-fluid"> + <div class="row"> + <div class="col-lg-6"> + <div class="panel panel-default"> + <div class="panel-heading clearfix"> + <h4 style="display: inline;">Overview</h4> + <a data-toggle="collapse" data-target="#panel_overview" class="btn pull-right" style="line-height: 1;" >Expand</a> + </div> + <div class="panel-body" id="panel_overview"> + <table class="table"> + <tr> + <td>Purpose</td> + <td>{{ booking.purpose }}</td> + </tr> + <tr> + <td>Project</td> + <td>{{ booking.project }}</td> + </tr> + <tr> + <td>Start Time</td> + <td>{{ booking.start }}</td> + </tr> + <tr> + <td>End Time</td> + <td>{{ booking.end }}</td> + </tr> + <tr> + <td>Pod Definition</td> + <td>{{ booking.resource.template }}</td> + </tr> + <tr> + <td>Pod Configuration</td> + <td>{{ booking.config_bundle }}</td> + </tr> + <tr> + <td>Lab Deployed At</td> + <td>{{ booking.lab }}</td> + </tr> + </table> + </div> + </div> + <div class="row"> + + <div class="col-lg-6"> + + <div class="panel panel-default"> + <div class="panel-heading clearfix"> + <h4 style="display: inline;">Pod</h4> + + <a data-toggle="collapse" data-target="#pod_panel" class="btn pull-right" style="line-height: 1;" >Expand</a> + </div> + <div class="panel-body pod_panel" id="pod_panel"> + <table class="table"> + {% for host in booking.resource.hosts.all %} + <tr> + <td><h4>{{host.template.resource.name}}</h4></td> + <td> + <table class="table"> + <tr> + <td>Hostname:</td> + <td>{{host.template.resource.name}}</td> + </tr> + <tr> + <td>Profile:</td> + <td>{{host.name}}</td> + </tr> + <tr> + <td>Role:</td> + <td>{{host.config.opnfvRole}}</td> + </tr> + <tr> + <td>Image:</td> + <td>{{host.config.image}}</td> + </tr> + <tr> + <td>RAM:</td> + <td>{{host.profile.ramprofile.first.amount}}G, + {{host.profile.ramprofile.first.channels}} channels</td> + </tr> + <tr> + <td>CPU:</td> + <td> + <table class="table"> + <tr> + <td>Arch:</td> + <td>{{host.profile.cpuprofile.first.architecture}}</td> + </tr> + <tr> + <td>Cores:</td> + <td>{{host.profile.cpuprofile.first.cores}}</td> + </tr> + <tr> + <td>Sockets:</td> + <td>{{host.profile.cpuprofile.first.cpus}}</td> + </tr> + </table> + </td> + </tr> + <tr> + <td>DISK:</td> + <td> + <table class="table"> + <tr> + <td>Size:</td> + <td>{{host.profile.diskprofile.first.size}}GiB</td> + </tr> + <tr> + <td>Type:</td> + <td>{{host.profile.diskprofile.first.media_type}}</td> + </tr> + <tr> + <td>Mount Point:</td> + <td>{{host.profile.diskprofile.first.name}}</td> + </tr> + </table> + </td> + </tr> + <tr> + <td>Interfaces:</td> + <td> + <style> + .borderless td { + border: none !important; + } + </style> + <table class="table"> + {% for intprof in host.profile.interfaceprofile.all %} + <tr> + <td> + <table class="table borderless"> + <tr> + <td>Name:</td> + <td>{{intprof.name}}</td> + </tr> + <tr> + <td>Speed:</td> + <td>{{intprof.speed}}</td> + </tr> + </table> + </td> + </tr> + {% endfor %} + </table> + </td> + </tr> + + + </table> + + </td> + {% endfor %} + </tr> + </table> + </div> + </div> + </div> + <div class="col-lg-6"> + + <div class="panel panel-default"> + <div class="panel-heading clearfix"> + <h4 style="display: inline;">PDF</h4> + <a data-toggle="collapse" data-target="#pdf_panel" class="btn pull-right" style="line-height: 1;" >Expand</a> + </div> + + <div class="panel-body" id="pdf_panel" style="padding: 0px;"> + <pre class="prettyprint lang-yaml" style="margin: 0px; padding: 0px; border: none;"> + + {{pdf}} + + + </pre> + </div> + </div> + </div> + </div> + </div> + + + + <div class="col-lg-6"> + <div class="panel panel-default"> + <div class="panel-heading clearfix"> + <h4 style="display: inline;">Deployment Progress</h4> + <p style="display: inline; margin-left: 10px;"> These are the different tasks that have to be completed before your deployment is ready</p> + <a data-toggle="collapse" data-target="#panel_tasks" class="btn pull-right" style="line-height: 1;" >Expand</a> + </div> + + <div class="panel-body" id="panel_tasks"> + <table class="table"> + <style> + .progress { + display: inline-block; + border: 3px solid #f3f3f3; + border-radius: 50%; + border-top: 3px solid #12aebb; + width: 20px; + height: 20px; + -webkit-animation: spin 2s linear infinite; /* Safari */ + animation: spin 2s linear infinite; + } + + @keyframes spin { + 0% {transform: rotate(0deg);} + 100% {transform: rotate(360deg);} + } + + .new { + display: inline-block; + width: 20px; + height: 20px; + background: #f3f3f3; + border-radius: 50%; + animation: fadeInOut 1s infinite alternate; + + + } + @keyframes fadeInOut { + from { opacity: 0;} + } + .done { + display: inline-block; + width: 20px; + height: 20px; + background: #40B976; + border-radius: 50%; + } + </style> + <tr> + <th></th> + <th>Status</th> + <th>Lab Response</th> + <th>Type</th> + </tr> + {% for task in booking.job.get_tasklist %} + <tr> + <td> + {% if task.status < 100 %} + <div class="new"></div> + {% elif task.status < 200 %} + <div class="progress"></div> + {% else %} + <div class="done"></div> + {% endif %} + </td> + + + <td> + {% if task.status < 100 %} + PENDING + {% elif task.status < 200 %} + IN PROGRESS + {% else %} + DONE + {% endif %} + </td> + <td> + + {% if task.message %} + {% if task.type_str == "Access Task" and user_id != task.config.user.id %} + Message from Lab: <pre>--secret--</pre> + {% else %} + Message from Lab: <pre>{{ task.message }}</pre> + {% endif %} + {% else %} + No response provided (yet) + {% endif %} + </td> + <td> + {{ task.type_str }} + + </td> + </tr> + {% endfor %} + </table> </div> </div> + + </div> </div> -</p> -{% endif %} -{% endif %} +</div> + + +{% endblock content %} diff --git a/src/templates/booking/booking_grid_item.html b/src/templates/booking/booking_grid_item.html new file mode 100644 index 0000000..3c72fd2 --- /dev/null +++ b/src/templates/booking/booking_grid_item.html @@ -0,0 +1,11 @@ +<p>{{ id|default:"id" }}</p> +<p>{{ lab|default:"lab" }}</p> +<p>{{ resource_name|default:"resource name" }}</p> +<p>{{ start|default:"start" }}</p> +<p>{{ end|default:"end" }}<p> +<p>Collaborators:</p> +<ul> + {% for collaborator in collaborators %} + <li>{{ collaborator }}</li> + {% endfor %} +</ul> diff --git a/src/templates/booking/booking_list.html b/src/templates/booking/booking_list.html index ccdc46d..a245450 100644 --- a/src/templates/booking/booking_list.html +++ b/src/templates/booking/booking_list.html @@ -1,7 +1,9 @@ {% extends "base.html" %} {% load staticfiles %} +{% load bootstrap3 %} {% block extrahead %} + {{ block.super }} <!-- DataTables CSS --> <link href="{% static "bower_components/datatables-plugins/integration/bootstrap/3/dataTables.bootstrap.css" %}" rel="stylesheet"> @@ -41,8 +43,8 @@ <script type="text/javascript"> $(document).ready(function () { $('#table').DataTable({ - scrollX: true, - }); + scrollX: true, + }); }); </script> {% endblock extrajs %} diff --git a/src/templates/booking/stats.html b/src/templates/booking/stats.html new file mode 100644 index 0000000..abb153b --- /dev/null +++ b/src/templates/booking/stats.html @@ -0,0 +1,60 @@ +{% extends "base.html" %} +{% load staticfiles %} + +{% block extrahead %} +{{ block.super }} +<script src="https://cdn.plot.ly/plotly-latest.min.js"></script> +<script> +function drawGraph(data, graph_id, graph_title){ + var container = document.getElementById(graph_id); + var plot_data = { x: data[0], + y: data[1], + fill: "tonexty", + type: "scatter", + mode: "none", + }; + var layout = { + title: graph_title + }; + Plotly.newPlot(container, [plot_data], layout); +} +function getData(){ + var req = new XMLHttpRequest(); + var url = "/booking/stats/json"; + var day_input = document.getElementById("number_days"); + var days = day_input.value; + //var days = document.getElementById("number_days").value; + if(days){ + url = url + "?days=" + days; + } + req.onreadystatechange = function(){ + if( req.readyState == XMLHttpRequest.DONE) { + var data = JSON.parse(req.responseText); + drawGraph(data["booking"], "booking_graph_container", "Active Bookings"); + drawGraph(data["user"], "user_graph_container", "Active Users"); + } + } + req.open("GET", url, true); + req.send(); +} +</script> +{% endblock %} + +{% block content %} + <p>Number of days to plot: </p> + <input id="number_days" type="number" min="1" step="1"/> + <button onclick="getData();">Submit</button> + <div id="all_graph_container"> + <div id="booking_graph_wrapper"> + <div id="booking_graph_container"/> + </div> + <div id="user_graph_wrapper" > + <div id="user_graph_container"/> + </div> + </div> + <script> +var data = {{data|safe}}; +drawGraph(data["booking"], "booking_graph_container", "Active Bookings"); +drawGraph(data["user"], "user_graph_container", "Active Users"); + </script> +{% endblock content %} diff --git a/src/templates/booking/steps/booking_confirm.html b/src/templates/booking/steps/booking_confirm.html new file mode 100644 index 0000000..9c7e951 --- /dev/null +++ b/src/templates/booking/steps/booking_confirm.html @@ -0,0 +1,25 @@ +{% extends "workflow/viewport-element.html" %} +{% load staticfiles %} + +{% load bootstrap3 %} + +{% block content %} + +<style> + #form_div { + width: 100%; + padding: 5%; + text-align: center; + } +</style> + +<div id="form_div"> +<p>confirm booking</p> +<p>{{info|default:"booking info"}}</p> +<form action="/wf/workflow/" method="post"> + {% csrf_token %} + {{form|default:"<p> No Form Loaded</p>"}} + <input type="submit" value="Submit" style="display: none;"/> +</form> +</div> +{% endblock content %}
\ No newline at end of file diff --git a/src/templates/booking/steps/booking_meta.html b/src/templates/booking/steps/booking_meta.html new file mode 100644 index 0000000..fa8f7a9 --- /dev/null +++ b/src/templates/booking/steps/booking_meta.html @@ -0,0 +1,66 @@ +{% extends "workflow/viewport-element.html" %} +{% load staticfiles %} + +{% load bootstrap3 %} + +{% block content %} + +<style> + .bkmeta_panel { + padding: 5%; + } + + .panel{ + padding: 5%; + /*border: solid 1px black;*/ + } + + .panel_wrap { + width: 100%; + display: grid; + grid-template-columns: 45% 10% 45%; + border: none; + } +</style> + +{% bootstrap_form_errors form type='non_fields' %} +<form id="booking_meta_form" action="/wf/workflow/" method="POST" class="form"> +{% csrf_token %} +<div id="form_div"> + <div class="panel_wrap"> + <div class="panel bkmeta_panel"> + {% bootstrap_field form.purpose %} + {% bootstrap_field form.project %} + {% bootstrap_field form.length %} + <p style="display:inline;">Days: </p><output id="daysout" style="display:inline;">0</output> + <script> + document.getElementById("id_length").setAttribute("oninput", "daysout.value=this.value"); + document.getElementById("daysout").value = document.getElementById("id_length").value; + </script> + {% bootstrap_field form.info_file %} + <p>You must provide a url to your project's INFO.yaml file if you are a PTL and you are trying to book a POD with multiple servers in it.</p> + </div> + <div class="panel panel_center"> + </div> + <div class="panel"> + {% bootstrap_field form.users %} + </div> + + {% buttons %} + <button type="submit" style="display: none;" class="btn btn-success">Confirm</button> + {% endbuttons %} + </div> +</div> +</form> +{% endblock content %} + +{% block onleave %} +var ajaxForm = $("#booking_meta_form"); +var formData = ajaxForm.serialize(); +console.log(formData); +req = new XMLHttpRequest(); +req.open("POST", "/wf/workflow/", false); +req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); +req.onerror = function() { alert("problem submitting form"); } +req.send(formData); +{% endblock %} diff --git a/src/templates/booking/steps/resource_select.html b/src/templates/booking/steps/resource_select.html new file mode 100644 index 0000000..7ccceb3 --- /dev/null +++ b/src/templates/booking/steps/resource_select.html @@ -0,0 +1,73 @@ +{% extends "workflow/viewport-element.html" %} +{% load staticfiles %} + +{% load bootstrap3 %} + +{% block content %} + +<style> + #resource_form_div { + width: 100%; + padding: 5%; + } + + .panel { + /*border: solid 1px black;*/ + border: none; + } + .select_panels { + width: 100%; + display: grid; + grid-template-columns: 45% 10% 45%; + + } + + .panel_center { + text-align: center; + border: none; + + + } + .panel_center p{ + font-size: 20pt; + } +</style> + +<div id="resource_form_div"> + <div class="select_panels"> + <div class="panel_chooser panel"> + <form id="resource_select_form" method="post" action="" class="form" id="resourceselectorform"> + {% csrf_token %} + {{ form|default:"<p>no form loaded</p>" }} + {% buttons %} + + {% endbuttons %} + </form> + </div> + <div class="panel_center panel"><p>OR</p></div> + <div class="panel_add panel"> + <button class="btn {% if disabled %} disabled {% endif %}" + style="width: 100%; height: 100%;" + {% if not disabled %}onclick="parent.add_wf(1)" + {% endif %}>Create Resource</button> + </div> + +</div> +</div> +<script> + {% if disabled %} + disable(); + {% endif %} +</script> + +{% endblock content %} +{% block onleave %} +var form = $("#resource_select_form"); +var formData = form.serialize(); +var req = new XMLHttpRequest(); +req.open("POST", "/wf/workflow/", false); +req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); +req.onerror = function() { alert("problem with form submission"); } +req.send(formData); +{% endblock %} + diff --git a/src/templates/booking/steps/swconfig_select.html b/src/templates/booking/steps/swconfig_select.html new file mode 100644 index 0000000..15c79d8 --- /dev/null +++ b/src/templates/booking/steps/swconfig_select.html @@ -0,0 +1,73 @@ +{% extends "workflow/viewport-element.html" %} +{% load staticfiles %} + +{% load bootstrap3 %} + +{% block content %} + +<style> + #sw_form_div { + width: 100%; + padding: 5%; + } + + .panel { + /*border: solid 1px black;*/ + border: none; + } + .select_panels { + width: 100%; + display: grid; + grid-template-columns: 45% 10% 45%; + + } + + .panel_center { + text-align: center; + border: none; + + + } + .panel_center p{ + font-size: 20pt; + } +</style> + +<div id="sw_form_div"> + <div class="select_panels"> + <div class="panel_chooser panel"> + <form id="software_select_form" method="post" action="" class="form" id="swselectorform"> + {% csrf_token %} + {{ form|default:"<p>no form loaded</p>" }} + {% buttons %} + + {% endbuttons %} + </form> + </div> + <div class="panel_center panel"><p>OR</p></div> + <div class="panel_add panel"> + <button class="btn {% if disabled %} disabled {% endif %}" + style="width: 100%; height: 100%;" + {% if not disabled %}onclick="parent.add_wf(2)" + {% endif %}>Create Config</button> + </div> + <script> + {% if disabled %} + disable(); + {% endif %} + </script> + +</div> +</div> + +{% endblock content %} + +{% block onleave %} +var form = $("#software_select_form"); +var formData = form.serialize(); +var req = new XMLHttpRequest(); +req.open("POST", "/wf/workflow/", false); +req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); +req.onerror = function() { alert("problem with form submission"); } +req.send(formData); +{% endblock %} |