Enhance documentation and validation for VM lifecycle and network management; add tests for legacy cleanup and address validation.

This commit is contained in:
2026-09-09 19:21:00 +05:30
parent ee26962902
commit 3d7af4adb8
9 changed files with 366 additions and 35 deletions
+20 -1
View File
@@ -89,6 +89,8 @@ class FirewallManager:
current = self._read_table(table["name"])
if current is not None:
self._transaction([self._delete_owned(current, table)])
if self._read_table(table["name"]) is not None:
raise UvmError("firewall table remains or was replaced after removal; keep the VM reservation")
def status(self, vm: VmRecord) -> str:
"""Inspect actual hooks, ordered rules, identity and complete port set.
@@ -137,9 +139,26 @@ class FirewallManager:
All four tuple addresses are checked, including NAT reply tuples. A
failed deletion is tolerated only if a successful list proves absence.
The caller must retain the address reservation until this succeeds.
Legacy non-RFC1918 addresses are allowed here; only the recorded subnet,
when present, determines host-address boundaries, never current settings.
"""
self._require_offline(vm)
address = self._guest_ip(vm)
try:
address = IPv4Address(str(vm.guest_ip))
stored_network = getattr(vm, "network", None)
network = IPv4Network(str(stored_network)) if stored_network is not None else None
except (ValueError, TypeError) as error:
raise ValidationError("conntrack cleanup requires a valid persisted IPv4 address/network") from error
if (address.is_unspecified or address.is_loopback or address.is_link_local
or address.is_multicast or address == IPv4Address("255.255.255.255")):
raise ValidationError("conntrack cleanup requires a usable persisted unicast IPv4 address")
# /31 point-to-point subnets and /32 host routes have no broadcast address.
if network is not None and (
address not in network or (network.prefixlen < 31
and address in {network.network_address, network.broadcast_address})
):
raise ValidationError("conntrack cleanup requires a usable guest IP inside its persisted network")
address = str(address)
for field in ("--orig-src", "--orig-dst", "--reply-src", "--reply-dst"):
selector = ("--family", "ipv4", field, address)
self._run(("conntrack", "--delete", *selector), check=False)