mirror of
https://github.com/retspen/webvirtcloud
synced 2026-11-11 12:46:49 +00:00
The host pages' dialogs (new network, fixed address, DHCP range, bandwidth limit, new interface, new filter, filter rule, filter clone, new storage pool, new secret, a secret's value) and the VM page's XML review closed with a grey filled Cancel beside their action; the redone dialogs use an outlined Cancel, so the action is the one filled button.
212 lines
13 KiB
HTML
212 lines
13 KiB
HTML
{% extends "base.html" %}
|
||
{% load i18n %}
|
||
{% load static %}
|
||
{% load bootstrap_icons %}
|
||
|
||
{% block title %}{% trans "Network filter" %} - {{ name }}{% endblock %}
|
||
|
||
{% block page_heading %}{{ compute.name }} - {% trans "Network filter" %}: {{ name }}{% endblock page_heading %}
|
||
|
||
{% block content %}
|
||
{% include "computes/host_nav.html" with section="nwfilters" detail=True %}
|
||
|
||
{% if uuid %}
|
||
<div class="card mb-3">
|
||
<div class="card-header">{% trans "Filter" %}</div>
|
||
<ul class="list-group list-group-flush">
|
||
<li class="list-group-item py-1 d-flex justify-content-between align-items-baseline gap-3">
|
||
<span class="text-body-secondary">{% trans "UUID" %}</span><span class="font-monospace text-break text-end">{{ uuid }}</span>
|
||
</li>
|
||
<li class="list-group-item py-1 d-flex justify-content-between align-items-baseline gap-3">
|
||
<span class="text-body-secondary">{% trans "Chain" %}</span><span class="font-monospace">{{ chain|default:"–" }}</span>
|
||
</li>
|
||
<li class="list-group-item py-1 d-flex justify-content-between align-items-baseline gap-3">
|
||
<span class="text-body-secondary">{% trans "Priority" %}</span><span>{% if priority is not None %}{{ priority }}{% else %}–{% endif %}</span>
|
||
</li>
|
||
<li class="list-group-item py-1 d-flex justify-content-between align-items-baseline gap-3">
|
||
<span class="text-body-secondary">{% trans "Directly used by" %}</span>
|
||
<span class="text-end">{% for vm in vms %}{% if vm.id %}<a href="{% url 'instances:instance' vm.id %}">{{ vm.name }}</a>{% else %}{{ vm.name }}{% endif %}{% if not forloop.last %}, {% endif %}{% empty %}–{% endfor %}</span>
|
||
</li>
|
||
<li class="list-group-item py-1 d-flex justify-content-between align-items-baseline gap-3">
|
||
<span class="text-body-secondary">{% trans "Referenced by" %}</span>
|
||
<span class="text-end">{% for other in referenced_by %}<a href="{% url 'nwfilter' compute.id other %}">{{ other }}</a>{% if not forloop.last %}, {% endif %}{% empty %}–{% endfor %}</span>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
|
||
<div class="card mb-3">
|
||
<div class="card-header d-flex flex-wrap align-items-center gap-2">
|
||
<span class="me-auto">{% trans "References" %}</span>
|
||
{% if ref_choices %}
|
||
<form method="post" class="d-flex gap-2">{% csrf_token %}
|
||
<select name="nwfilters_select" class="form-select form-select-sm w-auto" aria-label="{% trans "Filter to reference" %}" required>
|
||
{% for choice in ref_choices %}<option value="{{ choice }}">{{ choice }}</option>{% endfor %}
|
||
</select>
|
||
<button type="submit" class="btn btn-sm btn-primary text-nowrap" name="add_nwfilter_ref">{% bs_icon 'plus-lg' %} {% trans "Add" %}</button>
|
||
</form>
|
||
{% endif %}
|
||
</div>
|
||
{% if refs %}
|
||
<div class="table-responsive position-relative">
|
||
<table class="table table-hover align-middle mb-0">
|
||
<thead>
|
||
<tr>
|
||
<th scope="col">{% trans "Filter" %}</th>
|
||
<th scope="col">{% trans "Parameters" %}</th>
|
||
<th scope="col"><span class="visually-hidden">{% trans "Actions" %}</span></th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{% for ref in refs %}
|
||
<tr>
|
||
<td class="text-nowrap">
|
||
{% if ref.filter in filter_names %}<a href="{% url 'nwfilter' compute.id ref.filter %}">{{ ref.filter }}</a>
|
||
{% else %}{{ ref.filter }} <span class="badge bg-danger-subtle text-danger-emphasis">{% trans "Missing" %}</span>{% endif %}
|
||
</td>
|
||
<td class="font-monospace small">{% for param, value in ref.parameters %}<div>{{ param }}={{ value }}</div>{% empty %}–{% endfor %}</td>
|
||
<td class="text-end">
|
||
<form method="post">{% csrf_token %}
|
||
<input type="hidden" name="index" value="{{ forloop.counter0 }}">
|
||
<input type="hidden" name="ref" value="{{ ref.text }}">
|
||
<button type="submit" class="btn btn-sm btn-outline-danger" name="del_nwfilter_ref" aria-label="{% blocktrans with ref=ref.filter %}Remove the reference to {{ ref }}{% endblocktrans %}"
|
||
data-confirm="{% blocktrans with ref=ref.filter %}Remove the reference to {{ ref }}? Its rules no longer apply through this filter.{% endblocktrans %}"
|
||
onclick="return confirm(this.dataset.confirm)">{% bs_icon 'trash' %}</button>
|
||
</form>
|
||
</td>
|
||
</tr>
|
||
{% endfor %}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
{% else %}
|
||
<div class="card-body small text-body-secondary">{% trans "It references no other filter." %}</div>
|
||
{% endif %}
|
||
</div>
|
||
|
||
<div class="card mb-3">
|
||
<div class="card-header d-flex flex-wrap align-items-center gap-2">
|
||
<span class="me-auto">{% trans "Rules" %}</span>
|
||
<button type="button" class="btn btn-sm btn-primary" data-bs-toggle="modal" data-bs-target="#AddNWFilterRule">{% bs_icon 'plus-lg' %} {% trans "Add rule" %}</button>
|
||
</div>
|
||
{% if rules %}
|
||
<div class="table-responsive position-relative">
|
||
<table class="table table-hover align-middle mb-0">
|
||
<thead>
|
||
<tr>
|
||
<th scope="col">#</th>
|
||
<th scope="col">{% trans "Action" %}</th>
|
||
<th scope="col">{% trans "Direction" %}</th>
|
||
<th scope="col">{% trans "Priority" %}</th>
|
||
<th scope="col">{% trans "Matches" %}</th>
|
||
<th scope="col"><span class="visually-hidden">{% trans "Actions" %}</span></th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{% for rule in rules %}
|
||
<tr>
|
||
<th scope="row">{{ forloop.counter }}</th>
|
||
<td>
|
||
{% if rule.action == "accept" %}<span class="badge bg-success-subtle text-success-emphasis">{% trans "Accept" %}</span>
|
||
{% elif rule.action == "drop" or rule.action == "reject" %}<span class="badge bg-danger-subtle text-danger-emphasis">{{ rule.action|capfirst }}</span>
|
||
{% else %}<span class="badge text-bg-light border">{{ rule.action|capfirst }}</span>{% endif %}
|
||
</td>
|
||
<td class="text-nowrap">{% if rule.direction == "in" %}{% trans "To the VM" %}{% elif rule.direction == "out" %}{% trans "From the VM" %}{% elif rule.direction == "inout" %}{% trans "Both ways" %}{% else %}{{ rule.direction }}{% endif %}</td>
|
||
<td>{% if rule.priority is not None %}{{ rule.priority }}{% else %}500{% endif %}</td>
|
||
<td class="small">
|
||
{% for protocol in rule.protocols %}
|
||
<div><span class="badge text-bg-light border">{{ protocol.name }}</span>{% for attribute, value in protocol.attributes %} <span class="font-monospace">{{ attribute }}={{ value }}</span>{% endfor %}</div>
|
||
{% empty %}
|
||
<span class="text-body-secondary">{% trans "Any traffic of the chain" %}</span>
|
||
{% endfor %}
|
||
{% if rule.statematch == "false" or rule.statematch == "0" %}<div class="text-body-secondary">{% trans "Without connection tracking" %}</div>{% endif %}
|
||
</td>
|
||
<td class="text-end">
|
||
<form method="post">{% csrf_token %}
|
||
<input type="hidden" name="index" value="{{ forloop.counter0 }}">
|
||
<input type="hidden" name="rule" value="{{ rule.text }}">
|
||
<button type="submit" class="btn btn-sm btn-outline-danger" name="del_nwfilter_rule" aria-label="{% blocktrans with number=forloop.counter %}Delete rule {{ number }}{% endblocktrans %}"
|
||
data-confirm="{% trans "Delete this rule? Interfaces that use the filter no longer get it." %}"
|
||
onclick="return confirm(this.dataset.confirm)">{% bs_icon 'trash' %}</button>
|
||
</form>
|
||
</td>
|
||
</tr>
|
||
{% endfor %}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
{% else %}
|
||
<div class="card-body small text-body-secondary">{% trans "The filter has no rules of its own." %}</div>
|
||
{% endif %}
|
||
</div>
|
||
|
||
<section class="accordion mb-3" aria-labelledby="xml-heading">
|
||
<div class="accordion-item">
|
||
<h2 class="accordion-header" id="xml-heading">
|
||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#nwfilter-xml" aria-expanded="false" aria-controls="nwfilter-xml">
|
||
{% trans "XML definition" %}
|
||
</button>
|
||
</h2>
|
||
<div class="accordion-collapse collapse" id="nwfilter-xml">
|
||
<form method="post" class="accordion-body" aria-label="{% trans "Change the filter's XML" %}">{% csrf_token %}
|
||
<p class="small text-body-secondary">{% trans "A change applies at once, also to the running VMs that use the filter; it keeps the filter's name and UUID." %}</p>
|
||
<input type="hidden" name="edit_xml">
|
||
<div class="xmlheight mb-3"><textarea id="edit_editor" aria-label="{% trans "Filter XML" %}">{{ xml }}</textarea></div>
|
||
<div class="d-flex justify-content-end">
|
||
<button type="submit" class="btn btn-primary" name="edit_nwfilter">{% trans "Save" %}</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<div class="modal fade" id="AddNWFilterRule" tabindex="-1" aria-labelledby="AddNWFilterRuleLabel" aria-hidden="true">
|
||
<div class="modal-dialog modal-lg modal-fullscreen-sm-down">
|
||
<form method="post" class="modal-content">{% csrf_token %}
|
||
<div class="modal-header">
|
||
<h5 class="modal-title" id="AddNWFilterRuleLabel">{% trans "Add rule" %}</h5>
|
||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="{% trans "Close" %}"></button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<p class="small text-body-secondary" id="rule_editor_help">
|
||
{% trans "One rule: in is traffic to the VM, out from it; a lower priority applies first (500 if none is given)." %}
|
||
<a href="https://libvirt.org/formatnwfilter.html#supported-protocols" target="_blank" rel="noopener">{% trans "The protocols and their attributes" %}</a>
|
||
</p>
|
||
<input type="hidden" name="nwfilterrule_xml">
|
||
<div class="xmlheight"><textarea id="rule_editor" aria-label="{% trans "Rule XML" %}" aria-describedby="rule_editor_help"><rule action='accept' direction='in' priority='500'>
|
||
<tcp dstportstart='443'/>
|
||
</rule></textarea></div>
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">{% trans "Cancel" %}</button>
|
||
<button type="submit" class="btn btn-primary" name="add_nwfilter_rule">{% trans "Add rule" %}</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
{% endblock %}
|
||
|
||
{% block script %}
|
||
{% if uuid %}
|
||
<script src="{% static 'js/ace/ace.js' %}"></script>
|
||
<script>
|
||
// each editor's text goes in its form's hidden field, the starter too if sent as it is,
|
||
// and it measures itself when its section or dialog is shown
|
||
function xmlEditor(textareaId, fieldName, shownBy, shownEvent) {
|
||
var textarea = document.getElementById(textareaId);
|
||
var form = textarea.closest("form");
|
||
var editor = ace.edit(textareaId);
|
||
editor.getSession().setMode("ace/mode/xml");
|
||
editor.setOption("textInputAriaLabel", textarea.getAttribute("aria-label"));
|
||
var field = form.elements[fieldName];
|
||
field.value = editor.getSession().getValue();
|
||
editor.getSession().on("change", function () {
|
||
field.value = editor.getSession().getValue();
|
||
});
|
||
document.getElementById(shownBy).addEventListener(shownEvent, () => editor.resize());
|
||
}
|
||
xmlEditor("edit_editor", "edit_xml", "nwfilter-xml", "shown.bs.collapse");
|
||
xmlEditor("rule_editor", "nwfilterrule_xml", "AddNWFilterRule", "shown.bs.modal");
|
||
</script>
|
||
{% endif %}
|
||
{% endblock %}
|