add hostname provisioning step

This commit is contained in:
2025-09-01 16:50:18 +02:00
parent 3c03f2a58c
commit 2be380f614

View File

@@ -1,5 +1,5 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import ipaddress, os, re, subprocess, tempfile, json, datetime import ipaddress, os, re, subprocess, tempfile, json, datetime, shlex
from pathlib import Path from pathlib import Path
from dotenv import load_dotenv from dotenv import load_dotenv
@@ -111,13 +111,66 @@ def step_wireguard_provision(iot_host: str, client_name: str):
return {"wg_name": name, "wg_iface": iface} return {"wg_name": name, "wg_iface": iface}
def step_set_hostname(iot_host: str, hostname: str | None): def step_set_hostname(iot_host: str, hostname: str | None):
"""Placeholder: set hostname on the device via custom script under /home/caster/bumble-auracast/src/server. """Set hostname on the device by running the project's provision script over SSH.
Intention: SSH into device and run a script, e.g. /home/caster/bumble-auracast/src/server/set-hostname <hostname>. Executes: /home/caster/bumble-auracast/src/auracast/server/provision_domain_hostname.sh <hostname> <domain>
Currently does nothing. Domain is always 'local'. Returns a dict including whether the hostname appears changed.
""" """
print("⏭️ [placeholder] Skipping hostname change (no-op). Intended hostname:", hostname) if not hostname:
return {"hostname": hostname} print("⏭️ hostname: no hostname provided, skipping")
return {"hostname": None, "changed": False}
# Known locations where the script might live on the device
candidates = [
"/home/caster/bumble-auracast/src/auracast/server/provision_domain_hostname.sh",
"/home/caster/bumble-auracast/src/server/provision_domain_hostname.sh",
]
domain = "local"
# Build remote command. Use sudo since hostname changes typically require elevated privileges.
quoted_name = shlex.quote(hostname)
quoted_domain = shlex.quote(domain)
# Build a remote snippet that picks the first existing candidate and runs it via bash
# Using bash avoids executable-bit/shebang issues.
remote_candidates = " ".join(shlex.quote(c) for c in candidates)
remote = (
"set -e\n"
f"for p in {remote_candidates}; do\n"
" if [ -f \"$p\" ]; then sp=\"$p\"; break; fi\n"
"done\n"
"if [ -z \"${sp:-}\" ]; then echo 'script not found in any candidate path' >&2; exit 1; fi\n"
f"sudo bash \"$sp\" {quoted_name} {quoted_domain}\n"
"hostname 2>/dev/null || true\n"
)
ssh_cmd = ["ssh", "-p", str(SSH_PORT)]
if SSH_KEY:
ssh_cmd += ["-i", SSH_KEY]
ssh_cmd += [f"{SSH_USER}@{iot_host}", remote]
proc = subprocess.run(ssh_cmd, check=False, capture_output=True, text=True)
stdout = (proc.stdout or "").strip()
stderr = (proc.stderr or "").strip()
# After running the script, re-check the hostname from the device
facts_post = get_device_facts(iot_host)
new_hostname = facts_post.get("hostname")
changed = bool(new_hostname) and (new_hostname == hostname)
if proc.returncode != 0:
print(f"❌ hostname: remote script failed rc={proc.returncode}: {stderr}")
else:
print(f"✅ hostname: script executed. device hostname now '{new_hostname}' (rc={proc.returncode})")
return {
"hostname": hostname,
"domain": domain,
"device_hostname": new_hostname,
"changed": changed,
"rc": proc.returncode,
"out": stdout[-500:], # tail to keep logs compact
"err": stderr[-500:],
}
def step_update_app(iot_host: str, services: list[str] | None = None): def step_update_app(iot_host: str, services: list[str] | None = None):
"""Placeholder: start/enable required system services on the device. """Placeholder: start/enable required system services on the device.