pysnap.core package

Core business logic package for PySnap.

Submodules

pysnap.core.access module

How PySnap reaches the guest of a VM: serial console or ssh.

Base VMs registered with pysnap protosettings and their clones are known to have their console on COM1, so they are reached through the serial TCP server of COM1. Every other VM is reached through ssh: its NAT adapter forwards a host port on 127.0.0.1 to port 22 of the guest. The automatic choice can be overridden per VM with pysnap set VM access=com|ssh.

The override and the ssh user are kept in VirtualBox extra data of the VM; the forwarded port is read from the NAT port-forwarding rule named SSH_RULE_NAME.

class pysnap.core.access.AccessInfo(mode, source, serial_port, ssh_port, ssh_user)[source]

Bases: object

Describe how PySnap reaches the guest of one VM.

Parameters:
  • mode (str) – "com" for the COM1 serial console, "ssh" for ssh.

  • source (str) – Why this mode applies: "override" (pysnap set), "protosettings" (the VM or its parent is registered) or "default".

  • serial_port (int | None) – TCP port of COM1, if any.

  • ssh_port (int | None) – Host port forwarded to the guest ssh server, if any.

  • ssh_user (str) – User for ssh connections.

mode: str
source: str
serial_port: int | None
ssh_port: int | None
ssh_user: str
pysnap.core.access.resolve_access(info, proto_settings_vms)[source]

Return the access mode and connection details of a VM.

Parameters:
  • info (VMInfo) – VM information including PySnap metadata.

  • proto_settings_vms (Collection[str]) – Base VM names registered with protosettings.

Returns:

Access description.

Return type:

AccessInfo

pysnap.core.access.validate_access_mode(value)[source]

Validate an access= value.

Parameters:

value (str) – com, ssh or auto.

Returns:

The lower-case value.

Raises:

PySnapError – For other values.

Return type:

str

pysnap.core.access.validate_ssh_user(value)[source]

Validate an ssh-user= value.

Parameters:

value (str) – User name in the guest.

Returns:

The user name.

Raises:

PySnapError – For names that are not plain user names.

Return type:

str

pysnap.core.access.ssh_forwarding_rule(port)[source]

Return the VirtualBox NAT rule that forwards a host port to ssh.

Parameters:

port (int) – Host port on SSH_HOST_ADDRESS.

Returns:

Rule in VBoxManage --natpfN syntax.

Return type:

str

pysnap.core.access.parse_ssh_forwarding(rule)[source]

Return the host port of a PySnap ssh forwarding rule.

Parameters:

rule (str) – Rule as printed by showvminfo --machinereadable, name,protocol,host address,host port,guest address,guest port.

Returns:

Host port, or None for other rules.

Return type:

int | None

pysnap.core.appliance module

Helpers for reading appliance metadata from OVF and OVA files.

pysnap.core.appliance.read_appliance_vm_names(image_path)[source]

Read original VM names declared inside an OVF or OVA appliance.

Parameters:

image_path (str | Path) – Appliance path.

Returns:

Declared appliance VM names in descriptor order.

Raises:

PySnapError – If the descriptor cannot be read or parsed.

Return type:

tuple[str, …]

pysnap.core.models module

Domain models used by PySnap.

class pysnap.core.models.VMReference(name, uuid)[source]

Bases: object

Represent a lightweight VirtualBox VM reference.

Parameters:
  • name (str)

  • uuid (str)

name: str
uuid: str
class pysnap.core.models.SerialPortConfiguration(enabled, mode=None, port=None)[source]

Bases: object

Represent the raw UART1 configuration of one VM.

Parameters:
  • enabled (bool)

  • mode (str | None)

  • port (int | None)

enabled: bool
mode: str | None = None
port: int | None = None
class pysnap.core.models.ImportCandidate(vsys_index, vm_name, group, requires_eula_accept=False)[source]

Bases: object

Represent a VM discovered in an appliance import dry run.

