feat(builtin): NAT node returns its interface IP info in asdict()

NAT now includes an 'interfaces' field in its response, filtered to
contain only the mapped NAT interface (virbr0 on Linux, vmnet8 on
macOS/Windows). This lets connected nodes discover the NAT subnet
and gateway address without needing to list all host interfaces.

The field format mirrors Cloud.asdict(): name, type, special, and
ip_addresses list (both IPv4 and IPv6).
This commit is contained in:
YueGuobin 2026-07-19 18:10:51 +08:00
parent ddf7d4ca60
commit 4943567962
No known key found for this signature in database
2 changed files with 73 additions and 31 deletions

View File

@ -87,6 +87,23 @@ class Nat(Cloud):
return True
def asdict(self):
nat_interface = self._ports_mapping[0].get("interface", "") if self._ports_mapping else ""
host_interfaces = []
network_interfaces = gns3server.utils.interfaces.interfaces()
for interface in network_interfaces:
if interface["name"] == nat_interface:
host_interfaces.append(
{
"name": interface["name"],
"type": interface["type"],
"special": interface["special"],
"ip_addresses": interface.get("ip_addresses", []),
}
)
break
return {
"name": self.name,
"usage": self.usage,
@ -94,4 +111,5 @@ class Nat(Cloud):
"project_id": self.project.id,
"status": "started",
"ports_mapping": self.ports_mapping,
"interfaces": host_interfaces,
}

View File

@ -37,7 +37,15 @@ def test_json_gns3vm(on_gns3vm, compute_project):
"port_number": 0,
"type": "ethernet"
}
]
],
"interfaces": [
{
"name": "virbr0",
"type": "ethernet",
"special": True,
"ip_addresses": [],
},
],
}
@ -47,39 +55,55 @@ def test_json_darwin(darwin_platform, compute_project):
{"name": "eth0", "special": False, "type": "ethernet"},
{"name": "vmnet8", "special": True, "type": "ethernet"}]):
nat = Nat("nat1", str(uuid.uuid4()), compute_project, MagicMock())
assert nat.asdict() == {
"name": "nat1",
"usage": "",
"node_id": nat.id,
"project_id": compute_project.id,
"status": "started",
"ports_mapping": [
{
"interface": "vmnet8",
"name": "nat0",
"port_number": 0,
"type": "ethernet"
}
]
}
assert nat.asdict() == {
"name": "nat1",
"usage": "",
"node_id": nat.id,
"project_id": compute_project.id,
"status": "started",
"ports_mapping": [
{
"interface": "vmnet8",
"name": "nat0",
"port_number": 0,
"type": "ethernet"
}
],
"interfaces": [
{
"name": "vmnet8",
"type": "ethernet",
"special": True,
"ip_addresses": [],
},
],
}
def test_json_windows_with_full_name_of_interface(windows_platform, project):
with patch("gns3server.utils.interfaces.interfaces", return_value=[
{"name": "VMware Network Adapter VMnet8", "special": True, "type": "ethernet"}]):
nat = Nat("nat1", str(uuid.uuid4()), project, MagicMock())
assert nat.asdict() == {
"name": "nat1",
"usage": "",
"node_id": nat.id,
"project_id": project.id,
"status": "started",
"ports_mapping": [
{
"interface": "VMware Network Adapter VMnet8",
"name": "nat0",
"port_number": 0,
"type": "ethernet"
}
]
}
assert nat.asdict() == {
"name": "nat1",
"usage": "",
"node_id": nat.id,
"project_id": project.id,
"status": "started",
"ports_mapping": [
{
"interface": "VMware Network Adapter VMnet8",
"name": "nat0",
"port_number": 0,
"type": "ethernet"
}
],
"interfaces": [
{
"name": "VMware Network Adapter VMnet8",
"type": "ethernet",
"special": True,
"ip_addresses": [],
},
],
}