Files
local-ci-cd-system/src/ci_orchestrator/backends/protocol.py
T
Simone bd31a3f2f3 Phase A1: bootstrap Python ci_orchestrator package
- pyproject.toml (hatchling) with ruff/mypy/pytest dev deps
- src/ci_orchestrator/: config, credentials, backends (Protocol + WorkstationVmrunBackend), transport (WinRM via pypsrp, SSH via paramiko)
- CLI entry point with PoC 'wait-ready' subcommand (Windows + Linux guests)
- tests/python/: 35 unit tests, 83% coverage, all backends/transport mocked
- gitea/workflows/lint.yml: new 'python' job (ruff + mypy --strict + pytest --cov-fail-under=70)
- config.example.toml + README setup section + .gitignore Python entries

Neutral VmBackend Protocol prepared for Phase C ESXi extension.
See plans/implementation-plan-A-B.md step A1.
2026-05-14 18:34:02 +02:00

82 lines
2.4 KiB
Python

"""Neutral VM backend Protocol.
Phase C hook: keep names hypervisor-agnostic. ``clone_linked``, ``start``,
``stop``, ``delete``, ``get_ip``, ``list_snapshots`` map cleanly onto both
``vmrun`` and pyVmomi (vSphere). Identifiers are opaque strings (a VMX path
on Workstation, a managed object reference on ESXi).
"""
from __future__ import annotations
from dataclasses import dataclass
from enum import StrEnum
from typing import Protocol
class VmState(StrEnum):
"""Coarse VM power state, normalised across backends."""
POWERED_OFF = "powered_off"
POWERED_ON = "powered_on"
SUSPENDED = "suspended"
UNKNOWN = "unknown"
@dataclass(frozen=True)
class VmHandle:
"""Opaque reference to a VM managed by a backend.
``identifier`` is backend-specific (a VMX path for Workstation, a
managed object reference for vSphere). Callers MUST treat it as opaque.
"""
identifier: str
name: str | None = None
class VmBackend(Protocol):
"""Hypervisor-agnostic VM operations.
All methods raise :class:`~ci_orchestrator.backends.errors.BackendError`
on failure. Implementations are free to add backend-specific kwargs but
MUST honour the documented signature for the listed parameters.
"""
def clone_linked(
self,
template: str,
snapshot: str,
name: str,
destination: str | None = None,
) -> VmHandle:
"""Create a linked clone of ``template`` at snapshot ``snapshot``."""
...
def start(self, handle: VmHandle, headless: bool = True) -> None:
"""Power on the VM."""
...
def stop(self, handle: VmHandle, hard: bool = False) -> None:
"""Power off the VM. ``hard=True`` requests a forced power-off."""
...
def delete(self, handle: VmHandle) -> None:
"""Remove the VM from inventory and delete its files."""
...
def get_ip(self, handle: VmHandle, timeout: float = 0.0) -> str | None:
"""Return the guest's primary IP, or ``None`` if not yet known."""
...
def list_snapshots(self, handle: VmHandle) -> list[str]:
"""Return snapshot names defined on ``handle``."""
...
def is_running(self, handle: VmHandle) -> bool:
"""Return ``True`` if the VM appears in the running-VM list.
Implementations MUST NOT rely on guest tools (e.g. ``vmrun
getGuestIPAddress``) for this check — see ``AGENTS.md`` error #10.
"""
...