upd
This commit is contained in:
		
							
								
								
									
										52
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										52
									
								
								README.md
									
									
									
									
									
								
							@@ -16,7 +16,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
- `python3-lxml` 4.9.2
 | 
					- `python3-lxml` 4.9.2
 | 
				
			||||||
- `python3-docopt` 0.6.2
 | 
					- `python3-docopt` 0.6.2
 | 
				
			||||||
- `python3-libvirt` 9.0.0 (актуальная новее)
 | 
					- `python3-libvirt` 9.0.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
`docopt` скорее всего будет выброшен в будущем, так как интерфейс CLI сильно усложнится.
 | 
					`docopt` скорее всего будет выброшен в будущем, так как интерфейс CLI сильно усложнится.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -24,7 +24,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
# API
 | 
					# API
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Кодовая база растёт, необходимо автоматически генерировать документацию в README её больше небудет.
 | 
					Кодовая база растёт, необходимо автоматически генерировать документацию, в README её больше не будет.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
В структуре проекта сейчас бардак, многое будет переосмыслено и переделано позже. Основная цель на текущем этапе — получить минимально работающий код, с помощью которого возможно выполнить установку виртуальной машины и как-то управлять ею.
 | 
					В структуре проекта сейчас бардак, многое будет переосмыслено и переделано позже. Основная цель на текущем этапе — получить минимально работающий код, с помощью которого возможно выполнить установку виртуальной машины и как-то управлять ею.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -32,41 +32,27 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
- `LivbirtSession` - обёртка над объектом `libvirt.virConnect`.
 | 
					- `LivbirtSession` - обёртка над объектом `libvirt.virConnect`.
 | 
				
			||||||
- `VirtualMachine` - класс для работы с доменами, через него выполняется большинство действий.
 | 
					- `VirtualMachine` - класс для работы с доменами, через него выполняется большинство действий.
 | 
				
			||||||
- `VirtualMachineInstaller` - класс для установки ВМ, выполняет кучу проверок, генерирует XML конфиг и т.п.
 | 
					- `VirtualMachineInstaller` - класс для установки ВМ, выполняет кучу проверок, генерирует XML конфиг и т.п. [В ПРОЦЕССЕ]
 | 
				
			||||||
- `StoragePool` - обёртка для `libvirt.virStoragePool`.
 | 
					- `StoragePool` - обёртка над `libvirt.virStoragePool`.
 | 
				
			||||||
- `Volume` - объект для управления дисками.
 | 
					- `Volume` - класс для управления дисками.
 | 
				
			||||||
- `VolumeInfo` - датакласс хранящий информацию о диске, может собрать XML.
 | 
					- `VolumeInfo` - датакласс хранящий информацию о диске, с помощью метода `to_xml()` получаем XML описание.
 | 
				
			||||||
- `GuestAgent` - понятно что это.
 | 
					- `GuestAgent` - понятно что это.
 | 
				
			||||||
- `ConfigLoader` - загрузчик TOML-конфига, возможно будет выброшен на мороз.
 | 
					- `ConfigLoader` - загрузчик TOML-конфига.
 | 
				
			||||||
 | 
					 | 
				
			||||||