Parameters:
  • vsys_index (int)

  • vm_name (str)

  • group (str)

  • requires_eula_accept (bool)

vsys_index: int
vm_name: str
group: str
requires_eula_accept: bool = False
class pysnap.core.models.VMInfo(name, uuid, groups, serial_port=None, vm_state=None, parent_name=None, managed=False, metadata=<factory>, serial_tcp_ports=(), ssh_port=None)[source]

Bases: object

Represent the VM information required by the CLI.

Parameters:
  • name (str)

  • uuid (str)

  • groups (tuple[str, ...])

  • serial_port (int | None)

  • vm_state (str | None)

  • parent_name (str | None)

  • managed (bool)

  • metadata (dict[str, str])

  • serial_tcp_ports (tuple[int, ...])

  • ssh_port (int | None)

name: str
uuid: str
groups: tuple[str, ...]
serial_port: int | None = None
vm_state: str | None = None
parent_name: str | None = None
managed: bool = False
metadata: dict[str, str]
serial_tcp_ports: tuple[int, ...] = ()
ssh_port: int | None = None
property primary_group: str[source]

Return the primary VM group.

Returns:

Primary group name or /Others when unavailable.

class pysnap.core.models.VMGroup(name, vm_names)[source]

Bases: object

Represent a VM group and its members.

Parameters:
  • name (str)

  • vm_names (tuple[str, ...])

name: str
vm_names: tuple[str, ...]
class pysnap.core.models.IntegrationTestResult(machines, deleted_vm_names, monitor_records=())[source]

Bases: object

Represent the outcome of an integration test run.

Parameters:
  • machines (tuple[VMInfo, ...])

  • deleted_vm_names (tuple[str, ...])

  • monitor_records (tuple[VMMonitorRecord, ...])

machines: tuple[VMInfo, ...]
deleted_vm_names: tuple[str, ...]
monitor_records: tuple[VMMonitorRecord, ...] = ()
class pysnap.core.models.VMMonitorRecord(name, display_state, serial_port, group, raw_state, access_mode='com', ssh_port=None)[source]

Bases: object

Represent a compact monitor record for a VM.

Parameters:
  • name (str)

  • display_state (str)

  • serial_port (int | None)

  • group (str)

  • raw_state (str)

  • access_mode (str)

  • ssh_port (int | None)

name: str
display_state: str
serial_port: int | None
group: str
raw_state: str
access_mode: str = 'com'
ssh_port: int | None = None
class pysnap.core.models.ComPortSetting(mode, port=None, path=None, detail=None)[source]

Bases: object

Represent the configuration of one serial (COM) port.

Parameters:
  • mode (str) – "off", "tcpserver", "server" or "client" (host pipe), or another raw VirtualBox mode such as "file".

  • port (int | None) – TCP port of a tcpserver port.

  • path (str | None) – Host pipe path of a server or client port.

  • detail (str | None) – Raw VirtualBox value of other modes.

mode: str
port: int | None = None
path: str | None = None
detail: str | None = None
class pysnap.core.models.NicSetting(attachment, network=None)[source]

Bases: object

Represent the attachment of one network adapter.

Parameters:
  • attachment (str) – "none", "nat" or "intnet", or another raw VirtualBox attachment such as "bridged".

  • network (str | None) – Internal network name of an intnet adapter.

attachment: str
network: str | None = None
class pysnap.core.models.VMHardware(com_ports, nics)[source]

Bases: object

Represent the serial ports and network adapters of one VM.

Parameters:
  • com_ports (tuple[ComPortSetting, ...]) – Settings of every serial port the VM can have (COM1-COM4).

  • nics (tuple[NicSetting, ...]) – Settings of every network adapter the VM can have; their number depends on the chipset (8 for PIIX3, 36 for ICH9).

com_ports: tuple[ComPortSetting, ...]
nics: tuple[NicSetting, ...]
class pysnap.core.models.SshKeyResult(vm_name, users, ssh_user, changed_user=False, already_installed=False)[source]

