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 44f9b66  Network Namespace: implement Shared network when prepare the 
first NIC
44f9b66 is described below

commit 44f9b66037bab3b758167e9d4bc7ac354336acd2
Author: Wei Zhou <[email protected]>
AuthorDate: Thu Jun 4 15:04:22 2026 +0200

    Network Namespace: implement Shared network when prepare the first NIC
---
 Network-Namespace/network-namespace-wrapper.sh | 234 ++++++++++++++-----------
 1 file changed, 130 insertions(+), 104 deletions(-)

diff --git a/Network-Namespace/network-namespace-wrapper.sh 
b/Network-Namespace/network-namespace-wrapper.sh
index e723a95..4b653fc 100755
--- a/Network-Namespace/network-namespace-wrapper.sh
+++ b/Network-Namespace/network-namespace-wrapper.sh
@@ -466,7 +466,7 @@ parse_args() {
     local payload_file="$1"
 
     NETWORK_ID=$(_payload_json_get "${payload_file}" "payload.network_id")
-    NAMESPACE=$(_payload_json_get "${payload_file}" "payload.namespace")
+    GUEST_TYPE=$(_payload_json_get "${payload_file}" "payload.guest_type")
     VPC_ID=$(_payload_json_get "${payload_file}" "payload.vpc_id")
     VLAN=$(_payload_json_get "${payload_file}" "payload.vlan")
     GATEWAY=$(_payload_json_get "${payload_file}" "payload.gateway")
@@ -505,14 +505,12 @@ parse_args() {
     [ -z "${NETWORK_ID}" ] && die "Missing --network-id"
 
     # Namespace: VPC → cs-vpc-<vpcId>; standalone → cs-net-<networkId>
-    if [ -z "${NAMESPACE}" ]; then
-        if [ -n "${VPC_ID}" ]; then
-            NAMESPACE="cs-vpc-${VPC_ID}"
-        else
-            local NS_FROM_DETAILS
-            NS_FROM_DETAILS=$(_json_get "${EXTENSION_DETAILS}" "namespace")
-            NAMESPACE="${NS_FROM_DETAILS:-cs-net-${NETWORK_ID}}"
-        fi
+    if [ -n "${VPC_ID}" ]; then
+        NAMESPACE="cs-vpc-${VPC_ID}"
+    else
+        local NS_FROM_DETAILS
+        NS_FROM_DETAILS=$(_json_get "${EXTENSION_DETAILS}" "namespace")
+        NAMESPACE="${NS_FROM_DETAILS:-cs-net-${NETWORK_ID}}"
     fi
 
     # CHOSEN_ID selects vpc-id when present, otherwise network-id
@@ -661,27 +659,29 @@ cmd_implement_network() {
     # from appearing on the interface (idempotent)
     ip netns exec "${NAMESPACE}" sysctl -w 
net.ipv6.conf."${veth_n}".disable_ipv6=1 >/dev/null 2>&1 || true
 
-    # ---- 5. IP forwarding ----
-    ip netns exec "${NAMESPACE}" sysctl -w net.ipv4.ip_forward=1 >/dev/null 
2>&1 || true
+    if [ "${GUEST_TYPE}" = "isolated" ]; then
+         # ---- 5. IP forwarding  (needed for Isolated networks only) ----
+        ip netns exec "${NAMESPACE}" sysctl -w net.ipv4.ip_forward=1 
>/dev/null 2>&1 || true
 
-    # ---- 6. iptables chains ----
-    ensure_chain nat    "${nchain_pr}"
-    ensure_chain nat    "${nchain_post}"
-    ensure_chain filter "${fchain}"
-    ensure_jump  nat    PREROUTING  "${nchain_pr}"
-    ensure_jump  nat    POSTROUTING "${nchain_post}"
-    ensure_jump  filter FORWARD     "${fchain}"
+        # ---- 6. iptables chains  (needed for Isolated networks only) ----
+        ensure_chain nat    "${nchain_pr}"
+        ensure_chain nat    "${nchain_post}"
+        ensure_chain filter "${fchain}"
+        ensure_jump  nat    PREROUTING  "${nchain_pr}"
+        ensure_jump  nat    POSTROUTING "${nchain_post}"
+        ensure_jump  filter FORWARD     "${fchain}"
 
-    # Allow forwarding for guest traffic in/out of veth-ns-<vlan>
-    ip netns exec "${NAMESPACE}" iptables -t filter \
-        -C "${fchain}" -i "${veth_n}" -j ACCEPT 2>/dev/null || \
-    ip netns exec "${NAMESPACE}" iptables -t filter \
-        -A "${fchain}" -i "${veth_n}" -j ACCEPT
+        # Allow forwarding for guest traffic in/out of veth-ns-<vlan>
+        ip netns exec "${NAMESPACE}" iptables -t filter \
+            -C "${fchain}" -i "${veth_n}" -j ACCEPT 2>/dev/null || \
+        ip netns exec "${NAMESPACE}" iptables -t filter \
+            -A "${fchain}" -i "${veth_n}" -j ACCEPT
 
-    ip netns exec "${NAMESPACE}" iptables -t filter \
-        -C "${fchain}" -o "${veth_n}" -m state --state RELATED,ESTABLISHED -j 
ACCEPT 2>/dev/null || \
-    ip netns exec "${NAMESPACE}" iptables -t filter \
-        -A "${fchain}" -o "${veth_n}" -m state --state RELATED,ESTABLISHED -j 
ACCEPT
+        ip netns exec "${NAMESPACE}" iptables -t filter \
+            -C "${fchain}" -o "${veth_n}" -m state --state RELATED,ESTABLISHED 
-j ACCEPT 2>/dev/null || \
+        ip netns exec "${NAMESPACE}" iptables -t filter \
+            -A "${fchain}" -o "${veth_n}" -m state --state RELATED,ESTABLISHED 
-j ACCEPT
+    fi
 
     # ---- 7. Persist state ----
     # Per-network state → network-<networkId>/
@@ -719,47 +719,54 @@ cmd_shutdown_network() {
     _load_state
     acquire_lock "${NETWORK_ID}"
 
-    log "shutdown-network: network=${NETWORK_ID} ns=${NAMESPACE} vpc=${VPC_ID}"
+    log "shutdown-network: network=${NETWORK_ID} ns=${NAMESPACE} 
guest_type=${GUEST_TYPE} vpc=${VPC_ID}"
 
-    local nchain_pr nchain_post fchain
-    nchain_pr="${CHAIN_PREFIX}_${NETWORK_ID}_PR"
-    nchain_post="${CHAIN_PREFIX}_${NETWORK_ID}_POST"
-    fchain=$(filter_chain "${NETWORK_ID}")
+    # iptables NAT/FILTER chains only exist for non-Shared networks (Shared
+    # networks have no routing/NAT role; chains are never created for them).
+    if [ "${GUEST_TYPE}" = "isolated" ]; then
+        local nchain_pr nchain_post fchain
+        nchain_pr="${CHAIN_PREFIX}_${NETWORK_ID}_PR"
+        nchain_post="${CHAIN_PREFIX}_${NETWORK_ID}_POST"
+        fchain=$(filter_chain "${NETWORK_ID}")
+
+        # Remove iptables chain jumps for this network
+        ip netns exec "${NAMESPACE}" iptables -t nat    -D PREROUTING  -j 
"${nchain_pr}"   2>/dev/null || true
+        ip netns exec "${NAMESPACE}" iptables -t nat    -D POSTROUTING -j 
"${nchain_post}" 2>/dev/null || true
+        ip netns exec "${NAMESPACE}" iptables -t filter -D FORWARD     -j 
"${fchain}"      2>/dev/null || true
+
+        # Flush and delete this network's chains
+        ip netns exec "${NAMESPACE}" iptables -t nat    -F "${nchain_pr}"   
2>/dev/null || true
+        ip netns exec "${NAMESPACE}" iptables -t nat    -X "${nchain_pr}"   
2>/dev/null || true
+        ip netns exec "${NAMESPACE}" iptables -t nat    -F "${nchain_post}" 
2>/dev/null || true
+        ip netns exec "${NAMESPACE}" iptables -t nat    -X "${nchain_post}" 
2>/dev/null || true
+        ip netns exec "${NAMESPACE}" iptables -t filter -F "${fchain}"      
2>/dev/null || true
+        ip netns exec "${NAMESPACE}" iptables -t filter -X "${fchain}"      
2>/dev/null || true
+    fi
 
-    # Remove iptables chain jumps for this network
-    ip netns exec "${NAMESPACE}" iptables -t nat    -D PREROUTING  -j 
"${nchain_pr}"   2>/dev/null || true
-    ip netns exec "${NAMESPACE}" iptables -t nat    -D POSTROUTING -j 
"${nchain_post}" 2>/dev/null || true
-    ip netns exec "${NAMESPACE}" iptables -t filter -D FORWARD     -j 
"${fchain}"      2>/dev/null || true
-
-    # Flush and delete this network's chains
-    ip netns exec "${NAMESPACE}" iptables -t nat    -F "${nchain_pr}"   
2>/dev/null || true
-    ip netns exec "${NAMESPACE}" iptables -t nat    -X "${nchain_pr}"   
2>/dev/null || true
-    ip netns exec "${NAMESPACE}" iptables -t nat    -F "${nchain_post}" 
2>/dev/null || true
-    ip netns exec "${NAMESPACE}" iptables -t nat    -X "${nchain_post}" 
2>/dev/null || true
-    ip netns exec "${NAMESPACE}" iptables -t filter -F "${fchain}"      
2>/dev/null || true
-    ip netns exec "${NAMESPACE}" iptables -t filter -X "${fchain}"      
2>/dev/null || true
-
-    # Remove public veth pairs owned by THIS tier only (guarded by .tier file).
-    # IPs owned by other tiers are left untouched so those tiers keep working.
-    # Backward compat: if no .tier file exists assume the IP belongs here.
+    # Public veth pairs and NAT state only exist for non-Shared networks.
     local vsd; vsd=$(_vpc_state_dir)
-    if [ -d "${vsd}/ips" ]; then
-        for f in "${vsd}/ips/"*.pvlan; do
-            [ -f "${f}" ] || continue
-            local tier_f; tier_f="${f%.pvlan}.tier"
-            if [ -f "${tier_f}" ]; then
-                local owner_tier; owner_tier=$(cat "${tier_f}" 2>/dev/null || 
true)
-                if [ -n "${owner_tier}" ] && [ "${owner_tier}" != 
"${NETWORK_ID}" ]; then
-                    log "shutdown-network: skipping veth for $(basename 
"${f%.pvlan}") (owned by tier ${owner_tier})"
-                    continue
+    if [ "${GUEST_TYPE}" = "isolated" ]; then
+        # Remove public veth pairs owned by THIS tier only (guarded by .tier 
file).
+        # IPs owned by other tiers are left untouched so those tiers keep 
working.
+        # Backward compat: if no .tier file exists assume the IP belongs here.
+        if [ -d "${vsd}/ips" ]; then
+            for f in "${vsd}/ips/"*.pvlan; do
+                [ -f "${f}" ] || continue
+                local tier_f; tier_f="${f%.pvlan}.tier"
+                if [ -f "${tier_f}" ]; then
+                    local owner_tier; owner_tier=$(cat "${tier_f}" 2>/dev/null 
|| true)
+                    if [ -n "${owner_tier}" ] && [ "${owner_tier}" != 
"${NETWORK_ID}" ]; then
+                        log "shutdown-network: skipping veth for $(basename 
"${f%.pvlan}") (owned by tier ${owner_tier})"
+                        continue
+                    fi
                 fi
-            fi
-            local pvlan pveth_h
-            pvlan=$(cat "${f}")
-            pveth_h=$(pub_veth_host_name "${pvlan}" "${CHOSEN_ID}")
-            ip link del "${pveth_h}" 2>/dev/null || true
-            log "shutdown-network: removed public veth ${pveth_h}"
-        done
+                local pvlan pveth_h
+                pvlan=$(cat "${f}")
+                pveth_h=$(pub_veth_host_name "${pvlan}" "${CHOSEN_ID}")
+                ip link del "${pveth_h}" 2>/dev/null || true
+                log "shutdown-network: removed public veth ${pveth_h}"
+            done
+        fi
     fi
 
     # Remove this tier's guest veth pair (host-side)
@@ -768,15 +775,16 @@ cmd_shutdown_network() {
     ip link del "${veth_h}" 2>/dev/null || true
     log "shutdown-network: removed guest veth ${veth_h}"
 
-    # Clean transient public IP state.
-    # For isolated networks the state dir is per-network, so wipe it entirely.
-    # For VPC networks the ips/ dir is shared across all tiers; only remove the
-    # per-tier rule directories so other tiers are not affected.
-    if [ -z "${VPC_ID}" ]; then
-        rm -rf "${vsd}/ips" "${vsd}/static-nat" "${vsd}/port-forward"
-    else
-        rm -rf "${STATE_DIR}/network-${NETWORK_ID}/static-nat" \
-               "${STATE_DIR}/network-${NETWORK_ID}/port-forward" 2>/dev/null 
|| true
+    # Clean transient state directories.
+    # Shared networks have no public IP / NAT / port-forward state, so only
+    # isolated/VPC networks need this cleanup.
+    if [ "${GUEST_TYPE}" = "isolated" ]; then
+        if [ -z "${VPC_ID}" ]; then
+            rm -rf "${vsd}/ips" "${vsd}/static-nat" "${vsd}/port-forward"
+        else
+            rm -rf "${STATE_DIR}/network-${NETWORK_ID}/static-nat" \
+                   "${STATE_DIR}/network-${NETWORK_ID}/port-forward" 
2>/dev/null || true
+        fi
     fi
 
     # Stop per-network services (dnsmasq, haproxy, apache2, passwd-server)
@@ -785,8 +793,9 @@ cmd_shutdown_network() {
     _svc_stop_apache2
     _svc_stop_passwd_server
 
-    # For isolated networks delete the namespace; VPC namespaces are shared
-    # across tiers and must only be deleted when the last tier is destroyed.
+    # For isolated and Shared networks delete the namespace directly.
+    # VPC namespaces are shared across tiers and must only be deleted
+    # when the last tier is destroyed (shutdown-vpc).
     if [ -z "${VPC_ID}" ]; then
         ip netns del "${NAMESPACE}" 2>/dev/null || true
         rm -rf "/etc/netns/${NAMESPACE}" 2>/dev/null || true
@@ -811,7 +820,7 @@ cmd_destroy_network() {
     _load_state
     acquire_lock "${NETWORK_ID}"
 
-    log "destroy-network: network=${NETWORK_ID} ns=${NAMESPACE} vpc=${VPC_ID}"
+    log "destroy-network: network=${NETWORK_ID} ns=${NAMESPACE} 
guest_type=${GUEST_TYPE} vpc=${VPC_ID}"
 
     # Remove this tier's guest veth host-side
     local veth_h
@@ -820,26 +829,30 @@ cmd_destroy_network() {
 
     local vsd; vsd=$(_vpc_state_dir)
 
-    # Remove public veth pairs that belong to THIS tier (guarded by .tier 
file).
-    # IPs owned by other tiers are left untouched so those tiers keep working.
-    # Backward compat: if no .tier file exists assume the IP belongs here.
-    if [ -d "${vsd}/ips" ]; then
-        for f in "${vsd}/ips/"*.pvlan; do
-            [ -f "${f}" ] || continue
-            local tier_f; tier_f="${f%.pvlan}.tier"
-            if [ -f "${tier_f}" ]; then
-                local owner_tier; owner_tier=$(cat "${tier_f}" 2>/dev/null || 
true)
-                if [ -n "${owner_tier}" ] && [ "${owner_tier}" != 
"${NETWORK_ID}" ]; then
-                    log "destroy-network: skipping veth for $(basename 
"${f%.pvlan}") (owned by tier ${owner_tier})"
-                    continue
+    # Public veth pairs and their state files only exist for Isolated networks
+    # (Shared networks have no NAT/routing role, so no public veths are 
created).
+    if [ "${GUEST_TYPE}" = "isolated" ]; then
+        # Remove public veth pairs that belong to THIS tier (guarded by .tier 
file).
+        # IPs owned by other tiers are left untouched so those tiers keep 
working.
+        # Backward compat: if no .tier file exists assume the IP belongs here.
+        if [ -d "${vsd}/ips" ]; then
+            for f in "${vsd}/ips/"*.pvlan; do
+                [ -f "${f}" ] || continue
+                local tier_f; tier_f="${f%.pvlan}.tier"
+                if [ -f "${tier_f}" ]; then
+                    local owner_tier; owner_tier=$(cat "${tier_f}" 2>/dev/null 
|| true)
+                    if [ -n "${owner_tier}" ] && [ "${owner_tier}" != 
"${NETWORK_ID}" ]; then
+                        log "destroy-network: skipping veth for $(basename 
"${f%.pvlan}") (owned by tier ${owner_tier})"
+                        continue
+                    fi
                 fi
