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 06a505bd31d791513e00f29f549bbf59a2d61723 Author: Wei Zhou <[email protected]> AuthorDate: Wed Jun 3 18:08:24 2026 +0200 Network Namespace: use JSON and deprecate base64 string --- Network-Namespace/README.md | 26 +++---- Network-Namespace/network-namespace-wrapper.sh | 99 ++++++++++++-------------- 2 files changed, 55 insertions(+), 70 deletions(-) diff --git a/Network-Namespace/README.md b/Network-Namespace/README.md index 34b4ba7..f2853cf 100644 --- a/Network-Namespace/README.md +++ b/Network-Namespace/README.md @@ -1049,11 +1049,10 @@ Called when CloudStack applies or removes firewall rules for the network. network-namespace-wrapper.sh apply-fw-rules \ --network-id <id> \ --vlan <vlan-id> \ - { --fw-rules <base64-json> | --fw-rules-file <path-on-kvm-host> } \ [--vpc-id <vpc-id>] ``` -The `--fw-rules` value is a Base64-encoded JSON object: +The `fw_rules` field in the payload is a JSON object: ```json { "default_egress_allow": true, @@ -1096,11 +1095,10 @@ Apply Network ACL (Access Control List) rules for VPC networks. network-namespace-wrapper.sh apply-network-acl \ --network-id <id> \ --vlan <vlan-id> \ - { --acl-rules <base64-json> | --acl-rules-file <path-on-kvm-host> } \ [--vpc-id <vpc-id>] ``` -The `--acl-rules` value is a Base64-encoded JSON array of ACL rule objects: +The `acl_rules` field in the payload is a JSON array of ACL rule objects: ```json [ { @@ -1247,28 +1245,24 @@ Called on network restart and VM deploy. ``` network-namespace-wrapper.sh save-vm-data \ --network-id <id> \ - --ip <vm-ip> \ - { --vm-data <base64-json> | --vm-data-file <path-on-kvm-host> } + --ip <vm-ip> ``` -The `--vm-data` value (or the contents of `--vm-data-file`) is a Base64-encoded -JSON array of `{dir, file, content}` entries (same format as `generateVmData()` -in the Java layer). Writes files under +The `vm_data` field in the payload is a JSON array of `{dir, file, content}` +entries (same format as `generateVmData()` in the Java layer). Each `content` +value is a plain UTF-8 string. Writes files under `${STATE_DIR}/network-<id>/metadata/<vm-ip>/latest/`. After writing, starts or reloads both the **apache2 metadata HTTP service** (port 80) and the **VR-compatible password server** (port 8080) inside the namespace. -> `network-namespace.sh` uploads the single command payload file to the KVM host; -> nested fields like `vm_data` stay inside that payload JSON. - ### `save-userdata` / `save-password` / `save-sshkey` / `save-hypervisor-hostname` Granular variants that write individual VM metadata fields: ``` -network-namespace-wrapper.sh save-userdata --network-id <id> --ip <vm-ip> --userdata <base64> +network-namespace-wrapper.sh save-userdata --network-id <id> --ip <vm-ip> --userdata <plain> network-namespace-wrapper.sh save-password --network-id <id> --ip <vm-ip> --password <plain> -network-namespace-wrapper.sh save-sshkey --network-id <id> --ip <vm-ip> --sshkey <base64> +network-namespace-wrapper.sh save-sshkey --network-id <id> --ip <vm-ip> --sshkey <plain> network-namespace-wrapper.sh save-hypervisor-hostname \ --network-id <id> --ip <vm-ip> --hypervisor-hostname <name> ``` @@ -1302,11 +1296,13 @@ per-VM calls. ``` network-namespace-wrapper.sh restore-network \ --network-id <id> \ - { --restore-data <base64-json> | --restore-data-file <path-on-kvm-host> } \ [--gateway <gw>] [--cidr <cidr>] [--dns <dns>] \ [--domain <dom>] [--extension-ip <ip>] [--vpc-id <vpc-id>] ``` +The `restore_data` field in the payload is a JSON object (see +`buildRestoreNetworkData()` in `NetworkExtensionElement.java`). + ### `custom-action` ``` diff --git a/Network-Namespace/network-namespace-wrapper.sh b/Network-Namespace/network-namespace-wrapper.sh index bcfc039..bb8b429 100755 --- a/Network-Namespace/network-namespace-wrapper.sh +++ b/Network-Namespace/network-namespace-wrapper.sh @@ -2046,7 +2046,7 @@ cmd_remove_dns_entry() { ############################################################################## # Command: save-userdata -# Write base64-decoded user-data for a VM; start/reload apache2. +# Write user-data for a VM; start/reload apache2. ############################################################################## cmd_save_userdata() { @@ -2060,8 +2060,7 @@ cmd_save_userdata() { mkdir -p "${vm_dir}" if [ -n "${USERDATA}" ]; then - printf '%s' "${USERDATA}" | base64 -d > "${vm_dir}/user-data" 2>/dev/null || \ - printf '%s' "${USERDATA}" > "${vm_dir}/user-data" + printf '%s' "${USERDATA}" > "${vm_dir}/user-data" else # Create empty user-data file if USERDATA is empty rm -rf "${vm_dir}/user-data" && touch "${vm_dir}/user-data" @@ -2104,7 +2103,7 @@ cmd_save_password() { ############################################################################## # Command: save-sshkey -# Write a base64-encoded SSH public key for a VM. +# Write an SSH public key for a VM. ############################################################################## cmd_save_sshkey() { @@ -2116,11 +2115,9 @@ cmd_save_sshkey() { local meta_dir; meta_dir="$(_metadata_dir)/${VM_IP}/latest/meta-data" mkdir -p "${meta_dir}" - # SSH_KEY is base64-encoded by the Java caller. # All public keys for the VM are stored together in a single flat file # (one key per line) at latest/meta-data/public-keys. - printf '%s' "${SSH_KEY}" | base64 -d > "${meta_dir}/public-keys" 2>/dev/null || \ - printf '%s' "${SSH_KEY}" > "${meta_dir}/public-keys" + printf '%s' "${SSH_KEY}" > "${meta_dir}/public-keys" _write_apache2_conf _svc_start_or_reload_apache2 @@ -2153,9 +2150,8 @@ cmd_save_hypervisor_hostname() { ############################################################################## # Command: save-vm-data # Write the full VM metadata/userdata/password set in one call. -# --ip <vm_ip> IP address of the VM on this network -# --vm-data <base64> Base64-encoded JSON array of {dir,file,content} entries. -# Each 'content' value is itself Base64-encoded bytes. +# payload.vm_data JSON array of {dir,file,content} entries. +# Each 'content' value is a plain UTF-8 string. # # Path mapping from generateVmData() output: # [userdata, user_data, <bytes>] → latest/user-data @@ -2191,7 +2187,7 @@ cmd_save_vm_data() { touch "${passwd_f}" python3 - "${VM_IP}" "${meta_dir}" "${passwd_f}" "${vm_data_file}" << 'PYEOF' -import base64, json, os, sys +import json, os, sys vm_ip = sys.argv[1] meta_dir = sys.argv[2] @@ -2200,32 +2196,28 @@ data_file = sys.argv[4] try: with open(data_file, 'r', encoding='utf-8') as f: - data_b64 = f.read().strip() + data_json = f.read().strip() except Exception as e: print(f"save-vm-data: failed to read vm-data file: {e}", file=sys.stderr) sys.exit(1) -# Decode the outer base64 wrapper, then parse the JSON array +# Parse the JSON array directly (content is plain text, not base64-encoded) try: - entries = json.loads(base64.b64decode(data_b64).decode('utf-8')) + entries = json.loads(data_json) except Exception as e: - print(f"save-vm-data: failed to decode vm-data: {e}", file=sys.stderr) + print(f"save-vm-data: failed to parse vm-data: {e}", file=sys.stderr) sys.exit(1) password_written = None pub_keys = [] # accumulate all public-key entries; written as one flat file after the loop for entry in entries: - d = entry.get('dir', '') - f = entry.get('file', '') - c_b64 = entry.get('content', '') - if not c_b64: + d = entry.get('dir', '') + f = entry.get('file', '') + c = entry.get('content', '') + if not c: continue - # Each content value is base64-encoded actual bytes - try: - content = base64.b64decode(c_b64) - except Exception: - content = c_b64.encode('utf-8') if isinstance(c_b64, str) else b'' + content = c.encode('utf-8') if isinstance(c, str) else c if not content: continue @@ -2296,9 +2288,9 @@ PYEOF # Command: apply-fw-rules # # Rebuilds the per-network firewall chain CS_EXTNET_FWRULES_<networkId> from -# scratch using a Base64-encoded JSON payload supplied via --fw-rules. +# scratch using the JSON object supplied in payload.fw_rules. # -# JSON payload structure (base64-decoded): +# JSON payload structure (payload.fw_rules): # { # "default_egress_allow": true|false, # "cidr": "10.0.0.0/24", @@ -2380,7 +2372,7 @@ cmd_apply_fw_rules() { # ---- 4. Build iptables rules via Python ---- python3 - "${NAMESPACE}" "${fw_rules_file}" "${veth_n}" \ "${fw_chain}" << 'PYEOF' -import base64, json, re, subprocess, sys +import json, re, subprocess, sys namespace = sys.argv[1] rules_file = sys.argv[2] @@ -2389,7 +2381,7 @@ fw_chain = sys.argv[4] # filter table egress chain (CS_EXTNET_FWRULES_<N>) try: with open(rules_file, 'r', encoding='utf-8') as f: - rules_b64 = f.read().strip() + rules_json = f.read().strip() except Exception as e: print(f"apply-fw-rules: failed to read rules file: {e}", file=sys.stderr) sys.exit(1) @@ -2414,13 +2406,13 @@ def iptm(*args): _run('mangle', *args) # --------------------------------------------------------------------------- -# Decode payload +# Parse payload # --------------------------------------------------------------------------- -if rules_b64: +if rules_json: try: - data = json.loads(base64.b64decode(rules_b64).decode('utf-8')) + data = json.loads(rules_json) except Exception as e: - print(f"apply-fw-rules: failed to decode --fw-rules: {e}", file=sys.stderr) + print(f"apply-fw-rules: failed to parse fw-rules: {e}", file=sys.stderr) sys.exit(1) else: data = {} @@ -3002,7 +2994,7 @@ cmd_custom_action() { # # Required arguments: # --network-id <id> -# --restore-data <base64-json> JSON: see buildRestoreNetworkData() in Java +# payload.restore_data JSON object: see buildRestoreNetworkData() in Java # # Optional (for dnsmasq reconfiguration): # --gateway <gw> --cidr <cidr> --dns <dns> --domain <dom> --extension-ip <ip> @@ -3038,7 +3030,7 @@ cmd_restore_network() { "${dhcp_hosts}" "${dhcp_opts}" "${dns_hosts}" \ "${meta_dir}" "${passwd_f}" \ << 'PYEOF' -import base64, json, os, sys +import json, os, sys restore_file = sys.argv[1] dhcp_hosts_f = sys.argv[2] @@ -3049,16 +3041,16 @@ passwd_f = sys.argv[6] try: with open(restore_file, 'r', encoding='utf-8') as f: - restore_b64 = f.read().strip() + restore_json = f.read().strip() except Exception as e: print(f"restore-network: failed to read restore-data file: {e}", file=sys.stderr) sys.exit(1) -# ---- Decode the outer base64, then parse JSON ---- +# ---- Parse JSON directly ---- try: - data = json.loads(base64.b64decode(restore_b64).decode('utf-8')) + data = json.loads(restore_json) except Exception as e: - print(f"restore-network: failed to decode restore-data: {e}", file=sys.stderr) + print(f"restore-network: failed to parse restore-data: {e}", file=sys.stderr) sys.exit(1) dhcp_enabled = data.get('dhcp_enabled', False) @@ -3143,15 +3135,12 @@ if userdata_enabled: vm_count += 1 pub_keys = [] # accumulate public keys; written as a single flat file after inner loop for entry in vm_data: - d = entry.get('dir', '') - f_ = entry.get('file', '') - c_b64 = entry.get('content', '') - if not c_b64: + d = entry.get('dir', '') + f_ = entry.get('file', '') + c = entry.get('content', '') + if not c: continue - try: - content = base64.b64decode(c_b64) - except Exception: - content = c_b64.encode('utf-8') if isinstance(c_b64, str) else b'' + content = c.encode('utf-8') if isinstance(c, str) else c if not content: continue @@ -3205,10 +3194,10 @@ PYEOF # ------------------------------------------------------------------ local _r_flags _r_flags=$(python3 - "${restore_data_file}" 2>/dev/null << 'PYFLAGSEOF' -import base64, json, sys, pathlib +import json, sys, pathlib try: - b64_data = pathlib.Path(sys.argv[1]).read_text(encoding='utf-8').strip() - d = json.loads(base64.b64decode(b64_data).decode('utf-8')) + json_data = pathlib.Path(sys.argv[1]).read_text(encoding='utf-8').strip() + d = json.loads(json_data) dhcp = 'true' if d.get('dhcp_enabled', False) else 'false' dns = 'true' if d.get('dns_enabled', False) else 'false' print(dhcp + ' ' + dns) @@ -3558,7 +3547,7 @@ cmd_destroy_vpc() { ############################################################################## # Command: apply-network-acl # Applies VPC network ACL rules to the FORWARD chain inside the namespace. -# Rules are passed as a Base64-encoded JSON array in payload.acl_rules. +# Rules are passed as a JSON array in payload.acl_rules. # Each rule has: # number, action (allow|deny), trafficType (ingress|egress), # protocol, portStart, portEnd, icmpType, icmpCode, sourceCidrs[] @@ -3598,7 +3587,7 @@ cmd_apply_network_acl() { # ---- 4. Build iptables ACL rules via Python ---- python3 - "${NAMESPACE}" "${acl_rules_file}" "${veth_n}" \ "${acl_chain_name}" "${CIDR:-}" << 'PYEOF' -import base64, json, subprocess, sys +import json, subprocess, sys namespace = sys.argv[1] rules_file = sys.argv[2] @@ -3608,7 +3597,7 @@ net_cidr = sys.argv[5] # tier CIDR (may be empty) try: with open(rules_file, 'r', encoding='utf-8') as f: - rules_b64 = f.read().strip() + rules_json = f.read().strip() except Exception as e: print(f"apply-network-acl: failed to read rules file: {e}", file=sys.stderr) sys.exit(1) @@ -3623,11 +3612,11 @@ def _run(table, *args): def iptf(*args): _run('filter', *args) -if rules_b64: +if rules_json: try: - rules = json.loads(base64.b64decode(rules_b64).decode('utf-8')) + rules = json.loads(rules_json) except Exception as e: - print(f"apply-network-acl: failed to decode rules: {e}", file=sys.stderr) + print(f"apply-network-acl: failed to parse rules: {e}", file=sys.stderr) sys.exit(1) else: rules = []