```python
 | 
					 | 
				
			||||||
from na import LibvirtSession
 | 
					 | 
				
			||||||
from na.vm import VirtualMachineInstaller
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
session = LibvirtSession('config.toml')
 | 
					 | 
				
			||||||
compute = VirtualMachineInstaller(session).install(
 | 
					 | 
				
			||||||
    name='devuan',
 | 
					 | 
				
			||||||
    vcpus=4,
 | 
					 | 
				
			||||||
    vcpu_mode='host-model',
 | 
					 | 
				
			||||||
    memory=2048,
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
print(compute)
 | 
					 | 
				
			||||||
session.close()
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
# TODO
 | 
					# TODO
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- [ ] Установка ВМ
 | 
					- [x] Установка ВМ (всратый вариант)
 | 
				
			||||||
    - [x] Конструктор XML (базовый)
 | 
					    - [x] Конструктор XML (базовый)
 | 
				
			||||||
    - [x] Автоматический выбор модели процессора
 | 
					    - [x] Автоматический выбор модели процессора
 | 
				
			||||||
    - [ ] Метод создания дисков
 | 
					    - [x] Метод создания дисков
 | 
				
			||||||
    - [x] Дефайн, запуск и автостарт ВМ
 | 
					    - [x] Дефайн, запуск и автостарт ВМ
 | 
				
			||||||
    - [ ] Работа со StoragePool
 | 
					    - [x] Работа со StoragePool
 | 
				
			||||||
    - [ ] Создание блочных устройств
 | 
					    - [x] Создание блочных устройств
 | 
				
			||||||
    - [ ] Подключение/отключение устройств
 | 
					    - [x] Подключение/отключение устройств
 | 
				
			||||||
- [ ] Управление дисками
 | 
					    - [ ] Метод install()
 | 
				
			||||||
- [ ] Удаление ВМ
 | 
					- [ ] Установка ВМ (нормальный вариант)
 | 
				
			||||||
 | 
					- [x] Управление дисками (всратый вариант)
 | 
				
			||||||
 | 
					- [x] Удаление ВМ
 | 
				
			||||||
- [x] Изменение CPU
 | 
					- [x] Изменение CPU
 | 
				
			||||||
- [x] Изменение RAM
 | 
					- [x] Изменение RAM
 | 
				
			||||||
- [ ] Миграция ВМ между нодами
 | 
					- [ ] Миграция ВМ между нодами
 | 
				
			||||||
@@ -82,9 +68,13 @@ session.close()
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
# Заметки
 | 
					# Заметки
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Что там с LXC?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Можно добавить поддержку LXC, но только после реализации основного функционала для QEMU/KVM.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Будущее этой библиотеки
 | 
					## Будущее этой библиотеки
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Либа 
 | 
					Нужно ей придумать название.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Failover
 | 
					## Failover
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,3 +1,5 @@
 | 
				
			|||||||
from .config import ConfigLoader
 | 
					from .config import ConfigLoader
 | 
				
			||||||
from .session import LibvirtSession
 | 
					from .session import LibvirtSession
 | 
				
			||||||
 | 
					from .exceptions import *
 | 
				
			||||||
 | 
					from .volume import *
 | 
				
			||||||
from .vm import *
 | 
					from .vm import *
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,11 +10,9 @@ Usage:  na-vmctl [options] status <machine>
 | 
				
			|||||||
        na-vmctl [options] list [-a|--all]
 | 
					        na-vmctl [options] list [-a|--all]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Options:
 | 
					Options:
 | 
				
			||||||
    -c, --config <file>  Config file [default: /etc/node-agent/config.yaml]
 | 
					    -c, --config <file>  config file [default: /etc/node-agent/config.yaml]
 | 
				
			||||||
    -l, --loglvl <lvl>   Logging level
 | 
					    -l, --loglvl <lvl>   logging level
 | 
				
			||||||
    -a, --all            List all machines including inactive
 | 
					    -a, --all            list all machines including inactive
 | 
				
			||||||
    -f, --force          Force action. On shutdown calls graceful destroy()
 | 
					 | 
				
			||||||
    -9, --sigkill        Send SIGKILL to QEMU process. Not affects without --force
 | 
					 | 
				
			||||||
"""
 | 
					"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import logging
 | 
					import logging
 | 
				
			||||||
@@ -25,13 +23,13 @@ import libvirt
 | 
				
			|||||||
from docopt import docopt
 | 
					from docopt import docopt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from ..session import LibvirtSession
 | 
					from ..session import LibvirtSession
 | 
				
			||||||
from ..vm import VirtualMachine, VMError, VMNotFound
 | 
					from ..vm import VirtualMachine
 | 
				
			||||||
 | 
					from ..exceptions import VMError, VMNotFound
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
logger = logging.getLogger(__name__)
 | 
					logger = logging.getLogger(__name__)
 | 
				
			||||||
levels = logging.getLevelNamesMapping()
 | 
					levels = logging.getLevelNamesMapping()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Supress libvirt errors
 | 
					 | 
				
			||||||
libvirt.registerErrorHandler(lambda userdata, err: None, ctx=None)
 | 
					libvirt.registerErrorHandler(lambda userdata, err: None, ctx=None)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -43,21 +41,6 @@ class Color:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Table:
 | 
					class Table:
 | 
				
			||||||
    """Print table. Example::
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        t = Table()
 | 
					 | 
				
			||||||
        t.header(['KEY', 'VALUE'])  # header is optional
 | 
					 | 
				
			||||||
        t.row(['key 1', 'value 1'])
 | 
					 | 
				
			||||||
        t.row(['key 2', 'value 2'])
 | 
					 | 
				
			||||||
        t.rows(
 | 
					 | 
				
			||||||
            [
 | 
					 | 
				
			||||||
                ['key 3', 'value 3'],
 | 
					 | 
				
			||||||
                ['key 4', 'value 4']
 | 
					 | 
				
			||||||
            ]
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        t.print()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __init__(self, whitespace: str = '\t'):
 | 
					    def __init__(self, whitespace: str = '\t'):
 | 
				
			||||||
        self.__rows = []
 | 
					        self.__rows = []
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,10 +4,11 @@ Execute shell commands on guest via guest agent.
 | 
				
			|||||||
Usage:  na-vmexec [options] <machine> <command>
 | 
					Usage:  na-vmexec [options] <machine> <command>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Options:
 | 
					Options:
 | 
				
			||||||
    -c, --config <file>  Config file [default: /etc/node-agent/config.yaml]
 | 
					    -c, --config <file>  config file [default: /etc/node-agent/config.yaml]
 | 
				
			||||||
    -l, --loglvl <lvl>   Logging level
 | 
					    -l, --loglvl <lvl>   logging level
 | 
				
			||||||
    -s, --shell <shell>  Guest shell [default: /bin/sh]
 | 
					    -s, --shell <shell>  guest shell [default: /bin/sh]
 | 
				
			||||||
    -t, --timeout <sec>  QEMU timeout in seconds to stop polling command status [default: 60]
 | 
					    -t, --timeout <sec>  QEMU timeout in seconds to stop polling command status [default: 60]
 | 
				
			||||||
 | 
					    -p, --pid <PID>      PID on guest to poll output
 | 
				
			||||||
"""
 | 
					"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import logging
 | 
					import logging
 | 
				
			||||||
@@ -18,13 +19,13 @@ import libvirt
 | 
				
			|||||||
from docopt import docopt
 | 
					from docopt import docopt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from ..session import LibvirtSession
 | 
					from ..session import LibvirtSession
 | 
				
			||||||
from ..vm import GuestAgent, GuestAgentError, VMNotFound
 | 
					from ..vm import GuestAgent
 | 
				
			||||||
 | 
					from ..exceptions import GuestAgentError, VMNotFound
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
logger = logging.getLogger(__name__)
 | 
					logger = logging.getLogger(__name__)
 | 
				
			||||||
levels = logging.getLevelNamesMapping()
 | 
					levels = logging.getLevelNamesMapping()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Supress libvirt errors
 | 
					 | 
				
			||||||
libvirt.registerErrorHandler(lambda userdata, err: None, ctx=None)
 | 
					libvirt.registerErrorHandler(lambda userdata, err: None, ctx=None)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -58,31 +59,24 @@ def cli():
 | 
				
			|||||||
            exited, exitcode, stdout, stderr = ga.shellexec(
 | 
					            exited, exitcode, stdout, stderr = ga.shellexec(
 | 
				
			||||||
                cmd, executable=shell, capture_output=True, decode_output=True,
 | 
					                cmd, executable=shell, capture_output=True, decode_output=True,
 | 
				
			||||||
                timeout=int(args['--timeout']))
 | 
					                timeout=int(args['--timeout']))
 | 
				
			||||||
        except GuestAgentError as qemuerr:
 | 
					        except GuestAgentError as gaerr:
 | 
				
			||||||
            errmsg = f'{Color.RED}{qemuerr}{Color.NONE}'
 | 
					            errmsg = f'{Color.RED}{gaerr}{Color.NONE}'
 | 
				
			||||||
            if str(qemuerr).startswith('Polling command pid='):
 | 
					            if str(gaerr).startswith('Polling command pid='):
 | 
				
			||||||
                errmsg = (errmsg + Color.YELLOW +
 | 
					                errmsg = (errmsg + Color.YELLOW +
 | 
				
			||||||
                          '\n[NOTE: command may still running]' + Color.NONE)
 | 
					                          '\n[NOTE: command may still running on guest '
 | 
				
			||||||
 | 
					                          'pid={ga.last_pid}]' + Color.NONE)
 | 
				
			||||||
            sys.exit(errmsg)
 | 
					            sys.exit(errmsg)
 | 
				
			||||||
        except VMNotFound as err:
 | 
					        except VMNotFound as err:
 | 
				
			||||||
            sys.exit(f'{Color.RED}VM {machine} not found{Color.NONE}')
 | 
					            sys.exit(f'{Color.RED}VM {machine} not found{Color.NONE}')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if not exited:
 | 
					    if not exited:
 | 
				
			||||||
        print(Color.YELLOW + '[NOTE: command may still running]' + Color.NONE,
 | 
					        print(Color.YELLOW +
 | 
				
			||||||
              file=sys.stderr)
 | 
					              '[NOTE: command may still running on guest pid={ga.last_pid}]' +
 | 
				
			||||||
    else:
 | 
					              Color.NONE, file=sys.stderr)
 | 
				
			||||||
        if exitcode == 0:
 | 
					 | 
				
			||||||
            exitcolor = Color.GREEN
 | 
					 | 
				
			||||||
        else:
 | 
					 | 
				
			||||||
            exitcolor = Color.RED
 | 
					 | 
				
			||||||
        print(exitcolor + f'[command exited with exit code {exitcode}]' +
 | 
					 | 
				
			||||||
              Color.NONE,
 | 
					 | 
				
			||||||
              file=sys.stderr)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if stderr:
 | 
					    if stderr:
 | 
				
			||||||
        print(Color.RED + stderr.strip() + Color.NONE, file=sys.stderr)
 | 
					        print(stderr.strip(), file=sys.stderr)
 | 
				
			||||||
    if stdout:
 | 
					    if stdout:
 | 
				
			||||||
        print(Color.GREEN + stdout.strip() + Color.NONE, file=sys.stdout)
 | 
					        print(stdout.strip(), file=sys.stdout)
 | 
				
			||||||
    sys.exit(exitcode)
 | 
					    sys.exit(exitcode)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,15 +3,13 @@ import tomllib
 | 
				
			|||||||
from collections import UserDict
 | 
					from collections import UserDict
 | 
				
			||||||
from pathlib import Path
 | 
					from pathlib import Path
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from .exceptions import ConfigLoaderError
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
NODEAGENT_CONFIG_FILE = os.getenv('NODEAGENT_CONFIG_FILE')
 | 
					NODEAGENT_CONFIG_FILE = os.getenv('NODEAGENT_CONFIG_FILE')
 | 
				
			||||||
NODEAGENT_DEFAULT_CONFIG_FILE = '/etc/node-agent/config.toml'
 | 
					NODEAGENT_DEFAULT_CONFIG_FILE = '/etc/node-agent/config.toml'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ConfigLoaderError(Exception):
 | 
					 | 
				
			||||||
    """Bad config file syntax, unreachable file or bad config schema."""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class ConfigLoader(UserDict):
 | 
					class ConfigLoader(UserDict):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __init__(self, file: Path | None = None):
 | 
					    def __init__(self, file: Path | None = None):
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										21
									
								
								node_agent/exceptions.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								node_agent/exceptions.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
				
			|||||||
 | 
					class ConfigLoaderError(Exception):
 | 
				
			||||||
 | 
					    """Bad config file syntax, unreachable file or bad config schema."""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class LibvirtSessionError(Exception):
 | 
				
			||||||
 | 
					    """Something went wrong while connecting to libvirtd."""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class VMError(Exception):
 | 
				
			||||||
 | 
					    """Something went wrong while interacting with the domain."""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class VMNotFound(VMError):
 | 
				
			||||||
 | 
					    """Virtual machine not found on node."""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class GuestAgentError(Exception):
 | 
				
			||||||
 | 
					    """Mostly QEMU Guest Agent is not responding."""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class StoragePoolError(Exception):
 | 
				
			||||||
 | 
					    """Something went wrong when operating with storage pool."""
 | 
				
			||||||
@@ -2,12 +2,9 @@ from contextlib import AbstractContextManager
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import libvirt
 | 
					import libvirt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from .vm import GuestAgent, VirtualMachine, VMNotFound
 | 
					from .vm import GuestAgent, VirtualMachine
 | 
				
			||||||
from .volume import StoragePool
 | 
					from .volume import StoragePool
 | 
				
			||||||
 | 
					from .exceptions import LibvirtSessionError, VMNotFound
 | 
				
			||||||
 | 
					 | 
				
			||||||
class LibvirtSessionError(Exception):
 | 
					 | 
				
			||||||
    """Something went wrong while connecting to libvirtd."""
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class LibvirtSession(AbstractContextManager):
 | 
					class LibvirtSession(AbstractContextManager):
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,3 @@
 | 
				
			|||||||
from .exceptions import *
 | 
					 | 
				
			||||||
from .guest_agent import GuestAgent
 | 
					from .guest_agent import GuestAgent
 | 
				
			||||||
from .installer import CPUMode, CPUTopology, VirtualMachineInstaller
 | 
					from .installer import CPUMode, CPUTopology, VirtualMachineInstaller
 | 
				
			||||||
from .virtual_machine import VirtualMachine
 | 
					from .virtual_machine import VirtualMachine
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
import libvirt
 | 
					import libvirt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from .exceptions import VMError
 | 
					from ..exceptions import VMError
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class VirtualMachineBase:
 | 
					class VirtualMachineBase:
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,14 +0,0 @@
 | 
				
			|||||||
class GuestAgentError(Exception):
 | 
					 | 
				
			||||||
    """Mostly QEMU Guest Agent is not responding."""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class VMError(Exception):
 | 
					 | 
				
			||||||
    """Something went wrong while interacting with the domain."""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class VMNotFound(Exception):
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def __init__(self, domain, message='VM not found vm={domain}'):
 | 
					 | 
				
			||||||
        self.domain = domain
 | 
					 | 
				
			||||||
        self.message = message.format(domain=domain)
 | 
					 | 
				
			||||||
        super().__init__(self.message)
 | 
					 | 
				
			||||||
@@ -6,8 +6,8 @@ from time import sleep, time
 | 
				
			|||||||
import libvirt
 | 
					import libvirt
 | 
				
			||||||
import libvirt_qemu
 | 
					import libvirt_qemu
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from ..exceptions import GuestAgentError
 | 
				
			||||||
from .base import VirtualMachineBase
 | 
					from .base import VirtualMachineBase
 | 
				
			||||||
from .exceptions import GuestAgentError
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
logger = logging.getLogger(__name__)
 | 
					logger = logging.getLogger(__name__)
 | 
				
			||||||
@@ -33,6 +33,7 @@ class GuestAgent(VirtualMachineBase):
 | 
				
			|||||||
        super().__init__(domain)
 | 
					        super().__init__(domain)
 | 
				
			||||||
        self.timeout = timeout or QEMU_TIMEOUT  # timeout for guest agent
 | 
					        self.timeout = timeout or QEMU_TIMEOUT  # timeout for guest agent
 | 
				
			||||||
        self.flags = flags or libvirt_qemu.VIR_DOMAIN_QEMU_MONITOR_COMMAND_DEFAULT
 | 
					        self.flags = flags or libvirt_qemu.VIR_DOMAIN_QEMU_MONITOR_COMMAND_DEFAULT
 | 
				
			||||||
 | 
					        self.last_pid = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def execute(self,
 | 
					    def execute(self,
 | 
				
			||||||
                command: dict,
 | 
					                command: dict,
 | 
				
			||||||
@@ -68,9 +69,9 @@ class GuestAgent(VirtualMachineBase):
 | 
				
			|||||||
        cmd_out = self._execute(command)
 | 
					        cmd_out = self._execute(command)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if capture_output:
 | 
					        if capture_output:
 | 
				
			||||||
            cmd_pid = json.loads(cmd_out)['return']['pid']
 | 
					            self.last_pid = json.loads(cmd_out)['return']['pid']
 | 
				
			||||||
            return self._get_cmd_result(
 | 
					            return self._get_cmd_result(
 | 
				
			||||||
                cmd_pid,
 | 
					                self.last_pid,
 | 
				
			||||||
                decode_output=decode_output,
 | 
					                decode_output=decode_output,
 | 
				
			||||||
                wait=wait,
 | 
					                wait=wait,
 | 
				
			||||||
                timeout=timeout,
 | 
					                timeout=timeout,
 | 
				
			||||||
@@ -106,6 +107,10 @@ class GuestAgent(VirtualMachineBase):
 | 
				
			|||||||
            timeout=timeout,
 | 
					            timeout=timeout,
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def poll_pid(self, pid: int):
 | 
				
			||||||
 | 
					        # Нужно цепляться к PID и вывести результат
 | 
				
			||||||
 | 
					        pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _execute(self, command: dict):
 | 
					    def _execute(self, command: dict):
 | 
				
			||||||
        logging.debug('Execute command: vm=%s cmd=%s', self.domain_name,
 | 
					        logging.debug('Execute command: vm=%s cmd=%s', self.domain_name,
 | 
				
			||||||
                      command)
 | 
					                      command)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,9 +2,9 @@ import logging
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import libvirt
 | 
					import libvirt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from ..exceptions import VMError
 | 
				
			||||||
from ..volume import VolumeInfo
 | 
					from ..volume import VolumeInfo
 | 
				
			||||||
from .base import VirtualMachineBase
 | 
					from .base import VirtualMachineBase
 | 
				
			||||||
from .exceptions import VMError
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
logger = logging.getLogger(__name__)
 | 
					logger = logging.getLogger(__name__)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,8 +1,14 @@
 | 
				
			|||||||
 | 
					import logging
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import libvirt
 | 
					import libvirt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from ..exceptions import StoragePoolError
 | 
				
			||||||
from .volume import Volume, VolumeInfo
 | 
					from .volume import Volume, VolumeInfo
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					logger = logging.getLogger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class StoragePool:
 | 
					class StoragePool:
 | 
				
			||||||
    def __init__(self, pool: libvirt.virStoragePool):
 | 
					    def __init__(self, pool: libvirt.virStoragePool):
 | 
				
			||||||
        self.pool = pool
 | 
					        self.pool = pool
 | 
				
			||||||
@@ -23,15 +29,34 @@ class StoragePool:
 | 
				
			|||||||
    def refresh(self) -> None:
 | 
					    def refresh(self) -> None:
 | 
				
			||||||
        self.pool.refresh()
 | 
					        self.pool.refresh()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def create_volume(self, vol_info: VolumeInfo) -> None:
 | 
					    def create_volume(self, vol_info: VolumeInfo) -> Volume:
 | 
				
			||||||
        # todo: return Volume object?
 | 
					        """
 | 
				
			||||||
        self.pool.createXML(
 | 
					        Create storage volume and return Volume instance.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        logger.info(f'Create storage volume vol={vol_info.name} '
 | 
				
			||||||
 | 
					                    f'in pool={self.pool}')
 | 
				
			||||||
 | 
					        vol = self.pool.createXML(
 | 
				
			||||||
            vol_info.to_xml(),
 | 
					            vol_info.to_xml(),
 | 
				
			||||||
            flags=libvirt.VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA)
 | 
					            flags=libvirt.VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA)
 | 
				
			||||||
 | 
					        return Volume(self.pool, vol)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_volume(self, name: str) -> Volume:
 | 
					    def get_volume(self, name: str) -> Volume | None:
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Lookup and return Volume instance or None.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        logger.info(f'Lookup for storage volume vol={name} '
 | 
				
			||||||
 | 
					                    f'in pool={self.pool.name}')
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
            vol = self.pool.storageVolLookupByName(name)
 | 
					            vol = self.pool.storageVolLookupByName(name)
 | 
				
			||||||
            return Volume(self.pool, vol)
 | 
					            return Volume(self.pool, vol)
 | 
				
			||||||
 | 
					        except libvirt.libvirtError as err:
 | 
				
			||||||
 | 
					            if (err.get_error_domain() == libvirt.VIR_FROM_STORAGE
 | 
				
			||||||
 | 
					                err.get_error_code() == libvirt.VIR_ERR_NO_STORAGE_VOL):
 | 
				
			||||||
 | 
					                logger.error(err.get_error_message())
 | 
				
			||||||
 | 
					                return None
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                logger.error(f'libvirt error: {err}')
 | 
				
			||||||
 | 
					                raise StoragePoolError(f'libvirt error: {err}') from err
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def list_volumes(self) -> list[Volume]:
 | 
					    def list_volumes(self) -> list[Volume]:
 | 
				
			||||||
        return [Volume(self.pool, vol) for vol in self.pool.listAllVolumes()]
 | 
					        return [Volume(self.pool, vol) for vol in self.pool.listAllVolumes()]
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user