python-compute/node_agent/vm/main.py

159 lines
5.3 KiB
Python
Raw Normal View History

2023-07-22 23:59:49 +03:00
import logging
import libvirt
2023-07-29 14:29:37 +03:00
from .base import VirtualMachineBase
2023-07-29 15:35:36 +03:00
from .exceptions import VMError
2023-07-22 23:59:49 +03:00
logger = logging.getLogger(__name__)
2023-07-29 14:29:37 +03:00
class VirtualMachine(VirtualMachineBase):
2023-07-22 23:59:49 +03:00
@property
def name(self):
2023-07-28 01:01:32 +03:00
return self.domname
2023-07-22 23:59:49 +03:00
@property
def status(self) -> str:
"""
Return VM state: 'running', 'shutoff', etc. Reference:
https://libvirt.org/html/libvirt-libvirt-domain.html#virDomainState
"""
2023-07-28 01:01:32 +03:00
try:
# libvirt returns list [state: int, reason: int]
# https://libvirt.org/html/libvirt-libvirt-domain.html#virDomainGetState
state = self.domain.state()[0]
except libvirt.libvirtError as err:
2023-08-24 22:36:12 +03:00
raise VMError(
f'Cannot fetch VM status vm={self.domname}: {err}') from err
STATES = {
libvirt.VIR_DOMAIN_NOSTATE: 'nostate',
libvirt.VIR_DOMAIN_RUNNING: 'running',
libvirt.VIR_DOMAIN_BLOCKED: 'blocked',
libvirt.VIR_DOMAIN_PAUSED: 'paused',
libvirt.VIR_DOMAIN_SHUTDOWN: 'shutdown',
libvirt.VIR_DOMAIN_SHUTOFF: 'shutoff',
libvirt.VIR_DOMAIN_CRASHED: 'crashed',
libvirt.VIR_DOMAIN_PMSUSPENDED: 'pmsuspended',
}
return STATES.get(state)
2023-07-22 23:59:49 +03:00
@property
def is_running(self) -> bool:
"""Return True if VM is running, else return False."""
if self.domain.isActive() != 1:
# inactive (0) or error (-1)
return False
return True
2023-07-28 01:01:32 +03:00
@property
def is_autostart(self) -> bool:
"""Return True if VM autostart is enabled, else return False."""
try:
if self.domain.autostart() == 1:
return True
return False
except libvirt.libvirtError as err:
2023-08-24 22:36:12 +03:00
raise VMError(
f'Cannot get autostart status vm={self.domname}: {err}'
) from err
2023-07-28 01:01:32 +03:00
2023-07-22 23:59:49 +03:00
def start(self) -> None:
"""Start defined VM."""
logger.info('Starting VM: vm=%s', self.domname)
if self.is_running:
2023-08-24 22:36:12 +03:00
logger.debug('VM vm=%s is already started, nothing to do',
self.domname)
2023-07-22 23:59:49 +03:00
return
try:
2023-07-29 15:35:36 +03:00
self.domain.create()
2023-07-22 23:59:49 +03:00
except libvirt.libvirtError as err:
2023-07-29 14:29:37 +03:00
raise VMError(f'Cannot start vm={self.domname}: {err}') from err
2023-07-22 23:59:49 +03:00
2023-08-24 22:36:12 +03:00
def shutdown(self, mode: str | None = None) -> None:
2023-07-22 23:59:49 +03:00
"""
2023-08-24 22:36:12 +03:00
Send signal to guest OS to shutdown. Supports several modes:
* GUEST_AGENT - use guest agent
* NORMAL - use method choosen by hypervisor to shutdown machine
* SIGTERM - send SIGTERM to QEMU process, destroy machine gracefully
* SIGKILL - send SIGKILL, this option may corrupt guest data!
If mode is not passed use 'NORMAL' mode.
2023-07-22 23:59:49 +03:00
"""
2023-08-24 22:36:12 +03:00
MODES = {
'GUEST_AGENT': libvirt.VIR_DOMAIN_SHUTDOWN_GUEST_AGENT,
'NORMAL': libvirt.VIR_DOMAIN_SHUTDOWN_DEFAULT,
'SIGTERM': libvirt.VIR_DOMAIN_DESTROY_GRACEFUL,
'SIGKILL': libvirt.VIR_DOMAIN_DESTROY_DEFAULT
}
if mode is None:
mode = 'NORMAL'
if not isinstance(mode, str):
raise ValueError(f'Mode must be a string, not {type(mode)}')
if mode.upper() not in MODES:
raise ValueError(f"Unsupported mode: '{mode}'")
2023-07-28 01:01:32 +03:00
try:
2023-08-24 22:36:12 +03:00
if mode in ['GUEST_AGENT', 'NORMAL']:
self.domain.shutdownFlags(flags=MODES.get(mode))
elif mode in ['SIGTERM', 'SIGKILL']:
self.domain.destroyFlags(flags=MODES.get(mode))
2023-07-28 01:01:32 +03:00
except libvirt.libvirtError as err:
2023-08-24 22:36:12 +03:00
raise VMError(f'Cannot shutdown vm={self.domname} with '
f'mode={mode}: {err}') from err
2023-07-22 23:59:49 +03:00
2023-08-24 22:36:12 +03:00
def reset(self) -> None:
2023-07-22 23:59:49 +03:00
"""
2023-07-28 01:01:32 +03:00
Copypaste from libvirt doc:
2023-07-22 23:59:49 +03:00
2023-07-28 01:01:32 +03:00
Reset a domain immediately without any guest OS shutdown.
Reset emulates the power reset button on a machine, where all
hardware sees the RST line set and reinitializes internal state.
2023-07-22 23:59:49 +03:00
2023-07-28 01:01:32 +03:00
Note that there is a risk of data loss caused by reset without any
guest OS shutdown.
2023-07-22 23:59:49 +03:00
"""
2023-07-28 01:01:32 +03:00
try:
2023-07-29 14:29:37 +03:00
self.domain.reset()
2023-07-28 01:01:32 +03:00
except libvirt.libvirtError as err:
raise VMError(f'Cannot reset vm={self.domname}: {err}') from err
2023-07-22 23:59:49 +03:00
def reboot(self) -> None:
"""Send ACPI signal to guest OS to reboot. OS may ignore this."""
2023-07-28 01:01:32 +03:00
try:
self.domain.reboot()
except libvirt.libvirtError as err:
raise VMError(f'Cannot reboot vm={self.domname}: {err}') from err
2023-08-24 22:36:12 +03:00
def autostart(self, enable: bool) -> None:
2023-07-28 01:01:32 +03:00
"""
Configure VM to be automatically started when the host machine boots.
"""
2023-08-24 22:36:12 +03:00
if enable:
2023-07-28 01:01:32 +03:00
autostart_flag = 1
else:
autostart_flag = 0
try:
self.domain.setAutostart(autostart_flag)
except libvirt.libvirtError as err:
2023-08-24 22:36:12 +03:00
raise VMError(f'Cannot set autostart vm={self.domname} '
f'autostart={autostart_flag}: {err}') from err
2023-07-22 23:59:49 +03:00
2023-08-24 22:36:12 +03:00
def set_vcpus(self, count: int):
2023-07-22 23:59:49 +03:00
pass
2023-08-24 22:36:12 +03:00
def set_ram(self, count: int):
2023-07-22 23:59:49 +03:00
pass
2023-08-24 22:36:12 +03:00
def list_ssh_keys(self, user: str):
2023-07-22 23:59:49 +03:00
pass
2023-08-24 22:36:12 +03:00
def set_ssh_keys(self, user: str):
2023-07-22 23:59:49 +03:00
pass
2023-08-24 22:36:12 +03:00
def remove_ssh_keys(self, user: str):
2023-07-22 23:59:49 +03:00
pass
def set_user_password(self, user: str):
pass