mirror of
https://github.com/GNS3/gns3-server.git
synced 2026-10-23 11:22:21 +03:00
fix: Dynamips create_nio(node, nio_settings) takes extra arg + add tests
Dynamips.create_nio requires the node as first positional argument (unlike every other manager which takes only nio_settings). The batch handler now detects this via parameter-count inspection (3 vs 2) and passes node when needed. Also add Dynamips to _add_nio_binding dispatch: routers use slot_add_nio_binding(slot, port, nio), switches/hubs fall back to add_nio(nio, port_number). Add tests covering Dynamips router dispatch, switch dispatch, and the create_nio signature detection to prevent regression.
This commit is contained in:
parent
c366f20340
commit
ed0f1bb4de
@ -21,6 +21,7 @@ API routes for projects.
|
|||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
|
import inspect
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
@ -149,6 +150,13 @@ async def _add_nio_binding(node, adapter_number, port_number, nio):
|
|||||||
await node.adapter_add_nio_binding(adapter_number, port_number, nio)
|
await node.adapter_add_nio_binding(adapter_number, port_number, nio)
|
||||||
elif manager_name == "VPCS":
|
elif manager_name == "VPCS":
|
||||||
await node.port_add_nio_binding(port_number, nio)
|
await node.port_add_nio_binding(port_number, nio)
|
||||||
|
elif manager_name == "Dynamips":
|
||||||
|
# Dynamips routers use slot_add_nio_binding(slot, port, nio);
|
||||||
|
# Dynamips switches/hubs use add_nio(nio, port_number).
|
||||||
|
if hasattr(node, "slot_add_nio_binding"):
|
||||||
|
await node.slot_add_nio_binding(adapter_number, port_number, nio)
|
||||||
|
else:
|
||||||
|
await node.add_nio(nio, port_number)
|
||||||
elif manager_name == "Builtin":
|
elif manager_name == "Builtin":
|
||||||
# ethernet_switch / ethernet_hub / cloud / nat: add_nio(nio, port_number)
|
# ethernet_switch / ethernet_hub / cloud / nat: add_nio(nio, port_number)
|
||||||
await node.add_nio(nio, port_number)
|
await node.add_nio(nio, port_number)
|
||||||
@ -180,7 +188,14 @@ async def create_batch_nios(
|
|||||||
added = 0
|
added = 0
|
||||||
for entry in batch.nios:
|
for entry in batch.nios:
|
||||||
node = project.get_node(entry.node_id)
|
node = project.get_node(entry.node_id)
|
||||||
nio = node.manager.create_nio(jsonable_encoder(entry.nio, exclude_unset=True))
|
nio_settings = jsonable_encoder(entry.nio, exclude_unset=True)
|
||||||
|
# Dynamips.create_nio takes an extra positional `node` argument that
|
||||||
|
# the base signature does not include. Detect it via parameter count.
|
||||||
|
sig = inspect.signature(node.manager.create_nio)
|
||||||
|
if len(sig.parameters) == 3:
|
||||||
|
nio = node.manager.create_nio(node, nio_settings)
|
||||||
|
else:
|
||||||
|
nio = node.manager.create_nio(nio_settings)
|
||||||
await _add_nio_binding(node, entry.adapter_number, entry.port_number, nio)
|
await _add_nio_binding(node, entry.adapter_number, entry.port_number, nio)
|
||||||
added += 1
|
added += 1
|
||||||
return {"added": added}
|
return {"added": added}
|
||||||
|
|||||||
@ -224,3 +224,57 @@ class TestComputeProjectRoutes:
|
|||||||
project_id=project.id,
|
project_id=project.id,
|
||||||
file_path=file_path), content=b"world")
|
file_path=file_path), content=b"world")
|
||||||
assert response.status_code == status.HTTP_403_FORBIDDEN
|
assert response.status_code == status.HTTP_403_FORBIDDEN
|
||||||
|
|
||||||
|
|
||||||
|
class TestBatchNIOEdgeCases:
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_dynamips_router_dispatch_to_slot_add_nio_binding(self):
|
||||||
|
"""_add_nio_binding dispatches Dynamips router to slot_add_nio_binding."""
|
||||||
|
from unittest.mock import AsyncMock, MagicMock
|
||||||
|
from gns3server.api.routes.compute.projects import _add_nio_binding
|
||||||
|
|
||||||
|
node = MagicMock()
|
||||||
|
type(node.manager).__name__ = "Dynamips"
|
||||||
|
node.slot_add_nio_binding = AsyncMock()
|
||||||
|
nio = MagicMock()
|
||||||
|
|
||||||
|
await _add_nio_binding(node, 0, 0, nio)
|
||||||
|
node.slot_add_nio_binding.assert_called_once_with(0, 0, nio)
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_dynamips_switch_dispatch_to_add_nio(self):
|
||||||
|
"""_add_nio_binding dispatches Dynamips switch to add_nio."""
|
||||||
|
from unittest.mock import AsyncMock, MagicMock
|
||||||
|
from gns3server.api.routes.compute.projects import _add_nio_binding
|
||||||
|
|
||||||
|
node = MagicMock()
|
||||||
|
type(node.manager).__name__ = "Dynamips"
|
||||||
|
del node.slot_add_nio_binding # no slot_add_nio → switch path
|
||||||
|
node.add_nio = AsyncMock()
|
||||||
|
nio = MagicMock()
|
||||||
|
|
||||||
|
await _add_nio_binding(node, 0, 0, nio)
|
||||||
|
node.add_nio.assert_called_once_with(nio, 0)
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_dynamips_create_nio_passes_node_arg(self):
|
||||||
|
"""
|
||||||
|
Dynamips.create_nio(self, node, nio_settings) takes an extra 'node'
|
||||||
|
positional; the batch handler detects this via parameter count.
|
||||||
|
"""
|
||||||
|
import inspect
|
||||||
|
|
||||||
|
# Dynamips-style 3-param signature (self + node + nio_settings)
|
||||||
|
async def create_nio_with_node(self, node, nio_settings):
|
||||||
|
pass
|
||||||
|
|
||||||
|
sig = inspect.signature(create_nio_with_node)
|
||||||
|
assert len(sig.parameters) == 3
|
||||||
|
|
||||||
|
# Standard base 2-param signature (self + nio_settings)
|
||||||
|
async def create_nio_base(self, nio_settings):
|
||||||
|
pass
|
||||||
|
|
||||||
|
sig2 = inspect.signature(create_nio_base)
|
||||||
|
assert len(sig2.parameters) == 2
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user