-            fi
-            local pvlan pveth_h
-            pvlan=$(cat "${f}")
-            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
-        done
+                local pvlan pveth_h
+                pvlan=$(cat "${f}")
+                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
+            done
+        fi
     fi
 
     # Stop per-network services before removing state
@@ -851,13 +864,14 @@ cmd_destroy_network() {
     # Remove this network's per-tier state directory
     rm -rf "$(_net_state_dir)"
 
-    # Deregister this tier from VPC tracking
+    # Deregister this tier from VPC tracking, or delete the namespace for
+    # standalone networks (Isolated and Shared each own their own namespace).
     if [ -n "${VPC_ID}" ]; then
         rm -f "${vsd}/tiers/${NETWORK_ID}" 2>/dev/null || true
         # The VPC namespace is managed by shutdown-vpc / destroy-vpc — do NOT 
delete it here.
         log "destroy-network: deregistered tier ${NETWORK_ID} from VPC 
${VPC_ID} (namespace preserved)"
     else
-        # Isolated network: delete the namespace directly
+        # Isolated or Shared: each has its own namespace, remove it entirely.
         if ip netns list 2>/dev/null | grep -q "^${NAMESPACE}\b"; then
             ip netns del "${NAMESPACE}"
             rm -rf "/etc/netns/${NAMESPACE}" 2>/dev/null || true
@@ -2055,7 +2069,22 @@ cmd_prepare_nic() {
     parse_args "$@"
     _load_state
     acquire_lock "${NETWORK_ID}"
-    log "prepare-nic: network=${NETWORK_ID} ns=${NAMESPACE} mac=${MAC} 
ip=${VM_IP} hostname=${HOSTNAME}"
+    log "prepare-nic: network=${NETWORK_ID} ns=${NAMESPACE} 
guest_type=${GUEST_TYPE} mac=${MAC} ip=${VM_IP} hostname=${HOSTNAME}"
+
+    # ---- Shared network: lazily implement on first NIC attach ----
+    # For Shared networks implement-network is not called at deploy time
+    # (no dedicated gateway / no NAT), so the namespace + bridge + veth
+    # must be created here the first time a VM NIC is attached.
+    if [ "${GUEST_TYPE}" = "shared" ]; then
+        local nsd; nsd=$(_net_state_dir)
+        if [ ! -f "${nsd}/vlan" ]; then
+            log "prepare-nic: shared network — running implement-network for 
network=${NETWORK_ID}"
+            release_lock
+            cmd_implement_network "$@"
+            acquire_lock "${NETWORK_ID}"
+            _load_state
+        fi
+    fi
 
     local dnsmasq_reloaded="false"
 
@@ -3366,7 +3395,6 @@ parse_vpc_args() {
     local payload_file="$1"
 
     VPC_ID=$(_payload_json_get "${payload_file}" "payload.vpc_id")
-    NAMESPACE=$(_payload_json_get "${payload_file}" "payload.namespace")
     VPC_CIDR=$(_payload_json_get "${payload_file}" "payload.vpc_cidr")
     PUBLIC_IP=$(_payload_json_get "${payload_file}" "payload.public_ip")
     PUBLIC_VLAN=$(_payload_json_get "${payload_file}" "payload.public_vlan")
@@ -3378,11 +3406,9 @@ parse_vpc_args() {
 
     [ -z "${VPC_ID}" ] && die "Missing payload.vpc_id"
 
-    if [ -z "${NAMESPACE}" ]; then
-        local NS_FROM_DETAILS
-        NS_FROM_DETAILS=$(_json_get "${EXTENSION_DETAILS}" "namespace")
-        NAMESPACE="${NS_FROM_DETAILS:-cs-vpc-${VPC_ID}}"
-    fi
+    local NS_FROM_DETAILS
+    NS_FROM_DETAILS=$(_json_get "${EXTENSION_DETAILS}" "namespace")
+    NAMESPACE="${NS_FROM_DETAILS:-cs-vpc-${VPC_ID}}"
 
     # Normalise VLANs
     if [ -n "${PUBLIC_VLAN}" ]; then

Reply via email to