diff options
Diffstat (limited to 'src/templates/dashboard')
-rw-r--r-- | src/templates/dashboard/genericselect.html | 104 | ||||
-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/idf.yaml | 46 | ||||
-rw-r--r-- | src/templates/dashboard/lab_detail.html | 154 | ||||
-rw-r--r-- | src/templates/dashboard/lab_list.html | 28 | ||||
-rw-r--r-- | src/templates/dashboard/landing.html | 172 | ||||
-rw-r--r-- | src/templates/dashboard/login.html | 8 | ||||
-rw-r--r-- | src/templates/dashboard/multiple_select_filter_widget.html | 149 | ||||
-rw-r--r-- | src/templates/dashboard/pdf.yaml | 92 | ||||
-rw-r--r-- | src/templates/dashboard/resource.html | 57 | ||||
-rw-r--r-- | src/templates/dashboard/resource_all.html | 70 | ||||
-rw-r--r-- | src/templates/dashboard/resource_detail.html | 151 | ||||
-rw-r--r-- | src/templates/dashboard/searchable_select_multiple.html | 209 | ||||
-rw-r--r-- | src/templates/dashboard/server_table.html | 30 | ||||
-rw-r--r-- | src/templates/dashboard/table.html | 45 |
16 files changed, 1396 insertions, 0 deletions
diff --git a/src/templates/dashboard/genericselect.html b/src/templates/dashboard/genericselect.html new file mode 100644 index 0000000..441d8dc --- /dev/null +++ b/src/templates/dashboard/genericselect.html @@ -0,0 +1,104 @@ +{% extends "workflow/viewport-element.html" %} +{% load staticfiles %} + +{% load bootstrap4 %} + +{% block content %} + +<style> + #page-wrapper { + display: flex; + flex-direction: column; + } + + #{{select_type}}_form_div div { + } + + #{{select_type}}_form_div > * { + margin-left: 10px; + margin-right: 10px; + margin-bottom: 20px; + } + + #{{select_type}}_form_div div * { + } + + #{{select_type}}_form_div { + flex: 1; + margin: 30px; + display: flex; + flex-direction: column; + } + + #select_section { + flex: 1; + display: flex; + flex-direction: column; + } + + #{{select_type}}_select_form { + flex: 1; + display: flex; + flex-direction: column; + } + + .autocomplete { + flex: 1; + } + + #create_section { + } + + #select_header_section { + } + + h3 { + margin-top: 0; + margin-bottom: 0; + vertical-align: middle; + } + + .divider { + border-top: 1px solid #ccc; + } + + +</style> + +<div id="{{select_type}}_form_div"> + <h3 id="create_section">Create a Resource + <button class="btn btn-primary {% if disabled %} disabled {% endif %}" + {% if not disabled %}onclick="parent.add_wf({{addable_type_num}})" + {% endif %}>Here + </button> + </h3> + <div class="divider"></div> + <h3 id="select_header_section">Or select from the list below:</h3> + <div id="select_section"> + <form id="{{select_type}}_select_form" method="post" action="" class="form" id="{{select_type}}selectorform"> + {% csrf_token %} + {{ form|default:"<p>no form loaded</p>" }} + {% buttons %} + + {% endbuttons %} + </form> + </div> +</div> + +<script> + {% if disabled %} + disable(); + {% endif %} +</script> + +{% endblock content %} +{% block onleave %} +var form = $("#{{select_type}}_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/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/idf.yaml b/src/templates/dashboard/idf.yaml new file mode 100644 index 0000000..9e0cc26 --- /dev/null +++ b/src/templates/dashboard/idf.yaml @@ -0,0 +1,46 @@ +--- +idf: + version: {{version|default:"0.1"}} + net_config: + oob: + ip-range: {{net_config.oob.ip_range}} + vlan: {{net_config.oob.vlan}} + admin: + interface: {{net_config.admin.interface}} + vlan: {{net_config.admin.vlan}} + network: {{net_config.admin.network}} + mask: {{net_config.admin.mask}} + mgmt: + interface: {{net_config.mgmt.interface}} + vlan: {{net_config.mgmt.vlan}} + network: {{net_config.mgmt.network}} + mask: {{net_config.mgmt.mask}} + private: + interface: {{net_config.private.interface}} + vlan: {{net_config.private.vlan}} + network: {{net_config.private.network}} + mask: {{net_config.private.mask}} + public: + interface: {{net_config.public.interface}} + vlan: {{net_config.public.vlan}} + network: {{net_config.public.network}} + mask: {{net_config.public.mask}} + ip-range: {{net_config.public.ip_range}} + mask: {{net_config.public.mask}} + gateway: {{net_config.public.gateway}} + dns: {% for serv in net_config.public.dns %} + - {{serv}}{% endfor %} + fuel: + jumphost: + bridges: + admin: {{fuel.jumphost.bridges.admin}} + mgmt: {{fuel.jumphost.bridges.mgmt}} + private: {{fuel.jumphost.bridges.private}} + public: {{fuel.jumphost.bridges.public}} + network: {% for node in fuel.network.nodes %} + node: + - interfaces: {% for iface in node.interfaces %} + - {{ iface }}{% endfor %} + - busaddr: {% for addr in node.bus_addrs %} + - {{addr}}{% endfor %} + {% endfor %} diff --git a/src/templates/dashboard/lab_detail.html b/src/templates/dashboard/lab_detail.html new file mode 100644 index 0000000..336b32e --- /dev/null +++ b/src/templates/dashboard/lab_detail.html @@ -0,0 +1,154 @@ +{% 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="card my-2"> + <div class="card-header d-flex"> + <h4>Lab Profile</h4> + <button class="btn btn-outline-secondary ml-auto" data-toggle="collapse" data-target="#panel_overview">Expand</button> + </div> + <div id="panel_overview" class="card-body collapse show"> + <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="card my-2"> + <div class="card-header d-flex"> + <h4 class="d-inline-block">Host Profiles</h4> + <button data-toggle="collapse" data-target="#profile_panel" class="btn btn-outline-secondary ml-auto" style="line-height: 1;" >Expand</button> + </div> + <div id="profile_panel" class="card-body collapse show"> + <table class="table"> + {% for profile in hostprofiles %} + <tr> + <td>{{profile.name}}</td> + <td>{{profile.description}}</td> + <td><a href="/resource/profiles/{{ profile.id }}" class="btn btn-info">Profile</a></td> + </tr> + {% endfor %} + </table> + </div> + </div> + + + <div class="card my-2"> + <div class="card-header d-flex"> + <h4 style="display: inline;">Networking Capabilities</h4> + <button data-toggle="collapse" data-target="#network_panel" class="btn btn-outline-secondary ml-auto" style="line-height: 1;" >Expand</button> + </div> + + <div class="card-body collapse show" 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="card my-2"> + <div class="card-header d-flex"> + <h4>Images</h4> + <button data-toggle="collapse" data-target="#image_panel" class="btn btn-outline-secondary ml-auto">Expand</button> + </div> + <div class="card-body collapse show" 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="card my-2"> + <div class="card-header d-flex"> + <h4>Lab Hosts</h4> + <button data-toggle="collapse" data-target="#lab_hosts_panel" class="btn btn-outline-secondary ml-auto">Expand</button> + </div> + + <div class="card-body collapse show" 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..9cde80c --- /dev/null +++ b/src/templates/dashboard/lab_list.html @@ -0,0 +1,28 @@ +{% extends "base.html" %} +{% block content %} +<h2>Labs</h2> +<div class="card_container"> + {% for lab in labs %} + <div class="card"> + <div class="card-header"> + <h3 class="mt-2">{{lab.name}}</h3> + </div> + <div class="p-4"> + <ul class="list-group"> + <li class="list-group-item">name: {{lab.name}}</li> + <li class="list-group-item">description: {{lab.description}}</li> + <li class="list-group-item">location: {{lab.location}}</li> + {% if lab.status == 0 %} + <li class="list-group-item">status: Up</li> + {% elif lab.status == 100 %} + <li class="list-group-item">status: Down for Maintenance</li> + {% elif lab.status == 200 %} + <li class="list-group-item">status: Down</li> + {% endif %} + </ul> + <a class="btn btn-primary mt-4 w-100" href="/lab/{{lab.name}}/">Details</a> + </div> + </div> + {% endfor %} +</div> +{% endblock %}
\ No newline at end of file diff --git a/src/templates/dashboard/landing.html b/src/templates/dashboard/landing.html new file mode 100644 index 0000000..e6a235f --- /dev/null +++ b/src/templates/dashboard/landing.html @@ -0,0 +1,172 @@ +{% extends "base.html" %} +{% load staticfiles %} + +{% block content %} +<div class="" style="text-align: center;"> + {% if not request.user.is_anonymous %} + {% if not request.user.userprofile.ssh_public_key %} + <div class="alert alert-danger" role="alert"> + Warning: you need to upload an ssh key under <a href="/accounts/settings">account settings</a> if you wish to + log into the servers you book + </div> + {% endif %} + {% else %} + {% endif %} +</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%; + } + + .landing_container { + display: grid; + grid-template-columns: 1fr 30px 1fr; + } + + .grid_panel { + padding: 30px; + } + + .btn-primary { + margin: 10px; + } + + h2 { + border-bottom: 1px solid #cccccc; + } + + h1 {} +</style> +<div class="container-fluid"> + <div class="row"> + <!-- About us --> + <div class="col-12 col-lg-6 mb-4"> + <h2>About Us:</h2> + <p>The Lab as a Service (LaaS) project aims to help in the development and testing of LFN projects such as + OPNFV + by hosting hardware and providing access to the community. Currently, the only participating lab is the + University of New Hampshire Interoperability Lab (UNH-IOL).</p> + <p>To get started, you can request access to a server at the right. PTL's have the ability to design and + book a + whole block of servers with customized layer2 networks (e.g. a Pharos Pod). Read more here: <a + href="https://wiki.opnfv.org/display/INF/Lab+as+a+Service+2.0">LaaS Wiki</a></p> + </div> + <!-- Get started --> + <div class="col-12 col-lg-6 mb-4"> + <h2>Get Started:</h2> + {% if request.user.is_anonymous %} + <h4 style="text-align:center;">To get started, please log in with your <a href="/accounts/login">Linux + Foundation Jira account</a></h4> + {% else %} + <p>To get started, book a server below:</p> + <a class="wf_create btn btn-primary" + style="display: flex; flex-direction: column; justify-content: center; margin: 20px; height: 100pt; vertical-align: middle; text-align: center; color: #FFF;" + href="/booking/quick/"> + <p style="font-size: xx-large">Book a Server</p> + </a> + <p>PTLs can use our advanced options to book multi-node pods. If you are a PTL, you may use the options + below: + </p> + <div class='container'> + <div class="row"> + <div class="col-12 col-xl-4"> + <button class="wf_create btn btn-primary w-100" onclick="cwf(0)">Book a Pod</button> + </div> + <div class="col-12 col-xl-4"> + <button class="wf_create btn btn-primary w-100" onclick="cwf(1)">Design a Pod</button> + </div> + <div class="col-12 col-xl-4"> + <button class="wf_create btn btn-primary w-100" onclick="cwf(2)">Configure a Pod</button> + </div> + </div> + {% endif %} + </div> + </div> + <!-- Returning users --> + {% if not request.user.is_anonymous %} + <div class="col-12 col-lg-6 offset-lg-6 mb-4 mt-lg-4"> + <h2 class="ht-4">Returning Users:</h2> + <p>If you're a returning user, some of the following options may be of interest:</p> + <div class="container"> + <div class="row"> + <div class="col-12 col-xl-4"> + <button class="wf_create btn btn-primary w-100" onclick="cwf(3)">Snapshot a Host</button> + </div> + <div class="col-12 col-xl-4"> + <a class="wf_create btn btn-primary w-100" href="{% url 'account:my-bookings' %}">My + Bookings</a> + </div> + {% if manager == True %} + <div class="col-12 col-xl-4"> + <button class="wf_continue btn btn-primary w-100" onclick="continue_wf()">Resume + Workflow</button> + </div> + {% endif %} + </div> + </div> + </div> + {% endif %} + </div> +</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/"); + } +</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 %}
\ No newline at end of file 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..4302543 --- /dev/null +++ b/src/templates/dashboard/multiple_select_filter_widget.html @@ -0,0 +1,149 @@ +<script src="/static/js/dashboard.js"> +</script> + +<style> +.object_class_wrapper { + display: grid; + grid-template-columns: 1fr 1fr 1fr; + border: 0px; +} + +.class_grid_wrapper { + border: 0px; + text-align: center; + border-right: 1px; + border-style: solid; + border-color: grey; +} + +.class_grid_wrapper:last-child { + border-right: none; +} + +.grid_wrapper { + display: grid; + grid-template-columns: 1fr 1fr; +} + +.grid-item { + cursor: pointer; + border: 1px solid #cccccc; + border-radius: 5px; + margin: 20px; + height: 200px; + padding: 7px; + transition: border-color ease-in-out .1s,box-shadow ease-in-out .1s; + box-shadow: 0 1px 1px rgba(0,0,0,.075); + + display: flex; + flex-direction: column; +} + +.grid-item > .btn:active, .grid-item > .btn:focus { + outline: none; !important; + box-shadow: none; +} + +.grid-item-description { + flex: 1; +} + +.selected_node { + border-color: #40c640; + box-shadow: 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(109, 243, 76, 0.6); + transition: border-color ease-in-out .1s,box-shadow ease-in-out .1s; +} + +.grid-item:hover:not(.selected_node):not(.disabled_node) { + box-shadow: 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(100, 100, 100, 0.3); + transition: border-color ease-in-out .1s,box-shadow ease-in-out .1s; +} + +.disabled_node { + cursor: not-allowed; + background-color: #EFEFEF; +} + +.disabled_node:hover {} + +.cleared_node { + background-color: #FFFFFF; +} + +.grid-item-header { + font-weight: bold; + font-size: 20px; + margin-top: 10px; +} + +.dropdown_item { + border: 1px; + border-style: solid; + border-color: lightgray; + border-radius: 5px; + margin: 20px; + padding: 2px; + grid-column: 1; + display: grid; + grid-template-columns: 1fr 3fr 1fr; + justify-items: center; +} + +.dropdown_item > button { + margin: 2px; + justify-self: end; +} + +.dropdown_item > h5 { + margin: auto; +} + +.dropdown_item > input { + padding: 7px; + margin: 2px; + width: 90%; +} + +#dropdown_wrapper { + display: grid; + grid-template-columns: 4fr 5fr; +} +</style> + +<input name="filter_field" id="filter_field" type="hidden"/> +<div id="grid_wrapper" class="grid_wrapper"> +{% for object_class, object_list in display_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="{{ obj.id|default:'not_provided' }}" class="grid-item" onclick="multi_filter_widget.processClick( + '{{obj.id}}');"> + <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"> + {% if obj.multiple %}Add{% else %}Select{% endif %} + </button> + </div> + {% endfor %} + </div> + </div> +{% endfor %} +</div> + +<div id="dropdown_wrapper"> +</div> +<script> +function multipleSelectFilterWidgetEntry() { + const graph_neighbors = {{ neighbors|safe }}; + const filter_items = {{ filter_items|safe }}; + const initial_value = {{ initial_value|default_if_none:"{}"|safe }}; + + //global variable + multi_filter_widget = new MultipleSelectFilterWidget(graph_neighbors, filter_items, initial_value); +} + +multipleSelectFilterWidgetEntry(); +</script> diff --git a/src/templates/dashboard/pdf.yaml b/src/templates/dashboard/pdf.yaml new file mode 100644 index 0000000..c893919 --- /dev/null +++ b/src/templates/dashboard/pdf.yaml @@ -0,0 +1,92 @@ +--- +version: {{version|default:"1.0"}} +details: + contact: {{details.contact}} + lab: {{details.lab}} + link: {{details.link}} + location: {{details.location}} + pod_owner: {{details.owner}} + type: {{details.type}} +jumphost: + disks: + {% for disk in jumphost.disks %} + - disk_capacity: {{disk.capacity}} + disk_interface: {{disk.interface}} + disk_rotation: {{disk.rotation}} + disk_type: {{disk.type}} + name: {{disk.name}} + {% endfor %} + interfaces: + {% for interface in jumphost.interfaces %} + - features: {{interface.features}} + mac_address: {{interface.mac_address}} + name: {{interface.name}} + speed: {{interface.speed}} + {% endfor %} + name: {{jumphost.name}} + node: + arch: {{jumphost.node.arch}} + cores: {{jumphost.node.cores}} + cpu_cflags: {{jumphost.node.cpu_cflags}} + cpus: {{jumphost.node.cpus}} + memory: {{jumphost.node.memory}} + model: {{jumphost.node.model}} + type: {{jumphost.node.type}} + vendor: {{jumphost.node.vendor}} + os: {{jumphost.os}} + remote_management: + address: {{jumphost.remote.address}} + mac_address: {{jumphost.remote.mac_address}} + pass: {{jumphost.remote.pass}} + type: {{jumphost.remote.type}} + user: {{jumphost.remote.user}} + versions: + {% for version in jumphost.remote.versions %} + - {{version}} + {% endfor %} + remote_params: + pass: {{jumphost.remote.pass}} + type: {{jumphost.remote.type}} + user: {{jumphost.remote.user}} + versions: + {% for version in jumphost.remote.versions %} + - {{version}} + {% endfor %} +nodes: +{% for node in nodes %} +- disks: + {% for disk in node.disks %} + - disk_capacity: {{disk.capacity}} + disk_interface: {{disk.interface}} + disk_rotation: {{disk.rotation}} + disk_type: {{disk.type}} + name: {{disk.name}} + {% endfor %} + interfaces: + {% for interface in node.interfaces %} + - features: {{interface.features}} + mac_address: {{interface.mac_address}} + name: {{interface.name}} + speed: {{interface.speed}} + {% endfor %} + name: {{node.name}} + node: + arch: {{node.node.arch}} + cores: {{node.node.cores}} + cpu_cflags: {{node.node.cpu_cflags}} + cpus: {{node.node.cpus}} + memory: {{node.node.memory}} + model: {{node.node.model}} + type: {{node.node.type}} + vendor: {{node.node.vendor}} + remote_management: + address: {{node.remote.address}} + mac_address: {{node.remote.mac_address}} + pass: {{node.remote.pass}} + type: {{node.remote.type}} + user: {{node.remote.user}} + versions: + {% for version in node.remote.versions %} + - {{version}} + {% endfor %} +{% endfor %} diff --git a/src/templates/dashboard/resource.html b/src/templates/dashboard/resource.html new file mode 100644 index 0000000..f36ee7b --- /dev/null +++ b/src/templates/dashboard/resource.html @@ -0,0 +1,57 @@ +{% extends "base.html" %} +{% load staticfiles %} + +{% block extrahead %} + {{ block.super }} + <!-- Morris Charts CSS --> + <link href="{% static "bower_components/morrisjs/morris.css" %}" rel="stylesheet"> + + <!-- DataTables CSS --> + <link href="{% static "bower_components/datatables.net-bs4/css/dataTables.bootstrap4.min.css" %}" + rel="stylesheet"> + + <!-- DataTables Responsive CSS --> + <link href="{% static "bower_components/datatables.net-responsive-bs4/css/responsive.bootstrap4.min.css" %}" + rel="stylesheet"> +{% endblock extrahead %} + + +{% block content %} + {% include "dashboard/resource_detail.html" %} +{% endblock content %} + + +{% block extrajs %} + <!-- DataTables JavaScript --> + <link href="{% static "bower_components/datatables/media/css/dataTables.bootstrap.css" %}" + rel="stylesheet"> + + <script src={% static "bower_components/datatables.net/js/jquery.dataTables.min.js" %}></script> + <script src={% static "bower_components/datatables.net-bs4/js/dataTables.bootstrap4.min.js" %}></script> + + + + <!-- Flot Charts JavaScript --> + <script src="{% static "bower_components/flot/excanvas.min.js" %}"></script> + <script src="{% static "bower_components/flot/jquery.flot.js" %}"></script> + <script src="{% static "bower_components/flot/jquery.flot.pie.js" %}"></script> + <script src="{% static "bower_components/flot/jquery.flot.resize.js" %}"></script> + <script src="{% static "bower_components/flot/jquery.flot.time.js" %}"></script> + <script src="{% static "bower_components/flot.tooltip/js/jquery.flot.tooltip.min.js" %}"></script> + + <script src="{% static "js/flot-pie-chart.js" %}"></script> + + <script type="text/javascript"> + $(document).ready(function () { + $('#{{ resource.id }}_server_table').DataTable({}); + $('#{{ resource.id }}_bookings_table').DataTable({}); + $('#{{ resource.id }}_vpn_user_table').DataTable({}); + + 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); + + loadChartData(chart_id, utilization_url); + }); + </script> +{% endblock extrajs %}
\ No newline at end of file diff --git a/src/templates/dashboard/resource_all.html b/src/templates/dashboard/resource_all.html new file mode 100644 index 0000000..fb8cc7e --- /dev/null +++ b/src/templates/dashboard/resource_all.html @@ -0,0 +1,70 @@ +{% extends "base.html" %} +{% load staticfiles %} + +{% block extrahead %} + {{ block.super }} + <!-- Morris Charts CSS --> + <link href="{% static "bower_components/morrisjs/morris.css" %}" rel="stylesheet"> + + <!-- DataTables CSS --> + <link href="{% static "bower_components/datatables.net-bs4/css/dataTables.bootstrap4.min.css" %}" + rel="stylesheet"> + + <!-- DataTables Responsive CSS --> + <link href="{% static "bower_components/datatables.net-responsive-bs4/css/responsive.bootstrap4.min.css" %}" + rel="stylesheet"> +{% endblock extrahead %} + + +{% block content %} + {% for resource, utilization, bookings in pods %} + <div class="row"> + <div class="col-lg-12"> + <div class="panel panel-default"> + <div class="panel-heading"> + {{ resource.name }} + </div> + <div class="panel-body"> + {% include "dashboard/resource_detail.html" %} + </div> + </div> + </div> + </div> + {% endfor %} +{% endblock content %} + + +{% block extrajs %} + <!-- DataTables JavaScript --> + <link href="{% static "bower_components/datatables/media/css/dataTables.bootstrap.css" %}" + rel="stylesheet"> + + <script src={% static "bower_components/datatables.net/js/jquery.dataTables.min.js" %}></script> + <script src={% static "bower_components/datatables.net-bs4/js/dataTables.bootstrap4.min.js" %}></script> + + + + <!-- Flot Charts JavaScript --> + <script src="{% static "bower_components/flot/excanvas.min.js" %}"></script> + <script src="{% static "bower_components/flot/jquery.flot.js" %}"></script> + <script src="{% static "bower_components/flot/jquery.flot.pie.js" %}"></script> + <script src="{% static "bower_components/flot/jquery.flot.resize.js" %}"></script> + <script src="{% static "bower_components/flot/jquery.flot.time.js" %}"></script> + <script src="{% static "bower_components/flot.tooltip/js/jquery.flot.tooltip.min.js" %}"></script> + <script src="{% static "js/flot-pie-chart.js" %}"></script>< + + <script type="text/javascript"> + $(document).ready(function () { + {% for resource, utilization, bookings in pods %} + + $('#{{ resource.id }}_server_table').DataTable({}); + $('#{{ resource.id }}_bookings_table').DataTable({}); + $('#{{ resource.id }}_vpn_user_table').DataTable({}); + + 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); + {% endfor %} + }); + </script> +{% endblock extrajs %}
\ No newline at end of file diff --git a/src/templates/dashboard/resource_detail.html b/src/templates/dashboard/resource_detail.html new file mode 100644 index 0000000..0a443d9 --- /dev/null +++ b/src/templates/dashboard/resource_detail.html @@ -0,0 +1,151 @@ +<div class="row"> + <div class="col-lg-9"> + <div class="panel panel-default"> + <div class="panel-heading"> + Status + </div> + <div class="panel-body"> + <div class="list-group pre-scrollable"> + {% for status in resource.resourcestatus_set.all %} + <a href="#" class="list-group-item"> + <i class="fa fa-info fa-fw"></i> {{ status.title }} + <span class="pull-right text-muted small"> + <em>{{ status.timestamp }}</em> + </span> + </a> + {% endfor %} + </div> + </div> + </div> + </div> + <div class="col-lg-9"> + <div class="panel panel-default"> + <div class="panel-heading"> + Servers + </div> + <div class="panel-body"> + <div class="dataTables_wrapper"> + <table class="table table-striped table-bordered table-hover" + id="{{ resource.id }}_server_table" cellspacing="0" + width="100%"> + {% include "dashboard/server_table.html" %} + </table> + </div> + </div> + </div> + </div> +</div> +<div class="row"> + <div class="col-lg-3"> + <div class="panel panel-default"> + <div class="panel-heading"> + Booking Utilization + <div class="pull-right"> + <div class="form-group"> + <select onchange="loadChartData('{{ resource.id }}_booking_utilization', this.value);"> + <option value="{% url 'dashboard:booking_utilization' resource_id=resource.id weeks=-4 %}"> + Last Month + </option> + <option value="{% url 'dashboard:booking_utilization' resource_id=resource.id weeks=-1 %}"> + Last Week + </option> + <option value="{% url 'dashboard:booking_utilization' resource_id=resource.id weeks=1 %}"> + Next Week + </option> + <option selected="selected" + value="{% url 'dashboard:booking_utilization' resource_id=resource.id weeks=4 %}"> + Next Month + </option> + </select> + </div> + </div> + </div> + <div class="panel-body"> + <div class="flot-chart"> + <div class="flot-chart-content" + id="{{ resource.id }}_booking_utilization"></div> + </div> + </div> + </div> + </div> + <div class="col-lg-9"> + <div class="panel panel-default"> + <div class="panel-heading"> + Bookings + </div> + <div class="panel-body"> + <div class="dataTables_wrapper"> + <table class="table table-striped table-bordered table-hover" + id="{{ resource.id }}_bookings_table" cellspacing="0" + width="100%"> + {% include "booking/booking_table.html" %} + </table> + </div> + </div> + </div> + </div> +</div> +<div class="row"> + <div class="col-lg-3"> + <div class="panel panel-default"> + <div class="panel-heading"> + Contact + </div> + <div class="panel-body"> + <p> + <b>Lab Owner: </b> + {{ resource.owner.username }} + </p> + <p> + <b>Email: </b> + {{ resource.owner.email }} + </p> + <p> + <a href="{% url 'booking:create' resource_id=resource.id %}" class="btn btn-primary"> + Booking + </a> + <a href="{{ resource.url }}" class="btn btn-primary"> + OPNFV Wiki + </a> + </p> + </div> + </div> + </div> + <div class="col-lg-6"> + <div class="panel panel-default"> + <div class="panel-heading"> + VPN Users + </div> + <div class="panel-body"> + <div class="dataTables_wrapper"> + <table class="table table-striped table-bordered table-hover" + id="{{ resource.id }}_vpn_user_table" cellspacing="0" + width="100%"> + <thead> + <tr> + <th>User</th> + <th>Email</th> + <th>Company</th> + </tr> + </thead> + <tbody> + {% for user in resource.vpn_users.all %} + <tr> + <td> + {{ user.username }} + </td> + <td> + {{ user.email }} + </td> + <td> + {{ user.userprofile.company }} + </td> + </tr> + {% endfor %} + </table> + </tbody> + </div> + </div> + </div> + </div> +</div> diff --git a/src/templates/dashboard/searchable_select_multiple.html b/src/templates/dashboard/searchable_select_multiple.html new file mode 100644 index 0000000..8bcf890 --- /dev/null +++ b/src/templates/dashboard/searchable_select_multiple.html @@ -0,0 +1,209 @@ +<script src="https://code.jquery.com/jquery-2.2.4.min.js"></script> +<script src="/static/js/dashboard.js"></script> + +<div id="search_select_outer" class="autocomplete"> + <div id="warning_pane" style="background: #FFFFFF; color: #CC0000;"> + {% if incompatible == "true" %} + <h3>Warning: Incompatible Configuration</h3> + <p>Please make a different selection, as the current config conflicts with the selected pod</p> + {% endif %} + </div> + <div id="added_counter"> + <p id="added_number">0</p> + <p id="addable_limit">/ {% if selectable_limit > -1 %} {{ selectable_limit }} {% else %} ∞ {% endif %}added</p> + </div> + + <div id="added_list"> + + </div> + + <input id="user_field" name="ignore_this" class="form-control" autocomplete="off" type="text" placeholder="{{placeholder}}" value="" oninput="searchable_select_multiple_widget.search(this.value)" + {% if disabled %} disabled {% endif %} + > + </input> + + <input type="hidden" id="selector" name="{{ name }}" class="form-control" style="display: none;" + {% if disabled %} disabled {% endif %} + > + </input> + + <div id="scroll_restrictor"> + <ul id="drop_results"></ul> + </div> + <style> + #scroll_restrictor { + flex: 1; + position: relative; + overflow-y: auto; + padding-bottom: 10px; + } + + #added_list { + margin-bottom: 5px; + } + + .autocomplete { + display: flex; + flex: 1; + flex-direction: column; + } + #user_field { + font-size: 14pt; + padding: 5px; + height: 40px; + border: 1px solid #ccc; + border-radius: 5px; + + } + + #drop_results{ + list-style-type: none; + padding: 0; + margin: 0; + min-height: 0; + border: solid 1px #ddd; + border-top: none; + border-bottom: none; + visibility: inherit; + flex: 1; + + position: absolute; + width: 100%; + + } + + #drop_results li a{ + font-size: 14pt; + background-color: #f6f6f6; + padding: 7px; + text-decoration: none; + display: block; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + #drop_results li a { + border-bottom: 1px solid #ddd; + } + + .list_entry { + border: 1px solid #ccc; + border-radius: 5px; + margin-top: 5px; + vertical-align: middle; + line-height: 40px; + height: 40px; + padding-left: 12px; + width: 100%; + display: flex; + } + + #drop_results li a:hover{ + background-color: #ffffff; + } + + .added_entry_text { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + display: inline; + width: 100%; + } + + .btn-remove { + float: right; + height: 30px; + margin: 4px; + padding: 1px; + max-width: 20%; + width: 15%; + min-width: 70px; + overflow: hidden; + text-overflow: ellipsis; + } + + .entry_tooltip { + display: none; + } + + #drop_results li a:hover .entry_tooltip { + position: absolute; + background: #444; + color: #ddd; + text-align: center; + font-size: 12pt; + border-radius: 3px; + + } + + #drop_results { + max-width: 100%; + display: inline-block; + list-style-type: none; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + } + + #drop_results li { + overflow: hidden; + text-overflow: ellipsis; + } + + #added_counter { + text-align: center; + } + + #added_number, #addable_limit { + display: inline; + } + </style> +</div> + +<script type="text/javascript"> + function searchableSelectMultipleWidgetEntry() { + let format_vars = { + "show_from_noentry": {{show_from_noentry|yesno:"true,false"}}, + "show_x_results": {{show_x_results|default:-1}}, + "results_scrollable": {{results_scrollable|yesno:"true,false"}}, + "selectable_limit": {{selectable_limit|default:-1}}, + "placeholder": "{{placeholder|default:"begin typing"}}" + }; + + let field_dataset = {{items|safe}}; + + let field_initial = {{ initial|safe }}; + + //global + searchable_select_multiple_widget = new SearchableSelectMultipleWidget(format_vars, field_dataset, field_initial); + } + + searchableSelectMultipleWidgetEntry(); + + /* + var show_from_noentry = context(show_from_noentry|yesno:"true,false") // whether to show any results before user starts typing + var show_x_results = context(show_x_results|default:-1) // how many results to show at a time, -1 shows all results + var results_scrollable = {{results_scrollable|yesno:"true,false") // whether list should be scrollable + var selectable_limit = {{selectable_limit|default:-1) // how many selections can be made, -1 allows infinitely many + var placeholder = "context(placeholder|default:"begin typing")" // placeholder that goes in text box + + needed info + var items = context(items|safe) // items to add to trie. Type is a dictionary of dictionaries with structure: + { + id# : { + "id": any, identifiable on backend + "small_name": string, displayed first (before separator), searchable (use for e.g. username) + "expanded_name": string, displayed second (after separator), searchable (use for e.g. email address) + "string": string, not displayed, still searchable + } + } + + used later: + context(selectable_limit): changes what number displays for field + context(name): form identifiable name, relevant for backend + // when submitted, form will contain field data in post with name as the key + context(placeholder): "greyed out" contents put into search field initially to guide user as to what they're searching for + context(initial): in search_field_init(), marked safe, an array of id's each referring to an id from items + */ +</script> diff --git a/src/templates/dashboard/server_table.html b/src/templates/dashboard/server_table.html new file mode 100644 index 0000000..f01bd60 --- /dev/null +++ b/src/templates/dashboard/server_table.html @@ -0,0 +1,30 @@ +<thead> +<tr> + <th>Server</th> + <th>Model</th> + <th>CPU</th> + <th>RAM</th> + <th>Storage</th> +</tr> +</thead> +<tbody> +{% for server in resource.server_set.all %} + <tr> + <td> + {{ server.name }} + </td> + <td> + {{ server.model }} + </td> + <td> + {{ server.cpu }} + </td> + <td> + {{ server.ram }} + </td> + <td> + {{ server.storage }} + </td> + </tr> +{% endfor %} +</tbody>
\ No newline at end of file diff --git a/src/templates/dashboard/table.html b/src/templates/dashboard/table.html new file mode 100644 index 0000000..0a37ded --- /dev/null +++ b/src/templates/dashboard/table.html @@ -0,0 +1,45 @@ +{% extends "base.html" %} +{% load staticfiles %} + +{% block extrahead %} + {{ block.super }} + <!-- DataTables CSS --> + <link href="{% static "bower_components/datatables.net-bs4/css/dataTables.bootstrap4.min.css" %}" + rel="stylesheet"> + + <!-- DataTables Responsive CSS --> + <link href="{% static "bower_components/datatables.net-responsive-bs4/css/responsive.bootstrap4.min.css" %}" + rel="stylesheet"> +{% endblock extrahead %} + +{% block content %} + <div class="row"> + <div class="col-lg-12"> + <div class="dataTables_wrapper"> + <table class="table table-striped table-bordered table-hover" id="table" cellspacing="0" + width="100%"> + + {% block table %} + {% endblock table %} + + </table> + </div> + <!-- /.table-responsive --> + <!-- /.panel-body --> + <!-- /.panel --> + </div> + <!-- /.col-lg-12 --> + </div> +{% endblock content %} + +{% block extrajs %} + <!-- DataTables JavaScript --> + + <script src={% static "bower_components/datatables.net/js/jquery.dataTables.min.js" %}></script> + <script src={% static "bower_components/datatables.net-bs4/js/dataTables.bootstrap4.min.js" %}></script> + + <script src={% static "js/dataTables-sort.js" %}></script> + + {% block tablejs %} + {% endblock tablejs %} +{% endblock extrajs %} |