VMDict.vmid and VMConfigDict.vmid were int. Proxmox numbers its guests,
but VMware identifies a VM by UUID, which an int cannot hold. The
provisioning dicts already carried vmid as a string; the read side now
matches. Proxmox reports "100".
VMConfigDict gains optional hardware details -- os_name, cpu_type,
sockets, cores_per_socket, firmware, machine and passthrough (PCI/USB,
as VMPassthroughDict) -- so netOrk's VM hardware view can be filled by
any hypervisor instead of reading Proxmox's raw config through the
driver's private API.
Also fixes the README's hypervisor example, which still named the
pre-contract snapshot_create.
BREAKING CHANGE: VMDict.vmid and VMConfigDict.vmid are str.
A role base used to fill its methods with `raise NotImplementedError`. That is
not neutral under multiple inheritance: the placeholder wins the MRO against a
sibling base's working implementation and silently replaces it. Adding one stub
to a base was therefore a breaking change for every driver mixing that base with
another, and it broke three of them — OpenWrt grew seven forwarding methods,
QNAP one, and OpenMediaVault avoided inheriting StorageDriver at all.
Role bases now declare their surface under `if TYPE_CHECKING` and implement
nothing. There is no longer anything to shadow, so a device can finally say what
it is:
class QnapQtsDriver(StorageDriver, HypervisorDriver, LinuxDriver):
The order of those bases is the ranking, read back by roles_of(),
role_keys_of() and primary_role_of() in the new roles module. Nothing restates
it: no precedence table, no attribute to override.
Two consequences, both wanted. `hasattr` is a truthful capability probe again,
because a method exists exactly when a driver provided it. And a method that was
never implemented now raises AttributeError rather than NotImplementedError, so
callers should ask before calling.
Shared behaviour moves out of the roles and into function classes, each holding
it once: PackageManagementMixin (was five byte-identical copies),
HealthMetricsMixin (five), ServiceControlMixin, UpdateMixin, NatVpnMixin,
MacAclMixin, FirewallRuleMixin, InterfaceFilterMixin.
BREAKING CHANGE: methods whose contract genuinely differed were renamed apart —
StorageDriver.get_services -> get_storage_services, the storage and hypervisor
snapshot writers -> create/delete/rollback_{volume,vm}_snapshot,
HypervisorDriver.get_storage -> get_vm_storage_pools, get_snapshots ->
get_vm_snapshots, SwitchDriver.get_dot1x_config -> get_dot1x_ports. Two
duplicate names collapsed onto the one already in use: get_pending_updates ->
get_available_updates and remove_package -> uninstall_package.
Also fixes __doc__ being None on all seven role bases: TYPE_LABEL was assigned
above the triple-quoted string, which made it a bare expression rather than a
docstring.
Each abstract base class now carries a TYPE_LABEL: str attribute that
describes the device category in human-readable form:
AccessPointDriver → "Access Point"
FirewallDriver → "Firewall"
HypervisorDriver → "Hypervisor"
OSDriver → "OS"
ResidentialGatewayDriver → "Gateway"
StorageDriver → "Storage"
SwitchDriver → "Switch"
Concrete drivers can override TYPE_LABEL to express a more specific
category (e.g. LinuxDriver sets "Linux"). The backend reads this
attribute to expose a type_label in the DriverInfo API response,
replacing the hardcoded DRIVER_TYPE map in the frontend.
23 tests covering presence, value, inheritance, and override.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Introduce OSDriver abstract base class (os.py) for general-purpose OS
drivers (Linux, BSD, macOS) — registers package management, service
management, users, processes, cron job and the two new OS-specific
extension points
- Add get_docker_info() contract: returns DockerInfoDict covering
containers, images, volumes, networks and outdated image detection
- Add run_device_action() contract: generic extensibility point for
driver-specific one-off administrative actions
- Fix duplicate TypedDicts in models.py: remove early shadow definitions
of UserDict, ProcessDict, CronJobDict, ApplyUpdatesResultDict from the
Common section; keep the more complete definitions in the OS section
- Add Docker TypedDicts: DockerContainerDict, DockerImageDict,
DockerVolumeDict, DockerNetworkDict, DockerInfoDict
- Add DeviceActionResultDict
- Export OSDriver from package __init__; bump version to 0.3.0
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add ServiceDict and UpdateDict TypedDicts to models
- Add get_services() / manage_service() abstract methods for init-system interaction
- Add get_available_updates() / apply_updates() for package upgrade workflows
- Add _filter_interfaces() helper to exclude lo and phy* interfaces from interface dicts
- Extend WirelessClientDict with optional ip, hostname, and lease_end fields
- Add optional description field to VPNTunnelDict
- Bump version 0.1.0 → 0.2.0