Bases: object

Represent the outcome of pysnap ssh-key.

Parameters:
  • vm_name (str) – VM name.

  • users (tuple[str, ...]) – Guest users whose login with the PySnap key works.

  • ssh_user (str) – User that PySnap uses for ssh after the setup.

  • changed_user (bool) – Whether ssh_user was switched to make ssh work.

  • already_installed (bool) – Whether the key worked before anything was done.

vm_name: str
users: tuple[str, ...]
ssh_user: str
changed_user: bool = False
already_installed: bool = False
class pysnap.core.models.VMSettingsResult(vm_name, vm_state, hardware, warnings=(), access=None)[source]

Bases: object

Represent the outcome of pysnap set.

Parameters:
  • vm_name (str) – VM name.

  • vm_state (str | None) – Raw VirtualBox state.

  • hardware (VMHardware) – Serial ports and network adapters after the change.

  • warnings (tuple[str, ...]) – Notes about removed TCP servers and similar effects.

  • access (AccessInfo | None) – How PySnap reaches the guest after the change.

vm_name: str
vm_state: str | None
hardware: VMHardware
warnings: tuple[str, ...] = ()
access: AccessInfo | None = None

pysnap.core.service module

Application service layer for PySnap.

pysnap.core.service.normalize_group_name(group)[source]

Normalize a VirtualBox group name.

Parameters:

group (str | None) – Raw group name.

Returns:

Group name that always starts with /.

Return type:

str

class pysnap.core.service.PySnapService(client=None, session_registry=None, serial_probe_factory=None, proto_settings_store=None, ssh_key_store=None)[source]

Bases: object

Provide the main PySnap business operations.

Parameters:
SNAPSHOT_NAME = 'pysnap-base'
DEFAULT_SERIAL_TCP_PORT = 1024
DEFAULT_START_TIMEOUT = 30.0
DEFAULT_STOP_TIMEOUT = 60.0
DEFAULT_PAUSE_TIMEOUT = 30.0
DEFAULT_SSH_WAIT_TIMEOUT = 180.0
DEFAULT_INTEGRATION_STOP_TIMEOUT = 180.0
STOPPED_STATES = {'aborted', 'poweroff', 'saved'}
STOPPING_STATES = {'stopping'}
PAUSED_STATES = {'paused'}
ERROR_STATES = {'gurumeditation', 'stuck'}
import_image(image_path, vm_name=None, progress_callback=None)[source]

Import an OVA or OVF image into VirtualBox.

Parameters:
  • image_path (str) – Path to the OVA or OVF appliance.

  • vm_name (str | None) – Optional custom name for a single imported VM.

  • progress_callback (Callable[[int], None] | None) – Optional import progress callback.

Returns:

Imported VM information objects.

Raises:

PySnapError – If the file is invalid or import does not create VMs.

Return type:

list[VMInfo]

run_integration_test(image_path, name_token=None)[source]

Run an end-to-end integration scenario on an appliance.

The scenario imports one appliance VM, creates three linked clones connected in a triangle of internal networks, captures machine details, validates runtime monitoring by attaching to one clone and starting a second clone headlessly, and removes all created VMs one by one.

Parameters:
  • image_path (str) – Path to the appliance used for the test.

  • name_token (str | None) – Optional deterministic suffix for generated VM names.

Returns:

Integration test result with machine info and deletion order.

Raises:

PySnapError – If the appliance is invalid or the test fails.

Return type:

IntegrationTestResult

list_groups()[source]

List all VM groups and their members.

Returns:

Group information sorted by group name.

Return type:

list[VMGroup]

list_monitored_vms()[source]

List VMs that are running or changing state in a compact form.

Returns:

Monitor records sorted by VM name.

Return type:

list[VMMonitorRecord]

get_session_state_label(vm_name)[source]

