This is an automated email from the ASF dual-hosted git repository.
weizhouapache pushed a commit to branch network-namespace
in repository https://gitbox.apache.org/repos/asf/cloudstack-extensions.git
The following commit(s) were added to refs/heads/network-namespace by this push:
new c1fc905 NE: remove host bridge and VLAN sub-interface if no VMs
c1fc905 is described below
commit c1fc905577035635f0430d67a59583ad8c30ff3a
Author: Wei Zhou <[email protected]>
AuthorDate: Mon Jul 27 17:49:01 2026 +0200
NE: remove host bridge and VLAN sub-interface if no VMs
---
Network-Namespace/README.md | 17 +++++---
Network-Namespace/network-namespace-wrapper.sh | 54 ++++++++++++++++++++++++++
2 files changed, 66 insertions(+), 5 deletions(-)
diff --git a/Network-Namespace/README.md b/Network-Namespace/README.md
index 018ba8a..267cff2 100644
--- a/Network-Namespace/README.md
+++ b/Network-Namespace/README.md
@@ -648,8 +648,11 @@ network-namespace.sh destroy-network --network-id 42
--vlan 100
6. For **VPC tier** networks: deregisters this tier from the VPC — namespace is
only removed by a subsequent `destroy-vpc` call.
-> The host bridge `breth1-100` and VLAN sub-interface `eth1.100` are **not**
-> removed — they may still be used by other networks or for VM connectivity.
+> The host bridge `breth1-100` and VLAN sub-interface `eth1.100` are removed
+> once nothing else is attached to the bridge (checked via
+> `teardown_host_bridge_if_unused`). If another network/tenant is still
+> sharing the same physical VLAN, or a VM tap is still attached, the bridge
+> and VLAN sub-interface are left in place.
### 9. Unregister and delete the extension
@@ -886,8 +889,9 @@ Actions:
removed by a subsequent `destroy-vpc` call.
> The host bridge `br<GUEST_ETH>-<vlan>` and VLAN sub-interface
> `GUEST_ETH.<vlan>`
-> are NOT removed on destroy — they may still be used by other networks or for
-> VM connectivity.
+> are removed on destroy once the bridge has no remaining member interfaces
+> (`teardown_host_bridge_if_unused`). This is a no-op if another network/tenant
+> is still sharing the same physical VLAN, or a VM tap is still attached.
### VPC lifecycle commands: `implement-vpc`, `update-vpc-source-nat-ip`,
`shutdown-vpc`, `destroy-vpc`
@@ -1042,7 +1046,10 @@ Actions:
4. Remove host route `<public-ip>/32`.
5. Remove IP address from `vpn-<pvlan>-<id>` inside namespace.
6. If no other IPs share the same `<pvlan>/<id>` combination, delete
- `vph-<pvlan>-<id>` (host veth).
+ `vph-<pvlan>-<id>` (host veth), then attempt to remove the public bridge
+ `br<PUB_ETH>-<pvlan>` and VLAN sub-interface `PUB_ETH.<pvlan>` — a no-op
+ (`teardown_host_bridge_if_unused`) if another network/tenant is still
+ sharing the same public VLAN.
7. Remove state files.
### `add-static-nat`
diff --git a/Network-Namespace/network-namespace-wrapper.sh
b/Network-Namespace/network-namespace-wrapper.sh
index cf61e12..93072d0 100755
--- a/Network-Namespace/network-namespace-wrapper.sh
+++ b/Network-Namespace/network-namespace-wrapper.sh
@@ -456,6 +456,47 @@ ensure_host_bridge() {
echo "${br}"
}
+# teardown_host_bridge_if_unused <eth> <vlan>
+# Counterpart to ensure_host_bridge(): removes the VLAN sub-interface
(ethX.vlan)
+# and the bridge (br<eth>-<vlan>) it created, but ONLY if no VM (or other
+# consumer, e.g. another VPC tier/network/tenant riding the same public VLAN)
+# still has an interface plugged into the bridge.
+#
+# ensure_host_bridge() always enslaves the VLAN uplink (ethX.vlan) into the
+# bridge, so it is always present as a bridge member — it must be excluded
+# from the "is anything still using this bridge" check, otherwise the bridge
+# would never be considered unused.
+#
+# Safe to call unconditionally from any teardown path — it is a no-op
+# whenever the bridge is still in use (by a VM tap or otherwise), or already
+# gone.
+teardown_host_bridge_if_unused() {
+ local eth="$1" vlan_raw="$2" vlan br vif members
+ vlan=$(normalize_vlan "${vlan_raw}")
+ br=$(host_bridge_name "${eth}" "${vlan}")
+ vif="${eth}.${vlan}"
+
+ if ! ip link show "${br}" >/dev/null 2>&1; then
+ return 0
+ fi
+
+ if [ -d "/sys/class/net/${br}/brif" ]; then
+ members=$(ls -A "/sys/class/net/${br}/brif" 2>/dev/null | grep -v -x
"${vif}" || true)
+ if [ -n "${members}" ]; then
+ log "teardown_host_bridge_if_unused: ${br} still has member
interfaces (${members}), leaving in place"
+ return 0
+ fi
+ fi
+
+ ip link del "${br}" 2>/dev/null || true
+ log "Removed host bridge ${br}"
+
+ if ip link show "${vif}" >/dev/null 2>&1; then
+ ip link del "${vif}" 2>/dev/null || true
+ log "Removed VLAN interface ${vif}"
+ fi
+}
+
# _guard_ns_teardown <caller-label>
# When network_state is "shutdown", "destroy", or "allocated" and the namespace
# is already gone, exit successfully — the network has been torn down or was
@@ -896,6 +937,13 @@ cmd_destroy_network() {
veth_h=$(veth_host_name "${VLAN}" "${CHOSEN_ID}")
ip link del "${veth_h}" 2>/dev/null || true
+ # The guest VLAN bridge/sub-interface (ensure_host_bridge) is where VM taps
+ # are plugged in directly by the hypervisor. Destroying a network implies
+ # no VMs remain on it, so it is safe to tear these down here (guarded by
+ # teardown_host_bridge_if_unused() in case anything unexpected is still
+ # attached).
+ teardown_host_bridge_if_unused "${GUEST_ETH}" "${VLAN}"
+
local vsd; vsd=$(_vpc_state_dir)
# Public veth pairs and their state files only exist for Isolated networks
@@ -920,6 +968,9 @@ cmd_destroy_network() {
pveth_h=$(pub_veth_host_name "${pvlan}" "${CHOSEN_ID}")
ip link del "${pveth_h}" 2>/dev/null || true
rm -f "${f}" "${f%.pvlan}" "${tier_f}" 2>/dev/null || true
+ # Public bridge may be shared by other tiers/networks/tenants
on
+ # the same public VLAN; only removed once nothing else uses it.
+ teardown_host_bridge_if_unused "${PUB_ETH}" "${pvlan}"
done
fi
fi
@@ -1127,6 +1178,9 @@ cmd_release_ip() {
if [ "${remaining}" -eq 0 ]; then
ip link del "${pveth_h}" 2>/dev/null || true
log "release-ip: removed public veth ${pveth_h}"
+ # Public bridge may still be shared by other networks/tenants on the
+ # same public VLAN; only removed once nothing else uses it.
+ teardown_host_bridge_if_unused "${PUB_ETH}" "${PUBLIC_VLAN}"
fi
# Remove default route if no IPs remain