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 53b9270 Network Namespace: prepare-nic and release-nic
53b9270 is described below
commit 53b927083a4136b5c70cac646198d6491685605d
Author: Wei Zhou <[email protected]>
AuthorDate: Wed Jun 3 20:23:30 2026 +0200
Network Namespace: prepare-nic and release-nic
---
Network-Namespace/README.md | 49 ++++++++++
Network-Namespace/network-namespace-wrapper.sh | 119 +++++++++++++++++++++++++
2 files changed, 168 insertions(+)
diff --git a/Network-Namespace/README.md b/Network-Namespace/README.md
index f2853cf..9d93ac5 100644
--- a/Network-Namespace/README.md
+++ b/Network-Namespace/README.md
@@ -1041,6 +1041,55 @@ network-namespace-wrapper.sh delete-port-forward \
Removes the DNAT and FORWARD rules added by `add-port-forward`.
+### `prepare-nic`
+
+Called when a VM NIC is being attached to the network (before the VM boots).
+
+```
+network-namespace-wrapper.sh prepare-nic \
+ --network-id <id> \
+ --vlan <vlan-id> \
+ --mac <mac> \
+ --ip <vm-ip> \
+ [--hostname <name>] \
+ [--default-nic true|false] \
+ [--gateway <gw>] \
+ [--cidr <cidr>] \
+ [--extension-ip <ip>] \
+ [--vpc-id <vpc-id>]
+```
+
+Actions (all idempotent; silently skipped when the service is not yet
configured):
+1. If dnsmasq DHCP is active for the network — add a static lease
+ `<mac>,<ip>[,<hostname>],infinite` to the hosts file. For secondary NICs
+ (`default_nic=false`) the gateway DHCP option is suppressed via a
+ `set:norouter_<mac_tag>` tag so the VM does not receive a competing default
+ route from this NIC.
+2. If dnsmasq DNS is active — add a `<ip> <hostname>` line to the hosts file.
+3. Sends a SIGHUP / reload to dnsmasq so the new entries take effect
+ immediately.
+
+### `release-nic`
+
+Called when a VM NIC is being detached from the network (after the VM stops).
+
+```
+network-namespace-wrapper.sh release-nic \
+ --network-id <id> \
+ --mac <mac> \
+ --ip <vm-ip> \
+ [--vpc-id <vpc-id>]
+```
+
+Actions:
+1. Remove the MAC's DHCP static lease and any associated gateway-suppression
+ option from dnsmasq.
+2. Remove the VM's hostname from the dnsmasq hosts file.
+3. Reload dnsmasq.
+4. Delete the per-VM metadata directory
+ `${STATE_DIR}/network-<id>/metadata/<vm-ip>/`.
+5. Remove the VM's password entry from the passwords file.
+
### `apply-fw-rules`
Called when CloudStack applies or removes firewall rules for the network.
diff --git a/Network-Namespace/network-namespace-wrapper.sh
b/Network-Namespace/network-namespace-wrapper.sh
index bb8b429..d5ad279 100755
--- a/Network-Namespace/network-namespace-wrapper.sh
+++ b/Network-Namespace/network-namespace-wrapper.sh
@@ -2044,6 +2044,121 @@ cmd_remove_dns_entry() {
log "remove-dns-entry: done ${VM_IP}"
}
+##############################################################################
+# Command: prepare-nic
+# Called when a VM NIC is being attached to the network (before VM boots).
+# Idempotently adds DHCP and DNS entries so the NIC is reachable as soon
+# as the VM starts. A no-op when dnsmasq is not yet configured.
+##############################################################################
+
+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}"
+
+ local dnsmasq_reloaded="false"
+
+ # ---- DHCP entry ----
+ local dhcp_hosts; dhcp_hosts=$(_dnsmasq_dhcp_hosts)
+ if [ -n "${MAC}" ] && [ -n "${VM_IP}" ] && [ -f "${dhcp_hosts}" ]; then
+ local mac_tag; mac_tag=$(echo "${MAC}" | tr ':' '_' | tr '[:upper:]'
'[:lower:]')
+ grep -v "${MAC}" "${dhcp_hosts}" > "${dhcp_hosts}.tmp" 2>/dev/null ||
true
+ mv "${dhcp_hosts}.tmp" "${dhcp_hosts}"
+ if [ "${DEFAULT_NIC}" = "false" ]; then
+ if [ -n "${HOSTNAME}" ]; then
+ echo
"set:norouter_${mac_tag},${MAC},${VM_IP},${HOSTNAME},infinite" >>
"${dhcp_hosts}"
+ else
+ echo "set:norouter_${mac_tag},${MAC},${VM_IP},infinite" >>
"${dhcp_hosts}"
+ fi
+ local dhcp_opts; dhcp_opts=$(_dnsmasq_dhcp_opts)
+ touch "${dhcp_opts}"
+ grep -v "tag:norouter_${mac_tag}" "${dhcp_opts}" >
"${dhcp_opts}.tmp" 2>/dev/null || true
+ mv "${dhcp_opts}.tmp" "${dhcp_opts}"
+ echo "dhcp-option=tag:norouter_${mac_tag},option:router,0.0.0.0"
>> "${dhcp_opts}"
+ log "prepare-nic: non-default NIC ${MAC} (${VM_IP}) — gateway
suppressed"
+ else
+ if [ -n "${HOSTNAME}" ]; then
+ echo "${MAC},${VM_IP},${HOSTNAME},infinite" >> "${dhcp_hosts}"
+ else
+ echo "${MAC},${VM_IP},infinite" >> "${dhcp_hosts}"
+ fi
+ fi
+ dnsmasq_reloaded="true"
+ fi
+
+ # ---- DNS entry ----
+ local hosts; hosts=$(_dnsmasq_hosts)
+ if [ -n "${VM_IP}" ] && [ -n "${HOSTNAME}" ] && [ -f "${hosts}" ]; then
+ grep -v "^${VM_IP}[[:space:]]" "${hosts}" > "${hosts}.tmp" 2>/dev/null
|| true
+ mv "${hosts}.tmp" "${hosts}"
+ echo "${VM_IP} ${HOSTNAME}" >> "${hosts}"
+ dnsmasq_reloaded="true"
+ fi
+
+ [ "${dnsmasq_reloaded}" = "true" ] && _svc_start_or_reload_dnsmasq
+
+ release_lock
+ log "prepare-nic: done network=${NETWORK_ID} mac=${MAC} ip=${VM_IP}"
+}
+
+##############################################################################
+# Command: release-nic
+# Called when a VM NIC is being detached from the network (after VM stops).
+# Removes DHCP and DNS entries and cleans up per-VM metadata files.
+##############################################################################
+
+cmd_release_nic() {
+ parse_args "$@"
+ _load_state
+ acquire_lock "${NETWORK_ID}"
+ log "release-nic: network=${NETWORK_ID} ns=${NAMESPACE} mac=${MAC}
ip=${VM_IP}"
+
+ local dnsmasq_reloaded="false"
+
+ # ---- Remove DHCP entry ----
+ local dhcp_hosts; dhcp_hosts=$(_dnsmasq_dhcp_hosts)
+ if [ -n "${MAC}" ] && [ -f "${dhcp_hosts}" ]; then
+ local mac_tag; mac_tag=$(echo "${MAC}" | tr ':' '_' | tr '[:upper:]'
'[:lower:]')
+ grep -v "${MAC}" "${dhcp_hosts}" > "${dhcp_hosts}.tmp" 2>/dev/null ||
true
+ mv "${dhcp_hosts}.tmp" "${dhcp_hosts}"
+ local dhcp_opts; dhcp_opts=$(_dnsmasq_dhcp_opts)
+ if [ -f "${dhcp_opts}" ]; then
+ grep -v "tag:norouter_${mac_tag}" "${dhcp_opts}" >
"${dhcp_opts}.tmp" 2>/dev/null || true
+ mv "${dhcp_opts}.tmp" "${dhcp_opts}"
+ fi
+ dnsmasq_reloaded="true"
+ fi
+
+ # ---- Remove DNS entry ----
+ local hosts; hosts=$(_dnsmasq_hosts)
+ if [ -n "${VM_IP}" ] && [ -f "${hosts}" ]; then
+ grep -v "^${VM_IP}[[:space:]]" "${hosts}" > "${hosts}.tmp" 2>/dev/null
|| true
+ mv "${hosts}.tmp" "${hosts}"
+ dnsmasq_reloaded="true"
+ fi
+
+ [ "${dnsmasq_reloaded}" = "true" ] && _svc_start_or_reload_dnsmasq
+
+ # ---- Remove per-VM metadata ----
+ if [ -n "${VM_IP}" ]; then
+ local vm_meta_dir; vm_meta_dir="$(_metadata_dir)/${VM_IP}"
+ if [ -d "${vm_meta_dir}" ]; then
+ rm -rf "${vm_meta_dir}"
+ log "release-nic: removed metadata for ${VM_IP}"
+ fi
+ # Remove password entry
+ local passwd_f; passwd_f=$(_passwd_file)
+ if [ -f "${passwd_f}" ]; then
+ grep -v "^${VM_IP}=" "${passwd_f}" > "${passwd_f}.tmp" 2>/dev/null
|| true
+ mv "${passwd_f}.tmp" "${passwd_f}"
+ fi
+ fi
+
+ release_lock
+ log "release-nic: done network=${NETWORK_ID} mac=${MAC} ip=${VM_IP}"
+}
+
##############################################################################
# Command: save-userdata
# Write user-data for a VM; start/reload apache2.
@@ -3719,6 +3834,9 @@ case "${COMMAND}" in
delete-static-nat) cmd_delete_static_nat "$@" ;;
add-port-forward) cmd_add_port_forward "$@" ;;
delete-port-forward) cmd_delete_port_forward "$@" ;;
+ # NIC lifecycle
+ prepare-nic) cmd_prepare_nic "$@" ;;
+ release-nic) cmd_release_nic "$@" ;;
# DHCP / DNS (dnsmasq)
config-dhcp-subnet) cmd_config_dhcp_subnet "$@" ;;
remove-dhcp-subnet) cmd_remove_dhcp_subnet "$@" ;;
@@ -3747,6 +3865,7 @@ case "${COMMAND}" in
"implement-vpc|update-vpc-source-nat-ip|shutdown-vpc|destroy-vpc|" \
"assign-ip|release-ip|" \
"add-static-nat|delete-static-nat|add-port-forward|delete-port-forward|" \
+ "prepare-nic|release-nic|" \
"config-dhcp-subnet|remove-dhcp-subnet|add-dhcp-entry|remove-dhcp-entry|set-dhcp-options|"
\
"config-dns-subnet|remove-dns-subnet|add-dns-entry|remove-dns-entry|" \
"save-userdata|save-password|save-sshkey|save-hypervisor-hostname|save-vm-data|restore-network|"
\