Return the runtime label of the VM of a connect session cheaply.

A session asks for the state every second, so this runs one VBoxManage showvminfo and skips the existence check and the extra data of get_monitor_state_label().

Parameters:

vm_name (str) – VM name.

Returns:

User-facing monitor label.

Return type:

str

get_monitor_state_label(vm_name)[source]

Return the user-facing runtime label for one VM.

Parameters:

vm_name (str) – VM name.

Returns:

User-facing monitor label.

Return type:

str

prepare_vm_connection(vm_name, timeout=None)[source]

Ensure that a VM is ready for an interactive connection (COM1 or ssh).

Parameters:
  • vm_name (str) – VM name.

  • timeout (float | None) – Optional startup timeout in seconds.

Returns:

Updated VM information.

Raises:

PySnapError – If the VM cannot be connected.

Return type:

VMInfo

stop_runtime_vm(vm_name, timeout=None)[source]

Request a graceful stop for one running VM.

Parameters:
  • vm_name (str) – VM name.

  • timeout (float | None) – Optional timeout in seconds.

Raises:

PySnapError – If the VM is not in a stoppable state.

Return type:

None

stop_all_runtime_vms(timeout=None)[source]

Stop all currently running VMs that are in a normal running state.

Parameters:

timeout (float | None) – Optional per-VM timeout in seconds.

Returns:

Names of VMs that received a stop request and stopped.

Return type:

list[str]

pause_runtime_vm(vm_name, timeout=None)[source]

Pause one running VM; it stays in memory until it is resumed.

Parameters:
  • vm_name (str) – VM name.

  • timeout (float | None) – Optional timeout in seconds.

Raises:

PySnapError – If the VM is not running.

Return type:

None

resume_runtime_vm(vm_name, timeout=None)[source]

Resume one paused VM.

Parameters:
  • vm_name (str) – VM name.

  • timeout (float | None) – Optional timeout in seconds.

Raises:

PySnapError – If the VM is not paused.

Return type:

None

pause_all_runtime_vms(timeout=None)[source]

Pause all running VMs.

Parameters:

timeout (float | None) – Optional per-VM timeout in seconds.

Returns:

Names of the paused VMs.

Raises:

PySnapError – If one or more VMs cannot be paused.

Return type:

list[str]

resume_all_runtime_vms(timeout=None)[source]

Resume all paused VMs.

Parameters:

timeout (float | None) – Optional per-VM timeout in seconds.

Returns:

Names of the resumed VMs.

Raises:

PySnapError – If one or more VMs cannot be resumed.

Return type:

list[str]

show_vm(vm_name)[source]

Return details for a single VM.

Parameters:

vm_name (str) – Name of the VM to inspect.

Returns:

Detailed VM information.

Raises:

VMNotFoundError – If the VM is absent.

Return type:

VMInfo

plug_vm(vm_name)[source]

Configure one VM for PySnap connections.

A VM reached through COM1 gets UART1 as a TCP server: if it already is one, the VM is returned unchanged; otherwise PySnap assigns a free TCP port and enables the same UART1 tcpserver mode used by the imported protocol images. A VM reached through ssh gets a NAT rule that forwards a free host port to port 22 of the guest.

Parameters:

vm_name (str) – VM name to configure.

Returns:

Updated VM information.

Raises:

PySnapError – If the VM cannot be configured safely.

Return type:

VMInfo

set_vm_settings(vm_name, options)[source]

Change the serial ports and network adapters of a stopped VM.

Without options the current settings are returned unchanged. All options are validated before one modifyvm call applies them. For proto-settings clones the DMI system SKU is rebuilt from the new COM1 port and the internal networks of NIC2-NIC4.

Parameters:
  • vm_name (str) – VM name.

  • options (Sequence[str]) – comN=VALUE and nicN=VALUE options.

Returns:

Settings after the change and warnings.

Raises:

PySnapError – For invalid options, a VM that is not stopped or TCP ports in use.

