mirror of
https://github.com/retspen/webvirtcloud
synced 2026-11-08 19:26:48 +00:00
The tab put Power Off and Suspend, then Power Cycle and Force Off, in joined button groups with a paragraph under each, so which sentence told which button was left to the reader, and the two power cuts looked like one control as large as the rest. Now the tab shows the three states a VM moves between, Active, Suspended and Off, the current one marked, and under each the action that takes the VM there with one line on what it does. The power cuts are small outlined buttons on a line of their own with one warning; their confirmations stay. A state outside the three (shutting down, crashed) is named above them. The test of the tab now finds the outlined power-cut buttons, which its class match missed.
66 lines
2.5 KiB
Python
66 lines
2.5 KiB
Python
"""
|
|
libvirt VM states: their labels, and the actions each one allows a user.
|
|
The lists and the VM page use these through templatetags/vm_states.py.
|
|
"""
|
|
|
|
from django.utils.translation import gettext_lazy as _
|
|
|
|
# state (virDomainState): label, Bootstrap color
|
|
STATES = {
|
|
0: (_("No state"), "secondary"),
|
|
1: (_("Active"), "success"),
|
|
2: (_("Blocked"), "success"), # running, waiting for a resource
|
|
3: (_("Suspended"), "warning"),
|
|
4: (_("Shutting down"), "warning"),
|
|
5: (_("Off"), "secondary"), # shut off is not a fault
|
|
6: (_("Crashed"), "danger"),
|
|
7: (_("PM suspended"), "warning"), # suspended by the guest
|
|
}
|
|
UNKNOWN = (_("Unknown"), "secondary") # the VM cannot be read
|
|
|
|
RUNNING = {"suspend", "poweroff", "powercycle", "force_off", "console"}
|
|
# the states the power tab shows as its strip: active, blocked, suspended, off, PM suspended
|
|
POWER_STATES = (1, 2, 3, 5, 7)
|
|
STATE_ACTIONS = {
|
|
1: RUNNING,
|
|
2: RUNNING,
|
|
3: {"resume", "force_off"},
|
|
5: {"poweron"},
|
|
# shutting down, crashed, suspended by the guest: a forced power off ends
|
|
# it, and the VM can be started again
|
|
4: {"force_off"},
|
|
6: {"force_off"},
|
|
7: {"force_off"},
|
|
}
|
|
SUPERUSER_ONLY = {"suspend", "resume"} # their views are for superusers
|
|
|
|
|
|
def state_label(state):
|
|
"""(label, color) of a state; None for a VM that cannot be read."""
|
|
return UNKNOWN if state is None else STATES.get(state, STATES[0])
|
|
|
|
|
|
def manages(user, instance):
|
|
"""Superusers and a VM's owners: the users its power actions are for."""
|
|
# the lists prefetch userinstance_set
|
|
return user.is_superuser or any(ui.user_id == user.id for ui in instance.userinstance_set.all())
|
|
|
|
|
|
def allowed_actions(user, instance, state):
|
|
"""
|
|
The power actions the state allows and the user may use: superusers all,
|
|
a VM's owners all but suspend and resume, nobody else any (the
|
|
view_instances permission is read-only). A VM suspended by an
|
|
administrator offers its owner nothing. A template is not started (a
|
|
power cycle starts it again) but cloned, by anyone with the clone
|
|
permission who sees it.
|
|
"""
|
|
if state == 5 and instance.is_template:
|
|
return {"clone"} if user.has_perm("instances.clone_instances") else set()
|
|
if user.is_superuser:
|
|
actions = STATE_ACTIONS.get(state, set())
|
|
elif not manages(user, instance) or state == 3:
|
|
return set()
|
|
else:
|
|
actions = STATE_ACTIONS.get(state, set()) - SUPERUSER_ONLY
|
|
return actions - {"powercycle"} if instance.is_template else actions
|