Add quick-register one-liner for target-host registration
CI / Lint + build + test (push) Failing after 5m15s
CI / Lint + build + test (push) Failing after 5m15s
Operator pastes `curl -fsSL $ORCH/register/quick.sh | sudo bash` on the target host (pre-wipe). The script probes MAC + CPU/RAM/disks/NICs/GPUs, emits an expected-spec YAML, and POSTs to a new LAN-trusted JSON endpoint /api/v1/hosts. The register page shows the command prefilled with the orchestrator URL; the manual form moves into a collapsible "Register manually" disclosure.
This commit is contained in:
@@ -0,0 +1,146 @@
|
||||
#!/usr/bin/env bash
|
||||
# Vetting quick-register.
|
||||
#
|
||||
# Run on the target host (any Linux with root) before the host is wiped:
|
||||
# curl -fsSL {{.OrchestratorURL}}/register/quick.sh | sudo bash
|
||||
#
|
||||
# Detects the primary NIC's MAC, probes hardware (CPU / RAM / disks /
|
||||
# NICs / GPUs) into an expected-spec YAML, and POSTs everything to
|
||||
# {{.OrchestratorURL}}/api/v1/hosts. After registration, go to the
|
||||
# orchestrator's dashboard and click "Start vetting" for the new host.
|
||||
#
|
||||
# Env overrides (all optional):
|
||||
# NAME Host display name (default: `hostname -s`)
|
||||
# MAC Force a specific MAC (default: autodetect)
|
||||
# WOL_BROADCAST WoL broadcast IP (default: primary iface broadcast)
|
||||
# WOL_PORT WoL UDP port (default: 9)
|
||||
# NOTES Free-text notes
|
||||
# ORCH_URL Override orchestrator base URL
|
||||
set -euo pipefail
|
||||
|
||||
ORCH_URL="${ORCH_URL:-{{.OrchestratorURL}}}"
|
||||
|
||||
if [[ -z "${ORCH_URL}" ]]; then
|
||||
echo "ERROR: ORCH_URL is empty; pass it via env." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
primary_iface() {
|
||||
# Pick the first physical ethernet-style interface that isn't a loopback,
|
||||
# bridge, veth, docker, virbr, bond, tun, or tap.
|
||||
ip -o link show 2>/dev/null \
|
||||
| awk '$0 ~ /link\/ether/ {
|
||||
name=$2; sub(":","",name)
|
||||
if (name ~ /^(lo|docker|br-|veth|virbr|bond|tun|tap|wlan|wlp)/) next
|
||||
print name; exit
|
||||
}'
|
||||
}
|
||||
|
||||
IFACE="$(primary_iface || true)"
|
||||
if [[ -z "${IFACE}" ]]; then
|
||||
echo "ERROR: could not pick a primary network interface." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
NAME="${NAME:-$(hostname -s 2>/dev/null || hostname)}"
|
||||
MAC="${MAC:-$(cat /sys/class/net/${IFACE}/address 2>/dev/null || true)}"
|
||||
if [[ -z "${MAC}" ]]; then
|
||||
echo "ERROR: could not read MAC for ${IFACE}." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -z "${WOL_BROADCAST:-}" ]]; then
|
||||
WOL_BROADCAST="$(ip -o -4 addr show dev "${IFACE}" 2>/dev/null \
|
||||
| awk '{for(i=1;i<=NF;i++) if($i=="brd") {print $(i+1); exit}}')"
|
||||
fi
|
||||
WOL_BROADCAST="${WOL_BROADCAST:-255.255.255.255}"
|
||||
WOL_PORT="${WOL_PORT:-9}"
|
||||
|
||||
# --- Hardware probes ---
|
||||
cores="$(nproc 2>/dev/null || echo 0)"
|
||||
cpu_model="$(awk -F: '/^model name/ {sub(/^ */, "", $2); print $2; exit}' /proc/cpuinfo 2>/dev/null || true)"
|
||||
mem_gib="$(awk '/^MemTotal:/ {printf "%d", ($2/1024/1024) + 0.5; exit}' /proc/meminfo 2>/dev/null || echo 0)"
|
||||
|
||||
disk_yaml=""
|
||||
while read -r name size serial; do
|
||||
[[ -z "${name}" ]] && continue
|
||||
[[ "${name}" =~ ^(sd|nvme|vd|hd) ]] || continue
|
||||
[[ -z "${serial}" ]] && continue
|
||||
size_gb=$(( size / 1000 / 1000 / 1000 ))
|
||||
disk_yaml+=" - serial: \"${serial}\"
|
||||
size_gb: ${size_gb}
|
||||
"
|
||||
done < <(lsblk -dn -b -o NAME,SIZE,SERIAL 2>/dev/null || true)
|
||||
|
||||
nic_yaml=""
|
||||
for iface_dir in /sys/class/net/*; do
|
||||
iface_name="$(basename "${iface_dir}")"
|
||||
[[ "${iface_name}" =~ ^(lo|docker|br-|veth|virbr|bond|tun|tap)$ ]] && continue
|
||||
nic_mac="$(cat "${iface_dir}/address" 2>/dev/null || true)"
|
||||
[[ -z "${nic_mac}" || "${nic_mac}" == "00:00:00:00:00:00" ]] && continue
|
||||
speed_mbps="$(cat "${iface_dir}/speed" 2>/dev/null || echo 0)"
|
||||
if [[ "${speed_mbps}" =~ ^[0-9]+$ ]] && (( speed_mbps > 0 )); then
|
||||
speed_gbps=$(( (speed_mbps + 999) / 1000 ))
|
||||
else
|
||||
speed_gbps=0
|
||||
fi
|
||||
nic_yaml+=" - mac: \"${nic_mac}\"
|
||||
speed_gbps: ${speed_gbps}
|
||||
"
|
||||
done
|
||||
|
||||
gpu_yaml=""
|
||||
if command -v lspci >/dev/null 2>&1; then
|
||||
while IFS= read -r gpu; do
|
||||
[[ -z "${gpu}" ]] && continue
|
||||
gpu_yaml+=" - model: \"${gpu}\"
|
||||
"
|
||||
done < <(lspci -mm 2>/dev/null | awk -F\" '/"(VGA|3D|Display)/ {print $6}')
|
||||
fi
|
||||
|
||||
# Assemble the YAML spec. Empty sections are omitted so the diff engine
|
||||
# skips them rather than demanding empty arrays on the actual side.
|
||||
spec="cpu:
|
||||
model: \"${cpu_model}\"
|
||||
logical_cores: ${cores}
|
||||
memory:
|
||||
total_gib: ${mem_gib}
|
||||
"
|
||||
[[ -n "${disk_yaml}" ]] && spec+="disks:
|
||||
${disk_yaml}"
|
||||
[[ -n "${nic_yaml}" ]] && spec+="nics:
|
||||
${nic_yaml}"
|
||||
[[ -n "${gpu_yaml}" ]] && spec+="gpus:
|
||||
${gpu_yaml}"
|
||||
|
||||
# --- JSON escape (backslash, double-quote, newline, tab, CR) ---
|
||||
json_escape() {
|
||||
local s="$1"
|
||||
s="${s//\\/\\\\}"
|
||||
s="${s//\"/\\\"}"
|
||||
s="${s//$'\t'/\\t}"
|
||||
s="${s//$'\r'/\\r}"
|
||||
s="${s//$'\n'/\\n}"
|
||||
printf '%s' "${s}"
|
||||
}
|
||||
|
||||
payload=$(cat <<EOF
|
||||
{
|
||||
"name": "$(json_escape "${NAME}")",
|
||||
"mac": "$(json_escape "${MAC}")",
|
||||
"wol_broadcast_ip": "$(json_escape "${WOL_BROADCAST}")",
|
||||
"wol_port": ${WOL_PORT},
|
||||
"expected_spec_yaml": "$(json_escape "${spec}")",
|
||||
"notes": "$(json_escape "${NOTES:-registered via quick-register on $(date -Is)}")"
|
||||
}
|
||||
EOF
|
||||
)
|
||||
|
||||
echo "Registering ${NAME} (${MAC}) with ${ORCH_URL}..."
|
||||
resp="$(curl -fsS -X POST \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d "${payload}" \
|
||||
"${ORCH_URL}/api/v1/hosts")"
|
||||
echo "OK: ${resp}"
|
||||
echo
|
||||
echo "Open ${ORCH_URL}/ and click 'Start vetting' on ${NAME}."
|
||||
Reference in New Issue
Block a user