Return type:

VMSettingsResult

ssh_target(vm_name, user=None)[source]

Return where and as whom to reach a running VM through ssh.

Parameters:
  • vm_name (str) – VM name.

  • user (str | None) – Guest user; the ssh user of the VM by default.

Returns:

Connection target.

Raises:

PySnapError – Without a forwarded ssh port or a running VM.

Return type:

SshTarget

prepare_ssh_terminal(vm_name, *, timeout=None, notify=None, installer=None, poll_interval=2.0)[source]

Wait for the guest ssh server and make sure the PySnap key works.

A freshly started guest needs time to boot, so a missing ssh server is retried until timeout. A refused key starts the interactive key setup of setup_ssh_key() in the current terminal.

Parameters:
  • vm_name (str) – Name of a running VM reached through ssh.

  • timeout (float | None) – Seconds to wait for the ssh server.

  • notify (Callable[[str], None] | None) – Callback for progress messages.

  • installer – Interactive installer, for tests.

  • poll_interval (float) – Seconds between attempts.

Returns:

Connection target for the ssh user of the VM.

Raises:

PySnapError – When no ssh server answers in time or the key cannot be installed.

Return type:

SshTarget

setup_ssh_key(vm_name, user=None, *, console=False, installer=None, console_wait=10.0)[source]

Install the PySnap key in the guest so that ssh needs no password.

By default ssh logs in with the password of user (the ssh user of the VM) in the current terminal, adds the key for that user and, when the root password is given in the guest, for root as well. With console the key line is typed on the VM screen instead, for a shell that is logged in there. When the ssh user of the VM cannot use the key but user can, the ssh user is switched to user.

Parameters:
  • vm_name (str) – VM name.

  • user (str | None) – Guest user for the password login.

  • console (bool) – Type the command on the VM screen.

  • installer – Interactive installer, for tests.

  • console_wait (float) – Seconds to wait for a typed command to work.

Returns:

Users that can log in with the key afterwards.

Raises:

PySnapError – When no ssh server answers or nothing worked.

Return type:

SshKeyResult

get_vm_access(vm_name)[source]

Return how PySnap reaches the guest of a VM.

Parameters:

vm_name (str) – VM name.

Returns:

Access mode, its source and the connection details.

Return type:

AccessInfo

clone_vm(base_vm, clone_vm, serial_port=None, networks=())[source]

Create a linked clone and configure it.

Parameters:
  • base_vm (str) – Name of the source VM.

  • clone_vm (str) – Name of the linked clone to create.

  • serial_port (int | None) – Optional TCP port assigned to UART1.

  • networks (tuple[str, ...]) – Up to three internal network names.

Returns:

Detailed information about the created clone.

Raises:

PySnapError – If invalid parameters are supplied.

Return type:

VMInfo

register_proto_settings_vm(vm_name)[source]

Persist one base VM name in the proto-settings store.

Parameters:

vm_name (str) – Base VM name to register.

Returns:

Updated configured VM names.

Raises:

VMNotFoundError – If the VM does not exist.

Return type:

tuple[str, …]

erase_vm(vm_name)[source]

Erase one VM when no dependent clones exist.

Parameters:

vm_name (str) – Name of the VM to remove.

Raises:

VMDependencyError – If dependent clones still exist.

Return type:

None

erase_group(group_name)[source]

Erase all VMs from a single group.

Parameters:

group_name (str) – Group to erase.

Returns:

Names of removed VMs.

Raises:

PySnapError – If the group is empty or contains blocked base VMs.

Return type:

list[str]

erase_all()[source]

Erase all registered VirtualBox VMs.

Returns:

Names of removed VMs.

Return type:

list[str]

erase_clones(group_name=None)[source]

Erase all managed clone VMs, optionally limited to one group.

Clones are recognized through the pysnap/kind and pysnap/parent metadata written by clone_vm(), so no separate clone registry has to be kept in sync with VirtualBox.

