Add DHCP leases management to dashboard and UI
Implement a new API endpoint to retrieve current DHCP leases from dnsmasq, enhancing the dashboard's functionality for monitoring network devices. Update the home.html template to display DHCP lease information in a structured table format, including IP, MAC, hostname, and expiry details. Introduce buttons for enabling and disabling DHCP network boot, improving user interaction. Enhance JavaScript to fetch and display lease data dynamically, ensuring users have real-time visibility of network activity.
This commit is contained in:
@@ -42,6 +42,7 @@ CLOUDINIT_TEMPLATES_FILE = Path(os.environ.get("CM4_CLOUDINIT_TEMPLATES_FILE", s
|
||||
PORTAL_DESCRIPTIONS_FILE = Path(os.environ.get("CM4_PORTAL_DESCRIPTIONS_FILE", str(BASE_DIR / "portal_descriptions.json")))
|
||||
DB_PATH = Path(os.environ.get("CM4_DASHBOARD_DB", str(BASE_DIR / "dashboard.db")))
|
||||
TOGGLE_NETWORK_BOOT_SCRIPT = os.environ.get("CM4_TOGGLE_NETWORK_BOOT_SCRIPT", "/opt/cm4-provisioning/toggle-network-boot-dhcp.sh")
|
||||
DHCP_LEASES_FILE = os.environ.get("CM4_DHCP_LEASES_FILE", "/var/lib/misc/dnsmasq.leases")
|
||||
|
||||
|
||||
# --- Database (admin users + activity logs) ---
|
||||
@@ -593,13 +594,46 @@ def api_dhcp_network_boot_post():
|
||||
@app.route("/api/action-done", methods=["POST"])
|
||||
def api_action_done():
|
||||
"""Called by a device when deploy or backup has completed. Disables DHCP network-boot so the device boots from eMMC next time."""
|
||||
mac = (request.args.get("mac") or request.get_json(silent=True) or {}).get("mac", "")
|
||||
mac = request.args.get("mac") or ((request.get_json(silent=True) or {}).get("mac") or "")
|
||||
ok, _ = _dhcp_network_boot_run("disable")
|
||||
if not ok:
|
||||
return jsonify({"ok": False, "error": "Could not disable DHCP network boot"}), 500
|
||||
return jsonify({"ok": True, "message": "Network boot disabled; device will boot from eMMC on next boot"})
|
||||
|
||||
|
||||
def _read_dhcp_leases():
|
||||
"""Read dnsmasq lease file. Returns (leases_list, error_string). leases_list items: {expiry, mac, ip, hostname}."""
|
||||
if not DHCP_LEASES_FILE or not os.path.isfile(DHCP_LEASES_FILE):
|
||||
return [], None
|
||||
try:
|
||||
leases = []
|
||||
with open(DHCP_LEASES_FILE, "r") as f:
|
||||
for line in f:
|
||||
line = line.strip()
|
||||
if not line or line.startswith("#"):
|
||||
continue
|
||||
parts = line.split()
|
||||
if len(parts) >= 4:
|
||||
leases.append({
|
||||
"expiry": int(parts[0]) if parts[0].isdigit() else 0,
|
||||
"mac": parts[1],
|
||||
"ip": parts[2],
|
||||
"hostname": parts[3] if len(parts) > 3 else "",
|
||||
})
|
||||
return leases, None
|
||||
except (OSError, PermissionError) as e:
|
||||
return [], str(e)
|
||||
|
||||
|
||||
@app.route("/api/dhcp-leases")
|
||||
def api_dhcp_leases():
|
||||
"""Return current DHCP leases from dnsmasq (when dashboard runs on LXC with dnsmasq)."""
|
||||
leases, err = _read_dhcp_leases()
|
||||
if err:
|
||||
return jsonify({"leases": [], "error": err})
|
||||
return jsonify({"leases": leases, "error": None})
|
||||
|
||||
|
||||
@app.route("/api/device-action-poll")
|
||||
def api_device_action_poll():
|
||||
"""Network device polls this to get its assigned action (deploy/backup) and URL."""
|
||||
|
||||
Reference in New Issue
Block a user