Follow-up from review of the router audit / router repair commands added for #2330.
Each item below is a case where the audit inspects state in only one direction, or omits a field, so real drift passes clean. They are the same class of bug and could land as one change. Line references are against python/kubectl-us-net/us_net/commands/router_health.py.
1. Stale HA_Chassis members in otherwise-live groups are PASS and never cleaned
_audit_network_ha_chassis_groups computes stale_names but only uses it in the FAIL branch. As soon as one live member exists the group is reported PASS and the stale members are not mentioned. _ha_chassis_group_repair_operations only populates remove_member_uuids for groups with zero live members, so no cleanup happens either.
tests/test_router_health.py::test_audit_loads_southbound_chassis_once encodes exactly this state (one live member, one dead) and asserts exit 0.
This is also the blocker for retiring phase 1 of scripts/cleanup_dead_ovn_ha_chassis.py.
2. Orphaned uplink-* LSPs are invisible
_audit_network_uplink starts from Neutron uplink-* ports and looks up the matching LSP, so an uplink-* LSP present in the OVN NB DB with no corresponding Neutron port is never reported. That exact condition is what scripts/cleanup_orphaned_ovn_uplinks.py exists to detect.
The equivalent OVN-to-Neutron direction is already covered for router ports (the orphaned LRP / orphaned peer LSP findings). Uplinks need the same treatment.
3. The shared uplink port's UUID-named LSP is never audited
Each uplink has two OVN objects:
- the raw localnet LSP named
uplink-<segment_id>, created by create_uplink_port in python/neutron-understack/neutron_understack/routers.py
- the shared Neutron port's own LSP, named by port UUID, which needs the explicit
delete_shared_port_lsp teardown path
_audit_network_uplink looks the LSP up by the Neutron port's name, so only the first is ever checked. The second is the one that leaks. The port UUID is already in hand at that point.
Worth a comment while in there: the name-based lookup works only because the localnet LSP and the shared Neutron port coincidentally share the uplink-<segment_id> string. That is load-bearing and non-obvious.
4. options:network_name on the uplink localnet LSP is unchecked
create_uplink_port sets options:network_name to the physnet, alongside the mcast/FDB options. The audit checks type, addresses and the VLAN tag but not network_name — the field that actually maps the localnet to physical hardware. A wrong or missing physnet is silent breakage.
Known trade-off, not a task
_network_uplink_ports issues one conn.network.ports(network_id=...) per router network and filters by name prefix client-side, so a single-router audit pulls every port on each attached network — potentially thousands on a busy external network. Neutron has no name-prefix filter, so there is no clean API-side fix. Recording it as a deliberate trade-off.
Lower priority
_router_ports accepts the three ML2/OVS-migrated interface device owners (network:router_interface_distributed, network:ha_router_replicated_interface, network:router_ha_interface), but neutron-understack's is_router_interface only covers network:router_interface and network:router_gateway, so no uplink is ever created for those. A migrated port would report expected exactly one uplink-* Neutron port, found none. Migrated environments only.
Follow-up from review of the
router audit/router repaircommands added for #2330.Each item below is a case where the audit inspects state in only one direction, or omits a field, so real drift passes clean. They are the same class of bug and could land as one change. Line references are against
python/kubectl-us-net/us_net/commands/router_health.py.1. Stale
HA_Chassismembers in otherwise-live groups arePASSand never cleaned_audit_network_ha_chassis_groupscomputesstale_namesbut only uses it in the FAIL branch. As soon as one live member exists the group is reportedPASSand the stale members are not mentioned._ha_chassis_group_repair_operationsonly populatesremove_member_uuidsfor groups with zero live members, so no cleanup happens either.tests/test_router_health.py::test_audit_loads_southbound_chassis_onceencodes exactly this state (one live member, one dead) and asserts exit 0.This is also the blocker for retiring phase 1 of
scripts/cleanup_dead_ovn_ha_chassis.py.2. Orphaned
uplink-*LSPs are invisible_audit_network_uplinkstarts from Neutronuplink-*ports and looks up the matching LSP, so anuplink-*LSP present in the OVN NB DB with no corresponding Neutron port is never reported. That exact condition is whatscripts/cleanup_orphaned_ovn_uplinks.pyexists to detect.The equivalent OVN-to-Neutron direction is already covered for router ports (the orphaned LRP / orphaned peer LSP findings). Uplinks need the same treatment.
3. The shared uplink port's UUID-named LSP is never audited
Each uplink has two OVN objects:
uplink-<segment_id>, created bycreate_uplink_portinpython/neutron-understack/neutron_understack/routers.pydelete_shared_port_lspteardown path_audit_network_uplinklooks the LSP up by the Neutron port's name, so only the first is ever checked. The second is the one that leaks. The port UUID is already in hand at that point.Worth a comment while in there: the name-based lookup works only because the localnet LSP and the shared Neutron port coincidentally share the
uplink-<segment_id>string. That is load-bearing and non-obvious.4.
options:network_nameon the uplink localnet LSP is uncheckedcreate_uplink_portsetsoptions:network_nameto the physnet, alongside the mcast/FDB options. The audit checkstype,addressesand the VLAN tag but notnetwork_name— the field that actually maps the localnet to physical hardware. A wrong or missing physnet is silent breakage.Known trade-off, not a task
_network_uplink_portsissues oneconn.network.ports(network_id=...)per router network and filters by name prefix client-side, so a single-routerauditpulls every port on each attached network — potentially thousands on a busy external network. Neutron has no name-prefix filter, so there is no clean API-side fix. Recording it as a deliberate trade-off.Lower priority
_router_portsaccepts the three ML2/OVS-migrated interface device owners (network:router_interface_distributed,network:ha_router_replicated_interface,network:router_ha_interface), but neutron-understack'sis_router_interfaceonly coversnetwork:router_interfaceandnetwork:router_gateway, so no uplink is ever created for those. A migrated port would reportexpected exactly one uplink-* Neutron port, found none. Migrated environments only.