Parameters:

group_name (str | None) – Optional group that limits the deleted clones.

Returns:

Names of removed clone VMs.

Raises:

PySnapError – If the group is empty or clones outside the selection still depend on the selected clones.

Return type:

list[str]

is_clone_vm(vm_name)[source]

Return whether one VM was created as a managed linked clone.

Parameters:

vm_name (str) – VM name to check.

Returns:

True when the VM is a managed clone.

Raises:

VMNotFoundError – If the VM does not exist.

Return type:

bool

full_clean(paths)[source]

Delete VirtualBox data directories from the host file system.

Parameters:

paths (Sequence[Path]) – Directories to remove recursively.

Returns:

Paths of directories that were removed.

Raises:

PySnapError – If one or more directories cannot be removed.

Return type:

list[str]

pysnap.core.settings module

Parse pysnap set options for serial ports and network adapters.

Each option is KEY=VALUE:

  • comN: auto or a TCP port number for a TCP server, server:NAME or client:NAME for a host pipe between VMs, or off;

  • nicN: an internal network name, nat or off;

  • access: com, ssh or auto (the choice by protosettings);

  • ssh-user: the guest user for ssh connections.

N is any port or adapter number; whether the VM has it (VirtualBox offers COM1-COM4 and, depending on the chipset, NIC1-NIC8 or NIC1-NIC36) is checked against the VM.

A host pipe is given by a short name; host_pipe_path() turns it into the path format of the host system, so two VMs that use the same name are connected on any platform.

class pysnap.core.settings.ComPortRequest(mode, port=None, pipe_name=None)[source]

Bases: object

Represent a requested serial port change.

Parameters:
  • mode (str) – "off", "tcpserver", "server" or "client".

  • port (int | None) – Requested TCP port; None with tcpserver means auto.

  • pipe_name (str | None) – Short host pipe name of server and client.

mode: str
port: int | None = None
pipe_name: str | None = None
class pysnap.core.settings.SetRequest(com_ports, nics, access=None, ssh_user=None)[source]

Bases: object

Represent the changes requested by pysnap set.

Parameters:
  • com_ports (dict[int, ComPortRequest]) – Port numbers mapped to the requested change.

  • nics (dict[int, NicSetting]) – Adapter numbers mapped to the requested setting.

  • access (str | None) – "com", "ssh" or "auto", if requested.

  • ssh_user (str | None) – Guest user for ssh, if requested.

com_ports: dict[int, ComPortRequest]
nics: dict[int, NicSetting]
access: str | None = None
ssh_user: str | None = None
property empty: bool[source]

Return whether no change was requested.

property hardware_changes: bool[source]

Return whether serial ports or adapters change (the VM must be stopped).

pysnap.core.settings.parse_set_options(options)[source]

Parse KEY=VALUE options.

Parameters:

options (Sequence[str]) – Command line options.

Returns:

Requested changes.

Raises:

PySnapError – For unknown keys, repeated keys and invalid values.

Return type:

SetRequest

pysnap.core.settings.host_pipe_path(name, *, windows=None)[source]

Return the host pipe path for a short pipe name.

Parameters:
  • name (str) – Short pipe name.

  • windows (bool | None) – Override the host system check, for tests.

Returns:

\\.\pipe\pysnap-NAME on Windows, otherwise a socket path pysnap-NAME in the temporary directory.

Return type:

str

pysnap.core.settings.host_pipe_name(path)[source]

Return the short name of a pipe path created by PySnap.

Parameters:

path (str) – Host pipe path.

Returns:

Short name, or None for other paths.

Return type:

str | None

pysnap.core.settings.com_setting_from_request(request, port)[source]

Return the resulting port setting of a request.

Parameters:
  • request (ComPortRequest) – Requested change.

  • port (int | None) – Resolved TCP port of a tcpserver request.

Returns:

Port setting.

Return type:

ComPortSetting