Skip to main content
Glama
by kcsoukup
dhcp_management.py8.43 kB
""" DHCP management tools for static IP reservations. """ from typing import Any from mcp.types import TextContent from core.ssh_client import RouterSSHClient from utils.nvram_parser import build_dhcp_reservation_list, parse_dhcp_reservation_list from utils.validators import is_valid_ip, is_valid_mac, normalize_mac def handle_add_dhcp_reservation( router: RouterSSHClient, arguments: Any ) -> list[TextContent]: """ Add a DHCP static IP reservation for a device. Args: router: SSH client for router operations arguments: Dict with mac_address, ip_address, dns (optional), hostname (optional) Returns: List containing TextContent with result message """ mac_address = arguments.get("mac_address") ip_address = arguments.get("ip_address") dns = arguments.get("dns", "") hostname = arguments.get("hostname", "") # Validate inputs if not is_valid_mac(mac_address): return [ TextContent( type="text", text=f"Error: Invalid MAC address format: {mac_address}", ) ] if not is_valid_ip(ip_address): return [ TextContent( type="text", text=f"Error: Invalid IP address format: {ip_address}", ) ] # Normalize MAC mac_normalized = normalize_mac(mac_address) # Get current DHCP reservations (try both possible NVRAM variables) output, error, code = router.execute_command( "nvram get dhcp_staticlist 2>/dev/null || nvram get dhcp_reservelist 2>/dev/null" ) # Determine which variable to use var_check, _, _ = router.execute_command( "nvram show | grep -E 'dhcp_(staticlist|reservelist)=' | head -1" ) if "dhcp_staticlist" in var_check: dhcp_var = "dhcp_staticlist" elif "dhcp_reservelist" in var_check: dhcp_var = "dhcp_reservelist" else: # Default to dhcp_staticlist if neither exists dhcp_var = "dhcp_staticlist" current_reservations = parse_dhcp_reservation_list(output.strip()) # Check for duplicates for res in current_reservations: if res["mac"] == mac_normalized: return [ TextContent( type="text", text=f"Error: Reservation already exists for MAC {mac_normalized} with IP {res['ip']}", ) ] if res["ip"] == ip_address: return [ TextContent( type="text", text=f"Error: IP {ip_address} already reserved for MAC {res['mac']}", ) ] # Add new reservation current_reservations.append( { "mac": mac_normalized, "ip": ip_address, "dns": dns, "hostname": hostname, } ) new_list_str = build_dhcp_reservation_list(current_reservations) # Set DHCP reservation list set_cmd = f"nvram set {dhcp_var}='{new_list_str}'" output, error, code = router.execute_command(set_cmd) if code != 0: return [ TextContent(type="text", text=f"Error setting DHCP reservation: {error}") ] # Restart dnsmasq service output, error, code = router.execute_command("service restart_dnsmasq") result = "✓ DHCP reservation added:\n" result += f" MAC: {mac_normalized}\n" result += f" IP: {ip_address}\n" result += f" Hostname: {hostname}\n" if code == 0: result += "✓ DNSMASQ service restarted" else: result += f"⚠ DNSMASQ service restart failed: {error}" return [TextContent(type="text", text=result)] def handle_remove_dhcp_reservation( router: RouterSSHClient, arguments: Any ) -> list[TextContent]: """ Remove a DHCP static IP reservation for a device. Args: router: SSH client for router operations arguments: Dict with mac_address (optional) and/or ip_address (optional) Returns: List containing TextContent with result message """ mac_address = arguments.get("mac_address") ip_address = arguments.get("ip_address") # Must provide at least one if not mac_address and not ip_address: return [ TextContent( type="text", text="Error: Must provide either mac_address or ip_address", ) ] # Normalize MAC if provided if mac_address: if not is_valid_mac(mac_address): return [ TextContent( type="text", text=f"Error: Invalid MAC address format: {mac_address}", ) ] mac_address = normalize_mac(mac_address) # Validate IP if provided if ip_address and not is_valid_ip(ip_address): return [ TextContent( type="text", text=f"Error: Invalid IP address format: {ip_address}", ) ] # Get current DHCP reservations output, error, code = router.execute_command( "nvram get dhcp_staticlist 2>/dev/null || nvram get dhcp_reservelist 2>/dev/null" ) # Determine which variable to use var_check, _, _ = router.execute_command( "nvram show | grep -E 'dhcp_(staticlist|reservelist)=' | head -1" ) if "dhcp_staticlist" in var_check: dhcp_var = "dhcp_staticlist" elif "dhcp_reservelist" in var_check: dhcp_var = "dhcp_reservelist" else: dhcp_var = "dhcp_staticlist" current_reservations = parse_dhcp_reservation_list(output.strip()) # Find and remove matching reservation removed = None new_reservations = [] for res in current_reservations: if (mac_address and res["mac"] == mac_address) or ( ip_address and res["ip"] == ip_address ): removed = res else: new_reservations.append(res) if not removed: search_str = f"MAC {mac_address}" if mac_address else f"IP {ip_address}" return [ TextContent( type="text", text=f"Error: No DHCP reservation found for {search_str}", ) ] # Build new list new_list_str = build_dhcp_reservation_list(new_reservations) # Set DHCP reservation list set_cmd = f"nvram set {dhcp_var}='{new_list_str}'" output, error, code = router.execute_command(set_cmd) if code != 0: return [ TextContent(type="text", text=f"Error removing DHCP reservation: {error}") ] # Restart dnsmasq service output, error, code = router.execute_command("service restart_dnsmasq") result = "✓ DHCP reservation removed:\n" result += f" MAC: {removed['mac']}\n" result += f" IP: {removed['ip']}\n" result += f" Hostname: {removed['hostname']}\n" if code == 0: result += "✓ DNSMASQ service restarted" else: result += f"⚠ DNSMASQ service restart failed: {error}" return [TextContent(type="text", text=result)] def handle_list_dhcp_reservations( router: RouterSSHClient, _arguments: Any ) -> list[TextContent]: """ List all DHCP static IP reservations configured on the router. Args: router: SSH client for router operations arguments: Dict (unused for this operation) Returns: List containing TextContent with formatted reservation list """ # Get current DHCP reservations output, error, code = router.execute_command( "nvram get dhcp_staticlist 2>/dev/null || nvram get dhcp_reservelist 2>/dev/null" ) if code != 0: return [ TextContent(type="text", text=f"Error reading DHCP reservations: {error}") ] reservations = parse_dhcp_reservation_list(output.strip()) if not reservations: return [TextContent(type="text", text="No DHCP reservations configured")] result = f"DHCP Reservations ({len(reservations)}):\n\n" for i, res in enumerate(reservations, 1): hostname_display = res.get("hostname", "") or "(no hostname)" dns_display = res.get("dns", "") or "Default" result += f"{i}. {hostname_display}\n" result += f" MAC: {res['mac']}\n" result += f" IP: {res['ip']}\n" result += f" DNS: {dns_display}\n\n" return [TextContent(type="text", text=result.strip())]

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/kcsoukup/asus-merlin-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server