Merge pull request #3003 from yueguobin/fix/mcp-device-config-remote-compute

Fix MCP device tools for nodes on remote computes
This commit is contained in:
Jeremy Grossmann authored and GitHub committed 2026-10-16 10:49:56 +02:00
commit 578dbdceeb
6 files changed
+145 -6

No files matched your search

@@ -67,7 +67,7 @@ class GNS3TopologyTool(BaseTool):
Output: Dictionary with:
- `project_id`, `name`, `status`: Project metadata
- `nodes`: Dict of node details (node_id, name, ports, console_port,
type, etc.)
console_host, type, etc.)
- `links`: List of link connections
Use this to understand network structure before making changes.
@@ -43,9 +43,9 @@ def build_nodes_inventory(nodes: list[dict[str, Any]], server_host: str | None)
Build an inventory-style dict keyed by node name.
Shape (per node name):
{server, name, node_id, console_port, console_type, type, ports,
status, x, y, tags, netmiko_device_type, default_username,
default_password}
{server, name, node_id, console_port, console_host, console_type,
type, ports, status, x, y, tags, netmiko_device_type,
default_username, default_password}
"""
inventory: dict[str, Any] = {}
for n in nodes:
@@ -54,6 +54,9 @@ def build_nodes_inventory(nodes: list[dict[str, Any]], server_host: str | None)
"name": n.get("name"),
"node_id": n.get("node_id"),
"console_port": n.get("console"),
# Where the console port is bound: the compute's own host for
# remote computes, not the controller running this code.
"console_host": n.get("console_host"),
"console_type": n.get("console_type"),
"type": n.get("node_type"),
"ports": n.get("ports"),
@@ -451,7 +451,7 @@ class VPCSCommands(BaseTool):
continue
# VPCS devices use gns3_vpcs_telnet device type
hosts_data[device_name] = {
host_entry = {
"port": port,
"platform": "vpcs",
"groups": ["vpcs_devices"], # All VPCS devices share one group
@@ -465,6 +465,12 @@ class VPCSCommands(BaseTool):
}
},
}
# Remote computes bind console ports on their own host; carry the
# per-node hostname from the topology so it overrides the group's
# controller-wide default.
if "hostname" in device_ports[device_name]:
host_entry["hostname"] = device_ports[device_name]["hostname"]
hosts_data[device_name] = host_entry
return hosts_data
@@ -68,7 +68,12 @@ def get_device_ports_from_topology(
}
}
}
Devices that don't exist or missing console_port will not be included
Devices that don't exist or missing console_port will not be included.
Nodes whose console is bound on a remote compute also carry a
host-level "hostname" (copied from the topology's console_host) so
netmiko dials the compute hosting the console port instead of the
controller; for everything else the field is omitted and the shared
group hostname (the controller host) applies.
"""
# Log received parameters
logger.info("Called with device_names=%s, project_id=%s", device_names, project_id)
@@ -167,6 +172,15 @@ def get_device_ports_from_topology(
"connection_options": {"netmiko": {"extras": {"device_type": device_type}}},
}
# Per-node console hostname. Remote computes bind console ports
# on their own host, not on the controller running this code, so
# the group's controller-wide hostname cannot reach them. A
# host-level hostname (which wins over the group in nornir) fixes
# that; unset or wildcard values keep the group inheritance.
console_host = node_info.get("console_host")
if console_host and console_host not in ("0.0.0.0", "::"):
host_entry["hostname"] = console_host
# Per-node default credentials (seeded from the template appliance
# metadata) override the group's empty fallback. Only inject when
# set, so credential-less devices keep inheriting the group values.