mirror of
https://github.com/retspen/webvirtcloud
synced 2026-11-08 19:26:48 +00:00
Twenty-five prompts asked only "Are you sure?", and on the storage page one handler asked the same before every button, Start and Enable included. Each destructive button now names its object and says what happens: a stopped network cuts running VMs off, a deleted pool keeps its files, a deleted volume loses its data, a reverted snapshot loses the changes made since. Saves no longer ask, and disabled buttons lose prompts they could not show. The VM QoS messages said to restart the network where the VM has to be powered off and on. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
457 lines
No EOL
22 KiB
HTML
457 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">
|
|
<ol class="breadcrumb shadow-sm">
|
|
<li class="breadcrumb-item">
|
|
<a href="{% url 'overview' compute.id %}">{% bs_icon 'laptop' %} {% trans "Overview" %}</a>
|
|
</li>
|
|
<li class="breadcrumb-item">
|
|
<a href="{% url 'instances' compute.id %}">{% bs_icon 'server' %} {% trans "Instances" %}</a>
|
|
</li>
|
|
<li class="breadcrumb-item">
|
|
<a href="{% url 'storages' compute.id %}">{% bs_icon 'device-hdd' %} {% trans "Storages" %}</a>
|
|
</li>
|
|
<li class="breadcrumb-item">
|
|
<a href="{% url 'networks' compute.id %}">{% bs_icon 'hdd-network' %} {% trans "Networks" %}</a>
|
|
</li>
|
|
<li class="breadcrumb-item">
|
|
<a href="{% url 'interfaces' compute.id %}">{% bs_icon 'wifi' %} {% trans "Interfaces" %}</a>
|
|
</li>
|
|
<li class="breadcrumb-item">
|
|
<a href="{% url 'nwfilters' compute.id %}">{% bs_icon 'filter' %} {% trans "NWFilters" %}</a>
|
|
</li>
|
|
<li class="breadcrumb-item">
|
|
<a href="{% url 'virtsecrets' compute.id %}">{% bs_icon 'key' %} {% trans "Secrets" %}</a>
|
|
</li>
|
|
</ol>
|
|
|
|
</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="page-header">{% 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="page-header">{% 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 }}" 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"/>
|
|
<button type="submit" class="btn btn-sm btn-primary"
|
|
name="modify_fixed_address"
|
|
title="{% trans "Edit entry" %}">
|
|
{% bs_icon 'save' %}
|
|
</button>
|
|
<button type="submit" class="btn btn-sm btn-danger"
|
|
name="delete_fixed_address"
|
|
title="{% trans "Delete entry" %}" data-confirm="{% blocktrans with mac=fix4.mac %}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="page-header">{% 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="page-header">{% 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 }}" 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"/>
|
|
<button type="submit" class="btn btn-sm btn-primary"
|
|
name="modify_fixed_address"
|
|
title="{% trans "Edit entry" %}">
|
|
<span>{% bs_icon 'save' %} </span>
|
|
</button>
|
|
<button type="submit" class="btn btn-sm btn-danger"
|
|
name="delete_fixed_address"
|
|
title="{% trans "Delete entry" %}" data-confirm="{% blocktrans with id=fix6.id %}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 net_forward.0 == 'isolated' %}
|
|
{% 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="page-header">{% trans "QoS Configuration" %}</h5>
|
|
|
|
<div class="col-sm-12">
|
|
<div class="table-responsive">
|
|
<table class="table table-hover">
|
|
<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" data-bs-toggle="modal"
|
|
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 %} |