diff --git a/packaging/build/compute-0.1.0.dev1.tar.gz b/packaging/build/compute-0.1.0.dev1.tar.gz deleted file mode 100644 index fcb6882..0000000 Binary files a/packaging/build/compute-0.1.0.dev1.tar.gz and /dev/null differ diff --git a/packaging/build/compute-0.1.0.dev1/.pybuild/cpython3_3.11/build/compute-0.1.0.dev1.dist-info/METADATA b/packaging/build/compute-0.1.0.dev1/.pybuild/cpython3_3.11/build/compute-0.1.0.dev1.dist-info/METADATA deleted file mode 100644 index f4c22ad..0000000 --- a/packaging/build/compute-0.1.0.dev1/.pybuild/cpython3_3.11/build/compute-0.1.0.dev1.dist-info/METADATA +++ /dev/null @@ -1,81 +0,0 @@ -Metadata-Version: 2.1 -Name: compute -Version: 0.1.0.dev1 -Summary: Compute instances management library and tools -Author: ge -Author-email: ge@nixhacks.net -Requires-Python: >=3.11,<4.0 -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.11 -Requires-Dist: libvirt-python (==9.0.0) -Requires-Dist: lxml (>=4.9.2,<5.0.0) -Requires-Dist: pydantic (==1.10.4) -Requires-Dist: pyyaml (>=6.0.1,<7.0.0) -Description-Content-Type: text/markdown - -# Compute - -Compute instances management library and tools. - -## Docs - -Run `make serve-docs`. See [Development](#development) below. - -## Roadmap - -- [x] Create instances -- [ ] CDROM -- [ ] cloud-init for provisioning instances -- [x] Instance power management -- [x] Instance pause and resume -- [x] vCPU hotplug -- [x] Memory hotplug -- [x] Hot disk resize [not tested] -- [ ] CPU topology customization -- [x] CPU customization (emulation mode, model, vendor, features) -- [ ] BIOS/UEFI settings -- [x] Device attaching -- [x] Device detaching -- [ ] GPU passthrough -- [ ] CPU guarantied resource percent support -- [x] QEMU Guest Agent management -- [ ] Instance resources usage stats -- [ ] SSH-keys management -- [x] Setting user passwords in guest -- [x] QCOW2 disks support -- [ ] ZVOL support -- [ ] Network disks support -- [ ] Images service integration (Images service is not implemented yet) -- [ ] Manage storage pools -- [ ] Idempotency -- [ ] CLI [in progress] -- [ ] HTTP API -- [ ] Instance migrations -- [ ] Instance snapshots -- [ ] Instance backups -- [ ] LXC - -## Development - -Python 3.11+ is required. - -Install [poetry](https://python-poetry.org/), clone this repository and run: - -``` -poetry install --with dev --with docs -``` - -# Build Debian package - -Install Docker first, then run: - -``` -make build-deb -``` - -`compute` and `compute-doc` packages will built. See packaging/build directory. Packages can be installed via `dpkg` or `apt-get`: - -``` -apt-get install ./compute*.deb -``` - diff --git a/packaging/build/compute-0.1.0.dev1/.pybuild/cpython3_3.11/build/compute-0.1.0.dev1.dist-info/RECORD b/packaging/build/compute-0.1.0.dev1/.pybuild/cpython3_3.11/build/compute-0.1.0.dev1.dist-info/RECORD deleted file mode 100644 index 5f97163..0000000 --- a/packaging/build/compute-0.1.0.dev1/.pybuild/cpython3_3.11/build/compute-0.1.0.dev1.dist-info/RECORD +++ /dev/null @@ -1,23 +0,0 @@ -../scripts/compute,sha256=b-Gj6H6ssfbGalpouUMSX5pmsjqDnN9xMdTwnU-UfZY,216 -compute/__init__.py,sha256=x4zp_CoVPKgDT6AqhometspAyinGxJUXO48duJ5aHUM,873 -compute/__main__.py,sha256=zJyKJul6pCbguFPtVLZBoAuZl9RXibn4CCMn46jIgUQ,745 -compute/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -compute/cli/control.py,sha256=83wnR21pHOPyyk1i1n_YBIDz6dCFB6hmuIFguIk68rs,14634 -compute/common.py,sha256=G1qwC1EybG5LEJtyoux9ymiqB2ZOsgKXlCpbuhHv55Y,948 -compute/exceptions.py,sha256=Ga59L55qSAPeyDfjANPuMh4yVSRWHDYi9xqq5o4_7-0,2452 -compute/instance/__init__.py,sha256=kHN8jVamyrBZYZgi62tPtJ7rS73gUPhfswLalmPA5Zs,772 -compute/instance/guest_agent.py,sha256=fq89kQbcV5X5eFCsMmujRuwTOSghWO4ZhAjvxyUu84M,7018 -compute/instance/instance.py,sha256=WP6oTJfdAf6QlefwVLqdC8J6XoKHum6nZhwwHOEtjNk,23297 -compute/instance/schemas.py,sha256=B51ytPlxhnx0MrkR2WYhd49RaRT7Is7NsIM9OrMUpvI,4288 -compute/session.py,sha256=znYOIzoiCbSG62k-ViaXti_lOnw88wD8Syp3nCXAJ28,10050 -compute/storage/__init__.py,sha256=zNaVjZ2925DxrVUFWwVRsGU6bSYbF46sb4L6NsaiKbw,736 -compute/storage/pool.py,sha256=9z99bBDbb4ATGpfMkEWpxAO4fEQHNVOxxf0iUln9cN0,4197 -compute/storage/volume.py,sha256=_TbK9Y4d3NAeknPUiuhldAT3ZaN1sZgjy4QzC-Sw4Io,4110 -compute/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -compute/utils/config_loader.py,sha256=ul1J3sZg0D9R0HbOz5Pg9JmL4nFaMahAzQEdGaWFABU,1989 -compute/utils/ids.py,sha256=fg6Xsg4OMM-BIaU3DPu0L91ICwx-L3qNoELEwQZz2s0,1007 -compute/utils/units.py,sha256=UkwD0zQ-rlpSpkbfezCcvJx4D8iZlI9M-oXXvdVEvy0,1549 -compute-0.1.0.dev1.dist-info/METADATA,sha256=tbX8xp92Jwqf44sOwPB-HqKHLezab5dU9DrQDYFitDQ,1944 -compute-0.1.0.dev1.dist-info/WHEEL,sha256=vVCvjcmxuUltf8cYhJ0sJMRDLr1XsPuxEId8YDzbyCY,88 -compute-0.1.0.dev1.dist-info/entry_points.txt,sha256=xHhg-Fo9Z5gJnIahbG8pVIGNDqlH5Eordn8hnXUwscw,51 -compute-0.1.0.dev1.dist-info/RECORD,, diff --git a/packaging/build/compute-0.1.0.dev1/.pybuild/cpython3_3.11/build/compute-0.1.0.dev1.dist-info/WHEEL b/packaging/build/compute-0.1.0.dev1/.pybuild/cpython3_3.11/build/compute-0.1.0.dev1.dist-info/WHEEL deleted file mode 100644 index 4ba7671..0000000 --- a/packaging/build/compute-0.1.0.dev1/.pybuild/cpython3_3.11/build/compute-0.1.0.dev1.dist-info/WHEEL +++ /dev/null @@ -1,4 +0,0 @@ -Wheel-Version: 1.0 -Generator: poetry-core 1.4.0 -Root-Is-Purelib: true -Tag: py3-none-any diff --git a/packaging/build/compute-0.1.0.dev1/.pybuild/cpython3_3.11/build/compute-0.1.0.dev1.dist-info/entry_points.txt b/packaging/build/compute-0.1.0.dev1/.pybuild/cpython3_3.11/build/compute-0.1.0.dev1.dist-info/entry_points.txt deleted file mode 100644 index 4130f9f..0000000 --- a/packaging/build/compute-0.1.0.dev1/.pybuild/cpython3_3.11/build/compute-0.1.0.dev1.dist-info/entry_points.txt +++ /dev/null @@ -1,3 +0,0 @@ -[console_scripts] -compute=compute.cli.control:cli - diff --git a/packaging/build/compute-0.1.0.dev1/.pybuild/cpython3_3.11/build/compute/__init__.py b/packaging/build/compute-0.1.0.dev1/.pybuild/cpython3_3.11/build/compute/__init__.py deleted file mode 100644 index ffe06d7..0000000 --- a/packaging/build/compute-0.1.0.dev1/.pybuild/cpython3_3.11/build/compute/__init__.py +++ /dev/null @@ -1,22 +0,0 @@ -# This file is part of Compute -# -# Compute is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Ansible is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Ansible. If not, see . - -"""Compute instances management library.""" - -__version__ = '0.1.0-dev1' - -from .instance import Instance, InstanceConfig, InstanceSchema -from .session import Session -from .storage import StoragePool, Volume, VolumeConfig diff --git a/packaging/build/compute-0.1.0.dev1/.pybuild/cpython3_3.11/build/compute/__main__.py b/packaging/build/compute-0.1.0.dev1/.pybuild/cpython3_3.11/build/compute/__main__.py deleted file mode 100644 index 4995fbd..0000000 --- a/packaging/build/compute-0.1.0.dev1/.pybuild/cpython3_3.11/build/compute/__main__.py +++ /dev/null @@ -1,21 +0,0 @@ -# This file is part of Compute -# -# Compute is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Ansible is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Ansible. If not, see . - -"""Command line interface for compute module.""" - -from compute.cli import main - - -main.cli() diff --git a/packaging/build/compute-0.1.0.dev1/.pybuild/cpython3_3.11/build/compute/cli/__init__.py b/packaging/build/compute-0.1.0.dev1/.pybuild/cpython3_3.11/build/compute/cli/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/packaging/build/compute-0.1.0.dev1/.pybuild/cpython3_3.11/build/compute/cli/control.py b/packaging/build/compute-0.1.0.dev1/.pybuild/cpython3_3.11/build/compute/cli/control.py deleted file mode 100644 index f5a5b91..0000000 --- a/packaging/build/compute-0.1.0.dev1/.pybuild/cpython3_3.11/build/compute/cli/control.py +++ /dev/null @@ -1,501 +0,0 @@ -# This file is part of Compute -# -# Compute is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Ansible is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Ansible. If not, see . - -"""Command line interface.""" - -import argparse -import io -import logging -import os -import shlex -import sys -from collections import UserDict -from typing import Any -from uuid import uuid4 - -import libvirt -import yaml -from pydantic import ValidationError - -from compute import __version__ -from compute.exceptions import ComputeError, GuestAgentTimeoutExceededError -from compute.instance import GuestAgent -from compute.session import Session -from compute.utils import ids - - -log = logging.getLogger(__name__) -log_levels = [lv.lower() for lv in logging.getLevelNamesMapping()] - -libvirt.registerErrorHandler( - lambda userdata, err: None, # noqa: ARG005 - ctx=None, -) - - -class Table: - """Minimalistic text table constructor.""" - - def __init__(self, whitespace: str | None = None): - """Initialise Table.""" - self.whitespace = whitespace or '\t' - self.header = [] - self.rows = [] - self.table = '' - - def add_row(self, row: list) -> None: - """Add table row.""" - self.rows.append([str(col) for col in row]) - - def add_rows(self, rows: list[list]) -> None: - """Add multiple rows.""" - for row in rows: - self.add_row(row) - - def __str__(self) -> str: - """Build table and return.""" - widths = [max(map(len, col)) for col in zip(*self.rows, strict=True)] - self.rows.insert(0, [str(h).upper() for h in self.header]) - for row in self.rows: - self.table += self.whitespace.join( - ( - val.ljust(width) - for val, width in zip(row, widths, strict=True) - ) - ) - self.table += '\n' - return self.table.strip() - - -def _list_instances(session: Session) -> None: - table = Table() - table.header = ['NAME', 'STATE'] - for instance in session.list_instances(): - table.add_row( - [ - instance.name, - instance.get_status(), - ] - ) - print(table) - sys.exit() - - -def _exec_guest_agent_command( - session: Session, args: argparse.Namespace -) -> None: - instance = session.get_instance(args.instance) - ga = GuestAgent(instance.domain, timeout=args.timeout) - arguments = args.arguments.copy() - if len(arguments) > 1 and not args.no_join_args: - arguments = [shlex.join(arguments)] - if not args.no_join_args: - arguments.insert(0, '-c') - stdin = None - if not sys.stdin.isatty(): - stdin = sys.stdin.read() - try: - output = ga.guest_exec( - path=args.executable, - args=arguments, - env=args.env, - stdin=stdin, - capture_output=True, - decode_output=True, - poll=True, - ) - except GuestAgentTimeoutExceededError as e: - sys.exit( - f'{e}. NOTE: command may still running in guest, ' - f'PID={ga.last_pid}' - ) - if output.stderr: - print(output.stderr.strip(), file=sys.stderr) - if output.stdout: - print(output.stdout.strip(), file=sys.stdout) - sys.exit(output.exitcode) - - -class _NotPresent: - """ - Type for representing non-existent dictionary keys. - - See :class:`_FillableDict`. - """ - - -class _FillableDict(UserDict): - """Use :method:`fill` to add key if not present.""" - - def __init__(self, data: dict): - self.data = data - - def fill(self, key: str, value: Any) -> None: # noqa: ANN401 - if self.data.get(key, _NotPresent) is _NotPresent: - self.data[key] = value - - -def _merge_dicts(a: dict, b: dict, path: list[str] | None = None) -> dict: - """Merge `b` into `a`. Return modified `a`.""" - if path is None: - path = [] - for key in b: - if key in a: - if isinstance(a[key], dict) and isinstance(b[key], dict): - _merge_dicts(a[key], b[key], [path + str(key)]) - elif a[key] == b[key]: - pass # same leaf value - else: - a[key] = b[key] # replace existing key's values - else: - a[key] = b[key] - return a - - -def _create_instance(session: Session, file: io.TextIOWrapper) -> None: - try: - data = _FillableDict(yaml.load(file.read(), Loader=yaml.SafeLoader)) - log.debug('Read from file: %s', data) - except yaml.YAMLError as e: - sys.exit(f'error: cannot parse YAML: {e}') - - capabilities = session.get_capabilities() - node_info = session.get_node_info() - - data.fill('name', uuid4().hex) - data.fill('title', None) - data.fill('description', None) - data.fill('arch', capabilities.arch) - data.fill('machine', capabilities.machine) - data.fill('emulator', capabilities.emulator) - data.fill('max_vcpus', node_info.cpus) - data.fill('max_memory', node_info.memory) - data.fill('cpu', {}) - cpu = { - 'emulation_mode': 'host-passthrough', - 'model': None, - 'vendor': None, - 'topology': None, - 'features': None, - } - data['cpu'] = _merge_dicts(data['cpu'], cpu) - data.fill( - 'network_interfaces', - [{'source': 'default', 'mac': ids.random_mac()}], - ) - data.fill('boot', {'order': ['cdrom', 'hd']}) - - try: - log.debug('Input data: %s', data) - session.create_instance(**data) - except ValidationError as e: - for error in e.errors(): - fields = '.'.join([str(lc) for lc in error['loc']]) - print( - f"validation error: {fields}: {error['msg']}", - file=sys.stderr, - ) - sys.exit() - - -def _shutdown_instance(session: Session, args: argparse.Namespace) -> None: - instance = session.get_instance(args.instance) - if args.soft: - method = 'SOFT' - elif args.hard: - method = 'HARD' - elif args.unsafe: - method = 'UNSAFE' - else: - method = 'NORMAL' - instance.shutdown(method) - - -def main(session: Session, args: argparse.Namespace) -> None: - """Perform actions.""" - match args.command: - case 'init': - _create_instance(session, args.file) - case 'exec': - _exec_guest_agent_command(session, args) - case 'ls': - _list_instances(session) - case 'start': - instance = session.get_instance(args.instance) - instance.start() - case 'shutdown': - _shutdown_instance(session, args) - case 'reboot': - instance = session.get_instance(args.instance) - instance.reboot() - case 'reset': - instance = session.get_instance(args.instance) - instance.reset() - case 'powrst': - instance = session.get_instance(args.instance) - instance.power_reset() - case 'pause': - instance = session.get_instance(args.instance) - instance.pause() - case 'resume': - instance = session.get_instance(args.instance) - instance.resume() - case 'status': - instance = session.get_instance(args.instance) - print(instance.status) - case 'setvcpus': - instance = session.get_instance(args.instance) - instance.set_vcpus(args.nvcpus, live=True) - case 'setmem': - instance = session.get_instance(args.instance) - instance.set_memory(args.memory, live=True) - case 'setpass': - instance = session.get_instance(args.instance) - instance.set_user_password( - args.username, - args.password, - encrypted=args.encrypted, - ) - - -def cli() -> None: # noqa: PLR0915 - """Return command line arguments parser.""" - root = argparse.ArgumentParser( - prog='compute', - description='manage compute instances', - formatter_class=argparse.RawTextHelpFormatter, - ) - root.add_argument( - '-v', - '--verbose', - action='store_true', - default=False, - help='enable verbose mode', - ) - root.add_argument( - '-c', - '--connect', - metavar='URI', - help='libvirt connection URI', - ) - root.add_argument( - '-l', - '--log-level', - type=str.lower, - metavar='LEVEL', - choices=log_levels, - help='log level', - ) - root.add_argument( - '-V', - '--version', - action='version', - version=__version__, - ) - subparsers = root.add_subparsers(dest='command', metavar='COMMAND') - - # init command - init = subparsers.add_parser( - 'init', help='initialise instance using YAML config file' - ) - init.add_argument( - 'file', - type=argparse.FileType('r', encoding='UTF-8'), - nargs='?', - default='instance.yaml', - help='instance config [default: instance.yaml]', - ) - - # exec subcommand - execute = subparsers.add_parser( - 'exec', - help='execute command in guest via guest agent', - description=( - 'NOTE: any argument after instance name will be passed into ' - 'guest as shell command.' - ), - ) - execute.add_argument('instance') - execute.add_argument('arguments', nargs=argparse.REMAINDER) - execute.add_argument( - '-t', - '--timeout', - type=int, - default=60, - help=( - 'waiting time in seconds for a command to be executed ' - 'in guest [default: 60]' - ), - ) - execute.add_argument( - '-x', - '--executable', - default='/bin/sh', - help='path to executable in guest [default: /bin/sh]', - ) - execute.add_argument( - '-e', - '--env', - type=str, - nargs='?', - action='append', - help='environment variables to pass to executable in guest', - ) - execute.add_argument( - '-n', - '--no-join-args', - action='store_true', - default=False, - help=( - "do not join arguments list and add '-c' option, suitable " - 'for non-shell executables and other specific cases.' - ), - ) - - # ls subcommand - listall = subparsers.add_parser('ls', help='list instances') - listall.add_argument( - '-a', - '--all', - action='store_true', - default=False, - help='list all instances including inactive', - ) - - # start subcommand - start = subparsers.add_parser('start', help='start instance') - start.add_argument('instance') - - # shutdown subcommand - shutdown = subparsers.add_parser('shutdown', help='shutdown instance') - shutdown.add_argument('instance') - shutdown_opts = shutdown.add_mutually_exclusive_group() - shutdown_opts.add_argument( - '-s', - '--soft', - action='store_true', - help='normal guest OS shutdown, guest agent is used', - ) - shutdown_opts.add_argument( - '-n', - '--normal', - action='store_true', - help='shutdown with hypervisor selected method [default]', - ) - shutdown_opts.add_argument( - '-H', - '--hard', - action='store_true', - help=( - "gracefully destroy instance, it's like long " - 'pressing the power button' - ), - ) - shutdown_opts.add_argument( - '-u', - '--unsafe', - action='store_true', - help=( - 'destroy instance, this is similar to a power outage ' - 'and may result in data loss or corruption' - ), - ) - - # reboot subcommand - reboot = subparsers.add_parser('reboot', help='reboot instance') - reboot.add_argument('instance') - - # reset subcommand - reset = subparsers.add_parser('reset', help='reset instance') - reset.add_argument('instance') - - # powrst subcommand - powrst = subparsers.add_parser('powrst', help='power reset instance') - powrst.add_argument('instance') - - # pause subcommand - pause = subparsers.add_parser('pause', help='pause instance') - pause.add_argument('instance') - - # resume subcommand - resume = subparsers.add_parser('resume', help='resume paused instance') - resume.add_argument('instance') - - # status subcommand - status = subparsers.add_parser('status', help='display instance status') - status.add_argument('instance') - - # setvcpus subcommand - setvcpus = subparsers.add_parser('setvcpus', help='set vCPU number') - setvcpus.add_argument('instance') - setvcpus.add_argument('nvcpus', type=int) - - # setmem subcommand - setmem = subparsers.add_parser('setmem', help='set memory size') - setmem.add_argument('instance') - setmem.add_argument('memory', type=int, help='memory in MiB') - - # setpass subcommand - setpass = subparsers.add_parser( - 'setpass', - help='set user password in guest', - ) - setpass.add_argument('instance') - setpass.add_argument('username') - setpass.add_argument('password') - setpass.add_argument( - '-e', - '--encrypted', - action='store_true', - default=False, - help='set it if password is already encrypted', - ) - - args = root.parse_args() - if args.command is None: - root.print_help() - sys.exit() - - log_level = args.log_level or os.getenv('CMP_LOG') - - if isinstance(log_level, str) and log_level.lower() in log_levels: - logging.basicConfig( - level=logging.getLevelNamesMapping()[log_level.upper()] - ) - - log.debug('CLI started with args: %s', args) - - connect_uri = ( - args.connect - or os.getenv('CMP_LIBVIRT_URI') - or os.getenv('LIBVIRT_DEFAULT_URI') - or 'qemu:///system' - ) - - try: - with Session(connect_uri) as session: - main(session, args) - except ComputeError as e: - sys.exit(f'error: {e}') - except KeyboardInterrupt: - sys.exit() - except SystemExit as e: - sys.exit(e) - except Exception as e: # noqa: BLE001 - sys.exit(f'unexpected error {type(e)}: {e}') - - -if __name__ == '__main__': - cli() diff --git a/packaging/build/compute-0.1.0.dev1/.pybuild/cpython3_3.11/build/compute/common.py b/packaging/build/compute-0.1.0.dev1/.pybuild/cpython3_3.11/build/compute/common.py deleted file mode 100644 index 34a339a..0000000 --- a/packaging/build/compute-0.1.0.dev1/.pybuild/cpython3_3.11/build/compute/common.py +++ /dev/null @@ -1,30 +0,0 @@ -# This file is part of Compute -# -# Compute is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Ansible is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Ansible. If not, see . - -"""Common symbols.""" - -from abc import ABC, abstractmethod - - -class EntityConfig(ABC): - """An abstract entity XML config builder class.""" - - @abstractmethod - def to_xml(self) -> str: - """Return device XML config.""" - raise NotImplementedError - - -DeviceConfig = EntityConfig diff --git a/packaging/build/compute-0.1.0.dev1/.pybuild/cpython3_3.11/build/compute/exceptions.py b/packaging/build/compute-0.1.0.dev1/.pybuild/cpython3_3.11/build/compute/exceptions.py deleted file mode 100644 index 1eef8de..0000000 --- a/packaging/build/compute-0.1.0.dev1/.pybuild/cpython3_3.11/build/compute/exceptions.py +++ /dev/null @@ -1,80 +0,0 @@ -# This file is part of Compute -# -# Compute is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Ansible is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Ansible. If not, see . - -"""Exceptions.""" - - -class ComputeError(Exception): - """Basic exception class.""" - - -class ConfigLoaderError(ComputeError): - """Something went wrong when loading configuration.""" - - -class SessionError(ComputeError): - """Something went wrong while connecting to libvirtd.""" - - -class GuestAgentError(ComputeError): - """Something went wring when QEMU Guest Agent call.""" - - -class GuestAgentUnavailableError(GuestAgentError): - """Guest agent is not connected or is unavailable.""" - - -class GuestAgentTimeoutExceededError(GuestAgentError): - """QEMU timeout exceeded.""" - - def __init__(self, msg: int): - """Initialise GuestAgentTimeoutExceededError.""" - super().__init__(f'QEMU timeout ({msg} sec) exceeded') - - -class GuestAgentCommandNotSupportedError(GuestAgentError): - """Guest agent command is not supported or blacklisted on guest.""" - - -class StoragePoolError(ComputeError): - """Something went wrong when operating with storage pool.""" - - -class StoragePoolNotFoundError(StoragePoolError): - """Storage pool not found.""" - - def __init__(self, msg: str): - """Initialise StoragePoolNotFoundError.""" - super().__init__(f"storage pool named '{msg}' not found") - - -class VolumeNotFoundError(StoragePoolError): - """Storage volume not found.""" - - def __init__(self, msg: str): - """Initialise VolumeNotFoundError.""" - super().__init__(f"storage volume '{msg}' not found") - - -class InstanceError(ComputeError): - """Something went wrong while interacting with the domain.""" - - -class InstanceNotFoundError(InstanceError): - """Virtual machine or container not found on compute node.""" - - def __init__(self, msg: str): - """Initialise InstanceNotFoundError.""" - super().__init__(f"compute instance '{msg}' not found") diff --git a/packaging/build/compute-0.1.0.dev1/.pybuild/cpython3_3.11/build/compute/instance/__init__.py b/packaging/build/compute-0.1.0.dev1/.pybuild/cpython3_3.11/build/compute/instance/__init__.py deleted file mode 100644 index 6e2b150..0000000 --- a/packaging/build/compute-0.1.0.dev1/.pybuild/cpython3_3.11/build/compute/instance/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -# This file is part of Compute -# -# Compute is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Ansible is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Ansible. If not, see . - -from .guest_agent import GuestAgent -from .instance import Instance, InstanceConfig -from .schemas import InstanceSchema diff --git a/packaging/build/compute-0.1.0.dev1/.pybuild/cpython3_3.11/build/compute/instance/guest_agent.py b/packaging/build/compute-0.1.0.dev1/.pybuild/cpython3_3.11/build/compute/instance/guest_agent.py deleted file mode 100644 index 4381591..0000000 --- a/packaging/build/compute-0.1.0.dev1/.pybuild/cpython3_3.11/build/compute/instance/guest_agent.py +++ /dev/null @@ -1,208 +0,0 @@ -# This file is part of Compute -# -# Compute is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Ansible is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Ansible. If not, see . - -"""Interacting with the QEMU Guest Agent.""" - -import json -import logging -from base64 import b64decode, standard_b64encode -from time import sleep, time -from typing import NamedTuple - -import libvirt -import libvirt_qemu - -from compute.exceptions import ( - GuestAgentCommandNotSupportedError, - GuestAgentError, - GuestAgentTimeoutExceededError, - GuestAgentUnavailableError, -) - - -log = logging.getLogger(__name__) - - -class GuestExecOutput(NamedTuple): - """QEMU guest-exec command output.""" - - exited: bool | None = None - exitcode: int | None = None - stdout: str | None = None - stderr: str | None = None - - -class GuestAgent: - """Class for interacting with QEMU guest agent.""" - - def __init__(self, domain: libvirt.virDomain, timeout: int = 60): - """ - Initialise GuestAgent. - - :param domain: Libvirt domain object - :param timeout: QEMU timeout - """ - self.domain = domain - self.timeout = timeout - self.flags = libvirt_qemu.VIR_DOMAIN_QEMU_MONITOR_COMMAND_DEFAULT - self.last_pid = None - - def execute(self, command: dict) -> dict: - """ - Execute QEMU guest agent command. - - See: https://qemu-project.gitlab.io/qemu/interop/qemu-ga-ref.html - - :param command: QEMU guest agent command as dict - :return: Command output - :rtype: dict - """ - log.debug(command) - try: - output = libvirt_qemu.qemuAgentCommand( - self.domain, json.dumps(command), self.timeout, self.flags - ) - return json.loads(output) - except libvirt.libvirtError as e: - if e.get_error_code() == libvirt.VIR_ERR_AGENT_UNRESPONSIVE: - raise GuestAgentUnavailableError(e) from e - raise GuestAgentError(e) from e - - def is_available(self) -> bool: - """ - Execute guest-ping. - - :return: True or False if guest agent is unreachable. - :rtype: bool - """ - try: - if self.execute({'execute': 'guest-ping', 'arguments': {}}): - return True - except GuestAgentError: - return False - - def get_supported_commands(self) -> set[str]: - """Return set of supported guest agent commands.""" - output = self.execute({'execute': 'guest-info', 'arguments': {}}) - return { - cmd['name'] - for cmd in output['return']['supported_commands'] - if cmd['enabled'] is True - } - - def raise_for_commands(self, commands: list[str]) -> None: - """ - Raise exception if QEMU GA command is not available. - - :param commands: List of required commands - :raise: GuestAgentCommandNotSupportedError - """ - supported = self.get_supported_commands() - for command in commands: - if command not in supported: - raise GuestAgentCommandNotSupportedError(command) - - def guest_exec( # noqa: PLR0913 - self, - path: str, - args: list[str] | None = None, - env: list[str] | None = None, - stdin: str | None = None, - *, - capture_output: bool = False, - decode_output: bool = False, - poll: bool = False, - ) -> GuestExecOutput: - """ - Execute qemu-exec command and return output. - - :param path: Path ot executable on guest. - :param arg: List of arguments to pass to executable. - :param env: List of environment variables to pass to executable. - For example: ``['LANG=C', 'TERM=xterm']`` - :param stdin: Data to pass to executable STDIN. - :param capture_output: Capture command output. - :param decode_output: Use base64_decode() to decode command output. - Affects only if `capture_output` is True. - :param poll: Poll command output. Uses `self.timeout` and - POLL_INTERVAL constant. - :return: Command output - :rtype: GuestExecOutput - """ - self.raise_for_commands(['guest-exec', 'guest-exec-status']) - command = { - 'execute': 'guest-exec', - 'arguments': { - 'path': path, - **({'arg': args} if args else {}), - **({'env': env} if env else {}), - **( - { - 'input-data': standard_b64encode( - stdin.encode('utf-8') - ).decode('utf-8') - } - if stdin - else {} - ), - 'capture-output': capture_output, - }, - } - output = self.execute(command) - self.last_pid = pid = output['return']['pid'] - command_status = self.guest_exec_status(pid, poll=poll)['return'] - exited = command_status['exited'] - exitcode = command_status['exitcode'] - stdout = command_status.get('out-data', None) - stderr = command_status.get('err-data', None) - if decode_output: - stdout = b64decode(stdout or '').decode('utf-8') - stderr = b64decode(stderr or '').decode('utf-8') - return GuestExecOutput(exited, exitcode, stdout, stderr) - - def guest_exec_status( - self, pid: int, *, poll: bool = False, poll_interval: float = 0.3 - ) -> dict: - """ - Execute guest-exec-status and return output. - - :param pid: PID in guest. - :param poll: If True poll command status. - :param poll_interval: Time between attempts to obtain command status. - :return: Command output - :rtype: dict - """ - self.raise_for_commands(['guest-exec-status']) - command = { - 'execute': 'guest-exec-status', - 'arguments': {'pid': pid}, - } - if not poll: - return self.execute(command) - start_time = time() - while True: - command_status = self.execute(command) - if command_status['return']['exited']: - break - sleep(poll_interval) - now = time() - if now - start_time > self.timeout: - raise GuestAgentTimeoutExceededError(self.timeout) - log.debug( - 'Polling command pid=%s finished, time taken: %s seconds', - pid, - int(time() - start_time), - ) - return command_status diff --git a/packaging/build/compute-0.1.0.dev1/.pybuild/cpython3_3.11/build/compute/instance/instance.py b/packaging/build/compute-0.1.0.dev1/.pybuild/cpython3_3.11/build/compute/instance/instance.py deleted file mode 100644 index 5b806e6..0000000 --- a/packaging/build/compute-0.1.0.dev1/.pybuild/cpython3_3.11/build/compute/instance/instance.py +++ /dev/null @@ -1,675 +0,0 @@ -# This file is part of Compute -# -# Compute is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Ansible is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Ansible. If not, see . - -"""Manage compute instances.""" - -__all__ = ['Instance', 'InstanceConfig', 'InstanceInfo'] - -import logging -from typing import NamedTuple - -import libvirt -from lxml import etree -from lxml.builder import E - -from compute.common import DeviceConfig, EntityConfig -from compute.exceptions import ( - GuestAgentCommandNotSupportedError, - InstanceError, -) -from compute.storage import DiskConfig -from compute.utils import units - -from .guest_agent import GuestAgent -from .schemas import ( - CPUEmulationMode, - CPUSchema, - InstanceSchema, - NetworkInterfaceSchema, -) - - -log = logging.getLogger(__name__) - - -class InstanceConfig(EntityConfig): - """Compute instance XML config builder.""" - - def __init__(self, schema: InstanceSchema): - """ - Initialise InstanceConfig. - - :param schema: InstanceSchema object - """ - self.name = schema.name - self.title = schema.title - self.description = schema.description - self.memory = schema.memory - self.max_memory = schema.max_memory - self.vcpus = schema.vcpus - self.max_vcpus = schema.max_vcpus - self.cpu = schema.cpu - self.machine = schema.machine - self.emulator = schema.emulator - self.arch = schema.arch - self.boot = schema.boot - self.network_interfaces = schema.network_interfaces - - def _gen_cpu_xml(self, cpu: CPUSchema) -> etree.Element: - options = { - 'mode': cpu.emulation_mode, - 'match': 'exact', - 'check': 'partial', - } - if cpu.emulation_mode == CPUEmulationMode.HOST_PASSTHROUGH: - options['check'] = 'none' - options['migratable'] = 'on' - xml = E.cpu(**options) - if cpu.model: - xml.append(E.model(cpu.model, fallback='forbid')) - if cpu.vendor: - xml.append(E.vendor(cpu.vendor)) - if cpu.topology: - xml.append( - E.topology( - sockets=str(cpu.topology.sockets), - dies=str(cpu.topology.dies), - cores=str(cpu.topology.cores), - threads=str(cpu.topology.threads), - ) - ) - if cpu.features: - for feature in cpu.features.require: - xml.append(E.feature(policy='require', name=feature)) - for feature in cpu.features.disable: - xml.append(E.feature(policy='disable', name=feature)) - return xml - - def _gen_vcpus_xml(self, vcpus: int, max_vcpus: int) -> etree.Element: - xml = E.vcpus() - xml.append(E.vcpu(id='0', enabled='yes', hotpluggable='no', order='1')) - for i in range(max_vcpus - 1): - enabled = 'yes' if (i + 2) <= vcpus else 'no' - xml.append( - E.vcpu( - id=str(i + 1), - enabled=enabled, - hotpluggable='yes', - order=str(i + 2), - ) - ) - return xml - - def _gen_network_interface_xml( - self, interface: NetworkInterfaceSchema - ) -> etree.Element: - return E.interface( - E.source(network=interface.source), - E.mac(address=interface.mac), - type='network', - ) - - def to_xml(self) -> str: - """Return XML config for libvirt.""" - xml = E.domain(type='kvm') - xml.append(E.name(self.name)) - if self.title: - xml.append(E.title(self.title)) - if self.description: - xml.append(E.description(self.description)) - xml.append(E.metadata()) - xml.append(E.memory(str(self.max_memory * 1024), unit='KiB')) - xml.append(E.currentMemory(str(self.memory * 1024), unit='KiB')) - xml.append( - E.vcpu( - str(self.max_vcpus), - placement='static', - current=str(self.vcpus), - ) - ) - xml.append(self._gen_cpu_xml(self.cpu)) - os = E.os(E.type('hvm', machine=self.machine, arch=self.arch)) - for dev in self.boot.order: - os.append(E.boot(dev=dev)) - xml.append(os) - xml.append(E.features(E.acpi(), E.apic())) - xml.append(E.on_poweroff('destroy')) - xml.append(E.on_reboot('restart')) - xml.append(E.on_crash('restart')) - xml.append( - E.pm( - E('suspend-to-mem', enabled='no'), - E('suspend-to-disk', enabled='no'), - ) - ) - devices = E.devices() - devices.append(E.emulator(str(self.emulator))) - for interface in self.network_interfaces: - devices.append(self._gen_network_interface_xml(interface)) - devices.append(E.graphics(type='vnc', port='-1', autoport='yes')) - devices.append(E.input(type='tablet', bus='usb')) - devices.append( - E.channel( - E.source(mode='bind'), - E.target(type='virtio', name='org.qemu.guest_agent.0'), - E.address( - type='virtio-serial', controller='0', bus='0', port='1' - ), - type='unix', - ) - ) - devices.append( - E.console(E.target(type='serial', port='0'), type='pty') - ) - devices.append( - E.video( - E.model(type='vga', vram='16384', heads='1', primary='yes') - ) - ) - xml.append(devices) - return etree.tostring(xml, encoding='unicode', pretty_print=True) - - -class InstanceInfo(NamedTuple): - """ - Store compute instance info. - - Reference: - https://libvirt.org/html/libvirt-libvirt-domain.html#virDomainInfo - """ - - state: str - max_memory: int - memory: int - nproc: int - cputime: int - - -class Instance: - """Manage compute instances.""" - - def __init__(self, domain: libvirt.virDomain): - """ - Initialise Instance. - - :ivar libvirt.virDomain domain: domain object - :ivar libvirt.virConnect connection: connection object - :ivar str name: domain name - :ivar GuestAgent guest_agent: :class:`GuestAgent` object - - :param domain: libvirt domain object - """ - self.domain = domain - self.connection = domain.connect() - self.name = domain.name() - self.guest_agent = GuestAgent(domain) - - def _expand_instance_state(self, state: int) -> str: - 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[state] - - def get_info(self) -> InstanceInfo: - """Return instance info.""" - info = self.domain.info() - return InstanceInfo( - state=self._expand_instance_state(info[0]), - max_memory=info[1], - memory=info[2], - nproc=info[3], - cputime=info[4], - ) - - def get_status(self) -> str: - """ - Return instance state: 'running', 'shutoff', etc. - - Reference: - https://libvirt.org/html/libvirt-libvirt-domain.html#virDomainState - """ - try: - state, _ = self.domain.state() - except libvirt.libvirtError as e: - raise InstanceError( - 'Cannot fetch status of ' f'instance={self.name}: {e}' - ) from e - return self._expand_instance_state(state) - - def is_running(self) -> bool: - """Return True if instance is running, else return False.""" - if self.domain.isActive() != 1: - # 0 - is inactive, -1 - is error - return False - return True - - def is_autostart(self) -> bool: - """Return True if instance autostart is enabled, else return False.""" - try: - return bool(self.domain.autostart()) - except libvirt.libvirtError as e: - raise InstanceError( - f'Cannot get autostart status for ' - f'instance={self.name}: {e}' - ) from e - - def get_max_memory(self) -> int: - """Maximum memory value for domain in KiB.""" - return self.domain.maxMemory() - - def get_max_vcpus(self) -> int: - """Maximum vCPUs number for domain.""" - return self.domain.maxVcpus() - - def start(self) -> None: - """Start defined instance.""" - log.info('Starting instnce=%s', self.name) - if self.is_running(): - log.warning( - 'Already started, nothing to do instance=%s', self.name - ) - return - try: - self.domain.create() - except libvirt.libvirtError as e: - raise InstanceError( - f'Cannot start instance={self.name}: {e}' - ) from e - - def shutdown(self, method: str | None = None) -> None: - """ - Shutdown instance. - - Shutdown methods: - - SOFT - Use guest agent to shutdown. If guest agent is unavailable - NORMAL method will be used. - - NORMAL - Use method choosen by hypervisor to shutdown. Usually send ACPI - signal to guest OS. OS may ignore ACPI e.g. if guest is hanged. - - HARD - Shutdown instance without any guest OS shutdown. This is simular - to unplugging machine from power. Internally send SIGTERM to - instance process and destroy it gracefully. - - UNSAFE - Force shutdown. Internally send SIGKILL to instance process. - There is high data corruption risk! - - If method is None NORMAL method will used. - - :param method: Method used to shutdown instance - """ - methods = { - 'SOFT': libvirt.VIR_DOMAIN_SHUTDOWN_GUEST_AGENT, - 'NORMAL': libvirt.VIR_DOMAIN_SHUTDOWN_DEFAULT, - 'HARD': libvirt.VIR_DOMAIN_DESTROY_GRACEFUL, - 'UNSAFE': libvirt.VIR_DOMAIN_DESTROY_DEFAULT, - } - if method is None: - method = 'NORMAL' - if not isinstance(method, str): - raise TypeError( - f"Shutdown method must be a 'str', not {type(method)}" - ) - method = method.upper() - if method not in methods: - raise ValueError(f"Unsupported shutdown method: '{method}'") - try: - if method in ['SOFT', 'NORMAL']: - self.domain.shutdownFlags(flags=methods[method]) - elif method in ['HARD', 'UNSAFE']: - self.domain.destroyFlags(flags=methods[method]) - except libvirt.libvirtError as e: - raise InstanceError( - f'Cannot shutdown instance={self.name} ' f'{method=}: {e}' - ) from e - - def reboot(self) -> None: - """Send ACPI signal to guest OS to reboot. OS may ignore this.""" - try: - self.domain.reboot() - except libvirt.libvirtError as e: - raise InstanceError( - f'Cannot reboot instance={self.name}: {e}' - ) from e - - def reset(self) -> None: - """ - Reset instance. - - Copypaste from libvirt doc: - - 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. - - Note that there is a risk of data loss caused by reset without any - guest OS shutdown. - """ - try: - self.domain.reset() - except libvirt.libvirtError as e: - raise InstanceError( - f'Cannot reset instance={self.name}: {e}' - ) from e - - def power_reset(self) -> None: - """ - Shutdown instance and start. - - By analogy with real hardware, this is a normal server shutdown, - and then turning off from the power supply and turning it on again. - - This method is applicable in cases where there has been a - configuration change in libvirt and you need to restart the - instance to apply the new configuration. - """ - self.shutdown(method='NORMAL') - self.start() - - def set_autostart(self, *, enabled: bool) -> None: - """ - Set autostart flag for instance. - - :param enabled: Bool argument to set or unset autostart flag. - """ - autostart = 1 if enabled else 0 - try: - self.domain.setAutostart(autostart) - except libvirt.libvirtError as e: - raise InstanceError( - f'Cannot set autostart flag for instance={self.name} ' - f'{autostart=}: {e}' - ) from e - - def set_vcpus(self, nvcpus: int, *, live: bool = False) -> None: - """ - Set vCPU number. - - If `live` is True and instance is not currently running vCPUs - will set in config and will applied when instance boot. - - NB: Note that if this call is executed before the guest has - finished booting, the guest may fail to process the change. - - :param nvcpus: Number of vCPUs - :param live: Affect a running instance - """ - if nvcpus <= 0: - raise InstanceError('Cannot set zero vCPUs') - if nvcpus > self.get_max_vcpus(): - raise InstanceError('vCPUs count is greather than max_vcpus') - if nvcpus == self.get_info().nproc: - log.warning( - 'Instance instance=%s already have %s vCPUs, nothing to do', - self.name, - nvcpus, - ) - return - try: - flags = libvirt.VIR_DOMAIN_AFFECT_CONFIG - self.domain.setVcpusFlags(nvcpus, flags=flags) - if live is True: - if not self.is_running(): - log.warning( - 'Instance is not running, changes applied in ' - 'instance config.' - ) - return - flags = libvirt.VIR_DOMAIN_AFFECT_LIVE - self.domain.setVcpusFlags(nvcpus, flags=flags) - if self.guest_agent.is_available(): - try: - self.guest_agent.raise_for_commands( - ['guest-set-vcpus'] - ) - flags = libvirt.VIR_DOMAIN_VCPU_GUEST - self.domain.setVcpusFlags(nvcpus, flags=flags) - except GuestAgentCommandNotSupportedError: - log.warning( - 'Cannot set vCPUs in guest via agent, you may ' - 'need to apply changes in guest manually.' - ) - else: - log.warning( - 'Cannot set vCPUs in guest OS on instance=%s. ' - 'You may need to apply CPUs in guest manually.', - self.name, - ) - except libvirt.libvirtError as e: - raise InstanceError( - f'Cannot set vCPUs for instance={self.name}: {e}' - ) from e - - def set_memory(self, memory: int, *, live: bool = False) -> None: - """ - Set memory. - - If `live` is True and instance is not currently running set memory - in config and will applied when instance boot. - - :param memory: Memory value in mebibytes - :param live: Affect a running instance - """ - if memory <= 0: - raise InstanceError('Cannot set zero memory') - if (memory * 1024) > self.get_max_memory(): - raise InstanceError('Memory is greather than max_memory') - if (memory * 1024) == self.get_info().memory: - log.warning( - "Instance '%s' already have %s memory, nothing to do", - self.name, - memory, - ) - return - if live and self.is_running(): - flags = ( - libvirt.VIR_DOMAIN_AFFECT_LIVE - | libvirt.VIR_DOMAIN_AFFECT_CONFIG - ) - else: - flags = libvirt.VIR_DOMAIN_AFFECT_CONFIG - try: - self.domain.setMemoryFlags(memory * 1024, flags=flags) - except libvirt.libvirtError as e: - msg = f'Cannot set memory for instance={self.name} {memory=}: {e}' - raise InstanceError(msg) from e - - def _get_disk_by_target(self, target: str) -> etree.Element: - xml = etree.fromstring(self.dump_xml()) # noqa: S320 - child = xml.xpath(f'/domain/devices/disk/target[@dev="{target}"]') - return child[0].getparent() if child else None - - def attach_device( - self, device: DeviceConfig, *, live: bool = False - ) -> None: - """ - Attach device to compute instance. - - :param device: Object with device description e.g. DiskConfig - :param live: Affect a running instance - """ - if live and self.is_running(): - flags = ( - libvirt.VIR_DOMAIN_AFFECT_LIVE - | libvirt.VIR_DOMAIN_AFFECT_CONFIG - ) - else: - flags = libvirt.VIR_DOMAIN_AFFECT_CONFIG - if isinstance(device, DiskConfig): # noqa: SIM102 - if self._get_disk_by_target(device.target): - log.warning( - "Volume with target '%s' is already attached", - device.target, - ) - return - self.domain.attachDeviceFlags(device.to_xml(), flags=flags) - - def detach_device( - self, device: DeviceConfig, *, live: bool = False - ) -> None: - """ - Dettach device from compute instance. - - :param device: Object with device description e.g. DiskConfig - :param live: Affect a running instance - """ - if live and self.is_running(): - flags = ( - libvirt.VIR_DOMAIN_AFFECT_LIVE - | libvirt.VIR_DOMAIN_AFFECT_CONFIG - ) - else: - flags = libvirt.VIR_DOMAIN_AFFECT_CONFIG - if isinstance(device, DiskConfig): # noqa: SIM102 - if self._get_disk_by_target(device.target) is None: - log.warning( - "Volume with target '%s' is already detached", - device.target, - ) - return - self.domain.detachDeviceFlags(device.to_xml(), flags=flags) - - def detach_disk(self, name: str) -> None: - """ - Detach disk device by target name. - - There is no ``attach_disk()`` method. Use :func:`attach_device` - with :class:`DiskConfig` as argument. - - :param name: Disk name e.g. 'vda', 'sda', etc. This name may - not match the name of the disk inside the guest OS. - """ - xml = self._get_disk_by_target(name) - if xml is None: - log.warning( - "Volume with target '%s' is already detached", - name, - ) - return - disk_params = { - 'disk_type': xml.get('type'), - 'source': xml.find('source').get('file'), - 'target': xml.find('target').get('dev'), - 'readonly': False if xml.find('readonly') is None else True, # noqa: SIM211 - } - for param in disk_params: - if disk_params[param] is None: - msg = ( - f"Cannot detach volume with target '{name}': " - f"parameter '{param}' is not defined in libvirt XML " - 'config on host.' - ) - raise InstanceError(msg) - self.detach_device(DiskConfig(**disk_params), live=True) - - def resize_disk( - self, name: str, capacity: int, unit: units.DataUnit - ) -> None: - """ - Resize attached block device. - - :param name: Disk device name e.g. `vda`, `sda`, etc. - :param capacity: New capacity. - :param unit: Capacity unit. - """ - self.domain.blockResize( - name, - units.to_bytes(capacity, unit=unit), - flags=libvirt.VIR_DOMAIN_BLOCK_RESIZE_BYTES, - ) - - def get_disks(self) -> list[DiskConfig]: - """Return list of attached disks.""" - raise NotImplementedError - - def pause(self) -> None: - """Pause instance.""" - if not self.is_running(): - raise InstanceError('Cannot pause inactive instance') - self.domain.suspend() - - def resume(self) -> None: - """Resume paused instance.""" - self.domain.resume() - - def get_ssh_keys(self, user: str) -> list[str]: - """ - Return list of SSH keys on guest for specific user. - - :param user: Username. - """ - raise NotImplementedError - - def set_ssh_keys(self, user: str, ssh_keys: list[str]) -> None: - """ - Add SSH keys to guest for specific user. - - :param user: Username. - :param ssh_keys: List of public SSH keys. - """ - raise NotImplementedError - - def delete_ssh_keys(self, user: str, ssh_keys: list[str]) -> None: - """ - Remove SSH keys from guest for specific user. - - :param user: Username. - :param ssh_keys: List of public SSH keys. - """ - raise NotImplementedError - - def set_user_password( - self, user: str, password: str, *, encrypted: bool = False - ) -> None: - """ - Set new user password in guest OS. - - This action performs by guest agent inside the guest. - - :param user: Username. - :param password: Password. - :param encrypted: Set it to True if password is already encrypted. - Right encryption method depends on guest OS. - """ - if not self.guest_agent.is_available(): - raise InstanceError( - 'Cannot change password: guest agent is unavailable' - ) - self.guest_agent.raise_for_commands(['guest-set-user-password']) - flags = libvirt.VIR_DOMAIN_PASSWORD_ENCRYPTED if encrypted else 0 - self.domain.setUserPassword(user, password, flags=flags) - - def dump_xml(self, *, inactive: bool = False) -> str: - """Return instance XML description.""" - flags = libvirt.VIR_DOMAIN_XML_INACTIVE if inactive else 0 - return self.domain.XMLDesc(flags) - - def delete(self) -> None: - """Undefine instance.""" - # TODO @ge: delete local disks - self.shutdown(method='HARD') - self.domain.undefine() diff --git a/packaging/build/compute-0.1.0.dev1/.pybuild/cpython3_3.11/build/compute/instance/schemas.py b/packaging/build/compute-0.1.0.dev1/.pybuild/cpython3_3.11/build/compute/instance/schemas.py deleted file mode 100644 index f5a677c..0000000 --- a/packaging/build/compute-0.1.0.dev1/.pybuild/cpython3_3.11/build/compute/instance/schemas.py +++ /dev/null @@ -1,165 +0,0 @@ -# This file is part of Compute -# -# Compute is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Ansible is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Ansible. If not, see . - -"""Compute instance related objects schemas.""" - -import re -from enum import StrEnum -from pathlib import Path - -from pydantic import BaseModel, Extra, validator - -from compute.utils.units import DataUnit - - -class EntityModel(BaseModel): - """Basic entity model.""" - - class Config: - """Do not allow extra fields.""" - - extra = Extra.forbid - - -class CPUEmulationMode(StrEnum): - """CPU emulation mode enumerated.""" - - HOST_PASSTHROUGH = 'host-passthrough' - HOST_MODEL = 'host-model' - CUSTOM = 'custom' - MAXIMUM = 'maximum' - - -class CPUTopologySchema(EntityModel): - """CPU topology model.""" - - sockets: int - cores: int - threads: int - dies: int = 1 - - -class CPUFeaturesSchema(EntityModel): - """CPU features model.""" - - require: list[str] - disable: list[str] - - -class CPUSchema(EntityModel): - """CPU model.""" - - emulation_mode: CPUEmulationMode - model: str | None - vendor: str | None - topology: CPUTopologySchema | None - features: CPUFeaturesSchema | None - - -class VolumeType(StrEnum): - """Storage volume types enumeration.""" - - FILE = 'file' - - -class VolumeCapacitySchema(EntityModel): - """Storage volume capacity field model.""" - - value: int - unit: DataUnit - - -class VolumeSchema(EntityModel): - """Storage volume model.""" - - type: VolumeType # noqa: A003 - target: str - capacity: VolumeCapacitySchema - source: str | None = None - is_readonly: bool = False - is_system: bool = False - - -class NetworkInterfaceSchema(EntityModel): - """Network inerface model.""" - - source: str - mac: str - - -class BootOptionsSchema(EntityModel): - """Instance boot settings.""" - - order: tuple - - -class InstanceSchema(EntityModel): - """Compute instance model.""" - - name: str - title: str | None - description: str | None - memory: int - max_memory: int - vcpus: int - max_vcpus: int - cpu: CPUSchema - machine: str - emulator: Path - arch: str - boot: BootOptionsSchema - volumes: list[VolumeSchema] - network_interfaces: list[NetworkInterfaceSchema] - image: str | None = None - - @validator('name') - def _check_name(cls, value: str) -> str: # noqa: N805 - if not re.match(r'^[a-z0-9_]+$', value): - msg = ( - 'Name can contain only lowercase letters, numbers ' - 'and underscore.' - ) - raise ValueError(msg) - return value - - @validator('cpu') - def _check_topology(cls, cpu: int, values: dict) -> CPUSchema: # noqa: N805 - topo = cpu.topology - max_vcpus = values['max_vcpus'] - if topo and topo.sockets * topo.cores * topo.threads != max_vcpus: - msg = f'CPU topology does not match with {max_vcpus=}' - raise ValueError(msg) - return cpu - - @validator('volumes') - def _check_volumes(cls, volumes: list) -> list: # noqa: N805 - if len([v for v in volumes if v.is_system is True]) != 1: - msg = 'volumes list must contain one system volume' - raise ValueError(msg) - vol_with_source = 0 - for vol in volumes: - if vol.is_system is True and vol.is_readonly is True: - msg = 'volume marked as system cannot be readonly' - raise ValueError(msg) - if vol.source is not None: - vol_with_source += 1 - return volumes - - @validator('network_interfaces') - def _check_network_interfaces(cls, value: list) -> list: # noqa: N805 - if not value: - msg = 'Network interfaces list must contain at least one element' - raise ValueError(msg) - return value diff --git a/packaging/build/compute-0.1.0.dev1/.pybuild/cpython3_3.11/build/compute/session.py b/packaging/build/compute-0.1.0.dev1/.pybuild/cpython3_3.11/build/compute/session.py deleted file mode 100644 index de5f900..0000000 --- a/packaging/build/compute-0.1.0.dev1/.pybuild/cpython3_3.11/build/compute/session.py +++ /dev/null @@ -1,286 +0,0 @@ -# This file is part of Compute -# -# Compute is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Ansible is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Ansible. If not, see . - -"""Hypervisor session manager.""" - -import logging -import os -from contextlib import AbstractContextManager -from types import TracebackType -from typing import Any, NamedTuple -from uuid import uuid4 - -import libvirt -from lxml import etree - -from .exceptions import ( - InstanceNotFoundError, - SessionError, - StoragePoolNotFoundError, -) -from .instance import Instance, InstanceConfig, InstanceSchema -from .storage import DiskConfig, StoragePool, VolumeConfig -from .utils import units - - -log = logging.getLogger(__name__) - - -class Capabilities(NamedTuple): - """Store domain capabilities info.""" - - arch: str - virt_type: str - emulator: str - machine: str - max_vcpus: int - cpu_vendor: str - cpu_model: str - cpu_features: dict - usable_cpus: list[dict] - - -class NodeInfo(NamedTuple): - """ - Store compute node info. - - See https://libvirt.org/html/libvirt-libvirt-host.html#virNodeInfo - NOTE: memory unit in libvirt docs is wrong! Actual unit is MiB. - """ - - arch: str - memory: int - cpus: int - mhz: int - nodes: int - sockets: int - cores: int - threads: int - - -class Session(AbstractContextManager): - """ - Hypervisor session context manager. - - :cvar IMAGES_POOL: images storage pool name taken from env - :cvar VOLUMES_POOL: volumes storage pool name taken from env - """ - - IMAGES_POOL = os.getenv('CMP_IMAGES_POOL') - VOLUMES_POOL = os.getenv('CMP_VOLUMES_POOL') - - def __init__(self, uri: str | None = None): - """ - Initialise session with hypervisor. - - :ivar str uri: libvirt connection URI. - :ivar libvirt.virConnect connection: libvirt connection object. - - :param uri: libvirt connection URI. - """ - self.uri = uri or 'qemu:///system' - self.connection = libvirt.open(self.uri) - - def __enter__(self): - """Return Session object.""" - return self - - def __exit__( - self, - exc_type: type[BaseException] | None, - exc_value: BaseException | None, - exc_traceback: TracebackType | None, - ): - """Close the connection when leaving the context.""" - self.close() - - def close(self) -> None: - """Close connection to libvirt daemon.""" - self.connection.close() - - def get_node_info(self) -> NodeInfo: - """Return information about compute node.""" - info = self.connection.getInfo() - return NodeInfo( - arch=info[0], - memory=info[1], - cpus=info[2], - mhz=info[3], - nodes=info[4], - sockets=info[5], - cores=info[6], - threads=info[7], - ) - - def _cap_get_usable_cpus(self, xml: etree.Element) -> list[dict]: - x = xml.xpath('/domainCapabilities/cpu/mode[@name="custom"]')[0] - cpus = [] - for cpu in x.findall('model'): - if cpu.get('usable') == 'yes': - cpus.append( # noqa: PERF401 - { - 'vendor': cpu.get('vendor'), - 'model': cpu.text, - } - ) - return cpus - - def _cap_get_cpu_features(self, xml: etree.Element) -> dict: - x = xml.xpath('/domainCapabilities/cpu/mode[@name="host-model"]')[0] - require = [] - disable = [] - for feature in x.findall('feature'): - policy = feature.get('policy') - name = feature.get('name') - if policy == 'require': - require.append(name) - if policy == 'disable': - disable.append(name) - return {'require': require, 'disable': disable} - - def get_capabilities(self) -> Capabilities: - """Return capabilities e.g. arch, virt, emulator, etc.""" - prefix = '/domainCapabilities' - hprefix = f'{prefix}/cpu/mode[@name="host-model"]' - caps = etree.fromstring(self.connection.getDomainCapabilities()) # noqa: S320 - return Capabilities( - arch=caps.xpath(f'{prefix}/arch/text()')[0], - virt_type=caps.xpath(f'{prefix}/domain/text()')[0], - emulator=caps.xpath(f'{prefix}/path/text()')[0], - machine=caps.xpath(f'{prefix}/machine/text()')[0], - max_vcpus=int(caps.xpath(f'{prefix}/vcpu/@max')[0]), - cpu_vendor=caps.xpath(f'{hprefix}/vendor/text()')[0], - cpu_model=caps.xpath(f'{hprefix}/model/text()')[0], - cpu_features=self._cap_get_cpu_features(caps), - usable_cpus=self._cap_get_cpus(caps), - ) - - def create_instance(self, **kwargs: Any) -> Instance: - """ - Create and return new compute instance. - - :param name: Instance name. - :type name: str - :param title: Instance title for humans. - :type title: str - :param description: Some information about instance. - :type description: str - :param memory: Memory in MiB. - :type memory: int - :param max_memory: Maximum memory in MiB. - :type max_memory: int - :param vcpus: Number of vCPUs. - :type vcpus: int - :param max_vcpus: Maximum vCPUs. - :type max_vcpus: int - :param cpu: CPU configuration. See :class:`CPUSchema` for info. - :type cpu: dict - :param machine: QEMU emulated machine. - :type machine: str - :param emulator: Path to emulator. - :type emulator: str - :param arch: CPU architecture to virtualization. - :type arch: str - :param boot: Boot settings. See :class:`BootOptionsSchema`. - :type boot: dict - :param image: Source disk image name for system disk. - :type image: str - :param volumes: List of storage volume configs. For more info - see :class:`VolumeSchema`. - :type volumes: list[dict] - :param network_interfaces: List of virtual network interfaces - configs. See :class:`NetworkInterfaceSchema` for more info. - :type network_interfaces: list[dict] - """ - data = InstanceSchema(**kwargs) - config = InstanceConfig(data) - log.info('Define XML...') - log.info(config.to_xml()) - self.connection.defineXML(config.to_xml()) - log.info('Getting instance...') - instance = self.get_instance(config.name) - log.info('Creating volumes...') - for volume in data.volumes: - log.info('Creating volume=%s', volume) - capacity = units.to_bytes( - volume.capacity.value, volume.capacity.unit - ) - log.info('Connecting to images pool...') - images_pool = self.get_storage_pool(self.IMAGES_POOL) - log.info('Connecting to volumes pool...') - volumes_pool = self.get_storage_pool(self.VOLUMES_POOL) - log.info('Building volume configuration...') - if not volume.source: - vol_name = f'{uuid4()}.qcow2' - else: - vol_name = volume.source - vol_conf = VolumeConfig( - name=vol_name, - path=str(volumes_pool.path.joinpath(vol_name)), - capacity=capacity, - ) - log.info('Volume configuration is:\n %s', vol_conf.to_xml()) - if volume.is_system is True and data.image: - log.info( - "Volume is marked as 'system', start cloning image..." - ) - log.info('Get image %s', data.image) - image = images_pool.get_volume(data.image) - log.info('Cloning image into volumes pool...') - vol = volumes_pool.clone_volume(image, vol_conf) - log.info( - 'Resize cloned volume to specified size: %s', - capacity, - ) - vol.resize(capacity, unit=units.DataUnit.BYTES) - else: - log.info('Create volume...') - volumes_pool.create_volume(vol_conf) - log.info('Attaching volume to instance...') - instance.attach_device( - DiskConfig( - disk_type=volume.type, - source=vol_conf.path, - target=volume.target, - readonly=volume.is_readonly, - ) - ) - return instance - - def get_instance(self, name: str) -> Instance: - """Get compute instance by name.""" - try: - return Instance(self.connection.lookupByName(name)) - except libvirt.libvirtError as e: - if e.get_error_code() == libvirt.VIR_ERR_NO_DOMAIN: - raise InstanceNotFoundError(name) from e - raise SessionError(e) from e - - def list_instances(self) -> list[Instance]: - """List all instances.""" - return [Instance(dom) for dom in self.connection.listAllDomains()] - - def get_storage_pool(self, name: str) -> StoragePool: - """Get storage pool by name.""" - try: - return StoragePool(self.connection.storagePoolLookupByName(name)) - except libvirt.libvirtError as e: - if e.get_error_code() == libvirt.VIR_ERR_NO_STORAGE_POOL: - raise StoragePoolNotFoundError(name) from e - raise SessionError(e) from e - - def list_storage_pools(self) -> list[StoragePool]: - """List all strage pools.""" - return [StoragePool(p) for p in self.connection.listStoragePools()] diff --git a/packaging/build/compute-0.1.0.dev1/.pybuild/cpython3_3.11/build/compute/storage/__init__.py b/packaging/build/compute-0.1.0.dev1/.pybuild/cpython3_3.11/build/compute/storage/__init__.py deleted file mode 100644 index 34aae30..0000000 --- a/packaging/build/compute-0.1.0.dev1/.pybuild/cpython3_3.11/build/compute/storage/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -# This file is part of Compute -# -# Compute is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Ansible is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Ansible. If not, see . - -from .pool import StoragePool -from .volume import DiskConfig, Volume, VolumeConfig diff --git a/packaging/build/compute-0.1.0.dev1/.pybuild/cpython3_3.11/build/compute/storage/pool.py b/packaging/build/compute-0.1.0.dev1/.pybuild/cpython3_3.11/build/compute/storage/pool.py deleted file mode 100644 index cb17494..0000000 --- a/packaging/build/compute-0.1.0.dev1/.pybuild/cpython3_3.11/build/compute/storage/pool.py +++ /dev/null @@ -1,124 +0,0 @@ -# This file is part of Compute -# -# Compute is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Ansible is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Ansible. If not, see . - -"""Manage storage pools.""" - -import logging -from pathlib import Path -from typing import NamedTuple - -import libvirt -from lxml import etree - -from compute.exceptions import StoragePoolError, VolumeNotFoundError - -from .volume import Volume, VolumeConfig - - -log = logging.getLogger(__name__) - - -class StoragePoolUsageInfo(NamedTuple): - """Storage pool usage info.""" - - capacity: int - allocation: int - available: int - - -class StoragePool: - """Storage pool manipulating class.""" - - def __init__(self, pool: libvirt.virStoragePool): - """Initislise StoragePool.""" - self.pool = pool - self.name = pool.name() - self.path = self._get_path() - - def _get_path(self) -> Path: - """Return storage pool path.""" - xml = etree.fromstring(self.pool.XMLDesc()) # noqa: S320 - return Path(xml.xpath('/pool/target/path/text()')[0]) - - def get_usage_info(self) -> StoragePoolUsageInfo: - """Return info about storage pool usage.""" - xml = etree.fromstring(self.pool.XMLDesc()) # noqa: S320 - return StoragePoolUsageInfo( - capacity=int(xml.xpath('/pool/capacity/text()')[0]), - allocation=int(xml.xpath('/pool/allocation/text()')[0]), - available=int(xml.xpath('/pool/available/text()')[0]), - ) - - def dump_xml(self) -> str: - """Return storage pool XML description as string.""" - return self.pool.XMLDesc() - - def refresh(self) -> None: - """Refresh storage pool.""" - # TODO @ge: handle libvirt asynchronous job related exceptions - self.pool.refresh() - - def create_volume(self, vol_conf: VolumeConfig) -> Volume: - """Create storage volume and return Volume instance.""" - log.info( - 'Create storage volume vol=%s in pool=%s', vol_conf.name, self.name - ) - vol = self.pool.createXML( - vol_conf.to_xml(), - flags=libvirt.VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA, - ) - return Volume(self.pool, vol) - - def clone_volume(self, src: Volume, dst: VolumeConfig) -> Volume: - """ - Make storage volume copy. - - :param src: Input volume - :param dst: Output volume config - """ - log.info( - 'Start volume cloning ' - 'src_pool=%s src_vol=%s dst_pool=%s dst_vol=%s', - src.pool_name, - src.name, - self.pool.name, - dst.name, - ) - vol = self.pool.createXMLFrom( - dst.to_xml(), # new volume XML description - src.vol, # source volume virStorageVol object - flags=libvirt.VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA, - ) - if vol is None: - raise StoragePoolError - return Volume(self.pool, vol) - - def get_volume(self, name: str) -> Volume | None: - """Lookup and return Volume instance or None.""" - log.info( - 'Lookup for storage volume vol=%s in pool=%s', name, self.pool.name - ) - try: - vol = self.pool.storageVolLookupByName(name) - return Volume(self.pool, vol) - except libvirt.libvirtError as e: - if e.get_error_code() == libvirt.VIR_ERR_NO_STORAGE_VOL: - raise VolumeNotFoundError(name) from e - log.exception('unexpected error from libvirt') - raise StoragePoolError(e) from e - - def list_volumes(self) -> list[Volume]: - """Return list of volumes in storage pool.""" - return [Volume(self.pool, vol) for vol in self.pool.listAllVolumes()] diff --git a/packaging/build/compute-0.1.0.dev1/.pybuild/cpython3_3.11/build/compute/storage/volume.py b/packaging/build/compute-0.1.0.dev1/.pybuild/cpython3_3.11/build/compute/storage/volume.py deleted file mode 100644 index 11a1dc4..0000000 --- a/packaging/build/compute-0.1.0.dev1/.pybuild/cpython3_3.11/build/compute/storage/volume.py +++ /dev/null @@ -1,138 +0,0 @@ -# This file is part of Compute -# -# Compute is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Ansible is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Ansible. If not, see . - -"""Manage storage volumes.""" - -from dataclasses import dataclass -from pathlib import Path -from time import time - -import libvirt -from lxml import etree -from lxml.builder import E - -from compute.common import DeviceConfig, EntityConfig -from compute.utils import units - - -@dataclass -class VolumeConfig(EntityConfig): - """ - Storage volume XML config builder. - - Generate XML config for creating a volume in a libvirt - storage pool. - """ - - name: str - path: str - capacity: int - - def to_xml(self) -> str: - """Return XML config for libvirt.""" - unixtime = str(int(time())) - xml = E.volume(type='file') - xml.append(E.name(self.name)) - xml.append(E.key(self.path)) - xml.append(E.source()) - xml.append(E.capacity(str(self.capacity), unit='bytes')) - xml.append(E.allocation('0')) - xml.append( - E.target( - E.path(self.path), - E.format(type='qcow2'), - E.timestamps( - E.atime(unixtime), E.mtime(unixtime), E.ctime(unixtime) - ), - E.compat('1.1'), - E.features(E.lazy_refcounts()), - ) - ) - return etree.tostring(xml, encoding='unicode', pretty_print=True) - - -@dataclass -class DiskConfig(DeviceConfig): - """ - Disk XML config builder. - - Generate XML config for attaching or detaching storage volumes - to compute instances. - """ - - disk_type: str - source: str | Path - target: str - readonly: bool = False - - def to_xml(self) -> str: - """Return XML config for libvirt.""" - xml = E.disk(type=self.disk_type, device='disk') - xml.append(E.driver(name='qemu', type='qcow2', cache='writethrough')) - if self.disk_type == 'file': - xml.append(E.source(file=str(self.source))) - xml.append(E.target(dev=self.target, bus='virtio')) - if self.readonly: - xml.append(E.readonly()) - return etree.tostring(xml, encoding='unicode', pretty_print=True) - - -class Volume: - """Storage volume manipulating class.""" - - def __init__( - self, pool: libvirt.virStoragePool, vol: libvirt.virStorageVol - ): - """ - Initialise Volume. - - :param pool: libvirt virStoragePool object - :param vol: libvirt virStorageVol object - """ - self.pool = pool - self.pool_name = pool.name() - self.vol = vol - self.name = vol.name() - self.path = Path(vol.path()) - - def dump_xml(self) -> str: - """Return volume XML description as string.""" - return self.vol.XMLDesc() - - def clone(self, vol_conf: VolumeConfig) -> None: - """ - Make a copy of volume to the same storage pool. - - :param vol_info VolumeInfo: New storage volume dataclass object - """ - self.pool.createXMLFrom( - vol_conf.to_xml(), - self.vol, - flags=libvirt.VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA, - ) - - def resize(self, capacity: int, unit: units.DataUnit) -> None: - """ - Resize volume. - - :param capacity int: Volume new capacity. - :param unit DataUnit: Data unit. Internally converts into bytes. - """ - # TODO @ge: Check actual volume size before resize - self.vol.resize(units.to_bytes(capacity, unit=unit)) - - def delete(self) -> None: - """Delete volume from storage pool.""" - self.vol.delete() diff --git a/packaging/build/compute-0.1.0.dev1/.pybuild/cpython3_3.11/build/compute/utils/__init__.py b/packaging/build/compute-0.1.0.dev1/.pybuild/cpython3_3.11/build/compute/utils/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/packaging/build/compute-0.1.0.dev1/.pybuild/cpython3_3.11/build/compute/utils/config_loader.py b/packaging/build/compute-0.1.0.dev1/.pybuild/cpython3_3.11/build/compute/utils/config_loader.py deleted file mode 100644 index aaeb0fe..0000000 --- a/packaging/build/compute-0.1.0.dev1/.pybuild/cpython3_3.11/build/compute/utils/config_loader.py +++ /dev/null @@ -1,56 +0,0 @@ -# This file is part of Compute -# -# Compute is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Ansible is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Ansible. If not, see . - -"""Configuration loader.""" - -import tomllib -from collections import UserDict -from pathlib import Path - -from compute.exceptions import ConfigLoaderError - - -DEFAULT_CONFIGURATION = {} -DEFAULT_CONFIG_FILE = '/etc/computed/computed.toml' - - -class ConfigLoader(UserDict): - """UserDict for storing configuration.""" - - def __init__(self, file: Path | None = None): - """ - Initialise ConfigLoader. - - :param file: Path to configuration file. If `file` is None - use default path from DEFAULT_CONFIG_FILE constant. - """ - # TODO @ge: load deafult configuration - self.file = Path(file) if file else Path(DEFAULT_CONFIG_FILE) - super().__init__(self.load()) - - def load(self) -> dict: - """Load confguration object from TOML file.""" - try: - with Path(self.file).open('rb') as configfile: - return tomllib.load(configfile) - # TODO @ge: add config schema validation - except tomllib.TOMLDecodeError as tomlerr: - raise ConfigLoaderError( - f'Bad TOML syntax in config file: {self.file}: {tomlerr}' - ) from tomlerr - except (OSError, ValueError) as readerr: - raise ConfigLoaderError( - f'Cannot read config file: {self.file}: {readerr}' - ) from readerr diff --git a/packaging/build/compute-0.1.0.dev1/.pybuild/cpython3_3.11/build/compute/utils/ids.py b/packaging/build/compute-0.1.0.dev1/.pybuild/cpython3_3.11/build/compute/utils/ids.py deleted file mode 100644 index 8a6454a..0000000 --- a/packaging/build/compute-0.1.0.dev1/.pybuild/cpython3_3.11/build/compute/utils/ids.py +++ /dev/null @@ -1,33 +0,0 @@ -# This file is part of Compute -# -# Compute is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Ansible is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Ansible. If not, see . - -"""Random identificators.""" - -# ruff: noqa: S311, C417 - -import random - - -def random_mac() -> str: - """Retrun random MAC address.""" - mac = [ - 0x00, - 0x16, - 0x3E, - random.randint(0x00, 0x7F), - random.randint(0x00, 0xFF), - random.randint(0x00, 0xFF), - ] - return ':'.join(map(lambda x: '%02x' % x, mac)) diff --git a/packaging/build/compute-0.1.0.dev1/.pybuild/cpython3_3.11/build/compute/utils/units.py b/packaging/build/compute-0.1.0.dev1/.pybuild/cpython3_3.11/build/compute/utils/units.py deleted file mode 100644 index 57a4583..0000000 --- a/packaging/build/compute-0.1.0.dev1/.pybuild/cpython3_3.11/build/compute/utils/units.py +++ /dev/null @@ -1,54 +0,0 @@ -# This file is part of Compute -# -# Compute is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Ansible is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Ansible. If not, see . - -"""Tools for data units convertion.""" - -from enum import StrEnum - - -class DataUnit(StrEnum): - """Data units enumerated.""" - - BYTES = 'bytes' - KIB = 'KiB' - MIB = 'MiB' - GIB = 'GiB' - TIB = 'TiB' - - -class InvalidDataUnitError(ValueError): - """Data unit is not valid.""" - - def __init__(self, msg: str): - """Initialise InvalidDataUnitError.""" - super().__init__( - f'{msg}, valid units are: {", ".join(list(DataUnit))}' - ) - - -def to_bytes(value: int, unit: DataUnit = DataUnit.BYTES) -> int: - """Convert value to bytes. See :class:`DataUnit`.""" - try: - _ = DataUnit(unit) - except ValueError as e: - raise InvalidDataUnitError(e) from e - powers = { - DataUnit.BYTES: 0, - DataUnit.KIB: 1, - DataUnit.MIB: 2, - DataUnit.GIB: 3, - DataUnit.TIB: 4, - } - return value * pow(1024, powers[unit]) diff --git a/packaging/build/compute-0.1.0.dev1/.pybuild/cpython3_3.11/compute-0.1.0.dev1-py3-none-any.whl b/packaging/build/compute-0.1.0.dev1/.pybuild/cpython3_3.11/compute-0.1.0.dev1-py3-none-any.whl deleted file mode 100644 index acf2d64..0000000 Binary files a/packaging/build/compute-0.1.0.dev1/.pybuild/cpython3_3.11/compute-0.1.0.dev1-py3-none-any.whl and /dev/null differ diff --git a/packaging/build/compute-0.1.0.dev1/.pybuild/cpython3_3.11/scripts/compute b/packaging/build/compute-0.1.0.dev1/.pybuild/cpython3_3.11/scripts/compute deleted file mode 100755 index 56e33f2..0000000 --- a/packaging/build/compute-0.1.0.dev1/.pybuild/cpython3_3.11/scripts/compute +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/python3 -# -*- coding: utf-8 -*- -import re -import sys -from compute.cli.control import cli -if __name__ == "__main__": - sys.argv[0] = re.sub(r"(-script\.pyw|\.exe)?$", "", sys.argv[0]) - sys.exit(cli()) diff --git a/packaging/build/compute-0.1.0.dev1/PKG-INFO b/packaging/build/compute-0.1.0.dev1/PKG-INFO deleted file mode 100644 index f4c22ad..0000000 --- a/packaging/build/compute-0.1.0.dev1/PKG-INFO +++ /dev/null @@ -1,81 +0,0 @@ -Metadata-Version: 2.1 -Name: compute -Version: 0.1.0.dev1 -Summary: Compute instances management library and tools -Author: ge -Author-email: ge@nixhacks.net -Requires-Python: >=3.11,<4.0 -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.11 -Requires-Dist: libvirt-python (==9.0.0) -Requires-Dist: lxml (>=4.9.2,<5.0.0) -Requires-Dist: pydantic (==1.10.4) -Requires-Dist: pyyaml (>=6.0.1,<7.0.0) -Description-Content-Type: text/markdown - -# Compute - -Compute instances management library and tools. - -## Docs - -Run `make serve-docs`. See [Development](#development) below. - -## Roadmap - -- [x] Create instances -- [ ] CDROM -- [ ] cloud-init for provisioning instances -- [x] Instance power management -- [x] Instance pause and resume -- [x] vCPU hotplug -- [x] Memory hotplug -- [x] Hot disk resize [not tested] -- [ ] CPU topology customization -- [x] CPU customization (emulation mode, model, vendor, features) -- [ ] BIOS/UEFI settings -- [x] Device attaching -- [x] Device detaching -- [ ] GPU passthrough -- [ ] CPU guarantied resource percent support -- [x] QEMU Guest Agent management -- [ ] Instance resources usage stats -- [ ] SSH-keys management -- [x] Setting user passwords in guest -- [x] QCOW2 disks support -- [ ] ZVOL support -- [ ] Network disks support -- [ ] Images service integration (Images service is not implemented yet) -- [ ] Manage storage pools -- [ ] Idempotency -- [ ] CLI [in progress] -- [ ] HTTP API -- [ ] Instance migrations -- [ ] Instance snapshots -- [ ] Instance backups -- [ ] LXC - -## Development - -Python 3.11+ is required. - -Install [poetry](https://python-poetry.org/), clone this repository and run: - -``` -poetry install --with dev --with docs -``` - -# Build Debian package - -Install Docker first, then run: - -``` -make build-deb -``` - -`compute` and `compute-doc` packages will built. See packaging/build directory. Packages can be installed via `dpkg` or `apt-get`: - -``` -apt-get install ./compute*.deb -``` - diff --git a/packaging/build/compute-0.1.0.dev1/README.md b/packaging/build/compute-0.1.0.dev1/README.md deleted file mode 100644 index 0131e8e..0000000 --- a/packaging/build/compute-0.1.0.dev1/README.md +++ /dev/null @@ -1,65 +0,0 @@ -# Compute - -Compute instances management library and tools. - -## Docs - -Run `make serve-docs`. See [Development](#development) below. - -## Roadmap - -- [x] Create instances -- [ ] CDROM -- [ ] cloud-init for provisioning instances -- [x] Instance power management -- [x] Instance pause and resume -- [x] vCPU hotplug -- [x] Memory hotplug -- [x] Hot disk resize [not tested] -- [ ] CPU topology customization -- [x] CPU customization (emulation mode, model, vendor, features) -- [ ] BIOS/UEFI settings -- [x] Device attaching -- [x] Device detaching -- [ ] GPU passthrough -- [ ] CPU guarantied resource percent support -- [x] QEMU Guest Agent management -- [ ] Instance resources usage stats -- [ ] SSH-keys management -- [x] Setting user passwords in guest -- [x] QCOW2 disks support -- [ ] ZVOL support -- [ ] Network disks support -- [ ] Images service integration (Images service is not implemented yet) -- [ ] Manage storage pools -- [ ] Idempotency -- [ ] CLI [in progress] -- [ ] HTTP API -- [ ] Instance migrations -- [ ] Instance snapshots -- [ ] Instance backups -- [ ] LXC - -## Development - -Python 3.11+ is required. - -Install [poetry](https://python-poetry.org/), clone this repository and run: - -``` -poetry install --with dev --with docs -``` - -# Build Debian package - -Install Docker first, then run: - -``` -make build-deb -``` - -`compute` and `compute-doc` packages will built. See packaging/build directory. Packages can be installed via `dpkg` or `apt-get`: - -``` -apt-get install ./compute*.deb -``` diff --git a/packaging/build/compute-0.1.0.dev1/compute/__init__.py b/packaging/build/compute-0.1.0.dev1/compute/__init__.py deleted file mode 100644 index ffe06d7..0000000 --- a/packaging/build/compute-0.1.0.dev1/compute/__init__.py +++ /dev/null @@ -1,22 +0,0 @@ -# This file is part of Compute -# -# Compute is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Ansible is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Ansible. If not, see . - -"""Compute instances management library.""" - -__version__ = '0.1.0-dev1' - -from .instance import Instance, InstanceConfig, InstanceSchema -from .session import Session -from .storage import StoragePool, Volume, VolumeConfig diff --git a/packaging/build/compute-0.1.0.dev1/compute/__main__.py b/packaging/build/compute-0.1.0.dev1/compute/__main__.py deleted file mode 100644 index 4995fbd..0000000 --- a/packaging/build/compute-0.1.0.dev1/compute/__main__.py +++ /dev/null @@ -1,21 +0,0 @@ -# This file is part of Compute -# -# Compute is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Ansible is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Ansible. If not, see . - -"""Command line interface for compute module.""" - -from compute.cli import main - - -main.cli() diff --git a/packaging/build/compute-0.1.0.dev1/compute/cli/__init__.py b/packaging/build/compute-0.1.0.dev1/compute/cli/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/packaging/build/compute-0.1.0.dev1/compute/cli/control.py b/packaging/build/compute-0.1.0.dev1/compute/cli/control.py deleted file mode 100644 index f5a5b91..0000000 --- a/packaging/build/compute-0.1.0.dev1/compute/cli/control.py +++ /dev/null @@ -1,501 +0,0 @@ -# This file is part of Compute -# -# Compute is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Ansible is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Ansible. If not, see . - -"""Command line interface.""" - -import argparse -import io -import logging -import os -import shlex -import sys -from collections import UserDict -from typing import Any -from uuid import uuid4 - -import libvirt -import yaml -from pydantic import ValidationError - -from compute import __version__ -from compute.exceptions import ComputeError, GuestAgentTimeoutExceededError -from compute.instance import GuestAgent -from compute.session import Session -from compute.utils import ids - - -log = logging.getLogger(__name__) -log_levels = [lv.lower() for lv in logging.getLevelNamesMapping()] - -libvirt.registerErrorHandler( - lambda userdata, err: None, # noqa: ARG005 - ctx=None, -) - - -class Table: - """Minimalistic text table constructor.""" - - def __init__(self, whitespace: str | None = None): - """Initialise Table.""" - self.whitespace = whitespace or '\t' - self.header = [] - self.rows = [] - self.table = '' - - def add_row(self, row: list) -> None: - """Add table row.""" - self.rows.append([str(col) for col in row]) - - def add_rows(self, rows: list[list]) -> None: - """Add multiple rows.""" - for row in rows: - self.add_row(row) - - def __str__(self) -> str: - """Build table and return.""" - widths = [max(map(len, col)) for col in zip(*self.rows, strict=True)] - self.rows.insert(0, [str(h).upper() for h in self.header]) - for row in self.rows: - self.table += self.whitespace.join( - ( - val.ljust(width) - for val, width in zip(row, widths, strict=True) - ) - ) - self.table += '\n' - return self.table.strip() - - -def _list_instances(session: Session) -> None: - table = Table() - table.header = ['NAME', 'STATE'] - for instance in session.list_instances(): - table.add_row( - [ - instance.name, - instance.get_status(), - ] - ) - print(table) - sys.exit() - - -def _exec_guest_agent_command( - session: Session, args: argparse.Namespace -) -> None: - instance = session.get_instance(args.instance) - ga = GuestAgent(instance.domain, timeout=args.timeout) - arguments = args.arguments.copy() - if len(arguments) > 1 and not args.no_join_args: - arguments = [shlex.join(arguments)] - if not args.no_join_args: - arguments.insert(0, '-c') - stdin = None - if not sys.stdin.isatty(): - stdin = sys.stdin.read() - try: - output = ga.guest_exec( - path=args.executable, - args=arguments, - env=args.env, - stdin=stdin, - capture_output=True, - decode_output=True, - poll=True, - ) - except GuestAgentTimeoutExceededError as e: - sys.exit( - f'{e}. NOTE: command may still running in guest, ' - f'PID={ga.last_pid}' - ) - if output.stderr: - print(output.stderr.strip(), file=sys.stderr) - if output.stdout: - print(output.stdout.strip(), file=sys.stdout) - sys.exit(output.exitcode) - - -class _NotPresent: - """ - Type for representing non-existent dictionary keys. - - See :class:`_FillableDict`. - """ - - -class _FillableDict(UserDict): - """Use :method:`fill` to add key if not present.""" - - def __init__(self, data: dict): - self.data = data - - def fill(self, key: str, value: Any) -> None: # noqa: ANN401 - if self.data.get(key, _NotPresent) is _NotPresent: - self.data[key] = value - - -def _merge_dicts(a: dict, b: dict, path: list[str] | None = None) -> dict: - """Merge `b` into `a`. Return modified `a`.""" - if path is None: - path = [] - for key in b: - if key in a: - if isinstance(a[key], dict) and isinstance(b[key], dict): - _merge_dicts(a[key], b[key], [path + str(key)]) - elif a[key] == b[key]: - pass # same leaf value - else: - a[key] = b[key] # replace existing key's values - else: - a[key] = b[key] - return a - - -def _create_instance(session: Session, file: io.TextIOWrapper) -> None: - try: - data = _FillableDict(yaml.load(file.read(), Loader=yaml.SafeLoader)) - log.debug('Read from file: %s', data) - except yaml.YAMLError as e: - sys.exit(f'error: cannot parse YAML: {e}') - - capabilities = session.get_capabilities() - node_info = session.get_node_info() - - data.fill('name', uuid4().hex) - data.fill('title', None) - data.fill('description', None) - data.fill('arch', capabilities.arch) - data.fill('machine', capabilities.machine) - data.fill('emulator', capabilities.emulator) - data.fill('max_vcpus', node_info.cpus) - data.fill('max_memory', node_info.memory) - data.fill('cpu', {}) - cpu = { - 'emulation_mode': 'host-passthrough', - 'model': None, - 'vendor': None, - 'topology': None, - 'features': None, - } - data['cpu'] = _merge_dicts(data['cpu'], cpu) - data.fill( - 'network_interfaces', - [{'source': 'default', 'mac': ids.random_mac()}], - ) - data.fill('boot', {'order': ['cdrom', 'hd']}) - - try: - log.debug('Input data: %s', data) - session.create_instance(**data) - except ValidationError as e: - for error in e.errors(): - fields = '.'.join([str(lc) for lc in error['loc']]) - print( - f"validation error: {fields}: {error['msg']}", - file=sys.stderr, - ) - sys.exit() - - -def _shutdown_instance(session: Session, args: argparse.Namespace) -> None: - instance = session.get_instance(args.instance) - if args.soft: - method = 'SOFT' - elif args.hard: - method = 'HARD' - elif args.unsafe: - method = 'UNSAFE' - else: - method = 'NORMAL' - instance.shutdown(method) - - -def main(session: Session, args: argparse.Namespace) -> None: - """Perform actions.""" - match args.command: - case 'init': - _create_instance(session, args.file) - case 'exec': - _exec_guest_agent_command(session, args) - case 'ls': - _list_instances(session) - case 'start': - instance = session.get_instance(args.instance) - instance.start() - case 'shutdown': - _shutdown_instance(session, args) - case 'reboot': - instance = session.get_instance(args.instance) - instance.reboot() - case 'reset': - instance = session.get_instance(args.instance) - instance.reset() - case 'powrst': - instance = session.get_instance(args.instance) - instance.power_reset() - case 'pause': - instance = session.get_instance(args.instance) - instance.pause() - case 'resume': - instance = session.get_instance(args.instance) - instance.resume() - case 'status': - instance = session.get_instance(args.instance) - print(instance.status) - case 'setvcpus': - instance = session.get_instance(args.instance) - instance.set_vcpus(args.nvcpus, live=True) - case 'setmem': - instance = session.get_instance(args.instance) - instance.set_memory(args.memory, live=True) - case 'setpass': - instance = session.get_instance(args.instance) - instance.set_user_password( - args.username, - args.password, - encrypted=args.encrypted, - ) - - -def cli() -> None: # noqa: PLR0915 - """Return command line arguments parser.""" - root = argparse.ArgumentParser( - prog='compute', - description='manage compute instances', - formatter_class=argparse.RawTextHelpFormatter, - ) - root.add_argument( - '-v', - '--verbose', - action='store_true', - default=False, - help='enable verbose mode', - ) - root.add_argument( - '-c', - '--connect', - metavar='URI', - help='libvirt connection URI', - ) - root.add_argument( - '-l', - '--log-level', - type=str.lower, - metavar='LEVEL', - choices=log_levels, - help='log level', - ) - root.add_argument( - '-V', - '--version', - action='version', - version=__version__, - ) - subparsers = root.add_subparsers(dest='command', metavar='COMMAND') - - # init command - init = subparsers.add_parser( - 'init', help='initialise instance using YAML config file' - ) - init.add_argument( - 'file', - type=argparse.FileType('r', encoding='UTF-8'), - nargs='?', - default='instance.yaml', - help='instance config [default: instance.yaml]', - ) - - # exec subcommand - execute = subparsers.add_parser( - 'exec', - help='execute command in guest via guest agent', - description=( - 'NOTE: any argument after instance name will be passed into ' - 'guest as shell command.' - ), - ) - execute.add_argument('instance') - execute.add_argument('arguments', nargs=argparse.REMAINDER) - execute.add_argument( - '-t', - '--timeout', - type=int, - default=60, - help=( - 'waiting time in seconds for a command to be executed ' - 'in guest [default: 60]' - ), - ) - execute.add_argument( - '-x', - '--executable', - default='/bin/sh', - help='path to executable in guest [default: /bin/sh]', - ) - execute.add_argument( - '-e', - '--env', - type=str, - nargs='?', - action='append', - help='environment variables to pass to executable in guest', - ) - execute.add_argument( - '-n', - '--no-join-args', - action='store_true', - default=False, - help=( - "do not join arguments list and add '-c' option, suitable " - 'for non-shell executables and other specific cases.' - ), - ) - - # ls subcommand - listall = subparsers.add_parser('ls', help='list instances') - listall.add_argument( - '-a', - '--all', - action='store_true', - default=False, - help='list all instances including inactive', - ) - - # start subcommand - start = subparsers.add_parser('start', help='start instance') - start.add_argument('instance') - - # shutdown subcommand - shutdown = subparsers.add_parser('shutdown', help='shutdown instance') - shutdown.add_argument('instance') - shutdown_opts = shutdown.add_mutually_exclusive_group() - shutdown_opts.add_argument( - '-s', - '--soft', - action='store_true', - help='normal guest OS shutdown, guest agent is used', - ) - shutdown_opts.add_argument( - '-n', - '--normal', - action='store_true', - help='shutdown with hypervisor selected method [default]', - ) - shutdown_opts.add_argument( - '-H', - '--hard', - action='store_true', - help=( - "gracefully destroy instance, it's like long " - 'pressing the power button' - ), - ) - shutdown_opts.add_argument( - '-u', - '--unsafe', - action='store_true', - help=( - 'destroy instance, this is similar to a power outage ' - 'and may result in data loss or corruption' - ), - ) - - # reboot subcommand - reboot = subparsers.add_parser('reboot', help='reboot instance') - reboot.add_argument('instance') - - # reset subcommand - reset = subparsers.add_parser('reset', help='reset instance') - reset.add_argument('instance') - - # powrst subcommand - powrst = subparsers.add_parser('powrst', help='power reset instance') - powrst.add_argument('instance') - - # pause subcommand - pause = subparsers.add_parser('pause', help='pause instance') - pause.add_argument('instance') - - # resume subcommand - resume = subparsers.add_parser('resume', help='resume paused instance') - resume.add_argument('instance') - - # status subcommand - status = subparsers.add_parser('status', help='display instance status') - status.add_argument('instance') - - # setvcpus subcommand - setvcpus = subparsers.add_parser('setvcpus', help='set vCPU number') - setvcpus.add_argument('instance') - setvcpus.add_argument('nvcpus', type=int) - - # setmem subcommand - setmem = subparsers.add_parser('setmem', help='set memory size') - setmem.add_argument('instance') - setmem.add_argument('memory', type=int, help='memory in MiB') - - # setpass subcommand - setpass = subparsers.add_parser( - 'setpass', - help='set user password in guest', - ) - setpass.add_argument('instance') - setpass.add_argument('username') - setpass.add_argument('password') - setpass.add_argument( - '-e', - '--encrypted', - action='store_true', - default=False, - help='set it if password is already encrypted', - ) - - args = root.parse_args() - if args.command is None: - root.print_help() - sys.exit() - - log_level = args.log_level or os.getenv('CMP_LOG') - - if isinstance(log_level, str) and log_level.lower() in log_levels: - logging.basicConfig( - level=logging.getLevelNamesMapping()[log_level.upper()] - ) - - log.debug('CLI started with args: %s', args) - - connect_uri = ( - args.connect - or os.getenv('CMP_LIBVIRT_URI') - or os.getenv('LIBVIRT_DEFAULT_URI') - or 'qemu:///system' - ) - - try: - with Session(connect_uri) as session: - main(session, args) - except ComputeError as e: - sys.exit(f'error: {e}') - except KeyboardInterrupt: - sys.exit() - except SystemExit as e: - sys.exit(e) - except Exception as e: # noqa: BLE001 - sys.exit(f'unexpected error {type(e)}: {e}') - - -if __name__ == '__main__': - cli() diff --git a/packaging/build/compute-0.1.0.dev1/compute/common.py b/packaging/build/compute-0.1.0.dev1/compute/common.py deleted file mode 100644 index 34a339a..0000000 --- a/packaging/build/compute-0.1.0.dev1/compute/common.py +++ /dev/null @@ -1,30 +0,0 @@ -# This file is part of Compute -# -# Compute is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Ansible is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Ansible. If not, see . - -"""Common symbols.""" - -from abc import ABC, abstractmethod - - -class EntityConfig(ABC): - """An abstract entity XML config builder class.""" - - @abstractmethod - def to_xml(self) -> str: - """Return device XML config.""" - raise NotImplementedError - - -DeviceConfig = EntityConfig diff --git a/packaging/build/compute-0.1.0.dev1/compute/exceptions.py b/packaging/build/compute-0.1.0.dev1/compute/exceptions.py deleted file mode 100644 index 1eef8de..0000000 --- a/packaging/build/compute-0.1.0.dev1/compute/exceptions.py +++ /dev/null @@ -1,80 +0,0 @@ -# This file is part of Compute -# -# Compute is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Ansible is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Ansible. If not, see . - -"""Exceptions.""" - - -class ComputeError(Exception): - """Basic exception class.""" - - -class ConfigLoaderError(ComputeError): - """Something went wrong when loading configuration.""" - - -class SessionError(ComputeError): - """Something went wrong while connecting to libvirtd.""" - - -class GuestAgentError(ComputeError): - """Something went wring when QEMU Guest Agent call.""" - - -class GuestAgentUnavailableError(GuestAgentError): - """Guest agent is not connected or is unavailable.""" - - -class GuestAgentTimeoutExceededError(GuestAgentError): - """QEMU timeout exceeded.""" - - def __init__(self, msg: int): - """Initialise GuestAgentTimeoutExceededError.""" - super().__init__(f'QEMU timeout ({msg} sec) exceeded') - - -class GuestAgentCommandNotSupportedError(GuestAgentError): - """Guest agent command is not supported or blacklisted on guest.""" - - -class StoragePoolError(ComputeError): - """Something went wrong when operating with storage pool.""" - - -class StoragePoolNotFoundError(StoragePoolError): - """Storage pool not found.""" - - def __init__(self, msg: str): - """Initialise StoragePoolNotFoundError.""" - super().__init__(f"storage pool named '{msg}' not found") - - -class VolumeNotFoundError(StoragePoolError): - """Storage volume not found.""" - - def __init__(self, msg: str): - """Initialise VolumeNotFoundError.""" - super().__init__(f"storage volume '{msg}' not found") - - -class InstanceError(ComputeError): - """Something went wrong while interacting with the domain.""" - - -class InstanceNotFoundError(InstanceError): - """Virtual machine or container not found on compute node.""" - - def __init__(self, msg: str): - """Initialise InstanceNotFoundError.""" - super().__init__(f"compute instance '{msg}' not found") diff --git a/packaging/build/compute-0.1.0.dev1/compute/instance/__init__.py b/packaging/build/compute-0.1.0.dev1/compute/instance/__init__.py deleted file mode 100644 index 6e2b150..0000000 --- a/packaging/build/compute-0.1.0.dev1/compute/instance/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -# This file is part of Compute -# -# Compute is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Ansible is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Ansible. If not, see . - -from .guest_agent import GuestAgent -from .instance import Instance, InstanceConfig -from .schemas import InstanceSchema diff --git a/packaging/build/compute-0.1.0.dev1/compute/instance/guest_agent.py b/packaging/build/compute-0.1.0.dev1/compute/instance/guest_agent.py deleted file mode 100644 index 4381591..0000000 --- a/packaging/build/compute-0.1.0.dev1/compute/instance/guest_agent.py +++ /dev/null @@ -1,208 +0,0 @@ -# This file is part of Compute -# -# Compute is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Ansible is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Ansible. If not, see . - -"""Interacting with the QEMU Guest Agent.""" - -import json -import logging -from base64 import b64decode, standard_b64encode -from time import sleep, time -from typing import NamedTuple - -import libvirt -import libvirt_qemu - -from compute.exceptions import ( - GuestAgentCommandNotSupportedError, - GuestAgentError, - GuestAgentTimeoutExceededError, - GuestAgentUnavailableError, -) - - -log = logging.getLogger(__name__) - - -class GuestExecOutput(NamedTuple): - """QEMU guest-exec command output.""" - - exited: bool | None = None - exitcode: int | None = None - stdout: str | None = None - stderr: str | None = None - - -class GuestAgent: - """Class for interacting with QEMU guest agent.""" - - def __init__(self, domain: libvirt.virDomain, timeout: int = 60): - """ - Initialise GuestAgent. - - :param domain: Libvirt domain object - :param timeout: QEMU timeout - """ - self.domain = domain - self.timeout = timeout - self.flags = libvirt_qemu.VIR_DOMAIN_QEMU_MONITOR_COMMAND_DEFAULT - self.last_pid = None - - def execute(self, command: dict) -> dict: - """ - Execute QEMU guest agent command. - - See: https://qemu-project.gitlab.io/qemu/interop/qemu-ga-ref.html - - :param command: QEMU guest agent command as dict - :return: Command output - :rtype: dict - """ - log.debug(command) - try: - output = libvirt_qemu.qemuAgentCommand( - self.domain, json.dumps(command), self.timeout, self.flags - ) - return json.loads(output) - except libvirt.libvirtError as e: - if e.get_error_code() == libvirt.VIR_ERR_AGENT_UNRESPONSIVE: - raise GuestAgentUnavailableError(e) from e - raise GuestAgentError(e) from e - - def is_available(self) -> bool: - """ - Execute guest-ping. - - :return: True or False if guest agent is unreachable. - :rtype: bool - """ - try: - if self.execute({'execute': 'guest-ping', 'arguments': {}}): - return True - except GuestAgentError: - return False - - def get_supported_commands(self) -> set[str]: - """Return set of supported guest agent commands.""" - output = self.execute({'execute': 'guest-info', 'arguments': {}}) - return { - cmd['name'] - for cmd in output['return']['supported_commands'] - if cmd['enabled'] is True - } - - def raise_for_commands(self, commands: list[str]) -> None: - """ - Raise exception if QEMU GA command is not available. - - :param commands: List of required commands - :raise: GuestAgentCommandNotSupportedError - """ - supported = self.get_supported_commands() - for command in commands: - if command not in supported: - raise GuestAgentCommandNotSupportedError(command) - - def guest_exec( # noqa: PLR0913 - self, - path: str, - args: list[str] | None = None, - env: list[str] | None = None, - stdin: str | None = None, - *, - capture_output: bool = False, - decode_output: bool = False, - poll: bool = False, - ) -> GuestExecOutput: - """ - Execute qemu-exec command and return output. - - :param path: Path ot executable on guest. - :param arg: List of arguments to pass to executable. - :param env: List of environment variables to pass to executable. - For example: ``['LANG=C', 'TERM=xterm']`` - :param stdin: Data to pass to executable STDIN. - :param capture_output: Capture command output. - :param decode_output: Use base64_decode() to decode command output. - Affects only if `capture_output` is True. - :param poll: Poll command output. Uses `self.timeout` and - POLL_INTERVAL constant. - :return: Command output - :rtype: GuestExecOutput - """ - self.raise_for_commands(['guest-exec', 'guest-exec-status']) - command = { - 'execute': 'guest-exec', - 'arguments': { - 'path': path, - **({'arg': args} if args else {}), - **({'env': env} if env else {}), - **( - { - 'input-data': standard_b64encode( - stdin.encode('utf-8') - ).decode('utf-8') - } - if stdin - else {} - ), - 'capture-output': capture_output, - }, - } - output = self.execute(command) - self.last_pid = pid = output['return']['pid'] - command_status = self.guest_exec_status(pid, poll=poll)['return'] - exited = command_status['exited'] - exitcode = command_status['exitcode'] - stdout = command_status.get('out-data', None) - stderr = command_status.get('err-data', None) - if decode_output: - stdout = b64decode(stdout or '').decode('utf-8') - stderr = b64decode(stderr or '').decode('utf-8') - return GuestExecOutput(exited, exitcode, stdout, stderr) - - def guest_exec_status( - self, pid: int, *, poll: bool = False, poll_interval: float = 0.3 - ) -> dict: - """ - Execute guest-exec-status and return output. - - :param pid: PID in guest. - :param poll: If True poll command status. - :param poll_interval: Time between attempts to obtain command status. - :return: Command output - :rtype: dict - """ - self.raise_for_commands(['guest-exec-status']) - command = { - 'execute': 'guest-exec-status', - 'arguments': {'pid': pid}, - } - if not poll: - return self.execute(command) - start_time = time() - while True: - command_status = self.execute(command) - if command_status['return']['exited']: - break - sleep(poll_interval) - now = time() - if now - start_time > self.timeout: - raise GuestAgentTimeoutExceededError(self.timeout) - log.debug( - 'Polling command pid=%s finished, time taken: %s seconds', - pid, - int(time() - start_time), - ) - return command_status diff --git a/packaging/build/compute-0.1.0.dev1/compute/instance/instance.py b/packaging/build/compute-0.1.0.dev1/compute/instance/instance.py deleted file mode 100644 index 5b806e6..0000000 --- a/packaging/build/compute-0.1.0.dev1/compute/instance/instance.py +++ /dev/null @@ -1,675 +0,0 @@ -# This file is part of Compute -# -# Compute is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Ansible is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Ansible. If not, see . - -"""Manage compute instances.""" - -__all__ = ['Instance', 'InstanceConfig', 'InstanceInfo'] - -import logging -from typing import NamedTuple - -import libvirt -from lxml import etree -from lxml.builder import E - -from compute.common import DeviceConfig, EntityConfig -from compute.exceptions import ( - GuestAgentCommandNotSupportedError, - InstanceError, -) -from compute.storage import DiskConfig -from compute.utils import units - -from .guest_agent import GuestAgent -from .schemas import ( - CPUEmulationMode, - CPUSchema, - InstanceSchema, - NetworkInterfaceSchema, -) - - -log = logging.getLogger(__name__) - - -class InstanceConfig(EntityConfig): - """Compute instance XML config builder.""" - - def __init__(self, schema: InstanceSchema): - """ - Initialise InstanceConfig. - - :param schema: InstanceSchema object - """ - self.name = schema.name - self.title = schema.title - self.description = schema.description - self.memory = schema.memory - self.max_memory = schema.max_memory - self.vcpus = schema.vcpus - self.max_vcpus = schema.max_vcpus - self.cpu = schema.cpu - self.machine = schema.machine - self.emulator = schema.emulator - self.arch = schema.arch - self.boot = schema.boot - self.network_interfaces = schema.network_interfaces - - def _gen_cpu_xml(self, cpu: CPUSchema) -> etree.Element: - options = { - 'mode': cpu.emulation_mode, - 'match': 'exact', - 'check': 'partial', - } - if cpu.emulation_mode == CPUEmulationMode.HOST_PASSTHROUGH: - options['check'] = 'none' - options['migratable'] = 'on' - xml = E.cpu(**options) - if cpu.model: - xml.append(E.model(cpu.model, fallback='forbid')) - if cpu.vendor: - xml.append(E.vendor(cpu.vendor)) - if cpu.topology: - xml.append( - E.topology( - sockets=str(cpu.topology.sockets), - dies=str(cpu.topology.dies), - cores=str(cpu.topology.cores), - threads=str(cpu.topology.threads), - ) - ) - if cpu.features: - for feature in cpu.features.require: - xml.append(E.feature(policy='require', name=feature)) - for feature in cpu.features.disable: - xml.append(E.feature(policy='disable', name=feature)) - return xml - - def _gen_vcpus_xml(self, vcpus: int, max_vcpus: int) -> etree.Element: - xml = E.vcpus() - xml.append(E.vcpu(id='0', enabled='yes', hotpluggable='no', order='1')) - for i in range(max_vcpus - 1): - enabled = 'yes' if (i + 2) <= vcpus else 'no' - xml.append( - E.vcpu( - id=str(i + 1), - enabled=enabled, - hotpluggable='yes', - order=str(i + 2), - ) - ) - return xml - - def _gen_network_interface_xml( - self, interface: NetworkInterfaceSchema - ) -> etree.Element: - return E.interface( - E.source(network=interface.source), - E.mac(address=interface.mac), - type='network', - ) - - def to_xml(self) -> str: - """Return XML config for libvirt.""" - xml = E.domain(type='kvm') - xml.append(E.name(self.name)) - if self.title: - xml.append(E.title(self.title)) - if self.description: - xml.append(E.description(self.description)) - xml.append(E.metadata()) - xml.append(E.memory(str(self.max_memory * 1024), unit='KiB')) - xml.append(E.currentMemory(str(self.memory * 1024), unit='KiB')) - xml.append( - E.vcpu( - str(self.max_vcpus), - placement='static', - current=str(self.vcpus), - ) - ) - xml.append(self._gen_cpu_xml(self.cpu)) - os = E.os(E.type('hvm', machine=self.machine, arch=self.arch)) - for dev in self.boot.order: - os.append(E.boot(dev=dev)) - xml.append(os) - xml.append(E.features(E.acpi(), E.apic())) - xml.append(E.on_poweroff('destroy')) - xml.append(E.on_reboot('restart')) - xml.append(E.on_crash('restart')) - xml.append( - E.pm( - E('suspend-to-mem', enabled='no'), - E('suspend-to-disk', enabled='no'), - ) - ) - devices = E.devices() - devices.append(E.emulator(str(self.emulator))) - for interface in self.network_interfaces: - devices.append(self._gen_network_interface_xml(interface)) - devices.append(E.graphics(type='vnc', port='-1', autoport='yes')) - devices.append(E.input(type='tablet', bus='usb')) - devices.append( - E.channel( - E.source(mode='bind'), - E.target(type='virtio', name='org.qemu.guest_agent.0'), - E.address( - type='virtio-serial', controller='0', bus='0', port='1' - ), - type='unix', - ) - ) - devices.append( - E.console(E.target(type='serial', port='0'), type='pty') - ) - devices.append( - E.video( - E.model(type='vga', vram='16384', heads='1', primary='yes') - ) - ) - xml.append(devices) - return etree.tostring(xml, encoding='unicode', pretty_print=True) - - -class InstanceInfo(NamedTuple): - """ - Store compute instance info. - - Reference: - https://libvirt.org/html/libvirt-libvirt-domain.html#virDomainInfo - """ - - state: str - max_memory: int - memory: int - nproc: int - cputime: int - - -class Instance: - """Manage compute instances.""" - - def __init__(self, domain: libvirt.virDomain): - """ - Initialise Instance. - - :ivar libvirt.virDomain domain: domain object - :ivar libvirt.virConnect connection: connection object - :ivar str name: domain name - :ivar GuestAgent guest_agent: :class:`GuestAgent` object - - :param domain: libvirt domain object - """ - self.domain = domain - self.connection = domain.connect() - self.name = domain.name() - self.guest_agent = GuestAgent(domain) - - def _expand_instance_state(self, state: int) -> str: - 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[state] - - def get_info(self) -> InstanceInfo: - """Return instance info.""" - info = self.domain.info() - return InstanceInfo( - state=self._expand_instance_state(info[0]), - max_memory=info[1], - memory=info[2], - nproc=info[3], - cputime=info[4], - ) - - def get_status(self) -> str: - """ - Return instance state: 'running', 'shutoff', etc. - - Reference: - https://libvirt.org/html/libvirt-libvirt-domain.html#virDomainState - """ - try: - state, _ = self.domain.state() - except libvirt.libvirtError as e: - raise InstanceError( - 'Cannot fetch status of ' f'instance={self.name}: {e}' - ) from e - return self._expand_instance_state(state) - - def is_running(self) -> bool: - """Return True if instance is running, else return False.""" - if self.domain.isActive() != 1: - # 0 - is inactive, -1 - is error - return False - return True - - def is_autostart(self) -> bool: - """Return True if instance autostart is enabled, else return False.""" - try: - return bool(self.domain.autostart()) - except libvirt.libvirtError as e: - raise InstanceError( - f'Cannot get autostart status for ' - f'instance={self.name}: {e}' - ) from e - - def get_max_memory(self) -> int: - """Maximum memory value for domain in KiB.""" - return self.domain.maxMemory() - - def get_max_vcpus(self) -> int: - """Maximum vCPUs number for domain.""" - return self.domain.maxVcpus() - - def start(self) -> None: - """Start defined instance.""" - log.info('Starting instnce=%s', self.name) - if self.is_running(): - log.warning( - 'Already started, nothing to do instance=%s', self.name - ) - return - try: - self.domain.create() - except libvirt.libvirtError as e: - raise InstanceError( - f'Cannot start instance={self.name}: {e}' - ) from e - - def shutdown(self, method: str | None = None) -> None: - """ - Shutdown instance. - - Shutdown methods: - - SOFT - Use guest agent to shutdown. If guest agent is unavailable - NORMAL method will be used. - - NORMAL - Use method choosen by hypervisor to shutdown. Usually send ACPI - signal to guest OS. OS may ignore ACPI e.g. if guest is hanged. - - HARD - Shutdown instance without any guest OS shutdown. This is simular - to unplugging machine from power. Internally send SIGTERM to - instance process and destroy it gracefully. - - UNSAFE - Force shutdown. Internally send SIGKILL to instance process. - There is high data corruption risk! - - If method is None NORMAL method will used. - - :param method: Method used to shutdown instance - """ - methods = { - 'SOFT': libvirt.VIR_DOMAIN_SHUTDOWN_GUEST_AGENT, - 'NORMAL': libvirt.VIR_DOMAIN_SHUTDOWN_DEFAULT, - 'HARD': libvirt.VIR_DOMAIN_DESTROY_GRACEFUL, - 'UNSAFE': libvirt.VIR_DOMAIN_DESTROY_DEFAULT, - } - if method is None: - method = 'NORMAL' - if not isinstance(method, str): - raise TypeError( - f"Shutdown method must be a 'str', not {type(method)}" - ) - method = method.upper() - if method not in methods: - raise ValueError(f"Unsupported shutdown method: '{method}'") - try: - if method in ['SOFT', 'NORMAL']: - self.domain.shutdownFlags(flags=methods[method]) - elif method in ['HARD', 'UNSAFE']: - self.domain.destroyFlags(flags=methods[method]) - except libvirt.libvirtError as e: - raise InstanceError( - f'Cannot shutdown instance={self.name} ' f'{method=}: {e}' - ) from e - - def reboot(self) -> None: - """Send ACPI signal to guest OS to reboot. OS may ignore this.""" - try: - self.domain.reboot() - except libvirt.libvirtError as e: - raise InstanceError( - f'Cannot reboot instance={self.name}: {e}' - ) from e - - def reset(self) -> None: - """ - Reset instance. - - Copypaste from libvirt doc: - - 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. - - Note that there is a risk of data loss caused by reset without any - guest OS shutdown. - """ - try: - self.domain.reset() - except libvirt.libvirtError as e: - raise InstanceError( - f'Cannot reset instance={self.name}: {e}' - ) from e - - def power_reset(self) -> None: - """ - Shutdown instance and start. - - By analogy with real hardware, this is a normal server shutdown, - and then turning off from the power supply and turning it on again. - - This method is applicable in cases where there has been a - configuration change in libvirt and you need to restart the - instance to apply the new configuration. - """ - self.shutdown(method='NORMAL') - self.start() - - def set_autostart(self, *, enabled: bool) -> None: - """ - Set autostart flag for instance. - - :param enabled: Bool argument to set or unset autostart flag. - """ - autostart = 1 if enabled else 0 - try: - self.domain.setAutostart(autostart) - except libvirt.libvirtError as e: - raise InstanceError( - f'Cannot set autostart flag for instance={self.name} ' - f'{autostart=}: {e}' - ) from e - - def set_vcpus(self, nvcpus: int, *, live: bool = False) -> None: - """ - Set vCPU number. - - If `live` is True and instance is not currently running vCPUs - will set in config and will applied when instance boot. - - NB: Note that if this call is executed before the guest has - finished booting, the guest may fail to process the change. - - :param nvcpus: Number of vCPUs - :param live: Affect a running instance - """ - if nvcpus <= 0: - raise InstanceError('Cannot set zero vCPUs') - if nvcpus > self.get_max_vcpus(): - raise InstanceError('vCPUs count is greather than max_vcpus') - if nvcpus == self.get_info().nproc: - log.warning( - 'Instance instance=%s already have %s vCPUs, nothing to do', - self.name, - nvcpus, - ) - return - try: - flags = libvirt.VIR_DOMAIN_AFFECT_CONFIG - self.domain.setVcpusFlags(nvcpus, flags=flags) - if live is True: - if not self.is_running(): - log.warning( - 'Instance is not running, changes applied in ' - 'instance config.' - ) - return - flags = libvirt.VIR_DOMAIN_AFFECT_LIVE - self.domain.setVcpusFlags(nvcpus, flags=flags) - if self.guest_agent.is_available(): - try: - self.guest_agent.raise_for_commands( - ['guest-set-vcpus'] - ) - flags = libvirt.VIR_DOMAIN_VCPU_GUEST - self.domain.setVcpusFlags(nvcpus, flags=flags) - except GuestAgentCommandNotSupportedError: - log.warning( - 'Cannot set vCPUs in guest via agent, you may ' - 'need to apply changes in guest manually.' - ) - else: - log.warning( - 'Cannot set vCPUs in guest OS on instance=%s. ' - 'You may need to apply CPUs in guest manually.', - self.name, - ) - except libvirt.libvirtError as e: - raise InstanceError( - f'Cannot set vCPUs for instance={self.name}: {e}' - ) from e - - def set_memory(self, memory: int, *, live: bool = False) -> None: - """ - Set memory. - - If `live` is True and instance is not currently running set memory - in config and will applied when instance boot. - - :param memory: Memory value in mebibytes - :param live: Affect a running instance - """ - if memory <= 0: - raise InstanceError('Cannot set zero memory') - if (memory * 1024) > self.get_max_memory(): - raise InstanceError('Memory is greather than max_memory') - if (memory * 1024) == self.get_info().memory: - log.warning( - "Instance '%s' already have %s memory, nothing to do", - self.name, - memory, - ) - return - if live and self.is_running(): - flags = ( - libvirt.VIR_DOMAIN_AFFECT_LIVE - | libvirt.VIR_DOMAIN_AFFECT_CONFIG - ) - else: - flags = libvirt.VIR_DOMAIN_AFFECT_CONFIG - try: - self.domain.setMemoryFlags(memory * 1024, flags=flags) - except libvirt.libvirtError as e: - msg = f'Cannot set memory for instance={self.name} {memory=}: {e}' - raise InstanceError(msg) from e - - def _get_disk_by_target(self, target: str) -> etree.Element: - xml = etree.fromstring(self.dump_xml()) # noqa: S320 - child = xml.xpath(f'/domain/devices/disk/target[@dev="{target}"]') - return child[0].getparent() if child else None - - def attach_device( - self, device: DeviceConfig, *, live: bool = False - ) -> None: - """ - Attach device to compute instance. - - :param device: Object with device description e.g. DiskConfig - :param live: Affect a running instance - """ - if live and self.is_running(): - flags = ( - libvirt.VIR_DOMAIN_AFFECT_LIVE - | libvirt.VIR_DOMAIN_AFFECT_CONFIG - ) - else: - flags = libvirt.VIR_DOMAIN_AFFECT_CONFIG - if isinstance(device, DiskConfig): # noqa: SIM102 - if self._get_disk_by_target(device.target): - log.warning( - "Volume with target '%s' is already attached", - device.target, - ) - return - self.domain.attachDeviceFlags(device.to_xml(), flags=flags) - - def detach_device( - self, device: DeviceConfig, *, live: bool = False - ) -> None: - """ - Dettach device from compute instance. - - :param device: Object with device description e.g. DiskConfig - :param live: Affect a running instance - """ - if live and self.is_running(): - flags = ( - libvirt.VIR_DOMAIN_AFFECT_LIVE - | libvirt.VIR_DOMAIN_AFFECT_CONFIG - ) - else: - flags = libvirt.VIR_DOMAIN_AFFECT_CONFIG - if isinstance(device, DiskConfig): # noqa: SIM102 - if self._get_disk_by_target(device.target) is None: - log.warning( - "Volume with target '%s' is already detached", - device.target, - ) - return - self.domain.detachDeviceFlags(device.to_xml(), flags=flags) - - def detach_disk(self, name: str) -> None: - """ - Detach disk device by target name. - - There is no ``attach_disk()`` method. Use :func:`attach_device` - with :class:`DiskConfig` as argument. - - :param name: Disk name e.g. 'vda', 'sda', etc. This name may - not match the name of the disk inside the guest OS. - """ - xml = self._get_disk_by_target(name) - if xml is None: - log.warning( - "Volume with target '%s' is already detached", - name, - ) - return - disk_params = { - 'disk_type': xml.get('type'), - 'source': xml.find('source').get('file'), - 'target': xml.find('target').get('dev'), - 'readonly': False if xml.find('readonly') is None else True, # noqa: SIM211 - } - for param in disk_params: - if disk_params[param] is None: - msg = ( - f"Cannot detach volume with target '{name}': " - f"parameter '{param}' is not defined in libvirt XML " - 'config on host.' - ) - raise InstanceError(msg) - self.detach_device(DiskConfig(**disk_params), live=True) - - def resize_disk( - self, name: str, capacity: int, unit: units.DataUnit - ) -> None: - """ - Resize attached block device. - - :param name: Disk device name e.g. `vda`, `sda`, etc. - :param capacity: New capacity. - :param unit: Capacity unit. - """ - self.domain.blockResize( - name, - units.to_bytes(capacity, unit=unit), - flags=libvirt.VIR_DOMAIN_BLOCK_RESIZE_BYTES, - ) - - def get_disks(self) -> list[DiskConfig]: - """Return list of attached disks.""" - raise NotImplementedError - - def pause(self) -> None: - """Pause instance.""" - if not self.is_running(): - raise InstanceError('Cannot pause inactive instance') - self.domain.suspend() - - def resume(self) -> None: - """Resume paused instance.""" - self.domain.resume() - - def get_ssh_keys(self, user: str) -> list[str]: - """ - Return list of SSH keys on guest for specific user. - - :param user: Username. - """ - raise NotImplementedError - - def set_ssh_keys(self, user: str, ssh_keys: list[str]) -> None: - """ - Add SSH keys to guest for specific user. - - :param user: Username. - :param ssh_keys: List of public SSH keys. - """ - raise NotImplementedError - - def delete_ssh_keys(self, user: str, ssh_keys: list[str]) -> None: - """ - Remove SSH keys from guest for specific user. - - :param user: Username. - :param ssh_keys: List of public SSH keys. - """ - raise NotImplementedError - - def set_user_password( - self, user: str, password: str, *, encrypted: bool = False - ) -> None: - """ - Set new user password in guest OS. - - This action performs by guest agent inside the guest. - - :param user: Username. - :param password: Password. - :param encrypted: Set it to True if password is already encrypted. - Right encryption method depends on guest OS. - """ - if not self.guest_agent.is_available(): - raise InstanceError( - 'Cannot change password: guest agent is unavailable' - ) - self.guest_agent.raise_for_commands(['guest-set-user-password']) - flags = libvirt.VIR_DOMAIN_PASSWORD_ENCRYPTED if encrypted else 0 - self.domain.setUserPassword(user, password, flags=flags) - - def dump_xml(self, *, inactive: bool = False) -> str: - """Return instance XML description.""" - flags = libvirt.VIR_DOMAIN_XML_INACTIVE if inactive else 0 - return self.domain.XMLDesc(flags) - - def delete(self) -> None: - """Undefine instance.""" - # TODO @ge: delete local disks - self.shutdown(method='HARD') - self.domain.undefine() diff --git a/packaging/build/compute-0.1.0.dev1/compute/instance/schemas.py b/packaging/build/compute-0.1.0.dev1/compute/instance/schemas.py deleted file mode 100644 index f5a677c..0000000 --- a/packaging/build/compute-0.1.0.dev1/compute/instance/schemas.py +++ /dev/null @@ -1,165 +0,0 @@ -# This file is part of Compute -# -# Compute is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Ansible is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Ansible. If not, see . - -"""Compute instance related objects schemas.""" - -import re -from enum import StrEnum -from pathlib import Path - -from pydantic import BaseModel, Extra, validator - -from compute.utils.units import DataUnit - - -class EntityModel(BaseModel): - """Basic entity model.""" - - class Config: - """Do not allow extra fields.""" - - extra = Extra.forbid - - -class CPUEmulationMode(StrEnum): - """CPU emulation mode enumerated.""" - - HOST_PASSTHROUGH = 'host-passthrough' - HOST_MODEL = 'host-model' - CUSTOM = 'custom' - MAXIMUM = 'maximum' - - -class CPUTopologySchema(EntityModel): - """CPU topology model.""" - - sockets: int - cores: int - threads: int - dies: int = 1 - - -class CPUFeaturesSchema(EntityModel): - """CPU features model.""" - - require: list[str] - disable: list[str] - - -class CPUSchema(EntityModel): - """CPU model.""" - - emulation_mode: CPUEmulationMode - model: str | None - vendor: str | None - topology: CPUTopologySchema | None - features: CPUFeaturesSchema | None - - -class VolumeType(StrEnum): - """Storage volume types enumeration.""" - - FILE = 'file' - - -class VolumeCapacitySchema(EntityModel): - """Storage volume capacity field model.""" - - value: int - unit: DataUnit - - -class VolumeSchema(EntityModel): - """Storage volume model.""" - - type: VolumeType # noqa: A003 - target: str - capacity: VolumeCapacitySchema - source: str | None = None - is_readonly: bool = False - is_system: bool = False - - -class NetworkInterfaceSchema(EntityModel): - """Network inerface model.""" - - source: str - mac: str - - -class BootOptionsSchema(EntityModel): - """Instance boot settings.""" - - order: tuple - - -class InstanceSchema(EntityModel): - """Compute instance model.""" - - name: str - title: str | None - description: str | None - memory: int - max_memory: int - vcpus: int - max_vcpus: int - cpu: CPUSchema - machine: str - emulator: Path - arch: str - boot: BootOptionsSchema - volumes: list[VolumeSchema] - network_interfaces: list[NetworkInterfaceSchema] - image: str | None = None - - @validator('name') - def _check_name(cls, value: str) -> str: # noqa: N805 - if not re.match(r'^[a-z0-9_]+$', value): - msg = ( - 'Name can contain only lowercase letters, numbers ' - 'and underscore.' - ) - raise ValueError(msg) - return value - - @validator('cpu') - def _check_topology(cls, cpu: int, values: dict) -> CPUSchema: # noqa: N805 - topo = cpu.topology - max_vcpus = values['max_vcpus'] - if topo and topo.sockets * topo.cores * topo.threads != max_vcpus: - msg = f'CPU topology does not match with {max_vcpus=}' - raise ValueError(msg) - return cpu - - @validator('volumes') - def _check_volumes(cls, volumes: list) -> list: # noqa: N805 - if len([v for v in volumes if v.is_system is True]) != 1: - msg = 'volumes list must contain one system volume' - raise ValueError(msg) - vol_with_source = 0 - for vol in volumes: - if vol.is_system is True and vol.is_readonly is True: - msg = 'volume marked as system cannot be readonly' - raise ValueError(msg) - if vol.source is not None: - vol_with_source += 1 - return volumes - - @validator('network_interfaces') - def _check_network_interfaces(cls, value: list) -> list: # noqa: N805 - if not value: - msg = 'Network interfaces list must contain at least one element' - raise ValueError(msg) - return value diff --git a/packaging/build/compute-0.1.0.dev1/compute/session.py b/packaging/build/compute-0.1.0.dev1/compute/session.py deleted file mode 100644 index de5f900..0000000 --- a/packaging/build/compute-0.1.0.dev1/compute/session.py +++ /dev/null @@ -1,286 +0,0 @@ -# This file is part of Compute -# -# Compute is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Ansible is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Ansible. If not, see . - -"""Hypervisor session manager.""" - -import logging -import os -from contextlib import AbstractContextManager -from types import TracebackType -from typing import Any, NamedTuple -from uuid import uuid4 - -import libvirt -from lxml import etree - -from .exceptions import ( - InstanceNotFoundError, - SessionError, - StoragePoolNotFoundError, -) -from .instance import Instance, InstanceConfig, InstanceSchema -from .storage import DiskConfig, StoragePool, VolumeConfig -from .utils import units - - -log = logging.getLogger(__name__) - - -class Capabilities(NamedTuple): - """Store domain capabilities info.""" - - arch: str - virt_type: str - emulator: str - machine: str - max_vcpus: int - cpu_vendor: str - cpu_model: str - cpu_features: dict - usable_cpus: list[dict] - - -class NodeInfo(NamedTuple): - """ - Store compute node info. - - See https://libvirt.org/html/libvirt-libvirt-host.html#virNodeInfo - NOTE: memory unit in libvirt docs is wrong! Actual unit is MiB. - """ - - arch: str - memory: int - cpus: int - mhz: int - nodes: int - sockets: int - cores: int - threads: int - - -class Session(AbstractContextManager): - """ - Hypervisor session context manager. - - :cvar IMAGES_POOL: images storage pool name taken from env - :cvar VOLUMES_POOL: volumes storage pool name taken from env - """ - - IMAGES_POOL = os.getenv('CMP_IMAGES_POOL') - VOLUMES_POOL = os.getenv('CMP_VOLUMES_POOL') - - def __init__(self, uri: str | None = None): - """ - Initialise session with hypervisor. - - :ivar str uri: libvirt connection URI. - :ivar libvirt.virConnect connection: libvirt connection object. - - :param uri: libvirt connection URI. - """ - self.uri = uri or 'qemu:///system' - self.connection = libvirt.open(self.uri) - - def __enter__(self): - """Return Session object.""" - return self - - def __exit__( - self, - exc_type: type[BaseException] | None, - exc_value: BaseException | None, - exc_traceback: TracebackType | None, - ): - """Close the connection when leaving the context.""" - self.close() - - def close(self) -> None: - """Close connection to libvirt daemon.""" - self.connection.close() - - def get_node_info(self) -> NodeInfo: - """Return information about compute node.""" - info = self.connection.getInfo() - return NodeInfo( - arch=info[0], - memory=info[1], - cpus=info[2], - mhz=info[3], - nodes=info[4], - sockets=info[5], - cores=info[6], - threads=info[7], - ) - - def _cap_get_usable_cpus(self, xml: etree.Element) -> list[dict]: - x = xml.xpath('/domainCapabilities/cpu/mode[@name="custom"]')[0] - cpus = [] - for cpu in x.findall('model'): - if cpu.get('usable') == 'yes': - cpus.append( # noqa: PERF401 - { - 'vendor': cpu.get('vendor'), - 'model': cpu.text, - } - ) - return cpus - - def _cap_get_cpu_features(self, xml: etree.Element) -> dict: - x = xml.xpath('/domainCapabilities/cpu/mode[@name="host-model"]')[0] - require = [] - disable = [] - for feature in x.findall('feature'): - policy = feature.get('policy') - name = feature.get('name') - if policy == 'require': - require.append(name) - if policy == 'disable': - disable.append(name) - return {'require': require, 'disable': disable} - - def get_capabilities(self) -> Capabilities: - """Return capabilities e.g. arch, virt, emulator, etc.""" - prefix = '/domainCapabilities' - hprefix = f'{prefix}/cpu/mode[@name="host-model"]' - caps = etree.fromstring(self.connection.getDomainCapabilities()) # noqa: S320 - return Capabilities( - arch=caps.xpath(f'{prefix}/arch/text()')[0], - virt_type=caps.xpath(f'{prefix}/domain/text()')[0], - emulator=caps.xpath(f'{prefix}/path/text()')[0], - machine=caps.xpath(f'{prefix}/machine/text()')[0], - max_vcpus=int(caps.xpath(f'{prefix}/vcpu/@max')[0]), - cpu_vendor=caps.xpath(f'{hprefix}/vendor/text()')[0], - cpu_model=caps.xpath(f'{hprefix}/model/text()')[0], - cpu_features=self._cap_get_cpu_features(caps), - usable_cpus=self._cap_get_cpus(caps), - ) - - def create_instance(self, **kwargs: Any) -> Instance: - """ - Create and return new compute instance. - - :param name: Instance name. - :type name: str - :param title: Instance title for humans. - :type title: str - :param description: Some information about instance. - :type description: str - :param memory: Memory in MiB. - :type memory: int - :param max_memory: Maximum memory in MiB. - :type max_memory: int - :param vcpus: Number of vCPUs. - :type vcpus: int - :param max_vcpus: Maximum vCPUs. - :type max_vcpus: int - :param cpu: CPU configuration. See :class:`CPUSchema` for info. - :type cpu: dict - :param machine: QEMU emulated machine. - :type machine: str - :param emulator: Path to emulator. - :type emulator: str - :param arch: CPU architecture to virtualization. - :type arch: str - :param boot: Boot settings. See :class:`BootOptionsSchema`. - :type boot: dict - :param image: Source disk image name for system disk. - :type image: str - :param volumes: List of storage volume configs. For more info - see :class:`VolumeSchema`. - :type volumes: list[dict] - :param network_interfaces: List of virtual network interfaces - configs. See :class:`NetworkInterfaceSchema` for more info. - :type network_interfaces: list[dict] - """ - data = InstanceSchema(**kwargs) - config = InstanceConfig(data) - log.info('Define XML...') - log.info(config.to_xml()) - self.connection.defineXML(config.to_xml()) - log.info('Getting instance...') - instance = self.get_instance(config.name) - log.info('Creating volumes...') - for volume in data.volumes: - log.info('Creating volume=%s', volume) - capacity = units.to_bytes( - volume.capacity.value, volume.capacity.unit - ) - log.info('Connecting to images pool...') - images_pool = self.get_storage_pool(self.IMAGES_POOL) - log.info('Connecting to volumes pool...') - volumes_pool = self.get_storage_pool(self.VOLUMES_POOL) - log.info('Building volume configuration...') - if not volume.source: - vol_name = f'{uuid4()}.qcow2' - else: - vol_name = volume.source - vol_conf = VolumeConfig( - name=vol_name, - path=str(volumes_pool.path.joinpath(vol_name)), - capacity=capacity, - ) - log.info('Volume configuration is:\n %s', vol_conf.to_xml()) - if volume.is_system is True and data.image: - log.info( - "Volume is marked as 'system', start cloning image..." - ) - log.info('Get image %s', data.image) - image = images_pool.get_volume(data.image) - log.info('Cloning image into volumes pool...') - vol = volumes_pool.clone_volume(image, vol_conf) - log.info( - 'Resize cloned volume to specified size: %s', - capacity, - ) - vol.resize(capacity, unit=units.DataUnit.BYTES) - else: - log.info('Create volume...') - volumes_pool.create_volume(vol_conf) - log.info('Attaching volume to instance...') - instance.attach_device( - DiskConfig( - disk_type=volume.type, - source=vol_conf.path, - target=volume.target, - readonly=volume.is_readonly, - ) - ) - return instance - - def get_instance(self, name: str) -> Instance: - """Get compute instance by name.""" - try: - return Instance(self.connection.lookupByName(name)) - except libvirt.libvirtError as e: - if e.get_error_code() == libvirt.VIR_ERR_NO_DOMAIN: - raise InstanceNotFoundError(name) from e - raise SessionError(e) from e - - def list_instances(self) -> list[Instance]: - """List all instances.""" - return [Instance(dom) for dom in self.connection.listAllDomains()] - - def get_storage_pool(self, name: str) -> StoragePool: - """Get storage pool by name.""" - try: - return StoragePool(self.connection.storagePoolLookupByName(name)) - except libvirt.libvirtError as e: - if e.get_error_code() == libvirt.VIR_ERR_NO_STORAGE_POOL: - raise StoragePoolNotFoundError(name) from e - raise SessionError(e) from e - - def list_storage_pools(self) -> list[StoragePool]: - """List all strage pools.""" - return [StoragePool(p) for p in self.connection.listStoragePools()] diff --git a/packaging/build/compute-0.1.0.dev1/compute/storage/__init__.py b/packaging/build/compute-0.1.0.dev1/compute/storage/__init__.py deleted file mode 100644 index 34aae30..0000000 --- a/packaging/build/compute-0.1.0.dev1/compute/storage/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -# This file is part of Compute -# -# Compute is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Ansible is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Ansible. If not, see . - -from .pool import StoragePool -from .volume import DiskConfig, Volume, VolumeConfig diff --git a/packaging/build/compute-0.1.0.dev1/compute/storage/pool.py b/packaging/build/compute-0.1.0.dev1/compute/storage/pool.py deleted file mode 100644 index cb17494..0000000 --- a/packaging/build/compute-0.1.0.dev1/compute/storage/pool.py +++ /dev/null @@ -1,124 +0,0 @@ -# This file is part of Compute -# -# Compute is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Ansible is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Ansible. If not, see . - -"""Manage storage pools.""" - -import logging -from pathlib import Path -from typing import NamedTuple - -import libvirt -from lxml import etree - -from compute.exceptions import StoragePoolError, VolumeNotFoundError - -from .volume import Volume, VolumeConfig - - -log = logging.getLogger(__name__) - - -class StoragePoolUsageInfo(NamedTuple): - """Storage pool usage info.""" - - capacity: int - allocation: int - available: int - - -class StoragePool: - """Storage pool manipulating class.""" - - def __init__(self, pool: libvirt.virStoragePool): - """Initislise StoragePool.""" - self.pool = pool - self.name = pool.name() - self.path = self._get_path() - - def _get_path(self) -> Path: - """Return storage pool path.""" - xml = etree.fromstring(self.pool.XMLDesc()) # noqa: S320 - return Path(xml.xpath('/pool/target/path/text()')[0]) - - def get_usage_info(self) -> StoragePoolUsageInfo: - """Return info about storage pool usage.""" - xml = etree.fromstring(self.pool.XMLDesc()) # noqa: S320 - return StoragePoolUsageInfo( - capacity=int(xml.xpath('/pool/capacity/text()')[0]), - allocation=int(xml.xpath('/pool/allocation/text()')[0]), - available=int(xml.xpath('/pool/available/text()')[0]), - ) - - def dump_xml(self) -> str: - """Return storage pool XML description as string.""" - return self.pool.XMLDesc() - - def refresh(self) -> None: - """Refresh storage pool.""" - # TODO @ge: handle libvirt asynchronous job related exceptions - self.pool.refresh() - - def create_volume(self, vol_conf: VolumeConfig) -> Volume: - """Create storage volume and return Volume instance.""" - log.info( - 'Create storage volume vol=%s in pool=%s', vol_conf.name, self.name - ) - vol = self.pool.createXML( - vol_conf.to_xml(), - flags=libvirt.VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA, - ) - return Volume(self.pool, vol) - - def clone_volume(self, src: Volume, dst: VolumeConfig) -> Volume: - """ - Make storage volume copy. - - :param src: Input volume - :param dst: Output volume config - """ - log.info( - 'Start volume cloning ' - 'src_pool=%s src_vol=%s dst_pool=%s dst_vol=%s', - src.pool_name, - src.name, - self.pool.name, - dst.name, - ) - vol = self.pool.createXMLFrom( - dst.to_xml(), # new volume XML description - src.vol, # source volume virStorageVol object - flags=libvirt.VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA, - ) - if vol is None: - raise StoragePoolError - return Volume(self.pool, vol) - - def get_volume(self, name: str) -> Volume | None: - """Lookup and return Volume instance or None.""" - log.info( - 'Lookup for storage volume vol=%s in pool=%s', name, self.pool.name - ) - try: - vol = self.pool.storageVolLookupByName(name) - return Volume(self.pool, vol) - except libvirt.libvirtError as e: - if e.get_error_code() == libvirt.VIR_ERR_NO_STORAGE_VOL: - raise VolumeNotFoundError(name) from e - log.exception('unexpected error from libvirt') - raise StoragePoolError(e) from e - - def list_volumes(self) -> list[Volume]: - """Return list of volumes in storage pool.""" - return [Volume(self.pool, vol) for vol in self.pool.listAllVolumes()] diff --git a/packaging/build/compute-0.1.0.dev1/compute/storage/volume.py b/packaging/build/compute-0.1.0.dev1/compute/storage/volume.py deleted file mode 100644 index 11a1dc4..0000000 --- a/packaging/build/compute-0.1.0.dev1/compute/storage/volume.py +++ /dev/null @@ -1,138 +0,0 @@ -# This file is part of Compute -# -# Compute is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Ansible is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Ansible. If not, see . - -"""Manage storage volumes.""" - -from dataclasses import dataclass -from pathlib import Path -from time import time - -import libvirt -from lxml import etree -from lxml.builder import E - -from compute.common import DeviceConfig, EntityConfig -from compute.utils import units - - -@dataclass -class VolumeConfig(EntityConfig): - """ - Storage volume XML config builder. - - Generate XML config for creating a volume in a libvirt - storage pool. - """ - - name: str - path: str - capacity: int - - def to_xml(self) -> str: - """Return XML config for libvirt.""" - unixtime = str(int(time())) - xml = E.volume(type='file') - xml.append(E.name(self.name)) - xml.append(E.key(self.path)) - xml.append(E.source()) - xml.append(E.capacity(str(self.capacity), unit='bytes')) - xml.append(E.allocation('0')) - xml.append( - E.target( - E.path(self.path), - E.format(type='qcow2'), - E.timestamps( - E.atime(unixtime), E.mtime(unixtime), E.ctime(unixtime) - ), - E.compat('1.1'), - E.features(E.lazy_refcounts()), - ) - ) - return etree.tostring(xml, encoding='unicode', pretty_print=True) - - -@dataclass -class DiskConfig(DeviceConfig): - """ - Disk XML config builder. - - Generate XML config for attaching or detaching storage volumes - to compute instances. - """ - - disk_type: str - source: str | Path - target: str - readonly: bool = False - - def to_xml(self) -> str: - """Return XML config for libvirt.""" - xml = E.disk(type=self.disk_type, device='disk') - xml.append(E.driver(name='qemu', type='qcow2', cache='writethrough')) - if self.disk_type == 'file': - xml.append(E.source(file=str(self.source))) - xml.append(E.target(dev=self.target, bus='virtio')) - if self.readonly: - xml.append(E.readonly()) - return etree.tostring(xml, encoding='unicode', pretty_print=True) - - -class Volume: - """Storage volume manipulating class.""" - - def __init__( - self, pool: libvirt.virStoragePool, vol: libvirt.virStorageVol - ): - """ - Initialise Volume. - - :param pool: libvirt virStoragePool object - :param vol: libvirt virStorageVol object - """ - self.pool = pool - self.pool_name = pool.name() - self.vol = vol - self.name = vol.name() - self.path = Path(vol.path()) - - def dump_xml(self) -> str: - """Return volume XML description as string.""" - return self.vol.XMLDesc() - - def clone(self, vol_conf: VolumeConfig) -> None: - """ - Make a copy of volume to the same storage pool. - - :param vol_info VolumeInfo: New storage volume dataclass object - """ - self.pool.createXMLFrom( - vol_conf.to_xml(), - self.vol, - flags=libvirt.VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA, - ) - - def resize(self, capacity: int, unit: units.DataUnit) -> None: - """ - Resize volume. - - :param capacity int: Volume new capacity. - :param unit DataUnit: Data unit. Internally converts into bytes. - """ - # TODO @ge: Check actual volume size before resize - self.vol.resize(units.to_bytes(capacity, unit=unit)) - - def delete(self) -> None: - """Delete volume from storage pool.""" - self.vol.delete() diff --git a/packaging/build/compute-0.1.0.dev1/compute/utils/__init__.py b/packaging/build/compute-0.1.0.dev1/compute/utils/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/packaging/build/compute-0.1.0.dev1/compute/utils/config_loader.py b/packaging/build/compute-0.1.0.dev1/compute/utils/config_loader.py deleted file mode 100644 index aaeb0fe..0000000 --- a/packaging/build/compute-0.1.0.dev1/compute/utils/config_loader.py +++ /dev/null @@ -1,56 +0,0 @@ -# This file is part of Compute -# -# Compute is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Ansible is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Ansible. If not, see . - -"""Configuration loader.""" - -import tomllib -from collections import UserDict -from pathlib import Path - -from compute.exceptions import ConfigLoaderError - - -DEFAULT_CONFIGURATION = {} -DEFAULT_CONFIG_FILE = '/etc/computed/computed.toml' - - -class ConfigLoader(UserDict): - """UserDict for storing configuration.""" - - def __init__(self, file: Path | None = None): - """ - Initialise ConfigLoader. - - :param file: Path to configuration file. If `file` is None - use default path from DEFAULT_CONFIG_FILE constant. - """ - # TODO @ge: load deafult configuration - self.file = Path(file) if file else Path(DEFAULT_CONFIG_FILE) - super().__init__(self.load()) - - def load(self) -> dict: - """Load confguration object from TOML file.""" - try: - with Path(self.file).open('rb') as configfile: - return tomllib.load(configfile) - # TODO @ge: add config schema validation - except tomllib.TOMLDecodeError as tomlerr: - raise ConfigLoaderError( - f'Bad TOML syntax in config file: {self.file}: {tomlerr}' - ) from tomlerr - except (OSError, ValueError) as readerr: - raise ConfigLoaderError( - f'Cannot read config file: {self.file}: {readerr}' - ) from readerr diff --git a/packaging/build/compute-0.1.0.dev1/compute/utils/ids.py b/packaging/build/compute-0.1.0.dev1/compute/utils/ids.py deleted file mode 100644 index 8a6454a..0000000 --- a/packaging/build/compute-0.1.0.dev1/compute/utils/ids.py +++ /dev/null @@ -1,33 +0,0 @@ -# This file is part of Compute -# -# Compute is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Ansible is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Ansible. If not, see . - -"""Random identificators.""" - -# ruff: noqa: S311, C417 - -import random - - -def random_mac() -> str: - """Retrun random MAC address.""" - mac = [ - 0x00, - 0x16, - 0x3E, - random.randint(0x00, 0x7F), - random.randint(0x00, 0xFF), - random.randint(0x00, 0xFF), - ] - return ':'.join(map(lambda x: '%02x' % x, mac)) diff --git a/packaging/build/compute-0.1.0.dev1/compute/utils/units.py b/packaging/build/compute-0.1.0.dev1/compute/utils/units.py deleted file mode 100644 index 57a4583..0000000 --- a/packaging/build/compute-0.1.0.dev1/compute/utils/units.py +++ /dev/null @@ -1,54 +0,0 @@ -# This file is part of Compute -# -# Compute is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Ansible is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Ansible. If not, see . - -"""Tools for data units convertion.""" - -from enum import StrEnum - - -class DataUnit(StrEnum): - """Data units enumerated.""" - - BYTES = 'bytes' - KIB = 'KiB' - MIB = 'MiB' - GIB = 'GiB' - TIB = 'TiB' - - -class InvalidDataUnitError(ValueError): - """Data unit is not valid.""" - - def __init__(self, msg: str): - """Initialise InvalidDataUnitError.""" - super().__init__( - f'{msg}, valid units are: {", ".join(list(DataUnit))}' - ) - - -def to_bytes(value: int, unit: DataUnit = DataUnit.BYTES) -> int: - """Convert value to bytes. See :class:`DataUnit`.""" - try: - _ = DataUnit(unit) - except ValueError as e: - raise InvalidDataUnitError(e) from e - powers = { - DataUnit.BYTES: 0, - DataUnit.KIB: 1, - DataUnit.MIB: 2, - DataUnit.GIB: 3, - DataUnit.TIB: 4, - } - return value * pow(1024, powers[unit]) diff --git a/packaging/build/compute-0.1.0.dev1/debian/.debhelper/generated/compute-doc/dh_installchangelogs.dch.trimmed b/packaging/build/compute-0.1.0.dev1/debian/.debhelper/generated/compute-doc/dh_installchangelogs.dch.trimmed deleted file mode 100644 index bb9efc5..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/.debhelper/generated/compute-doc/dh_installchangelogs.dch.trimmed +++ /dev/null @@ -1,5 +0,0 @@ -compute (0.1.0.dev1-1) UNRELEASED; urgency=medium - - * This is the development build, see commits in upstream repo for info. - - -- ge Wed, 22 Nov 2023 23:06:43 +0000 diff --git a/packaging/build/compute-0.1.0.dev1/debian/.debhelper/generated/compute-doc/installed-by-dh_installdocs b/packaging/build/compute-0.1.0.dev1/debian/.debhelper/generated/compute-doc/installed-by-dh_installdocs deleted file mode 100644 index e69de29..0000000 diff --git a/packaging/build/compute-0.1.0.dev1/debian/.debhelper/generated/compute/dh_installchangelogs.dch.trimmed b/packaging/build/compute-0.1.0.dev1/debian/.debhelper/generated/compute/dh_installchangelogs.dch.trimmed deleted file mode 100644 index bb9efc5..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/.debhelper/generated/compute/dh_installchangelogs.dch.trimmed +++ /dev/null @@ -1,5 +0,0 @@ -compute (0.1.0.dev1-1) UNRELEASED; urgency=medium - - * This is the development build, see commits in upstream repo for info. - - -- ge Wed, 22 Nov 2023 23:06:43 +0000 diff --git a/packaging/build/compute-0.1.0.dev1/debian/.debhelper/generated/compute/installed-by-dh_installdocs b/packaging/build/compute-0.1.0.dev1/debian/.debhelper/generated/compute/installed-by-dh_installdocs deleted file mode 100644 index c2dd0c3..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/.debhelper/generated/compute/installed-by-dh_installdocs +++ /dev/null @@ -1 +0,0 @@ -./README.md diff --git a/packaging/build/compute-0.1.0.dev1/debian/changelog b/packaging/build/compute-0.1.0.dev1/debian/changelog deleted file mode 100644 index bb9efc5..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/changelog +++ /dev/null @@ -1,5 +0,0 @@ -compute (0.1.0.dev1-1) UNRELEASED; urgency=medium - - * This is the development build, see commits in upstream repo for info. - - -- ge Wed, 22 Nov 2023 23:06:43 +0000 diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute-doc.debhelper.log b/packaging/build/compute-0.1.0.dev1/debian/compute-doc.debhelper.log deleted file mode 100644 index 8dc2028..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute-doc.debhelper.log +++ /dev/null @@ -1 +0,0 @@ -dh_sphinxdoc diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute-doc.substvars b/packaging/build/compute-0.1.0.dev1/debian/compute-doc.substvars deleted file mode 100644 index c41bfd3..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute-doc.substvars +++ /dev/null @@ -1,4 +0,0 @@ -sphinxdoc:Depends=libjs-sphinxdoc (>= 1.0), libjs-sphinxdoc (>= 2.4.3-5~), libjs-sphinxdoc (>= 5.0), libjs-sphinxdoc (>= 5.2) -sphinxdoc:Built-Using=alabaster (= 0.7.12-1), sphinx (= 5.3.0-4) -misc:Depends= -misc:Pre-Depends= diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/DEBIAN/control b/packaging/build/compute-0.1.0.dev1/debian/compute-doc/DEBIAN/control deleted file mode 100644 index 72814c9..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/DEBIAN/control +++ /dev/null @@ -1,11 +0,0 @@ -Package: compute-doc -Source: compute -Version: 0.1.0.dev1-1 -Architecture: all -Maintainer: ge -Installed-Size: 376 -Depends: libjs-sphinxdoc (>= 5.2) -Section: doc -Priority: optional -Homepage: https://git.lulzette.ru/hstack/compute -Description: Compute instances management library and tools (documentation) diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/DEBIAN/md5sums b/packaging/build/compute-0.1.0.dev1/debian/compute-doc/DEBIAN/md5sums deleted file mode 100644 index 5ab5be6..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/DEBIAN/md5sums +++ /dev/null @@ -1,40 +0,0 @@ -6845278a102bd147f30f770ed1134ce5 usr/share/doc/compute-doc/changelog.Debian.gz -fb1a6c11d7a8fa5f238617c20b13b6a1 usr/share/doc/compute-doc/copyright -705113edf19bbf7f9d406fccd98ebef9 usr/share/doc/compute-doc/html/_sources/index.rst.txt -91934f7b742b8395043e25cfa73682af usr/share/doc/compute-doc/html/_sources/pyapi/exceptions.rst.txt -de8bc1c2c00774ddee5363aef80c0775 usr/share/doc/compute-doc/html/_sources/pyapi/index.rst.txt -2a0040e0a150de53ed929e963af635a8 usr/share/doc/compute-doc/html/_sources/pyapi/instance/guest_agent.rst.txt -dd6324cb85dc57ef37c4f8161aa2d233 usr/share/doc/compute-doc/html/_sources/pyapi/instance/index.rst.txt -c594567565cc48a247932409d9adcc4a usr/share/doc/compute-doc/html/_sources/pyapi/instance/instance.rst.txt -e6a69ab447e455dba6e7b865a3d872d2 usr/share/doc/compute-doc/html/_sources/pyapi/instance/schemas.rst.txt -ba27654c086857e64d58468b13bc31c4 usr/share/doc/compute-doc/html/_sources/pyapi/session.rst.txt -801ccc953fc57199b06ec122e10f784c usr/share/doc/compute-doc/html/_sources/pyapi/storage/index.rst.txt -324ae7c877f3cf7895b2a5d3af579345 usr/share/doc/compute-doc/html/_sources/pyapi/storage/pool.rst.txt -db91c0d83c2c80e9f9323a8943eeeff4 usr/share/doc/compute-doc/html/_sources/pyapi/storage/volume.rst.txt -572ed749dd8924c36f1afe9e8e14d4d3 usr/share/doc/compute-doc/html/_sources/pyapi/utils.rst.txt -4fc9d553e40384beedf38e21f205d2a7 usr/share/doc/compute-doc/html/_static/alabaster.css -23ffe661f835b08e157d492a86aae74d usr/share/doc/compute-doc/html/_static/basic.css -dad0c9b31e59069c83018ce87594ed65 usr/share/doc/compute-doc/html/_static/custom.css -5e103d51310d4e0c065325d795cc9def usr/share/doc/compute-doc/html/_static/documentation_options.js -ba0c95766a77a6c598a7ca542f1db738 usr/share/doc/compute-doc/html/_static/file.png -5b6b3233153feca50a94aa6c60873a5f usr/share/doc/compute-doc/html/_static/forkme_right_darkblue_121621.png -36b1a4b05451c7acde7ced60b2f6bc21 usr/share/doc/compute-doc/html/_static/minus.png -0d7849fd4d4148b7f78cab60a087633a usr/share/doc/compute-doc/html/_static/plus.png -4f81be1c1dd97a6ec76af15b8f926189 usr/share/doc/compute-doc/html/_static/pygments.css -fd297228a19ece7e38824d0704f3635d usr/share/doc/compute-doc/html/genindex.html -3e038e6169c721ebacf889ea4ac5c1bf usr/share/doc/compute-doc/html/index.html -b8e4906e5136e907ab0d7ae826720603 usr/share/doc/compute-doc/html/objects.inv -2658558520c0c9f209dd4c69516facfd usr/share/doc/compute-doc/html/py-modindex.html -4254a2ecc3e154f52646febebd0ef6e6 usr/share/doc/compute-doc/html/pyapi/exceptions.html -bf4609f321d2c60399574c3e52dd6a44 usr/share/doc/compute-doc/html/pyapi/index.html -730aab71986cb938e9aff03ba203c9a9 usr/share/doc/compute-doc/html/pyapi/instance/guest_agent.html -fad8eba8a9cb9b1befd8e0ecdf1bbe5f usr/share/doc/compute-doc/html/pyapi/instance/index.html -781272676f0b35c52f43b99f2ca86647 usr/share/doc/compute-doc/html/pyapi/instance/instance.html -ede88501ec628083bb1ad1cb86cdec9f usr/share/doc/compute-doc/html/pyapi/instance/schemas.html -4c8d372d298068aba7272d11feb2cc52 usr/share/doc/compute-doc/html/pyapi/session.html -000f86f6184a455843017772ff2fec9d usr/share/doc/compute-doc/html/pyapi/storage/index.html -a2b63c0194a1e55be8d7036b46851986 usr/share/doc/compute-doc/html/pyapi/storage/pool.html -8d4e9081b213585aad36b4daadc37e26 usr/share/doc/compute-doc/html/pyapi/storage/volume.html -307d7a44f4343b0f34ee758e4ab20d88 usr/share/doc/compute-doc/html/pyapi/utils.html -5999199d4710213969f7fb1b50647f4a usr/share/doc/compute-doc/html/search.html -148b182d3691ae88c629783c3623007d usr/share/doc/compute-doc/html/searchindex.js diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/changelog.Debian.gz b/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/changelog.Debian.gz deleted file mode 100644 index 40eae6f..0000000 Binary files a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/changelog.Debian.gz and /dev/null differ diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/copyright b/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/copyright deleted file mode 100644 index 185dcbf..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/copyright +++ /dev/null @@ -1,32 +0,0 @@ -Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ -Source: https://git.lulzette.ru/hstack/compute -Upstream-Name: compute - -Files: - * -Copyright: - 2023 ge -License: GPL-3.0+ - -Files: - debian/* -Copyright: - 2023 ge -License: GPL-3.0+ - -License: GPL-3.0+ - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - . - This package is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - . - You should have received a copy of the GNU General Public License - along with this program. If not, see . -Comment: - On Debian systems, the complete text of the GNU General - Public License version 3 can be found in "/usr/share/common-licenses/GPL-3". diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_sources/index.rst.txt b/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_sources/index.rst.txt deleted file mode 100644 index 81222c2..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_sources/index.rst.txt +++ /dev/null @@ -1,16 +0,0 @@ -Compute -======= - -Compute instances management library. - -.. toctree:: - :maxdepth: 1 - - pyapi/index - -Indices and tables ------------------- - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_sources/pyapi/exceptions.rst.txt b/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_sources/pyapi/exceptions.rst.txt deleted file mode 100644 index 3912721..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_sources/pyapi/exceptions.rst.txt +++ /dev/null @@ -1,5 +0,0 @@ -``exceptions`` -============== - -.. automodule:: compute.exceptions - :members: diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_sources/pyapi/index.rst.txt b/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_sources/pyapi/index.rst.txt deleted file mode 100644 index e0cebb8..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_sources/pyapi/index.rst.txt +++ /dev/null @@ -1,49 +0,0 @@ -Python API -========== - -The API allows you to perform actions on instances programmatically. Below is -an example of changing parameters and launching the `myinstance` instance. - -.. code-block:: python - - import logging - - from compute import Session - - logging.basicConfig(level=logging.DEBUG) - - with Session() as session: - instance = session.get_instance('myinstance') - instance.set_vcpus(4) - instance.start() - instance.set_autostart(enabled=True) - - -:class:`Session` context manager provides an abstraction over :class:`libvirt.virConnect` -and returns objects of other classes of the present library. - -Entity representation ---------------------- - -Entities such as a compute-instance are represented as classes. These classes directly -call libvirt methods to perform operations on the hypervisor. An example class is -:class:`Volume`. - -The configuration files of various libvirt objects in `compute` are described by special -dataclasses. The dataclass stores object parameters in its properties and can return an -XML config for libvirt using the ``to_xml()`` method. For example :class:`VolumeConfig`. - -`Pydantic `_ models are used to validate input data. -For example :class:`VolumeSchema`. - -Modules documentation ---------------------- - -.. toctree:: - :maxdepth: 4 - - session - instance/index - storage/index - utils - exceptions diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_sources/pyapi/instance/guest_agent.rst.txt b/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_sources/pyapi/instance/guest_agent.rst.txt deleted file mode 100644 index 1305140..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_sources/pyapi/instance/guest_agent.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -``guest_agent`` -=============== - -.. automodule:: compute.instance.guest_agent - :members: - :special-members: __init__ diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_sources/pyapi/instance/index.rst.txt b/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_sources/pyapi/instance/index.rst.txt deleted file mode 100644 index 659ffc2..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_sources/pyapi/instance/index.rst.txt +++ /dev/null @@ -1,10 +0,0 @@ -``instance`` -============ - -.. toctree:: - :maxdepth: 1 - :caption: Contents: - - instance - guest_agent - schemas diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_sources/pyapi/instance/instance.rst.txt b/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_sources/pyapi/instance/instance.rst.txt deleted file mode 100644 index 3c58f1f..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_sources/pyapi/instance/instance.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -``instance`` -============ - -.. automodule:: compute.instance.instance - :members: - :special-members: __init__ diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_sources/pyapi/instance/schemas.rst.txt b/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_sources/pyapi/instance/schemas.rst.txt deleted file mode 100644 index 7dacabf..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_sources/pyapi/instance/schemas.rst.txt +++ /dev/null @@ -1,5 +0,0 @@ -``schemas`` -=========== - -.. automodule:: compute.instance.schemas - :members: diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_sources/pyapi/session.rst.txt b/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_sources/pyapi/session.rst.txt deleted file mode 100644 index 2dec16e..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_sources/pyapi/session.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -``session`` -=========== - -.. automodule:: compute.session - :members: - :special-members: __init__ diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_sources/pyapi/storage/index.rst.txt b/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_sources/pyapi/storage/index.rst.txt deleted file mode 100644 index e9ea734..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_sources/pyapi/storage/index.rst.txt +++ /dev/null @@ -1,9 +0,0 @@ -``storage`` -============ - -.. toctree:: - :maxdepth: 1 - :caption: Contents: - - pool - volume diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_sources/pyapi/storage/pool.rst.txt b/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_sources/pyapi/storage/pool.rst.txt deleted file mode 100644 index 398124e..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_sources/pyapi/storage/pool.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -``pool`` -======== - -.. automodule:: compute.storage.pool - :members: - :special-members: __init__ diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_sources/pyapi/storage/volume.rst.txt b/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_sources/pyapi/storage/volume.rst.txt deleted file mode 100644 index e1ba8d0..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_sources/pyapi/storage/volume.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -``volume`` -========== - -.. automodule:: compute.storage.volume - :members: - :special-members: __init__ diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_sources/pyapi/utils.rst.txt b/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_sources/pyapi/utils.rst.txt deleted file mode 100644 index b5ab60a..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_sources/pyapi/utils.rst.txt +++ /dev/null @@ -1,14 +0,0 @@ -``utils`` -========= - -``utils.units`` ---------------- - -.. automodule:: compute.utils.units - :members: - -``utils.ids`` -------------- - -.. automodule:: compute.utils.ids - :members: diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_static/_sphinx_javascript_frameworks_compat.js b/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_static/_sphinx_javascript_frameworks_compat.js deleted file mode 120000 index e04de6d..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_static/_sphinx_javascript_frameworks_compat.js +++ /dev/null @@ -1 +0,0 @@ -../../../../javascript/sphinxdoc/1.0/_sphinx_javascript_frameworks_compat.js \ No newline at end of file diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_static/alabaster.css b/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_static/alabaster.css deleted file mode 100644 index 0eddaeb..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_static/alabaster.css +++ /dev/null @@ -1,701 +0,0 @@ -@import url("basic.css"); - -/* -- page layout ----------------------------------------------------------- */ - -body { - font-family: Georgia, serif; - font-size: 17px; - background-color: #fff; - color: #000; - margin: 0; - padding: 0; -} - - -div.document { - width: 940px; - margin: 30px auto 0 auto; -} - -div.documentwrapper { - float: left; - width: 100%; -} - -div.bodywrapper { - margin: 0 0 0 220px; -} - -div.sphinxsidebar { - width: 220px; - font-size: 14px; - line-height: 1.5; -} - -hr { - border: 1px solid #B1B4B6; -} - -div.body { - background-color: #fff; - color: #3E4349; - padding: 0 30px 0 30px; -} - -div.body > .section { - text-align: left; -} - -div.footer { - width: 940px; - margin: 20px auto 30px auto; - font-size: 14px; - color: #888; - text-align: right; -} - -div.footer a { - color: #888; -} - -p.caption { - font-family: inherit; - font-size: inherit; -} - - -div.relations { - display: none; -} - - -div.sphinxsidebar a { - color: #444; - text-decoration: none; - border-bottom: 1px dotted #999; -} - -div.sphinxsidebar a:hover { - border-bottom: 1px solid #999; -} - -div.sphinxsidebarwrapper { - padding: 18px 10px; -} - -div.sphinxsidebarwrapper p.logo { - padding: 0; - margin: -10px 0 0 0px; - text-align: center; -} - -div.sphinxsidebarwrapper h1.logo { - margin-top: -10px; - text-align: center; - margin-bottom: 5px; - text-align: left; -} - -div.sphinxsidebarwrapper h1.logo-name { - margin-top: 0px; -} - -div.sphinxsidebarwrapper p.blurb { - margin-top: 0; - font-style: normal; -} - -div.sphinxsidebar h3, -div.sphinxsidebar h4 { - font-family: Georgia, serif; - color: #444; - font-size: 24px; - font-weight: normal; - margin: 0 0 5px 0; - padding: 0; -} - -div.sphinxsidebar h4 { - font-size: 20px; -} - -div.sphinxsidebar h3 a { - color: #444; -} - -div.sphinxsidebar p.logo a, -div.sphinxsidebar h3 a, -div.sphinxsidebar p.logo a:hover, -div.sphinxsidebar h3 a:hover { - border: none; -} - -div.sphinxsidebar p { - color: #555; - margin: 10px 0; -} - -div.sphinxsidebar ul { - margin: 10px 0; - padding: 0; - color: #000; -} - -div.sphinxsidebar ul li.toctree-l1 > a { - font-size: 120%; -} - -div.sphinxsidebar ul li.toctree-l2 > a { - font-size: 110%; -} - -div.sphinxsidebar input { - border: 1px solid #CCC; - font-family: Georgia, serif; - font-size: 1em; -} - -div.sphinxsidebar hr { - border: none; - height: 1px; - color: #AAA; - background: #AAA; - - text-align: left; - margin-left: 0; - width: 50%; -} - -div.sphinxsidebar .badge { - border-bottom: none; -} - -div.sphinxsidebar .badge:hover { - border-bottom: none; -} - -/* To address an issue with donation coming after search */ -div.sphinxsidebar h3.donation { - margin-top: 10px; -} - -/* -- body styles ----------------------------------------------------------- */ - -a { - color: #004B6B; - text-decoration: underline; -} - -a:hover { - color: #6D4100; - text-decoration: underline; -} - -div.body h1, -div.body h2, -div.body h3, -div.body h4, -div.body h5, -div.body h6 { - font-family: Georgia, serif; - font-weight: normal; - margin: 30px 0px 10px 0px; - padding: 0; -} - -div.body h1 { margin-top: 0; padding-top: 0; font-size: 240%; } -div.body h2 { font-size: 180%; } -div.body h3 { font-size: 150%; } -div.body h4 { font-size: 130%; } -div.body h5 { font-size: 100%; } -div.body h6 { font-size: 100%; } - -a.headerlink { - color: #DDD; - padding: 0 4px; - text-decoration: none; -} - -a.headerlink:hover { - color: #444; - background: #EAEAEA; -} - -div.body p, div.body dd, div.body li { - line-height: 1.4em; -} - -div.admonition { - margin: 20px 0px; - padding: 10px 30px; - background-color: #EEE; - border: 1px solid #CCC; -} - -div.admonition tt.xref, div.admonition code.xref, div.admonition a tt { - background-color: #FBFBFB; - border-bottom: 1px solid #fafafa; -} - -div.admonition p.admonition-title { - font-family: Georgia, serif; - font-weight: normal; - font-size: 24px; - margin: 0 0 10px 0; - padding: 0; - line-height: 1; -} - -div.admonition p.last { - margin-bottom: 0; -} - -div.highlight { - background-color: #fff; -} - -dt:target, .highlight { - background: #FAF3E8; -} - -div.warning { - background-color: #FCC; - border: 1px solid #FAA; -} - -div.danger { - background-color: #FCC; - border: 1px solid #FAA; - -moz-box-shadow: 2px 2px 4px #D52C2C; - -webkit-box-shadow: 2px 2px 4px #D52C2C; - box-shadow: 2px 2px 4px #D52C2C; -} - -div.error { - background-color: #FCC; - border: 1px solid #FAA; - -moz-box-shadow: 2px 2px 4px #D52C2C; - -webkit-box-shadow: 2px 2px 4px #D52C2C; - box-shadow: 2px 2px 4px #D52C2C; -} - -div.caution { - background-color: #FCC; - border: 1px solid #FAA; -} - -div.attention { - background-color: #FCC; - border: 1px solid #FAA; -} - -div.important { - background-color: #EEE; - border: 1px solid #CCC; -} - -div.note { - background-color: #EEE; - border: 1px solid #CCC; -} - -div.tip { - background-color: #EEE; - border: 1px solid #CCC; -} - -div.hint { - background-color: #EEE; - border: 1px solid #CCC; -} - -div.seealso { - background-color: #EEE; - border: 1px solid #CCC; -} - -div.topic { - background-color: #EEE; -} - -p.admonition-title { - display: inline; -} - -p.admonition-title:after { - content: ":"; -} - -pre, tt, code { - font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; - font-size: 0.9em; -} - -.hll { - background-color: #FFC; - margin: 0 -12px; - padding: 0 12px; - display: block; -} - -img.screenshot { -} - -tt.descname, tt.descclassname, code.descname, code.descclassname { - font-size: 0.95em; -} - -tt.descname, code.descname { - padding-right: 0.08em; -} - -img.screenshot { - -moz-box-shadow: 2px 2px 4px #EEE; - -webkit-box-shadow: 2px 2px 4px #EEE; - box-shadow: 2px 2px 4px #EEE; -} - -table.docutils { - border: 1px solid #888; - -moz-box-shadow: 2px 2px 4px #EEE; - -webkit-box-shadow: 2px 2px 4px #EEE; - box-shadow: 2px 2px 4px #EEE; -} - -table.docutils td, table.docutils th { - border: 1px solid #888; - padding: 0.25em 0.7em; -} - -table.field-list, table.footnote { - border: none; - -moz-box-shadow: none; - -webkit-box-shadow: none; - box-shadow: none; -} - -table.footnote { - margin: 15px 0; - width: 100%; - border: 1px solid #EEE; - background: #FDFDFD; - font-size: 0.9em; -} - -table.footnote + table.footnote { - margin-top: -15px; - border-top: none; -} - -table.field-list th { - padding: 0 0.8em 0 0; -} - -table.field-list td { - padding: 0; -} - -table.field-list p { - margin-bottom: 0.8em; -} - -/* Cloned from - * https://github.com/sphinx-doc/sphinx/commit/ef60dbfce09286b20b7385333d63a60321784e68 - */ -.field-name { - -moz-hyphens: manual; - -ms-hyphens: manual; - -webkit-hyphens: manual; - hyphens: manual; -} - -table.footnote td.label { - width: .1px; - padding: 0.3em 0 0.3em 0.5em; -} - -table.footnote td { - padding: 0.3em 0.5em; -} - -dl { - margin: 0; - padding: 0; -} - -dl dd { - margin-left: 30px; -} - -blockquote { - margin: 0 0 0 30px; - padding: 0; -} - -ul, ol { - /* Matches the 30px from the narrow-screen "li > ul" selector below */ - margin: 10px 0 10px 30px; - padding: 0; -} - -pre { - background: #EEE; - padding: 7px 30px; - margin: 15px 0px; - line-height: 1.3em; -} - -div.viewcode-block:target { - background: #ffd; -} - -dl pre, blockquote pre, li pre { - margin-left: 0; - padding-left: 30px; -} - -tt, code { - background-color: #ecf0f3; - color: #222; - /* padding: 1px 2px; */ -} - -tt.xref, code.xref, a tt { - background-color: #FBFBFB; - border-bottom: 1px solid #fff; -} - -a.reference { - text-decoration: none; - border-bottom: 1px dotted #004B6B; -} - -/* Don't put an underline on images */ -a.image-reference, a.image-reference:hover { - border-bottom: none; -} - -a.reference:hover { - border-bottom: 1px solid #6D4100; -} - -a.footnote-reference { - text-decoration: none; - font-size: 0.7em; - vertical-align: top; - border-bottom: 1px dotted #004B6B; -} - -a.footnote-reference:hover { - border-bottom: 1px solid #6D4100; -} - -a:hover tt, a:hover code { - background: #EEE; -} - - -@media screen and (max-width: 870px) { - - div.sphinxsidebar { - display: none; - } - - div.document { - width: 100%; - - } - - div.documentwrapper { - margin-left: 0; - margin-top: 0; - margin-right: 0; - margin-bottom: 0; - } - - div.bodywrapper { - margin-top: 0; - margin-right: 0; - margin-bottom: 0; - margin-left: 0; - } - - ul { - margin-left: 0; - } - - li > ul { - /* Matches the 30px from the "ul, ol" selector above */ - margin-left: 30px; - } - - .document { - width: auto; - } - - .footer { - width: auto; - } - - .bodywrapper { - margin: 0; - } - - .footer { - width: auto; - } - - .github { - display: none; - } - - - -} - - - -@media screen and (max-width: 875px) { - - body { - margin: 0; - padding: 20px 30px; - } - - div.documentwrapper { - float: none; - background: #fff; - } - - div.sphinxsidebar { - display: block; - float: none; - width: 102.5%; - margin: 50px -30px -20px -30px; - padding: 10px 20px; - background: #333; - color: #FFF; - } - - div.sphinxsidebar h3, div.sphinxsidebar h4, div.sphinxsidebar p, - div.sphinxsidebar h3 a { - color: #fff; - } - - div.sphinxsidebar a { - color: #AAA; - } - - div.sphinxsidebar p.logo { - display: none; - } - - div.document { - width: 100%; - margin: 0; - } - - div.footer { - display: none; - } - - div.bodywrapper { - margin: 0; - } - - div.body { - min-height: 0; - padding: 0; - } - - .rtd_doc_footer { - display: none; - } - - .document { - width: auto; - } - - .footer { - width: auto; - } - - .footer { - width: auto; - } - - .github { - display: none; - } -} - - -/* misc. */ - -.revsys-inline { - display: none!important; -} - -/* Make nested-list/multi-paragraph items look better in Releases changelog - * pages. Without this, docutils' magical list fuckery causes inconsistent - * formatting between different release sub-lists. - */ -div#changelog > div.section > ul > li > p:only-child { - margin-bottom: 0; -} - -/* Hide fugly table cell borders in ..bibliography:: directive output */ -table.docutils.citation, table.docutils.citation td, table.docutils.citation th { - border: none; - /* Below needed in some edge cases; if not applied, bottom shadows appear */ - -moz-box-shadow: none; - -webkit-box-shadow: none; - box-shadow: none; -} - - -/* relbar */ - -.related { - line-height: 30px; - width: 100%; - font-size: 0.9rem; -} - -.related.top { - border-bottom: 1px solid #EEE; - margin-bottom: 20px; -} - -.related.bottom { - border-top: 1px solid #EEE; -} - -.related ul { - padding: 0; - margin: 0; - list-style: none; -} - -.related li { - display: inline; -} - -nav#rellinks { - float: right; -} - -nav#rellinks li+li:before { - content: "|"; -} - -nav#breadcrumbs li+li:before { - content: "\00BB"; -} - -/* Hide certain items when printing */ -@media print { - div.related { - display: none; - } -} \ No newline at end of file diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_static/basic.css b/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_static/basic.css deleted file mode 100644 index 4e9a9f1..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_static/basic.css +++ /dev/null @@ -1,900 +0,0 @@ -/* - * basic.css - * ~~~~~~~~~ - * - * Sphinx stylesheet -- basic theme. - * - * :copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS. - * :license: BSD, see LICENSE for details. - * - */ - -/* -- main layout ----------------------------------------------------------- */ - -div.clearer { - clear: both; -} - -div.section::after { - display: block; - content: ''; - clear: left; -} - -/* -- relbar ---------------------------------------------------------------- */ - -div.related { - width: 100%; - font-size: 90%; -} - -div.related h3 { - display: none; -} - -div.related ul { - margin: 0; - padding: 0 0 0 10px; - list-style: none; -} - -div.related li { - display: inline; -} - -div.related li.right { - float: right; - margin-right: 5px; -} - -/* -- sidebar --------------------------------------------------------------- */ - -div.sphinxsidebarwrapper { - padding: 10px 5px 0 10px; -} - -div.sphinxsidebar { - float: left; - width: 230px; - margin-left: -100%; - font-size: 90%; - word-wrap: break-word; - overflow-wrap : break-word; -} - -div.sphinxsidebar ul { - list-style: none; -} - -div.sphinxsidebar ul ul, -div.sphinxsidebar ul.want-points { - margin-left: 20px; - list-style: square; -} - -div.sphinxsidebar ul ul { - margin-top: 0; - margin-bottom: 0; -} - -div.sphinxsidebar form { - margin-top: 10px; -} - -div.sphinxsidebar input { - border: 1px solid #98dbcc; - font-family: sans-serif; - font-size: 1em; -} - -div.sphinxsidebar #searchbox form.search { - overflow: hidden; -} - -div.sphinxsidebar #searchbox input[type="text"] { - float: left; - width: 80%; - padding: 0.25em; - box-sizing: border-box; -} - -div.sphinxsidebar #searchbox input[type="submit"] { - float: left; - width: 20%; - border-left: none; - padding: 0.25em; - box-sizing: border-box; -} - - -img { - border: 0; - max-width: 100%; -} - -/* -- search page ----------------------------------------------------------- */ - -ul.search { - margin: 10px 0 0 20px; - padding: 0; -} - -ul.search li { - padding: 5px 0 5px 20px; - background-image: url(file.png); - background-repeat: no-repeat; - background-position: 0 7px; -} - -ul.search li a { - font-weight: bold; -} - -ul.search li p.context { - color: #888; - margin: 2px 0 0 30px; - text-align: left; -} - -ul.keywordmatches li.goodmatch a { - font-weight: bold; -} - -/* -- index page ------------------------------------------------------------ */ - -table.contentstable { - width: 90%; - margin-left: auto; - margin-right: auto; -} - -table.contentstable p.biglink { - line-height: 150%; -} - -a.biglink { - font-size: 1.3em; -} - -span.linkdescr { - font-style: italic; - padding-top: 5px; - font-size: 90%; -} - -/* -- general index --------------------------------------------------------- */ - -table.indextable { - width: 100%; -} - -table.indextable td { - text-align: left; - vertical-align: top; -} - -table.indextable ul { - margin-top: 0; - margin-bottom: 0; - list-style-type: none; -} - -table.indextable > tbody > tr > td > ul { - padding-left: 0em; -} - -table.indextable tr.pcap { - height: 10px; -} - -table.indextable tr.cap { - margin-top: 10px; - background-color: #f2f2f2; -} - -img.toggler { - margin-right: 3px; - margin-top: 3px; - cursor: pointer; -} - -div.modindex-jumpbox { - border-top: 1px solid #ddd; - border-bottom: 1px solid #ddd; - margin: 1em 0 1em 0; - padding: 0.4em; -} - -div.genindex-jumpbox { - border-top: 1px solid #ddd; - border-bottom: 1px solid #ddd; - margin: 1em 0 1em 0; - padding: 0.4em; -} - -/* -- domain module index --------------------------------------------------- */ - -table.modindextable td { - padding: 2px; - border-collapse: collapse; -} - -/* -- general body styles --------------------------------------------------- */ - -div.body { - min-width: 360px; - max-width: 800px; -} - -div.body p, div.body dd, div.body li, div.body blockquote { - -moz-hyphens: auto; - -ms-hyphens: auto; - -webkit-hyphens: auto; - hyphens: auto; -} - -a.headerlink { - visibility: hidden; -} - -h1:hover > a.headerlink, -h2:hover > a.headerlink, -h3:hover > a.headerlink, -h4:hover > a.headerlink, -h5:hover > a.headerlink, -h6:hover > a.headerlink, -dt:hover > a.headerlink, -caption:hover > a.headerlink, -p.caption:hover > a.headerlink, -div.code-block-caption:hover > a.headerlink { - visibility: visible; -} - -div.body p.caption { - text-align: inherit; -} - -div.body td { - text-align: left; -} - -.first { - margin-top: 0 !important; -} - -p.rubric { - margin-top: 30px; - font-weight: bold; -} - -img.align-left, figure.align-left, .figure.align-left, object.align-left { - clear: left; - float: left; - margin-right: 1em; -} - -img.align-right, figure.align-right, .figure.align-right, object.align-right { - clear: right; - float: right; - margin-left: 1em; -} - -img.align-center, figure.align-center, .figure.align-center, object.align-center { - display: block; - margin-left: auto; - margin-right: auto; -} - -img.align-default, figure.align-default, .figure.align-default { - display: block; - margin-left: auto; - margin-right: auto; -} - -.align-left { - text-align: left; -} - -.align-center { - text-align: center; -} - -.align-default { - text-align: center; -} - -.align-right { - text-align: right; -} - -/* -- sidebars -------------------------------------------------------------- */ - -div.sidebar, -aside.sidebar { - margin: 0 0 0.5em 1em; - border: 1px solid #ddb; - padding: 7px; - background-color: #ffe; - width: 40%; - float: right; - clear: right; - overflow-x: auto; -} - -p.sidebar-title { - font-weight: bold; -} -nav.contents, -aside.topic, -div.admonition, div.topic, blockquote { - clear: left; -} - -/* -- topics ---------------------------------------------------------------- */ -nav.contents, -aside.topic, -div.topic { - border: 1px solid #ccc; - padding: 7px; - margin: 10px 0 10px 0; -} - -p.topic-title { - font-size: 1.1em; - font-weight: bold; - margin-top: 10px; -} - -/* -- admonitions ----------------------------------------------------------- */ - -div.admonition { - margin-top: 10px; - margin-bottom: 10px; - padding: 7px; -} - -div.admonition dt { - font-weight: bold; -} - -p.admonition-title { - margin: 0px 10px 5px 0px; - font-weight: bold; -} - -div.body p.centered { - text-align: center; - margin-top: 25px; -} - -/* -- content of sidebars/topics/admonitions -------------------------------- */ - -div.sidebar > :last-child, -aside.sidebar > :last-child, -nav.contents > :last-child, -aside.topic > :last-child, -div.topic > :last-child, -div.admonition > :last-child { - margin-bottom: 0; -} - -div.sidebar::after, -aside.sidebar::after, -nav.contents::after, -aside.topic::after, -div.topic::after, -div.admonition::after, -blockquote::after { - display: block; - content: ''; - clear: both; -} - -/* -- tables ---------------------------------------------------------------- */ - -table.docutils { - margin-top: 10px; - margin-bottom: 10px; - border: 0; - border-collapse: collapse; -} - -table.align-center { - margin-left: auto; - margin-right: auto; -} - -table.align-default { - margin-left: auto; - margin-right: auto; -} - -table caption span.caption-number { - font-style: italic; -} - -table caption span.caption-text { -} - -table.docutils td, table.docutils th { - padding: 1px 8px 1px 5px; - border-top: 0; - border-left: 0; - border-right: 0; - border-bottom: 1px solid #aaa; -} - -th { - text-align: left; - padding-right: 5px; -} - -table.citation { - border-left: solid 1px gray; - margin-left: 1px; -} - -table.citation td { - border-bottom: none; -} - -th > :first-child, -td > :first-child { - margin-top: 0px; -} - -th > :last-child, -td > :last-child { - margin-bottom: 0px; -} - -/* -- figures --------------------------------------------------------------- */ - -div.figure, figure { - margin: 0.5em; - padding: 0.5em; -} - -div.figure p.caption, figcaption { - padding: 0.3em; -} - -div.figure p.caption span.caption-number, -figcaption span.caption-number { - font-style: italic; -} - -div.figure p.caption span.caption-text, -figcaption span.caption-text { -} - -/* -- field list styles ----------------------------------------------------- */ - -table.field-list td, table.field-list th { - border: 0 !important; -} - -.field-list ul { - margin: 0; - padding-left: 1em; -} - -.field-list p { - margin: 0; -} - -.field-name { - -moz-hyphens: manual; - -ms-hyphens: manual; - -webkit-hyphens: manual; - hyphens: manual; -} - -/* -- hlist styles ---------------------------------------------------------- */ - -table.hlist { - margin: 1em 0; -} - -table.hlist td { - vertical-align: top; -} - -/* -- object description styles --------------------------------------------- */ - -.sig { - font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; -} - -.sig-name, code.descname { - background-color: transparent; - font-weight: bold; -} - -.sig-name { - font-size: 1.1em; -} - -code.descname { - font-size: 1.2em; -} - -.sig-prename, code.descclassname { - background-color: transparent; -} - -.optional { - font-size: 1.3em; -} - -.sig-paren { - font-size: larger; -} - -.sig-param.n { - font-style: italic; -} - -/* C++ specific styling */ - -.sig-inline.c-texpr, -.sig-inline.cpp-texpr { - font-family: unset; -} - -.sig.c .k, .sig.c .kt, -.sig.cpp .k, .sig.cpp .kt { - color: #0033B3; -} - -.sig.c .m, -.sig.cpp .m { - color: #1750EB; -} - -.sig.c .s, .sig.c .sc, -.sig.cpp .s, .sig.cpp .sc { - color: #067D17; -} - - -/* -- other body styles ----------------------------------------------------- */ - -ol.arabic { - list-style: decimal; -} - -ol.loweralpha { - list-style: lower-alpha; -} - -ol.upperalpha { - list-style: upper-alpha; -} - -ol.lowerroman { - list-style: lower-roman; -} - -ol.upperroman { - list-style: upper-roman; -} - -:not(li) > ol > li:first-child > :first-child, -:not(li) > ul > li:first-child > :first-child { - margin-top: 0px; -} - -:not(li) > ol > li:last-child > :last-child, -:not(li) > ul > li:last-child > :last-child { - margin-bottom: 0px; -} - -ol.simple ol p, -ol.simple ul p, -ul.simple ol p, -ul.simple ul p { - margin-top: 0; -} - -ol.simple > li:not(:first-child) > p, -ul.simple > li:not(:first-child) > p { - margin-top: 0; -} - -ol.simple p, -ul.simple p { - margin-bottom: 0; -} -aside.footnote > span, -div.citation > span { - float: left; -} -aside.footnote > span:last-of-type, -div.citation > span:last-of-type { - padding-right: 0.5em; -} -aside.footnote > p { - margin-left: 2em; -} -div.citation > p { - margin-left: 4em; -} -aside.footnote > p:last-of-type, -div.citation > p:last-of-type { - margin-bottom: 0em; -} -aside.footnote > p:last-of-type:after, -div.citation > p:last-of-type:after { - content: ""; - clear: both; -} - -dl.field-list { - display: grid; - grid-template-columns: fit-content(30%) auto; -} - -dl.field-list > dt { - font-weight: bold; - word-break: break-word; - padding-left: 0.5em; - padding-right: 5px; -} - -dl.field-list > dd { - padding-left: 0.5em; - margin-top: 0em; - margin-left: 0em; - margin-bottom: 0em; -} - -dl { - margin-bottom: 15px; -} - -dd > :first-child { - margin-top: 0px; -} - -dd ul, dd table { - margin-bottom: 10px; -} - -dd { - margin-top: 3px; - margin-bottom: 10px; - margin-left: 30px; -} - -dl > dd:last-child, -dl > dd:last-child > :last-child { - margin-bottom: 0; -} - -dt:target, span.highlighted { - background-color: #fbe54e; -} - -rect.highlighted { - fill: #fbe54e; -} - -dl.glossary dt { - font-weight: bold; - font-size: 1.1em; -} - -.versionmodified { - font-style: italic; -} - -.system-message { - background-color: #fda; - padding: 5px; - border: 3px solid red; -} - -.footnote:target { - background-color: #ffa; -} - -.line-block { - display: block; - margin-top: 1em; - margin-bottom: 1em; -} - -.line-block .line-block { - margin-top: 0; - margin-bottom: 0; - margin-left: 1.5em; -} - -.guilabel, .menuselection { - font-family: sans-serif; -} - -.accelerator { - text-decoration: underline; -} - -.classifier { - font-style: oblique; -} - -.classifier:before { - font-style: normal; - margin: 0 0.5em; - content: ":"; - display: inline-block; -} - -abbr, acronym { - border-bottom: dotted 1px; - cursor: help; -} - -/* -- code displays --------------------------------------------------------- */ - -pre { - overflow: auto; - overflow-y: hidden; /* fixes display issues on Chrome browsers */ -} - -pre, div[class*="highlight-"] { - clear: both; -} - -span.pre { - -moz-hyphens: none; - -ms-hyphens: none; - -webkit-hyphens: none; - hyphens: none; - white-space: nowrap; -} - -div[class*="highlight-"] { - margin: 1em 0; -} - -td.linenos pre { - border: 0; - background-color: transparent; - color: #aaa; -} - -table.highlighttable { - display: block; -} - -table.highlighttable tbody { - display: block; -} - -table.highlighttable tr { - display: flex; -} - -table.highlighttable td { - margin: 0; - padding: 0; -} - -table.highlighttable td.linenos { - padding-right: 0.5em; -} - -table.highlighttable td.code { - flex: 1; - overflow: hidden; -} - -.highlight .hll { - display: block; -} - -div.highlight pre, -table.highlighttable pre { - margin: 0; -} - -div.code-block-caption + div { - margin-top: 0; -} - -div.code-block-caption { - margin-top: 1em; - padding: 2px 5px; - font-size: small; -} - -div.code-block-caption code { - background-color: transparent; -} - -table.highlighttable td.linenos, -span.linenos, -div.highlight span.gp { /* gp: Generic.Prompt */ - user-select: none; - -webkit-user-select: text; /* Safari fallback only */ - -webkit-user-select: none; /* Chrome/Safari */ - -moz-user-select: none; /* Firefox */ - -ms-user-select: none; /* IE10+ */ -} - -div.code-block-caption span.caption-number { - padding: 0.1em 0.3em; - font-style: italic; -} - -div.code-block-caption span.caption-text { -} - -div.literal-block-wrapper { - margin: 1em 0; -} - -code.xref, a code { - background-color: transparent; - font-weight: bold; -} - -h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { - background-color: transparent; -} - -.viewcode-link { - float: right; -} - -.viewcode-back { - float: right; - font-family: sans-serif; -} - -div.viewcode-block:target { - margin: -1px -10px; - padding: 0 10px; -} - -/* -- math display ---------------------------------------------------------- */ - -img.math { - vertical-align: middle; -} - -div.body div.math p { - text-align: center; -} - -span.eqno { - float: right; -} - -span.eqno a.headerlink { - position: absolute; - z-index: 1; -} - -div.math:hover a.headerlink { - visibility: visible; -} - -/* -- printout stylesheet --------------------------------------------------- */ - -@media print { - div.document, - div.documentwrapper, - div.bodywrapper { - margin: 0 !important; - width: 100%; - } - - div.sphinxsidebar, - div.related, - div.footer, - #top-link { - display: none; - } -} \ No newline at end of file diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_static/custom.css b/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_static/custom.css deleted file mode 100644 index 2a924f1..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_static/custom.css +++ /dev/null @@ -1 +0,0 @@ -/* This file intentionally left blank. */ diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_static/doctools.js b/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_static/doctools.js deleted file mode 120000 index e51872e..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_static/doctools.js +++ /dev/null @@ -1 +0,0 @@ -../../../../javascript/sphinxdoc/1.0/doctools.js \ No newline at end of file diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_static/documentation_options.js b/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_static/documentation_options.js deleted file mode 100644 index e49ed18..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_static/documentation_options.js +++ /dev/null @@ -1,14 +0,0 @@ -var DOCUMENTATION_OPTIONS = { - URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'), - VERSION: '0.1.0', - LANGUAGE: 'en', - COLLAPSE_INDEX: false, - BUILDER: 'html', - FILE_SUFFIX: '.html', - LINK_SUFFIX: '.html', - HAS_SOURCE: true, - SOURCELINK_SUFFIX: '.txt', - NAVIGATION_WITH_KEYS: false, - SHOW_SEARCH_SUMMARY: true, - ENABLE_SEARCH_SHORTCUTS: true, -}; \ No newline at end of file diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_static/file.png b/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_static/file.png deleted file mode 100644 index a858a41..0000000 Binary files a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_static/file.png and /dev/null differ diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_static/forkme_right_darkblue_121621.png b/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_static/forkme_right_darkblue_121621.png deleted file mode 100644 index 146ef8a..0000000 Binary files a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_static/forkme_right_darkblue_121621.png and /dev/null differ diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_static/jquery.js b/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_static/jquery.js deleted file mode 120000 index e82f704..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_static/jquery.js +++ /dev/null @@ -1 +0,0 @@ -../../../../javascript/sphinxdoc/1.0/jquery.js \ No newline at end of file diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_static/language_data.js b/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_static/language_data.js deleted file mode 120000 index 678e1c9..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_static/language_data.js +++ /dev/null @@ -1 +0,0 @@ -../../../../javascript/sphinxdoc/1.0/language_data.js \ No newline at end of file diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_static/minus.png b/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_static/minus.png deleted file mode 100644 index d96755f..0000000 Binary files a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_static/minus.png and /dev/null differ diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_static/plus.png b/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_static/plus.png deleted file mode 100644 index 7107cec..0000000 Binary files a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_static/plus.png and /dev/null differ diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_static/pygments.css b/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_static/pygments.css deleted file mode 100644 index 9abe04b..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_static/pygments.css +++ /dev/null @@ -1,83 +0,0 @@ -pre { line-height: 125%; } -td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } -span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } -td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } -span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } -.highlight .hll { background-color: #ffffcc } -.highlight { background: #f8f8f8; } -.highlight .c { color: #8f5902; font-style: italic } /* Comment */ -.highlight .err { color: #a40000; border: 1px solid #ef2929 } /* Error */ -.highlight .g { color: #000000 } /* Generic */ -.highlight .k { color: #004461; font-weight: bold } /* Keyword */ -.highlight .l { color: #000000 } /* Literal */ -.highlight .n { color: #000000 } /* Name */ -.highlight .o { color: #582800 } /* Operator */ -.highlight .x { color: #000000 } /* Other */ -.highlight .p { color: #000000; font-weight: bold } /* Punctuation */ -.highlight .ch { color: #8f5902; font-style: italic } /* Comment.Hashbang */ -.highlight .cm { color: #8f5902; font-style: italic } /* Comment.Multiline */ -.highlight .cp { color: #8f5902 } /* Comment.Preproc */ -.highlight .cpf { color: #8f5902; font-style: italic } /* Comment.PreprocFile */ -.highlight .c1 { color: #8f5902; font-style: italic } /* Comment.Single */ -.highlight .cs { color: #8f5902; font-style: italic } /* Comment.Special */ -.highlight .gd { color: #a40000 } /* Generic.Deleted */ -.highlight .ge { color: #000000; font-style: italic } /* Generic.Emph */ -.highlight .gr { color: #ef2929 } /* Generic.Error */ -.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ -.highlight .gi { color: #00A000 } /* Generic.Inserted */ -.highlight .go { color: #888888 } /* Generic.Output */ -.highlight .gp { color: #745334 } /* Generic.Prompt */ -.highlight .gs { color: #000000; font-weight: bold } /* Generic.Strong */ -.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ -.highlight .gt { color: #a40000; font-weight: bold } /* Generic.Traceback */ -.highlight .kc { color: #004461; font-weight: bold } /* Keyword.Constant */ -.highlight .kd { color: #004461; font-weight: bold } /* Keyword.Declaration */ -.highlight .kn { color: #004461; font-weight: bold } /* Keyword.Namespace */ -.highlight .kp { color: #004461; font-weight: bold } /* Keyword.Pseudo */ -.highlight .kr { color: #004461; font-weight: bold } /* Keyword.Reserved */ -.highlight .kt { color: #004461; font-weight: bold } /* Keyword.Type */ -.highlight .ld { color: #000000 } /* Literal.Date */ -.highlight .m { color: #990000 } /* Literal.Number */ -.highlight .s { color: #4e9a06 } /* Literal.String */ -.highlight .na { color: #c4a000 } /* Name.Attribute */ -.highlight .nb { color: #004461 } /* Name.Builtin */ -.highlight .nc { color: #000000 } /* Name.Class */ -.highlight .no { color: #000000 } /* Name.Constant */ -.highlight .nd { color: #888888 } /* Name.Decorator */ -.highlight .ni { color: #ce5c00 } /* Name.Entity */ -.highlight .ne { color: #cc0000; font-weight: bold } /* Name.Exception */ -.highlight .nf { color: #000000 } /* Name.Function */ -.highlight .nl { color: #f57900 } /* Name.Label */ -.highlight .nn { color: #000000 } /* Name.Namespace */ -.highlight .nx { color: #000000 } /* Name.Other */ -.highlight .py { color: #000000 } /* Name.Property */ -.highlight .nt { color: #004461; font-weight: bold } /* Name.Tag */ -.highlight .nv { color: #000000 } /* Name.Variable */ -.highlight .ow { color: #004461; font-weight: bold } /* Operator.Word */ -.highlight .pm { color: #000000; font-weight: bold } /* Punctuation.Marker */ -.highlight .w { color: #f8f8f8; text-decoration: underline } /* Text.Whitespace */ -.highlight .mb { color: #990000 } /* Literal.Number.Bin */ -.highlight .mf { color: #990000 } /* Literal.Number.Float */ -.highlight .mh { color: #990000 } /* Literal.Number.Hex */ -.highlight .mi { color: #990000 } /* Literal.Number.Integer */ -.highlight .mo { color: #990000 } /* Literal.Number.Oct */ -.highlight .sa { color: #4e9a06 } /* Literal.String.Affix */ -.highlight .sb { color: #4e9a06 } /* Literal.String.Backtick */ -.highlight .sc { color: #4e9a06 } /* Literal.String.Char */ -.highlight .dl { color: #4e9a06 } /* Literal.String.Delimiter */ -.highlight .sd { color: #8f5902; font-style: italic } /* Literal.String.Doc */ -.highlight .s2 { color: #4e9a06 } /* Literal.String.Double */ -.highlight .se { color: #4e9a06 } /* Literal.String.Escape */ -.highlight .sh { color: #4e9a06 } /* Literal.String.Heredoc */ -.highlight .si { color: #4e9a06 } /* Literal.String.Interpol */ -.highlight .sx { color: #4e9a06 } /* Literal.String.Other */ -.highlight .sr { color: #4e9a06 } /* Literal.String.Regex */ -.highlight .s1 { color: #4e9a06 } /* Literal.String.Single */ -.highlight .ss { color: #4e9a06 } /* Literal.String.Symbol */ -.highlight .bp { color: #3465a4 } /* Name.Builtin.Pseudo */ -.highlight .fm { color: #000000 } /* Name.Function.Magic */ -.highlight .vc { color: #000000 } /* Name.Variable.Class */ -.highlight .vg { color: #000000 } /* Name.Variable.Global */ -.highlight .vi { color: #000000 } /* Name.Variable.Instance */ -.highlight .vm { color: #000000 } /* Name.Variable.Magic */ -.highlight .il { color: #990000 } /* Literal.Number.Integer.Long */ \ No newline at end of file diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_static/searchtools.js b/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_static/searchtools.js deleted file mode 120000 index 2d33672..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_static/searchtools.js +++ /dev/null @@ -1 +0,0 @@ -../../../../javascript/sphinxdoc/1.0/searchtools.js \ No newline at end of file diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_static/sphinx_highlight.js b/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_static/sphinx_highlight.js deleted file mode 120000 index 75db705..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_static/sphinx_highlight.js +++ /dev/null @@ -1 +0,0 @@ -../../../../javascript/sphinxdoc/1.0/sphinx_highlight.js \ No newline at end of file diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_static/underscore.js b/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_static/underscore.js deleted file mode 120000 index 94df107..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/_static/underscore.js +++ /dev/null @@ -1 +0,0 @@ -../../../../javascript/sphinxdoc/1.0/underscore.js \ No newline at end of file diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/genindex.html b/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/genindex.html deleted file mode 100644 index 9434347..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/genindex.html +++ /dev/null @@ -1,614 +0,0 @@ - - - - - - - - Index — Compute 0.1.0 documentation - - - - - - - - - - - - - - - - - - - -
-
-
- - -
- - -

Index

- -
- _ - | A - | B - | C - | D - | E - | G - | I - | L - | M - | N - | P - | R - | S - | T - | U - | V - -
-

_

- - -
- -

A

- - - -
- -

B

- - -
- -

C

- - - -
- -

D

- - - -
- -

E

- - - -
- -

G

- - - -
- -

I

- - - -
- -

L

- - - -
- -

M

- - -
- -

N

- - - -
- -

P

- - - -
- -

R

- - - -
- -

S

- - - -
- -

T

- - - -
- -

U

- - -
- -

V

- - - -
- - - -
- -
-
- -
-
- - - - - - - \ No newline at end of file diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/index.html b/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/index.html deleted file mode 100644 index dfb6fab..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/index.html +++ /dev/null @@ -1,122 +0,0 @@ - - - - - - - - - Compute — Compute 0.1.0 documentation - - - - - - - - - - - - - - - - - - - - -
-
-
- - -
- -
-

Compute

-

Compute instances management library.

-
- -
-
-

Indices and tables

- -
-
- - -
- -
-
- -
-
- - - - - - - \ No newline at end of file diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/objects.inv b/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/objects.inv deleted file mode 100644 index 353c123..0000000 Binary files a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/objects.inv and /dev/null differ diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/py-modindex.html b/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/py-modindex.html deleted file mode 100644 index d9f35ef..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/py-modindex.html +++ /dev/null @@ -1,165 +0,0 @@ - - - - - - - - Python Module Index — Compute 0.1.0 documentation - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - -
- - -

Python Module Index

- -
- c -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
- c
- compute -
    - compute.exceptions -
    - compute.instance.guest_agent -
    - compute.instance.instance -
    - compute.instance.schemas -
    - compute.session -
    - compute.storage.pool -
    - compute.storage.volume -
    - compute.utils.ids -
    - compute.utils.units -
- - -
- -
-
- -
-
- - - - - - - \ No newline at end of file diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/pyapi/exceptions.html b/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/pyapi/exceptions.html deleted file mode 100644 index 1868528..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/pyapi/exceptions.html +++ /dev/null @@ -1,183 +0,0 @@ - - - - - - - - - exceptions — Compute 0.1.0 documentation - - - - - - - - - - - - - - - - - - - - -
-
-
- - -
- -
-

exceptions

-

Exceptions.

-
-
-exception compute.exceptions.ComputeError
-

Basic exception class.

-
- -
-
-exception compute.exceptions.ConfigLoaderError
-

Something went wrong when loading configuration.

-
- -
-
-exception compute.exceptions.GuestAgentCommandNotSupportedError
-

Guest agent command is not supported or blacklisted on guest.

-
- -
-
-exception compute.exceptions.GuestAgentError
-

Something went wring when QEMU Guest Agent call.

-
- -
-
-exception compute.exceptions.GuestAgentTimeoutExceededError(msg: int)
-

QEMU timeout exceeded.

-
- -
-
-exception compute.exceptions.GuestAgentUnavailableError
-

Guest agent is not connected or is unavailable.

-
- -
-
-exception compute.exceptions.InstanceError
-

Something went wrong while interacting with the domain.

-
- -
-
-exception compute.exceptions.InstanceNotFoundError(msg: str)
-

Virtual machine or container not found on compute node.

-
- -
-
-exception compute.exceptions.SessionError
-

Something went wrong while connecting to libvirtd.

-
- -
-
-exception compute.exceptions.StoragePoolError
-

Something went wrong when operating with storage pool.

-
- -
-
-exception compute.exceptions.StoragePoolNotFoundError(msg: str)
-

Storage pool not found.

-
- -
-
-exception compute.exceptions.VolumeNotFoundError(msg: str)
-

Storage volume not found.

-
- -
- - -
- -
-
- -
-
- - - - - - - \ No newline at end of file diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/pyapi/index.html b/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/pyapi/index.html deleted file mode 100644 index 7162de0..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/pyapi/index.html +++ /dev/null @@ -1,342 +0,0 @@ - - - - - - - - - Python API — Compute 0.1.0 documentation - - - - - - - - - - - - - - - - - - - - - -
-
-
- - -
- -
-

Python API

-

The API allows you to perform actions on instances programmatically. Below is -an example of changing parameters and launching the myinstance instance.

-
import logging
-
-from compute import Session
-
-logging.basicConfig(level=logging.DEBUG)
-
-with Session() as session:
-    instance = session.get_instance('myinstance')
-    instance.set_vcpus(4)
-    instance.start()
-    instance.set_autostart(enabled=True)
-
-
-

Session context manager provides an abstraction over libvirt.virConnect -and returns objects of other classes of the present library.

-
-

Entity representation

-

Entities such as a compute-instance are represented as classes. These classes directly -call libvirt methods to perform operations on the hypervisor. An example class is -Volume.

-

The configuration files of various libvirt objects in compute are described by special -dataclasses. The dataclass stores object parameters in its properties and can return an -XML config for libvirt using the to_xml() method. For example VolumeConfig.

-

Pydantic models are used to validate input data. -For example VolumeSchema.

-
-
-

Modules documentation

-
- -
-
-
- - -
- -
-
- -
-
- - - - - - - \ No newline at end of file diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/pyapi/instance/guest_agent.html b/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/pyapi/instance/guest_agent.html deleted file mode 100644 index a8cca32..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/pyapi/instance/guest_agent.html +++ /dev/null @@ -1,266 +0,0 @@ - - - - - - - - - guest_agent — Compute 0.1.0 documentation - - - - - - - - - - - - - - - - - - - - - -
-
-
- - -
- -
-

guest_agent

-

Interacting with the QEMU Guest Agent.

-
-
-class compute.instance.guest_agent.GuestAgent(domain: virDomain, timeout: int = 60)
-

Class for interacting with QEMU guest agent.

-
-
-__init__(domain: virDomain, timeout: int = 60)
-

Initialise GuestAgent.

-
-
Parameters:
-
    -
  • domain – Libvirt domain object

  • -
  • timeout – QEMU timeout

  • -
-
-
-
- -
-
-execute(command: dict) dict
-

Execute QEMU guest agent command.

-

See: https://qemu-project.gitlab.io/qemu/interop/qemu-ga-ref.html

-
-
Parameters:
-

command – QEMU guest agent command as dict

-
-
Returns:
-

Command output

-
-
Return type:
-

dict

-
-
-
- -
-
-get_supported_commands() set[str]
-

Return set of supported guest agent commands.

-
- -
-
-guest_exec(path: str, args: list[str] | None = None, env: list[str] | None = None, stdin: str | None = None, *, capture_output: bool = False, decode_output: bool = False, poll: bool = False) GuestExecOutput
-

Execute qemu-exec command and return output.

-
-
Parameters:
-
    -
  • path – Path ot executable on guest.

  • -
  • arg – List of arguments to pass to executable.

  • -
  • env – List of environment variables to pass to executable. -For example: ['LANG=C', 'TERM=xterm']

  • -
  • stdin – Data to pass to executable STDIN.

  • -
  • capture_output – Capture command output.

  • -
  • decode_output – Use base64_decode() to decode command output. -Affects only if capture_output is True.

  • -
  • poll – Poll command output. Uses self.timeout and -POLL_INTERVAL constant.

  • -
-
-
Returns:
-

Command output

-
-
Return type:
-

GuestExecOutput

-
-
-
- -
-
-guest_exec_status(pid: int, *, poll: bool = False, poll_interval: float = 0.3) dict
-

Execute guest-exec-status and return output.

-
-
Parameters:
-
    -
  • pid – PID in guest.

  • -
  • poll – If True poll command status.

  • -
  • poll_interval – Time between attempts to obtain command status.

  • -
-
-
Returns:
-

Command output

-
-
Return type:
-

dict

-
-
-
- -
-
-is_available() bool
-

Execute guest-ping.

-
-
Returns:
-

True or False if guest agent is unreachable.

-
-
Return type:
-

bool

-
-
-
- -
-
-raise_for_commands(commands: list[str]) None
-

Raise exception if QEMU GA command is not available.

-
-
Parameters:
-

commands – List of required commands

-
-
Raise:
-

GuestAgentCommandNotSupportedError

-
-
-
- -
- -
-
-class compute.instance.guest_agent.GuestExecOutput(exited: bool | None = None, exitcode: int | None = None, stdout: str | None = None, stderr: str | None = None)
-

QEMU guest-exec command output.

-
-
-exitcode: int | None
-

Alias for field number 1

-
- -
-
-exited: bool | None
-

Alias for field number 0

-
- -
-
-stderr: str | None
-

Alias for field number 3

-
- -
-
-stdout: str | None
-

Alias for field number 2

-
- -
- -
- - -
- -
-
- -
-
- - - - - - - \ No newline at end of file diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/pyapi/instance/index.html b/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/pyapi/instance/index.html deleted file mode 100644 index 87e074f..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/pyapi/instance/index.html +++ /dev/null @@ -1,120 +0,0 @@ - - - - - - - - - instance — Compute 0.1.0 documentation - - - - - - - - - - - - - - - - - - - - - -
-
-
- - -
- -
-

instance

-
-

Contents:

- -
-
- - -
- -
-
- -
-
- - - - - - - \ No newline at end of file diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/pyapi/instance/instance.html b/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/pyapi/instance/instance.html deleted file mode 100644 index 9f34c04..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/pyapi/instance/instance.html +++ /dev/null @@ -1,490 +0,0 @@ - - - - - - - - - instance — Compute 0.1.0 documentation - - - - - - - - - - - - - - - - - - - - - -
-
-
- - -
- -
-

instance

-

Manage compute instances.

-
-
-class compute.instance.instance.Instance(domain: virDomain)
-

Manage compute instances.

-
-
-__init__(domain: virDomain)
-

Initialise Instance.

-
-
Variables:
-
    -
  • domain (libvirt.virDomain) – domain object

  • -
  • connection (libvirt.virConnect) – connection object

  • -
  • name (str) – domain name

  • -
  • guest_agent (GuestAgent) – GuestAgent object

  • -
-
-
Parameters:
-

domain – libvirt domain object

-
-
-
- -
-
-attach_device(device: EntityConfig, *, live: bool = False) None
-

Attach device to compute instance.

-
-
Parameters:
-
    -
  • device – Object with device description e.g. DiskConfig

  • -
  • live – Affect a running instance

  • -
-
-
-
- -
-
-delete() None
-

Undefine instance.

-
- -
-
-delete_ssh_keys(user: str, ssh_keys: list[str]) None
-

Remove SSH keys from guest for specific user.

-
-
Parameters:
-
    -
  • user – Username.

  • -
  • ssh_keys – List of public SSH keys.

  • -
-
-
-
- -
-
-detach_device(device: EntityConfig, *, live: bool = False) None
-

Dettach device from compute instance.

-
-
Parameters:
-
    -
  • device – Object with device description e.g. DiskConfig

  • -
  • live – Affect a running instance

  • -
-
-
-
- -
-
-detach_disk(name: str) None
-

Detach disk device by target name.

-

There is no attach_disk() method. Use attach_device() -with DiskConfig as argument.

-
-
Parameters:
-

name – Disk name e.g. ‘vda’, ‘sda’, etc. This name may -not match the name of the disk inside the guest OS.

-
-
-
- -
-
-dump_xml(*, inactive: bool = False) str
-

Return instance XML description.

-
- -
-
-get_disks() list[compute.storage.volume.DiskConfig]
-

Return list of attached disks.

-
- -
-
-get_info() InstanceInfo
-

Return instance info.

-
- -
-
-get_max_memory() int
-

Maximum memory value for domain in KiB.

-
- -
-
-get_max_vcpus() int
-

Maximum vCPUs number for domain.

-
- -
-
-get_ssh_keys(user: str) list[str]
-

Return list of SSH keys on guest for specific user.

-
-
Parameters:
-

user – Username.

-
-
-
- -
-
-get_status() str
-

Return instance state: ‘running’, ‘shutoff’, etc.

-

Reference: -https://libvirt.org/html/libvirt-libvirt-domain.html#virDomainState

-
- -
-
-is_autostart() bool
-

Return True if instance autostart is enabled, else return False.

-
- -
-
-is_running() bool
-

Return True if instance is running, else return False.

-
- -
-
-pause() None
-

Pause instance.

-
- -
-
-power_reset() None
-

Shutdown instance and start.

-

By analogy with real hardware, this is a normal server shutdown, -and then turning off from the power supply and turning it on again.

-

This method is applicable in cases where there has been a -configuration change in libvirt and you need to restart the -instance to apply the new configuration.

-
- -
-
-reboot() None
-

Send ACPI signal to guest OS to reboot. OS may ignore this.

-
- -
-
-reset() None
-

Reset instance.

-

Copypaste from libvirt doc:

-

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.

-

Note that there is a risk of data loss caused by reset without any -guest OS shutdown.

-
- -
-
-resize_disk(name: str, capacity: int, unit: DataUnit) None
-

Resize attached block device.

-
-
Parameters:
-
    -
  • name – Disk device name e.g. vda, sda, etc.

  • -
  • capacity – New capacity.

  • -
  • unit – Capacity unit.

  • -
-
-
-
- -
-
-resume() None
-

Resume paused instance.

-
- -
-
-set_autostart(*, enabled: bool) None
-

Set autostart flag for instance.

-
-
Parameters:
-

enabled – Bool argument to set or unset autostart flag.

-
-
-
- -
-
-set_memory(memory: int, *, live: bool = False) None
-

Set memory.

-

If live is True and instance is not currently running set memory -in config and will applied when instance boot.

-
-
Parameters:
-
    -
  • memory – Memory value in mebibytes

  • -
  • live – Affect a running instance

  • -
-
-
-
- -
-
-set_ssh_keys(user: str, ssh_keys: list[str]) None
-

Add SSH keys to guest for specific user.

-
-
Parameters:
-
    -
  • user – Username.

  • -
  • ssh_keys – List of public SSH keys.

  • -
-
-
-
- -
-
-set_user_password(user: str, password: str, *, encrypted: bool = False) None
-

Set new user password in guest OS.

-

This action performs by guest agent inside the guest.

-
-
Parameters:
-
    -
  • user – Username.

  • -
  • password – Password.

  • -
  • encrypted – Set it to True if password is already encrypted. -Right encryption method depends on guest OS.

  • -
-
-
-
- -
-
-set_vcpus(nvcpus: int, *, live: bool = False) None
-

Set vCPU number.

-

If live is True and instance is not currently running vCPUs -will set in config and will applied when instance boot.

-

NB: Note that if this call is executed before the guest has -finished booting, the guest may fail to process the change.

-
-
Parameters:
-
    -
  • nvcpus – Number of vCPUs

  • -
  • live – Affect a running instance

  • -
-
-
-
- -
-
-shutdown(method: str | None = None) None
-

Shutdown instance.

-

Shutdown methods:

-
-
SOFT

Use guest agent to shutdown. If guest agent is unavailable -NORMAL method will be used.

-
-
NORMAL

Use method choosen by hypervisor to shutdown. Usually send ACPI -signal to guest OS. OS may ignore ACPI e.g. if guest is hanged.

-
-
HARD

Shutdown instance without any guest OS shutdown. This is simular -to unplugging machine from power. Internally send SIGTERM to -instance process and destroy it gracefully.

-
-
UNSAFE

Force shutdown. Internally send SIGKILL to instance process. -There is high data corruption risk!

-
-
-

If method is None NORMAL method will used.

-
-
Parameters:
-

method – Method used to shutdown instance

-
-
-
- -
-
-start() None
-

Start defined instance.

-
- -
- -
-
-class compute.instance.instance.InstanceConfig(schema: InstanceSchema)
-

Compute instance XML config builder.

-
-
-__init__(schema: InstanceSchema)
-

Initialise InstanceConfig.

-
-
Parameters:
-

schema – InstanceSchema object

-
-
-
- -
-
-to_xml() str
-

Return XML config for libvirt.

-
- -
- -
-
-class compute.instance.instance.InstanceInfo(state: str, max_memory: int, memory: int, nproc: int, cputime: int)
-

Store compute instance info.

-

Reference: -https://libvirt.org/html/libvirt-libvirt-domain.html#virDomainInfo

-
-
-cputime: int
-

Alias for field number 4

-
- -
-
-max_memory: int
-

Alias for field number 1

-
- -
-
-memory: int
-

Alias for field number 2

-
- -
-
-nproc: int
-

Alias for field number 3

-
- -
-
-state: str
-

Alias for field number 0

-
- -
- -
- - -
- -
-
- -
-
- - - - - - - \ No newline at end of file diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/pyapi/instance/schemas.html b/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/pyapi/instance/schemas.html deleted file mode 100644 index ef15eb2..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/pyapi/instance/schemas.html +++ /dev/null @@ -1,187 +0,0 @@ - - - - - - - - - schemas — Compute 0.1.0 documentation - - - - - - - - - - - - - - - - - - - - - -
-
-
- - -
- -
-

schemas

-

Compute instance related objects schemas.

-
-
-class compute.instance.schemas.BootOptionsSchema(*, order: tuple)
-

Instance boot settings.

-
- -
-
-class compute.instance.schemas.CPUEmulationMode(value, names=None, *, module=None, qualname=None, type=None, start=1, boundary=None)
-

CPU emulation mode enumerated.

-
- -
-
-class compute.instance.schemas.CPUFeaturesSchema(*, require: list[str], disable: list[str])
-

CPU features model.

-
- -
-
-class compute.instance.schemas.CPUSchema(*, emulation_mode: CPUEmulationMode, model: str | None = None, vendor: str | None = None, topology: compute.instance.schemas.CPUTopologySchema | None = None, features: compute.instance.schemas.CPUFeaturesSchema | None = None)
-

CPU model.

-
- -
-
-class compute.instance.schemas.CPUTopologySchema(*, sockets: int, cores: int, threads: int, dies: int = 1)
-

CPU topology model.

-
- -
-
-class compute.instance.schemas.EntityModel
-

Basic entity model.

-
-
-class Config
-

Do not allow extra fields.

-
- -
- -
-
-class compute.instance.schemas.InstanceSchema(*, name: str, title: str | None = None, description: str | None = None, memory: int, max_memory: int, vcpus: int, max_vcpus: int, cpu: CPUSchema, machine: str, emulator: Path, arch: str, boot: BootOptionsSchema, volumes: list[compute.instance.schemas.VolumeSchema], network_interfaces: list[compute.instance.schemas.NetworkInterfaceSchema], image: str | None = None)
-

Compute instance model.

-
- -
-
-class compute.instance.schemas.NetworkInterfaceSchema(*, source: str, mac: str)
-

Network inerface model.

-
- -
-
-class compute.instance.schemas.VolumeCapacitySchema(*, value: int, unit: DataUnit)
-

Storage volume capacity field model.

-
- -
-
-class compute.instance.schemas.VolumeSchema(*, type: VolumeType, target: str, capacity: VolumeCapacitySchema, source: str | None = None, is_readonly: bool = False, is_system: bool = False)
-

Storage volume model.

-
- -
-
-class compute.instance.schemas.VolumeType(value, names=None, *, module=None, qualname=None, type=None, start=1, boundary=None)
-

Storage volume types enumeration.

-
- -
- - -
- -
-
- -
-
- - - - - - - \ No newline at end of file diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/pyapi/session.html b/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/pyapi/session.html deleted file mode 100644 index 6babb3e..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/pyapi/session.html +++ /dev/null @@ -1,331 +0,0 @@ - - - - - - - - - session — Compute 0.1.0 documentation - - - - - - - - - - - - - - - - - - - - - -
-
-
- - -
- -
-

session

-

Hypervisor session manager.

-
-
-class compute.session.Capabilities(arch: str, virt_type: str, emulator: str, machine: str, max_vcpus: int, cpu_vendor: str, cpu_model: str, cpu_features: dict, usable_cpus: list[dict])
-

Store domain capabilities info.

-
-
-arch: str
-

Alias for field number 0

-
- -
-
-cpu_features: dict
-

Alias for field number 7

-
- -
-
-cpu_model: str
-

Alias for field number 6

-
- -
-
-cpu_vendor: str
-

Alias for field number 5

-
- -
-
-emulator: str
-

Alias for field number 2

-
- -
-
-machine: str
-

Alias for field number 3

-
- -
-
-max_vcpus: int
-

Alias for field number 4

-
- -
-
-usable_cpus: list[dict]
-

Alias for field number 8

-
- -
-
-virt_type: str
-

Alias for field number 1

-
- -
- -
-
-class compute.session.NodeInfo(arch: str, memory: int, cpus: int, mhz: int, nodes: int, sockets: int, cores: int, threads: int)
-

Store compute node info.

-

See https://libvirt.org/html/libvirt-libvirt-host.html#virNodeInfo -NOTE: memory unit in libvirt docs is wrong! Actual unit is MiB.

-
-
-arch: str
-

Alias for field number 0

-
- -
-
-cores: int
-

Alias for field number 6

-
- -
-
-cpus: int
-

Alias for field number 2

-
- -
-
-memory: int
-

Alias for field number 1

-
- -
-
-mhz: int
-

Alias for field number 3

-
- -
-
-nodes: int
-

Alias for field number 4

-
- -
-
-sockets: int
-

Alias for field number 5

-
- -
-
-threads: int
-

Alias for field number 7

-
- -
- -
-
-class compute.session.Session(uri: str | None = None)
-

Hypervisor session context manager.

-
-
Variables:
-
    -
  • IMAGES_POOL – images storage pool name taken from env

  • -
  • VOLUMES_POOL – volumes storage pool name taken from env

  • -
-
-
-
-
-__init__(uri: str | None = None)
-

Initialise session with hypervisor.

-
-
Variables:
-
    -
  • uri (str) – libvirt connection URI.

  • -
  • connection (libvirt.virConnect) – libvirt connection object.

  • -
-
-
Parameters:
-

uri – libvirt connection URI.

-
-
-
- -
-
-close() None
-

Close connection to libvirt daemon.

-
- -
-
-create_instance(**kwargs: Any) Instance
-

Create and return new compute instance.

-
-
Parameters:
-
    -
  • name (str) – Instance name.

  • -
  • title (str) – Instance title for humans.

  • -
  • description (str) – Some information about instance.

  • -
  • memory (int) – Memory in MiB.

  • -
  • max_memory (int) – Maximum memory in MiB.

  • -
  • vcpus (int) – Number of vCPUs.

  • -
  • max_vcpus (int) – Maximum vCPUs.

  • -
  • cpu (dict) – CPU configuration. See CPUSchema for info.

  • -
  • machine (str) – QEMU emulated machine.

  • -
  • emulator (str) – Path to emulator.

  • -
  • arch (str) – CPU architecture to virtualization.

  • -
  • boot (dict) – Boot settings. See BootOptionsSchema.

  • -
  • image (str) – Source disk image name for system disk.

  • -
  • volumes (list[dict]) – List of storage volume configs. For more info -see VolumeSchema.

  • -
  • network_interfaces (list[dict]) – List of virtual network interfaces -configs. See NetworkInterfaceSchema for more info.

  • -
-
-
-
- -
-
-get_capabilities() Capabilities
-

Return capabilities e.g. arch, virt, emulator, etc.

-
- -
-
-get_instance(name: str) Instance
-

Get compute instance by name.

-
- -
-
-get_node_info() NodeInfo
-

Return information about compute node.

-
- -
-
-get_storage_pool(name: str) StoragePool
-

Get storage pool by name.

-
- -
-
-list_instances() list[compute.instance.instance.Instance]
-

List all instances.

-
- -
-
-list_storage_pools() list[compute.storage.pool.StoragePool]
-

List all strage pools.

-
- -
- -
- - -
- -
-
- -
-
- - - - - - - \ No newline at end of file diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/pyapi/storage/index.html b/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/pyapi/storage/index.html deleted file mode 100644 index 0a6ca1b..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/pyapi/storage/index.html +++ /dev/null @@ -1,119 +0,0 @@ - - - - - - - - - storage — Compute 0.1.0 documentation - - - - - - - - - - - - - - - - - - - - - -
-
-
- - -
- -
-

storage

-
-

Contents:

- -
-
- - -
- -
-
- -
-
- - - - - - - \ No newline at end of file diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/pyapi/storage/pool.html b/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/pyapi/storage/pool.html deleted file mode 100644 index 43fa341..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/pyapi/storage/pool.html +++ /dev/null @@ -1,201 +0,0 @@ - - - - - - - - - pool — Compute 0.1.0 documentation - - - - - - - - - - - - - - - - - - - - - -
-
-
- - -
- -
-

pool

-

Manage storage pools.

-
-
-class compute.storage.pool.StoragePool(pool: virStoragePool)
-

Storage pool manipulating class.

-
-
-__init__(pool: virStoragePool)
-

Initislise StoragePool.

-
- -
-
-clone_volume(src: Volume, dst: VolumeConfig) Volume
-

Make storage volume copy.

-
-
Parameters:
-
    -
  • src – Input volume

  • -
  • dst – Output volume config

  • -
-
-
-
- -
-
-create_volume(vol_conf: VolumeConfig) Volume
-

Create storage volume and return Volume instance.

-
- -
-
-dump_xml() str
-

Return storage pool XML description as string.

-
- -
-
-get_usage_info() StoragePoolUsageInfo
-

Return info about storage pool usage.

-
- -
-
-get_volume(name: str) compute.storage.volume.Volume | None
-

Lookup and return Volume instance or None.

-
- -
-
-list_volumes() list[compute.storage.volume.Volume]
-

Return list of volumes in storage pool.

-
- -
-
-refresh() None
-

Refresh storage pool.

-
- -
- -
-
-class compute.storage.pool.StoragePoolUsageInfo(capacity: int, allocation: int, available: int)
-

Storage pool usage info.

-
-
-allocation: int
-

Alias for field number 1

-
- -
-
-available: int
-

Alias for field number 2

-
- -
-
-capacity: int
-

Alias for field number 0

-
- -
- -
- - -
- -
-
- -
-
- - - - - - - \ No newline at end of file diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/pyapi/storage/volume.html b/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/pyapi/storage/volume.html deleted file mode 100644 index 9e35b8f..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/pyapi/storage/volume.html +++ /dev/null @@ -1,210 +0,0 @@ - - - - - - - - - volume — Compute 0.1.0 documentation - - - - - - - - - - - - - - - - - - - - - -
-
-
- - -
- -
-

volume

-

Manage storage volumes.

-
-
-class compute.storage.volume.DiskConfig(disk_type: str, source: str | pathlib.Path, target: str, readonly: bool = False)
-

Disk XML config builder.

-

Generate XML config for attaching or detaching storage volumes -to compute instances.

-
-
-__init__(disk_type: str, source: str | pathlib.Path, target: str, readonly: bool = False) None
-
- -
-
-to_xml() str
-

Return XML config for libvirt.

-
- -
- -
-
-class compute.storage.volume.Volume(pool: virStoragePool, vol: virStorageVol)
-

Storage volume manipulating class.

-
-
-__init__(pool: virStoragePool, vol: virStorageVol)
-

Initialise Volume.

-
-
Parameters:
-
    -
  • pool – libvirt virStoragePool object

  • -
  • vol – libvirt virStorageVol object

  • -
-
-
-
- -
-
-clone(vol_conf: VolumeConfig) None
-

Make a copy of volume to the same storage pool.

-
-
Parameters:
-

VolumeInfo (vol_info) – New storage volume dataclass object

-
-
-
- -
-
-delete() None
-

Delete volume from storage pool.

-
- -
-
-dump_xml() str
-

Return volume XML description as string.

-
- -
-
-resize(capacity: int, unit: DataUnit) None
-

Resize volume.

-
-
Parameters:
-
    -
  • int (capacity) – Volume new capacity.

  • -
  • DataUnit (unit) – Data unit. Internally converts into bytes.

  • -
-
-
-
- -
- -
-
-class compute.storage.volume.VolumeConfig(name: str, path: str, capacity: int)
-

Storage volume XML config builder.

-

Generate XML config for creating a volume in a libvirt -storage pool.

-
-
-__init__(name: str, path: str, capacity: int) None
-
- -
-
-to_xml() str
-

Return XML config for libvirt.

-
- -
- -
- - -
- -
-
- -
-
- - - - - - - \ No newline at end of file diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/pyapi/utils.html b/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/pyapi/utils.html deleted file mode 100644 index b09e008..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/pyapi/utils.html +++ /dev/null @@ -1,144 +0,0 @@ - - - - - - - - - utils — Compute 0.1.0 documentation - - - - - - - - - - - - - - - - - - - - - -
-
-
- - -
- -
-

utils

-
-

utils.units

-

Tools for data units convertion.

-
-
-class compute.utils.units.DataUnit(value, names=None, *, module=None, qualname=None, type=None, start=1, boundary=None)
-

Data units enumerated.

-
- -
-
-exception compute.utils.units.InvalidDataUnitError(msg: str)
-

Data unit is not valid.

-
- -
-
-compute.utils.units.to_bytes(value: int, unit: DataUnit = DataUnit.BYTES) int
-

Convert value to bytes. See DataUnit.

-
- -
-
-

utils.ids

-

Random identificators.

-
-
-compute.utils.ids.random_mac() str
-

Retrun random MAC address.

-
- -
-
- - -
- -
-
- -
-
- - - - - - - \ No newline at end of file diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/search.html b/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/search.html deleted file mode 100644 index 60189cf..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/search.html +++ /dev/null @@ -1,124 +0,0 @@ - - - - - - - - Search — Compute 0.1.0 documentation - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - -
- -

Search

- - - - -

- Searching for multiple words only shows matches that contain - all words. -

- - -
- - - -
- - - -
- -
- - -
- -
-
- -
-
- - - - - - - \ No newline at end of file diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/searchindex.js b/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/searchindex.js deleted file mode 100644 index d20a6ca..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute-doc/usr/share/doc/compute-doc/html/searchindex.js +++ /dev/null @@ -1 +0,0 @@ -Search.setIndex({"docnames": ["index", "pyapi/exceptions", "pyapi/index", "pyapi/instance/guest_agent", "pyapi/instance/index", "pyapi/instance/instance", "pyapi/instance/schemas", "pyapi/session", "pyapi/storage/index", "pyapi/storage/pool", "pyapi/storage/volume", "pyapi/utils"], "filenames": ["index.rst", "pyapi/exceptions.rst", "pyapi/index.rst", "pyapi/instance/guest_agent.rst", "pyapi/instance/index.rst", "pyapi/instance/instance.rst", "pyapi/instance/schemas.rst", "pyapi/session.rst", "pyapi/storage/index.rst", "pyapi/storage/pool.rst", "pyapi/storage/volume.rst", "pyapi/utils.rst"], "titles": ["Compute", "exceptions", "Python API", "guest_agent", "instance", "instance", "schemas", "session", "storage", "pool", "volume", "utils"], "terms": {"instanc": [0, 2, 3, 6, 7, 9, 10], "manag": [0, 2, 5, 7, 9, 10], "librari": [0, 2], "python": 0, "api": 0, "index": 0, "modul": [0, 6, 11], "search": 0, "page": 0, "comput": [1, 2, 3, 5, 6, 7, 9, 10, 11], "computeerror": [1, 2], "basic": [1, 6], "class": [1, 2, 3, 5, 6, 7, 9, 10, 11], "configloadererror": [1, 2], "someth": 1, "went": 1, "wrong": [1, 7], "when": [1, 5], "load": 1, "configur": [1, 2, 5, 7], "guestagentcommandnotsupportederror": [1, 2, 3], "guest": [1, 3, 5], "agent": [1, 3, 5], "command": [1, 3], "i": [1, 2, 3, 5, 7, 11], "support": [1, 3], "blacklist": 1, "guestagenterror": [1, 2], "wring": 1, "qemu": [1, 3, 7], "call": [1, 2, 5], "guestagenttimeoutexceedederror": [1, 2], "msg": [1, 11], "int": [1, 3, 5, 6, 7, 9, 10, 11], "timeout": [1, 3], "exceed": 1, "guestagentunavailableerror": [1, 2], "connect": [1, 5, 7], "unavail": [1, 5], "instanceerror": [1, 2], "while": 1, "interact": [1, 3], "domain": [1, 3, 5, 7], "instancenotfounderror": [1, 2], "str": [1, 3, 5, 6, 7, 9, 10, 11], "virtual": [1, 7], "machin": [1, 2, 5, 6, 7], "contain": 1, "found": 1, "node": [1, 2, 7], "sessionerror": [1, 2], "libvirtd": 1, "storagepoolerror": [1, 2], "oper": [1, 2], "storag": [1, 2, 5, 6, 7, 9, 10], "pool": [1, 2, 7, 8, 10], "storagepoolnotfounderror": [1, 2], "volumenotfounderror": [1, 2], "volum": [1, 2, 5, 6, 7, 8, 9], "The": 2, "allow": [2, 6], "you": [2, 5], "perform": [2, 5], "action": [2, 5], "programmat": 2, "below": 2, "an": 2, "exampl": [2, 3], "chang": [2, 5], "paramet": [2, 3, 5, 7, 9, 10], "launch": 2, "myinstanc": 2, "import": 2, "log": 2, "from": [2, 5, 7, 10], "session": 2, "basicconfig": 2, "level": 2, "debug": 2, "get_inst": [2, 7], "set_vcpu": [2, 5], "4": [2, 5, 7], "start": [2, 5, 6, 11], "set_autostart": [2, 5], "enabl": [2, 5], "true": [2, 3, 5], "context": [2, 7], "provid": 2, "abstract": 2, "over": 2, "libvirt": [2, 3, 5, 7, 10], "virconnect": [2, 5, 7], "return": [2, 3, 5, 7, 9, 10], "object": [2, 3, 5, 6, 7, 10], "other": 2, "present": 2, "ar": 2, "repres": 2, "These": 2, "directli": 2, "method": [2, 5], "hypervisor": [2, 5, 7], "file": 2, "variou": 2, "describ": 2, "special": 2, "dataclass": [2, 10], "store": [2, 5, 7], "its": 2, "properti": 2, "can": 2, "xml": [2, 5, 9, 10], "config": [2, 5, 6, 7, 9, 10], "us": [2, 3, 5], "to_xml": [2, 5, 10], "For": [2, 3, 7], "volumeconfig": [2, 9, 10], "pydant": 2, "model": [2, 6], "valid": [2, 11], "input": [2, 9], "data": [2, 3, 5, 10, 11], "volumeschema": [2, 6, 7], "capabl": [2, 7], "arch": [2, 6, 7], "cpu_featur": [2, 7], "cpu_model": [2, 7], "cpu_vendor": [2, 7], "emul": [2, 5, 6, 7], "max_vcpu": [2, 6, 7], "usable_cpu": [2, 7], "virt_typ": [2, 7], "nodeinfo": [2, 7], "core": [2, 6, 7], "cpu": [2, 6, 7], "memori": [2, 5, 6, 7], "mhz": [2, 7], "socket": [2, 6, 7], "thread": [2, 6, 7], "__init__": [2, 3, 5, 7, 9, 10], "close": [2, 7], "create_inst": [2, 7], "get_cap": [2, 7], "get_node_info": [2, 7], "get_storage_pool": [2, 7], "list_inst": [2, 7], "list_storage_pool": [2, 7], "attach_devic": [2, 5], "delet": [2, 5, 10], "delete_ssh_kei": [2, 5], "detach_devic": [2, 5], "detach_disk": [2, 5], "dump_xml": [2, 5, 9, 10], "get_disk": [2, 5], "get_info": [2, 5], "get_max_memori": [2, 5], "get_max_vcpu": [2, 5], "get_ssh_kei": [2, 5], "get_statu": [2, 5], "is_autostart": [2, 5], "is_run": [2, 5], "paus": [2, 5], "power_reset": [2, 5], "reboot": [2, 5], "reset": [2, 5], "resize_disk": [2, 5], "resum": [2, 5], "set_memori": [2, 5], "set_ssh_kei": [2, 5], "set_user_password": [2, 5], "shutdown": [2, 5], "instanceconfig": [2, 5], "instanceinfo": [2, 5], "cputim": [2, 5], "max_memori": [2, 5, 6, 7], "nproc": [2, 5], "state": [2, 5], "guest_ag": [2, 4, 5], "guestag": [2, 3, 5], "execut": [2, 3, 5], "get_supported_command": [2, 3], "guest_exec": [2, 3], "guest_exec_statu": [2, 3], "is_avail": [2, 3], "raise_for_command": [2, 3], "guestexecoutput": [2, 3], "exitcod": [2, 3], "exit": [2, 3], "stderr": [2, 3], "stdout": [2, 3], "schema": [2, 4, 5], "bootoptionsschema": [2, 6, 7], "cpuemulationmod": [2, 6], "cpufeaturesschema": [2, 6], "cpuschema": [2, 6, 7], "cputopologyschema": [2, 6], "entitymodel": [2, 6], "instanceschema": [2, 5, 6], "networkinterfaceschema": [2, 6, 7], "volumecapacityschema": [2, 6], "volumetyp": [2, 6], "storagepool": [2, 7, 9], "clone_volum": [2, 9], "create_volum": [2, 9], "get_usage_info": [2, 9], "get_volum": [2, 9], "list_volum": [2, 9], "refresh": [2, 9], "storagepoolusageinfo": [2, 9], "alloc": [2, 9], "avail": [2, 3, 9], "capac": [2, 5, 6, 9, 10], "diskconfig": [2, 5, 10], "clone": [2, 10], "resiz": [2, 5, 10], "util": 2, "unit": [2, 5, 6, 7, 10], "dataunit": [2, 5, 6, 10, 11], "invaliddatauniterror": [2, 11], "to_byt": [2, 11], "id": 2, "random_mac": [2, 11], "except": [2, 3, 11], "virdomain": [3, 5], "60": 3, "initialis": [3, 5, 7, 10], "dict": [3, 7], "see": [3, 5, 7, 11], "http": [3, 5, 7], "project": 3, "gitlab": 3, "io": 3, "interop": 3, "ga": 3, "ref": 3, "html": [3, 5, 7], "output": [3, 9], "type": [3, 6, 11], "set": [3, 5, 6, 7], "path": [3, 6, 7, 10], "arg": 3, "list": [3, 5, 6, 7, 9], "none": [3, 5, 6, 7, 9, 10, 11], "env": [3, 7], "stdin": 3, "capture_output": 3, "bool": [3, 5, 6, 10], "fals": [3, 5, 6, 10], "decode_output": 3, "poll": 3, "exec": 3, "ot": 3, "argument": [3, 5], "pass": 3, "environ": 3, "variabl": [3, 5, 7], "lang": 3, "c": 3, "term": 3, "xterm": 3, "captur": 3, "base64_decod": 3, "decod": 3, "affect": [3, 5], "onli": 3, "self": 3, "poll_interv": 3, "constant": 3, "pid": 3, "float": 3, "0": [3, 5, 7, 9], "3": [3, 5, 7], "statu": 3, "If": [3, 5], "time": 3, "between": 3, "attempt": 3, "obtain": 3, "ping": 3, "unreach": 3, "rais": 3, "requir": [3, 6], "alia": [3, 5, 7, 9], "field": [3, 5, 6, 7, 9], "number": [3, 5, 7, 9], "1": [3, 5, 6, 7, 9, 11], "2": [3, 5, 7, 9], "name": [5, 6, 7, 9, 10, 11], "devic": 5, "entityconfig": 5, "live": 5, "attach": [5, 10], "descript": [5, 6, 7, 9, 10], "e": [5, 7], "g": [5, 7], "run": 5, "undefin": 5, "user": 5, "ssh_kei": 5, "remov": 5, "ssh": 5, "kei": 5, "specif": 5, "usernam": 5, "public": 5, "dettach": 5, "detach": [5, 10], "disk": [5, 7, 10], "target": [5, 6, 10], "There": 5, "attach_disk": 5, "vda": 5, "sda": 5, "etc": [5, 7], "thi": 5, "mai": 5, "match": 5, "insid": 5, "o": 5, "inact": 5, "info": [5, 7, 9], "maximum": [5, 7], "valu": [5, 6, 11], "kib": 5, "vcpu": [5, 6, 7], "shutoff": 5, "refer": 5, "org": [5, 7], "virdomainst": 5, "autostart": 5, "els": 5, "By": 5, "analogi": 5, "real": 5, "hardwar": 5, "normal": 5, "server": 5, "turn": 5, "off": 5, "power": 5, "suppli": 5, "again": 5, "applic": 5, "case": 5, "where": 5, "ha": 5, "been": 5, "need": 5, "restart": 5, "appli": 5, "new": [5, 7, 10], "send": 5, "acpi": 5, "signal": 5, "ignor": 5, "copypast": 5, "doc": [5, 7], "immedi": 5, "without": 5, "ani": [5, 7], "button": 5, "all": [5, 7], "rst": 5, "line": 5, "reiniti": 5, "intern": [5, 10], "note": [5, 7], "risk": 5, "loss": 5, "caus": 5, "block": 5, "flag": 5, "unset": 5, "current": 5, "boot": [5, 6, 7], "mebibyt": 5, "add": 5, "password": 5, "encrypt": 5, "alreadi": 5, "right": 5, "depend": 5, "nvcpu": 5, "nb": 5, "befor": 5, "finish": 5, "fail": 5, "process": 5, "soft": 5, "choosen": 5, "usual": 5, "hang": 5, "hard": 5, "simular": 5, "unplug": 5, "sigterm": 5, "destroi": 5, "gracefulli": 5, "unsaf": 5, "forc": 5, "sigkil": 5, "high": 5, "corrupt": 5, "defin": 5, "builder": [5, 10], "virdomaininfo": 5, "relat": 6, "order": 6, "tupl": 6, "qualnam": [6, 11], "boundari": [6, 11], "mode": 6, "enumer": [6, 11], "disabl": 6, "featur": 6, "emulation_mod": 6, "vendor": 6, "topologi": 6, "di": 6, "entiti": 6, "do": 6, "extra": 6, "titl": [6, 7], "network_interfac": [6, 7], "imag": [6, 7], "sourc": [6, 7, 10], "mac": [6, 11], "network": [6, 7], "inerfac": 6, "is_readonli": 6, "is_system": 6, "7": 7, "6": 7, "5": 7, "8": 7, "host": 7, "virnodeinfo": 7, "actual": 7, "mib": 7, "uri": 7, "images_pool": 7, "taken": 7, "volumes_pool": 7, "daemon": 7, "kwarg": 7, "creat": [7, 9, 10], "human": 7, "some": 7, "inform": 7, "about": [7, 9], "architectur": 7, "system": 7, "more": 7, "interfac": 7, "virt": 7, "get": 7, "strage": 7, "virstoragepool": [9, 10], "manipul": [9, 10], "initislis": 9, "src": 9, "dst": 9, "make": [9, 10], "copi": [9, 10], "vol_conf": [9, 10], "string": [9, 10], "usag": 9, "lookup": 9, "disk_typ": 10, "pathlib": 10, "readonli": 10, "gener": 10, "vol": 10, "virstoragevol": 10, "same": 10, "volumeinfo": 10, "vol_info": 10, "convert": [10, 11], "byte": [10, 11], "tool": 11, "random": 11, "identif": 11, "retrun": 11, "address": 11}, "objects": {"compute": [[1, 0, 0, "-", "exceptions"], [7, 0, 0, "-", "session"]], "compute.exceptions": [[1, 1, 1, "", "ComputeError"], [1, 1, 1, "", "ConfigLoaderError"], [1, 1, 1, "", "GuestAgentCommandNotSupportedError"], [1, 1, 1, "", "GuestAgentError"], [1, 1, 1, "", "GuestAgentTimeoutExceededError"], [1, 1, 1, "", "GuestAgentUnavailableError"], [1, 1, 1, "", "InstanceError"], [1, 1, 1, "", "InstanceNotFoundError"], [1, 1, 1, "", "SessionError"], [1, 1, 1, "", "StoragePoolError"], [1, 1, 1, "", "StoragePoolNotFoundError"], [1, 1, 1, "", "VolumeNotFoundError"]], "compute.instance": [[3, 0, 0, "-", "guest_agent"], [5, 0, 0, "-", "instance"], [6, 0, 0, "-", "schemas"]], "compute.instance.guest_agent": [[3, 2, 1, "", "GuestAgent"], [3, 2, 1, "", "GuestExecOutput"]], "compute.instance.guest_agent.GuestAgent": [[3, 3, 1, "", "__init__"], [3, 3, 1, "", "execute"], [3, 3, 1, "", "get_supported_commands"], [3, 3, 1, "", "guest_exec"], [3, 3, 1, "", "guest_exec_status"], [3, 3, 1, "", "is_available"], [3, 3, 1, "", "raise_for_commands"]], "compute.instance.guest_agent.GuestExecOutput": [[3, 4, 1, "", "exitcode"], [3, 4, 1, "", "exited"], [3, 4, 1, "", "stderr"], [3, 4, 1, "", "stdout"]], "compute.instance.instance": [[5, 2, 1, "", "Instance"], [5, 2, 1, "", "InstanceConfig"], [5, 2, 1, "", "InstanceInfo"]], "compute.instance.instance.Instance": [[5, 3, 1, "", "__init__"], [5, 3, 1, "", "attach_device"], [5, 3, 1, "", "delete"], [5, 3, 1, "", "delete_ssh_keys"], [5, 3, 1, "", "detach_device"], [5, 3, 1, "", "detach_disk"], [5, 3, 1, "", "dump_xml"], [5, 3, 1, "", "get_disks"], [5, 3, 1, "", "get_info"], [5, 3, 1, "", "get_max_memory"], [5, 3, 1, "", "get_max_vcpus"], [5, 3, 1, "", "get_ssh_keys"], [5, 3, 1, "", "get_status"], [5, 3, 1, "", "is_autostart"], [5, 3, 1, "", "is_running"], [5, 3, 1, "", "pause"], [5, 3, 1, "", "power_reset"], [5, 3, 1, "", "reboot"], [5, 3, 1, "", "reset"], [5, 3, 1, "", "resize_disk"], [5, 3, 1, "", "resume"], [5, 3, 1, "", "set_autostart"], [5, 3, 1, "", "set_memory"], [5, 3, 1, "", "set_ssh_keys"], [5, 3, 1, "", "set_user_password"], [5, 3, 1, "", "set_vcpus"], [5, 3, 1, "", "shutdown"], [5, 3, 1, "", "start"]], "compute.instance.instance.InstanceConfig": [[5, 3, 1, "", "__init__"], [5, 3, 1, "", "to_xml"]], "compute.instance.instance.InstanceInfo": [[5, 4, 1, "", "cputime"], [5, 4, 1, "", "max_memory"], [5, 4, 1, "", "memory"], [5, 4, 1, "", "nproc"], [5, 4, 1, "", "state"]], "compute.instance.schemas": [[6, 2, 1, "", "BootOptionsSchema"], [6, 2, 1, "", "CPUEmulationMode"], [6, 2, 1, "", "CPUFeaturesSchema"], [6, 2, 1, "", "CPUSchema"], [6, 2, 1, "", "CPUTopologySchema"], [6, 2, 1, "", "EntityModel"], [6, 2, 1, "", "InstanceSchema"], [6, 2, 1, "", "NetworkInterfaceSchema"], [6, 2, 1, "", "VolumeCapacitySchema"], [6, 2, 1, "", "VolumeSchema"], [6, 2, 1, "", "VolumeType"]], "compute.instance.schemas.EntityModel": [[6, 2, 1, "", "Config"]], "compute.session": [[7, 2, 1, "", "Capabilities"], [7, 2, 1, "", "NodeInfo"], [7, 2, 1, "", "Session"]], "compute.session.Capabilities": [[7, 4, 1, "", "arch"], [7, 4, 1, "", "cpu_features"], [7, 4, 1, "", "cpu_model"], [7, 4, 1, "", "cpu_vendor"], [7, 4, 1, "", "emulator"], [7, 4, 1, "", "machine"], [7, 4, 1, "", "max_vcpus"], [7, 4, 1, "", "usable_cpus"], [7, 4, 1, "", "virt_type"]], "compute.session.NodeInfo": [[7, 4, 1, "", "arch"], [7, 4, 1, "", "cores"], [7, 4, 1, "", "cpus"], [7, 4, 1, "", "memory"], [7, 4, 1, "", "mhz"], [7, 4, 1, "", "nodes"], [7, 4, 1, "", "sockets"], [7, 4, 1, "", "threads"]], "compute.session.Session": [[7, 3, 1, "", "__init__"], [7, 3, 1, "", "close"], [7, 3, 1, "", "create_instance"], [7, 3, 1, "", "get_capabilities"], [7, 3, 1, "", "get_instance"], [7, 3, 1, "", "get_node_info"], [7, 3, 1, "", "get_storage_pool"], [7, 3, 1, "", "list_instances"], [7, 3, 1, "", "list_storage_pools"]], "compute.storage": [[9, 0, 0, "-", "pool"], [10, 0, 0, "-", "volume"]], "compute.storage.pool": [[9, 2, 1, "", "StoragePool"], [9, 2, 1, "", "StoragePoolUsageInfo"]], "compute.storage.pool.StoragePool": [[9, 3, 1, "", "__init__"], [9, 3, 1, "", "clone_volume"], [9, 3, 1, "", "create_volume"], [9, 3, 1, "", "dump_xml"], [9, 3, 1, "", "get_usage_info"], [9, 3, 1, "", "get_volume"], [9, 3, 1, "", "list_volumes"], [9, 3, 1, "", "refresh"]], "compute.storage.pool.StoragePoolUsageInfo": [[9, 4, 1, "", "allocation"], [9, 4, 1, "", "available"], [9, 4, 1, "", "capacity"]], "compute.storage.volume": [[10, 2, 1, "", "DiskConfig"], [10, 2, 1, "", "Volume"], [10, 2, 1, "", "VolumeConfig"]], "compute.storage.volume.DiskConfig": [[10, 3, 1, "", "__init__"], [10, 3, 1, "", "to_xml"]], "compute.storage.volume.Volume": [[10, 3, 1, "", "__init__"], [10, 3, 1, "", "clone"], [10, 3, 1, "", "delete"], [10, 3, 1, "", "dump_xml"], [10, 3, 1, "", "resize"]], "compute.storage.volume.VolumeConfig": [[10, 3, 1, "", "__init__"], [10, 3, 1, "", "to_xml"]], "compute.utils": [[11, 0, 0, "-", "ids"], [11, 0, 0, "-", "units"]], "compute.utils.ids": [[11, 5, 1, "", "random_mac"]], "compute.utils.units": [[11, 2, 1, "", "DataUnit"], [11, 1, 1, "", "InvalidDataUnitError"], [11, 5, 1, "", "to_bytes"]]}, "objtypes": {"0": "py:module", "1": "py:exception", "2": "py:class", "3": "py:method", "4": "py:attribute", "5": "py:function"}, "objnames": {"0": ["py", "module", "Python module"], "1": ["py", "exception", "Python exception"], "2": ["py", "class", "Python class"], "3": ["py", "method", "Python method"], "4": ["py", "attribute", "Python attribute"], "5": ["py", "function", "Python function"]}, "titleterms": {"comput": 0, "indic": 0, "tabl": 0, "except": 1, "python": 2, "api": 2, "entiti": 2, "represent": 2, "modul": 2, "document": 2, "guest_ag": 3, "instanc": [4, 5], "content": [4, 8], "schema": 6, "session": 7, "storag": 8, "pool": 9, "volum": 10, "util": 11, "unit": 11, "id": 11}, "envversion": {"sphinx.domains.c": 2, "sphinx.domains.changeset": 1, "sphinx.domains.citation": 1, "sphinx.domains.cpp": 8, "sphinx.domains.index": 1, "sphinx.domains.javascript": 2, "sphinx.domains.math": 2, "sphinx.domains.python": 3, "sphinx.domains.rst": 2, "sphinx.domains.std": 2, "sphinx": 57}, "alltitles": {"Compute": [[0, "compute"]], "Indices and tables": [[0, "indices-and-tables"]], "exceptions": [[1, "module-compute.exceptions"]], "Python API": [[2, "python-api"]], "Entity representation": [[2, "entity-representation"]], "Modules documentation": [[2, "modules-documentation"]], "guest_agent": [[3, "module-compute.instance.guest_agent"]], "instance": [[4, "instance"], [5, "module-compute.instance.instance"]], "Contents:": [[4, null], [8, null]], "schemas": [[6, "module-compute.instance.schemas"]], "session": [[7, "module-compute.session"]], "storage": [[8, "storage"]], "pool": [[9, "module-compute.storage.pool"]], "volume": [[10, "module-compute.storage.volume"]], "utils": [[11, "utils"]], "utils.units": [[11, "module-compute.utils.units"]], "utils.ids": [[11, "module-compute.utils.ids"]]}, "indexentries": {"computeerror": [[1, "compute.exceptions.ComputeError"]], "configloadererror": [[1, "compute.exceptions.ConfigLoaderError"]], "guestagentcommandnotsupportederror": [[1, "compute.exceptions.GuestAgentCommandNotSupportedError"]], "guestagenterror": [[1, "compute.exceptions.GuestAgentError"]], "guestagenttimeoutexceedederror": [[1, "compute.exceptions.GuestAgentTimeoutExceededError"]], "guestagentunavailableerror": [[1, "compute.exceptions.GuestAgentUnavailableError"]], "instanceerror": [[1, "compute.exceptions.InstanceError"]], "instancenotfounderror": [[1, "compute.exceptions.InstanceNotFoundError"]], "sessionerror": [[1, "compute.exceptions.SessionError"]], "storagepoolerror": [[1, "compute.exceptions.StoragePoolError"]], "storagepoolnotfounderror": [[1, "compute.exceptions.StoragePoolNotFoundError"]], "volumenotfounderror": [[1, "compute.exceptions.VolumeNotFoundError"]], "compute.exceptions": [[1, "module-compute.exceptions"]], "module": [[1, "module-compute.exceptions"], [3, "module-compute.instance.guest_agent"], [5, "module-compute.instance.instance"], [6, "module-compute.instance.schemas"], [7, "module-compute.session"], [9, "module-compute.storage.pool"], [10, "module-compute.storage.volume"], [11, "module-compute.utils.ids"], [11, "module-compute.utils.units"]], "guestagent (class in compute.instance.guest_agent)": [[3, "compute.instance.guest_agent.GuestAgent"]], "guestexecoutput (class in compute.instance.guest_agent)": [[3, "compute.instance.guest_agent.GuestExecOutput"]], "__init__() (compute.instance.guest_agent.guestagent method)": [[3, "compute.instance.guest_agent.GuestAgent.__init__"]], "compute.instance.guest_agent": [[3, "module-compute.instance.guest_agent"]], "execute() (compute.instance.guest_agent.guestagent method)": [[3, "compute.instance.guest_agent.GuestAgent.execute"]], "exitcode (compute.instance.guest_agent.guestexecoutput attribute)": [[3, "compute.instance.guest_agent.GuestExecOutput.exitcode"]], "exited (compute.instance.guest_agent.guestexecoutput attribute)": [[3, "compute.instance.guest_agent.GuestExecOutput.exited"]], "get_supported_commands() (compute.instance.guest_agent.guestagent method)": [[3, "compute.instance.guest_agent.GuestAgent.get_supported_commands"]], "guest_exec() (compute.instance.guest_agent.guestagent method)": [[3, "compute.instance.guest_agent.GuestAgent.guest_exec"]], "guest_exec_status() (compute.instance.guest_agent.guestagent method)": [[3, "compute.instance.guest_agent.GuestAgent.guest_exec_status"]], "is_available() (compute.instance.guest_agent.guestagent method)": [[3, "compute.instance.guest_agent.GuestAgent.is_available"]], "raise_for_commands() (compute.instance.guest_agent.guestagent method)": [[3, "compute.instance.guest_agent.GuestAgent.raise_for_commands"]], "stderr (compute.instance.guest_agent.guestexecoutput attribute)": [[3, "compute.instance.guest_agent.GuestExecOutput.stderr"]], "stdout (compute.instance.guest_agent.guestexecoutput attribute)": [[3, "compute.instance.guest_agent.GuestExecOutput.stdout"]], "instance (class in compute.instance.instance)": [[5, "compute.instance.instance.Instance"]], "instanceconfig (class in compute.instance.instance)": [[5, "compute.instance.instance.InstanceConfig"]], "instanceinfo (class in compute.instance.instance)": [[5, "compute.instance.instance.InstanceInfo"]], "__init__() (compute.instance.instance.instance method)": [[5, "compute.instance.instance.Instance.__init__"]], "__init__() (compute.instance.instance.instanceconfig method)": [[5, "compute.instance.instance.InstanceConfig.__init__"]], "attach_device() (compute.instance.instance.instance method)": [[5, "compute.instance.instance.Instance.attach_device"]], "compute.instance.instance": [[5, "module-compute.instance.instance"]], "cputime (compute.instance.instance.instanceinfo attribute)": [[5, "compute.instance.instance.InstanceInfo.cputime"]], "delete() (compute.instance.instance.instance method)": [[5, "compute.instance.instance.Instance.delete"]], "delete_ssh_keys() (compute.instance.instance.instance method)": [[5, "compute.instance.instance.Instance.delete_ssh_keys"]], "detach_device() (compute.instance.instance.instance method)": [[5, "compute.instance.instance.Instance.detach_device"]], "detach_disk() (compute.instance.instance.instance method)": [[5, "compute.instance.instance.Instance.detach_disk"]], "dump_xml() (compute.instance.instance.instance method)": [[5, "compute.instance.instance.Instance.dump_xml"]], "get_disks() (compute.instance.instance.instance method)": [[5, "compute.instance.instance.Instance.get_disks"]], "get_info() (compute.instance.instance.instance method)": [[5, "compute.instance.instance.Instance.get_info"]], "get_max_memory() (compute.instance.instance.instance method)": [[5, "compute.instance.instance.Instance.get_max_memory"]], "get_max_vcpus() (compute.instance.instance.instance method)": [[5, "compute.instance.instance.Instance.get_max_vcpus"]], "get_ssh_keys() (compute.instance.instance.instance method)": [[5, "compute.instance.instance.Instance.get_ssh_keys"]], "get_status() (compute.instance.instance.instance method)": [[5, "compute.instance.instance.Instance.get_status"]], "is_autostart() (compute.instance.instance.instance method)": [[5, "compute.instance.instance.Instance.is_autostart"]], "is_running() (compute.instance.instance.instance method)": [[5, "compute.instance.instance.Instance.is_running"]], "max_memory (compute.instance.instance.instanceinfo attribute)": [[5, "compute.instance.instance.InstanceInfo.max_memory"]], "memory (compute.instance.instance.instanceinfo attribute)": [[5, "compute.instance.instance.InstanceInfo.memory"]], "nproc (compute.instance.instance.instanceinfo attribute)": [[5, "compute.instance.instance.InstanceInfo.nproc"]], "pause() (compute.instance.instance.instance method)": [[5, "compute.instance.instance.Instance.pause"]], "power_reset() (compute.instance.instance.instance method)": [[5, "compute.instance.instance.Instance.power_reset"]], "reboot() (compute.instance.instance.instance method)": [[5, "compute.instance.instance.Instance.reboot"]], "reset() (compute.instance.instance.instance method)": [[5, "compute.instance.instance.Instance.reset"]], "resize_disk() (compute.instance.instance.instance method)": [[5, "compute.instance.instance.Instance.resize_disk"]], "resume() (compute.instance.instance.instance method)": [[5, "compute.instance.instance.Instance.resume"]], "set_autostart() (compute.instance.instance.instance method)": [[5, "compute.instance.instance.Instance.set_autostart"]], "set_memory() (compute.instance.instance.instance method)": [[5, "compute.instance.instance.Instance.set_memory"]], "set_ssh_keys() (compute.instance.instance.instance method)": [[5, "compute.instance.instance.Instance.set_ssh_keys"]], "set_user_password() (compute.instance.instance.instance method)": [[5, "compute.instance.instance.Instance.set_user_password"]], "set_vcpus() (compute.instance.instance.instance method)": [[5, "compute.instance.instance.Instance.set_vcpus"]], "shutdown() (compute.instance.instance.instance method)": [[5, "compute.instance.instance.Instance.shutdown"]], "start() (compute.instance.instance.instance method)": [[5, "compute.instance.instance.Instance.start"]], "state (compute.instance.instance.instanceinfo attribute)": [[5, "compute.instance.instance.InstanceInfo.state"]], "to_xml() (compute.instance.instance.instanceconfig method)": [[5, "compute.instance.instance.InstanceConfig.to_xml"]], "bootoptionsschema (class in compute.instance.schemas)": [[6, "compute.instance.schemas.BootOptionsSchema"]], "cpuemulationmode (class in compute.instance.schemas)": [[6, "compute.instance.schemas.CPUEmulationMode"]], "cpufeaturesschema (class in compute.instance.schemas)": [[6, "compute.instance.schemas.CPUFeaturesSchema"]], "cpuschema (class in compute.instance.schemas)": [[6, "compute.instance.schemas.CPUSchema"]], "cputopologyschema (class in compute.instance.schemas)": [[6, "compute.instance.schemas.CPUTopologySchema"]], "entitymodel (class in compute.instance.schemas)": [[6, "compute.instance.schemas.EntityModel"]], "entitymodel.config (class in compute.instance.schemas)": [[6, "compute.instance.schemas.EntityModel.Config"]], "instanceschema (class in compute.instance.schemas)": [[6, "compute.instance.schemas.InstanceSchema"]], "networkinterfaceschema (class in compute.instance.schemas)": [[6, "compute.instance.schemas.NetworkInterfaceSchema"]], "volumecapacityschema (class in compute.instance.schemas)": [[6, "compute.instance.schemas.VolumeCapacitySchema"]], "volumeschema (class in compute.instance.schemas)": [[6, "compute.instance.schemas.VolumeSchema"]], "volumetype (class in compute.instance.schemas)": [[6, "compute.instance.schemas.VolumeType"]], "compute.instance.schemas": [[6, "module-compute.instance.schemas"]], "capabilities (class in compute.session)": [[7, "compute.session.Capabilities"]], "nodeinfo (class in compute.session)": [[7, "compute.session.NodeInfo"]], "session (class in compute.session)": [[7, "compute.session.Session"]], "__init__() (compute.session.session method)": [[7, "compute.session.Session.__init__"]], "arch (compute.session.capabilities attribute)": [[7, "compute.session.Capabilities.arch"]], "arch (compute.session.nodeinfo attribute)": [[7, "compute.session.NodeInfo.arch"]], "close() (compute.session.session method)": [[7, "compute.session.Session.close"]], "compute.session": [[7, "module-compute.session"]], "cores (compute.session.nodeinfo attribute)": [[7, "compute.session.NodeInfo.cores"]], "cpu_features (compute.session.capabilities attribute)": [[7, "compute.session.Capabilities.cpu_features"]], "cpu_model (compute.session.capabilities attribute)": [[7, "compute.session.Capabilities.cpu_model"]], "cpu_vendor (compute.session.capabilities attribute)": [[7, "compute.session.Capabilities.cpu_vendor"]], "cpus (compute.session.nodeinfo attribute)": [[7, "compute.session.NodeInfo.cpus"]], "create_instance() (compute.session.session method)": [[7, "compute.session.Session.create_instance"]], "emulator (compute.session.capabilities attribute)": [[7, "compute.session.Capabilities.emulator"]], "get_capabilities() (compute.session.session method)": [[7, "compute.session.Session.get_capabilities"]], "get_instance() (compute.session.session method)": [[7, "compute.session.Session.get_instance"]], "get_node_info() (compute.session.session method)": [[7, "compute.session.Session.get_node_info"]], "get_storage_pool() (compute.session.session method)": [[7, "compute.session.Session.get_storage_pool"]], "list_instances() (compute.session.session method)": [[7, "compute.session.Session.list_instances"]], "list_storage_pools() (compute.session.session method)": [[7, "compute.session.Session.list_storage_pools"]], "machine (compute.session.capabilities attribute)": [[7, "compute.session.Capabilities.machine"]], "max_vcpus (compute.session.capabilities attribute)": [[7, "compute.session.Capabilities.max_vcpus"]], "memory (compute.session.nodeinfo attribute)": [[7, "compute.session.NodeInfo.memory"]], "mhz (compute.session.nodeinfo attribute)": [[7, "compute.session.NodeInfo.mhz"]], "nodes (compute.session.nodeinfo attribute)": [[7, "compute.session.NodeInfo.nodes"]], "sockets (compute.session.nodeinfo attribute)": [[7, "compute.session.NodeInfo.sockets"]], "threads (compute.session.nodeinfo attribute)": [[7, "compute.session.NodeInfo.threads"]], "usable_cpus (compute.session.capabilities attribute)": [[7, "compute.session.Capabilities.usable_cpus"]], "virt_type (compute.session.capabilities attribute)": [[7, "compute.session.Capabilities.virt_type"]], "storagepool (class in compute.storage.pool)": [[9, "compute.storage.pool.StoragePool"]], "storagepoolusageinfo (class in compute.storage.pool)": [[9, "compute.storage.pool.StoragePoolUsageInfo"]], "__init__() (compute.storage.pool.storagepool method)": [[9, "compute.storage.pool.StoragePool.__init__"]], "allocation (compute.storage.pool.storagepoolusageinfo attribute)": [[9, "compute.storage.pool.StoragePoolUsageInfo.allocation"]], "available (compute.storage.pool.storagepoolusageinfo attribute)": [[9, "compute.storage.pool.StoragePoolUsageInfo.available"]], "capacity (compute.storage.pool.storagepoolusageinfo attribute)": [[9, "compute.storage.pool.StoragePoolUsageInfo.capacity"]], "clone_volume() (compute.storage.pool.storagepool method)": [[9, "compute.storage.pool.StoragePool.clone_volume"]], "compute.storage.pool": [[9, "module-compute.storage.pool"]], "create_volume() (compute.storage.pool.storagepool method)": [[9, "compute.storage.pool.StoragePool.create_volume"]], "dump_xml() (compute.storage.pool.storagepool method)": [[9, "compute.storage.pool.StoragePool.dump_xml"]], "get_usage_info() (compute.storage.pool.storagepool method)": [[9, "compute.storage.pool.StoragePool.get_usage_info"]], "get_volume() (compute.storage.pool.storagepool method)": [[9, "compute.storage.pool.StoragePool.get_volume"]], "list_volumes() (compute.storage.pool.storagepool method)": [[9, "compute.storage.pool.StoragePool.list_volumes"]], "refresh() (compute.storage.pool.storagepool method)": [[9, "compute.storage.pool.StoragePool.refresh"]], "diskconfig (class in compute.storage.volume)": [[10, "compute.storage.volume.DiskConfig"]], "volume (class in compute.storage.volume)": [[10, "compute.storage.volume.Volume"]], "volumeconfig (class in compute.storage.volume)": [[10, "compute.storage.volume.VolumeConfig"]], "__init__() (compute.storage.volume.diskconfig method)": [[10, "compute.storage.volume.DiskConfig.__init__"]], "__init__() (compute.storage.volume.volume method)": [[10, "compute.storage.volume.Volume.__init__"]], "__init__() (compute.storage.volume.volumeconfig method)": [[10, "compute.storage.volume.VolumeConfig.__init__"]], "clone() (compute.storage.volume.volume method)": [[10, "compute.storage.volume.Volume.clone"]], "compute.storage.volume": [[10, "module-compute.storage.volume"]], "delete() (compute.storage.volume.volume method)": [[10, "compute.storage.volume.Volume.delete"]], "dump_xml() (compute.storage.volume.volume method)": [[10, "compute.storage.volume.Volume.dump_xml"]], "resize() (compute.storage.volume.volume method)": [[10, "compute.storage.volume.Volume.resize"]], "to_xml() (compute.storage.volume.diskconfig method)": [[10, "compute.storage.volume.DiskConfig.to_xml"]], "to_xml() (compute.storage.volume.volumeconfig method)": [[10, "compute.storage.volume.VolumeConfig.to_xml"]], "dataunit (class in compute.utils.units)": [[11, "compute.utils.units.DataUnit"]], "invaliddatauniterror": [[11, "compute.utils.units.InvalidDataUnitError"]], "compute.utils.ids": [[11, "module-compute.utils.ids"]], "compute.utils.units": [[11, "module-compute.utils.units"]], "random_mac() (in module compute.utils.ids)": [[11, "compute.utils.ids.random_mac"]], "to_bytes() (in module compute.utils.units)": [[11, "compute.utils.units.to_bytes"]]}}) \ No newline at end of file diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute.bash-completion b/packaging/build/compute-0.1.0.dev1/debian/compute.bash-completion deleted file mode 100644 index a0dcdf2..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute.bash-completion +++ /dev/null @@ -1,93 +0,0 @@ -# compute bash completion script - -_compute_root_cmd=" - --version - --verbose - --connect - --log-level - init - exec - ls - start - shutdown - reboot - reset - powrst - pause - resume - status - setvcpus - setmem - setpasswd" -_compute_init_opts="" -_compute_exec_opts=" - --timeout - --executable - --env - --no-join-args" -_compute_ls_opts="" -_compute_start_opts="" -_compute_shutdown_opts="--method" -_compute_reboot_opts="" -_compute_reset_opts="" -_compute_powrst_opts="" -_compute_pause_opts="" -_compute_resume_opts="" -_compute_status_opts="" -_compute_setvcpus_opts="" -_compute_setmem_opts="" -_compute_setpasswd_opts="--encrypted" - -_compute_complete_instances() -{ - for file in /etc/libvirt/qemu/*.xml; do - nodir="${file##*/}" - printf '%s ' "${nodir//\.xml}" - done -} - -_compute_compreply() -{ - if [[ "$current" = [a-z]* ]]; then - _compute_compwords="$(_compute_complete_instances)" - else - _compute_compwords="$*" - fi - COMPREPLY=($(compgen -W "$_compute_compwords" -- "$current")) -} - -_compute_complete() -{ - local current previous nshift - current="${COMP_WORDS[COMP_CWORD]}" - case "$COMP_CWORD" in - 1) COMPREPLY=($(compgen -W "$_compute_root_cmd" -- "$current")) - ;; - 2|3|4|5) - nshift=$((COMP_CWORD-1)) - previous="${COMP_WORDS[COMP_CWORD-nshift]}" - case "$previous" in - init) COMPREPLY=($(compgen -f -- "$current"));; - exec) _compute_compreply "$_compute_exec_opts";; - ls) COMPREPLY=($(compgen -W "$_compute_ls_opts" -- "$current"));; - start) _compute_compreply "$_compute_start_opts";; - shutdown) _compute_compreply "$_compute_shutdown_opts";; - reboot) _compute_compreply "$_compute_reboot_opts";; - reset) _compute_compreply "$_compute_reset_opts";; - powrst) _compute_compreply "$_compute_powrst_opts";; - pause) _compute_compreply "$_compute_pause_opts";; - resume) _compute_compreply "$_compute_resume_opts";; - status) _compute_compreply "$_compute_status_opts";; - setvcpus) _compute_compreply "$_compute_setvcpus_opts";; - setmem) _compute_compreply "$_compute_setmem_opts";; - setpasswd) _compute_compreply "$_compute_setpasswd_opts";; - *) COMPREPLY=() - esac - ;; - *) COMPREPLY=($(compgen -W "$_compute_compwords" -- "$current")) - esac -} - -complete -F _compute_complete compute - -# vim: ft=bash diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute.debhelper.log b/packaging/build/compute-0.1.0.dev1/debian/compute.debhelper.log deleted file mode 100644 index 8dc2028..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute.debhelper.log +++ /dev/null @@ -1 +0,0 @@ -dh_sphinxdoc diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute.postinst.debhelper b/packaging/build/compute-0.1.0.dev1/debian/compute.postinst.debhelper deleted file mode 100644 index 2545e7a..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute.postinst.debhelper +++ /dev/null @@ -1,10 +0,0 @@ - -# Automatically added by dh_python3 -if command -v py3compile >/dev/null 2>&1; then - py3compile -p compute -fi -if command -v pypy3compile >/dev/null 2>&1; then - pypy3compile -p compute || true -fi - -# End automatically added section diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute.prerm.debhelper b/packaging/build/compute-0.1.0.dev1/debian/compute.prerm.debhelper deleted file mode 100644 index 062ac2f..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute.prerm.debhelper +++ /dev/null @@ -1,10 +0,0 @@ - -# Automatically added by dh_python3 -if command -v py3clean >/dev/null 2>&1; then - py3clean -p compute -else - dpkg -L compute | sed -En -e '/^(.*)\/(.+)\.py$/s,,rm "\1/__pycache__/\2".*,e' - find /usr/lib/python3/dist-packages/ -type d -name __pycache__ -empty -print0 | xargs --null --no-run-if-empty rmdir -fi - -# End automatically added section diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute.substvars b/packaging/build/compute-0.1.0.dev1/debian/compute.substvars deleted file mode 100644 index 6561153..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute.substvars +++ /dev/null @@ -1,3 +0,0 @@ -python3:Depends=python3-libvirt, python3-lxml, python3-pydantic, python3-yaml, python3:any -misc:Depends= -misc:Pre-Depends= diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute/DEBIAN/control b/packaging/build/compute-0.1.0.dev1/debian/compute/DEBIAN/control deleted file mode 100644 index 906243f..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute/DEBIAN/control +++ /dev/null @@ -1,12 +0,0 @@ -Package: compute -Version: 0.1.0.dev1-1 -Architecture: all -Maintainer: ge -Installed-Size: 118 -Depends: python3-libvirt, python3-lxml, python3-pydantic, python3-yaml, python3:any, qemu-system, qemu-utils, libvirt-daemon-system, libvirt-clients -Recommends: dnsmasq -Suggests: compute-doc -Section: admin -Priority: optional -Homepage: https://git.lulzette.ru/hstack/compute -Description: Compute instances management library and tools (Python 3) diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute/DEBIAN/md5sums b/packaging/build/compute-0.1.0.dev1/debian/compute/DEBIAN/md5sums deleted file mode 100644 index 28d1b77..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute/DEBIAN/md5sums +++ /dev/null @@ -1,27 +0,0 @@ -e21aa7b0b8fd557e047296cdf5ced826 usr/bin/compute -f9bc2efd4317ac0a92b8c7d283b947b8 usr/lib/python3/dist-packages/compute-0.1.0.dev1.dist-info/METADATA -db790365fd79ce4e960409f8cfc71dae usr/lib/python3/dist-packages/compute-0.1.0.dev1.dist-info/RECORD -b65598d0aa0cfe0f390246499e741adb usr/lib/python3/dist-packages/compute-0.1.0.dev1.dist-info/WHEEL -d6561300b96471e4e471ea1615006527 usr/lib/python3/dist-packages/compute-0.1.0.dev1.dist-info/entry_points.txt -9c54095f8462231dc4be8f87fadee594 usr/lib/python3/dist-packages/compute/__init__.py -a1b4018266bd8295c5e829c45948f642 usr/lib/python3/dist-packages/compute/__main__.py -d41d8cd98f00b204e9800998ecf8427e usr/lib/python3/dist-packages/compute/cli/__init__.py -8c13534e878816096e129b15462d0840 usr/lib/python3/dist-packages/compute/cli/control.py -1c4b0023246c9cd9d37e2addc255d7f9 usr/lib/python3/dist-packages/compute/common.py -665c006c01d16e64323037b0089cacef usr/lib/python3/dist-packages/compute/exceptions.py -1ff1400c5f71bd3a55ce2521258b5bd2 usr/lib/python3/dist-packages/compute/instance/__init__.py -82ec67ce83d65b991a8aba5e70f30e76 usr/lib/python3/dist-packages/compute/instance/guest_agent.py -135f6785552229c6fac04ab1d7c3113b usr/lib/python3/dist-packages/compute/instance/instance.py -00df3cb0195a2b97f1972f020bdbb243 usr/lib/python3/dist-packages/compute/instance/schemas.py -1d557cf313b52726a3591bd2e59c3c9b usr/lib/python3/dist-packages/compute/session.py -0a98a65c1a665afb4e4ed9cb3aef38f5 usr/lib/python3/dist-packages/compute/storage/__init__.py -ecf7a8e68c733d8e5b241ca33ae7cae0 usr/lib/python3/dist-packages/compute/storage/pool.py -c4a6cb9dbccfaa9217c2dbc4a833e8c9 usr/lib/python3/dist-packages/compute/storage/volume.py -d41d8cd98f00b204e9800998ecf8427e usr/lib/python3/dist-packages/compute/utils/__init__.py -e7797202c176137f38a6652cf45170a2 usr/lib/python3/dist-packages/compute/utils/config_loader.py -6c36830706d7d714d9b3c1d23dcccf14 usr/lib/python3/dist-packages/compute/utils/ids.py -964156c54ebe27ba2b14313f8f9f9754 usr/lib/python3/dist-packages/compute/utils/units.py -1fd80db613384b8d5782cf8c5843eb94 usr/share/bash-completion/completions/compute -672a4b3f13e2a14e4040c7a513ed60ba usr/share/doc/compute/README.md -6845278a102bd147f30f770ed1134ce5 usr/share/doc/compute/changelog.Debian.gz -fb1a6c11d7a8fa5f238617c20b13b6a1 usr/share/doc/compute/copyright diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute/DEBIAN/postinst b/packaging/build/compute-0.1.0.dev1/debian/compute/DEBIAN/postinst deleted file mode 100755 index cebdb00..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute/DEBIAN/postinst +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/sh -set -e - -# Automatically added by dh_python3 -if command -v py3compile >/dev/null 2>&1; then - py3compile -p compute -fi -if command -v pypy3compile >/dev/null 2>&1; then - pypy3compile -p compute || true -fi - -# End automatically added section diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute/DEBIAN/prerm b/packaging/build/compute-0.1.0.dev1/debian/compute/DEBIAN/prerm deleted file mode 100755 index d867122..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute/DEBIAN/prerm +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/sh -set -e - -# Automatically added by dh_python3 -if command -v py3clean >/dev/null 2>&1; then - py3clean -p compute -else - dpkg -L compute | sed -En -e '/^(.*)\/(.+)\.py$/s,,rm "\1/__pycache__/\2".*,e' - find /usr/lib/python3/dist-packages/ -type d -name __pycache__ -empty -print0 | xargs --null --no-run-if-empty rmdir -fi - -# End automatically added section diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute/usr/bin/compute b/packaging/build/compute-0.1.0.dev1/debian/compute/usr/bin/compute deleted file mode 100755 index 56e33f2..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute/usr/bin/compute +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/python3 -# -*- coding: utf-8 -*- -import re -import sys -from compute.cli.control import cli -if __name__ == "__main__": - sys.argv[0] = re.sub(r"(-script\.pyw|\.exe)?$", "", sys.argv[0]) - sys.exit(cli()) diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute/usr/lib/python3/dist-packages/compute-0.1.0.dev1.dist-info/METADATA b/packaging/build/compute-0.1.0.dev1/debian/compute/usr/lib/python3/dist-packages/compute-0.1.0.dev1.dist-info/METADATA deleted file mode 100644 index f4c22ad..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute/usr/lib/python3/dist-packages/compute-0.1.0.dev1.dist-info/METADATA +++ /dev/null @@ -1,81 +0,0 @@ -Metadata-Version: 2.1 -Name: compute -Version: 0.1.0.dev1 -Summary: Compute instances management library and tools -Author: ge -Author-email: ge@nixhacks.net -Requires-Python: >=3.11,<4.0 -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.11 -Requires-Dist: libvirt-python (==9.0.0) -Requires-Dist: lxml (>=4.9.2,<5.0.0) -Requires-Dist: pydantic (==1.10.4) -Requires-Dist: pyyaml (>=6.0.1,<7.0.0) -Description-Content-Type: text/markdown - -# Compute - -Compute instances management library and tools. - -## Docs - -Run `make serve-docs`. See [Development](#development) below. - -## Roadmap - -- [x] Create instances -- [ ] CDROM -- [ ] cloud-init for provisioning instances -- [x] Instance power management -- [x] Instance pause and resume -- [x] vCPU hotplug -- [x] Memory hotplug -- [x] Hot disk resize [not tested] -- [ ] CPU topology customization -- [x] CPU customization (emulation mode, model, vendor, features) -- [ ] BIOS/UEFI settings -- [x] Device attaching -- [x] Device detaching -- [ ] GPU passthrough -- [ ] CPU guarantied resource percent support -- [x] QEMU Guest Agent management -- [ ] Instance resources usage stats -- [ ] SSH-keys management -- [x] Setting user passwords in guest -- [x] QCOW2 disks support -- [ ] ZVOL support -- [ ] Network disks support -- [ ] Images service integration (Images service is not implemented yet) -- [ ] Manage storage pools -- [ ] Idempotency -- [ ] CLI [in progress] -- [ ] HTTP API -- [ ] Instance migrations -- [ ] Instance snapshots -- [ ] Instance backups -- [ ] LXC - -## Development - -Python 3.11+ is required. - -Install [poetry](https://python-poetry.org/), clone this repository and run: - -``` -poetry install --with dev --with docs -``` - -# Build Debian package - -Install Docker first, then run: - -``` -make build-deb -``` - -`compute` and `compute-doc` packages will built. See packaging/build directory. Packages can be installed via `dpkg` or `apt-get`: - -``` -apt-get install ./compute*.deb -``` - diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute/usr/lib/python3/dist-packages/compute-0.1.0.dev1.dist-info/RECORD b/packaging/build/compute-0.1.0.dev1/debian/compute/usr/lib/python3/dist-packages/compute-0.1.0.dev1.dist-info/RECORD deleted file mode 100644 index 5f97163..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute/usr/lib/python3/dist-packages/compute-0.1.0.dev1.dist-info/RECORD +++ /dev/null @@ -1,23 +0,0 @@ -../scripts/compute,sha256=b-Gj6H6ssfbGalpouUMSX5pmsjqDnN9xMdTwnU-UfZY,216 -compute/__init__.py,sha256=x4zp_CoVPKgDT6AqhometspAyinGxJUXO48duJ5aHUM,873 -compute/__main__.py,sha256=zJyKJul6pCbguFPtVLZBoAuZl9RXibn4CCMn46jIgUQ,745 -compute/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -compute/cli/control.py,sha256=83wnR21pHOPyyk1i1n_YBIDz6dCFB6hmuIFguIk68rs,14634 -compute/common.py,sha256=G1qwC1EybG5LEJtyoux9ymiqB2ZOsgKXlCpbuhHv55Y,948 -compute/exceptions.py,sha256=Ga59L55qSAPeyDfjANPuMh4yVSRWHDYi9xqq5o4_7-0,2452 -compute/instance/__init__.py,sha256=kHN8jVamyrBZYZgi62tPtJ7rS73gUPhfswLalmPA5Zs,772 -compute/instance/guest_agent.py,sha256=fq89kQbcV5X5eFCsMmujRuwTOSghWO4ZhAjvxyUu84M,7018 -compute/instance/instance.py,sha256=WP6oTJfdAf6QlefwVLqdC8J6XoKHum6nZhwwHOEtjNk,23297 -compute/instance/schemas.py,sha256=B51ytPlxhnx0MrkR2WYhd49RaRT7Is7NsIM9OrMUpvI,4288 -compute/session.py,sha256=znYOIzoiCbSG62k-ViaXti_lOnw88wD8Syp3nCXAJ28,10050 -compute/storage/__init__.py,sha256=zNaVjZ2925DxrVUFWwVRsGU6bSYbF46sb4L6NsaiKbw,736 -compute/storage/pool.py,sha256=9z99bBDbb4ATGpfMkEWpxAO4fEQHNVOxxf0iUln9cN0,4197 -compute/storage/volume.py,sha256=_TbK9Y4d3NAeknPUiuhldAT3ZaN1sZgjy4QzC-Sw4Io,4110 -compute/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -compute/utils/config_loader.py,sha256=ul1J3sZg0D9R0HbOz5Pg9JmL4nFaMahAzQEdGaWFABU,1989 -compute/utils/ids.py,sha256=fg6Xsg4OMM-BIaU3DPu0L91ICwx-L3qNoELEwQZz2s0,1007 -compute/utils/units.py,sha256=UkwD0zQ-rlpSpkbfezCcvJx4D8iZlI9M-oXXvdVEvy0,1549 -compute-0.1.0.dev1.dist-info/METADATA,sha256=tbX8xp92Jwqf44sOwPB-HqKHLezab5dU9DrQDYFitDQ,1944 -compute-0.1.0.dev1.dist-info/WHEEL,sha256=vVCvjcmxuUltf8cYhJ0sJMRDLr1XsPuxEId8YDzbyCY,88 -compute-0.1.0.dev1.dist-info/entry_points.txt,sha256=xHhg-Fo9Z5gJnIahbG8pVIGNDqlH5Eordn8hnXUwscw,51 -compute-0.1.0.dev1.dist-info/RECORD,, diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute/usr/lib/python3/dist-packages/compute-0.1.0.dev1.dist-info/WHEEL b/packaging/build/compute-0.1.0.dev1/debian/compute/usr/lib/python3/dist-packages/compute-0.1.0.dev1.dist-info/WHEEL deleted file mode 100644 index 4ba7671..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute/usr/lib/python3/dist-packages/compute-0.1.0.dev1.dist-info/WHEEL +++ /dev/null @@ -1,4 +0,0 @@ -Wheel-Version: 1.0 -Generator: poetry-core 1.4.0 -Root-Is-Purelib: true -Tag: py3-none-any diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute/usr/lib/python3/dist-packages/compute-0.1.0.dev1.dist-info/entry_points.txt b/packaging/build/compute-0.1.0.dev1/debian/compute/usr/lib/python3/dist-packages/compute-0.1.0.dev1.dist-info/entry_points.txt deleted file mode 100644 index 4130f9f..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute/usr/lib/python3/dist-packages/compute-0.1.0.dev1.dist-info/entry_points.txt +++ /dev/null @@ -1,3 +0,0 @@ -[console_scripts] -compute=compute.cli.control:cli - diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute/usr/lib/python3/dist-packages/compute/__init__.py b/packaging/build/compute-0.1.0.dev1/debian/compute/usr/lib/python3/dist-packages/compute/__init__.py deleted file mode 100644 index ffe06d7..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute/usr/lib/python3/dist-packages/compute/__init__.py +++ /dev/null @@ -1,22 +0,0 @@ -# This file is part of Compute -# -# Compute is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Ansible is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Ansible. If not, see . - -"""Compute instances management library.""" - -__version__ = '0.1.0-dev1' - -from .instance import Instance, InstanceConfig, InstanceSchema -from .session import Session -from .storage import StoragePool, Volume, VolumeConfig diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute/usr/lib/python3/dist-packages/compute/__main__.py b/packaging/build/compute-0.1.0.dev1/debian/compute/usr/lib/python3/dist-packages/compute/__main__.py deleted file mode 100644 index 4995fbd..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute/usr/lib/python3/dist-packages/compute/__main__.py +++ /dev/null @@ -1,21 +0,0 @@ -# This file is part of Compute -# -# Compute is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Ansible is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Ansible. If not, see . - -"""Command line interface for compute module.""" - -from compute.cli import main - - -main.cli() diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute/usr/lib/python3/dist-packages/compute/cli/__init__.py b/packaging/build/compute-0.1.0.dev1/debian/compute/usr/lib/python3/dist-packages/compute/cli/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute/usr/lib/python3/dist-packages/compute/cli/control.py b/packaging/build/compute-0.1.0.dev1/debian/compute/usr/lib/python3/dist-packages/compute/cli/control.py deleted file mode 100644 index f5a5b91..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute/usr/lib/python3/dist-packages/compute/cli/control.py +++ /dev/null @@ -1,501 +0,0 @@ -# This file is part of Compute -# -# Compute is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Ansible is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Ansible. If not, see . - -"""Command line interface.""" - -import argparse -import io -import logging -import os -import shlex -import sys -from collections import UserDict -from typing import Any -from uuid import uuid4 - -import libvirt -import yaml -from pydantic import ValidationError - -from compute import __version__ -from compute.exceptions import ComputeError, GuestAgentTimeoutExceededError -from compute.instance import GuestAgent -from compute.session import Session -from compute.utils import ids - - -log = logging.getLogger(__name__) -log_levels = [lv.lower() for lv in logging.getLevelNamesMapping()] - -libvirt.registerErrorHandler( - lambda userdata, err: None, # noqa: ARG005 - ctx=None, -) - - -class Table: - """Minimalistic text table constructor.""" - - def __init__(self, whitespace: str | None = None): - """Initialise Table.""" - self.whitespace = whitespace or '\t' - self.header = [] - self.rows = [] - self.table = '' - - def add_row(self, row: list) -> None: - """Add table row.""" - self.rows.append([str(col) for col in row]) - - def add_rows(self, rows: list[list]) -> None: - """Add multiple rows.""" - for row in rows: - self.add_row(row) - - def __str__(self) -> str: - """Build table and return.""" - widths = [max(map(len, col)) for col in zip(*self.rows, strict=True)] - self.rows.insert(0, [str(h).upper() for h in self.header]) - for row in self.rows: - self.table += self.whitespace.join( - ( - val.ljust(width) - for val, width in zip(row, widths, strict=True) - ) - ) - self.table += '\n' - return self.table.strip() - - -def _list_instances(session: Session) -> None: - table = Table() - table.header = ['NAME', 'STATE'] - for instance in session.list_instances(): - table.add_row( - [ - instance.name, - instance.get_status(), - ] - ) - print(table) - sys.exit() - - -def _exec_guest_agent_command( - session: Session, args: argparse.Namespace -) -> None: - instance = session.get_instance(args.instance) - ga = GuestAgent(instance.domain, timeout=args.timeout) - arguments = args.arguments.copy() - if len(arguments) > 1 and not args.no_join_args: - arguments = [shlex.join(arguments)] - if not args.no_join_args: - arguments.insert(0, '-c') - stdin = None - if not sys.stdin.isatty(): - stdin = sys.stdin.read() - try: - output = ga.guest_exec( - path=args.executable, - args=arguments, - env=args.env, - stdin=stdin, - capture_output=True, - decode_output=True, - poll=True, - ) - except GuestAgentTimeoutExceededError as e: - sys.exit( - f'{e}. NOTE: command may still running in guest, ' - f'PID={ga.last_pid}' - ) - if output.stderr: - print(output.stderr.strip(), file=sys.stderr) - if output.stdout: - print(output.stdout.strip(), file=sys.stdout) - sys.exit(output.exitcode) - - -class _NotPresent: - """ - Type for representing non-existent dictionary keys. - - See :class:`_FillableDict`. - """ - - -class _FillableDict(UserDict): - """Use :method:`fill` to add key if not present.""" - - def __init__(self, data: dict): - self.data = data - - def fill(self, key: str, value: Any) -> None: # noqa: ANN401 - if self.data.get(key, _NotPresent) is _NotPresent: - self.data[key] = value - - -def _merge_dicts(a: dict, b: dict, path: list[str] | None = None) -> dict: - """Merge `b` into `a`. Return modified `a`.""" - if path is None: - path = [] - for key in b: - if key in a: - if isinstance(a[key], dict) and isinstance(b[key], dict): - _merge_dicts(a[key], b[key], [path + str(key)]) - elif a[key] == b[key]: - pass # same leaf value - else: - a[key] = b[key] # replace existing key's values - else: - a[key] = b[key] - return a - - -def _create_instance(session: Session, file: io.TextIOWrapper) -> None: - try: - data = _FillableDict(yaml.load(file.read(), Loader=yaml.SafeLoader)) - log.debug('Read from file: %s', data) - except yaml.YAMLError as e: - sys.exit(f'error: cannot parse YAML: {e}') - - capabilities = session.get_capabilities() - node_info = session.get_node_info() - - data.fill('name', uuid4().hex) - data.fill('title', None) - data.fill('description', None) - data.fill('arch', capabilities.arch) - data.fill('machine', capabilities.machine) - data.fill('emulator', capabilities.emulator) - data.fill('max_vcpus', node_info.cpus) - data.fill('max_memory', node_info.memory) - data.fill('cpu', {}) - cpu = { - 'emulation_mode': 'host-passthrough', - 'model': None, - 'vendor': None, - 'topology': None, - 'features': None, - } - data['cpu'] = _merge_dicts(data['cpu'], cpu) - data.fill( - 'network_interfaces', - [{'source': 'default', 'mac': ids.random_mac()}], - ) - data.fill('boot', {'order': ['cdrom', 'hd']}) - - try: - log.debug('Input data: %s', data) - session.create_instance(**data) - except ValidationError as e: - for error in e.errors(): - fields = '.'.join([str(lc) for lc in error['loc']]) - print( - f"validation error: {fields}: {error['msg']}", - file=sys.stderr, - ) - sys.exit() - - -def _shutdown_instance(session: Session, args: argparse.Namespace) -> None: - instance = session.get_instance(args.instance) - if args.soft: - method = 'SOFT' - elif args.hard: - method = 'HARD' - elif args.unsafe: - method = 'UNSAFE' - else: - method = 'NORMAL' - instance.shutdown(method) - - -def main(session: Session, args: argparse.Namespace) -> None: - """Perform actions.""" - match args.command: - case 'init': - _create_instance(session, args.file) - case 'exec': - _exec_guest_agent_command(session, args) - case 'ls': - _list_instances(session) - case 'start': - instance = session.get_instance(args.instance) - instance.start() - case 'shutdown': - _shutdown_instance(session, args) - case 'reboot': - instance = session.get_instance(args.instance) - instance.reboot() - case 'reset': - instance = session.get_instance(args.instance) - instance.reset() - case 'powrst': - instance = session.get_instance(args.instance) - instance.power_reset() - case 'pause': - instance = session.get_instance(args.instance) - instance.pause() - case 'resume': - instance = session.get_instance(args.instance) - instance.resume() - case 'status': - instance = session.get_instance(args.instance) - print(instance.status) - case 'setvcpus': - instance = session.get_instance(args.instance) - instance.set_vcpus(args.nvcpus, live=True) - case 'setmem': - instance = session.get_instance(args.instance) - instance.set_memory(args.memory, live=True) - case 'setpass': - instance = session.get_instance(args.instance) - instance.set_user_password( - args.username, - args.password, - encrypted=args.encrypted, - ) - - -def cli() -> None: # noqa: PLR0915 - """Return command line arguments parser.""" - root = argparse.ArgumentParser( - prog='compute', - description='manage compute instances', - formatter_class=argparse.RawTextHelpFormatter, - ) - root.add_argument( - '-v', - '--verbose', - action='store_true', - default=False, - help='enable verbose mode', - ) - root.add_argument( - '-c', - '--connect', - metavar='URI', - help='libvirt connection URI', - ) - root.add_argument( - '-l', - '--log-level', - type=str.lower, - metavar='LEVEL', - choices=log_levels, - help='log level', - ) - root.add_argument( - '-V', - '--version', - action='version', - version=__version__, - ) - subparsers = root.add_subparsers(dest='command', metavar='COMMAND') - - # init command - init = subparsers.add_parser( - 'init', help='initialise instance using YAML config file' - ) - init.add_argument( - 'file', - type=argparse.FileType('r', encoding='UTF-8'), - nargs='?', - default='instance.yaml', - help='instance config [default: instance.yaml]', - ) - - # exec subcommand - execute = subparsers.add_parser( - 'exec', - help='execute command in guest via guest agent', - description=( - 'NOTE: any argument after instance name will be passed into ' - 'guest as shell command.' - ), - ) - execute.add_argument('instance') - execute.add_argument('arguments', nargs=argparse.REMAINDER) - execute.add_argument( - '-t', - '--timeout', - type=int, - default=60, - help=( - 'waiting time in seconds for a command to be executed ' - 'in guest [default: 60]' - ), - ) - execute.add_argument( - '-x', - '--executable', - default='/bin/sh', - help='path to executable in guest [default: /bin/sh]', - ) - execute.add_argument( - '-e', - '--env', - type=str, - nargs='?', - action='append', - help='environment variables to pass to executable in guest', - ) - execute.add_argument( - '-n', - '--no-join-args', - action='store_true', - default=False, - help=( - "do not join arguments list and add '-c' option, suitable " - 'for non-shell executables and other specific cases.' - ), - ) - - # ls subcommand - listall = subparsers.add_parser('ls', help='list instances') - listall.add_argument( - '-a', - '--all', - action='store_true', - default=False, - help='list all instances including inactive', - ) - - # start subcommand - start = subparsers.add_parser('start', help='start instance') - start.add_argument('instance') - - # shutdown subcommand - shutdown = subparsers.add_parser('shutdown', help='shutdown instance') - shutdown.add_argument('instance') - shutdown_opts = shutdown.add_mutually_exclusive_group() - shutdown_opts.add_argument( - '-s', - '--soft', - action='store_true', - help='normal guest OS shutdown, guest agent is used', - ) - shutdown_opts.add_argument( - '-n', - '--normal', - action='store_true', - help='shutdown with hypervisor selected method [default]', - ) - shutdown_opts.add_argument( - '-H', - '--hard', - action='store_true', - help=( - "gracefully destroy instance, it's like long " - 'pressing the power button' - ), - ) - shutdown_opts.add_argument( - '-u', - '--unsafe', - action='store_true', - help=( - 'destroy instance, this is similar to a power outage ' - 'and may result in data loss or corruption' - ), - ) - - # reboot subcommand - reboot = subparsers.add_parser('reboot', help='reboot instance') - reboot.add_argument('instance') - - # reset subcommand - reset = subparsers.add_parser('reset', help='reset instance') - reset.add_argument('instance') - - # powrst subcommand - powrst = subparsers.add_parser('powrst', help='power reset instance') - powrst.add_argument('instance') - - # pause subcommand - pause = subparsers.add_parser('pause', help='pause instance') - pause.add_argument('instance') - - # resume subcommand - resume = subparsers.add_parser('resume', help='resume paused instance') - resume.add_argument('instance') - - # status subcommand - status = subparsers.add_parser('status', help='display instance status') - status.add_argument('instance') - - # setvcpus subcommand - setvcpus = subparsers.add_parser('setvcpus', help='set vCPU number') - setvcpus.add_argument('instance') - setvcpus.add_argument('nvcpus', type=int) - - # setmem subcommand - setmem = subparsers.add_parser('setmem', help='set memory size') - setmem.add_argument('instance') - setmem.add_argument('memory', type=int, help='memory in MiB') - - # setpass subcommand - setpass = subparsers.add_parser( - 'setpass', - help='set user password in guest', - ) - setpass.add_argument('instance') - setpass.add_argument('username') - setpass.add_argument('password') - setpass.add_argument( - '-e', - '--encrypted', - action='store_true', - default=False, - help='set it if password is already encrypted', - ) - - args = root.parse_args() - if args.command is None: - root.print_help() - sys.exit() - - log_level = args.log_level or os.getenv('CMP_LOG') - - if isinstance(log_level, str) and log_level.lower() in log_levels: - logging.basicConfig( - level=logging.getLevelNamesMapping()[log_level.upper()] - ) - - log.debug('CLI started with args: %s', args) - - connect_uri = ( - args.connect - or os.getenv('CMP_LIBVIRT_URI') - or os.getenv('LIBVIRT_DEFAULT_URI') - or 'qemu:///system' - ) - - try: - with Session(connect_uri) as session: - main(session, args) - except ComputeError as e: - sys.exit(f'error: {e}') - except KeyboardInterrupt: - sys.exit() - except SystemExit as e: - sys.exit(e) - except Exception as e: # noqa: BLE001 - sys.exit(f'unexpected error {type(e)}: {e}') - - -if __name__ == '__main__': - cli() diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute/usr/lib/python3/dist-packages/compute/common.py b/packaging/build/compute-0.1.0.dev1/debian/compute/usr/lib/python3/dist-packages/compute/common.py deleted file mode 100644 index 34a339a..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute/usr/lib/python3/dist-packages/compute/common.py +++ /dev/null @@ -1,30 +0,0 @@ -# This file is part of Compute -# -# Compute is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Ansible is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Ansible. If not, see . - -"""Common symbols.""" - -from abc import ABC, abstractmethod - - -class EntityConfig(ABC): - """An abstract entity XML config builder class.""" - - @abstractmethod - def to_xml(self) -> str: - """Return device XML config.""" - raise NotImplementedError - - -DeviceConfig = EntityConfig diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute/usr/lib/python3/dist-packages/compute/exceptions.py b/packaging/build/compute-0.1.0.dev1/debian/compute/usr/lib/python3/dist-packages/compute/exceptions.py deleted file mode 100644 index 1eef8de..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute/usr/lib/python3/dist-packages/compute/exceptions.py +++ /dev/null @@ -1,80 +0,0 @@ -# This file is part of Compute -# -# Compute is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Ansible is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Ansible. If not, see . - -"""Exceptions.""" - - -class ComputeError(Exception): - """Basic exception class.""" - - -class ConfigLoaderError(ComputeError): - """Something went wrong when loading configuration.""" - - -class SessionError(ComputeError): - """Something went wrong while connecting to libvirtd.""" - - -class GuestAgentError(ComputeError): - """Something went wring when QEMU Guest Agent call.""" - - -class GuestAgentUnavailableError(GuestAgentError): - """Guest agent is not connected or is unavailable.""" - - -class GuestAgentTimeoutExceededError(GuestAgentError): - """QEMU timeout exceeded.""" - - def __init__(self, msg: int): - """Initialise GuestAgentTimeoutExceededError.""" - super().__init__(f'QEMU timeout ({msg} sec) exceeded') - - -class GuestAgentCommandNotSupportedError(GuestAgentError): - """Guest agent command is not supported or blacklisted on guest.""" - - -class StoragePoolError(ComputeError): - """Something went wrong when operating with storage pool.""" - - -class StoragePoolNotFoundError(StoragePoolError): - """Storage pool not found.""" - - def __init__(self, msg: str): - """Initialise StoragePoolNotFoundError.""" - super().__init__(f"storage pool named '{msg}' not found") - - -class VolumeNotFoundError(StoragePoolError): - """Storage volume not found.""" - - def __init__(self, msg: str): - """Initialise VolumeNotFoundError.""" - super().__init__(f"storage volume '{msg}' not found") - - -class InstanceError(ComputeError): - """Something went wrong while interacting with the domain.""" - - -class InstanceNotFoundError(InstanceError): - """Virtual machine or container not found on compute node.""" - - def __init__(self, msg: str): - """Initialise InstanceNotFoundError.""" - super().__init__(f"compute instance '{msg}' not found") diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute/usr/lib/python3/dist-packages/compute/instance/__init__.py b/packaging/build/compute-0.1.0.dev1/debian/compute/usr/lib/python3/dist-packages/compute/instance/__init__.py deleted file mode 100644 index 6e2b150..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute/usr/lib/python3/dist-packages/compute/instance/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -# This file is part of Compute -# -# Compute is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Ansible is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Ansible. If not, see . - -from .guest_agent import GuestAgent -from .instance import Instance, InstanceConfig -from .schemas import InstanceSchema diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute/usr/lib/python3/dist-packages/compute/instance/guest_agent.py b/packaging/build/compute-0.1.0.dev1/debian/compute/usr/lib/python3/dist-packages/compute/instance/guest_agent.py deleted file mode 100644 index 4381591..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute/usr/lib/python3/dist-packages/compute/instance/guest_agent.py +++ /dev/null @@ -1,208 +0,0 @@ -# This file is part of Compute -# -# Compute is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Ansible is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Ansible. If not, see . - -"""Interacting with the QEMU Guest Agent.""" - -import json -import logging -from base64 import b64decode, standard_b64encode -from time import sleep, time -from typing import NamedTuple - -import libvirt -import libvirt_qemu - -from compute.exceptions import ( - GuestAgentCommandNotSupportedError, - GuestAgentError, - GuestAgentTimeoutExceededError, - GuestAgentUnavailableError, -) - - -log = logging.getLogger(__name__) - - -class GuestExecOutput(NamedTuple): - """QEMU guest-exec command output.""" - - exited: bool | None = None - exitcode: int | None = None - stdout: str | None = None - stderr: str | None = None - - -class GuestAgent: - """Class for interacting with QEMU guest agent.""" - - def __init__(self, domain: libvirt.virDomain, timeout: int = 60): - """ - Initialise GuestAgent. - - :param domain: Libvirt domain object - :param timeout: QEMU timeout - """ - self.domain = domain - self.timeout = timeout - self.flags = libvirt_qemu.VIR_DOMAIN_QEMU_MONITOR_COMMAND_DEFAULT - self.last_pid = None - - def execute(self, command: dict) -> dict: - """ - Execute QEMU guest agent command. - - See: https://qemu-project.gitlab.io/qemu/interop/qemu-ga-ref.html - - :param command: QEMU guest agent command as dict - :return: Command output - :rtype: dict - """ - log.debug(command) - try: - output = libvirt_qemu.qemuAgentCommand( - self.domain, json.dumps(command), self.timeout, self.flags - ) - return json.loads(output) - except libvirt.libvirtError as e: - if e.get_error_code() == libvirt.VIR_ERR_AGENT_UNRESPONSIVE: - raise GuestAgentUnavailableError(e) from e - raise GuestAgentError(e) from e - - def is_available(self) -> bool: - """ - Execute guest-ping. - - :return: True or False if guest agent is unreachable. - :rtype: bool - """ - try: - if self.execute({'execute': 'guest-ping', 'arguments': {}}): - return True - except GuestAgentError: - return False - - def get_supported_commands(self) -> set[str]: - """Return set of supported guest agent commands.""" - output = self.execute({'execute': 'guest-info', 'arguments': {}}) - return { - cmd['name'] - for cmd in output['return']['supported_commands'] - if cmd['enabled'] is True - } - - def raise_for_commands(self, commands: list[str]) -> None: - """ - Raise exception if QEMU GA command is not available. - - :param commands: List of required commands - :raise: GuestAgentCommandNotSupportedError - """ - supported = self.get_supported_commands() - for command in commands: - if command not in supported: - raise GuestAgentCommandNotSupportedError(command) - - def guest_exec( # noqa: PLR0913 - self, - path: str, - args: list[str] | None = None, - env: list[str] | None = None, - stdin: str | None = None, - *, - capture_output: bool = False, - decode_output: bool = False, - poll: bool = False, - ) -> GuestExecOutput: - """ - Execute qemu-exec command and return output. - - :param path: Path ot executable on guest. - :param arg: List of arguments to pass to executable. - :param env: List of environment variables to pass to executable. - For example: ``['LANG=C', 'TERM=xterm']`` - :param stdin: Data to pass to executable STDIN. - :param capture_output: Capture command output. - :param decode_output: Use base64_decode() to decode command output. - Affects only if `capture_output` is True. - :param poll: Poll command output. Uses `self.timeout` and - POLL_INTERVAL constant. - :return: Command output - :rtype: GuestExecOutput - """ - self.raise_for_commands(['guest-exec', 'guest-exec-status']) - command = { - 'execute': 'guest-exec', - 'arguments': { - 'path': path, - **({'arg': args} if args else {}), - **({'env': env} if env else {}), - **( - { - 'input-data': standard_b64encode( - stdin.encode('utf-8') - ).decode('utf-8') - } - if stdin - else {} - ), - 'capture-output': capture_output, - }, - } - output = self.execute(command) - self.last_pid = pid = output['return']['pid'] - command_status = self.guest_exec_status(pid, poll=poll)['return'] - exited = command_status['exited'] - exitcode = command_status['exitcode'] - stdout = command_status.get('out-data', None) - stderr = command_status.get('err-data', None) - if decode_output: - stdout = b64decode(stdout or '').decode('utf-8') - stderr = b64decode(stderr or '').decode('utf-8') - return GuestExecOutput(exited, exitcode, stdout, stderr) - - def guest_exec_status( - self, pid: int, *, poll: bool = False, poll_interval: float = 0.3 - ) -> dict: - """ - Execute guest-exec-status and return output. - - :param pid: PID in guest. - :param poll: If True poll command status. - :param poll_interval: Time between attempts to obtain command status. - :return: Command output - :rtype: dict - """ - self.raise_for_commands(['guest-exec-status']) - command = { - 'execute': 'guest-exec-status', - 'arguments': {'pid': pid}, - } - if not poll: - return self.execute(command) - start_time = time() - while True: - command_status = self.execute(command) - if command_status['return']['exited']: - break - sleep(poll_interval) - now = time() - if now - start_time > self.timeout: - raise GuestAgentTimeoutExceededError(self.timeout) - log.debug( - 'Polling command pid=%s finished, time taken: %s seconds', - pid, - int(time() - start_time), - ) - return command_status diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute/usr/lib/python3/dist-packages/compute/instance/instance.py b/packaging/build/compute-0.1.0.dev1/debian/compute/usr/lib/python3/dist-packages/compute/instance/instance.py deleted file mode 100644 index 5b806e6..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute/usr/lib/python3/dist-packages/compute/instance/instance.py +++ /dev/null @@ -1,675 +0,0 @@ -# This file is part of Compute -# -# Compute is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Ansible is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Ansible. If not, see . - -"""Manage compute instances.""" - -__all__ = ['Instance', 'InstanceConfig', 'InstanceInfo'] - -import logging -from typing import NamedTuple - -import libvirt -from lxml import etree -from lxml.builder import E - -from compute.common import DeviceConfig, EntityConfig -from compute.exceptions import ( - GuestAgentCommandNotSupportedError, - InstanceError, -) -from compute.storage import DiskConfig -from compute.utils import units - -from .guest_agent import GuestAgent -from .schemas import ( - CPUEmulationMode, - CPUSchema, - InstanceSchema, - NetworkInterfaceSchema, -) - - -log = logging.getLogger(__name__) - - -class InstanceConfig(EntityConfig): - """Compute instance XML config builder.""" - - def __init__(self, schema: InstanceSchema): - """ - Initialise InstanceConfig. - - :param schema: InstanceSchema object - """ - self.name = schema.name - self.title = schema.title - self.description = schema.description - self.memory = schema.memory - self.max_memory = schema.max_memory - self.vcpus = schema.vcpus - self.max_vcpus = schema.max_vcpus - self.cpu = schema.cpu - self.machine = schema.machine - self.emulator = schema.emulator - self.arch = schema.arch - self.boot = schema.boot - self.network_interfaces = schema.network_interfaces - - def _gen_cpu_xml(self, cpu: CPUSchema) -> etree.Element: - options = { - 'mode': cpu.emulation_mode, - 'match': 'exact', - 'check': 'partial', - } - if cpu.emulation_mode == CPUEmulationMode.HOST_PASSTHROUGH: - options['check'] = 'none' - options['migratable'] = 'on' - xml = E.cpu(**options) - if cpu.model: - xml.append(E.model(cpu.model, fallback='forbid')) - if cpu.vendor: - xml.append(E.vendor(cpu.vendor)) - if cpu.topology: - xml.append( - E.topology( - sockets=str(cpu.topology.sockets), - dies=str(cpu.topology.dies), - cores=str(cpu.topology.cores), - threads=str(cpu.topology.threads), - ) - ) - if cpu.features: - for feature in cpu.features.require: - xml.append(E.feature(policy='require', name=feature)) - for feature in cpu.features.disable: - xml.append(E.feature(policy='disable', name=feature)) - return xml - - def _gen_vcpus_xml(self, vcpus: int, max_vcpus: int) -> etree.Element: - xml = E.vcpus() - xml.append(E.vcpu(id='0', enabled='yes', hotpluggable='no', order='1')) - for i in range(max_vcpus - 1): - enabled = 'yes' if (i + 2) <= vcpus else 'no' - xml.append( - E.vcpu( - id=str(i + 1), - enabled=enabled, - hotpluggable='yes', - order=str(i + 2), - ) - ) - return xml - - def _gen_network_interface_xml( - self, interface: NetworkInterfaceSchema - ) -> etree.Element: - return E.interface( - E.source(network=interface.source), - E.mac(address=interface.mac), - type='network', - ) - - def to_xml(self) -> str: - """Return XML config for libvirt.""" - xml = E.domain(type='kvm') - xml.append(E.name(self.name)) - if self.title: - xml.append(E.title(self.title)) - if self.description: - xml.append(E.description(self.description)) - xml.append(E.metadata()) - xml.append(E.memory(str(self.max_memory * 1024), unit='KiB')) - xml.append(E.currentMemory(str(self.memory * 1024), unit='KiB')) - xml.append( - E.vcpu( - str(self.max_vcpus), - placement='static', - current=str(self.vcpus), - ) - ) - xml.append(self._gen_cpu_xml(self.cpu)) - os = E.os(E.type('hvm', machine=self.machine, arch=self.arch)) - for dev in self.boot.order: - os.append(E.boot(dev=dev)) - xml.append(os) - xml.append(E.features(E.acpi(), E.apic())) - xml.append(E.on_poweroff('destroy')) - xml.append(E.on_reboot('restart')) - xml.append(E.on_crash('restart')) - xml.append( - E.pm( - E('suspend-to-mem', enabled='no'), - E('suspend-to-disk', enabled='no'), - ) - ) - devices = E.devices() - devices.append(E.emulator(str(self.emulator))) - for interface in self.network_interfaces: - devices.append(self._gen_network_interface_xml(interface)) - devices.append(E.graphics(type='vnc', port='-1', autoport='yes')) - devices.append(E.input(type='tablet', bus='usb')) - devices.append( - E.channel( - E.source(mode='bind'), - E.target(type='virtio', name='org.qemu.guest_agent.0'), - E.address( - type='virtio-serial', controller='0', bus='0', port='1' - ), - type='unix', - ) - ) - devices.append( - E.console(E.target(type='serial', port='0'), type='pty') - ) - devices.append( - E.video( - E.model(type='vga', vram='16384', heads='1', primary='yes') - ) - ) - xml.append(devices) - return etree.tostring(xml, encoding='unicode', pretty_print=True) - - -class InstanceInfo(NamedTuple): - """ - Store compute instance info. - - Reference: - https://libvirt.org/html/libvirt-libvirt-domain.html#virDomainInfo - """ - - state: str - max_memory: int - memory: int - nproc: int - cputime: int - - -class Instance: - """Manage compute instances.""" - - def __init__(self, domain: libvirt.virDomain): - """ - Initialise Instance. - - :ivar libvirt.virDomain domain: domain object - :ivar libvirt.virConnect connection: connection object - :ivar str name: domain name - :ivar GuestAgent guest_agent: :class:`GuestAgent` object - - :param domain: libvirt domain object - """ - self.domain = domain - self.connection = domain.connect() - self.name = domain.name() - self.guest_agent = GuestAgent(domain) - - def _expand_instance_state(self, state: int) -> str: - 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[state] - - def get_info(self) -> InstanceInfo: - """Return instance info.""" - info = self.domain.info() - return InstanceInfo( - state=self._expand_instance_state(info[0]), - max_memory=info[1], - memory=info[2], - nproc=info[3], - cputime=info[4], - ) - - def get_status(self) -> str: - """ - Return instance state: 'running', 'shutoff', etc. - - Reference: - https://libvirt.org/html/libvirt-libvirt-domain.html#virDomainState - """ - try: - state, _ = self.domain.state() - except libvirt.libvirtError as e: - raise InstanceError( - 'Cannot fetch status of ' f'instance={self.name}: {e}' - ) from e - return self._expand_instance_state(state) - - def is_running(self) -> bool: - """Return True if instance is running, else return False.""" - if self.domain.isActive() != 1: - # 0 - is inactive, -1 - is error - return False - return True - - def is_autostart(self) -> bool: - """Return True if instance autostart is enabled, else return False.""" - try: - return bool(self.domain.autostart()) - except libvirt.libvirtError as e: - raise InstanceError( - f'Cannot get autostart status for ' - f'instance={self.name}: {e}' - ) from e - - def get_max_memory(self) -> int: - """Maximum memory value for domain in KiB.""" - return self.domain.maxMemory() - - def get_max_vcpus(self) -> int: - """Maximum vCPUs number for domain.""" - return self.domain.maxVcpus() - - def start(self) -> None: - """Start defined instance.""" - log.info('Starting instnce=%s', self.name) - if self.is_running(): - log.warning( - 'Already started, nothing to do instance=%s', self.name - ) - return - try: - self.domain.create() - except libvirt.libvirtError as e: - raise InstanceError( - f'Cannot start instance={self.name}: {e}' - ) from e - - def shutdown(self, method: str | None = None) -> None: - """ - Shutdown instance. - - Shutdown methods: - - SOFT - Use guest agent to shutdown. If guest agent is unavailable - NORMAL method will be used. - - NORMAL - Use method choosen by hypervisor to shutdown. Usually send ACPI - signal to guest OS. OS may ignore ACPI e.g. if guest is hanged. - - HARD - Shutdown instance without any guest OS shutdown. This is simular - to unplugging machine from power. Internally send SIGTERM to - instance process and destroy it gracefully. - - UNSAFE - Force shutdown. Internally send SIGKILL to instance process. - There is high data corruption risk! - - If method is None NORMAL method will used. - - :param method: Method used to shutdown instance - """ - methods = { - 'SOFT': libvirt.VIR_DOMAIN_SHUTDOWN_GUEST_AGENT, - 'NORMAL': libvirt.VIR_DOMAIN_SHUTDOWN_DEFAULT, - 'HARD': libvirt.VIR_DOMAIN_DESTROY_GRACEFUL, - 'UNSAFE': libvirt.VIR_DOMAIN_DESTROY_DEFAULT, - } - if method is None: - method = 'NORMAL' - if not isinstance(method, str): - raise TypeError( - f"Shutdown method must be a 'str', not {type(method)}" - ) - method = method.upper() - if method not in methods: - raise ValueError(f"Unsupported shutdown method: '{method}'") - try: - if method in ['SOFT', 'NORMAL']: - self.domain.shutdownFlags(flags=methods[method]) - elif method in ['HARD', 'UNSAFE']: - self.domain.destroyFlags(flags=methods[method]) - except libvirt.libvirtError as e: - raise InstanceError( - f'Cannot shutdown instance={self.name} ' f'{method=}: {e}' - ) from e - - def reboot(self) -> None: - """Send ACPI signal to guest OS to reboot. OS may ignore this.""" - try: - self.domain.reboot() - except libvirt.libvirtError as e: - raise InstanceError( - f'Cannot reboot instance={self.name}: {e}' - ) from e - - def reset(self) -> None: - """ - Reset instance. - - Copypaste from libvirt doc: - - 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. - - Note that there is a risk of data loss caused by reset without any - guest OS shutdown. - """ - try: - self.domain.reset() - except libvirt.libvirtError as e: - raise InstanceError( - f'Cannot reset instance={self.name}: {e}' - ) from e - - def power_reset(self) -> None: - """ - Shutdown instance and start. - - By analogy with real hardware, this is a normal server shutdown, - and then turning off from the power supply and turning it on again. - - This method is applicable in cases where there has been a - configuration change in libvirt and you need to restart the - instance to apply the new configuration. - """ - self.shutdown(method='NORMAL') - self.start() - - def set_autostart(self, *, enabled: bool) -> None: - """ - Set autostart flag for instance. - - :param enabled: Bool argument to set or unset autostart flag. - """ - autostart = 1 if enabled else 0 - try: - self.domain.setAutostart(autostart) - except libvirt.libvirtError as e: - raise InstanceError( - f'Cannot set autostart flag for instance={self.name} ' - f'{autostart=}: {e}' - ) from e - - def set_vcpus(self, nvcpus: int, *, live: bool = False) -> None: - """ - Set vCPU number. - - If `live` is True and instance is not currently running vCPUs - will set in config and will applied when instance boot. - - NB: Note that if this call is executed before the guest has - finished booting, the guest may fail to process the change. - - :param nvcpus: Number of vCPUs - :param live: Affect a running instance - """ - if nvcpus <= 0: - raise InstanceError('Cannot set zero vCPUs') - if nvcpus > self.get_max_vcpus(): - raise InstanceError('vCPUs count is greather than max_vcpus') - if nvcpus == self.get_info().nproc: - log.warning( - 'Instance instance=%s already have %s vCPUs, nothing to do', - self.name, - nvcpus, - ) - return - try: - flags = libvirt.VIR_DOMAIN_AFFECT_CONFIG - self.domain.setVcpusFlags(nvcpus, flags=flags) - if live is True: - if not self.is_running(): - log.warning( - 'Instance is not running, changes applied in ' - 'instance config.' - ) - return - flags = libvirt.VIR_DOMAIN_AFFECT_LIVE - self.domain.setVcpusFlags(nvcpus, flags=flags) - if self.guest_agent.is_available(): - try: - self.guest_agent.raise_for_commands( - ['guest-set-vcpus'] - ) - flags = libvirt.VIR_DOMAIN_VCPU_GUEST - self.domain.setVcpusFlags(nvcpus, flags=flags) - except GuestAgentCommandNotSupportedError: - log.warning( - 'Cannot set vCPUs in guest via agent, you may ' - 'need to apply changes in guest manually.' - ) - else: - log.warning( - 'Cannot set vCPUs in guest OS on instance=%s. ' - 'You may need to apply CPUs in guest manually.', - self.name, - ) - except libvirt.libvirtError as e: - raise InstanceError( - f'Cannot set vCPUs for instance={self.name}: {e}' - ) from e - - def set_memory(self, memory: int, *, live: bool = False) -> None: - """ - Set memory. - - If `live` is True and instance is not currently running set memory - in config and will applied when instance boot. - - :param memory: Memory value in mebibytes - :param live: Affect a running instance - """ - if memory <= 0: - raise InstanceError('Cannot set zero memory') - if (memory * 1024) > self.get_max_memory(): - raise InstanceError('Memory is greather than max_memory') - if (memory * 1024) == self.get_info().memory: - log.warning( - "Instance '%s' already have %s memory, nothing to do", - self.name, - memory, - ) - return - if live and self.is_running(): - flags = ( - libvirt.VIR_DOMAIN_AFFECT_LIVE - | libvirt.VIR_DOMAIN_AFFECT_CONFIG - ) - else: - flags = libvirt.VIR_DOMAIN_AFFECT_CONFIG - try: - self.domain.setMemoryFlags(memory * 1024, flags=flags) - except libvirt.libvirtError as e: - msg = f'Cannot set memory for instance={self.name} {memory=}: {e}' - raise InstanceError(msg) from e - - def _get_disk_by_target(self, target: str) -> etree.Element: - xml = etree.fromstring(self.dump_xml()) # noqa: S320 - child = xml.xpath(f'/domain/devices/disk/target[@dev="{target}"]') - return child[0].getparent() if child else None - - def attach_device( - self, device: DeviceConfig, *, live: bool = False - ) -> None: - """ - Attach device to compute instance. - - :param device: Object with device description e.g. DiskConfig - :param live: Affect a running instance - """ - if live and self.is_running(): - flags = ( - libvirt.VIR_DOMAIN_AFFECT_LIVE - | libvirt.VIR_DOMAIN_AFFECT_CONFIG - ) - else: - flags = libvirt.VIR_DOMAIN_AFFECT_CONFIG - if isinstance(device, DiskConfig): # noqa: SIM102 - if self._get_disk_by_target(device.target): - log.warning( - "Volume with target '%s' is already attached", - device.target, - ) - return - self.domain.attachDeviceFlags(device.to_xml(), flags=flags) - - def detach_device( - self, device: DeviceConfig, *, live: bool = False - ) -> None: - """ - Dettach device from compute instance. - - :param device: Object with device description e.g. DiskConfig - :param live: Affect a running instance - """ - if live and self.is_running(): - flags = ( - libvirt.VIR_DOMAIN_AFFECT_LIVE - | libvirt.VIR_DOMAIN_AFFECT_CONFIG - ) - else: - flags = libvirt.VIR_DOMAIN_AFFECT_CONFIG - if isinstance(device, DiskConfig): # noqa: SIM102 - if self._get_disk_by_target(device.target) is None: - log.warning( - "Volume with target '%s' is already detached", - device.target, - ) - return - self.domain.detachDeviceFlags(device.to_xml(), flags=flags) - - def detach_disk(self, name: str) -> None: - """ - Detach disk device by target name. - - There is no ``attach_disk()`` method. Use :func:`attach_device` - with :class:`DiskConfig` as argument. - - :param name: Disk name e.g. 'vda', 'sda', etc. This name may - not match the name of the disk inside the guest OS. - """ - xml = self._get_disk_by_target(name) - if xml is None: - log.warning( - "Volume with target '%s' is already detached", - name, - ) - return - disk_params = { - 'disk_type': xml.get('type'), - 'source': xml.find('source').get('file'), - 'target': xml.find('target').get('dev'), - 'readonly': False if xml.find('readonly') is None else True, # noqa: SIM211 - } - for param in disk_params: - if disk_params[param] is None: - msg = ( - f"Cannot detach volume with target '{name}': " - f"parameter '{param}' is not defined in libvirt XML " - 'config on host.' - ) - raise InstanceError(msg) - self.detach_device(DiskConfig(**disk_params), live=True) - - def resize_disk( - self, name: str, capacity: int, unit: units.DataUnit - ) -> None: - """ - Resize attached block device. - - :param name: Disk device name e.g. `vda`, `sda`, etc. - :param capacity: New capacity. - :param unit: Capacity unit. - """ - self.domain.blockResize( - name, - units.to_bytes(capacity, unit=unit), - flags=libvirt.VIR_DOMAIN_BLOCK_RESIZE_BYTES, - ) - - def get_disks(self) -> list[DiskConfig]: - """Return list of attached disks.""" - raise NotImplementedError - - def pause(self) -> None: - """Pause instance.""" - if not self.is_running(): - raise InstanceError('Cannot pause inactive instance') - self.domain.suspend() - - def resume(self) -> None: - """Resume paused instance.""" - self.domain.resume() - - def get_ssh_keys(self, user: str) -> list[str]: - """ - Return list of SSH keys on guest for specific user. - - :param user: Username. - """ - raise NotImplementedError - - def set_ssh_keys(self, user: str, ssh_keys: list[str]) -> None: - """ - Add SSH keys to guest for specific user. - - :param user: Username. - :param ssh_keys: List of public SSH keys. - """ - raise NotImplementedError - - def delete_ssh_keys(self, user: str, ssh_keys: list[str]) -> None: - """ - Remove SSH keys from guest for specific user. - - :param user: Username. - :param ssh_keys: List of public SSH keys. - """ - raise NotImplementedError - - def set_user_password( - self, user: str, password: str, *, encrypted: bool = False - ) -> None: - """ - Set new user password in guest OS. - - This action performs by guest agent inside the guest. - - :param user: Username. - :param password: Password. - :param encrypted: Set it to True if password is already encrypted. - Right encryption method depends on guest OS. - """ - if not self.guest_agent.is_available(): - raise InstanceError( - 'Cannot change password: guest agent is unavailable' - ) - self.guest_agent.raise_for_commands(['guest-set-user-password']) - flags = libvirt.VIR_DOMAIN_PASSWORD_ENCRYPTED if encrypted else 0 - self.domain.setUserPassword(user, password, flags=flags) - - def dump_xml(self, *, inactive: bool = False) -> str: - """Return instance XML description.""" - flags = libvirt.VIR_DOMAIN_XML_INACTIVE if inactive else 0 - return self.domain.XMLDesc(flags) - - def delete(self) -> None: - """Undefine instance.""" - # TODO @ge: delete local disks - self.shutdown(method='HARD') - self.domain.undefine() diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute/usr/lib/python3/dist-packages/compute/instance/schemas.py b/packaging/build/compute-0.1.0.dev1/debian/compute/usr/lib/python3/dist-packages/compute/instance/schemas.py deleted file mode 100644 index f5a677c..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute/usr/lib/python3/dist-packages/compute/instance/schemas.py +++ /dev/null @@ -1,165 +0,0 @@ -# This file is part of Compute -# -# Compute is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Ansible is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Ansible. If not, see . - -"""Compute instance related objects schemas.""" - -import re -from enum import StrEnum -from pathlib import Path - -from pydantic import BaseModel, Extra, validator - -from compute.utils.units import DataUnit - - -class EntityModel(BaseModel): - """Basic entity model.""" - - class Config: - """Do not allow extra fields.""" - - extra = Extra.forbid - - -class CPUEmulationMode(StrEnum): - """CPU emulation mode enumerated.""" - - HOST_PASSTHROUGH = 'host-passthrough' - HOST_MODEL = 'host-model' - CUSTOM = 'custom' - MAXIMUM = 'maximum' - - -class CPUTopologySchema(EntityModel): - """CPU topology model.""" - - sockets: int - cores: int - threads: int - dies: int = 1 - - -class CPUFeaturesSchema(EntityModel): - """CPU features model.""" - - require: list[str] - disable: list[str] - - -class CPUSchema(EntityModel): - """CPU model.""" - - emulation_mode: CPUEmulationMode - model: str | None - vendor: str | None - topology: CPUTopologySchema | None - features: CPUFeaturesSchema | None - - -class VolumeType(StrEnum): - """Storage volume types enumeration.""" - - FILE = 'file' - - -class VolumeCapacitySchema(EntityModel): - """Storage volume capacity field model.""" - - value: int - unit: DataUnit - - -class VolumeSchema(EntityModel): - """Storage volume model.""" - - type: VolumeType # noqa: A003 - target: str - capacity: VolumeCapacitySchema - source: str | None = None - is_readonly: bool = False - is_system: bool = False - - -class NetworkInterfaceSchema(EntityModel): - """Network inerface model.""" - - source: str - mac: str - - -class BootOptionsSchema(EntityModel): - """Instance boot settings.""" - - order: tuple - - -class InstanceSchema(EntityModel): - """Compute instance model.""" - - name: str - title: str | None - description: str | None - memory: int - max_memory: int - vcpus: int - max_vcpus: int - cpu: CPUSchema - machine: str - emulator: Path - arch: str - boot: BootOptionsSchema - volumes: list[VolumeSchema] - network_interfaces: list[NetworkInterfaceSchema] - image: str | None = None - - @validator('name') - def _check_name(cls, value: str) -> str: # noqa: N805 - if not re.match(r'^[a-z0-9_]+$', value): - msg = ( - 'Name can contain only lowercase letters, numbers ' - 'and underscore.' - ) - raise ValueError(msg) - return value - - @validator('cpu') - def _check_topology(cls, cpu: int, values: dict) -> CPUSchema: # noqa: N805 - topo = cpu.topology - max_vcpus = values['max_vcpus'] - if topo and topo.sockets * topo.cores * topo.threads != max_vcpus: - msg = f'CPU topology does not match with {max_vcpus=}' - raise ValueError(msg) - return cpu - - @validator('volumes') - def _check_volumes(cls, volumes: list) -> list: # noqa: N805 - if len([v for v in volumes if v.is_system is True]) != 1: - msg = 'volumes list must contain one system volume' - raise ValueError(msg) - vol_with_source = 0 - for vol in volumes: - if vol.is_system is True and vol.is_readonly is True: - msg = 'volume marked as system cannot be readonly' - raise ValueError(msg) - if vol.source is not None: - vol_with_source += 1 - return volumes - - @validator('network_interfaces') - def _check_network_interfaces(cls, value: list) -> list: # noqa: N805 - if not value: - msg = 'Network interfaces list must contain at least one element' - raise ValueError(msg) - return value diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute/usr/lib/python3/dist-packages/compute/session.py b/packaging/build/compute-0.1.0.dev1/debian/compute/usr/lib/python3/dist-packages/compute/session.py deleted file mode 100644 index de5f900..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute/usr/lib/python3/dist-packages/compute/session.py +++ /dev/null @@ -1,286 +0,0 @@ -# This file is part of Compute -# -# Compute is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Ansible is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Ansible. If not, see . - -"""Hypervisor session manager.""" - -import logging -import os -from contextlib import AbstractContextManager -from types import TracebackType -from typing import Any, NamedTuple -from uuid import uuid4 - -import libvirt -from lxml import etree - -from .exceptions import ( - InstanceNotFoundError, - SessionError, - StoragePoolNotFoundError, -) -from .instance import Instance, InstanceConfig, InstanceSchema -from .storage import DiskConfig, StoragePool, VolumeConfig -from .utils import units - - -log = logging.getLogger(__name__) - - -class Capabilities(NamedTuple): - """Store domain capabilities info.""" - - arch: str - virt_type: str - emulator: str - machine: str - max_vcpus: int - cpu_vendor: str - cpu_model: str - cpu_features: dict - usable_cpus: list[dict] - - -class NodeInfo(NamedTuple): - """ - Store compute node info. - - See https://libvirt.org/html/libvirt-libvirt-host.html#virNodeInfo - NOTE: memory unit in libvirt docs is wrong! Actual unit is MiB. - """ - - arch: str - memory: int - cpus: int - mhz: int - nodes: int - sockets: int - cores: int - threads: int - - -class Session(AbstractContextManager): - """ - Hypervisor session context manager. - - :cvar IMAGES_POOL: images storage pool name taken from env - :cvar VOLUMES_POOL: volumes storage pool name taken from env - """ - - IMAGES_POOL = os.getenv('CMP_IMAGES_POOL') - VOLUMES_POOL = os.getenv('CMP_VOLUMES_POOL') - - def __init__(self, uri: str | None = None): - """ - Initialise session with hypervisor. - - :ivar str uri: libvirt connection URI. - :ivar libvirt.virConnect connection: libvirt connection object. - - :param uri: libvirt connection URI. - """ - self.uri = uri or 'qemu:///system' - self.connection = libvirt.open(self.uri) - - def __enter__(self): - """Return Session object.""" - return self - - def __exit__( - self, - exc_type: type[BaseException] | None, - exc_value: BaseException | None, - exc_traceback: TracebackType | None, - ): - """Close the connection when leaving the context.""" - self.close() - - def close(self) -> None: - """Close connection to libvirt daemon.""" - self.connection.close() - - def get_node_info(self) -> NodeInfo: - """Return information about compute node.""" - info = self.connection.getInfo() - return NodeInfo( - arch=info[0], - memory=info[1], - cpus=info[2], - mhz=info[3], - nodes=info[4], - sockets=info[5], - cores=info[6], - threads=info[7], - ) - - def _cap_get_usable_cpus(self, xml: etree.Element) -> list[dict]: - x = xml.xpath('/domainCapabilities/cpu/mode[@name="custom"]')[0] - cpus = [] - for cpu in x.findall('model'): - if cpu.get('usable') == 'yes': - cpus.append( # noqa: PERF401 - { - 'vendor': cpu.get('vendor'), - 'model': cpu.text, - } - ) - return cpus - - def _cap_get_cpu_features(self, xml: etree.Element) -> dict: - x = xml.xpath('/domainCapabilities/cpu/mode[@name="host-model"]')[0] - require = [] - disable = [] - for feature in x.findall('feature'): - policy = feature.get('policy') - name = feature.get('name') - if policy == 'require': - require.append(name) - if policy == 'disable': - disable.append(name) - return {'require': require, 'disable': disable} - - def get_capabilities(self) -> Capabilities: - """Return capabilities e.g. arch, virt, emulator, etc.""" - prefix = '/domainCapabilities' - hprefix = f'{prefix}/cpu/mode[@name="host-model"]' - caps = etree.fromstring(self.connection.getDomainCapabilities()) # noqa: S320 - return Capabilities( - arch=caps.xpath(f'{prefix}/arch/text()')[0], - virt_type=caps.xpath(f'{prefix}/domain/text()')[0], - emulator=caps.xpath(f'{prefix}/path/text()')[0], - machine=caps.xpath(f'{prefix}/machine/text()')[0], - max_vcpus=int(caps.xpath(f'{prefix}/vcpu/@max')[0]), - cpu_vendor=caps.xpath(f'{hprefix}/vendor/text()')[0], - cpu_model=caps.xpath(f'{hprefix}/model/text()')[0], - cpu_features=self._cap_get_cpu_features(caps), - usable_cpus=self._cap_get_cpus(caps), - ) - - def create_instance(self, **kwargs: Any) -> Instance: - """ - Create and return new compute instance. - - :param name: Instance name. - :type name: str - :param title: Instance title for humans. - :type title: str - :param description: Some information about instance. - :type description: str - :param memory: Memory in MiB. - :type memory: int - :param max_memory: Maximum memory in MiB. - :type max_memory: int - :param vcpus: Number of vCPUs. - :type vcpus: int - :param max_vcpus: Maximum vCPUs. - :type max_vcpus: int - :param cpu: CPU configuration. See :class:`CPUSchema` for info. - :type cpu: dict - :param machine: QEMU emulated machine. - :type machine: str - :param emulator: Path to emulator. - :type emulator: str - :param arch: CPU architecture to virtualization. - :type arch: str - :param boot: Boot settings. See :class:`BootOptionsSchema`. - :type boot: dict - :param image: Source disk image name for system disk. - :type image: str - :param volumes: List of storage volume configs. For more info - see :class:`VolumeSchema`. - :type volumes: list[dict] - :param network_interfaces: List of virtual network interfaces - configs. See :class:`NetworkInterfaceSchema` for more info. - :type network_interfaces: list[dict] - """ - data = InstanceSchema(**kwargs) - config = InstanceConfig(data) - log.info('Define XML...') - log.info(config.to_xml()) - self.connection.defineXML(config.to_xml()) - log.info('Getting instance...') - instance = self.get_instance(config.name) - log.info('Creating volumes...') - for volume in data.volumes: - log.info('Creating volume=%s', volume) - capacity = units.to_bytes( - volume.capacity.value, volume.capacity.unit - ) - log.info('Connecting to images pool...') - images_pool = self.get_storage_pool(self.IMAGES_POOL) - log.info('Connecting to volumes pool...') - volumes_pool = self.get_storage_pool(self.VOLUMES_POOL) - log.info('Building volume configuration...') - if not volume.source: - vol_name = f'{uuid4()}.qcow2' - else: - vol_name = volume.source - vol_conf = VolumeConfig( - name=vol_name, - path=str(volumes_pool.path.joinpath(vol_name)), - capacity=capacity, - ) - log.info('Volume configuration is:\n %s', vol_conf.to_xml()) - if volume.is_system is True and data.image: - log.info( - "Volume is marked as 'system', start cloning image..." - ) - log.info('Get image %s', data.image) - image = images_pool.get_volume(data.image) - log.info('Cloning image into volumes pool...') - vol = volumes_pool.clone_volume(image, vol_conf) - log.info( - 'Resize cloned volume to specified size: %s', - capacity, - ) - vol.resize(capacity, unit=units.DataUnit.BYTES) - else: - log.info('Create volume...') - volumes_pool.create_volume(vol_conf) - log.info('Attaching volume to instance...') - instance.attach_device( - DiskConfig( - disk_type=volume.type, - source=vol_conf.path, - target=volume.target, - readonly=volume.is_readonly, - ) - ) - return instance - - def get_instance(self, name: str) -> Instance: - """Get compute instance by name.""" - try: - return Instance(self.connection.lookupByName(name)) - except libvirt.libvirtError as e: - if e.get_error_code() == libvirt.VIR_ERR_NO_DOMAIN: - raise InstanceNotFoundError(name) from e - raise SessionError(e) from e - - def list_instances(self) -> list[Instance]: - """List all instances.""" - return [Instance(dom) for dom in self.connection.listAllDomains()] - - def get_storage_pool(self, name: str) -> StoragePool: - """Get storage pool by name.""" - try: - return StoragePool(self.connection.storagePoolLookupByName(name)) - except libvirt.libvirtError as e: - if e.get_error_code() == libvirt.VIR_ERR_NO_STORAGE_POOL: - raise StoragePoolNotFoundError(name) from e - raise SessionError(e) from e - - def list_storage_pools(self) -> list[StoragePool]: - """List all strage pools.""" - return [StoragePool(p) for p in self.connection.listStoragePools()] diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute/usr/lib/python3/dist-packages/compute/storage/__init__.py b/packaging/build/compute-0.1.0.dev1/debian/compute/usr/lib/python3/dist-packages/compute/storage/__init__.py deleted file mode 100644 index 34aae30..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute/usr/lib/python3/dist-packages/compute/storage/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -# This file is part of Compute -# -# Compute is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Ansible is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Ansible. If not, see . - -from .pool import StoragePool -from .volume import DiskConfig, Volume, VolumeConfig diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute/usr/lib/python3/dist-packages/compute/storage/pool.py b/packaging/build/compute-0.1.0.dev1/debian/compute/usr/lib/python3/dist-packages/compute/storage/pool.py deleted file mode 100644 index cb17494..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute/usr/lib/python3/dist-packages/compute/storage/pool.py +++ /dev/null @@ -1,124 +0,0 @@ -# This file is part of Compute -# -# Compute is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Ansible is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Ansible. If not, see . - -"""Manage storage pools.""" - -import logging -from pathlib import Path -from typing import NamedTuple - -import libvirt -from lxml import etree - -from compute.exceptions import StoragePoolError, VolumeNotFoundError - -from .volume import Volume, VolumeConfig - - -log = logging.getLogger(__name__) - - -class StoragePoolUsageInfo(NamedTuple): - """Storage pool usage info.""" - - capacity: int - allocation: int - available: int - - -class StoragePool: - """Storage pool manipulating class.""" - - def __init__(self, pool: libvirt.virStoragePool): - """Initislise StoragePool.""" - self.pool = pool - self.name = pool.name() - self.path = self._get_path() - - def _get_path(self) -> Path: - """Return storage pool path.""" - xml = etree.fromstring(self.pool.XMLDesc()) # noqa: S320 - return Path(xml.xpath('/pool/target/path/text()')[0]) - - def get_usage_info(self) -> StoragePoolUsageInfo: - """Return info about storage pool usage.""" - xml = etree.fromstring(self.pool.XMLDesc()) # noqa: S320 - return StoragePoolUsageInfo( - capacity=int(xml.xpath('/pool/capacity/text()')[0]), - allocation=int(xml.xpath('/pool/allocation/text()')[0]), - available=int(xml.xpath('/pool/available/text()')[0]), - ) - - def dump_xml(self) -> str: - """Return storage pool XML description as string.""" - return self.pool.XMLDesc() - - def refresh(self) -> None: - """Refresh storage pool.""" - # TODO @ge: handle libvirt asynchronous job related exceptions - self.pool.refresh() - - def create_volume(self, vol_conf: VolumeConfig) -> Volume: - """Create storage volume and return Volume instance.""" - log.info( - 'Create storage volume vol=%s in pool=%s', vol_conf.name, self.name - ) - vol = self.pool.createXML( - vol_conf.to_xml(), - flags=libvirt.VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA, - ) - return Volume(self.pool, vol) - - def clone_volume(self, src: Volume, dst: VolumeConfig) -> Volume: - """ - Make storage volume copy. - - :param src: Input volume - :param dst: Output volume config - """ - log.info( - 'Start volume cloning ' - 'src_pool=%s src_vol=%s dst_pool=%s dst_vol=%s', - src.pool_name, - src.name, - self.pool.name, - dst.name, - ) - vol = self.pool.createXMLFrom( - dst.to_xml(), # new volume XML description - src.vol, # source volume virStorageVol object - flags=libvirt.VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA, - ) - if vol is None: - raise StoragePoolError - return Volume(self.pool, vol) - - def get_volume(self, name: str) -> Volume | None: - """Lookup and return Volume instance or None.""" - log.info( - 'Lookup for storage volume vol=%s in pool=%s', name, self.pool.name - ) - try: - vol = self.pool.storageVolLookupByName(name) - return Volume(self.pool, vol) - except libvirt.libvirtError as e: - if e.get_error_code() == libvirt.VIR_ERR_NO_STORAGE_VOL: - raise VolumeNotFoundError(name) from e - log.exception('unexpected error from libvirt') - raise StoragePoolError(e) from e - - def list_volumes(self) -> list[Volume]: - """Return list of volumes in storage pool.""" - return [Volume(self.pool, vol) for vol in self.pool.listAllVolumes()] diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute/usr/lib/python3/dist-packages/compute/storage/volume.py b/packaging/build/compute-0.1.0.dev1/debian/compute/usr/lib/python3/dist-packages/compute/storage/volume.py deleted file mode 100644 index 11a1dc4..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute/usr/lib/python3/dist-packages/compute/storage/volume.py +++ /dev/null @@ -1,138 +0,0 @@ -# This file is part of Compute -# -# Compute is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Ansible is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Ansible. If not, see . - -"""Manage storage volumes.""" - -from dataclasses import dataclass -from pathlib import Path -from time import time - -import libvirt -from lxml import etree -from lxml.builder import E - -from compute.common import DeviceConfig, EntityConfig -from compute.utils import units - - -@dataclass -class VolumeConfig(EntityConfig): - """ - Storage volume XML config builder. - - Generate XML config for creating a volume in a libvirt - storage pool. - """ - - name: str - path: str - capacity: int - - def to_xml(self) -> str: - """Return XML config for libvirt.""" - unixtime = str(int(time())) - xml = E.volume(type='file') - xml.append(E.name(self.name)) - xml.append(E.key(self.path)) - xml.append(E.source()) - xml.append(E.capacity(str(self.capacity), unit='bytes')) - xml.append(E.allocation('0')) - xml.append( - E.target( - E.path(self.path), - E.format(type='qcow2'), - E.timestamps( - E.atime(unixtime), E.mtime(unixtime), E.ctime(unixtime) - ), - E.compat('1.1'), - E.features(E.lazy_refcounts()), - ) - ) - return etree.tostring(xml, encoding='unicode', pretty_print=True) - - -@dataclass -class DiskConfig(DeviceConfig): - """ - Disk XML config builder. - - Generate XML config for attaching or detaching storage volumes - to compute instances. - """ - - disk_type: str - source: str | Path - target: str - readonly: bool = False - - def to_xml(self) -> str: - """Return XML config for libvirt.""" - xml = E.disk(type=self.disk_type, device='disk') - xml.append(E.driver(name='qemu', type='qcow2', cache='writethrough')) - if self.disk_type == 'file': - xml.append(E.source(file=str(self.source))) - xml.append(E.target(dev=self.target, bus='virtio')) - if self.readonly: - xml.append(E.readonly()) - return etree.tostring(xml, encoding='unicode', pretty_print=True) - - -class Volume: - """Storage volume manipulating class.""" - - def __init__( - self, pool: libvirt.virStoragePool, vol: libvirt.virStorageVol - ): - """ - Initialise Volume. - - :param pool: libvirt virStoragePool object - :param vol: libvirt virStorageVol object - """ - self.pool = pool - self.pool_name = pool.name() - self.vol = vol - self.name = vol.name() - self.path = Path(vol.path()) - - def dump_xml(self) -> str: - """Return volume XML description as string.""" - return self.vol.XMLDesc() - - def clone(self, vol_conf: VolumeConfig) -> None: - """ - Make a copy of volume to the same storage pool. - - :param vol_info VolumeInfo: New storage volume dataclass object - """ - self.pool.createXMLFrom( - vol_conf.to_xml(), - self.vol, - flags=libvirt.VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA, - ) - - def resize(self, capacity: int, unit: units.DataUnit) -> None: - """ - Resize volume. - - :param capacity int: Volume new capacity. - :param unit DataUnit: Data unit. Internally converts into bytes. - """ - # TODO @ge: Check actual volume size before resize - self.vol.resize(units.to_bytes(capacity, unit=unit)) - - def delete(self) -> None: - """Delete volume from storage pool.""" - self.vol.delete() diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute/usr/lib/python3/dist-packages/compute/utils/__init__.py b/packaging/build/compute-0.1.0.dev1/debian/compute/usr/lib/python3/dist-packages/compute/utils/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute/usr/lib/python3/dist-packages/compute/utils/config_loader.py b/packaging/build/compute-0.1.0.dev1/debian/compute/usr/lib/python3/dist-packages/compute/utils/config_loader.py deleted file mode 100644 index aaeb0fe..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute/usr/lib/python3/dist-packages/compute/utils/config_loader.py +++ /dev/null @@ -1,56 +0,0 @@ -# This file is part of Compute -# -# Compute is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Ansible is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Ansible. If not, see . - -"""Configuration loader.""" - -import tomllib -from collections import UserDict -from pathlib import Path - -from compute.exceptions import ConfigLoaderError - - -DEFAULT_CONFIGURATION = {} -DEFAULT_CONFIG_FILE = '/etc/computed/computed.toml' - - -class ConfigLoader(UserDict): - """UserDict for storing configuration.""" - - def __init__(self, file: Path | None = None): - """ - Initialise ConfigLoader. - - :param file: Path to configuration file. If `file` is None - use default path from DEFAULT_CONFIG_FILE constant. - """ - # TODO @ge: load deafult configuration - self.file = Path(file) if file else Path(DEFAULT_CONFIG_FILE) - super().__init__(self.load()) - - def load(self) -> dict: - """Load confguration object from TOML file.""" - try: - with Path(self.file).open('rb') as configfile: - return tomllib.load(configfile) - # TODO @ge: add config schema validation - except tomllib.TOMLDecodeError as tomlerr: - raise ConfigLoaderError( - f'Bad TOML syntax in config file: {self.file}: {tomlerr}' - ) from tomlerr - except (OSError, ValueError) as readerr: - raise ConfigLoaderError( - f'Cannot read config file: {self.file}: {readerr}' - ) from readerr diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute/usr/lib/python3/dist-packages/compute/utils/ids.py b/packaging/build/compute-0.1.0.dev1/debian/compute/usr/lib/python3/dist-packages/compute/utils/ids.py deleted file mode 100644 index 8a6454a..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute/usr/lib/python3/dist-packages/compute/utils/ids.py +++ /dev/null @@ -1,33 +0,0 @@ -# This file is part of Compute -# -# Compute is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Ansible is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Ansible. If not, see . - -"""Random identificators.""" - -# ruff: noqa: S311, C417 - -import random - - -def random_mac() -> str: - """Retrun random MAC address.""" - mac = [ - 0x00, - 0x16, - 0x3E, - random.randint(0x00, 0x7F), - random.randint(0x00, 0xFF), - random.randint(0x00, 0xFF), - ] - return ':'.join(map(lambda x: '%02x' % x, mac)) diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute/usr/lib/python3/dist-packages/compute/utils/units.py b/packaging/build/compute-0.1.0.dev1/debian/compute/usr/lib/python3/dist-packages/compute/utils/units.py deleted file mode 100644 index 57a4583..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute/usr/lib/python3/dist-packages/compute/utils/units.py +++ /dev/null @@ -1,54 +0,0 @@ -# This file is part of Compute -# -# Compute is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Ansible is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Ansible. If not, see . - -"""Tools for data units convertion.""" - -from enum import StrEnum - - -class DataUnit(StrEnum): - """Data units enumerated.""" - - BYTES = 'bytes' - KIB = 'KiB' - MIB = 'MiB' - GIB = 'GiB' - TIB = 'TiB' - - -class InvalidDataUnitError(ValueError): - """Data unit is not valid.""" - - def __init__(self, msg: str): - """Initialise InvalidDataUnitError.""" - super().__init__( - f'{msg}, valid units are: {", ".join(list(DataUnit))}' - ) - - -def to_bytes(value: int, unit: DataUnit = DataUnit.BYTES) -> int: - """Convert value to bytes. See :class:`DataUnit`.""" - try: - _ = DataUnit(unit) - except ValueError as e: - raise InvalidDataUnitError(e) from e - powers = { - DataUnit.BYTES: 0, - DataUnit.KIB: 1, - DataUnit.MIB: 2, - DataUnit.GIB: 3, - DataUnit.TIB: 4, - } - return value * pow(1024, powers[unit]) diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute/usr/share/bash-completion/completions/compute b/packaging/build/compute-0.1.0.dev1/debian/compute/usr/share/bash-completion/completions/compute deleted file mode 100644 index a0dcdf2..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute/usr/share/bash-completion/completions/compute +++ /dev/null @@ -1,93 +0,0 @@ -# compute bash completion script - -_compute_root_cmd=" - --version - --verbose - --connect - --log-level - init - exec - ls - start - shutdown - reboot - reset - powrst - pause - resume - status - setvcpus - setmem - setpasswd" -_compute_init_opts="" -_compute_exec_opts=" - --timeout - --executable - --env - --no-join-args" -_compute_ls_opts="" -_compute_start_opts="" -_compute_shutdown_opts="--method" -_compute_reboot_opts="" -_compute_reset_opts="" -_compute_powrst_opts="" -_compute_pause_opts="" -_compute_resume_opts="" -_compute_status_opts="" -_compute_setvcpus_opts="" -_compute_setmem_opts="" -_compute_setpasswd_opts="--encrypted" - -_compute_complete_instances() -{ - for file in /etc/libvirt/qemu/*.xml; do - nodir="${file##*/}" - printf '%s ' "${nodir//\.xml}" - done -} - -_compute_compreply() -{ - if [[ "$current" = [a-z]* ]]; then - _compute_compwords="$(_compute_complete_instances)" - else - _compute_compwords="$*" - fi - COMPREPLY=($(compgen -W "$_compute_compwords" -- "$current")) -} - -_compute_complete() -{ - local current previous nshift - current="${COMP_WORDS[COMP_CWORD]}" - case "$COMP_CWORD" in - 1) COMPREPLY=($(compgen -W "$_compute_root_cmd" -- "$current")) - ;; - 2|3|4|5) - nshift=$((COMP_CWORD-1)) - previous="${COMP_WORDS[COMP_CWORD-nshift]}" - case "$previous" in - init) COMPREPLY=($(compgen -f -- "$current"));; - exec) _compute_compreply "$_compute_exec_opts";; - ls) COMPREPLY=($(compgen -W "$_compute_ls_opts" -- "$current"));; - start) _compute_compreply "$_compute_start_opts";; - shutdown) _compute_compreply "$_compute_shutdown_opts";; - reboot) _compute_compreply "$_compute_reboot_opts";; - reset) _compute_compreply "$_compute_reset_opts";; - powrst) _compute_compreply "$_compute_powrst_opts";; - pause) _compute_compreply "$_compute_pause_opts";; - resume) _compute_compreply "$_compute_resume_opts";; - status) _compute_compreply "$_compute_status_opts";; - setvcpus) _compute_compreply "$_compute_setvcpus_opts";; - setmem) _compute_compreply "$_compute_setmem_opts";; - setpasswd) _compute_compreply "$_compute_setpasswd_opts";; - *) COMPREPLY=() - esac - ;; - *) COMPREPLY=($(compgen -W "$_compute_compwords" -- "$current")) - esac -} - -complete -F _compute_complete compute - -# vim: ft=bash diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute/usr/share/doc/compute/README.md b/packaging/build/compute-0.1.0.dev1/debian/compute/usr/share/doc/compute/README.md deleted file mode 100644 index 0131e8e..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute/usr/share/doc/compute/README.md +++ /dev/null @@ -1,65 +0,0 @@ -# Compute - -Compute instances management library and tools. - -## Docs - -Run `make serve-docs`. See [Development](#development) below. - -## Roadmap - -- [x] Create instances -- [ ] CDROM -- [ ] cloud-init for provisioning instances -- [x] Instance power management -- [x] Instance pause and resume -- [x] vCPU hotplug -- [x] Memory hotplug -- [x] Hot disk resize [not tested] -- [ ] CPU topology customization -- [x] CPU customization (emulation mode, model, vendor, features) -- [ ] BIOS/UEFI settings -- [x] Device attaching -- [x] Device detaching -- [ ] GPU passthrough -- [ ] CPU guarantied resource percent support -- [x] QEMU Guest Agent management -- [ ] Instance resources usage stats -- [ ] SSH-keys management -- [x] Setting user passwords in guest -- [x] QCOW2 disks support -- [ ] ZVOL support -- [ ] Network disks support -- [ ] Images service integration (Images service is not implemented yet) -- [ ] Manage storage pools -- [ ] Idempotency -- [ ] CLI [in progress] -- [ ] HTTP API -- [ ] Instance migrations -- [ ] Instance snapshots -- [ ] Instance backups -- [ ] LXC - -## Development - -Python 3.11+ is required. - -Install [poetry](https://python-poetry.org/), clone this repository and run: - -``` -poetry install --with dev --with docs -``` - -# Build Debian package - -Install Docker first, then run: - -``` -make build-deb -``` - -`compute` and `compute-doc` packages will built. See packaging/build directory. Packages can be installed via `dpkg` or `apt-get`: - -``` -apt-get install ./compute*.deb -``` diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute/usr/share/doc/compute/changelog.Debian.gz b/packaging/build/compute-0.1.0.dev1/debian/compute/usr/share/doc/compute/changelog.Debian.gz deleted file mode 100644 index 40eae6f..0000000 Binary files a/packaging/build/compute-0.1.0.dev1/debian/compute/usr/share/doc/compute/changelog.Debian.gz and /dev/null differ diff --git a/packaging/build/compute-0.1.0.dev1/debian/compute/usr/share/doc/compute/copyright b/packaging/build/compute-0.1.0.dev1/debian/compute/usr/share/doc/compute/copyright deleted file mode 100644 index 185dcbf..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/compute/usr/share/doc/compute/copyright +++ /dev/null @@ -1,32 +0,0 @@ -Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ -Source: https://git.lulzette.ru/hstack/compute -Upstream-Name: compute - -Files: - * -Copyright: - 2023 ge -License: GPL-3.0+ - -Files: - debian/* -Copyright: - 2023 ge -License: GPL-3.0+ - -License: GPL-3.0+ - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - . - This package is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - . - You should have received a copy of the GNU General Public License - along with this program. If not, see . -Comment: - On Debian systems, the complete text of the GNU General - Public License version 3 can be found in "/usr/share/common-licenses/GPL-3". diff --git a/packaging/build/compute-0.1.0.dev1/debian/control b/packaging/build/compute-0.1.0.dev1/debian/control deleted file mode 100644 index 6b99835..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/control +++ /dev/null @@ -1,48 +0,0 @@ -Source: compute -Section: admin -Priority: optional -Maintainer: ge -Rules-Requires-Root: no -Build-Depends: - debhelper-compat (= 13), - dh-sequence-python3, - bash-completion, - pybuild-plugin-pyproject, - python3-poetry-core, - python3-setuptools, - python3-all, - python3-sphinx, - python3-sphinx-multiversion, - python3-libvirt, - python3-lxml, - python3-yaml, - python3-pydantic -Standards-Version: 4.6.2 -Homepage: https://git.lulzette.ru/hstack/compute - -Package: compute -Architecture: all -Depends: - ${python3:Depends}, - ${misc:Depends}, - qemu-system, - qemu-utils, - libvirt-daemon-system, - libvirt-clients, - python3-libvirt, - python3-lxml, - python3-yaml, - python3-pydantic -Recommends: - dnsmasq -Suggests: - compute-doc -Description: Compute instances management library and tools (Python 3) - -Package: compute-doc -Section: doc -Architecture: all -Depends: - ${sphinxdoc:Depends}, - ${misc:Depends}, -Description: Compute instances management library and tools (documentation) diff --git a/packaging/build/compute-0.1.0.dev1/debian/copyright b/packaging/build/compute-0.1.0.dev1/debian/copyright deleted file mode 100644 index 185dcbf..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/copyright +++ /dev/null @@ -1,32 +0,0 @@ -Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ -Source: https://git.lulzette.ru/hstack/compute -Upstream-Name: compute - -Files: - * -Copyright: - 2023 ge -License: GPL-3.0+ - -Files: - debian/* -Copyright: - 2023 ge -License: GPL-3.0+ - -License: GPL-3.0+ - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - . - This package is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - . - You should have received a copy of the GNU General Public License - along with this program. If not, see . -Comment: - On Debian systems, the complete text of the GNU General - Public License version 3 can be found in "/usr/share/common-licenses/GPL-3". diff --git a/packaging/build/compute-0.1.0.dev1/debian/debhelper-build-stamp b/packaging/build/compute-0.1.0.dev1/debian/debhelper-build-stamp deleted file mode 100644 index 3445b01..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/debhelper-build-stamp +++ /dev/null @@ -1,2 +0,0 @@ -compute -compute-doc diff --git a/packaging/build/compute-0.1.0.dev1/debian/docs b/packaging/build/compute-0.1.0.dev1/debian/docs deleted file mode 100644 index b43bf86..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/docs +++ /dev/null @@ -1 +0,0 @@ -README.md diff --git a/packaging/build/compute-0.1.0.dev1/debian/files b/packaging/build/compute-0.1.0.dev1/debian/files deleted file mode 100644 index e63edf9..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/files +++ /dev/null @@ -1,3 +0,0 @@ -compute-doc_0.1.0.dev1-1_all.deb doc optional -compute_0.1.0.dev1-1_all.deb admin optional -compute_0.1.0.dev1-1_amd64.buildinfo admin optional diff --git a/packaging/build/compute-0.1.0.dev1/debian/rules b/packaging/build/compute-0.1.0.dev1/debian/rules deleted file mode 100755 index f99ef32..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/rules +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/make -f - -export DH_VERBOSE = 1 -export PYBUILD_DESTDIR_python3=debian/compute - -%: - dh $@ --with python3,sphinxdoc,bash-completion --buildsystem=pybuild - -override_dh_auto_test: - @echo No tests there - -override_dh_sphinxdoc: -ifeq (,$(findstring nodoc, $(DEB_BUILD_OPTIONS))) - http_proxy=127.0.0.1:9 https_proxy=127.0.0.1:9 \ - HTTP_PROXY=127.0.0.1:9 HTTPS_PROXY=127.0.0.1:9 \ - PYTHONPATH=. PYTHON=python3 python3 -m sphinx $(SPHINXOPTS) -b html \ - ../docs/source \ - $(CURDIR)/debian/compute-doc/usr/share/doc/compute-doc/html - dh_sphinxdoc -endif diff --git a/packaging/build/compute-0.1.0.dev1/debian/source/format b/packaging/build/compute-0.1.0.dev1/debian/source/format deleted file mode 100644 index 163aaf8..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/source/format +++ /dev/null @@ -1 +0,0 @@ -3.0 (quilt) diff --git a/packaging/build/compute-0.1.0.dev1/debian/source/options b/packaging/build/compute-0.1.0.dev1/debian/source/options deleted file mode 100644 index cb61fa5..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/source/options +++ /dev/null @@ -1 +0,0 @@ -extend-diff-ignore = "^[^/]*[.]egg-info/" diff --git a/packaging/build/compute-0.1.0.dev1/debian/upstream/metadata.ex b/packaging/build/compute-0.1.0.dev1/debian/upstream/metadata.ex deleted file mode 100644 index 3fc47cc..0000000 --- a/packaging/build/compute-0.1.0.dev1/debian/upstream/metadata.ex +++ /dev/null @@ -1,10 +0,0 @@ -# Example file for upstream/metadata. -# See https://wiki.debian.org/UpstreamMetadata for more info/fields. -# Below an example based on a github project. - -# Bug-Database: https://github.com//compute/issues -# Bug-Submit: https://github.com//compute/issues/new -# Changelog: https://github.com//compute/blob/master/CHANGES -# Documentation: https://github.com//compute/wiki -# Repository-Browse: https://github.com//compute -# Repository: https://github.com//compute.git diff --git a/packaging/build/compute-0.1.0.dev1/pyproject.toml b/packaging/build/compute-0.1.0.dev1/pyproject.toml deleted file mode 100644 index f7aab25..0000000 --- a/packaging/build/compute-0.1.0.dev1/pyproject.toml +++ /dev/null @@ -1,61 +0,0 @@ -[tool.poetry] -name = 'compute' -version = '0.1.0-dev1' -description = 'Compute instances management library and tools' -authors = ['ge '] -readme = 'README.md' - -[tool.poetry.dependencies] -python = '^3.11' -libvirt-python = '9.0.0' -lxml = '^4.9.2' -pydantic = '1.10.4' -pyyaml = "^6.0.1" - -[tool.poetry.scripts] -compute = 'compute.cli.control:cli' - -[tool.poetry.group.dev.dependencies] -ruff = '^0.1.3' -isort = '^5.12.0' - -[tool.poetry.group.docs.dependencies] -sphinx = '^7.2.6' -sphinx-autobuild = '^2021.3.14' -sphinx-multiversion = '^0.2.4' - -[build-system] -requires = ['poetry-core'] -build-backend = 'poetry.core.masonry.api' - -[tool.isort] -skip = ['.gitignore'] -lines_after_imports = 2 -include_trailing_comma = true -split_on_trailing_comma = true - -[tool.ruff] -line-length = 79 -indent-width = 4 -target-version = 'py311' - -[tool.ruff.lint] -select = ['ALL'] -ignore = [ - 'Q000', 'Q003', 'D211', 'D212', - 'ANN101', 'ISC001', 'COM812', - 'D203', 'ANN204', 'T201', - 'EM102', 'TRY003', 'EM101', - 'TD003', 'TD006', 'FIX002', # 'todo' strings linting -] -exclude = ['__init__.py'] - -[tool.ruff.lint.flake8-annotations] -mypy-init-return = true -allow-star-arg-any = true - -[tool.ruff.format] -quote-style = 'single' - -[tool.ruff.isort] -lines-after-imports = 2 diff --git a/packaging/build/compute-doc_0.1.0.dev1-1_all.deb b/packaging/build/compute-doc_0.1.0.dev1-1_all.deb deleted file mode 100644 index c855890..0000000 Binary files a/packaging/build/compute-doc_0.1.0.dev1-1_all.deb and /dev/null differ diff --git a/packaging/build/compute_0.1.0.dev1-1.debian.tar.xz b/packaging/build/compute_0.1.0.dev1-1.debian.tar.xz deleted file mode 100644 index ce59eb3..0000000 Binary files a/packaging/build/compute_0.1.0.dev1-1.debian.tar.xz and /dev/null differ diff --git a/packaging/build/compute_0.1.0.dev1-1.dsc b/packaging/build/compute_0.1.0.dev1-1.dsc deleted file mode 100644 index 91f1fdb..0000000 --- a/packaging/build/compute_0.1.0.dev1-1.dsc +++ /dev/null @@ -1,21 +0,0 @@ -Format: 3.0 (quilt) -Source: compute -Binary: compute, compute-doc -Architecture: all -Version: 0.1.0.dev1-1 -Maintainer: ge -Homepage: https://git.lulzette.ru/hstack/compute -Standards-Version: 4.6.2 -Build-Depends: debhelper-compat (= 13), dh-sequence-python3, bash-completion, pybuild-plugin-pyproject, python3-poetry-core, python3-setuptools, python3-all, python3-sphinx, python3-sphinx-multiversion, python3-libvirt, python3-lxml, python3-yaml, python3-pydantic -Package-List: - compute deb admin optional arch=all - compute-doc deb doc optional arch=all -Checksums-Sha1: - 94be605a5a0ca8b0ea93a46dd5029a2513486190 20824 compute_0.1.0.dev1.orig.tar.gz - 57790e9df9659f913fa1da65b77c84a3aba4976c 2660 compute_0.1.0.dev1-1.debian.tar.xz -Checksums-Sha256: - e310d2ddbdb334737efc7adcc98eac2db2f158e5e989ddfade2ddfae07a6174d 20824 compute_0.1.0.dev1.orig.tar.gz - c9e267c79fa5a9e06625ac0502af528f4b526c74035fa23e6933c2c8e6429ab2 2660 compute_0.1.0.dev1-1.debian.tar.xz -Files: - de78bd5eecc56034a990dd9395a089c4 20824 compute_0.1.0.dev1.orig.tar.gz - cb9d6978a83d7f1842063315333d6278 2660 compute_0.1.0.dev1-1.debian.tar.xz diff --git a/packaging/build/compute_0.1.0.dev1-1_all.deb b/packaging/build/compute_0.1.0.dev1-1_all.deb deleted file mode 100644 index d448f22..0000000 Binary files a/packaging/build/compute_0.1.0.dev1-1_all.deb and /dev/null differ diff --git a/packaging/build/compute_0.1.0.dev1-1_amd64.buildinfo b/packaging/build/compute_0.1.0.dev1-1_amd64.buildinfo deleted file mode 100644 index 4110b27..0000000 --- a/packaging/build/compute_0.1.0.dev1-1_amd64.buildinfo +++ /dev/null @@ -1,270 +0,0 @@ -Format: 1.0 -Source: compute -Binary: compute compute-doc -Architecture: all source -Version: 0.1.0.dev1-1 -Checksums-Md5: - 635eae482cdff5bbe99a3911ed9e915c 1123 compute_0.1.0.dev1-1.dsc - 8a8c6490cb363870735ec2572cf65cdf 40424 compute-doc_0.1.0.dev1-1_all.deb - bf5fb2ffd00e5373a54461f34b2d7033 21644 compute_0.1.0.dev1-1_all.deb -Checksums-Sha1: - 455d0b203d96d97d4272d30be72c27cdde50fdc5 1123 compute_0.1.0.dev1-1.dsc - ef2a0d6dd481adc0cf4bed1a2bc98536f1795adf 40424 compute-doc_0.1.0.dev1-1_all.deb - 20ecb0342e494a426634a5124462e49c6f7fd2c9 21644 compute_0.1.0.dev1-1_all.deb -Checksums-Sha256: - c92ba4e3db43b496e01aa912f6e59240f7cd647b8b4950005182d91d071e31ac 1123 compute_0.1.0.dev1-1.dsc - 1c0d14fc87885f5dafe8bcbe1b6a07d9a57ce2dc0943a9837f751e3142ee8a42 40424 compute-doc_0.1.0.dev1-1_all.deb - a1f5a032f653276be3e4dc43818d663850463167a2b4b39138e184be1dabb44f 21644 compute_0.1.0.dev1-1_all.deb -Build-Origin: Debian -Build-Architecture: amd64 -Build-Date: Wed, 22 Nov 2023 23:06:48 +0000 -Build-Tainted-By: - merged-usr-via-aliased-dirs -Installed-Build-Depends: - autoconf (= 2.71-3), - automake (= 1:1.16.5-1.3), - autopoint (= 0.21-12), - autotools-dev (= 20220109.1), - base-files (= 12.4+deb12u2), - base-passwd (= 3.6.1), - bash (= 5.2.15-2+b2), - bash-completion (= 1:2.11-6), - binutils (= 2.40-2), - binutils-common (= 2.40-2), - binutils-x86-64-linux-gnu (= 2.40-2), - bsdextrautils (= 2.38.1-5+b1), - bsdutils (= 1:2.38.1-5+b1), - build-essential (= 12.9), - bzip2 (= 1.0.8-5+b1), - ca-certificates (= 20230311), - coreutils (= 9.1-1), - cpp (= 4:12.2.0-3), - cpp-12 (= 12.2.0-14), - dash (= 0.5.12-2), - debconf (= 1.5.82), - debhelper (= 13.11.4), - debianutils (= 5.7-0.5~deb12u1), - dh-autoreconf (= 20), - dh-python (= 5.20230130+deb12u1), - dh-strip-nondeterminism (= 1.13.1-1), - diffutils (= 1:3.8-4), - docutils-common (= 0.19+dfsg-6), - dpkg (= 1.21.22), - dpkg-dev (= 1.21.22), - dwz (= 0.15-1), - file (= 1:5.44-3), - findutils (= 4.9.0-4), - g++ (= 4:12.2.0-3), - g++-12 (= 12.2.0-14), - gcc (= 4:12.2.0-3), - gcc-12 (= 12.2.0-14), - gcc-12-base (= 12.2.0-14), - gettext (= 0.21-12), - gettext-base (= 0.21-12), - grep (= 3.8-5), - groff-base (= 1.22.4-10), - gzip (= 1.12-1), - hostname (= 3.23+nmu1), - init-system-helpers (= 1.65.2), - intltool-debian (= 0.35.0+20060710.6), - libacl1 (= 2.3.1-3), - libapparmor1 (= 3.0.8-3), - libarchive-zip-perl (= 1.68-1), - libasan8 (= 12.2.0-14), - libatomic1 (= 12.2.0-14), - libattr1 (= 1:2.5.1-4), - libaudit-common (= 1:3.0.9-1), - libaudit1 (= 1:3.0.9-1), - libbinutils (= 2.40-2), - libblkid1 (= 2.38.1-5+b1), - libbrotli1 (= 1.0.9-2+b6), - libbz2-1.0 (= 1.0.8-5+b1), - libc-bin (= 2.36-9+deb12u3), - libc-dev-bin (= 2.36-9+deb12u3), - libc6 (= 2.36-9+deb12u3), - libc6-dev (= 2.36-9+deb12u3), - libcap-ng0 (= 0.8.3-1+b3), - libcap2 (= 1:2.66-4), - libcc1-0 (= 12.2.0-14), - libcom-err2 (= 1.47.0-2), - libcrypt-dev (= 1:4.4.33-2), - libcrypt1 (= 1:4.4.33-2), - libctf-nobfd0 (= 2.40-2), - libctf0 (= 2.40-2), - libcurl3-gnutls (= 7.88.1-10+deb12u4), - libdb5.3 (= 5.3.28+dfsg2-1), - libdebconfclient0 (= 0.270), - libdebhelper-perl (= 13.11.4), - libdpkg-perl (= 1.21.22), - libelf1 (= 0.188-2.1), - libexpat1 (= 2.5.0-1), - libffi8 (= 3.4.4-1), - libfile-stripnondeterminism-perl (= 1.13.1-1), - libgcc-12-dev (= 12.2.0-14), - libgcc-s1 (= 12.2.0-14), - libgcrypt20 (= 1.10.1-3), - libgdbm-compat4 (= 1.23-3), - libgdbm6 (= 1.23-3), - libglib2.0-0 (= 2.74.6-2), - libgmp10 (= 2:6.2.1+dfsg1-1.1), - libgnutls30 (= 3.7.9-2), - libgomp1 (= 12.2.0-14), - libgpg-error0 (= 1.46-1), - libgprofng0 (= 2.40-2), - libgssapi-krb5-2 (= 1.20.1-2+deb12u1), - libhogweed6 (= 3.8.1-2), - libicu72 (= 72.1-3), - libidn2-0 (= 2.3.3-1+b1), - libisl23 (= 0.25-1), - libitm1 (= 12.2.0-14), - libjansson4 (= 2.14-2), - libjs-jquery (= 3.6.1+dfsg+~3.5.14-1), - libjs-sphinxdoc (= 5.3.0-4), - libjs-underscore (= 1.13.4~dfsg+~1.11.4-3), - libjson-perl (= 4.10000-1), - libk5crypto3 (= 1.20.1-2+deb12u1), - libkeyutils1 (= 1.6.3-2), - libkrb5-3 (= 1.20.1-2+deb12u1), - libkrb5support0 (= 1.20.1-2+deb12u1), - libldap-2.5-0 (= 2.5.13+dfsg-5), - liblsan0 (= 12.2.0-14), - liblz4-1 (= 1.9.4-1), - liblzma5 (= 5.4.1-0.2), - libmagic-mgc (= 1:5.44-3), - libmagic1 (= 1:5.44-3), - libmd0 (= 1.0.4-2), - libmount1 (= 2.38.1-5+b1), - libmpc3 (= 1.3.1-1), - libmpfr6 (= 4.2.0-1), - libncursesw6 (= 6.4-4), - libnettle8 (= 3.8.1-2), - libnghttp2-14 (= 1.52.0-1), - libnl-3-200 (= 3.7.0-0.2+b1), - libnsl-dev (= 1.3.0-2), - libnsl2 (= 1.3.0-2), - libnuma1 (= 2.0.16-1), - libp11-kit0 (= 0.24.1-2), - libpam-modules (= 1.5.2-6+deb12u1), - libpam-modules-bin (= 1.5.2-6+deb12u1), - libpam-runtime (= 1.5.2-6+deb12u1), - libpam0g (= 1.5.2-6+deb12u1), - libpcre2-8-0 (= 10.42-1), - libperl5.36 (= 5.36.0-7), - libpipeline1 (= 1.5.7-1), - libpsl5 (= 0.21.2-1), - libpython3-stdlib (= 3.11.2-1+b1), - libpython3.11-minimal (= 3.11.2-6), - libpython3.11-stdlib (= 3.11.2-6), - libquadmath0 (= 12.2.0-14), - libreadline8 (= 8.2-1.3), - librtmp1 (= 2.4+20151223.gitfa8646d.1-2+b2), - libsasl2-2 (= 2.1.28+dfsg-10), - libsasl2-modules-db (= 2.1.28+dfsg-10), - libseccomp2 (= 2.5.4-1+b3), - libselinux1 (= 3.4-1+b6), - libsmartcols1 (= 2.38.1-5+b1), - libsqlite3-0 (= 3.40.1-2), - libssh-4 (= 0.10.5-2), - libssh2-1 (= 1.10.0-3+b1), - libssl3 (= 3.0.11-1~deb12u2), - libstdc++-12-dev (= 12.2.0-14), - libstdc++6 (= 12.2.0-14), - libsub-override-perl (= 0.09-4), - libsystemd0 (= 252.17-1~deb12u1), - libtasn1-6 (= 4.19.0-2), - libtinfo6 (= 6.4-4), - libtirpc-common (= 1.3.3+ds-1), - libtirpc-dev (= 1.3.3+ds-1), - libtirpc3 (= 1.3.3+ds-1), - libtool (= 2.4.7-5), - libtsan2 (= 12.2.0-14), - libubsan1 (= 12.2.0-14), - libuchardet0 (= 0.0.7-1), - libudev1 (= 252.17-1~deb12u1), - libunistring2 (= 1.0-2), - libuuid1 (= 2.38.1-5+b1), - libvirt0 (= 9.0.0-4), - libxml2 (= 2.9.14+dfsg-1.3~deb12u1), - libxslt1.1 (= 1.1.35-1), - libyajl2 (= 2.1.0-3+deb12u2), - libyaml-0-2 (= 0.2.5-1), - libzstd1 (= 1.5.4+dfsg2-5), - linux-libc-dev (= 6.1.55-1), - login (= 1:4.13+dfsg1-1+b1), - m4 (= 1.4.19-3), - make (= 4.3-4.1), - man-db (= 2.11.2-2), - mawk (= 1.3.4.20200120-3.1), - media-types (= 10.0.0), - ncurses-base (= 6.4-4), - ncurses-bin (= 6.4-4), - openssl (= 3.0.11-1~deb12u2), - patch (= 2.7.6-7), - perl (= 5.36.0-7), - perl-base (= 5.36.0-7), - perl-modules-5.36 (= 5.36.0-7), - po-debconf (= 1.0.21+nmu1), - pybuild-plugin-pyproject (= 5.20230130+deb12u1), - python-babel-localedata (= 2.10.3-1), - python3 (= 3.11.2-1+b1), - python3-alabaster (= 0.7.12-1), - python3-all (= 3.11.2-1+b1), - python3-babel (= 2.10.3-1), - python3-build (= 0.9.0-1), - python3-certifi (= 2022.9.24-1), - python3-chardet (= 5.1.0+dfsg-2), - python3-charset-normalizer (= 3.0.1-2), - python3-distutils (= 3.11.2-3), - python3-docutils (= 0.19+dfsg-6), - python3-idna (= 3.3-1), - python3-imagesize (= 1.4.1-1), - python3-importlib-metadata (= 4.12.0-1), - python3-installer (= 0.6.0+dfsg1-1), - python3-jinja2 (= 3.1.2-1), - python3-lib2to3 (= 3.11.2-3), - python3-libvirt (= 9.0.0-1), - python3-lxml (= 4.9.2-1+b1), - python3-markupsafe (= 2.1.2-1+b1), - python3-minimal (= 3.11.2-1+b1), - python3-more-itertools (= 8.10.0-2), - python3-packaging (= 23.0-1), - python3-pep517 (= 0.13.0-2), - python3-pkg-resources (= 66.1.1-1), - python3-poetry-core (= 1.4.0-4), - python3-pydantic (= 1.10.4-1), - python3-pygments (= 2.14.0+dfsg-1), - python3-requests (= 2.28.1+dfsg-1), - python3-roman (= 3.3-3), - python3-setuptools (= 66.1.1-1), - python3-six (= 1.16.0-4), - python3-snowballstemmer (= 2.2.0-2), - python3-sphinx (= 5.3.0-4), - python3-sphinx-multiversion (= 0.2.4-2), - python3-toml (= 0.10.2-1), - python3-tomli (= 2.0.1-2), - python3-typing-extensions (= 4.4.0-1), - python3-tz (= 2022.7.1-4), - python3-urllib3 (= 1.26.12-1), - python3-wheel (= 0.38.4-2), - python3-yaml (= 6.0-3+b2), - python3-zipp (= 1.0.0-6), - python3.11 (= 3.11.2-6), - python3.11-minimal (= 3.11.2-6), - readline-common (= 8.2-1.3), - rpcsvc-proto (= 1.4.3-1), - sed (= 4.9-1), - sensible-utils (= 0.0.17+nmu1), - sgml-base (= 1.31), - sphinx-common (= 5.3.0-4), - sysvinit-utils (= 3.06-4), - tar (= 1.34+dfsg-1.2), - tzdata (= 2023c-5), - usr-is-merged (= 35), - util-linux (= 2.38.1-5+b1), - util-linux-extra (= 2.38.1-5+b1), - xml-core (= 0.18+nmu1), - xz-utils (= 5.4.1-0.2), - zlib1g (= 1:1.2.13.dfsg-1) -Environment: - DEB_BUILD_OPTIONS="parallel=16" - SOURCE_DATE_EPOCH="1700694403" diff --git a/packaging/build/compute_0.1.0.dev1-1_amd64.changes b/packaging/build/compute_0.1.0.dev1-1_amd64.changes deleted file mode 100644 index c300293..0000000 --- a/packaging/build/compute_0.1.0.dev1-1_amd64.changes +++ /dev/null @@ -1,38 +0,0 @@ -Format: 1.8 -Date: Wed, 22 Nov 2023 23:06:43 +0000 -Source: compute -Binary: compute compute-doc -Architecture: source all -Version: 0.1.0.dev1-1 -Distribution: UNRELEASED -Urgency: medium -Maintainer: ge -Changed-By: ge -Description: - compute - Compute instances management library and tools (Python 3) - compute-doc - Compute instances management library and tools (documentation) -Changes: - compute (0.1.0.dev1-1) UNRELEASED; urgency=medium - . - * This is the development build, see commits in upstream repo for info. -Checksums-Sha1: - 455d0b203d96d97d4272d30be72c27cdde50fdc5 1123 compute_0.1.0.dev1-1.dsc - 94be605a5a0ca8b0ea93a46dd5029a2513486190 20824 compute_0.1.0.dev1.orig.tar.gz - 57790e9df9659f913fa1da65b77c84a3aba4976c 2660 compute_0.1.0.dev1-1.debian.tar.xz - ef2a0d6dd481adc0cf4bed1a2bc98536f1795adf 40424 compute-doc_0.1.0.dev1-1_all.deb - 20ecb0342e494a426634a5124462e49c6f7fd2c9 21644 compute_0.1.0.dev1-1_all.deb - 78f050568f3f50c415f23caf851482663c78513e 8126 compute_0.1.0.dev1-1_amd64.buildinfo -Checksums-Sha256: - c92ba4e3db43b496e01aa912f6e59240f7cd647b8b4950005182d91d071e31ac 1123 compute_0.1.0.dev1-1.dsc - e310d2ddbdb334737efc7adcc98eac2db2f158e5e989ddfade2ddfae07a6174d 20824 compute_0.1.0.dev1.orig.tar.gz - c9e267c79fa5a9e06625ac0502af528f4b526c74035fa23e6933c2c8e6429ab2 2660 compute_0.1.0.dev1-1.debian.tar.xz - 1c0d14fc87885f5dafe8bcbe1b6a07d9a57ce2dc0943a9837f751e3142ee8a42 40424 compute-doc_0.1.0.dev1-1_all.deb - a1f5a032f653276be3e4dc43818d663850463167a2b4b39138e184be1dabb44f 21644 compute_0.1.0.dev1-1_all.deb - d4ae62c2a518e36ba1acd6d56a94d1b218ffd766f5f558d7c73ac7a2ffe8102d 8126 compute_0.1.0.dev1-1_amd64.buildinfo -Files: - 635eae482cdff5bbe99a3911ed9e915c 1123 admin optional compute_0.1.0.dev1-1.dsc - de78bd5eecc56034a990dd9395a089c4 20824 admin optional compute_0.1.0.dev1.orig.tar.gz - cb9d6978a83d7f1842063315333d6278 2660 admin optional compute_0.1.0.dev1-1.debian.tar.xz - 8a8c6490cb363870735ec2572cf65cdf 40424 doc optional compute-doc_0.1.0.dev1-1_all.deb - bf5fb2ffd00e5373a54461f34b2d7033 21644 admin optional compute_0.1.0.dev1-1_all.deb - c319ab6fc548baccadb65dee3b0869af 8126 admin optional compute_0.1.0.dev1-1_amd64.buildinfo diff --git a/packaging/build/compute_0.1.0.dev1.orig.tar.gz b/packaging/build/compute_0.1.0.dev1.orig.tar.gz deleted file mode 100644 index fcb6882..0000000 Binary files a/packaging/build/compute_0.1.0.dev1.orig.tar.gz and /dev/null differ diff --git a/packaging/build/docs/Makefile b/packaging/build/docs/Makefile deleted file mode 100644 index d0c3cbf..0000000 --- a/packaging/build/docs/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -# Minimal makefile for Sphinx documentation -# - -# You can set these variables from the command line, and also -# from the environment for the first two. -SPHINXOPTS ?= -SPHINXBUILD ?= sphinx-build -SOURCEDIR = source -BUILDDIR = build - -# Put it first so that "make" without argument is like "make help". -help: - @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) - -.PHONY: help Makefile - -# Catch-all target: route all unknown targets to Sphinx using the new -# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). -%: Makefile - @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/packaging/build/docs/make.bat b/packaging/build/docs/make.bat deleted file mode 100644 index 747ffb7..0000000 --- a/packaging/build/docs/make.bat +++ /dev/null @@ -1,35 +0,0 @@ -@ECHO OFF - -pushd %~dp0 - -REM Command file for Sphinx documentation - -if "%SPHINXBUILD%" == "" ( - set SPHINXBUILD=sphinx-build -) -set SOURCEDIR=source -set BUILDDIR=build - -%SPHINXBUILD% >NUL 2>NUL -if errorlevel 9009 ( - echo. - echo.The 'sphinx-build' command was not found. Make sure you have Sphinx - echo.installed, then set the SPHINXBUILD environment variable to point - echo.to the full path of the 'sphinx-build' executable. Alternatively you - echo.may add the Sphinx directory to PATH. - echo. - echo.If you don't have Sphinx installed, grab it from - echo.https://www.sphinx-doc.org/ - exit /b 1 -) - -if "%1" == "" goto help - -%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% -goto end - -:help -%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% - -:end -popd diff --git a/packaging/build/docs/source/_templates/versioning.html b/packaging/build/docs/source/_templates/versioning.html deleted file mode 100644 index 318bd87..0000000 --- a/packaging/build/docs/source/_templates/versioning.html +++ /dev/null @@ -1,8 +0,0 @@ -{% if versions %} -

{{ _('Версии') }}

- -{% endif %} diff --git a/packaging/build/docs/source/conf.py b/packaging/build/docs/source/conf.py deleted file mode 100644 index d8738f3..0000000 --- a/packaging/build/docs/source/conf.py +++ /dev/null @@ -1,33 +0,0 @@ -# Add /mnt/build/compute-0.1.0.dev1 to path for autodoc Sphinx extension -import os -import sys -sys.path.insert(0, os.path.abspath('/mnt/build/compute-0.1.0.dev1')) - -# Project information -project = 'Compute' -copyright = '2023, Compute Authors' -author = 'Compute Authors' -release = '0.1.0' - -# Sphinx general settings -extensions = [ - 'sphinx.ext.autodoc', - 'sphinx_multiversion', -] -templates_path = ['_templates'] -exclude_patterns = [] -language = 'en' - -# HTML output settings -html_theme = 'alabaster' -html_static_path = ['_static'] -html_sidebars = { - '**': [ - 'about.html', - 'navigation.html', - 'relations.html', - 'searchbox.html', - 'donate.html', - 'versioning.html', - ] -} diff --git a/packaging/build/docs/source/index.rst b/packaging/build/docs/source/index.rst deleted file mode 100644 index 81222c2..0000000 --- a/packaging/build/docs/source/index.rst +++ /dev/null @@ -1,16 +0,0 @@ -Compute -======= - -Compute instances management library. - -.. toctree:: - :maxdepth: 1 - - pyapi/index - -Indices and tables ------------------- - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` diff --git a/packaging/build/docs/source/pyapi/exceptions.rst b/packaging/build/docs/source/pyapi/exceptions.rst deleted file mode 100644 index 3912721..0000000 --- a/packaging/build/docs/source/pyapi/exceptions.rst +++ /dev/null @@ -1,5 +0,0 @@ -``exceptions`` -============== - -.. automodule:: compute.exceptions - :members: diff --git a/packaging/build/docs/source/pyapi/index.rst b/packaging/build/docs/source/pyapi/index.rst deleted file mode 100644 index e0cebb8..0000000 --- a/packaging/build/docs/source/pyapi/index.rst +++ /dev/null @@ -1,49 +0,0 @@ -Python API -========== - -The API allows you to perform actions on instances programmatically. Below is -an example of changing parameters and launching the `myinstance` instance. - -.. code-block:: python - - import logging - - from compute import Session - - logging.basicConfig(level=logging.DEBUG) - - with Session() as session: - instance = session.get_instance('myinstance') - instance.set_vcpus(4) - instance.start() - instance.set_autostart(enabled=True) - - -:class:`Session` context manager provides an abstraction over :class:`libvirt.virConnect` -and returns objects of other classes of the present library. - -Entity representation ---------------------- - -Entities such as a compute-instance are represented as classes. These classes directly -call libvirt methods to perform operations on the hypervisor. An example class is -:class:`Volume`. - -The configuration files of various libvirt objects in `compute` are described by special -dataclasses. The dataclass stores object parameters in its properties and can return an -XML config for libvirt using the ``to_xml()`` method. For example :class:`VolumeConfig`. - -`Pydantic `_ models are used to validate input data. -For example :class:`VolumeSchema`. - -Modules documentation ---------------------- - -.. toctree:: - :maxdepth: 4 - - session - instance/index - storage/index - utils - exceptions diff --git a/packaging/build/docs/source/pyapi/instance/guest_agent.rst b/packaging/build/docs/source/pyapi/instance/guest_agent.rst deleted file mode 100644 index 1305140..0000000 --- a/packaging/build/docs/source/pyapi/instance/guest_agent.rst +++ /dev/null @@ -1,6 +0,0 @@ -``guest_agent`` -=============== - -.. automodule:: compute.instance.guest_agent - :members: - :special-members: __init__ diff --git a/packaging/build/docs/source/pyapi/instance/index.rst b/packaging/build/docs/source/pyapi/instance/index.rst deleted file mode 100644 index 659ffc2..0000000 --- a/packaging/build/docs/source/pyapi/instance/index.rst +++ /dev/null @@ -1,10 +0,0 @@ -``instance`` -============ - -.. toctree:: - :maxdepth: 1 - :caption: Contents: - - instance - guest_agent - schemas diff --git a/packaging/build/docs/source/pyapi/instance/instance.rst b/packaging/build/docs/source/pyapi/instance/instance.rst deleted file mode 100644 index 3c58f1f..0000000 --- a/packaging/build/docs/source/pyapi/instance/instance.rst +++ /dev/null @@ -1,6 +0,0 @@ -``instance`` -============ - -.. automodule:: compute.instance.instance - :members: - :special-members: __init__ diff --git a/packaging/build/docs/source/pyapi/instance/schemas.rst b/packaging/build/docs/source/pyapi/instance/schemas.rst deleted file mode 100644 index 7dacabf..0000000 --- a/packaging/build/docs/source/pyapi/instance/schemas.rst +++ /dev/null @@ -1,5 +0,0 @@ -``schemas`` -=========== - -.. automodule:: compute.instance.schemas - :members: diff --git a/packaging/build/docs/source/pyapi/session.rst b/packaging/build/docs/source/pyapi/session.rst deleted file mode 100644 index 2dec16e..0000000 --- a/packaging/build/docs/source/pyapi/session.rst +++ /dev/null @@ -1,6 +0,0 @@ -``session`` -=========== - -.. automodule:: compute.session - :members: - :special-members: __init__ diff --git a/packaging/build/docs/source/pyapi/storage/index.rst b/packaging/build/docs/source/pyapi/storage/index.rst deleted file mode 100644 index e9ea734..0000000 --- a/packaging/build/docs/source/pyapi/storage/index.rst +++ /dev/null @@ -1,9 +0,0 @@ -``storage`` -============ - -.. toctree:: - :maxdepth: 1 - :caption: Contents: - - pool - volume diff --git a/packaging/build/docs/source/pyapi/storage/pool.rst b/packaging/build/docs/source/pyapi/storage/pool.rst deleted file mode 100644 index 398124e..0000000 --- a/packaging/build/docs/source/pyapi/storage/pool.rst +++ /dev/null @@ -1,6 +0,0 @@ -``pool`` -======== - -.. automodule:: compute.storage.pool - :members: - :special-members: __init__ diff --git a/packaging/build/docs/source/pyapi/storage/volume.rst b/packaging/build/docs/source/pyapi/storage/volume.rst deleted file mode 100644 index e1ba8d0..0000000 --- a/packaging/build/docs/source/pyapi/storage/volume.rst +++ /dev/null @@ -1,6 +0,0 @@ -``volume`` -========== - -.. automodule:: compute.storage.volume - :members: - :special-members: __init__ diff --git a/packaging/build/docs/source/pyapi/utils.rst b/packaging/build/docs/source/pyapi/utils.rst deleted file mode 100644 index b5ab60a..0000000 --- a/packaging/build/docs/source/pyapi/utils.rst +++ /dev/null @@ -1,14 +0,0 @@ -``utils`` -========= - -``utils.units`` ---------------- - -.. automodule:: compute.utils.units - :members: - -``utils.ids`` -------------- - -.. automodule:: compute.utils.ids - :members: