Compare commits
No commits in common. "1bdde9c128f053666574fe19d9e7ed7e9b1007d5" and "5a14e2d409542980eee8d98399da0727809d5f91" have entirely different histories.
1bdde9c128
...
5a14e2d409
11
README.md
11
README.md
|
|
@ -727,13 +727,14 @@ Worker indexes are stable. Index `1` maps to VMID `9010`, node name
|
||||||
so on. `LAB_PIMOX_SKIP_WORKER_INDEXES=1` leaves the first slot unmanaged while
|
so on. `LAB_PIMOX_SKIP_WORKER_INDEXES=1` leaves the first slot unmanaged while
|
||||||
allowing higher indexes to be automated.
|
allowing higher indexes to be automated.
|
||||||
|
|
||||||
Worker networking uses DHCP by default to preserve the known-good Pimox bridge
|
Worker networking is deterministic by default. After cloning or reusing a VM,
|
||||||
path. Set `LAB_PIMOX_WORKER_NETWORK_MODE=static` only when DHCP reservations are
|
the pipeline uses qemu-guest-agent to write a static `/etc/network/interfaces`
|
||||||
not available and you explicitly want Jeannie to configure guest static IPs with
|
entry before waiting for SSH, so a missing DHCP lease cannot stall the build.
|
||||||
qemu-guest-agent. Static mode uses
|
Defaults are `LAB_PIMOX_WORKER_NETWORK_MODE=static`,
|
||||||
`LAB_PIMOX_WORKER_STATIC_IPS="192.168.100.66 192.168.100.67 192.168.100.76"`,
|
`LAB_PIMOX_WORKER_STATIC_IPS="192.168.100.66 192.168.100.67 192.168.100.76"`,
|
||||||
`LAB_PIMOX_WORKER_GATEWAY=192.168.100.1`, and
|
`LAB_PIMOX_WORKER_GATEWAY=192.168.100.1`, and
|
||||||
`LAB_PIMOX_WORKER_DNS_SERVERS="192.168.100.89 1.1.1.1"`.
|
`LAB_PIMOX_WORKER_DNS_SERVERS="192.168.100.89 1.1.1.1"`. Set
|
||||||
|
`LAB_PIMOX_WORKER_NETWORK_MODE=dhcp` to restore the old DHCP-only behavior.
|
||||||
Worker creation fails faster than template creation: static guest-network
|
Worker creation fails faster than template creation: static guest-network
|
||||||
configuration waits up to `LAB_PIMOX_GUEST_AGENT_CONFIG_TIMEOUT_SECONDS=120`,
|
configuration waits up to `LAB_PIMOX_GUEST_AGENT_CONFIG_TIMEOUT_SECONDS=120`,
|
||||||
worker IP/SSH readiness waits up to `LAB_PIMOX_WORKER_WAIT_TIMEOUT_SECONDS=600`,
|
worker IP/SSH readiness waits up to `LAB_PIMOX_WORKER_WAIT_TIMEOUT_SECONDS=600`,
|
||||||
|
|
|
||||||
|
|
@ -727,13 +727,14 @@ Worker indexes are stable. Index `1` maps to VMID `9010`, node name
|
||||||
so on. `LAB_PIMOX_SKIP_WORKER_INDEXES=1` leaves the first slot unmanaged while
|
so on. `LAB_PIMOX_SKIP_WORKER_INDEXES=1` leaves the first slot unmanaged while
|
||||||
allowing higher indexes to be automated.
|
allowing higher indexes to be automated.
|
||||||
|
|
||||||
Worker networking uses DHCP by default to preserve the known-good Pimox bridge
|
Worker networking is deterministic by default. After cloning or reusing a VM,
|
||||||
path. Set `LAB_PIMOX_WORKER_NETWORK_MODE=static` only when DHCP reservations are
|
the pipeline uses qemu-guest-agent to write a static `/etc/network/interfaces`
|
||||||
not available and you explicitly want Jeannie to configure guest static IPs with
|
entry before waiting for SSH, so a missing DHCP lease cannot stall the build.
|
||||||
qemu-guest-agent. Static mode uses
|
Defaults are `LAB_PIMOX_WORKER_NETWORK_MODE=static`,
|
||||||
`LAB_PIMOX_WORKER_STATIC_IPS="192.168.100.66 192.168.100.67 192.168.100.76"`,
|
`LAB_PIMOX_WORKER_STATIC_IPS="192.168.100.66 192.168.100.67 192.168.100.76"`,
|
||||||
`LAB_PIMOX_WORKER_GATEWAY=192.168.100.1`, and
|
`LAB_PIMOX_WORKER_GATEWAY=192.168.100.1`, and
|
||||||
`LAB_PIMOX_WORKER_DNS_SERVERS="192.168.100.89 1.1.1.1"`.
|
`LAB_PIMOX_WORKER_DNS_SERVERS="192.168.100.89 1.1.1.1"`. Set
|
||||||
|
`LAB_PIMOX_WORKER_NETWORK_MODE=dhcp` to restore the old DHCP-only behavior.
|
||||||
Worker creation fails faster than template creation: static guest-network
|
Worker creation fails faster than template creation: static guest-network
|
||||||
configuration waits up to `LAB_PIMOX_GUEST_AGENT_CONFIG_TIMEOUT_SECONDS=120`,
|
configuration waits up to `LAB_PIMOX_GUEST_AGENT_CONFIG_TIMEOUT_SECONDS=120`,
|
||||||
worker IP/SSH readiness waits up to `LAB_PIMOX_WORKER_WAIT_TIMEOUT_SECONDS=600`,
|
worker IP/SSH readiness waits up to `LAB_PIMOX_WORKER_WAIT_TIMEOUT_SECONDS=600`,
|
||||||
|
|
|
||||||
|
|
@ -142,12 +142,14 @@ destroy and recreate existing worker VMs from the current template. The pipeline
|
||||||
refuses `LAB_PIMOX_WORKER_STORAGE=local` so only the template VM lives on local
|
refuses `LAB_PIMOX_WORKER_STORAGE=local` so only the template VM lives on local
|
||||||
storage.
|
storage.
|
||||||
|
|
||||||
Worker networking defaults to DHCP to preserve the known-good Pimox bridge path:
|
Worker networking defaults to deterministic static LAN addresses configured
|
||||||
`LAB_PIMOX_WORKER_NETWORK_MODE=dhcp`. Static mode is still available when DHCP
|
through qemu-guest-agent after clone/start and before SSH wait:
|
||||||
reservations are not available: set `LAB_PIMOX_WORKER_NETWORK_MODE=static`,
|
`LAB_PIMOX_WORKER_NETWORK_MODE=static`,
|
||||||
`LAB_PIMOX_WORKER_STATIC_IPS="192.168.100.66 192.168.100.67 192.168.100.76"`,
|
`LAB_PIMOX_WORKER_STATIC_IPS="192.168.100.66 192.168.100.67 192.168.100.76"`,
|
||||||
`LAB_PIMOX_WORKER_GATEWAY=192.168.100.1`, `LAB_PIMOX_WORKER_INTERFACE=enp0s18`,
|
`LAB_PIMOX_WORKER_GATEWAY=192.168.100.1`, `LAB_PIMOX_WORKER_INTERFACE=enp0s18`,
|
||||||
and `LAB_PIMOX_WORKER_DNS_SERVERS="192.168.100.89 1.1.1.1"`.
|
and `LAB_PIMOX_WORKER_DNS_SERVERS="192.168.100.89 1.1.1.1"`. Use
|
||||||
|
`LAB_PIMOX_WORKER_NETWORK_MODE=dhcp` only when DHCP reservations are known to be
|
||||||
|
stable.
|
||||||
Worker clone failures are intentionally faster than template build failures:
|
Worker clone failures are intentionally faster than template build failures:
|
||||||
`LAB_PIMOX_GUEST_AGENT_CONFIG_TIMEOUT_SECONDS=120`,
|
`LAB_PIMOX_GUEST_AGENT_CONFIG_TIMEOUT_SECONDS=120`,
|
||||||
`LAB_PIMOX_WORKER_WAIT_TIMEOUT_SECONDS=600`, and
|
`LAB_PIMOX_WORKER_WAIT_TIMEOUT_SECONDS=600`, and
|
||||||
|
|
|
||||||
|
|
@ -539,10 +539,6 @@ falls back to the first Lynis warning, then the first suggestion.
|
||||||
: Desired Pimox worker count when not supplied by generated cluster topology
|
: Desired Pimox worker count when not supplied by generated cluster topology
|
||||||
state.
|
state.
|
||||||
|
|
||||||
`LAB_PIMOX_WORKER_NETWORK_MODE`
|
|
||||||
: Worker guest networking mode. Defaults to `dhcp`. Set to `static` only when
|
|
||||||
Jeannie should configure guest static IPs through qemu-guest-agent.
|
|
||||||
|
|
||||||
`LAB_PIMOX_WORKER_REQUIRE_LAN_PROBE`
|
`LAB_PIMOX_WORKER_REQUIRE_LAN_PROBE`
|
||||||
: Require the worker guest to ping the LAN gateway and probe host after static
|
: Require the worker guest to ping the LAN gateway and probe host after static
|
||||||
IP assignment. Defaults to `true`.
|
IP assignment. Defaults to `true`.
|
||||||
|
|
|
||||||
30
jeannie
30
jeannie
|
|
@ -1051,26 +1051,6 @@ echo 'Pimox VM ${vmid} guest-agent network-get-interfaces:'
|
||||||
sudo '${qm_bin}' guest cmd '${vmid}' network-get-interfaces" >&2 || true
|
sudo '${qm_bin}' guest cmd '${vmid}' network-get-interfaces" >&2 || true
|
||||||
}
|
}
|
||||||
|
|
||||||
pimox_guest_exec_exitcode() {
|
|
||||||
local guest_exec_json="$1"
|
|
||||||
|
|
||||||
GUEST_EXEC_JSON="${guest_exec_json}" python3 - <<'PY'
|
|
||||||
import json
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
|
|
||||||
try:
|
|
||||||
document = json.loads(os.environ.get("GUEST_EXEC_JSON", ""))
|
|
||||||
except Exception:
|
|
||||||
sys.exit(2)
|
|
||||||
|
|
||||||
try:
|
|
||||||
print(int(document.get("exitcode", 1)))
|
|
||||||
except Exception:
|
|
||||||
sys.exit(2)
|
|
||||||
PY
|
|
||||||
}
|
|
||||||
|
|
||||||
wait_for_pimox_guest_ssh() {
|
wait_for_pimox_guest_ssh() {
|
||||||
local host="$1"
|
local host="$1"
|
||||||
local user="$2"
|
local user="$2"
|
||||||
|
|
@ -1234,7 +1214,6 @@ configure_pimox_worker_guest_network() {
|
||||||
local script
|
local script
|
||||||
local encoded_script
|
local encoded_script
|
||||||
local network_output
|
local network_output
|
||||||
local network_exitcode
|
|
||||||
local worker_address
|
local worker_address
|
||||||
local lan_probe_host="${LAB_PIMOX_WORKER_LAN_PROBE_HOST:-${LAB_DEBIAN_LAN_IP:-}}"
|
local lan_probe_host="${LAB_PIMOX_WORKER_LAN_PROBE_HOST:-${LAB_DEBIAN_LAN_IP:-}}"
|
||||||
local require_lan_probe="${LAB_PIMOX_WORKER_REQUIRE_LAN_PROBE:-true}"
|
local require_lan_probe="${LAB_PIMOX_WORKER_REQUIRE_LAN_PROBE:-true}"
|
||||||
|
|
@ -1329,15 +1308,8 @@ EOF
|
||||||
while ((SECONDS < deadline)); do
|
while ((SECONDS < deadline)); do
|
||||||
if network_output="$(pimox_ssh "${pimox_host}" "${pimox_user}" "${pimox_key}" "sudo '${qm_bin}' guest exec '${vmid}' -- bash -lc \"printf '%s' '${encoded_script}' | base64 -d | sudo bash\"" 2>&1)"; then
|
if network_output="$(pimox_ssh "${pimox_host}" "${pimox_user}" "${pimox_key}" "sudo '${qm_bin}' guest exec '${vmid}' -- bash -lc \"printf '%s' '${encoded_script}' | base64 -d | sudo bash\"" 2>&1)"; then
|
||||||
printf '%s\n' "${network_output}" | sed 's/^/ guest-network: /'
|
printf '%s\n' "${network_output}" | sed 's/^/ guest-network: /'
|
||||||
if network_exitcode="$(pimox_guest_exec_exitcode "${network_output}")" && [[ "${network_exitcode}" == "0" ]]; then
|
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
echo "Static guest network validation failed inside Pimox VM ${vmid}." >&2
|
|
||||||
if [[ -n "${network_exitcode:-}" ]]; then
|
|
||||||
echo "Guest exit code: ${network_exitcode}" >&2
|
|
||||||
fi
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
sleep 5
|
sleep 5
|
||||||
done
|
done
|
||||||
|
|
||||||
|
|
@ -1425,7 +1397,7 @@ ensure_pimox_worker_node() {
|
||||||
local worker_storage="${19}"
|
local worker_storage="${19}"
|
||||||
local worker_replace_existing="${20}"
|
local worker_replace_existing="${20}"
|
||||||
local worker_cpu_affinity="${21}"
|
local worker_cpu_affinity="${21}"
|
||||||
local worker_network_mode="${LAB_PIMOX_WORKER_NETWORK_MODE:-dhcp}"
|
local worker_network_mode="${LAB_PIMOX_WORKER_NETWORK_MODE:-static}"
|
||||||
local worker_static_ips="${LAB_PIMOX_WORKER_STATIC_IPS:-192.168.100.66 192.168.100.67 192.168.100.76}"
|
local worker_static_ips="${LAB_PIMOX_WORKER_STATIC_IPS:-192.168.100.66 192.168.100.67 192.168.100.76}"
|
||||||
local worker_gateway="${LAB_PIMOX_WORKER_GATEWAY:-192.168.100.1}"
|
local worker_gateway="${LAB_PIMOX_WORKER_GATEWAY:-192.168.100.1}"
|
||||||
local worker_dns_servers="${LAB_PIMOX_WORKER_DNS_SERVERS:-192.168.100.89 1.1.1.1}"
|
local worker_dns_servers="${LAB_PIMOX_WORKER_DNS_SERVERS:-192.168.100.89 1.1.1.1}"
|
||||||
|
|
|
||||||
|
|
@ -330,12 +330,6 @@ test_pimox_worker_static_ip_selection() {
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
test_pimox_guest_exec_exitcode_parser() {
|
|
||||||
assert_eq "pimox guest exec parses success exitcode" "0" "$(pimox_guest_exec_exitcode '{"exitcode":0,"exited":1}')"
|
|
||||||
assert_eq "pimox guest exec parses failed exitcode" "1" "$(pimox_guest_exec_exitcode '{"exitcode":1,"exited":1}')"
|
|
||||||
assert_command_failure_contains "pimox guest exec rejects invalid JSON" "" pimox_guest_exec_exitcode 'not json'
|
|
||||||
}
|
|
||||||
|
|
||||||
test_pimox_worker_count_detects_generated_topology
|
test_pimox_worker_count_detects_generated_topology
|
||||||
test_configured_worker_count_cannot_hide_existing_workers
|
test_configured_worker_count_cannot_hide_existing_workers
|
||||||
test_configured_worker_count_can_expand_target_set
|
test_configured_worker_count_can_expand_target_set
|
||||||
|
|
@ -349,7 +343,6 @@ test_inventory_validator_accepts_current_inventory
|
||||||
test_inventory_validator_rejects_ip_drift
|
test_inventory_validator_rejects_ip_drift
|
||||||
test_heal_cooldown_escalates_to_diagnostics
|
test_heal_cooldown_escalates_to_diagnostics
|
||||||
test_pimox_worker_static_ip_selection
|
test_pimox_worker_static_ip_selection
|
||||||
test_pimox_guest_exec_exitcode_parser
|
|
||||||
|
|
||||||
if ((FAILURES > 0)); then
|
if ((FAILURES > 0)); then
|
||||||
printf '\n%s unit test(s) failed.\n' "${FAILURES}" >&2
|
printf '\n%s unit test(s) failed.\n' "${FAILURES}" >&2
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue