mirror of
https://github.com/retspen/webvirtcloud
synced 2026-11-08 11:16:47 +00:00
feat(instances): the VM page's add dialogs in the panel's dialog look
The Add disk, Add network device and bandwidth dialogs of the VM page set their labels in a narrow column beside the fields and closed with a grey Close: Add disk split "GB" over two lines and offered an unexplained Metadata box, its existing-volume pool was a dropdown of links that wrote a hidden field; the network list prefixed every choice with "Network" or "Interface"; the bandwidth fields asked for "kilobytes" where libvirt counts KiB/s and KiB. Now their labels sit above the fields, tied to them, in two columns. Add disk is one form whose source, New disk or Existing volume, is a radio card as in the new VM form, the chosen source's fields shown and sent and its action taken; the size has a GiB suffix and "Preallocate metadata (qcow2)", the existing volume's pool is a select that loads its volumes, the bus and the cache are shared. Add network device picks its source from Networks and Interfaces groups and says how its MAC is filled; the bandwidth limit names its units and when a running VM gets it. Move says when the host has no other pool and is disabled then. Every dialog closes with Cancel and its action; the posted fields are unchanged.
This commit is contained in:
parent
fd080438c3
commit
0fd3e69c8e
8 changed files with 220 additions and 248 deletions
|
|
@ -3,63 +3,59 @@
|
|||
{% if request.user.is_superuser %}
|
||||
<button type="button" class="btn btn-sm btn-primary" data-bs-toggle="modal" data-bs-target="#addInstanceNetwork">{% bs_icon 'plus' %} {% trans "Add network device" %}</button>
|
||||
|
||||
<!-- Modal pool -->
|
||||
<div class="modal fade" id="addInstanceNetwork" tabindex="-1" role="dialog" aria-labelledby="addInstanceNetworkLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<form class="modal-content" action="{% url 'instances:add_network' instance.id %}" method="post" aria-label="{% trans "Add instance network form" %}">{% csrf_token %}
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="addInstanceNetworkLabel">{% trans "Add Instance Network" %}</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
<h5 class="modal-title" id="addInstanceNetworkLabel">{% trans "Add network device" %}</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="{% trans "Close" %}"></button>
|
||||
</div>
|
||||
<form action="{% url 'instances:add_network' instance.id %}" method="post" role="form" aria-label="Add instance network form">{% csrf_token %}
|
||||
<div class="modal-body">
|
||||
<div class="row">
|
||||
<label class="col-sm-4 col-form-label">{% trans "MAC" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<input type="text" class="form-control" name="add-net-mac">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<label class="col-sm-4 col-form-label">{% trans "Network" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<select class="form-select" name="add-net-network">
|
||||
<div class="modal-body">
|
||||
<div class="row g-3">
|
||||
<div class="col-12">
|
||||
<label class="form-label" for="add-net-network">{% trans "Source" %}</label>
|
||||
<select class="form-select" id="add-net-network" name="add-net-network">
|
||||
<optgroup label="{% trans "Networks" %}">
|
||||
{% for c_net in networks_host %}
|
||||
<option value="net:{{ c_net }}">Network {{ c_net }}</option>
|
||||
<option value="net:{{ c_net }}">{{ c_net }}</option>
|
||||
{% endfor %}
|
||||
</optgroup>
|
||||
<optgroup label="{% trans "Interfaces" %}">
|
||||
{% for c_iface in interfaces_host %}
|
||||
<option value="iface:{{ c_iface }}">Interface {{ c_iface }}</option>
|
||||
<option value="iface:{{ c_iface }}">{{ c_iface }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</optgroup>
|
||||
</select>
|
||||
</div>
|
||||
<div class="row">
|
||||
<label class="col-sm-4 col-form-label">{% trans "NWFilter" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<select class="form-select" name="add-net-nwfilter">
|
||||
<option value="">{% trans "None" %}</option>
|
||||
{% for nwfilter in nwfilters_host %}
|
||||
<option value="{{ nwfilter }}">{{ nwfilter }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<label class="form-label" for="add-net-model">{% trans "Model" %}</label>
|
||||
<select class="form-select" id="add-net-model" name="add-net-model">
|
||||
{% for model in net_models_host %}
|
||||
<option value="{{ model }}"{% if model == 'default' %} selected{% endif %}>{{ model }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="row">
|
||||
<label class="col-sm-4 col-form-label">{% trans "NIC Type" %} </label>
|
||||
<div class="col-sm-6">
|
||||
<select class="form-select" name="add-net-model">
|
||||
{% for model in net_models_host %}
|
||||
<option value="{{ model }}" {% if model == 'default' %} selected {% endif %}>{{ model }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<label class="form-label" for="add-net-nwfilter">{% trans "NWFilter" %}</label>
|
||||
<select class="form-select" id="add-net-nwfilter" name="add-net-nwfilter">
|
||||
<option value="">{% trans "None" %}</option>
|
||||
{% for nwfilter in nwfilters_host %}
|
||||
<option value="{{ nwfilter }}">{{ nwfilter }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<label class="form-label" for="add-net-mac">{% trans "MAC" %}</label>
|
||||
<input type="text" class="form-control font-monospace" id="add-net-mac" name="add-net-mac" aria-describedby="add-net-mac-help">
|
||||
<div class="form-text" id="add-net-mac-help">{% trans "A random address is filled in; empty: libvirt chooses one." %}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{% trans "Close" %}</button>
|
||||
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">{% trans "Cancel" %}</button>
|
||||
<button type="submit" class="btn btn-primary" name="add_network">{% trans "Add" %}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div> <!-- /.modal-content -->
|
||||
</div> <!-- /.modal-dialog -->
|
||||
</div> <!-- /.modal -->
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
|
|
|||
|
|
@ -3,13 +3,12 @@
|
|||
{% if request.user.is_superuser %}
|
||||
<button type="button" class="btn btn-sm btn-primary" data-bs-toggle="modal" data-bs-target="#addInstanceOwner">{% bs_icon 'plus' %} {% trans "Add owner" %}</button>
|
||||
|
||||
<!-- Modal pool -->
|
||||
<div class="modal fade" id="addInstanceOwner" tabindex="-1" role="dialog" aria-labelledby="addInstanceOwnerLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="addInstanceOwnerLabel">{% trans "Add owner" %}</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="{% trans "Close" %}"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="add-owner-form" method="post" action="{% url 'instances:add_owner' instance.id %}" role="form" aria-label="Add instance owner form">{% csrf_token %}
|
||||
|
|
@ -27,7 +26,7 @@
|
|||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{% trans "Close" %}</button>
|
||||
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">{% trans "Cancel" %}</button>
|
||||
<button type="submit" class="btn btn-primary" form="add-owner-form"{% if not users %} disabled{% endif %}>{% trans "Add" %}</button>
|
||||
</div>
|
||||
</div> <!-- /.modal-content -->
|
||||
|
|
|
|||
|
|
@ -1,157 +1,123 @@
|
|||
{% load i18n %}
|
||||
{% load static %}
|
||||
{% load bootstrap_icons %}
|
||||
{% if request.user.is_superuser %}
|
||||
<a href="#addvol" type="button" class="btn btn-sm btn-primary" data-bs-toggle="modal">{% bs_icon 'plus' %} {% trans "Add disk" %}</a>
|
||||
|
||||
<!-- Modal pool -->
|
||||
{# one form: a card per disk source, the fields of the chosen one (the others hidden and disabled, so not sent); the source chooses the form's action #}
|
||||
<div class="modal fade" id="addvol" tabindex="-1" role="dialog" aria-labelledby="addInstanceVolumeLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<form class="modal-content" id="addvol-form" method="post" action="{% url 'instances:add_new_vol' instance.id %}"
|
||||
data-new="{% url 'instances:add_new_vol' instance.id %}" data-existing="{% url 'instances:add_existing_vol' instance.id %}"
|
||||
aria-label="{% trans "Add disk form" %}">{% csrf_token %}
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">{% trans "Add Instance Volume" %}</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
<h5 class="modal-title" id="addInstanceVolumeLabel">{% trans "Add disk" %}</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="{% trans "Close" %}"></button>
|
||||
</div>
|
||||
|
||||
<ul class="nav nav-tabs" aria-label="Disk attach type">
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link active" aria-controls="NewDisk" data-bs-toggle="tab" data-bs-target="#NewDisk" type="button" role="tab" aria-selected="true">
|
||||
{% trans 'New Disk' %}
|
||||
</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link" aria-controls="ExistingDisk" data-bs-toggle="tab" data-bs-target="#ExistingDisk" type="button" role="tab" aria-selected="true">
|
||||
{% trans 'Existing Disk' %}
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane active" id="NewDisk">
|
||||
<form action="{% url 'instances:add_new_vol' instance.id %}" method="post" role="form" aria-label="Add new volume to disk form">{% csrf_token %}
|
||||
<div class="modal-body">
|
||||
<p class="fw-bold">{% trans "Volume parameters" %}</p>
|
||||
<div class="row">
|
||||
<label class="col-sm-3 col-form-label">{% trans "Storage" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<select name="storage" class="form-select image-format">
|
||||
{% for storage in storages_host %}
|
||||
<option value="{{ storage }}">{{ storage }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<label class="col-sm-3 col-form-label">{% trans "Name" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<input type="text" class="form-control" name="name" placeholder="{% trans "Name" %}" required pattern="[a-zA-Z0-9\.\-_]+">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<label class="col-sm-3 col-form-label">{% trans "Format" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<select name="format" class="form-select image-format">
|
||||
{% for format in instance.formats %}
|
||||
<option value="{{ format }}" {% if format == default_format %}selected{% endif %}>{% trans format %}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<label class="col-sm-3 col-form-label">{% trans "Size" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<input type="text" class="form-control" name="size" value="10" maxlength="5" required pattern="[0-9]+">
|
||||
</div>
|
||||
<label class="col-sm-1 col-form-label">{% trans "GB" %}</label>
|
||||
</div>
|
||||
<div class="row">
|
||||
<label class="col-sm-3 col-form-label">{% trans "Bus" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<select name="bus" class="form-select image-format">
|
||||
{% for bus in bus_host %}
|
||||
<option value="{{ bus }}" {% if bus == default_bus %}selected{% endif %}>{% trans bus %}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<label class="col-sm-3 col-form-label">{% trans "Cache" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<select name="cache" class="form-select image-format">
|
||||
{% for mode, name in instance.cache_modes %}
|
||||
<option value="{{ mode }}" {% if mode == default_cache %}selected{% endif %}>{% trans name %}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row meta-prealloc">
|
||||
<label class="col-sm-3 col-form-label">{% trans "Metadata" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<input type="checkbox" name="meta_prealloc" value="true">
|
||||
</div>
|
||||
</div>
|
||||
</div> <!-- /.modal-body -->
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{% trans "Close" %}</button>
|
||||
<button type="submit" class="btn btn-primary" name="add_new_vol">{% trans "Add Volume" %}</button>
|
||||
<div class="modal-body">
|
||||
<fieldset class="mb-3">
|
||||
<legend class="form-label fs-6">{% trans "Disk source" %}</legend>
|
||||
<div class="row row-cols-1 row-cols-sm-2 g-2">
|
||||
{% trans "New disk" as title %}{% trans "An empty disk of the size you set, made in a pool." as help %}
|
||||
{% include "radio_card.html" with name="disk_source" value="new" chosen="new" %}
|
||||
{% trans "Existing volume" as title %}{% trans "A volume already in a pool of the host." as help %}
|
||||
{% include "radio_card.html" with name="disk_source" value="existing" chosen="new" %}
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset data-kinds="new">
|
||||
<div class="row g-3">
|
||||
<div class="col-sm-6">
|
||||
<label class="form-label" for="addvol-storage">{% trans "Storage pool" %}</label>
|
||||
<select id="addvol-storage" name="storage" class="form-select">
|
||||
{% for storage in storages_host %}
|
||||
<option value="{{ storage }}">{{ storage }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="tab-pane" id="ExistingDisk">
|
||||
<form action="{% url 'instances:add_existing_vol' instance.id %}" method="post" role="form" aria-label="Add existing volume to instance form">{% csrf_token %}
|
||||
<div class="modal-body">
|
||||
<p class="fw-bold">{% trans "Volume parameters" %}</p>
|
||||
<div class="row">
|
||||
<label class="col-sm-3 col-form-label">{% trans "Storage" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<div class="dropdown">
|
||||
<button id="select_storage" class="btn btn-secondary dropdown-toggle form-control" type="button" data-bs-toggle="dropdown">{% trans 'Select Pool' %}...</button>
|
||||
<div class="dropdown-menu">
|
||||
{% for storage in storages_host %}
|
||||
<a class="dropdown-item" href="#" onclick="get_volumes({{ instance.compute.id }}, '{{ storage }}')">{{ storage }}</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<input id="selected_storage" name="selected_storage" hidden/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<label class="col-sm-3 col-form-label" >{% trans "Volume" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<select id="vols" name="vols" class="form-select" disabled>
|
||||
<option value="" selected>{% trans 'None' %}</option>
|
||||
<!-- populate with javascript -->
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<label class="col-sm-3 col-form-label">{% trans "Bus" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<select name="bus" class="form-select image-format">
|
||||
{% for bus in bus_host %}
|
||||
<option value="{{ bus }}" {% if bus == default_bus %}selected{% endif %}>{% trans bus %}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<label class="col-sm-3 col-form-label">{% trans "Cache" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<select name="cache" class="form-select image-format">
|
||||
{% for mode, name in instance.cache_modes %}
|
||||
<option value="{{ mode }}" {% if mode == default_cache %}selected{% endif %}>{% trans name %}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<label class="form-label" for="addvol-name">{% trans "Name" %}</label>
|
||||
<input id="addvol-name" type="text" class="form-control" name="name" required pattern="[a-zA-Z0-9\.\-_]+" aria-describedby="addvol-name-help">
|
||||
<div class="form-text" id="addvol-name-help">{% trans "Letters, digits, dot, dash and underscore." %}</div>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<label class="form-label" for="addvol-size">{% trans "Size" %}</label>
|
||||
<div class="input-group">
|
||||
<input id="addvol-size" type="text" class="form-control" name="size" value="10" maxlength="5" required pattern="[0-9]+" inputmode="numeric" aria-describedby="addvol-size-unit">
|
||||
<span class="input-group-text" id="addvol-size-unit">GiB</span>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{% trans "Close" %}</button>
|
||||
<button type="submit" class="btn btn-primary">{% trans "Add Volume" %}</button>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<label class="form-label" for="addvol-format">{% trans "Format" %}</label>
|
||||
<select id="addvol-format" name="format" class="form-select">
|
||||
{% for format in instance.formats %}
|
||||
<option value="{{ format }}"{% if format == default_format %} selected{% endif %}>{% trans format %}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="addvol-prealloc" name="meta_prealloc" value="true">
|
||||
<label class="form-check-label" for="addvol-prealloc">{% trans "Preallocate metadata (qcow2)" %}</label>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset data-kinds="existing">
|
||||
<div class="row g-3">
|
||||
<div class="col-sm-6">
|
||||
<label class="form-label" for="selected_storage">{% trans "Storage pool" %}</label>
|
||||
<select id="selected_storage" name="selected_storage" class="form-select" required
|
||||
onchange="get_volumes({{ instance.compute.id }}, this.value)">
|
||||
<option value="" selected disabled>{% trans "Choose a pool" %}</option>
|
||||
{% for storage in storages_host %}
|
||||
<option value="{{ storage }}">{{ storage }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<label class="form-label" for="vols">{% trans "Volume" %}</label>
|
||||
<select id="vols" name="vols" class="form-select" required>
|
||||
<option value="" selected disabled>{% trans "Choose a pool first" %}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<div class="row g-3 mt-0">
|
||||
<div class="col-sm-6">
|
||||
<label class="form-label" for="addvol-bus">{% trans "Bus" %}</label>
|
||||
<select id="addvol-bus" name="bus" class="form-select">
|
||||
{% for bus in bus_host %}
|
||||
<option value="{{ bus }}"{% if bus == default_bus %} selected{% endif %}>{% trans bus %}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<label class="form-label" for="addvol-cache">{% trans "Cache mode" %}</label>
|
||||
<select id="addvol-cache" name="cache" class="form-select">
|
||||
{% for mode, name in instance.cache_modes %}
|
||||
<option value="{{ mode }}"{% if mode == default_cache %} selected{% endif %}>{% trans name %}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div> <!-- /.modal-content -->
|
||||
</div> <!-- /.modal-dialog -->
|
||||
</div> <!-- /.modal -->
|
||||
</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">{% trans "Add disk" %}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<script src="{% static 'js/choice_dialog.js' %}"></script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
var form = document.getElementById('addvol-form');
|
||||
choiceDialog(document.getElementById('addvol'), 'disk_source', function (source) {
|
||||
form.action = form.dataset[source];
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endif %}
|
||||
|
|
|
|||
|
|
@ -109,27 +109,14 @@
|
|||
{% block script %}
|
||||
<script src="{% static 'js/ace/ace.js' %}" type="text/javascript" charset="utf-8"></script>
|
||||
<script>
|
||||
// the volumes of the pool chosen in the Add disk dialog
|
||||
function get_volumes(compute_id, pool) {
|
||||
get_vol_url = "/computes/" + compute_id + "/storage/" + pool + "/volumes";
|
||||
$.getJSON(get_vol_url, function (data) {
|
||||
$("#vols").find('option').remove();
|
||||
$("#vols").removeAttr("disabled");
|
||||
|
||||
$.each(data['vols'], function(i, item) {
|
||||
$("#vols").append($("<option>").val(item).text(item));
|
||||
})
|
||||
$.getJSON("/computes/" + compute_id + "/storage/" + encodeURIComponent(pool) + "/volumes/", function (data) {
|
||||
var vols = $("#vols").empty();
|
||||
$.each(data['vols'], function (i, item) {
|
||||
vols.append($("<option>").val(item).text(item));
|
||||
});
|
||||
});
|
||||
|
||||
var sto_drop = document.getElementById('select_storage');
|
||||
sto_drop.value = pool;
|
||||
sto_drop.textContent = pool;
|
||||
var caret = document.createElement("span");
|
||||
caret.className = "caret";
|
||||
sto_drop.appendChild(caret);
|
||||
|
||||
var sto_input = document.getElementById('selected_storage');
|
||||
sto_input.setAttribute('value', pool);
|
||||
//sto_input.innerHTML = pool;
|
||||
}
|
||||
</script>
|
||||
<script src="{% static 'js/xml_editor.js' %}"></script>
|
||||
|
|
|
|||
|
|
@ -17,11 +17,15 @@
|
|||
<select class="form-select" name="pool" id="movedisk{{ id }}-pool" required>
|
||||
{% for storage in storages_host %}{% if storage != disk.storage %}<option value="{{ storage }}">{{ storage }}</option>{% endif %}{% endfor %}
|
||||
</select>
|
||||
{# no pool but the disk's own #}
|
||||
{% if not storages_host or storages_host|length == 1 and disk.storage in storages_host %}
|
||||
<div class="form-text text-danger">{% trans "The host has no other storage pool to move the disk to." %}</div>
|
||||
{% endif %}
|
||||
<div class="form-text text-break">{% blocktrans with path=disk.path %}The disk is copied into the pool, a running VM's without stopping it, and the VM uses the copy; {{ path }} is deleted then, unless a VM or volume of any host uses it.{% endblocktrans %}</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{% trans "Cancel" %}</button>
|
||||
<button type="submit" class="btn btn-primary">{% trans "Move" %}</button>
|
||||
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">{% trans "Cancel" %}</button>
|
||||
<button type="submit" class="btn btn-primary"{% if not storages_host or storages_host|length == 1 and disk.storage in storages_host %} disabled{% endif %}>{% trans "Move" %}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -122,6 +122,38 @@ class PendingEditsTestCase(TestCase):
|
|||
(switch,) = doc.xpath("//div[@id='editvol0']//input[@name=$n]", n=name)
|
||||
self.assertEqual(doc.xpath("//div[@id='editvol0']//label[@for=$i]", i=switch.get("id"))[0].tag, "label")
|
||||
|
||||
def test_the_add_dialogs_label_their_fields_and_units(self):
|
||||
disks = [disk("vda", "default", "")]
|
||||
nic = {"type": "network", "mac": "52:54:10:00:00:01", "nic": "default", "state": "up", "model": "virtio",
|
||||
"ipv4": None, "ipv6": None, "filterref": "", "target": "", "inbound": [], "outbound": []}
|
||||
doc = self.page(live=disks, config=disks, nics=([nic], [nic]), get_networks=(["default"],) * 2,
|
||||
get_ifaces=(["br0"],) * 2, get_storages=(["default"],) * 2)
|
||||
# every field of the Add disk, Add network device and bandwidth dialogs has its label
|
||||
for dialog in ("addvol", "addInstanceNetwork", "AddQos0"):
|
||||
# a radio card's label wraps its radio
|
||||
fields = doc.xpath("//div[@id=$d]//*[self::input or self::select][not(@type='hidden' or @type='radio')]",
|
||||
d=dialog)
|
||||
self.assertTrue(fields, dialog)
|
||||
for field in fields:
|
||||
self.assertTrue(doc.xpath("//label[@for=$i]", i=field.get("id") or "-"), (dialog, field.get("name")))
|
||||
self.assertEqual(doc.xpath("//div[@id='addvol']//span[@class='input-group-text']/text()"), ["GiB"])
|
||||
# Add disk: the source is a radio card, each source's fields a fieldset; the script sets the form's action
|
||||
self.assertEqual(doc.xpath("//div[@id='addvol']//input[@name='disk_source']/@value"), ["new", "existing"])
|
||||
self.assertEqual(doc.xpath("//div[@id='addvol']//fieldset/@data-kinds"), ["new", "existing"])
|
||||
(form,) = doc.xpath("//form[@id='addvol-form']")
|
||||
self.assertEqual((form.get("data-new"), form.get("data-existing")),
|
||||
(reverse("instances:add_new_vol", args=[self.instance.id]),
|
||||
reverse("instances:add_existing_vol", args=[self.instance.id])))
|
||||
self.assertEqual(doc.xpath("//select[@name='add-net-network']//option/@value"), ["net:default", "iface:br0"])
|
||||
self.assertEqual(doc.xpath("//div[@id='AddQos0']//label/text()"),
|
||||
["Direction", "Average (KiB/s)", "Peak (KiB/s)", "Burst (KiB)"])
|
||||
# Move needs a pool other than the disk's own
|
||||
for pools, disabled in (([], ["disabled"]), (["default"], ["disabled"]), (["other"], []),
|
||||
(["default", "other"], [])):
|
||||
with self.subTest(pools=pools):
|
||||
doc = self.page(live=disks, config=disks, get_storages=(pools,) * 2)
|
||||
self.assertEqual(doc.xpath("//div[@id='movedisk0']//button[@type='submit']/@disabled"), disabled)
|
||||
|
||||
def test_the_clone_form_lists_the_persistent_devices(self):
|
||||
nic = {"type": "network", "mac": "52:54:10:00:00:01", "nic": "default", "state": "up", "model": "virtio",
|
||||
"ipv4": None, "ipv6": None, "filterref": "", "target": "", "inbound": [], "outbound": []}
|
||||
|
|
|
|||
|
|
@ -136,7 +136,6 @@ HTML_SINK = re.compile(r"""\.html\(|innerHTML\s*=|append\(\s*['"]<""")
|
|||
ALLOWED_HTML_SINKS = {
|
||||
("accounts/templates/login.html", "$btn.html("): "static spinner markup",
|
||||
("accounts/templates/accounts/otp_login.html", "$btn.html("): "static spinner markup",
|
||||
("instances/templates/instance.html", "//sto_input.innerHTML = pool;"): "comment",
|
||||
("instances/templates/instance.html", "hostDevices.innerHTML = html;"): "the panel's own fragment, host_devices_form.html, escaped by Django",
|
||||
("instances/templates/instance.html", "migratePlan.innerHTML = html;"): "the panel's own fragment, migrate_plan.html, escaped by Django",
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,52 +1,41 @@
|
|||
{% load i18n %}
|
||||
{% if request.user.is_superuser %}
|
||||
<!-- Modal pool -->
|
||||
<div class="modal fade" id="AddQos{{ id }}" tabindex="-1" role="dialog" aria-labelledby="AddQosLabel{{ id }}"
|
||||
aria-hidden="true">
|
||||
<div class="modal fade" id="AddQos{{ id }}" tabindex="-1" role="dialog" aria-labelledby="AddQosLabel{{ id }}" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="AddQosLabel{{ id }}">{% trans "Add QoS for Network" %}</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
<h5 class="modal-title" id="AddQosLabel{{ id }}">{% blocktrans with nic=id %}Bandwidth limit of eth{{ nic }}{% endblocktrans %}</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="{% trans "Close" %}"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="row">
|
||||
<label class="col-sm-4 col-form-label">{% trans "Direction" %}:</label>
|
||||
<div class="col-sm-6">
|
||||
<select class="form-select" name="qos_direction">
|
||||
<option value="inbound">{% trans 'Inbound' %}</option>
|
||||
<option value="outbound">{% trans 'Outbound' %}</option>
|
||||
<div class="row g-3">
|
||||
<div class="col-12">
|
||||
<label class="form-label" for="qos_direction{{ id }}">{% trans "Direction" %}</label>
|
||||
<select class="form-select" id="qos_direction{{ id }}" name="qos_direction">
|
||||
<option value="inbound">{% trans "Inbound: to the VM" %}</option>
|
||||
<option value="outbound">{% trans "Outbound: from the VM" %}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<label class="col-sm-4 col-form-label">{% trans "Average" %}:</label>
|
||||
<div class="col-sm-6">
|
||||
<input class="form-control" name="qos_average" placeholder="{% trans "kilobytes" %}"
|
||||
required pattern="[0-9]+"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<label class="col-sm-4 col-form-label">{% trans "Peak" %}:</label>
|
||||
<div class="col-sm-6">
|
||||
<input class="form-control" name="qos_peak" placeholder="{% trans "kilobytes" %}"
|
||||
pattern="[0-9]+"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<label class="col-sm-4 col-form-label">{% trans "Burst" %}:</label>
|
||||
<div class="col-sm-6">
|
||||
<input class="form-control" name="qos_burst" placeholder="{% trans "kilobytes" %}"
|
||||
pattern="[0-9]+"/>
|
||||
<div class="col-sm-4">
|
||||
<label class="form-label" for="qos_average{{ id }}">{% trans "Average (KiB/s)" %}</label>
|
||||
<input class="form-control" id="qos_average{{ id }}" name="qos_average" required pattern="[0-9]+" inputmode="numeric">
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
<label class="form-label" for="qos_peak{{ id }}">{% trans "Peak (KiB/s)" %}</label>
|
||||
<input class="form-control" id="qos_peak{{ id }}" name="qos_peak" pattern="[0-9]+" inputmode="numeric">
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
<label class="form-label" for="qos_burst{{ id }}">{% trans "Burst (KiB)" %}</label>
|
||||
<input class="form-control" id="qos_burst{{ id }}" name="qos_burst" pattern="[0-9]+" inputmode="numeric">
|
||||
</div>
|
||||
</div>
|
||||
<p class="small text-body-secondary mt-3 mb-0">{% trans "A running VM gets a change after it is powered off and on." %}</p>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{% trans 'Close' %}</button>
|
||||
<button type="submit" class="btn btn-primary" name="set_qos">{% trans 'Save' %}</button>
|
||||
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">{% trans "Cancel" %}</button>
|
||||
<button type="submit" class="btn btn-primary" name="set_qos">{% trans "Save" %}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div> <!-- /.modal-dialog -->
|
||||
</div> <!-- /.modal -->
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue