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

commit 2f73b4164b10fd81cd6459a4de6115487a7bece7
Author: Wei Zhou <[email protected]>
AuthorDate: Sat Jun 13 11:41:11 2026 +0200

    Network Namespace: ignore errors when shutdown a network
---
 Network-Namespace/README.md                    | 20 ++++++++++---
 Network-Namespace/network-namespace-wrapper.sh | 39 +++++++++++++++++++++++---
 2 files changed, 51 insertions(+), 8 deletions(-)

diff --git a/Network-Namespace/README.md b/Network-Namespace/README.md
index c15e147..e0dcd0f 100644
--- a/Network-Namespace/README.md
+++ b/Network-Namespace/README.md
@@ -386,9 +386,20 @@ cmk updateRegisteredExtension \
     "details[1].key=username"              "details[1].value=root" \
     "details[2].key=sshkey"                
"details[2].value=<pem-key-contents>" \
     "details[3].key=guest.network.device"  "details[3].value=eth1" \
-    "details[4].key=public.network.device" "details[4].value=eth1"
+    "details[4].key=public.network.device" "details[4].value=eth1" \
+    "details[5].key=isolation_method"      "details[5].value=NetworkExtension"
 ```
 
+> **`isolation_method=NetworkExtension`** causes CloudStack to use
+> `NetworkExtensionGuestNetworkGuru` when designing guest networks backed by
+> this extension.  The network-namespace extension uses VLAN-based isolation
+> and does not rely on the script output from `implement-network` to override
+> the broadcast domain type, so this detail is not strictly required for basic
+> operation.  It is included here as best practice and for forward
+> compatibility — extensions that return `network.broadcast_domain_type` or
+> `network.broadcast_uri` from `implement-network` **must** set it or those
+> updates will be silently ignored by CloudStack.
+
 The `hosts` value is a comma-separated list of KVM host IPs; 
`ensure-network-device`
 picks one per network and stores it in `--network-extension-details`.  Use 
`sshkey`
 (PEM private key) for passwordless authentication, or `password` + `sshpass`.
@@ -1486,6 +1497,7 @@ name bridges as `br<eth>-<vlan>` and veth pairs as 
`vh-<vlan>-<id>` /
 | `vlan` | Guest VLAN tag |
 | `zone_id` | CloudStack zone ID |
 | `guest_type` | Guest network type: `"isolated"`, `"shared"`, or `"l2"`. The 
wrapper uses this to skip iptables / NAT / public-veth operations for `shared` 
networks. |
+| `network_state` | Guest network state: `"allocated"`, `"setup"`, 
`"implementing"`, `"implemented"`, `"shutdown"` or `"destroy"`. |
 | `gateway` | Guest network gateway |
 | `cidr` | Guest network CIDR |
 | `vpc_id` | Present when the network belongs to a VPC; namespace becomes 
`cs-vpc-<vpcId>` |
@@ -1508,9 +1520,9 @@ name bridges as `br<eth>-<vlan>` and veth pairs as 
`vh-<vlan>-<id>` /
 | `netmask` | VM NIC IPv4 netmask |
 | `default_nic` | `"false"` for secondary NICs (gateway DHCP option 
suppressed) |
 | `device_id` | NIC device slot index |
-| `nic_ip6_address` | VM NIC IPv6 address, when configured |
-| `nic_ip6_gateway` | VM NIC IPv6 gateway, when available |
-| `nic_ip6_cidr` | VM NIC IPv6 CIDR, when available |
+| `ip6_address` | VM NIC IPv6 address, when configured |
+| `ip6_gateway` | VM NIC IPv6 gateway, when available |
+| `ip6_cidr` | VM NIC IPv6 CIDR, when available |
 
 #### Public-IP fields
 
diff --git a/Network-Namespace/network-namespace-wrapper.sh 
b/Network-Namespace/network-namespace-wrapper.sh
index 0780600..d437754 100755
--- a/Network-Namespace/network-namespace-wrapper.sh
+++ b/Network-Namespace/network-namespace-wrapper.sh
@@ -423,15 +423,27 @@ ensure_host_bridge() {
 
     # VLAN sub-interface
     if ! ip link show "${vif}" >/dev/null 2>&1; then
-        ip link add link "${eth}" name "${vif}" type vlan id "${vlan}"
+        if ! ip link add link "${eth}" name "${vif}" type vlan id "${vlan}" 
2>/dev/null; then
+            if [ "${NETWORK_STATE:-}" = "shutdown" ]; then
+                log "ensure_host_bridge: failed to create ${vif} 
(network_state=shutdown, ignoring)"
+                echo "${br}"; return 0
+            fi
+            ip link add link "${eth}" name "${vif}" type vlan id "${vlan}"
+        fi
         log "Created VLAN interface ${vif}"
     fi
     ip link set "${vif}" up 2>/dev/null || true
 
     # Bridge
     if ! ip link show "${br}" >/dev/null 2>&1; then
-        ip link add name "${br}" type bridge
-        ip link set "${br}" up
+        if ! ip link add name "${br}" type bridge 2>/dev/null; then
+            if [ "${NETWORK_STATE:-}" = "shutdown" ]; then
+                log "ensure_host_bridge: failed to create ${br} 
(network_state=shutdown, ignoring)"
+                echo "${br}"; return 0
+            fi
+            ip link add name "${br}" type bridge
+        fi
+        ip link set "${br}" up 2>/dev/null || true
         log "Created host bridge ${br}"
     fi
 
@@ -444,6 +456,18 @@ ensure_host_bridge() {
     echo "${br}"
 }
 
+# _guard_ns_shutdown <caller-label>
+# When network_state is "shutdown" and the namespace is already gone, exit
+# successfully — the network has already been torn down on this host.
+# Call this after acquire_lock in any command that does namespace operations.
+_guard_ns_shutdown() {
+    [ "${NETWORK_STATE:-}" = "shutdown" ] || return 0
+    ip netns list 2>/dev/null | grep -q "^${NAMESPACE}\b" && return 0
+    log "${1:-command}: namespace ${NAMESPACE} not found 
(network_state=shutdown) — treating as success"
+    release_lock
+    exit 0
+}
+
 ensure_chain() {
     local table="$1" chain="$2"
     ip netns exec "${NAMESPACE}" iptables -t "${table}" -n -L "${chain}" 
>/dev/null 2>&1 || \
@@ -501,6 +525,7 @@ parse_args() {
     RESTORE_DATA=$(_payload_json_get "${payload_file}" "payload.restore_data")
     FW_RULES_JSON=$(_payload_json_get "${payload_file}" "payload.fw_rules")
     ACL_RULES_JSON=$(_payload_json_get "${payload_file}" "payload.acl_rules")
+    NETWORK_STATE=$(_payload_json_get "${payload_file}" 
"payload.network_state")
 
     [ -z "${SOURCE_NAT}" ] && SOURCE_NAT="false"
     [ -z "${LB_RULES_JSON}" ] && LB_RULES_JSON="[]"
@@ -899,6 +924,7 @@ cmd_assign_ip() {
     _load_state
     acquire_lock "${NETWORK_ID}"
 
+    _guard_ns_shutdown "assign-ip"
     log "assign-ip: network=${NETWORK_ID} ns=${NAMESPACE} ip=${PUBLIC_IP} 
source_nat=${SOURCE_NAT}"
     [ -z "${PUBLIC_IP}" ]   && die "Missing --public-ip"
     [ -z "${PUBLIC_VLAN}" ] && die "Missing --public-vlan"
@@ -1091,6 +1117,7 @@ cmd_add_static_nat() {
     _load_state
     acquire_lock "${NETWORK_ID}"
 
+    _guard_ns_shutdown "add-static-nat"
     log "add-static-nat: network=${NETWORK_ID} ns=${NAMESPACE} ${PUBLIC_IP} 
<-> ${PRIVATE_IP}"
     [ -z "${PUBLIC_IP}" ]  && die "Missing --public-ip"
     [ -z "${PRIVATE_IP}" ] && die "Missing --private-ip"
@@ -1198,6 +1225,7 @@ cmd_add_port_forward() {
     _load_state
     acquire_lock "${NETWORK_ID}"
 
+    _guard_ns_shutdown "add-port-forward"
     log "add-port-forward: network=${NETWORK_ID} ns=${NAMESPACE} 
${PUBLIC_IP}:${PUBLIC_PORT} -> ${PRIVATE_IP}:${PRIVATE_PORT} (${PROTOCOL})"
     [ -z "${PUBLIC_IP}" ]    && die "Missing --public-ip"
     [ -z "${PUBLIC_PORT}" ]  && die "Missing --public-port"
@@ -1687,7 +1715,8 @@ _svc_start_or_reload_apache2() {
     fi
 
     # Allow metadata traffic inbound to the namespace (INPUT) from guest 
subnet only.
-    if [ -n "${CIDR}" ]; then
+    # Skip if namespace is gone (e.g. network already shut down).
+    if [ -n "${CIDR}" ] && ip netns list 2>/dev/null | grep -q 
"^${NAMESPACE}\b"; then
         ip netns exec "${NAMESPACE}" iptables -t filter \
             -C INPUT -p tcp -s "${CIDR}" --dport 80 -j ACCEPT 2>/dev/null || \
         ip netns exec "${NAMESPACE}" iptables -t filter \
@@ -2490,6 +2519,7 @@ cmd_apply_fw_rules() {
     parse_args "$@"
     _load_state
     acquire_lock "${NETWORK_ID}"
+    _guard_ns_shutdown "apply-fw-rules"
     log "apply-fw-rules: network=${NETWORK_ID} ns=${NAMESPACE}"
 
     local fw_rules_file=""
@@ -3702,6 +3732,7 @@ cmd_apply_network_acl() {
     parse_args "$@"
     _load_state
     acquire_lock "${NETWORK_ID}"
+    _guard_ns_shutdown "apply-network-acl"
     log "apply-network-acl: network=${NETWORK_ID} ns=${NAMESPACE} cidr=${CIDR}"
 
     local acl_rules_file=""

Reply via email to