Skip to content

Commit 73a8b0f

Browse files
committed
extension: deal with max 2MB shell parameters limitation
``` $ getconf ARG_MAX 2097152 ```
1 parent bde08c4 commit 73a8b0f

3 files changed

Lines changed: 167 additions & 20 deletions

File tree

extensions/network-namespace/network-namespace-wrapper.sh

Lines changed: 81 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -460,10 +460,13 @@ parse_args() {
460460
LB_RULES_JSON="[]"
461461
DEFAULT_NIC="true"
462462
VM_DATA=""
463+
VM_DATA_FILE=""
463464
DOMAIN=""
464465
EXTENSION_IP=""
465466
RESTORE_DATA=""
467+
RESTORE_DATA_FILE=""
466468
FW_RULES_JSON=""
469+
FW_RULES_FILE=""
467470

468471
while [ $# -gt 0 ]; do
469472
case "$1" in
@@ -495,10 +498,13 @@ parse_args() {
495498
--lb-rules) LB_RULES_JSON="$2"; shift 2 ;;
496499
--default-nic) DEFAULT_NIC="$2"; shift 2 ;;
497500
--vm-data) VM_DATA="$2"; shift 2 ;;
501+
--vm-data-file) VM_DATA_FILE="$2"; shift 2 ;;
498502
--domain) DOMAIN="$2"; shift 2 ;;
499503
--extension-ip) EXTENSION_IP="$2"; shift 2 ;;
500504
--restore-data) RESTORE_DATA="$2"; shift 2 ;;
505+
--restore-data-file) RESTORE_DATA_FILE="$2"; shift 2 ;;
501506
--fw-rules) FW_RULES_JSON="$2"; shift 2 ;;
507+
--fw-rules-file) FW_RULES_FILE="$2"; shift 2 ;;
502508
# consumed by _pre_scan_args — skip silently
503509
--physical-network-extension-details|--network-extension-details)
504510
shift 2 ;;
@@ -2231,20 +2237,36 @@ cmd_save_vm_data() {
22312237
acquire_lock "${NETWORK_ID}"
22322238
log "save-vm-data: network=${NETWORK_ID} ip=${VM_IP}"
22332239
[ -z "${VM_IP}" ] && die "save-vm-data: missing --ip"
2234-
[ -z "${VM_DATA}" ] && die "save-vm-data: missing --vm-data"
2240+
2241+
local vm_data_file="${VM_DATA_FILE}"
2242+
local cleanup_vm_data_file="false"
2243+
if [ -z "${vm_data_file}" ]; then
2244+
[ -z "${VM_DATA}" ] && die "save-vm-data: missing --vm-data or --vm-data-file"
2245+
vm_data_file=$(mktemp /tmp/cs-extnet-vm-data-XXXXXX)
2246+
cleanup_vm_data_file="true"
2247+
printf '%s' "${VM_DATA}" > "${vm_data_file}"
2248+
fi
2249+
[ -f "${vm_data_file}" ] || die "save-vm-data: payload file not found: ${vm_data_file}"
22352250

22362251
local meta_dir; meta_dir=$(_metadata_dir)
22372252
local passwd_f; passwd_f=$(_passwd_file)
22382253
mkdir -p "${meta_dir}" "$(dirname "${passwd_f}")"
22392254
touch "${passwd_f}"
22402255

2241-
python3 - "${VM_IP}" "${meta_dir}" "${passwd_f}" "${VM_DATA}" << 'PYEOF'
2256+
python3 - "${VM_IP}" "${meta_dir}" "${passwd_f}" "${vm_data_file}" << 'PYEOF'
22422257
import base64, json, os, sys
22432258
22442259
vm_ip = sys.argv[1]
22452260
meta_dir = sys.argv[2]
22462261
passwd_f = sys.argv[3]
2247-
data_b64 = sys.argv[4]
2262+
data_file = sys.argv[4]
2263+
2264+
try:
2265+
with open(data_file, 'r', encoding='utf-8') as f:
2266+
data_b64 = f.read().strip()
2267+
except Exception as e:
2268+
print(f"save-vm-data: failed to read vm-data file: {e}", file=sys.stderr)
2269+
sys.exit(1)
22482270
22492271
# Decode the outer base64 wrapper, then parse the JSON array
22502272
try:
@@ -2321,6 +2343,10 @@ if password_written:
23212343
print(f"save-vm-data: wrote {len(entries)} entries for {vm_ip}")
23222344
PYEOF
23232345

2346+
if [ "${cleanup_vm_data_file}" = "true" ]; then
2347+
rm -f "${vm_data_file}" 2>/dev/null || true
2348+
fi
2349+
23242350
_write_apache2_conf
23252351
_svc_start_or_reload_apache2
23262352
_svc_start_or_reload_passwd_server
@@ -2389,6 +2415,15 @@ cmd_apply_fw_rules() {
23892415
acquire_lock "${NETWORK_ID}"
23902416
log "apply-fw-rules: network=${NETWORK_ID} ns=${NAMESPACE}"
23912417

2418+
local fw_rules_file="${FW_RULES_FILE}"
2419+
local cleanup_fw_rules_file="false"
2420+
if [ -z "${fw_rules_file}" ]; then
2421+
fw_rules_file=$(mktemp /tmp/cs-extnet-fw-rules-XXXXXX)
2422+
cleanup_fw_rules_file="true"
2423+
printf '%s' "${FW_RULES_JSON:-}" > "${fw_rules_file}"
2424+
fi
2425+
[ -f "${fw_rules_file}" ] || die "apply-fw-rules: payload file not found: ${fw_rules_file}"
2426+
23922427
local veth_n fchain fw_chain
23932428
veth_n=$(veth_ns_name "${VLAN}" "${CHOSEN_ID}")
23942429
fchain=$(filter_chain "${NETWORK_ID}")
@@ -2406,15 +2441,22 @@ cmd_apply_fw_rules() {
24062441
ip netns exec "${NAMESPACE}" iptables -t filter -N "${fw_chain}"
24072442

24082443
# ---- 4. Build iptables rules via Python ----
2409-
python3 - "${NAMESPACE}" "${FW_RULES_JSON:-}" "${veth_n}" \
2444+
python3 - "${NAMESPACE}" "${fw_rules_file}" "${veth_n}" \
24102445
"${fw_chain}" << 'PYEOF'
24112446
import base64, json, re, subprocess, sys
24122447
24132448
namespace = sys.argv[1]
2414-
rules_b64 = sys.argv[2]
2449+
rules_file = sys.argv[2]
24152450
veth_n = sys.argv[3]
24162451
fw_chain = sys.argv[4] # filter table egress chain (CS_EXTNET_FWRULES_<N>)
24172452
2453+
try:
2454+
with open(rules_file, 'r', encoding='utf-8') as f:
2455+
rules_b64 = f.read().strip()
2456+
except Exception as e:
2457+
print(f"apply-fw-rules: failed to read rules file: {e}", file=sys.stderr)
2458+
sys.exit(1)
2459+
24182460
# Prefix for per-public-IP ingress chains in the mangle table.
24192461
# e.g. CS_EXTNET_FWI_10.0.56.20
24202462
FW_INGRESS_PREFIX = 'CS_EXTNET_FWI_'
@@ -2592,6 +2634,11 @@ print(f"apply-fw-rules: built {n_in} ingress rule(s) across {len(pub_ip_rules)}
25922634
PYEOF
25932635

25942636
local py_exit=$?
2637+
2638+
if [ "${cleanup_fw_rules_file}" = "true" ]; then
2639+
rm -f "${fw_rules_file}" 2>/dev/null || true
2640+
fi
2641+
25952642
if [ ${py_exit} -ne 0 ]; then
25962643
# Python script failed — leave chain empty but continue so that the
25972644
# fchain catch-all rules remain effective (fail-open for existing traffic).
@@ -2768,7 +2815,16 @@ cmd_restore_network() {
27682815
_load_state
27692816
acquire_lock "${NETWORK_ID}"
27702817
log "restore-network: network=${NETWORK_ID} ns=${NAMESPACE}"
2771-
[ -z "${RESTORE_DATA}" ] && die "restore-network: missing --restore-data"
2818+
2819+
local restore_data_file="${RESTORE_DATA_FILE}"
2820+
local cleanup_restore_data_file="false"
2821+
if [ -z "${restore_data_file}" ]; then
2822+
[ -z "${RESTORE_DATA}" ] && die "restore-network: missing --restore-data or --restore-data-file"
2823+
restore_data_file=$(mktemp /tmp/cs-extnet-restore-data-XXXXXX)
2824+
cleanup_restore_data_file="true"
2825+
printf '%s' "${RESTORE_DATA}" > "${restore_data_file}"
2826+
fi
2827+
[ -f "${restore_data_file}" ] || die "restore-network: payload file not found: ${restore_data_file}"
27722828

27732829
local dhcp_hosts; dhcp_hosts=$(_dnsmasq_dhcp_hosts)
27742830
local dhcp_opts; dhcp_opts=$(_dnsmasq_dhcp_opts)
@@ -2780,19 +2836,26 @@ cmd_restore_network() {
27802836
touch "${dhcp_hosts}" "${dhcp_opts}" "${dns_hosts}" "${passwd_f}"
27812837

27822838
python3 - \
2783-
"${RESTORE_DATA}" \
2839+
"${restore_data_file}" \
27842840
"${dhcp_hosts}" "${dhcp_opts}" "${dns_hosts}" \
27852841
"${meta_dir}" "${passwd_f}" \
27862842
<< 'PYEOF'
27872843
import base64, json, os, sys
27882844
2789-
restore_b64 = sys.argv[1]
2845+
restore_file = sys.argv[1]
27902846
dhcp_hosts_f = sys.argv[2]
27912847
dhcp_opts_f = sys.argv[3]
27922848
dns_hosts_f = sys.argv[4]
27932849
meta_dir = sys.argv[5]
27942850
passwd_f = sys.argv[6]
27952851
2852+
try:
2853+
with open(restore_file, 'r', encoding='utf-8') as f:
2854+
restore_b64 = f.read().strip()
2855+
except Exception as e:
2856+
print(f"restore-network: failed to read restore-data file: {e}", file=sys.stderr)
2857+
sys.exit(1)
2858+
27962859
# ---- Decode the outer base64, then parse JSON ----
27972860
try:
27982861
data = json.loads(base64.b64decode(restore_b64).decode('utf-8'))
@@ -2938,23 +3001,27 @@ print("restore-network: done")
29383001
PYEOF
29393002

29403003
# ------------------------------------------------------------------
2941-
# Decode dhcp_enabled / dns_enabled from RESTORE_DATA so we can
3004+
# Decode dhcp_enabled / dns_enabled from restore_data_file so we can
29423005
# reconfigure dnsmasq correctly even when the namespace (and therefore
2943-
# dnsmasq.conf) was deleted and recreated. RESTORE_DATA is passed as
2944-
# a positional argument to avoid shell-quoting issues with base64 data.
3006+
# dnsmasq.conf) was deleted and recreated.
29453007
# ------------------------------------------------------------------
29463008
local _r_flags
2947-
_r_flags=$(python3 - "${RESTORE_DATA}" 2>/dev/null << 'PYFLAGSEOF'
2948-
import base64, json, sys
3009+
_r_flags=$(python3 - "${restore_data_file}" 2>/dev/null << 'PYFLAGSEOF'
3010+
import base64, json, sys, pathlib
29493011
try:
2950-
d = json.loads(base64.b64decode(sys.argv[1]).decode('utf-8'))
3012+
b64_data = pathlib.Path(sys.argv[1]).read_text(encoding='utf-8').strip()
3013+
d = json.loads(base64.b64decode(b64_data).decode('utf-8'))
29513014
dhcp = 'true' if d.get('dhcp_enabled', False) else 'false'
29523015
dns = 'true' if d.get('dns_enabled', False) else 'false'
29533016
print(dhcp + ' ' + dns)
29543017
except Exception:
29553018
print('false false')
29563019
PYFLAGSEOF
29573020
)
3021+
3022+
if [ "${cleanup_restore_data_file}" = "true" ]; then
3023+
rm -f "${restore_data_file}" 2>/dev/null || true
3024+
fi
29583025
local _r_dhcp _r_dns
29593026
_r_dhcp="${_r_flags%% *}"; _r_dhcp="${_r_dhcp:-false}"
29603027
_r_dns="${_r_flags##* }"; _r_dns="${_r_dns:-false}"

extensions/network-namespace/network-namespace.sh

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,9 @@ EXTENSION_DETAILS="{}"
152152
NETWORK_ID=""
153153
CURRENT_DETAILS="{}"
154154
VPC_ID=""
155+
VM_DATA_FILE=""
156+
FW_RULES_FILE=""
157+
RESTORE_DATA_FILE=""
155158
FORWARD_ARGS=()
156159

157160
while [ $# -gt 0 ]; do
@@ -173,6 +176,15 @@ while [ $# -gt 0 ]; do
173176
--current-details)
174177
CURRENT_DETAILS="${2:-{}}"
175178
shift 2 ;;
179+
--vm-data-file)
180+
VM_DATA_FILE="${2:-}"
181+
shift 2 ;;
182+
--fw-rules-file)
183+
FW_RULES_FILE="${2:-}"
184+
shift 2 ;;
185+
--restore-data-file)
186+
RESTORE_DATA_FILE="${2:-}"
187+
shift 2 ;;
176188
*)
177189
FORWARD_ARGS+=("$1")
178190
shift ;;
@@ -280,6 +292,22 @@ ssh_exec() {
280292
fi
281293
}
282294

295+
upload_file_to_remote() {
296+
local host="$1" local_file="$2" tag="$3"
297+
[ -f "${local_file}" ] || die "Missing local payload file: ${local_file}" 1
298+
299+
local remote_tmp
300+
remote_tmp=$(ssh_exec "${host}" "mktemp /tmp/cs-extnet-${tag}-XXXXXX") || \
301+
die "Failed to create remote temp file for ${tag}" 2
302+
remote_tmp=$(printf '%s' "${remote_tmp}" | tr -d '\r\n')
303+
[ -n "${remote_tmp}" ] || die "Failed to resolve remote temp file for ${tag}" 2
304+
305+
cat "${local_file}" | ssh_exec "${host}" "cat > '${remote_tmp}' && chmod 600 '${remote_tmp}'" || \
306+
die "Failed to upload payload file for ${tag}" 2
307+
308+
printf '%s' "${remote_tmp}"
309+
}
310+
283311
# ---------------------------------------------------------------------------
284312
# ensure-network-device
285313
# ---------------------------------------------------------------------------
@@ -386,6 +414,23 @@ for arg in "${FORWARD_ARGS[@]}"; do
386414
remote_args+=("'${arg//"'"/"'\\''"}'" )
387415
done
388416

417+
REMOTE_PAYLOAD_FILES=()
418+
if [ -n "${VM_DATA_FILE}" ]; then
419+
REMOTE_VM_DATA_FILE=$(upload_file_to_remote "${REMOTE_HOST}" "${VM_DATA_FILE}" "vm-data")
420+
REMOTE_PAYLOAD_FILES+=("${REMOTE_VM_DATA_FILE}")
421+
remote_args+=("'--vm-data-file'" "'${REMOTE_VM_DATA_FILE//"'"/"'\\''"}'")
422+
fi
423+
if [ -n "${FW_RULES_FILE}" ]; then
424+
REMOTE_FW_RULES_FILE=$(upload_file_to_remote "${REMOTE_HOST}" "${FW_RULES_FILE}" "fw-rules")
425+
REMOTE_PAYLOAD_FILES+=("${REMOTE_FW_RULES_FILE}")
426+
remote_args+=("'--fw-rules-file'" "'${REMOTE_FW_RULES_FILE//"'"/"'\\''"}'")
427+
fi
428+
if [ -n "${RESTORE_DATA_FILE}" ]; then
429+
REMOTE_RESTORE_DATA_FILE=$(upload_file_to_remote "${REMOTE_HOST}" "${RESTORE_DATA_FILE}" "restore-data")
430+
REMOTE_PAYLOAD_FILES+=("${REMOTE_RESTORE_DATA_FILE}")
431+
remote_args+=("'--restore-data-file'" "'${REMOTE_RESTORE_DATA_FILE//"'"/"'\\''"}'")
432+
fi
433+
389434
PHYS_ESCAPED="${PHYS_DETAILS//\'/\'\\\'\'}"
390435
EXT_ESCAPED="${EXTENSION_DETAILS//\'/\'\\\'\'}"
391436
REMOTE_CMD="'${REMOTE_SCRIPT}' '${COMMAND}' ${remote_args[*]} --physical-network-extension-details '${PHYS_ESCAPED}' --network-extension-details '${EXT_ESCAPED}'"
@@ -395,6 +440,12 @@ log "Remote: ${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_PORT} cmd=${COMMAND}"
395440
RC=0
396441
ssh_exec "${REMOTE_HOST}" "${REMOTE_CMD}" || RC=$?
397442

443+
if [ ${#REMOTE_PAYLOAD_FILES[@]} -gt 0 ]; then
444+
for _rf in "${REMOTE_PAYLOAD_FILES[@]}"; do
445+
ssh_exec "${REMOTE_HOST}" "rm -f '${_rf}'" >/dev/null 2>&1 || true
446+
done
447+
fi
448+
398449
if [ ${RC} -ne 0 ]; then
399450
if [ ${RC} -eq 255 ]; then
400451
log "SSH connection failed (rc=255): host=${REMOTE_HOST}:${REMOTE_PORT} user=${REMOTE_USER}"

framework/extensions/src/main/java/org/apache/cloudstack/framework/extensions/network/NetworkExtensionElement.java

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import java.util.Set;
2626
import java.net.InetAddress;
2727
import java.nio.ByteBuffer;
28+
import java.nio.file.Files;
2829

2930
import javax.inject.Inject;
3031
import javax.naming.ConfigurationException;
@@ -933,6 +934,34 @@ protected boolean executeScript(Network network, String command, String... args)
933934
}
934935
}
935936

937+
/**
938+
* Writes a potentially large payload to a temporary file and passes the file path
939+
* to the extension script via {@code payloadArgName}. This avoids argv size limits
940+
* for multi-MB payloads.
941+
*/
942+
protected boolean executeScriptWithFilePayload(Network network, String command,
943+
String payloadArgName, String payload, String... args) {
944+
File payloadFile = null;
945+
try {
946+
payloadFile = File.createTempFile("cs-extnet-" + command + "-", ".payload");
947+
Files.writeString(payloadFile.toPath(), payload != null ? payload : "", StandardCharsets.UTF_8);
948+
949+
List<String> cmdArgs = new ArrayList<>();
950+
cmdArgs.addAll(Arrays.asList(args));
951+
cmdArgs.add(payloadArgName);
952+
cmdArgs.add(payloadFile.getAbsolutePath());
953+
954+
return executeScript(network, command, cmdArgs.toArray(new String[0]));
955+
} catch (Exception e) {
956+
throw new CloudRuntimeException(
957+
String.format("Failed preparing payload file for command %s", command), e);
958+
} finally {
959+
if (payloadFile != null && payloadFile.exists() && !payloadFile.delete()) {
960+
payloadFile.deleteOnExit();
961+
}
962+
}
963+
}
964+
936965
// ---- Detail helpers ----
937966

938967
/**
@@ -1502,10 +1531,10 @@ public boolean addPasswordAndUserdata(Network network, NicProfile nic, VirtualMa
15021531
args.add("--network-id"); args.add(String.valueOf(network.getId()));
15031532
args.add("--ip"); args.add(safeStr(nicIpAddress));
15041533
args.add("--gateway"); args.add(safeStr(nic.getIPv4Gateway()));
1505-
args.add("--vm-data"); args.add(vmDataArg);
15061534
args.add("--extension-ip"); args.add(safeStr(ensureExtensionIp(network)));
15071535
args.addAll(getVpcIdArgs(network));
1508-
return executeScript(network, "save-vm-data", args.toArray(new String[0]));
1536+
return executeScriptWithFilePayload(network, "save-vm-data", "--vm-data-file",
1537+
vmDataArg, args.toArray(new String[0]));
15091538
}
15101539

15111540
@Override
@@ -1840,10 +1869,10 @@ public boolean applyFWRules(Network network, List<? extends FirewallRule> rules)
18401869
args.add("--vlan"); args.add(safeStr(getVlanId(network)));
18411870
args.add("--gateway"); args.add(safeStr(network.getGateway()));
18421871
args.add("--cidr"); args.add(safeStr(network.getCidr()));
1843-
args.add("--fw-rules"); args.add(rulesBase64);
18441872
args.addAll(getVpcIdArgs(network));
18451873

1846-
boolean result = executeScript(network, "apply-fw-rules", args.toArray(new String[0]));
1874+
boolean result = executeScriptWithFilePayload(network, "apply-fw-rules", "--fw-rules-file",
1875+
rulesBase64, args.toArray(new String[0]));
18471876
if (!result) {
18481877
throw new ResourceUnavailableException(
18491878
"Failed to apply firewall rules for network " + network.getId(),
@@ -1919,10 +1948,10 @@ public boolean completeAggregatedExecution(Network network, DeployDestination de
19191948
args.add("--extension-ip"); args.add(safeStr(extensionIp));
19201949
args.add("--dns"); args.add(safeStr(getNetworkDns(network)));
19211950
args.add("--domain"); args.add(safeStr(network.getNetworkDomain()));
1922-
args.add("--restore-data"); args.add(restoreDataBase64);
19231951
args.addAll(getVpcIdArgs(network));
19241952

1925-
return executeScript(network, "restore-network", args.toArray(new String[0]));
1953+
return executeScriptWithFilePayload(network, "restore-network", "--restore-data-file",
1954+
restoreDataBase64, args.toArray(new String[0]));
19261955
}
19271956

19281957
/**

0 commit comments

Comments
 (0)