1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2026-11-09 19:56:48 +00:00
webvirtcloud/computes/templates/computes/instances.html
catborise 5bc8b1915e fix(instances): every VM state has a label and the actions it allows
The instance lists, the VM page header and the power tab knew three
libvirt states: running, paused and shut off. A VM that was blocked,
shutting down, crashed, suspended by its guest or without a state got
an empty status cell and no actions, so a crashed VM could not even be
forced off from the list. The lists also offered actions users could
not use: Suspend and Resume (views for superusers) to everyone, and
power actions to users who see every VM through the read-only
view_instances permission.

instances/states.py holds the rules in one place: the label and color
of each state, the actions each state allows, and which of them a user
may use (superusers all; a VM's owners all but suspend and resume, and
nothing on a VM an administrator suspended, as the VM page already
showed; nobody else any). Blocked is a running VM; shutting down,
crashed and suspended by the guest get Force Off, after which the VM
can be started. A shut-off template offers Clone to whoever has the
clone permission, as the clone view allows.

The templates read them through two tags (vm_states): the lists and the
VM page header show the state, instance_actions.html has one slot per
button, each enabled or disabled from the allowed actions. The power
tab offers Force Off in the states that need it.

The new tests check the rules directly and render every state in the
lists and on the VM page for a superuser, an owner and a
view_instances user; they fail on the previous code.
2026-10-05 19:34:30 +03:00

115 lines
No EOL
5.1 KiB
HTML

{% extends "base.html" %}
{% load i18n %}
{% load static %}
{% load bootstrap_icons %}
{% block title %}{% trans "Instances" %} - {{ compute.name }}{% endblock %}
{% block style %}
<link rel="stylesheet" href="{% static "css/sortable-theme-bootstrap.css" %}" />
{% endblock %}
{% block page_heading %}{{ compute.name }} - {% trans "Instances" %}{% endblock page_heading %}
{% block page_heading_extra %}
<a href="{% url 'instances:create_instance_select_type' compute.id %}"
class="btn btn-success btn-header float-end">
{% bs_icon 'plus-circle-fill' %}
</a>
{% if instances %}
{% include 'search_block.html' %}
{% endif %}
{% endblock page_heading_extra %}
{% block content %}
<div class="row">
<div class="col-lg-12">
<nav aria-label="breadcrumb">
<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">
<span class="fw-bold">{% bs_icon 'server' %} {% trans "Instances" %}</span>
</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>
</nav>
</div>
</div>
<div class="row">
<div class="col-lg-12">
{% if compute.status is not True %}
<div class="alert alert-danger shadow-sm">
{% trans 'Problem occurred with host' %} {{ compute.name }}: {{ compute.connection_error }}
</div>
{% endif %}
{% if not instances %}
{% if compute.status is True %}
<div class="alert alert-warning shadow-sm fade show">
{% bs_icon 'exclamation-triangle' %} <strong>{% trans "Warning" %}:</strong>
{% trans "Hypervisor doesn't have any Instances" %}
</div>
{% endif %}
</div>
{% else %}
<table class="table table-hover sortable-theme-bootstrap" data-sortable>
<thead>
<tr>
<th scope="col">{% trans 'Name' %}<br>{% trans 'Description' %}</th>
<th scope="col">{% trans 'User' %}</th>
<th scope="col">{% trans 'Status' %}</th>
<th scope="col">{% trans 'VCPU' %}</th>
<th scope="col" class="text-end">{% trans 'Memory' %}</th>
<th scope="col" data-sortable="false">{% trans 'Actions' %}</th>
</tr>
</thead>
<tbody class="searchable">
{% for instance in instances %}
<tr>
<td>
<a class="link-primary" href="{% url 'instances:instance' instance.id %}">{{ instance.name }}</a>
<br>
{% if instance.info %}<p class="m-0 small fst-italic">{{ instance.title }}</p>{% endif %}
</td>
<td>
<em>
{% if instance.userinstance_set.all.count > 0 %}
{{ instance.userinstance_set.all.0.user }}
{% if instance.userinstance_set.all.count > 1 %}
(+{{ instance.userinstance_set.all.count|add:"-1" }})
{% endif %}
{% endif %}
</em>
</td>
<td>
{% include 'instance_state.html' %}
</td>
<td>{{ instance.info.3 }}</td>
<td><p class="text-end">{% if instance.info %}{% widthratio instance.info.1 1024 1 %} MiB{% endif %}</p></td>
<td class="text-nowrap">
{% include 'instance_actions.html' %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
</div>
</div>
{% endblock %}
{% block script %}
<script src="{% static "js/sortable.min.js" %}"></script>
<script src="{% static 'js/filter-table.js' %}"></script>
{% endblock %}