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/dashboard | |
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/dashboard')
-rw-r--r-- | src/templates/dashboard/ci_pods.html | 61 | ||||
-rw-r--r-- | src/templates/dashboard/dev_pods.html | 70 | ||||
-rw-r--r-- | src/templates/dashboard/grid.html | 10 | ||||
-rw-r--r-- | src/templates/dashboard/host_profile_detail.html | 71 | ||||
-rw-r--r-- | src/templates/dashboard/jenkins_slaves.html | 46 | ||||
-rw-r--r-- | src/templates/dashboard/lab_detail.html | 157 | ||||
-rw-r--r-- | src/templates/dashboard/lab_list.html | 87 | ||||
-rw-r--r-- | src/templates/dashboard/landing.html | 88 | ||||
-rw-r--r-- | src/templates/dashboard/login.html | 8 | ||||
-rw-r--r-- | src/templates/dashboard/multiple_select_filter_widget.html | 402 | ||||
-rw-r--r-- | src/templates/dashboard/pdf.yaml | 95 | ||||
-rw-r--r-- | src/templates/dashboard/resource.html | 3 | ||||
-rw-r--r-- | src/templates/dashboard/resource_all.html | 5 | ||||
-rw-r--r-- | src/templates/dashboard/resource_detail.html | 52 | ||||
-rw-r--r-- | src/templates/dashboard/searchable_select_multiple.html | 408 | ||||
-rw-r--r-- | src/templates/dashboard/table.html | 1 |
16 files changed, 1329 insertions, 235 deletions
diff --git a/src/templates/dashboard/ci_pods.html b/src/templates/dashboard/ci_pods.html deleted file mode 100644 index a20be95..0000000 --- a/src/templates/dashboard/ci_pods.html +++ /dev/null @@ -1,61 +0,0 @@ -{% extends "dashboard/table.html" %} -{% load staticfiles %} -{% load jenkins_filters %} - -{% block table %} - <thead> - <tr> - <th>Name</th> - <th>Slave Name</th> - <th>Status</th> - <th>Installer</th> - <th>Scenario</th> - <th>Branch</th> - <th>Job</th> - </tr> - </thead> - <tbody> - {% for pod in ci_pods %} - <tr> - <td> - <a target='_blank' href={{ pod.url }}>{{ pod.name }}</a> - </td> - <td> - <a target='_blank' href={{ pod.slave.url }}>{{ pod.slave.name }}</a> - </td> - <td style="background-color:{{ pod.slave.status | jenkins_status_color }}"> - {{ pod.slave.status }} - </td> - <td {{ pod.slave.last_job_result | jenkins_job_blink }}> - {{ pod.slave.last_job_installer }} - </td> - <td {{ pod.slave.last_job_result | jenkins_job_blink }}> - {{ pod.slave.last_job_scenario }} - </td> - <td {{ pod.slave.last_job_result | jenkins_job_blink }}> - {{ pod.slave.last_job_branch }} - </td> - <td><a {{ pod.slave.last_job_result | jenkins_job_blink }} - style="color:{{ pod.slave.last_job_result | jenkins_job_color }}" - target='_blank' - href={{ pod.slave.last_job_url }}>{{ pod.slave.last_job_name }}</a> - </td> - </tr> - {% endfor %} - </tbody> -{% endblock table %} - - -{% block tablejs %} - <script type="text/javascript"> - $(document).ready(function () { - $('#table').DataTable({ - scrollX: true, - columnDefs: [ - {type: 'status', targets: 2} - ], - "order": [[2, "asc"]] - }); - }); - </script> -{% endblock tablejs %} diff --git a/src/templates/dashboard/dev_pods.html b/src/templates/dashboard/dev_pods.html deleted file mode 100644 index a6f3b2e..0000000 --- a/src/templates/dashboard/dev_pods.html +++ /dev/null @@ -1,70 +0,0 @@ -{% extends "dashboard/table.html" %} -{% load staticfiles %} -{% load jenkins_filters %} - -{% block table %} - <thead> - <tr> - <th>Name</th> - <th>Slave Name</th> - <th>Booked by</th> - <th>Booked until</th> - <th>Purpose</th> - <th>Utilization</th> - <th>Status</th> - <th></th> - <th></th> - </tr> - </thead> - <tbody> - {% for pod, booking, utilization in dev_pods %} - <tr> - <td> - <a href={% url 'dashboard:resource' resource_id=pod.id %}>{{ pod.name }}</a> - </td> - <td> - <a target='_blank' href={{ pod.slave.url }}>{{ pod.slave.name }}</a> - </td> - <td> - {{ booking.user.username }} - </td> - <td> - {{ booking.end }} - </td> - <td> - {{ booking.purpose }} - </td> - <td> - {{ utilization }} - </td> - <td style="background-color:{{ pod.slave.status | jenkins_status_color }}"> - {{ pod.slave.status }} - </td> - <td> - <a href="{% url 'booking:create' resource_id=pod.id %}" class="btn btn-primary"> - Book - </a> - </td> - <td> - <a href="{% url 'dashboard:resource' resource_id=pod.id %}" class="btn btn-primary"> - Info - </a> - </td> - </tr> - {% endfor %} - </tbody> -{% endblock table %} - -{% block tablejs %} - <script type="text/javascript"> - $(document).ready(function () { - $('#table').DataTable({ - scrollX: true, - columnDefs: [ - {type: 'status', targets: 6} - ], - "order": [[6, "asc"]] - }); - }); - </script> -{% endblock tablejs %} diff --git a/src/templates/dashboard/grid.html b/src/templates/dashboard/grid.html new file mode 100644 index 0000000..ca47f44 --- /dev/null +++ b/src/templates/dashboard/grid.html @@ -0,0 +1,10 @@ +{% extends "base.html" %} +{% load staticfiles %} +{% if script %} <script>{{ script }}</script> {% endif %} +{% block content %} + {% for item in grid_items %} + <div class="grid-item" style="border:2px; border-style:solid; border-color:black; margin:3px"> + {{ item }} + </div> + {% endfor %} +{% endblock content %} diff --git a/src/templates/dashboard/host_profile_detail.html b/src/templates/dashboard/host_profile_detail.html new file mode 100644 index 0000000..abc7648 --- /dev/null +++ b/src/templates/dashboard/host_profile_detail.html @@ -0,0 +1,71 @@ +{% 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 %} +<script type="text/javascript"> + $('.grid').masonry( + { + itemSelector: '.grid-item', + columnWidth: '.grid-sizer', + percentPosition: true + } + ); +</script> + +<style media="screen"> + + @media screen and (min-width: 0px) and (max-width: 767px) + { + + .grid-item + { + width: 100%; + } +} + @media screen and (min-width: 768px) and (max-width: 1200px) { + .grid-item { + width: 50%; + } + + } + + @media screen and (min-width: 1201px) and (max-width: 2000px) { + .grid-item { + width: 33%; + } + } + @media screen and (min-width: 2001px) { + .grid-item { + width: 25%; + } + + } + .grid-item-content { + height: 100px; + background: #D26; + border: 2px solid hsla(0, 0%, 0%, 0.5); + border-radius: 5px; +} + +.grid-item-content--height2 { height: 200px; } +</style> + +<div class="container-fluid"> + <!-- add extra container element for Masonry --> + <div class="grid"> + {% for host in hosts %} + <div class="grid-item col-xs-4"> + <p>stub for current PR</p> + </div> + {% endfor %} + </div> +</div> + +{% endblock content %} diff --git a/src/templates/dashboard/jenkins_slaves.html b/src/templates/dashboard/jenkins_slaves.html deleted file mode 100644 index fa361b1..0000000 --- a/src/templates/dashboard/jenkins_slaves.html +++ /dev/null @@ -1,46 +0,0 @@ -{% extends "dashboard/table.html" %} -{% load staticfiles %} - -{% load jenkins_filters %} - -{% block table %} - <thead> - <tr> - <th>Slave name</th> - <th>Status</th> - <th>Job</th> - </tr> - </thead> - <tbody> - {% for slave in slaves %} - <tr> - <td><a target='_blank' - href={{ slave.url }}>{{ slave.name }}</a> - </td> - <td style="background-color:{{ slave.status | jenkins_status_color }}"> - {{ slave.status }} - </td> - <td><a {{ slave.last_job_result | jenkins_job_blink }} - style="color:{{ slave.last_job_result | jenkins_job_color }}" - target="_blank" href={{ slave.last_job_url }}> - {{ slave.last_job_name }}</a> - </td> - </tr> - {% endfor %} - </tbody> -{% endblock table %} - - -{% block tablejs %} - <script type="text/javascript"> - $(document).ready(function () { - $('#table').DataTable({ - scrollX: true, - columnDefs: [ - {type: 'status', targets: 1} - ], - "order": [[1, "asc"]] - }); - }); - </script> -{% endblock tablejs %} diff --git a/src/templates/dashboard/lab_detail.html b/src/templates/dashboard/lab_detail.html new file mode 100644 index 0000000..4c06245 --- /dev/null +++ b/src/templates/dashboard/lab_detail.html @@ -0,0 +1,157 @@ +{% 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> +{% endblock %} + +{% block content %} +<div class="row"> + <div class="col-lg-4"> + <div class="panel panel-default"> + <div class="panel-heading clearfix"> + <h4 style="display: inline;">Lab Profile</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>Lab Name: </td><td>{{lab.name}}</td> + </tr> + <tr> + <td>Lab Location: </td><td>{{lab.location}}</td> + </tr> + <tr> + <td>Lab Email: </td> + <td>{{lab.contact_email}}</td> + </tr> + {% if lab.contact_phone %} + <tr> + <td>Lab Phone: </td> + <td>{{lab.contact_phone}}</td> + </tr> + {% endif %} + <tr> + <td>Lab Status: </td> + {% if lab.status < 100 %} + <td><div style="border-radius: 50%; background:#40B976; height: 20px; width: 20px; display: inline-block;"></div> + Up</td> + {% elif lab.status < 200 %} + <td> + <div style="border-radius: 50%; background:#eeee00; height: 20px; width: 20px; display: inline-block;"></div> + Temporarily Offline</td> + {% else %} + <td> + <div style="border-radius: 50%; background:#e50000; height: 20px; width: 20px; display: inline-block;"></div> + Offline Indefinitely</td> + {% endif %} + </tr> + </table> + </div> + </div> + <div class="panel panel-default"> + <div class="panel-heading clearfix"> + <h4 style="display: inline;">Host Profiles</h4> + + <a data-toggle="collapse" data-target="#profile_panel" class="btn pull-right" style="line-height: 1;" >Expand</a> + </div> + <div class="panel-body pod_panel" id="profile_panel"> + <table class="table"> + {% for profile in hostprofiles %} + <tr> + <td>{{profile.name}}</td> + <td>{{profile.description}}</td> + <td>{{profile.labs}}</td> + </tr> + {% endfor %} + </table> + </div> + </div> + + + <div class="panel panel-default"> + <div class="panel-heading clearfix"> + <h4 style="display: inline;">Networking Capabilities</h4> + <a data-toggle="collapse" data-target="#network_panel" class="btn pull-right" style="line-height: 1;" >Expand</a> + </div> + + <div class="panel-body" id="network_panel"> + + <table class="table"> + <tr> + <td>Block Size: (number of VLANs allowed per deployment)</td><td>{{lab.vlan_manager.block_size}}</td> + </tr> + <tr> + <td>Overlapping Vlans Allowed (user can pick which VLANs they wish to use): </td> + <td>{{lab.vlan_manager.allow_overlapping}}</td> + </tr> + </table> + </div> + </div> + <div class="panel panel-default"> + <div class="panel-heading clearfix"> + <h4 style="display: inline;">Images</h4> + <a data-toggle="collapse" data-target="#image_panel" class="btn pull-right" style="line-height: 1;" >Expand</a> + </div> + <div class="panel-body" id="image_panel"> + <table class="table"> + <tr> + <th>Name</th> + <th>Owner</th> + <th>For Host Type</th> + <th>Description</th> + </tr> + {% for image in images %} + <tr> + <td>{{image.name}}</td> + <td>{{image.owner}}</td> + <td>{{image.host_type}}</td> + <td>{{image.description}}</td> + </tr> + {% endfor %} + </table> + </div> + </div> + + </div> + <div class="col-lg-8"> + <div class="panel panel-default"> + <div class="panel-heading clearfix"> + <h4 style="display: inline;">Lab Hosts</h4> + <p style="display: inline; margin-left: 10px;"></p> + <a data-toggle="collapse" data-target="#lab_hosts_panel" class="btn pull-right" style="line-height: 1;" >Expand</a> + </div> + + <div class="panel-body" id="lab_hosts_panel"> + <table class="table"> + <tr> + <th>Name</th> + <th>Profile</th> + <th>Booked</th> + <th>Working</th> + <th>Vendor</th> + </tr> + {% for host in lab.host_set.all %} + <tr> + <td>{{host.labid}}</td> + <td>{{host.profile}}</td> + <td>{{host.booked}}</td> + {% if host.working %} + <td style="background-color: #40B976;">{{host.working}}</td> + {% else %} + <td>{{host.working}}</td> + {% endif %} + <td>{{host.vendor}}</td> + </tr> + {% endfor %} + </table> + </div> + </div> + </div> + +</div> + + +{% endblock content %} + diff --git a/src/templates/dashboard/lab_list.html b/src/templates/dashboard/lab_list.html new file mode 100644 index 0000000..a86f7f4 --- /dev/null +++ b/src/templates/dashboard/lab_list.html @@ -0,0 +1,87 @@ +{% 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> +{% endblock %} + +{% block content %} + <style> + .grid-item-container { + padding: 10px; + } + + .grid-item { + cursor: pointer; + border:2px; + border-style:none; + border-color:black; + border-radius: 5px; + padding: 7px; + color: inherit; + + box-shadow: 0px 0px 7px 0px rgba(0,0,0,0.75); + transition-property: box-shadow, background-color; + transition-duration: .2s; + } + + .grid-item-text + { + color: inherit; + text-decoration: none; + } + .grid-item-text:hover + { + color: #121212; + text-decoration: none; + } + + .grid-item:hover { + box-shadow: 0px 0px 14px 0px rgba(0,0,0,0.75); + transition-property: box-shadow; + transition-duration: .2s; + + } + + .selected_node { + box-shadow: 0px 0px 14px 0px rgba(0,0,0,0.75); + background-color: #CCECD7; + transition-property: background-color; + transition-duration: .2s; + } + + .disabled_node { + cursor: not-allowed; + background-color: #EFEFEF; + box-shadow: 0px 0px 1px 0px rgba(0,0,0,0.75); + transition-property: box-shadow; + transition-duration: .2s; + } + + .disabled_node:hover { + box-shadow: 0px 0px 1px 0px rgba(0,0,0,0.75); + } + + </style> + <div class="container-fluid"> + <div class="row"> + + <div class="listgrid"> + {% for lab in labs %} + <div class="grid-item-container col-lg-2 col-mid-4 col-sm-6"> + + <a href="{{ lab.name }}" class="grid-item-text"> + + <div class="grid-item"> + <h4 class="grid-item-header">{{ lab.name }}</h4> + <p class="grid-item-description">{{ lab.description }}</p> + </div> + </a> + </div> + {% endfor %} + </div> + </div> + </div> + +{% endblock content %} diff --git a/src/templates/dashboard/landing.html b/src/templates/dashboard/landing.html new file mode 100644 index 0000000..3e0aacd --- /dev/null +++ b/src/templates/dashboard/landing.html @@ -0,0 +1,88 @@ +{% extends "base.html" %} +{% load staticfiles %} + +{% block content %} + <div class=""> + <p style="text-align:center;">Welcome to the Pharos Dashboard! To get started, select one of the options below:</p> + </div> +{% csrf_token %} + +<style> + .wf_create{ + display: inline-block; + text-align: center; + } + .wf_create_div{ + text-align: center; + } + .hidden_form{ + display: none; + } + + .panel { + border: none; + } + + .panels { + display: grid; + grid-template-columns: 33% 34% 33%; + } +</style> +<script type="text/javascript"> + function cwf(wf_type){ + document.getElementById('id_workflow').selectedIndex = wf_type; + document.getElementById('wf_selection_form').submit(); + } +</script> +<div class='wf_create_div'> +<button class="wf_create btn" onclick="cwf(0)">Create a Booking</button> +<button class="wf_create btn" onclick="cwf(1)">Create a Pod</button> +<button class="wf_create btn" onclick="cwf(2)">Configure a Pod</button> +<button class="wf_create btn" onclick="cwf(3)">Create a Snapshot</button> +{% if manager == True %} +<button class="wf_continue btn" onclick="continue_wf()">Finish Unfinished Business</button> +{% endif %} +</div> + +<script type="text/javascript"> + function cwf(type) + { + $.ajax({ + type: "POST", + url: "/", + data: {"create":type}, + beforeSend: function(request) { + request.setRequestHeader("X-CSRFToken", + $('input[name="csrfmiddlewaretoken"]').val() + ); + } + + }).done(function (data) { + window.location.replace("/wf/"); + }).fail(function(jqxHR, textstatus) { + alert("Something went wrong...");}); + } + function continue_wf() + { + window.location.replace("/wf/"); + } + + //success: window.location.replace("/wf/") + +</script> + +<div class="hidden_form" id="form_div"> + <form method="post" action="" class="form" id="wf_selection_form"> + {% csrf_token %} + + <input type="hidden" id="landing_action"> + + <button type="submit" class="btn btn btn-success"> + Confirm Edit + </button> + </form> +</div> + +{% block vport_comm %} +{% endblock %} +{% endblock content %} diff --git a/src/templates/dashboard/login.html b/src/templates/dashboard/login.html new file mode 100644 index 0000000..d3aa4ad --- /dev/null +++ b/src/templates/dashboard/login.html @@ -0,0 +1,8 @@ +{% extends "base.html" %} + +{% block content %} +<h3>You Must Login To Do That</h3> + +<a href="/accounts/login">Login Here</a> + +{% endblock %} diff --git a/src/templates/dashboard/multiple_select_filter_widget.html b/src/templates/dashboard/multiple_select_filter_widget.html new file mode 100644 index 0000000..ed29ed6 --- /dev/null +++ b/src/templates/dashboard/multiple_select_filter_widget.html @@ -0,0 +1,402 @@ +<style> +.object_class_wrapper { + display: grid; + grid-template-columns: 1fr 1fr 1fr; + border: 0px; +} +.class_grid_wrapper { + border: 0px; + border-left: 1px; + border-right: 1px; + border-style: solid; + border-color: grey; + text-align: center; +} +.grid_wrapper { + display: grid; + grid-template-columns: 1fr 1fr; +} +.grid-item { + cursor: pointer; + border:2px; + border-style:none; + border-color:black; + border-radius: 5px; + margin:20px; + height: 200px; + padding: 7px; + box-shadow: 0px 0px 7px 0px rgba(0,0,0,0.75); + transition-property: box-shadow, background-color; + transition-duration: .2s; +} + +.grid-item:hover { + box-shadow: 0px 0px 14px 0px rgba(0,0,0,0.75); + transition-property: box-shadow; + transition-duration: .2s; + +} + +.selected_node { + box-shadow: 0px 0px 14px 0px rgba(0,0,0,0.75); + background-color: #CCECD7; + transition-property: background-color; + transition-duration: .2s; +} + +.disabled_node { + cursor: not-allowed; + background-color: #EFEFEF; + box-shadow: 0px 0px 1px 0px rgba(0,0,0,0.75); + transition-property: box-shadow; + transition-duration: .2s; +} + +.disabled_node:hover { + box-shadow: 0px 0px 1px 0px rgba(0,0,0,0.75); +} + +.cleared_node { + background-color: #FFFFFF; +} + +.grid-item-header +{ + font-weight: bold; + font-size: 20px; + margin-top: 10px; +} + +</style> +<input name="filter_field" id="filter_field" type="hidden"/> +<div id="grid_wrapper" class="grid_wrapper"> +{% for object_class, object_list in filter_objects %} +<div class="class_grid_wrapper"> + <div style="display:inline-block;margin:auto"> + <h4>{{object_class}}</h4> + </div> + <div id="{{object_class}}" class="object_class_wrapper"> + {% for obj in object_list %} + <div id="object_parent"> + <div id="{{ obj.id|default:'not_provided' }}" class="grid-item"> + <p class="grid-item-header">{{obj.name}}</p> + <p class="grid-item-description">{{obj.description}}</p> + <button type="button" class="btn btn-success grid-item-select-btn" onclick="processClick('{{obj.id}}', {% if obj.multiple %}true{% else %}false{% endif %});">{% if obj.multiple %}Add{% else %}Select{% endif %}</button> + </div> + <input type="hidden" name="{{obj.id}}_selected" value="false"/> + </div> + {% endfor %} + </div> + </div> +{% endfor %} +</div> + +<div id="dropdown_wrapper"> +</div> + +<script> +var initialized = false; +var mapping = {{ mapping|safe }}; +var items = {{ items|safe }}; +var result = {}; +var selection = {{selection_data|default_if_none:"null"|safe}}; +var dropdown_count = 0; + +{% if selection_data %} +make_selection({{selection_data|safe}}); +{% endif %} + +function make_selection( selection_data ){ + if(!initialized) { + init(); + } + for(var k in selection_data) { + selected_items = selection_data[k]; + for( var item in selected_items ){ + var node = items[item]; + if(!node['multiple']){ + var input_value = selected_items[item]; + if( input_value != 'false' ) { + select(node); + markAndSweep(node); + } + var div = document.getElementById(item) + var input = div.parentNode.getElementsByTagName("input")[0] + input.value = input_value; + updateResult(item); + } else { + make_multiple_selection(selected_items, item); + } + } + } +} + +function make_multiple_selection(data, item_class){ + var node = items[item_class]; + select(node); + markAndSweep(node); + prepop_data = data[item_class]; + for(var i=0; i<prepop_data.length; i++){ + var div = add_item_prepopulate(node, prepop_data[i]); + updateObjectResult(div); + } +} + +function markAndSweep(root){ + for(var nodeId in items) { + node = items[nodeId]; + node['marked'] = true; //mark all nodes + //clears grey background of everything + } + + toCheck = []; + toCheck.push(root); + + while(toCheck.length > 0){ + node = toCheck.pop(); + if(!node['marked']) { + //already visited, just continue + continue; + } + node['marked'] = false; //mark as visited + if(node['follow'] || node == root){ //add neighbors if we want to follow this node (labs) + var mappingId = node.id + var neighbors = mapping[mappingId]; + for(var neighId in neighbors) { + neighId = neighbors[neighId]; + var neighbor = items[neighId]; + toCheck.push(neighbor); + } + } + } + + //now remove all nodes still marked + for(var nodeId in items){ + node = items[nodeId]; + if(node['marked']){ + disable(node); + } + } +} + +function process(node) { + if(node['selected']) { + markAndSweep(node); + } + else { + var selected = [] + //remember the currently selected, then reset everything and reselect one at a time + for(var nodeId in items) { + node = items[nodeId]; + if(node['selected']) { + selected.push(node); + } + clear(node); + + } + for(var i=0; i<selected.length; i++) { + node = selected[i]; + select(node); + markAndSweep(selected[i]); + } + } +} + +function select(node) { + elem = document.getElementById(node['id']); + node['selected'] = true; + elem.classList.remove('cleared_node') + elem.classList.remove('disabled_node') + elem.classList.add('selected_node') + var input = elem.parentNode.getElementsByTagName("input")[0]; + input.disabled = false; + input.value = true; +} + +function clear(node) { + elem = document.getElementById(node['id']); + node['selected'] = false; + node['selectable'] = true; + elem.classList.add('cleared_node') + elem.classList.remove('disabled_node') + elem.classList.remove('selected_node') + elem.parentNode.getElementsByTagName("input")[0].disabled = true; +} + +function disable(node) { + elem = document.getElementById(node['id']); + node['selected'] = false; + node['selectable'] = false; + elem.classList.remove('cleared_node') + elem.classList.add('disabled_node') + elem.classList.remove('selected_node') + elem.parentNode.getElementsByTagName("input")[0].disabled = true; +} + +function processClick(id, multiple){ + if(!initialized){ + init(); + } + var element = document.getElementById(id); + var node = items[id]; + if(!node['selectable']){ + return; + } + if(multiple){ + return processClickMultipleObject(node); + } + node['selected'] = !node['selected']; //toggle on click + + if(node['selected']) { + select(node); + } + else { + clear(node); + } + process(node); + updateResult(id); +} + +function processClickMultipleObject(node){ + select(node); + add_item(node); + process(node); +} + +function add_item(node){ + return add_item_prepopulate(node, {}); +} + +inputs = [] + +function restrictchars(input) +{ + if( input.validity.patternMismatch ) + { + input.setCustomValidity("Only alphanumeric characters (a-z, A-Z, 0-9), underscore(_), and hyphen (-) are allowed"); + input.reportValidity(); + } + + input.value = input.value.replace(/([^A-Za-z0-9-_.])+/g, ""); + + checkunique(input); +} + +function checkunique(tocheck) +{ + val = tocheck.value; + for( var i = 0; i < inputs.length; i++ ) + { + if( inputs[i].value == val && inputs[i] != tocheck) + { + tocheck.setCustomValidity("All hostnames must be unique"); + tocheck.reportValidity(); + return; + } + } + tocheck.setCustomValidity(""); +} + +function add_item_prepopulate(node, prepopulate){ + inputs = []; + var div = document.createElement("DIV"); + div.class = node['id']; + div.id = "dropdown_" + dropdown_count; + dropdown_count++; + var label = document.createElement("H5"); + label.style['display'] = 'inline'; + label.appendChild(document.createTextNode(node['name'])); + div.appendChild(label); + for(var i=0; i<node['forms'].length; i++){ + form = node['forms'][i]; + var input = document.createElement("INPUT"); + input.type = form['type']; + input.name = form['name']; + input.pattern = "(?=^.{1,253}$)(^([A-Za-z0-9-_]{1,62}\.)*[A-Za-z0-9-_]{1,63})"; + input.title = "Only alphanumeric characters (a-z, A-Z, 0-9), underscore(_), and hyphen (-) are allowed" + input.placeholder = form['placeholder']; + inputs.push(input); + input.onchange = function() { updateObjectResult(div); restrictchars(this); }; + input.oninput = function() { restrictchars(this); }; + if(form['name'] in prepopulate){ + input.value = prepopulate[form['name']]; + } + div.appendChild(input); + } + //add class id to dropdown object + var hiddenInput = document.createElement("INPUT"); + hiddenInput.type = "hidden"; + hiddenInput.name = "class"; + hiddenInput.value = node['id']; + div.appendChild(hiddenInput); + button = document.createElement("BUTTON"); + button.onclick = function(){ + remove_dropdown(div.id); + } + button.type = "button"; + button.appendChild(document.createTextNode("Remove")); + div.appendChild(button); + document.getElementById("dropdown_wrapper").appendChild(div); + return div; +} + +function remove_dropdown(id){ + var div = document.getElementById(id); + var parent = div.parentNode; + div.parentNode.removeChild(div); + //checks if we have removed last item in class + var deselect_class = true; + var div_inputs = div.getElementsByTagName("input"); + var div_class = div_inputs[div_inputs.length-1].value; + var result_class = document.getElementById(div_class).parentNode.parentNode.id; + delete result[result_class][div.id]; + for(var i=0; i<parent.children.length; i++){ + var inputs = parent.children[i].getElementsByTagName("input"); + var object_class = ""; + for(var k=0; k<inputs.length; k++){ + if(inputs[k].name == "class"){ + object_class = inputs[k].value; + } + } + if(object_class == div_class){ + deselect_class = false; + } + } + if(deselect_class){ + clear(items[div_class]); + } +} + +function updateResult(nodeId){ + if(!initialized){ + init(); + } + if(!items[nodeId]['multiple']){ + var node = document.getElementById(nodeId); + var value = {} + value[nodeId] = node.parentNode.getElementsByTagName("input")[0].value; + result[node.parentNode.parentNode.id][nodeId] = value; + } +} + +function updateObjectResult(parentElem){ + node_type = document.getElementById(parentElem.class).parentNode.parentNode.id; + input = {}; + inputs = parentElem.getElementsByTagName("input"); + for(var i in inputs){ + var e = inputs[i]; + input[e.name] = e.value; + } + result[node_type][parentElem.id] = input; +} + +function init() { + for(nodeId in items) { + element = document.getElementById(nodeId); + node = items[nodeId]; + result[element.parentNode.parentNode.id] = {} + } + initialized = true; +} + +</script> diff --git a/src/templates/dashboard/pdf.yaml b/src/templates/dashboard/pdf.yaml new file mode 100644 index 0000000..297e04b --- /dev/null +++ b/src/templates/dashboard/pdf.yaml @@ -0,0 +1,95 @@ +--- +version: {{version|default:"1.0"}} +details: + pod_owner: {{details.owner}} + contact: {{details.contact}} + lab: {{details.lab}} + location: {{details.location}} + type: {{details.type}} + link: {{details.link}} + +jumphost: + name: {{jumphost.name}} + node: + type: {{jumphost.node.type}} + vendor: {{jumphost.node.vendor}} + model: {{jumphost.node.model}} + arch: {{jumphost.node.arch}} + cpus: {{jumphost.node.cpus}} + cpu_cflags: {{jumphost.node.cpu_cflags}} + cores: {{jumphost.node.cores}} + memory: {{jumphost.node.memory}} + disks: + {% for disk in jumphost.disks %} + - name: {{disk.name}} + disk_capacity: {{disk.capacity}} + disk_type: {{disk.type}} + disk_interface: {{disk.interface}} + disk_rotation: {{disk.rotation}} + + {% endfor %} + os: {{jumphost.os}} + remote_params: + type: {{jumphost.remote.type}} + versions: + {% for version in jumphost.remote.versions %} + - {{version}} + {% endfor %} + user: {{jumphost.remote.user}} + pass: {{jumphost.remote.pass}} + remote_management: + type: {{jumphost.remote.type}} + versions: + {% for version in jumphost.remote.versions %} + - {{version}} + {% endfor %} + user: {{jumphost.remote.user}} + pass: {{jumphost.remote.pass}} + address: {{jumphost.remote.address}} + mac_address: {{jumphost.remote.mac_address}} + interfaces: + {% for interface in jumphost.interfaces %} + - name: {{interface.name}} + address: {{interface.address}} + mac_address: {{interface.mac_address}} + vlan: {{interface.vlan}} + {% endfor %} +nodes: + {% for node in nodes %} + - name: {{node.name}} + node: + type: {{node.node.type}} + vendor: {{node.node.vendor}} + model: {{node.node.model}} + arch: {{node.node.arch}} + cpus: {{node.node.cpus}} + cpu_cflags: {{node.node.cpu_cflags}} + cores: {{node.node.cores}} + memory: {{node.node.memory}} + disks: + {% for disk in node.disks %} + - name: {{disk.name}} + disk_capacity: {{disk.capacity}} + disk_type: {{disk.type}} + disk_interface: {{disk.interface}} + disk_rotation: {{disk.rotation}} + + {% endfor %} + remote_management: + type: {{node.remote.type}} + versions: + {% for version in node.remote.versions %} + - {{version}} + {% endfor %} + user: {{node.remote.user}} + pass: {{node.remote.pass}} + address: {{node.remote.address}} + mac_address: {{node.remote.mac_address}} + interfaces: + {% for interface in node.interfaces %} + - name: {{interface.name}} + address: {{interface.address}} + mac_address: {{interface.mac_address}} + vlan: {{interface.vlan}} + {% endfor %} + {% endfor %} diff --git a/src/templates/dashboard/resource.html b/src/templates/dashboard/resource.html index c9e5735..28e7998 100644 --- a/src/templates/dashboard/resource.html +++ b/src/templates/dashboard/resource.html @@ -2,6 +2,7 @@ {% load staticfiles %} {% block extrahead %} + {{ block.super }} <!-- Morris Charts CSS --> <link href="{% static "bower_components/morrisjs/morris.css" %}" rel="stylesheet"> @@ -50,8 +51,6 @@ var utilization_url = "{% url 'dashboard:booking_utilization' resource_id=resource.id weeks=4 %}"; loadChartData(chart_id, utilization_url); - var chart_id = "{{ resource.id }}_jenkins_utilization"; - var utilization_url = "{% url 'dashboard:jenkins_utilization' resource_id=resource.id weeks=1 %}"; loadChartData(chart_id, utilization_url); }); </script> diff --git a/src/templates/dashboard/resource_all.html b/src/templates/dashboard/resource_all.html index a770d4e..0b0d0d4 100644 --- a/src/templates/dashboard/resource_all.html +++ b/src/templates/dashboard/resource_all.html @@ -2,6 +2,7 @@ {% load staticfiles %} {% block extrahead %} + {{ block.super }} <!-- Morris Charts CSS --> <link href="{% static "bower_components/morrisjs/morris.css" %}" rel="stylesheet"> @@ -63,10 +64,6 @@ var chart_id = "{{ resource.id }}_booking_utilization"; var utilization_url = "{% url 'dashboard:booking_utilization' resource_id=resource.id weeks=4 %}"; loadChartData(chart_id, utilization_url); - - var chart_id = "{{ resource.id }}_jenkins_utilization"; - var utilization_url = "{% url 'dashboard:jenkins_utilization' resource_id=resource.id weeks=1 %}"; - loadChartData(chart_id, utilization_url); {% endfor %} }); </script> diff --git a/src/templates/dashboard/resource_detail.html b/src/templates/dashboard/resource_detail.html index 740dd25..79389f0 100644 --- a/src/templates/dashboard/resource_detail.html +++ b/src/templates/dashboard/resource_detail.html @@ -1,32 +1,4 @@ -{% load jenkins_filters %} - <div class="row"> - <div class="col-lg-3"> - <div class="panel panel-default"> - <div class="panel-heading"> - Jenkins Utilization - <div class="pull-right"> - <div class="form-group"> - <select onchange="loadChartData('{{ resource.id }}_jenkins_utilization', this.value);"> - <option value="{% url 'dashboard:jenkins_utilization' resource_id=resource.id weeks=1 %}"> - Last Week - </option> - <option value="{% url 'dashboard:jenkins_utilization' resource_id=resource.id weeks=4 %}"> - Last Month - </option> - </select> - </div> - </div> - </div> - <div class="panel-body"> - <div class="flot-chart"> - <div class="flot-chart-content" - id="{{ resource.id }}_jenkins_utilization"></div> - </div> - </div> - </div> - </div> - <div class="col-lg-9"> <div class="panel panel-default"> <div class="panel-heading"> @@ -141,30 +113,6 @@ </div> </div> </div> - <div class="col-lg-3"> - <div class="panel panel-default"> - <div class="panel-heading"> - Jenkins Status - </div> - <div class="panel-body"> - <p> - <b>Slave Name: </b> - <a target='_blank' - href={{ resource.slave.url }}>{{ resource.slave.name }}</a> - </p> - <p> - <b>Status: </b> - {{ resource.slave.status }} - </p> - <p> - <b>Last Job: </b> - <a href="{{ resource.slave.last_job_url }}"> - {{ resource.slave.last_job_name }} - </a> - </p> - </div> - </div> - </div> <div class="col-lg-6"> <div class="panel panel-default"> <div class="panel-heading"> diff --git a/src/templates/dashboard/searchable_select_multiple.html b/src/templates/dashboard/searchable_select_multiple.html new file mode 100644 index 0000000..e7128b0 --- /dev/null +++ b/src/templates/dashboard/searchable_select_multiple.html @@ -0,0 +1,408 @@ +<script src="https://code.jquery.com/jquery-2.2.4.min.js"></script> + +<div class="autocomplete" style="width:400px;"> + <input id="user_field" name="ignore_this" class="form-control" autocomplete="off" type="text" placeholder="{{placeholder}}" value="{{initial.name}}" oninput="search(this.value)" + {% if disabled %} disabled {% endif %} + > + + <input type="hidden" id="selector" name="{{ name }}" class="form-control" style="display: none;" + {% if disabled %} disabled {% endif %} + > + </input> + + <ul id="drop_results"></ul> + + + <div id="default_entry_wrap" style="display: none;"> + <div class="list_entry unremovable_list_entry"> + <p id="default_text" class="full_name"></p> + <button class="btn-remove btn disabled">remove</button> + </div> + </div> + + <div id="added_list"> + + </div> + <div id="added_counter" style="text-align: center; margin: 10px;"><p id="added_number" style="display: inline;">0</p><p style="display: inline;">/ + {% if selectable_limit > -1 %} {{ selectable_limit }} {% else %} ∞ {% endif %}added</p></div> + <style> + #user_field { + font-size: 14pt; + width: 400px; + padding: 5px; + + } + + #drop_results{ + list-style-type: none; + padding: 0; + margin: 0; + max-height: 300px; + min-height: 0; + overflow-y: scroll; + overflow-x: hidden; + border: solid 1px #ddd; + display: none; + + } + + #drop_results li a{ + font-size: 14pt; + border: 1px solid #ddd; + background-color: #f6f6f6; + padding: 12px; + text-decoration: none; + display: block; + width: 400px; + } + + .btn-remove { + float: right; + height: 30px; + margin: 4px; + } + + .list_entry { + width: 400px; + border: 1px solid #ddd; + border-radius: 3px; + margin-top: 5px; + vertical-align: middle; + line-height: 40px; + height: 40px; + padding-left: 12px; + } + + #drop_results li a:hover{ + background-color: #ffffff; + } + + .small_name { + display: inline-block; + } + + .full_name { + display: inline-block; + } + + </style> +</div> + +<script type="text/javascript"> + //flags + var show_from_noentry = {{show_from_noentry|default:"false"}}; + var show_x_results = {{show_x_results|default:-1}}; + var results_scrollable = {{results_scrollable|default:"false"}}; + var selectable_limit = {{selectable_limit|default:-1}}; + var field_name = "{{name|default:"users"}}"; + var placeholder = "{{placeholder|default:"begin typing"}}"; + var default_entry = "{{default_entry}}"; + + //needed info + var items = {{items|safe}} + + //tries + var expanded_name_trie = {} + expanded_name_trie.isComplete = false; + var small_name_trie = {} + small_name_trie.isComplete = false; + var string_trie = {} + string_trie.isComplete = false; + + var added_items = []; + + var added_template = {{ added_list|default:"{}" }}; + + if( default_entry ) + { + var default_entry_div = document.getElementById("default_entry_wrap"); + default_entry_div.style.display = "inherit"; + + var entry_p = document.getElementById("default_text"); + entry_p.innerText = default_entry; + } + + init(); + + if( show_from_noentry ) + { + search(""); + } + + function disable() { + var textfield = document.getElementById("user_field"); + var drop = document.getElementById("drop_results"); + + textfield.disabled = "True"; + drop.style.display = "none"; + + var btns = document.getElementsByClassName("btn-remove"); + for( var i = 0; i < btns.length; i++ ) + { + btns[i].classList.add("disabled"); + } + } + + function init() { + build_all_tries(items); + + var initial = {{ initial|safe }}; + + for( var i = 0; i < initial.length; i++) + { + select_item(String(initial[i])); + } + if(initial.length == 1) + { + search(items[initial[0]]["small_name"]); + document.getElementById("user_field").value = items[initial[0]]["small_name"]; + } + } + + function build_all_tries(dict) + { + for( var i in dict ) + { + add_item(dict[i]); + } + } + + function add_item(item) + { + var id = item['id']; + add_to_tree(item['expanded_name'], id, expanded_name_trie); + add_to_tree(item['small_name'], id, small_name_trie); + add_to_tree(item['string'], id, string_trie); + } + + function add_to_tree(str, id, trie) + { + inner_trie = trie; + while( str ) + { + if( !inner_trie[str.charAt(0)] ) + { + new_trie = {}; + inner_trie[str.charAt(0)] = new_trie; + } + else + { + new_trie = inner_trie[str.charAt(0)]; + } + + if( str.length == 1 ) + { + new_trie.isComplete = true; + new_trie.itemID = id; + } + inner_trie = new_trie; + str = str.substring(1); + } + } + + function search(input) + { + if( input.length == 0 && !show_from_noentry){ + dropdown([]); + return; + } + else if( input.length == 0 && show_from_noentry) + { + dropdown(items); //show all items + } + else + { + var trees = [] + var tr1 = getSubtree(input, expanded_name_trie); + trees.push(tr1); + var tr2 = getSubtree(input, small_name_trie); + trees.push(tr2); + var tr3 = getSubtree(input, string_trie); + trees.push(tr3); + var results = collate(trees); + dropdown(results); + } + } + + function getSubtree(input, given_trie) + { + /* + recursive function to return the trie accessed at input + */ + + if( input.length == 0 ){ + return given_trie; + } + + else{ + var substr = input.substring(0, input.length - 1); + var last_char = input.charAt(input.length-1); + var subtrie = getSubtree(substr, given_trie); + if( !subtrie ) //substr not in the trie + { + return {}; + } + var indexed_trie = subtrie[last_char]; + return indexed_trie; + } + } + + function serialize(trie) + { + /* + takes in a trie and returns a list of its item id's + */ + var itemIDs = []; + if ( !trie ) + { + return itemIDs; //empty, base case + } + for( var key in trie ) + { + if(key.length > 1) + { + continue; + } + itemIDs = itemIDs.concat(serialize(trie[key])); + } + if ( trie.isComplete ) + { + itemIDs.push( trie.itemID ); + } + + return itemIDs; + } + + function collate(trees) + { + /* + takes a list of tries + returns a list of ids of objects that are available + */ + results = []; + for( var i in trees ) + { + var available_IDs = serialize(trees[i]); + for( var j=0; j<available_IDs.length; j++){ + var itemID = available_IDs[j]; + results[itemID] = items[itemID]; + } + } + return results; + } + + function dropdown(ids) + { + /* + takes in a mapping of ids to objects in items + and displays them in the dropdown + */ + var drop = document.getElementById("drop_results"); + while(drop.firstChild) + { + drop.removeChild(drop.firstChild); + } + + for( var id in ids ) + { + var result_entry = document.createElement("li"); + var result_button = document.createElement("a"); + var obj = items[id]; + var result_text = document.createTextNode(obj['small_name'] + " : " + obj['expanded_name']); + result_button.appendChild(result_text); + result_button.setAttribute('onclick', 'select_item("' + obj['id'] + '")'); + result_entry.appendChild(result_button); + drop.appendChild(result_entry); + } + + if( !drop.firstChild ) + { + drop.style.display = 'none'; + } + else + { + drop.style.display = 'inherit'; + } + } + + function select_item(item_id) + { + //TODO make faster + var item = items[item_id]; + if( (selectable_limit > -1 && added_items.length < selectable_limit) || selectable_limit < 0 ) + { + if( added_items.indexOf(item) == -1 ) + { + added_items.push(item); + } + } + + update_selected_list(); + document.getElementById("user_field").focus(); + } + + function remove_item(item_ref) + { + + item = Object.values(items)[item_ref]; + var index = added_items.indexOf(item); + added_items.splice(index, 1); + + update_selected_list() + document.getElementById("user_field").focus(); + } + + function edit_item(item_id){ + var wf_type = "{{wf_type}}"; + parent.add_edit_wf(wf_type, item_id); + } + + function update_selected_list() + { + document.getElementById("added_number").innerText = added_items.length; + selector = document.getElementById('selector'); + selector.value = JSON.stringify(added_items); + added_list = document.getElementById('added_list'); + + while(selector.firstChild) + { + selector.removeChild(selector.firstChild); + } + while(added_list.firstChild) + { + added_list.removeChild(added_list.firstChild); + } + + list_html = ""; + + for( var key in added_items ) + { + item = added_items[key]; + + list_html += '<div class="list_entry"><p class="full_name">' + + item["expanded_name"] + + '</p><p class="small_name">, ' + + item["small_name"] + + '</p><button onclick="remove_item(' + + Object.values(items).indexOf(item) + + ')" class="btn-remove btn">remove</button>'; + {% if edit %} + list_html += '<button onclick="edit_item(' + + item['id'] + + ')" class="btn-remove btn">edit</button>'; + {% endif %} + list_html += '</div>'; + } + + added_list.innerHTML = list_html; + } + +</script> +<style> + .full_name { + display: inline-block; + } + .small_name { + display: inline-block; + } +</style> diff --git a/src/templates/dashboard/table.html b/src/templates/dashboard/table.html index d59f0e3..b3f4b5f 100644 --- a/src/templates/dashboard/table.html +++ b/src/templates/dashboard/table.html @@ -2,6 +2,7 @@ {% load staticfiles %} {% block extrahead %} + {{ block.super }} <!-- DataTables CSS --> <link href="{% static "bower_components/datatables-plugins/integration/bootstrap/3/dataTables.bootstrap.css" %}" rel="stylesheet"> |