mirror of
https://github.com/retspen/webvirtcloud
synced 2026-11-08 19:26:48 +00:00
The QoS table showed the running VM's bandwidth while an edit changes the definition for the next start. Each row posts average, peak and burst, so an edit wrote the running values back over a pending change, and a direction added while the VM ran had no row. The table shows the persistent definition now. An edit also kept only the posted values and dropped an inbound floor; it now updates them and keeps the rest.
922 lines
59 KiB
HTML
922 lines
59 KiB
HTML
{% load i18n %}
|
|
{% load django_bootstrap5 %}
|
|
{% load bootstrap_icons %}
|
|
<div class="tab-pane" id="settings" role="tabpanel">
|
|
<!-- Nav tabs -->
|
|
<ul class="nav nav-tabs" role="tablist" aria-label="Instance settings">
|
|
{% if instance.guest_agent_ready %}
|
|
<li class="nav-item" role="presentation">
|
|
<button class="px-1 nav-link" data-bs-toggle="tab" id="osinfo-tab" data-bs-target="#osinfo" type="button" role="tab" aria-controls="osinfo" aria-selected="false" onclick="get_osinfo();">
|
|
<span>{% bs_icon 'info-circle' %} </span>
|
|
</button>
|
|
</li>
|
|
{% endif %}
|
|
{% if request.user.is_superuser %}
|
|
<li class="nav-item" role="presentation">
|
|
<button class="nav-link active" data-bs-toggle="tab" data-bs-target="#boot_opt" type="button" role="tab" aria-controls="boot_opt" aria-selected="true">
|
|
{% trans "Boot" %}
|
|
</button>
|
|
</li>
|
|
<li class="nav-item" role="presentation">
|
|
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#disks" type="button" role="tab" aria-controls="disks" aria-selected="false">
|
|
{% trans "Disk" %}
|
|
</button>
|
|
</li>
|
|
{% endif %}
|
|
{% if can_manage_console %}
|
|
<li class="nav-item" role="presentation">
|
|
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#vncsettings" type="button" role="tab" aria-controls="vncsettings" aria-selected="false">
|
|
{% trans "Console" %}
|
|
</button>
|
|
</li>
|
|
{% endif %}
|
|
{% if request.user.is_superuser %}
|
|
<li class="nav-item" role="presentation">
|
|
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#network" type="button" role="tab" aria-controls="network" aria-selected="false">
|
|
{% trans "Network" %}
|
|
</button>
|
|
</li>
|
|
{% endif %}
|
|
{% if can_clone %}
|
|
<li class="nav-item" role="presentation">
|
|
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#clone" type="button" role="tab" aria-controls="clone" aria-selected="false">
|
|
{% trans "Clone" %}
|
|
</button>
|
|
</li>
|
|
{% endif %}
|
|
{% if request.user.is_superuser %}
|
|
<li class="nav-item" role="presentation">
|
|
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#migrate" type="button" role="tab" aria-controls="migrate" aria-selected="false">
|
|
{% trans "Migrate" %}
|
|
</button>
|
|
</li>
|
|
<li class="nav-item" role="presentation">
|
|
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#xmledit" type="button" role="tab" aria-controls="xmledit" aria-selected="false">
|
|
{% trans "XML" %}
|
|
</button>
|
|
</li>
|
|
{% endif %}
|
|
{% if can_change %}
|
|
<li class="nav-item" role="presentation">
|
|
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#options" type="button" role="tab" aria-controls="options" aria-selected="false">
|
|
{% trans "Options" %}
|
|
</button>
|
|
</li>
|
|
{% endif %}
|
|
{% if request.user.is_superuser %}
|
|
<li class="nav-item" role="presentation">
|
|
<button class="px-1 nav-link" data-bs-toggle="tab" data-bs-target="#users" type="button" role="tab" aria-controls="users" aria-selected="false">
|
|
{% trans "Users" %}
|
|
</button>
|
|
</li>
|
|
{% endif %}
|
|
</ul>
|
|
<!-- Tab panes -->
|
|
<div class="tab-content">
|
|
{% if instance.guest_agent_ready %}
|
|
{% include 'instances/info_tab.html' %}
|
|
{% endif %}
|
|
{% if request.user.is_superuser %}
|
|
<div role="tabpanel" class="tab-pane tab-pane-bordered active" id="boot_opt">
|
|
<p class="fw-bold">{% trans 'Autostart' %}</p>
|
|
<div class="row">
|
|
<div class="col-sm-12 text-center">
|
|
<p>{% trans "Autostart your instance when host server is power on " %}
|
|
{% if instance.autostart == 0 %}
|
|
<form action="{% url 'instances:set_autostart' instance.id %}" method="post" class="d-inline">{% csrf_token %}
|
|
<button type="submit" class="btn btn-success">{% trans "Enable" %}</button>
|
|
</form>
|
|
{% else %}
|
|
<form action="{% url 'instances:unset_autostart' instance.id %}" method="post" class="d-inline">{% csrf_token %}
|
|
<button type="submit" class="btn btn-danger">{% trans "Disable" %}</button>
|
|
</form>
|
|
{% endif %}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
<p class="fw-bold">{% trans 'Boot Order' %}</p>
|
|
<div class="row">
|
|
<div class="col-sm-12 text-center">
|
|
{% if instance.status == 5 %}
|
|
<p>{% trans "Enable Boot Menu for your instance when it starts up " %}
|
|
{% if instance.bootmenu == 0 %}
|
|
<form action="{% url 'instances:set_bootmenu' instance.id %}" method="post" role="form" aria-label="Enable/disable instance boot order form">{% csrf_token %}
|
|
<input type="submit" class="btn btn-success" name="set_bootmenu" title="{% trans 'Show boot menu' %}" value="{% trans "Enable" %}">
|
|
</form>
|
|
{% else %}
|
|
<form action="{% url 'instances:unset_bootmenu' instance.id %}" method="post" role="form" aria-label="Enable/disable instance boot order form">{% csrf_token %}
|
|
<input type="submit" class="btn btn-danger" name="unset_bootmenu" title="{% trans 'Hide boot menu' %}" value="{% trans "Disable" %}">
|
|
</form>
|
|
{% endif %}
|
|
{% else %}
|
|
{% if instance.bootmenu == 0 %}
|
|
<p>**** {% trans "Please shutdown instance to modify boot menu" %} ****</p>
|
|
{% endif %}
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
{% if instance.bootmenu == 1 %}
|
|
<div class="d-flex justify-content-center">
|
|
<div class="col-sm-6 bg-light rounded shadow-sm">
|
|
{% for idx, val in instance.boot_order.items %}
|
|
<label>{{ idx|add:1 }}:{{ val.target }}, </label>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
<form action="{% url 'instances:set_bootorder' instance.id %}" method="post" role="form" aria-label="Boot order edit form">{% csrf_token %}
|
|
<input id="bootorder" name="bootorder" hidden>
|
|
<div class="d-flex justify-content-center">
|
|
<div id="b_order" class="multipleselect border-0">
|
|
{% for disk in instance.disks %}
|
|
<label><input type="checkbox" name="disk:{{ disk.dev }}" value="disk:{{ disk.dev }}" onclick="set_orderlist($('#bootorder'))" />{{ disk.dev }} - {{ disk.image }}</label>
|
|
{% endfor %}
|
|
{% for cd in instance.media %}
|
|
<label><input type="checkbox" name="cdrom:{{ cd.dev }}" value="cdrom:{{ cd.dev }}" onclick="set_orderlist($('#bootorder'))"/>{{ cd.dev }} - {{ cd.image }}</label>
|
|
{% endfor %}
|
|
{% for net in instance.networks %}
|
|
<label><input type="checkbox" name="network:{{ net.mac }}" value="network:{{ net.mac }}" onclick="set_orderlist($('#bootorder'))"/>NIC - {{ net.mac|slice:"9:" }}</label>
|
|
{% endfor %}
|
|
</div>
|
|
<div>
|
|
<div class="row mt-4">
|
|
<a href="#" id="boot_order_up" class="btn btn-light shadow-sm"><span title="{% trans 'up: move selected devices' %}">{% bs_icon 'arrow-up' %} </span></a>
|
|
</div>
|
|
<div class="row mt-2">
|
|
<a href="#" id="boot_order_down" class="btn btn-light shadow-sm"><span title="{% trans 'down: move selected devices' %}">{% bs_icon 'arrow-down' %} </span></a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="d-flex justify-content-center">
|
|
<div class="d-grid col-sm-6">
|
|
<input type="submit" class="btn btn-success" name="set_bootorder" value="{% trans "Apply" %}">
|
|
</div>
|
|
</div>
|
|
</form>
|
|
{% endif %}
|
|
<div class="clearfix"></div>
|
|
</div>
|
|
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="disks">
|
|
<form action="{% url 'instances:add_cdrom' instance.id %}" method="post" role="form" aria-label="Add CD-ROM form">{% csrf_token %}
|
|
<p class="fw-bold">
|
|
{% trans "Instance Media" %}
|
|
<button class="btn btn-success float-end"
|
|
type="submit" type="button"
|
|
title="{% trans 'Add CD-ROM' %}"
|
|
{% if instance.status != 5 %} disabled {% endif %}>
|
|
<span>{% bs_icon 'plus' %} </span>
|
|
</button>
|
|
</p>
|
|
</form>
|
|
<div class="clearfix"></div>
|
|
{% for cd in instance.media %}
|
|
<div class="row mt-2">
|
|
<div class="col-sm-12">
|
|
{% if not cd.image %}
|
|
<form action="{% url 'instances:mount_iso' instance.id %}" method="post">
|
|
{% csrf_token %}
|
|
<div class="input-group">
|
|
<a class="btn btn-secondary"
|
|
role="button"
|
|
name="details"
|
|
title="{% trans 'Details' %}"
|
|
tabindex="0"
|
|
data-trigger="focus"
|
|
data-bs-toggle="popover"
|
|
data-bs-html="true"
|
|
data-bs-content="<strong>{% trans 'Bus' %}:</strong> {{ cd.bus }} <br/>
|
|
<strong>{% trans 'Device' %}:</strong> {{ cd.dev }}">
|
|
{% trans "CD-ROM" %} {{ forloop.counter }}
|
|
</a>
|
|
<select name="media" class="form-select">
|
|
{% if instance.media_iso %}
|
|
{% for iso in instance.media_iso %}
|
|
<option value="{{ iso }}">{{ iso }}</option>
|
|
{% endfor %}
|
|
{% else %}
|
|
<option value="none">{% trans "None" %}</option>
|
|
{% endif %}
|
|
</select>
|
|
|
|
{% if instance.media_iso %}
|
|
<button type="submit" class="btn btn-sm btn-success" name="mount_iso" value="{{ cd.dev }}">{% trans "Mount" %}</button>
|
|
{% else %}
|
|
<button class="btn btn-sm btn-success disabled">{% trans "Mount" %}</button>
|
|
{% endif %}
|
|
{% if instance.status == 5 %}
|
|
<button type="submit" class="btn btn-sm btn-danger float-end" formaction="{% url 'instances:detach_cdrom' instance.id cd.dev %}" formmethod="post" title="{% trans "Detach CD-ROM (remove device)" %}">
|
|
{% bs_icon 'dash-circle' %}
|
|
</button>
|
|
{% endif %}
|
|
|
|
</div>
|
|
</form>
|
|
{% else %}
|
|
<form action="{% url 'instances:unmount_iso' instance.id %}" method="post">
|
|
{% csrf_token %}
|
|
<div class="input-group">
|
|
<a class="btn btn-secondary"
|
|
role="button"
|
|
name="details"
|
|
title="{% trans 'Details' %}"
|
|
tabindex="0"
|
|
data-trigger="focus"
|
|
data-bs-toggle="popover"
|
|
data-bs-html="true"
|
|
data-bs-content="<strong>{% trans 'Bus' %}:</strong> {{ cd.bus }} <br/>
|
|
<strong>{% trans 'Device' %}:</strong> {{ cd.dev }}">
|
|
{% trans "CD-ROM" %} {{ forloop.counter }}
|
|
</a>
|
|
<input type="text" class="form-control" value="{{ cd.image }}" disabled/>
|
|
<input type="hidden" name="path" value="{{ cd.path }}">
|
|
<button type="submit" class="btn btn-sm btn-success" value="{{ cd.dev }}" name="umount_iso">{% trans "Unmount" %}</button>
|
|
</div>
|
|
</form>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
{% empty %}
|
|
<div class="offset-3 col-sm-6">
|
|
<div class="bg-warning rounded shadow-sm">{% trans 'There is not any CD-ROM device.' %}</div>
|
|
</div>
|
|
{% endfor %}
|
|
<div class="clearfix"></div>
|
|
<p class="fw-bold">
|
|
{% trans "Instance Volume" %}
|
|
{% include 'add_instance_volume.html' %}
|
|
</p>
|
|
<table class="table table-hover">
|
|
<thead>
|
|
<tr>
|
|
<th scope="col">{% trans "Device" %}</th>
|
|
<th scope="col">{% trans "Used" %}</th>
|
|
<th scope="col">{% trans "Capacity" %}</th>
|
|
<th scope="col">{% trans "Storage" %}</th>
|
|
<th scope="col">{% trans "Source" %}</th>
|
|
<th scope="col">{% trans "Action" %}</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for disk in instance.config_disks %}
|
|
<tr>
|
|
<td>
|
|
<button type="submit" class="btn btn-sm btn-secondary"
|
|
name="details{{ forloop.counter0 }}"
|
|
title="{% trans 'Details' %}"
|
|
tabindex="1"
|
|
data-trigger="focus"
|
|
data-bs-toggle="popover"
|
|
data-bs-html="true"
|
|
data-bs-content="<strong>{% trans 'Bus' %}:</strong> {{ disk.bus }} <br/>
|
|
<strong>{% trans 'Format' %}:</strong> {{ disk.format }} <br/>
|
|
<strong>{% trans 'Cache' %}:</strong> {{ disk.cache }} <br/>
|
|
<strong>{% trans 'Serial' %}:</strong> {{ disk.serial }} <br/>
|
|
<strong>{% trans 'Readonly' %}:</strong> {{ disk.readonly }} <br/>
|
|
<strong>{% trans 'Shareable' %}:</strong> {{ disk.shareable }}</br>
|
|
<strong>{% trans 'IO Mode' %}:</strong> {{ disk.io }} <br/>
|
|
<strong>{% trans 'Discard' %}:</strong> {{ disk.discard }} <br/>
|
|
<strong>{% trans 'Detect Zeroes' %}:</strong> {{ disk.detect_zeroes }}">
|
|
<span>{% bs_icon 'info' %} </span>
|
|
</button>
|
|
{{ disk.dev }}
|
|
</td>
|
|
{% if disk.storage is None %}
|
|
<td colspan="4">
|
|
<div class="alert alert-danger">
|
|
{% trans "Error getting disk info" %}
|
|
</div>
|
|
</td>
|
|
{% else %}
|
|
<td>{{ disk.used | filesizeformat}}</td>
|
|
<td>{{ disk.size | filesizeformat }}</td>
|
|
<td>{{ disk.storage }}</td>
|
|
<td>{{ disk.path }}</td>
|
|
{% endif %}
|
|
<td class="text-nowrap">
|
|
<form class="d-inline" action="{% url 'instances:edit_volume' instance.id %}" method="post" role="form" aria-label="Edit instance volume form">
|
|
{% csrf_token %}
|
|
<input type="hidden" name="path" value="{{ disk.path }}">
|
|
<input type="hidden" name="dev" value="{{ disk.dev }}">
|
|
<input type="hidden" name="storage" value="{{ disk.storage }}">
|
|
<input type="hidden" name="name" value="{{ disk.image }}">
|
|
{% include 'instances/edit_instance_volume.html' with id=forloop.counter0 %}
|
|
</form>
|
|
<form class="d-inline" action="{% url 'instances:detach_vol' instance.id %}" method="post">
|
|
{% csrf_token %}
|
|
<input type="hidden" name="path" value="{{ disk.path }}">
|
|
<input type="hidden" name="dev" value="{{ disk.dev }}">
|
|
<input type="hidden" name="storage" value="{{ disk.storage }}">
|
|
<input type="hidden" name="name" value="{{ disk.image }}">
|
|
{% if instance.status == 5 %}
|
|
<button type="submit" class="btn btn-sm btn-secondary" value="{{ disk.dev }}" title="{% trans "Detach" %}" onclick="return confirm('{% trans "Are you sure to detach volume?" %}')">
|
|
{% bs_icon 'eject' %}
|
|
</button>
|
|
{% else %}
|
|
<button class="btn btn-sm btn-secondary disabled" value="{{ disk.dev }}" title="{% trans "Detach" %}" onclick="return confirm('{% trans "Are you sure? This may lead data corruption!" %}')">
|
|
{% bs_icon 'eject' %}
|
|
</button>
|
|
{% endif %}
|
|
</form>
|
|
<form class="d-inline" action="{% url 'instances:delete_vol' instance.id %}" method="post">
|
|
{% csrf_token %}
|
|
<input type="hidden" name="path" value="{{ disk.path }}">
|
|
<input type="hidden" name="dev" value="{{ disk.dev }}">
|
|
<input type="hidden" name="storage" value="{{ disk.storage }}">
|
|
<input type="hidden" name="name" value="{{ disk.image }}">
|
|
{% if instance.status == 5 %}
|
|
<button type="submit" class="btn btn-sm btn-secondary" title="{% trans "Delete" %}" onclick="return confirm('{% trans "Are you sure to delete volume?" %}')">
|
|
{% bs_icon 'trash' %}
|
|
</button>
|
|
{% else %}
|
|
<button class="btn btn-sm btn-secondary disabled" title="{% trans "Delete" %}" onclick="return confirm('{% trans "Are you sure? This may lead data corruption!" %}')">
|
|
{% bs_icon 'trash' %}
|
|
</button>
|
|
{% endif %}
|
|
</form>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
<div class="clearfix"></div>
|
|
</div>
|
|
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="network">
|
|
<p>
|
|
{% trans "Add a network device" %}
|
|
{% include 'add_instance_network_block.html' %}
|
|
</p>
|
|
|
|
<div class="row mt-3">
|
|
<div class="col-lg-12 mt-3">
|
|
<h5 class="fw-bold">{% trans "Network Devices" %}</h5>
|
|
<table class="table">
|
|
<thead>
|
|
<tr>
|
|
<th scope="col">{% trans 'Name' %}</th>
|
|
<th scope="col" class="d-none d-table-cell d-sm-table-cell" colspan="2">{% trans 'Info' %}</th>
|
|
<th scope="colgroup" colspan="3" class="col-sm-2">{% trans 'Actions' %}</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for network in instance.config_networks %}
|
|
<tr>
|
|
<td rowspan="2">eth{{ forloop.counter0 }}({{ network.target|default:"no target" }})
|
|
<form action="{% url 'instances:set_link_state' instance.id %}" method="post" aria-label="set instance link state form">
|
|
{% csrf_token %}
|
|
<input name="mac" value="{{ network.mac }}" hidden/>
|
|
<input name="set_link_state" value="{{ network.state }}" hidden/>
|
|
<input type="checkbox" {% if network.state == 'up' %} checked{% endif %} onclick='submit();' />
|
|
<strong>{% trans 'active' %}</strong>
|
|
<small>{{ network.type }}</small>
|
|
</form>
|
|
</td>
|
|
<td>
|
|
<div class="input-group">
|
|
<span class="input-group-text">{% trans 'MAC' %}</span>
|
|
<input type="text" class="form-control" value="{{ network.mac }}" aria-label="mac" aria-describedby="mac" readonly>
|
|
</div>
|
|
</td>
|
|
<td class="d-none d-table-cell">
|
|
<div class="input-group">
|
|
<span class="input-group-text">{% trans 'Filter' %}</span>
|
|
<input type="text" class="form-control" value="{{ network.filterref|default:'None' }}" aria-label="filter" aria-describedby="filter" readonly>
|
|
</div>
|
|
</td>
|
|
<td>
|
|
<div class="input-group">
|
|
<span class="input-group-text">{% trans 'Source' %}</span>
|
|
<input type="text" class="form-control" value="{{ network.nic }}" aria-label="source" aria-describedby="source" readonly>
|
|
</div>
|
|
</td>
|
|
<td>
|
|
<form action="{% url 'instances:change_network' instance.id %}" method="post" name="edit_network{{ forloop.counter0 }}" role="form">{% csrf_token %}
|
|
<button class="btn btn-sm btn-primary"
|
|
type="button"
|
|
title="{% trans "Edit NIC" %}"
|
|
data-bs-target="#editInstanceNetwork{{ forloop.counter0 }}"
|
|
data-bs-toggle="modal">
|
|
<span aria-hidden="true">{% bs_icon 'pencil-square' %} </span>
|
|
</button>
|
|
|
|
<div class="modal fade" id="editInstanceNetwork{{ forloop.counter0 }}" role="dialog" aria-labelledby="editInstanceNetworkLabel" aria-hidden="true">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">{% trans "Edit Instance Network" %}</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div class="container">
|
|
<div class="row">
|
|
<label class="col-form-label">{% trans "MAC" %}</label>
|
|
<div class="input-group">
|
|
<input class="form-control" type="text" name="net-old-mac-{{ forloop.counter0 }}" value="{{ network.mac }}" readonly/>
|
|
<input class="form-control" type="text" name="net-mac-{{ forloop.counter0 }}" value="{{ network.mac }}"/>
|
|
</div>
|
|
</div>
|
|
<div class="row">
|
|
<label class="col-form-label">{% trans "Net Source" %}</label>
|
|
<div class="input-group">
|
|
<input class="form-control" type="text" value="{{ network.nic }}" readonly/>
|
|
<select class="form-select" name="net-source-{{ forloop.counter0 }}">
|
|
{% for c_net in networks_host %}
|
|
<option value="net:{{ c_net }}" {% if c_net == network.nic %} selected {% endif %}>{% trans 'Network' %} {{ c_net }}</option>
|
|
{% endfor %}
|
|
{% for c_iface in interfaces_host %}
|
|
<option value="iface:{{ c_iface }}" {% if c_iface == network.nic %} selected {% endif %}>{% trans 'Interface' %} {{ c_iface }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div class="row">
|
|
<label class="col-form-label">{% trans "NWFilter" %}</label>
|
|
<div class="input-group">
|
|
<input class="form-control" type="text" value="{{ network.filterref }}" readonly/>
|
|
<select class="form-select" name="net-nwfilter-{{ forloop.counter0 }}">
|
|
<option value="">{% trans "None" %}</option>
|
|
{% for c_filters in nwfilters_host %}
|
|
<option value="{{ c_filters }}" {% if c_filters == network.filterref %} selected {% endif %}>{{ c_filters }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div class="row">
|
|
<label class="col-form-label">{% trans "Model" %} </label>
|
|
<div class="input-group">
|
|
<input class="form-control" type="text" value="{{ network.model }}" readonly/>
|
|
<select class="form-select" name="net-model-{{ forloop.counter0 }}">
|
|
{% for model in net_models_host %}
|
|
<option value="{{ model }}" {% if model == network.model %} selected {% endif %}>{{ model }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button class="btn btn-secondary" type="button" data-bs-dismiss="modal">{% trans 'Close' %}</button>
|
|
<button class="btn btn-success" name="change_network" title="{% trans "Apply network changes" %}">{% trans "Apply" %}</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</td>
|
|
<td>
|
|
<form action="{% url 'instances:set_qos' instance.id %}" method="post" name="add_qos{{ forloop.counter0 }}" role="form" aria-label="Add network qos form">
|
|
{% csrf_token %}
|
|
<input type="text" name="net-mac-{{ forloop.counter0 }}" value="{{ network.mac }}" hidden/>
|
|
{% include 'add_network_qos.html' with id=forloop.counter0 %}
|
|
</form>
|
|
</td>
|
|
<td>
|
|
<form action="{% url 'instances:delete_network' instance.id %}" method="post" name="delete_network" role="form">{% csrf_token %}
|
|
<button class="btn btn-sm btn-danger" value="{{ network.mac }}" name="delete_network" title="{% trans "Delete Device" %}"
|
|
onclick="return confirm('{% trans "Are you sure?" %}')">
|
|
{% bs_icon 'trash' %}
|
|
</button>
|
|
</form>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td>
|
|
<div class="input-group">
|
|
<span class="input-group-text">{% trans 'IPv4' %}</span>
|
|
<input type="text" class="form-control" value="{% for ipv4 in network.ipv4|default:'unknown' %}{{ ipv4 }}{% endfor %}" aria-label="ipv4" aria-describedby="ipv4" readonly>
|
|
</div>
|
|
</td>
|
|
<td>
|
|
<div class="input-group">
|
|
<span class="input-group-text">{% trans 'IPv6' %}</span>
|
|
<input type="text" class="form-control" value="{% for ipv6 in network.ipv6|default:'unknown' %}{{ ipv6 }}{% endfor %}" aria-label="ipv6" aria-describedby="ipv6" readonly>
|
|
</div>
|
|
</td>
|
|
<td colspan="3">
|
|
<div class="input-group">
|
|
<span class="input-group-text px-2">{% trans 'Model' %}</span>
|
|
<input type="text" class="form-control px-2" value="{{ network.model }}" aria-label="model" aria-describedby="model" readonly>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="py-1 bg-primary text-white" colspan="9">
|
|
{% if network.type == 'direct' %}
|
|
<small>{% trans 'In most configurations, macvtap does not work for host to guest network communication' %}</small>
|
|
{% endif %}
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
{% if instance.config_qos %}
|
|
<div class="row mt-3">
|
|
<div class="col-sm-10">
|
|
<p><strong>{% trans "QoS Configuration" %}</strong></p>
|
|
</div>
|
|
<div class="col-12 col-sm-12">
|
|
<table class="table table-hover">
|
|
<thead>
|
|
<tr class="d-flex">
|
|
<th scope="col" class="col-2">{% trans "MAC" %}/{% trans "Direction" %}</th>
|
|
<th scope="col" class="col-2">{% trans "Average" %}</th>
|
|
<th scope="col" class="col-3">{% trans "Peak" %}</th>
|
|
<th scope="col" class="col-3">{% trans "Burst" %}</th>
|
|
<th scope="col" class="col-2">{% trans "Actions" %}</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for q, attrs in instance.config_qos.items %}
|
|
{% for att in attrs %}
|
|
<tr class="d-flex">
|
|
<form action="{% url 'instances:set_qos' instance.id %}" method="post" role="form" aria-label="Instance QoS configuration form">
|
|
{% csrf_token %}
|
|
<td class="col-2"><label class="col-form-label">{{ q }} {{ att.direction | capfirst }}</label></td>
|
|
<td class="col-2"><input id="qos_average" class="form-control" name="qos_average"
|
|
value="{{ att.average|default:'' }}"/>
|
|
</td>
|
|
<td class="col-3"><input id="qos_peak" class="form-control" name="qos_peak"
|
|
value="{{ att.peak|default:'' }}"/>
|
|
</td>
|
|
<td class="col-3"><input id="qos_burst" class="form-control" name="qos_burst"
|
|
value="{{ att.burst|default:'' }}"/>
|
|
</td>
|
|
<td class="col-sm-2">
|
|
<input name="qos_direction" value="{{ att.direction }}" hidden/>
|
|
<input name="net-mac" value="{{ q }}" hidden/>
|
|
<button type="submit" class="btn btn-sm btn-primary"
|
|
name="set_qos" data-bs-toggle="modal"
|
|
title="{% trans "Edit QoS" %}" onclick="return confirm('{% trans "Are you sure?" %}')">
|
|
<span>{% bs_icon 'save' %} </span>
|
|
</button>
|
|
</form>
|
|
<form action="{% url 'instances:unset_qos' instance.id %}" method="post" role="form" aria-label="Instance QoS configuration form">
|
|
{% csrf_token %}
|
|
<input name="qos_direction" value="{{ att.direction }}" hidden/>
|
|
<input name="net-mac" value="{{ q }}" hidden/>
|
|
<button type="submit" class="btn btn-sm btn-danger"
|
|
name="unset_qos"
|
|
title="{% trans "Delete QoS" %}" onclick="return confirm('{% trans "Are you sure?" %}')">
|
|
{% bs_icon 'trash' %}
|
|
</button>
|
|
</form>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
<div class="clearfix"></div>
|
|
</div>
|
|
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="migrate">
|
|
<p>{% trans "For migration both host servers must have equal settings and OS type" %}</p>
|
|
<form action="{% url 'instances:migrate' instance.id %}" class="ms-3 form" method="post" role="form" aria-label="Migrate instance form">
|
|
{% csrf_token %}
|
|
<div class="row">
|
|
<label class="col-sm-3 col-form-label">{% trans "Original host" %}</label>
|
|
<div class="col-sm-6">
|
|
<label class="form-control" readonly="readonly">{{ compute.name }}</label>
|
|
</div>
|
|
</div>
|
|
<div class="row">
|
|
<label class="col-sm-3 col-form-label">{% trans "Host migration" %}</label>
|
|
<div class="col-sm-6">
|
|
<select name="compute_id" class="form-select">
|
|
{% if computes_count != 1 %}
|
|
{% for comp in computes %}
|
|
{% if comp.id != compute.id %}
|
|
<option value="{{ comp.id }}">{{ comp.name }}</option>
|
|
{% endif %}
|
|
{% endfor %}
|
|
{% endif %}
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div class="row">
|
|
<div class="col-sm-6 offset-3">
|
|
<div class="form-check form-switch">
|
|
<input class="form-check-input" type="checkbox" name="live_migrate" value="true" id="vm_live_migrate" {% if instance.status != 5 %}checked{% else %}disabled{% endif %}>
|
|
<label class="form-check-label" for="vm_live_migrate">{% trans "Live migration" %}</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="row">
|
|
<div class="col-sm-6 offset-3">
|
|
<div class="form-check form-switch">
|
|
<input class="form-check-input" type="checkbox" name="unsafe_migrate" value="true" id="vm_unsafe_migrate">
|
|
<label class="form-check-label" for="vm_unsafe_migrate">{% trans "Unsafe migration" %}</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="row">
|
|
<div class="col-sm-6 offset-3">
|
|
<div class="form-check form-switch">
|
|
<input class="form-check-input" type="checkbox" name="offline_migrate" value="true" id="offline_migrate" {% if instance.status == 5 %}checked{% else %}disabled{% endif %}>
|
|
<label class="form-check-label" for="offline_migrate">{% trans "Offline migration" %}</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="row">
|
|
<div class="col-sm-6 offset-3">
|
|
<div class="form-check form-switch">
|
|
<input class="form-check-input" type="checkbox" name="autoconverge" value="true" id="autoconverge" {% if instance.status != 1 %}disabled{% else %}checked{% endif %}>
|
|
<label class="form-check-label" for="autoconverge" title="{% trans 'Forces CPU convergence during live migration' %}">{% trans "Auto converge" %}</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="row">
|
|
<div class="col-sm-6 offset-3">
|
|
<div class="form-check form-switch">
|
|
<input class="form-check-input" type="checkbox" name="compress" value="true" id="compress" {% if instance.status != 1 %}disabled{% endif %}>
|
|
<label class="form-check-label" for="compress" title="{% trans 'Compress instance memory for fast migration' %}">{% trans "Compressed" %}</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% if computes_count != 1 %}
|
|
<button type="submit" class="btn btn-lg btn-success float-end" onclick="showPleaseWaitDialog()" name="migrate" >{% trans "Migrate" %}</button>
|
|
{% else %}
|
|
<button class="btn btn-lg btn-success float-end disabled">{% trans "Migrate" %}</button>
|
|
{% endif %}
|
|
</form>
|
|
<div class="clearfix"></div>
|
|
</div>
|
|
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="xmledit">
|
|
{% if instance.status != 5 %}
|
|
<p>{% trans "If you need to edit XML please Power Off the instance" %}</p>
|
|
{% endif %}
|
|
<form action="{% url 'instances:change_xml' instance.id %}" method="post" role="form" aria-label="Edit instance XML form">
|
|
{% csrf_token %}
|
|
<div class="col-sm-12" id="xmlheight">
|
|
<textarea id="editor">{{ instance.inst_xml }}</textarea>
|
|
</div>
|
|
{% if instance.status == 5 %}
|
|
<input type="hidden" name="inst_xml">
|
|
<button type="submit" class="btn btn-lg btn-success float-end" name="change_xml">
|
|
{% trans "Change" %}
|
|
</button>
|
|
{% else %}
|
|
<button class="btn btn-lg btn-success float-end disabled">
|
|
{% trans "Change" %}
|
|
</button>
|
|
{% endif %}
|
|
</form>
|
|
<div class="clearfix"></div>
|
|
</div>
|
|
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="users">
|
|
<div>
|
|
<p class="fw-bold">
|
|
{% trans "Instance owners" %}
|
|
{% include 'add_instance_owner_block.html' %}
|
|
</p>
|
|
</div>
|
|
<div class="table-responsive">
|
|
<table class="table table-striped sortable-theme-bootstrap mt-3" data-sortable>
|
|
<tbody class="searchable">
|
|
{% for userinstance in userinstances %}
|
|
<tr>
|
|
<td><a href="{% url 'accounts:account' userinstance.user.id %}">{{ userinstance.user }}</a></td>
|
|
<td style="width:30px;">
|
|
<a href="{% url 'accounts:user_instance_delete' userinstance.id %}?next={% url 'instances:instance' instance.id %}#users">
|
|
{% bs_icon 'trash' %}
|
|
</a>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<div class="clearfix"></div>
|
|
</div>
|
|
{% endif %}
|
|
{% if can_manage_console %}
|
|
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="vncsettings">
|
|
<form method="post" action="{% url 'instances:update_console' instance.id %}">
|
|
{% csrf_token %}
|
|
{% if instance.status != 5 %}
|
|
<div class="alert alert-warning">
|
|
{% trans "To change console settings, shutdown the instance." %}
|
|
</div>
|
|
{% endif %}
|
|
{% bootstrap_form console_form layout='horizontal' %}
|
|
<div class="float-end">
|
|
{% if instance.status != 5 %}
|
|
<button class="btn btn-success" disabled>{% trans "Update" %}</button>
|
|
{% else %}
|
|
<button type="submit" class="btn btn-success ">{% trans "Update" %}</button>
|
|
{% endif %}
|
|
</div>
|
|
</form>
|
|
<div class="clearfix"></div>
|
|
</div>
|
|
{% endif %}
|
|
{% if can_clone %}
|
|
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="clone">
|
|
<p class="fw-bold">{% trans "Create a clone" %}</p>
|
|
<form class="form" action="{% url 'instances:clone' instance.id %}" method="post" role="form" aria-label="Create clone form">{% csrf_token %}
|
|
<div class="row">
|
|
<label class="col-sm-3 col-form-label fw-normal">{% trans "Clone Name" %}</label>
|
|
{% if request.user.is_superuser %}
|
|
<div class="col-sm-6">
|
|
<div class="input-group">
|
|
<input id="clone_name" type="text" class="form-control" name="name" value="{{ instance.name }}-clone">
|
|
<button type="button" class="btn btn-success" name="guess-clone-name"
|
|
onclick="guess_clone_name()">{% trans "Guess" %}</button>
|
|
</div>
|
|
</div>
|
|
{% elif app_settings.CLONE_INSTANCE_AUTO_NAME == 'True'%}
|
|
<div class="col-sm-4">
|
|
<input id="clone_instance_auto_name" type="text" class="form-control" name="auto_name" value="Automatic" disabled/>
|
|
</div>
|
|
{% else %}
|
|
<div class="col-sm-4">
|
|
<select id="select_clone_name" class="form-control" name="name" size="1">
|
|
{% for name in clone_free_names %}
|
|
<option value="{{ name }}">{{ name }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
{% if request.user.is_superuser %}
|
|
<label>{% trans "Network devices" %}:</label>
|
|
{% for network in instance.config_networks %}
|
|
<p>
|
|
<div class="row">
|
|
<label class="col-sm-2 col-form-label offset-1">eth{{ forloop.counter0 }} ({{ network.nic }})</label>
|
|
<div class="col-sm-6">
|
|
<div class="input-group">
|
|
<input type="text" class="form-control" name="clone-net-mac-{{ forloop.counter0 }}" value="{{ network.mac }}"/>
|
|
<button type="button" class="btn btn-success" name="random-mac-{{ forloop.counter0 }}"
|
|
onclick="random_mac('clone-net-mac-{{ forloop.counter0 }}')">{% trans "Random" %}</button>
|
|
<button type="button" class="btn btn-success" name="guess-mac-{{ forloop.counter0 }}"
|
|
onclick="guess_mac_address('#clone_name', {{ forloop.counter0 }})">{% trans "Guess" %}</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</p>
|
|
{% endfor %}
|
|
{% else %}
|
|
{% for network in instance.config_networks %}
|
|
<input type="hidden" class="form-control" name="clone-net-mac-{{ forloop.counter0 }}" value="{{ network.mac }}"/>
|
|
{% endfor %}
|
|
{% endif %}
|
|
{% if request.user.is_superuser %}
|
|
<label>{% trans "Storage devices" %}:</label>
|
|
{% for disk in instance.config_disks %}
|
|
<div class="row">
|
|
<label class="col-sm-2 col-form-label offset-1">{{ disk.dev }} ({{ disk.storage }})</label>
|
|
<div class="col-sm-6">
|
|
<div class="input-group mb-3">
|
|
<input id="disk_name-{{ disk.dev }}" type="text" class="form-control" name="disk-{{ disk.dev }}" value="{{ disk.image }}"/>
|
|
{% if disk.format == 'qcow2' %}
|
|
<span class="input-group-text" >{% trans 'Metadata' %}</span>
|
|
<div class="input-group-text">
|
|
<input type="checkbox" name="meta-{{ disk.dev }}" value="true">
|
|
</div>
|
|
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
{% else %}
|
|
{% for disk in instance.disks %}
|
|
<input id="disk_name-{{ disk.dev }}" type="hidden" class="form-control" name="disk-{{ disk.dev }}" value="{{ disk.image }}"/>
|
|
{% endfor %}
|
|
{% endif %}
|
|
<div class="row mb-3">
|
|
<label class="col-sm-3 col-form-label">{% trans "Title" %}</label>
|
|
<div class="col-sm-6">
|
|
<input type="text" name="clone-title" class="form-control">
|
|
</div>
|
|
</div>
|
|
<div class="row">
|
|
<label class="col-sm-3 col-form-label">{% trans "Description" %}</label>
|
|
<div class="col-sm-6">
|
|
<textarea name="clone-description" class="form-control"></textarea>
|
|
</div>
|
|
</div>
|
|
{% if instance.status == 5 %}
|
|
<button type="submit" class="btn btn-lg btn-success float-end" name="clone" onclick="showPleaseWaitDialog();">{% trans "Clone" %}</button>
|
|
{% else %}
|
|
<button class="btn btn-lg btn-success float-end disabled" name="clone">{% trans "Clone" %}</button>
|
|
{% endif %}
|
|
</form>
|
|
<div class="clearfix"></div>
|
|
</div>
|
|
{% endif %}
|
|
{% if can_change %}
|
|
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="options">
|
|
<p>{% trans "To set instance template name description, shutdown the instance." %}</p>
|
|
<form action="{% url 'instances:change_options' instance.id %}" method="post" role="form" aria-label="Set instance template name description form">{% csrf_token %}
|
|
<div class="row">
|
|
<label class="col-sm-3 col-form-label">{% trans "Title" %}</label>
|
|
<div class="col-sm-6">
|
|
<input type="text" name="title" class="form-control" value="{{ instance.title }}">
|
|
</div>
|
|
</div>
|
|
<div class="row">
|
|
<label class="col-sm-3 col-form-label">{% trans "Description" %}</label>
|
|
<div class="col-sm-6">
|
|
<textarea name="description" class="form-control">{{ instance.description }}</textarea>
|
|
</div>
|
|
</div>
|
|
<div class="row">
|
|
<label class="col-sm-3 col-form-label">{% trans "Is template" %}</label>
|
|
<div class="col-sm-6">
|
|
<input type="checkbox"
|
|
name="is_template"
|
|
value="True"
|
|
id="is_template"
|
|
{% if instance.is_template %}checked{% endif %}
|
|
{% if not request.user.is_superuser and not request.user.is_staff %}disabled{% endif %}>
|
|
</div>
|
|
</div>
|
|
<div class="row">
|
|
<div class="d-grid offset-3 col-sm-6">
|
|
{% if instance.status == 5 %}
|
|
<button type="submit" class="btn btn-success" name="change_options">{% trans "Change" %}</button>
|
|
{% else %}
|
|
<button class="btn btn-success disabled" name="change_options">{% trans "Change" %}</button>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</form>
|
|
{% if request.user.is_superuser %}
|
|
<p class="fw-bold">{% trans "To set instance video model, shutdown the instance." %}</p>
|
|
<form action="{% url 'instances:set_video_model' instance.id %}" method="post" role="form" aria-label="Set instance video model form">{% csrf_token %}
|
|
<div class="row">
|
|
<label class="col-sm-3 col-form-label" for="video_model_select">{% trans "Primary Video Model" %}</label>
|
|
<div class="col-sm-6">
|
|
<div class="input-group">
|
|
<select id="video_model_select" class="form-select" name="video_model">
|
|
<option class="fw-bold" value="">{% trans "please choose" %}</option>
|
|
{% for vmodel in instance.video_models %}
|
|
<option value="{{ vmodel }}"{% if vmodel == instance.video_model %} selected{% endif %}>{{ vmodel }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
{% if instance.status == 5 %}
|
|
<button type="submit" class="btn btn-success" name="set_video_model">{% trans "Set" %}</button>
|
|
{% else %}
|
|
<button class="btn btn-success disabled" name="set_video_model">{% trans "Set" %}</button>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
|
|
<p class="fw-bold">{% trans "To set instance vCPUs hotpluggable" %}</p>
|
|
<form action="{% url 'instances:set_vcpu_hotplug' instance.id %}" method="post" role="form" aria-label="Set instance vCPUs hotpluggable form">{% csrf_token %}
|
|
<div class="row">
|
|
<label for="vcpu_hotplug" class="col-sm-3 col-form-label">{% trans "vCPU Hot Plug" %}</label>
|
|
<div class="col-sm-6">
|
|
<div class="input-group">
|
|
<select id="vcpu_hotplug" class="form-control" name="vcpu_hotplug">
|
|
<option value="True" {% if instance.vcpus %} selected {% endif %}>{% trans 'Enabled' %}</option>
|
|
<option value="False" {% if not instance.vcpus %} selected {% endif %}>{% trans 'Disabled' %}</option>
|
|
</select>
|
|
{% if instance.status == 5 %}
|
|
<button type="submit" class="btn btn-success" name="set_vcpu_hotplug">{% trans "Set" %}</button>
|
|
{% else %}
|
|
<button class="btn btn-success" name="set_vcpu_hotplug" disabled>{% trans "Set" %}</button>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
|
|
<p class="fw-bold">{% trans "To Enable/Disable Qemu Guest Agent. Status" %}:
|
|
{% if instance.status == 1 %}
|
|
{% if instance.guest_agent_ready %}
|
|
<label class="badge bg-success">{% trans 'Connected' %}</label>
|
|
{% else %}
|
|
<label class="badge bg-danger">{% trans 'Disconnected' %}</label>
|
|
{% endif %}</p>
|
|
{% else %}
|
|
<label class="badge bg-default">{% trans 'Unknown' %}</label>
|
|
{% endif %}
|
|
<form action="{% url 'instances:set_guest_agent' instance.id %}" method="post" role="form" aria-label="Enable/Disable Qemu Guest Agent form">{% csrf_token %}
|
|
<div class="row">
|
|
<label class="col-sm-3 col-form-label" for="guest_agent">{% trans "Qemu Guest Agent" %}</label>
|
|
<div class="col-sm-6">
|
|
<div class="input-group">
|
|
<select id="guest_agent" class="form-select" name="guest_agent">
|
|
<option value="True" {% if instance.guest_agent %} selected {% endif %}>{% trans 'Enabled' %}</option>
|
|
<option value="False" {% if not instance.guest_agent %} selected {% endif %}>{% trans 'Disabled' %}</option>
|
|
</select>
|
|
<button type="submit" class="btn btn-success" name="set_guest_agent">{% trans "Set" %}</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
{% endif %}
|
|
<div class="clearfix"></div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|