mirror of
https://github.com/retspen/webvirtcloud
synced 2026-11-09 11:46:49 +00:00
Three upstream rules in webvirtcloud.css set every page's layout
against Bootstrap 5.3:
- .container { max-width: 900px } held each page to 900px at any
screen width, below Bootstrap's own widths. Pages now take
Bootstrap's container widths up to its lg one, 960px, and stop
there on wider screens: its xl and xxl widths (1140px, 1320px) leave
the panel's forms and lists too wide;
- .page-header (margins of 5px 5px 20px 20px) put headings 20px right
of the content under them; its 14 uses are mb-3, flush with the
content (one carried Bootstrap 4's mr-auto, now me-auto);
- p { margin: 10px } indented every paragraph 10px from the content
beside it; paragraphs take Bootstrap's own bottom margin.
webvirtcloud.css keeps only what Bootstrap has no utility for: the
page width cap, the XML editor's place and height, the Tom Select
colour fix, the sortable table headers' arrows, the grouped VM list's
collapsed hosts and the charts' height.
439 lines
No EOL
22 KiB
HTML
439 lines
No EOL
22 KiB
HTML
{% extends "base.html" %}
|
|
{% load i18n %}
|
|
{% load static %}
|
|
{% load bootstrap_icons %}
|
|
|
|
{% block title %}{% trans "Network" %} - {{ pool }}{% endblock %}
|
|
|
|
{% block page_heading %}{% trans "Network" %}: {{ pool }}{% endblock page_heading %}
|
|
|
|
{% block content %}
|
|
<div class="row">
|
|
<div class="col-lg-12">
|
|
{% include "computes/host_nav.html" with section="networks" detail=True %}
|
|
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<dl class="ms-3 row">
|
|
<dt class="col-6">{% trans "Network name" %}</dt>
|
|
<dd class="col-6">{{ pool }}</dd>
|
|
<dt class="col-6">{% trans "Device" %}</dt>
|
|
<dd class="col-6">{{ device }}</dd>
|
|
<dt class="col-6">{% trans "State" %}</dt>
|
|
<dd class="col-6">
|
|
<form action="" method="post" role="form" aria-label="Network start/stop/delete form">{% csrf_token %}
|
|
{% if state == 0 %}
|
|
<input type="submit" class="btn btn-sm btn-success" name="start" value="{% trans "Start" %}">
|
|
<input type="submit" class="btn btn-sm btn-danger" name="delete" value="{% trans "Delete" %}" data-confirm="{% blocktrans with name=pool %}Delete network {{ name }}? VMs that use it no longer start.{% endblocktrans %}" onclick="return confirm(this.dataset.confirm)">
|
|
{% else %}
|
|
<input type="submit" class="btn btn-sm btn-secondary" name="stop" value="{% trans "Stop" %}" data-confirm="{% blocktrans with name=pool %}Stop network {{ name }}? Running VMs on it lose their connection through it at once.{% endblocktrans %}" onclick="return confirm(this.dataset.confirm)">
|
|
{% endif %}
|
|
</form>
|
|
</dd>
|
|
<dt class="col-6">{% trans "Autostart" %}</dt>
|
|
<dd class="col-6">
|
|
<form action="" method="post" role="form" aria-label="Network enable/disable autostart form">{% csrf_token %}
|
|
{% if autostart == 0 %}
|
|
<input type="submit" class="btn btn-sm btn-success" name="set_autostart" value="{% trans "Enable" %}">
|
|
{% else %}
|
|
<input type="submit" class="btn btn-sm btn-secondary" name="unset_autostart" data-confirm="{% blocktrans with name=pool %}Disable autostart of network {{ name }}? After the host restarts it stays stopped, and VMs that use it do not start.{% endblocktrans %}" onclick="return confirm(this.dataset.confirm)" value="{% trans "Disable" %}">
|
|
{% endif %}
|
|
</form>
|
|
</dd>
|
|
</dl>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="d-grid col-lg-12">
|
|
<button class="btn btn-light" role="button" data-bs-toggle="collapse" data-bs-target="#collapseXML" aria-expanded="true" aria-controls="collapseXML">
|
|
<span>{% bs_icon 'chevron-down' %} </span>
|
|
{% trans 'XML' %}
|
|
</button>
|
|
<div class="collapse" id="collapseXML">
|
|
<div class="card card-body">
|
|
<form method="post" role="form" aria-label="Network edit with XML form">{% csrf_token %}
|
|
<div class="xmlheight">
|
|
<input type="hidden" name="edit_xml"/>
|
|
<textarea id="edit_editor">{{ xml }}</textarea>
|
|
</div>
|
|
<button type="submit" class="btn btn-primary float-end" name="edit_network">
|
|
{% trans "Edit" %}
|
|
</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<h5 class="mb-3">{% trans "IPv4 Configuration" %}</h5>
|
|
|
|
<dl class="ms-3 row">
|
|
<dt class="col-6">{% trans "IPv4 Forwarding" %}</dt>
|
|
<dd class="col-6">
|
|
{% if net_forward.0 == 'nat' %}
|
|
{% trans "NAT" %}
|
|
{% endif %}
|
|
{% if net_forward.0 == 'route' %}
|
|
{% trans "ROUTE" %}
|
|
{% endif %}
|
|
{% if net_forward.0 == 'bridge' %}
|
|
{% trans "BRIDGE" %}
|
|
{% endif %}
|
|
{% if not net_forward.0 %}
|
|
{% trans "ISOLATE" %}
|
|
{% endif %}
|
|
</dd>
|
|
<dt class="col-6">{% trans "Network" %}</dt>
|
|
<dd class="col-6">{{ ipv4_network|default:"-" }}</dd>
|
|
<dt class="col-6">{% trans "DHCP" %}:</dt>
|
|
<dd class="col-6">
|
|
{% if ipv4_dhcp_range_start and ipv4_dhcp_range_end %}
|
|
<span class="badge bg-success-subtle text-success-emphasis">{% trans "ON" %}</span>
|
|
{% else %}
|
|
<span class="badge bg-danger-subtle text-danger-emphasis">{% trans "OFF" %}</span>
|
|
{% endif %}
|
|
</dd>
|
|
{% if ipv4_dhcp_range_start and ipv4_dhcp_range_end %}
|
|
{% if state %}
|
|
<dt class="col-6">{% trans "Start" %}</dt>
|
|
<dd class="col-6">{{ ipv4_dhcp_range_start }}</dd>
|
|
<dt class="col-6">{% trans "End" %}</dt>
|
|
<dd class="col-6">{{ ipv4_dhcp_range_end }}</dd>
|
|
{% else %}
|
|
<form class="col-lg-12 ps-0" method="post" role="form" aria-label="Edit dhcp range form">{% csrf_token %}
|
|
<dt class="col-6">{% trans "Start" %}</dt>
|
|
<dd class="col-6"><input name="range_start" value="{{ ipv4_dhcp_range_start }}"/></dd>
|
|
<dt class="col-6">{% trans "End" %}</dt>
|
|
<dd class="col-6"><input name="range_end" value="{{ ipv4_dhcp_range_end }}"/></dd>
|
|
<dt class="col-6"></dt>
|
|
<dd class="col-6 ">
|
|
<input type="submit"
|
|
class="btn btn-primary"
|
|
value="Apply"
|
|
name="modify_dhcp_range"
|
|
title="{% trans "Edit DHCP Range" %}"
|
|
/>
|
|
</dd>
|
|
<input hidden name="family" value="ipv4"/>
|
|
</form>
|
|
{% endif %}
|
|
{% endif %}
|
|
</dl>
|
|
|
|
{% if ipv4_dhcp_range_start and ipv4_dhcp_range_end %}
|
|
{% if state %}
|
|
{% include 'modify_ipv4_fixed_address.html' %}
|
|
{% endif %}
|
|
|
|
<h5 class="mb-3">{% trans "IPv4 Fixed Addresses" %}</h5>
|
|
|
|
{% endif %}
|
|
|
|
{% if ipv4_fixed_address %}
|
|
|
|
<div class="d-grid col-12">
|
|
<button class="btn btn-light" role="button" data-bs-toggle="collapse" data-bs-target="#collapseipv4fix" aria-expanded="true" aria-controls="collapseXML">
|
|
<span>{% bs_icon 'chevron-down' %} </span>
|
|
{% trans 'Show' %}
|
|
</button>
|
|
<div id="collapseipv4fix" class="collapse">
|
|
<div class="col-4 float-end mt-2">
|
|
<div class="input-group ">
|
|
<input type="text" class="form-control" id="filter_input">
|
|
<input type="button" class="btn btn-secondary" id="filter_button" value="Filter">
|
|
<button type="button" class="btn btn-secondary" id="filter_clear">{% trans 'Clear' %}</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="table-responsive">
|
|
<table id="ipv4_table" class="table table-hover">
|
|
<thead>
|
|
<tr>
|
|
<th scope="col">{% trans "MAC" %}</th>
|
|
<th scope="col">{% trans "Address" %}</th>
|
|
<th scope="col">{% trans "Name" %}</th>
|
|
<th scope="col">{% trans "Actions" %}</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for fix4 in ipv4_fixed_address %}
|
|
<tr>
|
|
<form method="post" role="form" aria-label="IPv4 edit/delete form">{% csrf_token %}
|
|
<td><input class="form-control" value="{{ fix4.mac|default_if_none:"" }}" name="mac" readonly/></td>
|
|
<td><input class="form-control" value="{{ fix4.ip }}" name="address" /></td>
|
|
<td><input class="form-control" value="{{ fix4.name }}" name="name" /></td>
|
|
<td class="text-nowrap">
|
|
<input hidden name="family" value="ipv4"/>
|
|
{% if fix4.mac %}{# an entry is changed by its MAC #}
|
|
<button type="submit" class="btn btn-sm btn-primary"
|
|
name="modify_fixed_address"
|
|
title="{% trans "Edit entry" %}">
|
|
{% bs_icon 'save' %}
|
|
</button>
|
|
{% endif %}
|
|
<button type="submit" class="btn btn-sm btn-danger"
|
|
name="delete_fixed_address"
|
|
title="{% trans "Delete entry" %}" data-confirm="{% blocktrans with mac=fix4.mac|default:fix4.ip %}Delete the fixed address of {{ mac }}? The VM may get another address when its lease ends.{% endblocktrans %}" onclick="return confirm(this.dataset.confirm)">
|
|
{% bs_icon 'trash' %}
|
|
</button>
|
|
</td>
|
|
</form>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<h5 class="mb-3">{% trans "IPv6 Configuration" %}</h5>
|
|
|
|
<dl class="ms-3 row">
|
|
<dt class="col-6">{% trans "IPv6 Forwarding" %}</dt>
|
|
<dd class="col-6">
|
|
{% if not net_forward.0 %}
|
|
{% trans "ISOLATE" %}
|
|
{% else %}
|
|
{% trans "ROUTE" %}
|
|
{% endif %}
|
|
</dd>
|
|
<dt class="col-6">{% trans "Network" %}</dt>
|
|
<dd class="col-6">{{ ipv6_network|default:"-" }}</dd>
|
|
<dt class="col-6">{% trans "DHCP" %}</dt>
|
|
|
|
<dd class="col-6">
|
|
{% if ipv6_dhcp_range_start and ipv6_dhcp_range_end %}
|
|
<span class="badge bg-success-subtle text-success-emphasis">{% trans "ON" %}</span>
|
|
{% else %}
|
|
<span class="badge bg-danger-subtle text-danger-emphasis">{% trans "OFF" %}</span>
|
|
{% endif %}
|
|
</dd>
|
|
{% if ipv6_dhcp_range_start and ipv6_dhcp_range_end %}
|
|
{% if state %}
|
|
<dt class="col-6">{% trans "Start" %}</dt>
|
|
<dd class="col-6">{{ ipv6_dhcp_range_start }}</dd>
|
|
<dt class="col-6">{% trans "End" %}</dt>
|
|
<dd class="col-6">{{ ipv6_dhcp_range_end }}</dd>
|
|
{% else %}
|
|
<form class="col-lg-12 ps-0" method="post" role="form" aria-label="Edit dhcp range form">{% csrf_token %}
|
|
<dt class="col-6">{% trans "Start" %}</dt>
|
|
<dd class="col-6"><input name="range_start" value="{{ ipv6_dhcp_range_start }}"/></dd>
|
|
<dt class="col-6">{% trans "End" %}</dt>
|
|
<dd class="col-6"><input name="range_end" value="{{ ipv6_dhcp_range_end }}"/></dd>
|
|
<dt class="col-6"></dt>
|
|
<dd class="d-grid col-6">
|
|
<input
|
|
type="submit"
|
|
class="btn btn-primary"
|
|
value="Apply"
|
|
name="modify_dhcp_range"
|
|
title="{% trans "Edit DHCP Range" %}"
|
|
/>
|
|
</dd>
|
|
<input hidden name="family" value="ipv6"/>
|
|
</form>
|
|
{% endif %}
|
|
{% endif %}
|
|
</dl>
|
|
|
|
{% if ipv6_dhcp_range_start and ipv6_dhcp_range_end %}
|
|
{% if state %}
|
|
{% include 'modify_ipv6_fixed_address.html' %}
|
|
{% endif %}
|
|
<h5 class="mb-3">{% trans "IPv6 Fixed Addresses" %}</h5>
|
|
{% endif %}
|
|
|
|
{% if ipv6_fixed_address %}
|
|
<div class="d-grid col-12">
|
|
<button class="btn btn-light" role="button" data-bs-toggle="collapse" data-bs-target="#collapseipv6fix" aria-expanded="true" aria-controls="collapseipv6fix">
|
|
<span>{% bs_icon 'chevron-down' %} </span>
|
|
{% trans 'Show' %}
|
|
</button>
|
|
<div id="collapseipv6fix" class="collapse">
|
|
<div class="col-4 float-end mt-2">
|
|
<div class="input-group">
|
|
<input type="text" class="form-control" id="filter_input_ipv6">
|
|
<input type="button" class="btn btn-secondary" id="filter_button_ipv6" value="Filter">
|
|
<button type="button" class="btn btn-secondary" id="filter_clear_ipv6">{% trans 'Clear' %}</button>
|
|
</div>
|
|
</div>
|
|
<div class="table-responsive">
|
|
<table id="ipv6_table" class="table table-hover">
|
|
<thead>
|
|
<tr>
|
|
<th scope="col">{% trans "ID" %}</th>
|
|
<th scope="col">{% trans "Address" %}</th>
|
|
<th scope="col">{% trans "Name" %}</th>
|
|
<th scope="col">{% trans "Actions" %}</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for fix6 in ipv6_fixed_address %}
|
|
<tr>
|
|
<form method="post" role="form" aria-label="IP v6 edit/delete form">{% csrf_token %}
|
|
<td><input class="form-control" value="{{ fix6.id|default_if_none:"" }}" name="id" readonly/></td>
|
|
<td><input class="form-control" value="{{ fix6.ip }}" name="address" /></td>
|
|
<td><input class="form-control" value="{{ fix6.name }}" name="name" /></td>
|
|
<td class="text-nowrap">
|
|
<input hidden name="family" value="ipv6"/>
|
|
{% if fix6.id %}{# an entry is changed by its DUID #}
|
|
<button type="submit" class="btn btn-sm btn-primary"
|
|
name="modify_fixed_address"
|
|
title="{% trans "Edit entry" %}">
|
|
<span>{% bs_icon 'save' %} </span>
|
|
</button>
|
|
{% endif %}
|
|
<button type="submit" class="btn btn-sm btn-danger"
|
|
name="delete_fixed_address"
|
|
title="{% trans "Delete entry" %}" data-confirm="{% blocktrans with id=fix6.id|default:fix6.ip %}Delete the fixed address of {{ id }}? The VM may get another address when its lease ends.{% endblocktrans %}" onclick="return confirm(this.dataset.confirm)">
|
|
<span>{% bs_icon 'trash' %} </span>
|
|
</button>
|
|
</td>
|
|
</form>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if net_forward.0 == 'route' or net_forward.0 == 'nat' or not net_forward.0 %}{# an isolated network has no forward #}
|
|
{% if state == 0 and qos.items|length != 2%}
|
|
<form method="post" name="set_qos" role="form" aria-label="Create network qos form">{% csrf_token %}
|
|
{% include 'add_network_qos.html' %}
|
|
</form>
|
|
{% endif %}
|
|
<h5 class="mb-3">{% trans "QoS Configuration" %}</h5>
|
|
|
|
<div class="col-sm-12">
|
|
<div class="table-responsive">
|
|
<table class="table table-hover align-middle">
|
|
<thead>
|
|
<tr>
|
|
<th scope="col">{% trans "Direction" %}</th>
|
|
<th scope="col">{% trans "Average" %}</th>
|
|
<th scope="col">{% trans "Peak" %}</th>
|
|
<th scope="col">{% trans "Burst" %}</th>
|
|
<th scope="col">{% trans "Actions" %}</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for q, att in qos.items %}
|
|
<form method="post" role="form" aria-label="QoS configuration form">{% csrf_token %}
|
|
<tr>
|
|
<td><label class="col-form-label">{{ q | capfirst }}</label></td>
|
|
<td><input id="qos_average" class="form-control" name="qos_average"
|
|
value="{{ att.average|default:'' }}"/>
|
|
</td>
|
|
<td><input id="qos_peak" class="form-control" name="qos_peak"
|
|
value="{{ att.peak|default:'' }}"/>
|
|
</td>
|
|
<td><input id="qos_burst" class="form-control" name="qos_burst"
|
|
value="{{ att.burst|default:'' }}"/>
|
|
</td>
|
|
<td class="text-nowrap">
|
|
<input name="qos_direction" value="{{ q }}" hidden/>
|
|
<button type="submit" class="btn btn-sm btn-primary"
|
|
name="set_qos"
|
|
title="{% trans "Edit QoS" %}">
|
|
{% bs_icon 'save' %}
|
|
</button>
|
|
<button type="submit" class="btn btn-sm btn-danger"
|
|
name="unset_qos"
|
|
title="{% trans "Delete QoS" %}" data-confirm="{% blocktrans with direction=q name=pool %}Remove the {{ direction }} limit of network {{ name }}? It stays in force until the network is stopped and started.{% endblocktrans %}" onclick="return confirm(this.dataset.confirm)">
|
|
{% bs_icon 'trash' %}
|
|
</button>
|
|
</td>
|
|
</tr>
|
|
</form>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
{% endblock %}
|
|
{% block script %}
|
|
<script>
|
|
$.expr[':'].Contains = $.expr.createPseudo(function (arg) {
|
|
return function (elem) {
|
|
return $(elem).text().toUpperCase().indexOf(arg.toUpperCase()) >= 0;
|
|
};
|
|
});
|
|
|
|
$(document).ready(function () {
|
|
// add event button labeled "filter"
|
|
$('#filter_button').click(function (event) {
|
|
// get value
|
|
let filter_val = $('#filter_input').val();
|
|
if (filter_val == '') {
|
|
// show all
|
|
$('#ipv4_table tbody tr').show();
|
|
} else {
|
|
// hide non-matching items
|
|
let row_not_contains4 = $('#ipv4_table tbody tr input:not([value*=\'' + filter_val + '\'])');
|
|
row_not_contains4.closest('tr').hide();
|
|
// show only matches
|
|
let row_contains4 = $('#ipv4_table tbody tr input[value*=\'' + filter_val + '\']');
|
|
row_contains4.closest('tr').show();
|
|
}
|
|
});
|
|
// add event button labeled "clear"
|
|
$('#filter_clear').click(function (event) {
|
|
$('#filter_input').val('');
|
|
$('#filter_button').click();
|
|
});
|
|
|
|
// trigger filter when enter key pressed
|
|
$('#filter_input').keyup(function (event) {
|
|
if (event.keyCode == 13) {
|
|
$('#filter_button').click();
|
|
}
|
|
});
|
|
|
|
// add event button labeled "filter"
|
|
$('#filter_button_ipv6').click(function (event) {
|
|
// get value
|
|
let filter_val = $('#filter_input_ipv6').val();
|
|
if (filter_val == '') {
|
|
// show all
|
|
$('#ipv6_table tbody tr').show();
|
|
} else {
|
|
// hide non-matching items
|
|
let row_not_contains6 = $('#ipv6_table tbody tr input:not([value*=\'' + filter_val + '\'])');
|
|
row_not_contains6.closest('tr').hide();
|
|
// show only matches
|
|
let row_contains6 = $('#ipv6_table tbody tr input[value*=\'' + filter_val + '\']');
|
|
row_contains6.closest('tr').show();
|
|
}
|
|
});
|
|
// add event button labeled "clear"
|
|
$('#filter_clear_ipv6').click(function (event) {
|
|
$('#filter_input_ipv6').val('');
|
|
$('#filter_button_ipv6').click();
|
|
});
|
|
|
|
// trigger filter when enter key pressed
|
|
$('#filter_input_ipv6').keyup(function (event) {
|
|
if (event.keyCode == 13) {
|
|
$('#filter_button_ipv6').click();
|
|
}
|
|
});
|
|
});
|
|
</script>
|
|
<script src="{% static "js/ace/ace.js" %}"></script>
|
|
<script>
|
|
var editor = ace.edit("edit_editor");
|
|
editor.getSession().setMode("ace/mode/xml");
|
|
|
|
var edit_input = $('input[name="edit_xml"]');
|
|
editor.getSession().on("change", function () {
|
|
edit_input.val(editor.getSession().getValue());
|
|
});
|
|
</script>
|
|
{% endblock %} |