Commit Graph
10 Commits
Author SHA1 Message Date
christianmanivong b5c40019af feat: a new VM's CPU model can be chosen, from a list the hypervisor offers
create_vm_from_cloud_init takes cpu_type. Proxmox gives a VM created without
one the kvm64 model, which has no AVX, so MongoDB 5.0 and later do not start
there, and netOrk's graylog role failed on every VM it provisioned
(netork#494). Which model is right depends on the cluster: host cannot
live-migrate between different CPUs, x86-64-v3 does not start on a CPU older
than Haswell. So the caller chooses.

get_vm_cpu_types() is the new, optional listing behind that choice. Each
VMCpuTypeDict names the model, says what it is for, lists the /proc/cpuinfo
flags the guest gets (so a caller can ask "does this give AVX?" without
knowing model names), whether the node at hand can run it, and which one is
the default. A hypervisor whose VMs have no per-VM CPU model (VMware sets CPU
compatibility per cluster) does not implement it and must reject any
cpu_type other than None.

Both declarations sit under TYPE_CHECKING like the rest of the contract, so
hasattr stays a truthful capability probe; the tests read the signature from
the source.
2026-10-04 12:02:38 +02:00
christianmanivong 31949eca0a feat: port forwards are a firewall reader too, and only the WAN's
get_port_forwards was declared on ResidentialGatewayDriver alone, as if a
port forward were a home-router feature. A firewall forwards ports just the
same (OPNsense calls it destination NAT), and netOrk asks both: is this host
reachable from the internet, which CVEs are exposed. The declaration moves to
NatVpnMixin, where the two roles already overlap, and PortForwardDict next to
NATTranslationDict.

The contract now says what counts. Destination NAT between internal networks
and rules that only exempt traffic are not port forwards: callers read every
entry as "reachable from outside". "ANY" forwards every protocol and an
external port of 0 every port -- a whole host forwarded is the most exposed
case and must not fall out for lack of a port number.

Declaration only, under TYPE_CHECKING: nothing changes at runtime.
2026-10-03 16:30:32 +02:00
christianmanivong 34b8f10ffa feat: reboot_host contract, guest agent declaration, port group targets
HostRebootMixin declares reboot_host(), mixed into DeviceTypeDriver so
any device may be restartable. netOrk restarted hosts by sending
/sbin/reboot through a driver's private _send_command; a driver talking
to an API had no such method and the reboot was silently skipped.

HypervisorDriver gains GUEST_AGENT_PACKAGES / GUEST_AGENT_RUNCMD, the
agent cloud-init installs so the hypervisor can read a new VM's IP.
The default stays qemu-guest-agent; VMware declares open-vm-tools.

NetworkTargetDict.kind may be "portgroup": a VMware port group fixes its
VLAN like an SDN vnet does, without being one.
2026-09-24 10:00:04 +02:00
christianmanivong 1ce0a0b6f2 feat!: a VM's vmid is a string, and its config can describe its hardware
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.
2026-09-24 09:06:36 +02:00
christianmanivong d8dbc7a442 feat!: role bases declare their methods instead of stubbing them
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.
2026-08-21 12:49:45 +07:00
christianmanivong b8977cdaa5 feat(dhcp): add DhcpServerMixin for static DHCP reservations
Adds the generic half of DHCP reservation management: diff_dhcp_reservations
matches desired against live reservations by normalised MAC, and
apply_dhcp_reservationset walks the diff and commits once at the end.

Both are concrete here because neither is vendor-specific — only
get_dhcp_reservations/apply_dhcp_reservation/commit_dhcp_reservations touch
the device (Kea REST on OPNsense, dnsmasq/odhcpd UCI on OpenWrt).

Two deliberate choices:

- The MAC is the matching key, not a description as with firewall rules. A
  reservation has a natural identity and this is it. That also means a host
  moving to another VLAN is an update of the existing entry rather than a
  second one for the same MAC.
- An empty diff skips the commit. Committing reloads the DHCP daemon and
  drops in-flight requests, which is too high a price for a no-op run. This
  differs from apply_firewall_ruleset, which always commits.

Live reservations with no desired counterpart are never reported for
deletion — a DHCP server routinely carries hand-created entries the caller's
desired set was never meant to describe.

Mixed into FirewallDriver and ResidentialGatewayDriver: both device types
commonly run the DHCP server for their networks.
2026-08-19 07:24:39 +07:00
christianmanivong a211629875 feat(ping): add a generic ping sweep every driver inherits
Sweeping a range is orchestration, not device mechanics: the only
vendor-specific part is executing a single ping, and NAPALM already
standardises that. PingSweepMixin therefore owns the loop, the reply parsing,
the target cap and the progress reporting, and is mixed into DeviceTypeDriver
so any driver implementing ping() becomes a usable sweep source without
writing sweep code of its own.

driver_supports_ping() answers "can this driver ping?" by introspection
instead of a hand-maintained list, with SUPPORTS_PING = False as the opt-out
for a driver that inherits a ping it cannot actually use.

The generic implementation is deliberately sequential — a NAPALM connection is
a single session and not safe to drive from several threads at once. A driver
whose device offers something faster overrides ping_sweep and keeps the return
shape; see napalm-opnsense's batched job API version.
2026-08-13 16:50:47 +07:00
christianmanivong b3d67d1517 docs: document generic-vs-device-specific design principle
Makes explicit a rule that's been applied ad hoc: matching/comparison/
orchestration logic that's identical across every driver of a device-type
belongs as a concrete method on the abstract base class; only actual
device communication (REST/CLI/payload format) belongs in the concrete
vendor driver as an implementation of an abstract method. Uses the
upcoming FirewallDriver diff/apply mechanism as the worked example.
2026-07-20 14:56:15 +02:00
christianmanivongandClaude Sonnet 4.6 197e0b4ab7 feat: add OSDriver base class with Docker and device action APIs
- 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>
2026-06-01 01:53:10 +02:00
christianmanivong b03e4355c9 Initial release (v0.1.0)
Add abstract device-type base classes for NAPALM drivers:
- AccessPointDriver  (wireless APs)
- SwitchDriver       (Ethernet switches)
- FirewallDriver     (firewalls / UTM)
- HypervisorDriver   (Proxmox VE, ESXi, KVM, Hyper-V)
- StorageDriver      (NAS/SAN appliances)

All return types modelled as TypedDicts in napalm_device_types.models.
2026-05-11 21:31:52 +02:00