mirror of
https://github.com/GNS3/gns3-server.git
synced 2026-11-03 14:01:15 +02:00
refactor: ruff autofixes
This commit is contained in:
455 files changed
+12322
-14762
No files matched your search
@@ -40,7 +40,7 @@ async def test_query_success(vm):
|
||||
|
||||
response = MagicMock()
|
||||
response.status = 200
|
||||
response.headers = {'CONTENT-TYPE': 'application/json'}
|
||||
response.headers = {"CONTENT-TYPE": "application/json"}
|
||||
|
||||
async def read():
|
||||
return b'{"c": false}'
|
||||
@@ -48,12 +48,14 @@ async def test_query_success(vm):
|
||||
response.read.side_effect = read
|
||||
vm._session.request = AsyncioMagicMock(return_value=response)
|
||||
data = await vm.query("POST", "test", data={"a": True}, params={"b": 1})
|
||||
vm._session.request.assert_called_with('POST',
|
||||
'http://docker/v{}/test'.format(DOCKER_MINIMUM_API_VERSION),
|
||||
data='{"a": true}',
|
||||
headers={'content-type': 'application/json'},
|
||||
params={'b': 1},
|
||||
timeout=300)
|
||||
vm._session.request.assert_called_with(
|
||||
"POST",
|
||||
"http://docker/v{}/test".format(DOCKER_MINIMUM_API_VERSION),
|
||||
data='{"a": true}',
|
||||
headers={"content-type": "application/json"},
|
||||
params={"b": 1},
|
||||
timeout=300,
|
||||
)
|
||||
|
||||
assert data == {"c": False}
|
||||
|
||||
@@ -71,12 +73,14 @@ async def test_query_error(vm):
|
||||
vm._session.request = AsyncioMagicMock(return_value=response)
|
||||
with pytest.raises(DockerError):
|
||||
await vm.query("POST", "test", data={"a": True}, params={"b": 1})
|
||||
vm._session.request.assert_called_with('POST',
|
||||
'http://docker/v{}/test'.format(DOCKER_MINIMUM_API_VERSION),
|
||||
data='{"a": true}',
|
||||
headers={'content-type': 'application/json'},
|
||||
params={'b': 1},
|
||||
timeout=300)
|
||||
vm._session.request.assert_called_with(
|
||||
"POST",
|
||||
"http://docker/v{}/test".format(DOCKER_MINIMUM_API_VERSION),
|
||||
data='{"a": true}',
|
||||
headers={"content-type": "application/json"},
|
||||
params={"b": 1},
|
||||
timeout=300,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@@ -92,12 +96,14 @@ async def test_query_error_json(vm):
|
||||
vm._session.request = AsyncioMagicMock(return_value=response)
|
||||
with pytest.raises(DockerError):
|
||||
await vm.query("POST", "test", data={"a": True}, params={"b": 1})
|
||||
vm._session.request.assert_called_with('POST',
|
||||
'http://docker/v{}/test'.format(DOCKER_MINIMUM_API_VERSION),
|
||||
data='{"a": true}',
|
||||
headers={'content-type': 'application/json'},
|
||||
params={'b': 1},
|
||||
timeout=300)
|
||||
vm._session.request.assert_called_with(
|
||||
"POST",
|
||||
"http://docker/v{}/test".format(DOCKER_MINIMUM_API_VERSION),
|
||||
data='{"a": true}',
|
||||
headers={"content-type": "application/json"},
|
||||
params={"b": 1},
|
||||
timeout=300,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@@ -105,28 +111,20 @@ async def test_list_images():
|
||||
|
||||
response = [
|
||||
{
|
||||
"RepoTags": [
|
||||
"ubuntu:12.04",
|
||||
"ubuntu:precise",
|
||||
"ubuntu:latest"
|
||||
],
|
||||
"RepoTags": ["ubuntu:12.04", "ubuntu:precise", "ubuntu:latest"],
|
||||
"Id": "8dbd9e392a964056420e5d58ca5cc376ef18e2de93b5cc90e868a1bbc8318c1c",
|
||||
"Created": 1365714795,
|
||||
"Size": 131506275,
|
||||
"VirtualSize": 131506275
|
||||
"VirtualSize": 131506275,
|
||||
},
|
||||
{
|
||||
"RepoTags": [
|
||||
"ubuntu:12.10",
|
||||
"ubuntu:quantal",
|
||||
"<none>:<none>"
|
||||
],
|
||||
"RepoTags": ["ubuntu:12.10", "ubuntu:quantal", "<none>:<none>"],
|
||||
"ParentId": "27cf784147099545",
|
||||
"Id": "b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc",
|
||||
"Created": 1364102658,
|
||||
"Size": 24653,
|
||||
"VirtualSize": 180116135
|
||||
}
|
||||
"VirtualSize": 180116135,
|
||||
},
|
||||
]
|
||||
|
||||
with asyncio_patch("gns3server.compute.docker.Docker.query", return_value=response) as mock:
|
||||
@@ -196,7 +194,6 @@ async def test_force_pull_image():
|
||||
async def test_pull_image_error():
|
||||
|
||||
class Content:
|
||||
|
||||
def __init__(self):
|
||||
self._chunks = [b'{"error": "image not found"}', b""]
|
||||
|
||||
@@ -217,7 +214,6 @@ async def test_pull_image_error():
|
||||
async def test_pull_image_rejects_incomplete_response():
|
||||
|
||||
class Content:
|
||||
|
||||
def __init__(self):
|
||||
self._read = False
|
||||
|
||||
@@ -241,7 +237,6 @@ async def test_pull_image_rejects_incomplete_response():
|
||||
async def test_pull_image_propagates_timeout():
|
||||
|
||||
class Content:
|
||||
|
||||
async def read(self, size):
|
||||
raise asyncio.TimeoutError
|
||||
|
||||
@@ -259,7 +254,6 @@ async def test_pull_image_propagates_timeout():
|
||||
async def test_load_image():
|
||||
|
||||
class Content:
|
||||
|
||||
def __init__(self):
|
||||
self._chunks = [b'{"stream": "Loaded image ID: sha256:e90e34656806"}', b""]
|
||||
|
||||
@@ -285,7 +279,6 @@ async def test_load_image():
|
||||
async def test_load_image_error():
|
||||
|
||||
class Content:
|
||||
|
||||
def __init__(self):
|
||||
self._chunks = [b'{"error": "invalid tar file"}', b""]
|
||||
|
||||
@@ -307,13 +300,12 @@ async def test_load_image_error():
|
||||
@pytest.mark.asyncio
|
||||
async def test_docker_check_connection_docker_minimum_version(vm):
|
||||
|
||||
response = {
|
||||
'ApiVersion': '1.01',
|
||||
'Version': '1.12'
|
||||
}
|
||||
response = {"ApiVersion": "1.01", "Version": "1.12"}
|
||||
|
||||
with patch("gns3server.compute.docker.Docker.connector"), \
|
||||
asyncio_patch("gns3server.compute.docker.Docker.query", return_value=response):
|
||||
with (
|
||||
patch("gns3server.compute.docker.Docker.connector"),
|
||||
asyncio_patch("gns3server.compute.docker.Docker.query", return_value=response),
|
||||
):
|
||||
vm._connected = False
|
||||
with pytest.raises(DockerError):
|
||||
await vm._check_connection()
|
||||
@@ -323,13 +315,14 @@ async def test_docker_check_connection_docker_minimum_version(vm):
|
||||
async def test_docker_check_connection_docker_preferred_version_against_newer(vm):
|
||||
|
||||
response = {
|
||||
'ApiVersion': '1.52',
|
||||
'Version': '29.0.1',
|
||||
|
||||
"ApiVersion": "1.52",
|
||||
"Version": "29.0.1",
|
||||
}
|
||||
|
||||
with patch("gns3server.compute.docker.Docker.connector"), \
|
||||
asyncio_patch("gns3server.compute.docker.Docker.query", return_value=response):
|
||||
with (
|
||||
patch("gns3server.compute.docker.Docker.connector"),
|
||||
asyncio_patch("gns3server.compute.docker.Docker.query", return_value=response),
|
||||
):
|
||||
vm._connected = False
|
||||
await vm._check_connection()
|
||||
assert vm._api_version == DOCKER_PREFERRED_API_VERSION
|
||||
@@ -338,14 +331,12 @@ async def test_docker_check_connection_docker_preferred_version_against_newer(vm
|
||||
@pytest.mark.asyncio
|
||||
async def test_docker_check_connection_docker_preferred_version_against_older(vm):
|
||||
|
||||
response = {
|
||||
'ApiVersion': '1.43',
|
||||
'Version': '24.0.2',
|
||||
'MinAPIVersion': '1.40'
|
||||
}
|
||||
response = {"ApiVersion": "1.43", "Version": "24.0.2", "MinAPIVersion": "1.40"}
|
||||
|
||||
with patch("gns3server.compute.docker.Docker.connector"), \
|
||||
asyncio_patch("gns3server.compute.docker.Docker.query", return_value=response):
|
||||
with (
|
||||
patch("gns3server.compute.docker.Docker.connector"),
|
||||
asyncio_patch("gns3server.compute.docker.Docker.query", return_value=response),
|
||||
):
|
||||
vm._connected = False
|
||||
await vm._check_connection()
|
||||
assert vm._api_version == DOCKER_MINIMUM_API_VERSION
|
||||
@@ -355,12 +346,14 @@ async def test_docker_check_connection_docker_preferred_version_against_older(vm
|
||||
async def test_docker_check_connection_docker_unsupported_version(vm):
|
||||
|
||||
response = {
|
||||
'ApiVersion': '1.25',
|
||||
'Version': '1.13.1',
|
||||
"ApiVersion": "1.25",
|
||||
"Version": "1.13.1",
|
||||
}
|
||||
|
||||
with patch("gns3server.compute.docker.Docker.connector"), \
|
||||
asyncio_patch("gns3server.compute.docker.Docker.query", return_value=response):
|
||||
with (
|
||||
patch("gns3server.compute.docker.Docker.connector"),
|
||||
asyncio_patch("gns3server.compute.docker.Docker.query", return_value=response),
|
||||
):
|
||||
vm._connected = False
|
||||
with pytest.raises(DockerError) as e:
|
||||
await vm._check_connection()
|
||||
@@ -370,12 +363,14 @@ async def test_docker_check_connection_docker_unsupported_version(vm):
|
||||
async def test_install_busybox():
|
||||
|
||||
mock_process = MagicMock()
|
||||
mock_process.returncode = 1 # means that busybox is not dynamically linked
|
||||
mock_process.returncode = 1 # means that busybox is not dynamically linked
|
||||
mock_process.communicate = AsyncioMagicMock(return_value=(b"", b"not a dynamic executable"))
|
||||
|
||||
with patch("gns3server.compute.docker.os.path.isfile", return_value=False):
|
||||
with patch("gns3server.compute.docker.shutil.which", return_value="/usr/bin/busybox"):
|
||||
with asyncio_patch("gns3server.compute.docker.asyncio.create_subprocess_exec", return_value=mock_process) as create_subprocess_mock:
|
||||
with asyncio_patch(
|
||||
"gns3server.compute.docker.asyncio.create_subprocess_exec", return_value=mock_process
|
||||
) as create_subprocess_mock:
|
||||
with patch("gns3server.compute.docker.shutil.copy2") as copy2_mock:
|
||||
dst_dir = Docker.resources_path()
|
||||
await Docker.install_busybox(dst_dir)
|
||||
@@ -396,12 +391,18 @@ async def test_install_busybox_dynamic_linked():
|
||||
mock_process.communicate = AsyncioMagicMock(return_value=(b"Dynamically linked library", b""))
|
||||
|
||||
with patch("os.path.isfile", return_value=False):
|
||||
with patch("gns3server.compute.docker.shutil.which", side_effect=lambda name: "/usr/bin/busybox" if name == "busybox" else None):
|
||||
with patch(
|
||||
"gns3server.compute.docker.shutil.which",
|
||||
side_effect=lambda name: "/usr/bin/busybox" if name == "busybox" else None,
|
||||
):
|
||||
with asyncio_patch("gns3server.compute.docker.asyncio.create_subprocess_exec", return_value=mock_process):
|
||||
with pytest.raises(DockerError) as e:
|
||||
dst_dir = Docker.resources_path()
|
||||
await Docker.install_busybox(dst_dir)
|
||||
assert str(e.value) == "No busybox executable could be found, please install busybox (apt install busybox-static on Debian/Ubuntu) and make sure it is in your PATH"
|
||||
assert (
|
||||
str(e.value)
|
||||
== "No busybox executable could be found, please install busybox (apt install busybox-static on Debian/Ubuntu) and make sure it is in your PATH"
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@@ -412,7 +413,10 @@ async def test_install_busybox_no_executables():
|
||||
with pytest.raises(DockerError) as e:
|
||||
dst_dir = Docker.resources_path()
|
||||
await Docker.install_busybox(dst_dir)
|
||||
assert str(e.value) == "No busybox executable could be found, please install busybox (apt install busybox-static on Debian/Ubuntu) and make sure it is in your PATH"
|
||||
assert (
|
||||
str(e.value)
|
||||
== "No busybox executable could be found, please install busybox (apt install busybox-static on Debian/Ubuntu) and make sure it is in your PATH"
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@@ -494,4 +498,4 @@ async def test_check_host_readiness_continues_past_unreadable_key(caplog):
|
||||
|
||||
message = " ".join(r.message for r in caplog.records)
|
||||
assert "max_user_instances=128" in message # collected before the gap
|
||||
assert "modprobe fuse" in message # FUSE check still ran
|
||||
assert "modprobe fuse" in message # FUSE check still ran
|
||||
File diff suppressed because it is too large.
Load diff
@@ -72,13 +72,20 @@ async def manager(port_manager):
|
||||
return m
|
||||
|
||||
|
||||
def _make_vm(compute_project, manager, environment="GNS3_IOL_RUNNER=1",
|
||||
extra_volumes=None, adapters=4, console_type="telnet"):
|
||||
def _make_vm(
|
||||
compute_project, manager, environment="GNS3_IOL_RUNNER=1", extra_volumes=None, adapters=4, console_type="telnet"
|
||||
):
|
||||
"""Build an IOLDockerVM with a fake cid (no create() called)."""
|
||||
vm = IOLDockerVM(
|
||||
"iol-xe-1", str(uuid.uuid4()), compute_project, manager, "iol-xe/iol-xe:17-18-02",
|
||||
console_type=console_type, environment=environment,
|
||||
extra_volumes=extra_volumes or [], adapters=adapters,
|
||||
"iol-xe-1",
|
||||
str(uuid.uuid4()),
|
||||
compute_project,
|
||||
manager,
|
||||
"iol-xe/iol-xe:17-18-02",
|
||||
console_type=console_type,
|
||||
environment=environment,
|
||||
extra_volumes=extra_volumes or [],
|
||||
adapters=adapters,
|
||||
)
|
||||
vm._cid = "e90e34656842"
|
||||
# mirrors the controller flow, which always delivers an allocated id
|
||||
@@ -127,40 +134,37 @@ def _wiring_dir(vm):
|
||||
# Factory selection
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_factory_selects_iol_for_env_marker(manager):
|
||||
|
||||
assert manager._select_node_class(console_type="telnet",
|
||||
environment="GNS3_IOL_RUNNER=1") is IOLDockerVM
|
||||
assert manager._select_node_class(console_type="telnet", environment="GNS3_IOL_RUNNER=1") is IOLDockerVM
|
||||
|
||||
|
||||
def test_factory_tolerates_whitespace_and_comma(manager):
|
||||
|
||||
assert manager._select_node_class(console_type="telnet",
|
||||
environment=" GNS3_IOL_RUNNER=1,\nFOO=bar") is IOLDockerVM
|
||||
assert manager._select_node_class(console_type="telnet", environment=" GNS3_IOL_RUNNER=1,\nFOO=bar") is IOLDockerVM
|
||||
|
||||
|
||||
def test_factory_docker_exec_wins_over_iol_marker(manager):
|
||||
|
||||
assert manager._select_node_class(console_type="docker_exec",
|
||||
environment="GNS3_IOL_RUNNER=1") is VendorDockerVM
|
||||
assert manager._select_node_class(console_type="docker_exec", environment="GNS3_IOL_RUNNER=1") is VendorDockerVM
|
||||
|
||||
|
||||
def test_factory_plain_environment_is_base(manager):
|
||||
|
||||
assert manager._select_node_class(console_type="telnet",
|
||||
environment="FOO=bar\nGNS3_BAZ=nope") is DockerVM
|
||||
assert manager._select_node_class(console_type="telnet", environment="FOO=bar\nGNS3_BAZ=nope") is DockerVM
|
||||
|
||||
|
||||
def test_factory_generic_unix_knob_selects_vendor(manager):
|
||||
|
||||
assert manager._select_node_class(console_type="telnet",
|
||||
environment="GNS3_UNIX_SOCKET_NIO=1") is VendorDockerVM
|
||||
assert manager._select_node_class(console_type="telnet", environment="GNS3_UNIX_SOCKET_NIO=1") is VendorDockerVM
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Knob parsing
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_marker_forces_skip_init_and_unix_nio(compute_project, manager):
|
||||
|
||||
vm = _make_vm(compute_project, manager, environment="GNS3_IOL_RUNNER=1")
|
||||
@@ -172,12 +176,10 @@ def test_marker_forces_skip_init_and_unix_nio(compute_project, manager):
|
||||
|
||||
def test_iol_memory_knob(compute_project, manager):
|
||||
|
||||
vm = _make_vm(compute_project, manager,
|
||||
environment="GNS3_IOL_RUNNER=1\nGNS3_IOL_MEMORY=4096")
|
||||
vm = _make_vm(compute_project, manager, environment="GNS3_IOL_RUNNER=1\nGNS3_IOL_MEMORY=4096")
|
||||
assert vm._iol_memory == 4096
|
||||
|
||||
vm = _make_vm(compute_project, manager,
|
||||
environment="GNS3_IOL_RUNNER=1\nGNS3_IOL_MEMORY=notanumber")
|
||||
vm = _make_vm(compute_project, manager, environment="GNS3_IOL_RUNNER=1\nGNS3_IOL_MEMORY=notanumber")
|
||||
assert vm._iol_memory == 2048
|
||||
|
||||
|
||||
@@ -185,15 +187,13 @@ def test_iol_memory_knob(compute_project, manager):
|
||||
# create()
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_create_keeps_image_entrypoint(compute_project, manager):
|
||||
|
||||
with asyncio_patch("gns3server.compute.docker.Docker.list_images",
|
||||
return_value=[{"image": "iol-xe"}]):
|
||||
with asyncio_patch("gns3server.compute.docker.Docker.query",
|
||||
return_value=_create_response()) as mock:
|
||||
with patch("asyncio.subprocess.create_subprocess_exec",
|
||||
return_value=_seed_proc()):
|
||||
with asyncio_patch("gns3server.compute.docker.Docker.list_images", return_value=[{"image": "iol-xe"}]):
|
||||
with asyncio_patch("gns3server.compute.docker.Docker.query", return_value=_create_response()) as mock:
|
||||
with patch("asyncio.subprocess.create_subprocess_exec", return_value=_seed_proc()):
|
||||
vm = _make_vm(compute_project, manager)
|
||||
await vm.create()
|
||||
sent = mock.call_args.kwargs["data"]
|
||||
@@ -205,12 +205,9 @@ async def test_create_keeps_image_entrypoint(compute_project, manager):
|
||||
@pytest.mark.asyncio
|
||||
async def test_create_auto_adds_config_and_tmp_run_volumes(compute_project, manager):
|
||||
|
||||
with asyncio_patch("gns3server.compute.docker.Docker.list_images",
|
||||
return_value=[{"image": "iol-xe"}]):
|
||||
with asyncio_patch("gns3server.compute.docker.Docker.query",
|
||||
return_value=_create_response()) as mock:
|
||||
with patch("asyncio.subprocess.create_subprocess_exec",
|
||||
return_value=_seed_proc()):
|
||||
with asyncio_patch("gns3server.compute.docker.Docker.list_images", return_value=[{"image": "iol-xe"}]):
|
||||
with asyncio_patch("gns3server.compute.docker.Docker.query", return_value=_create_response()) as mock:
|
||||
with patch("asyncio.subprocess.create_subprocess_exec", return_value=_seed_proc()):
|
||||
vm = _make_vm(compute_project, manager, extra_volumes=[])
|
||||
await vm.create()
|
||||
sent = mock.call_args.kwargs["data"]
|
||||
@@ -235,12 +232,9 @@ async def test_create_start_command_becomes_runner_flags(compute_project, manage
|
||||
|
||||
vm = _make_vm(compute_project, manager)
|
||||
vm.start_command = "-keep"
|
||||
with asyncio_patch("gns3server.compute.docker.Docker.list_images",
|
||||
return_value=[{"image": "iol-xe"}]):
|
||||
with asyncio_patch("gns3server.compute.docker.Docker.query",
|
||||
return_value=_create_response()) as mock:
|
||||
with patch("asyncio.subprocess.create_subprocess_exec",
|
||||
return_value=_seed_proc()):
|
||||
with asyncio_patch("gns3server.compute.docker.Docker.list_images", return_value=[{"image": "iol-xe"}]):
|
||||
with asyncio_patch("gns3server.compute.docker.Docker.query", return_value=_create_response()) as mock:
|
||||
with patch("asyncio.subprocess.create_subprocess_exec", return_value=_seed_proc()):
|
||||
await vm.create()
|
||||
sent = mock.call_args.kwargs["data"]
|
||||
# start_command is the container CMD = extra iol-runner flags
|
||||
@@ -251,6 +245,7 @@ async def test_create_start_command_becomes_runner_flags(compute_project, manage
|
||||
# start() — runtime preparation
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_start_writes_iol_config(compute_project, manager):
|
||||
|
||||
@@ -402,6 +397,7 @@ async def test_restart_is_graceful_stop_then_start(compute_project, manager):
|
||||
# Wiring — unix-socket NIO
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_add_ubridge_connection_unix_wiring(compute_project, manager):
|
||||
|
||||
@@ -463,8 +459,7 @@ async def test_add_ubridge_connection_timeout_is_actionable(compute_project, man
|
||||
async def raise_timeout(path, timeout=60):
|
||||
raise asyncio.TimeoutError()
|
||||
|
||||
with patch("gns3server.compute.docker.vendor_docker_vm.wait_for_file_creation",
|
||||
side_effect=raise_timeout):
|
||||
with patch("gns3server.compute.docker.vendor_docker_vm.wait_for_file_creation", side_effect=raise_timeout):
|
||||
with pytest.raises(DockerError) as excinfo:
|
||||
await vm._add_ubridge_connection(None, 0)
|
||||
# the message names the adapter and the exact wiring path
|
||||
@@ -497,6 +492,7 @@ async def test_add_ubridge_connection_without_nio_still_wires(compute_project, m
|
||||
# IOU-style port model — 1 adapter = 4 ethernet ports
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_adapters_are_four_port_units(compute_project, manager):
|
||||
|
||||
vm = _make_vm(compute_project, manager, adapters=2)
|
||||
@@ -523,7 +519,7 @@ async def test_wiring_addresses_ports_within_adapters(compute_project, manager):
|
||||
await vm._add_ubridge_connection(nio, 1, port_number=2)
|
||||
|
||||
flat = "\n".join(str(c) for c in vm._ubridge_hypervisor.method_calls)
|
||||
assert 'bridge add_nio_unix bridge1_2 ' in flat
|
||||
assert "bridge add_nio_unix bridge1_2 " in flat
|
||||
assert f'"{os.path.join(wiring_dir, "c06.sock")}"' in flat
|
||||
assert f'"{os.path.join(wiring_dir, "s06.sock")}"' in flat
|
||||
assert "add_nio_udp bridge1_2 4242 127.0.0.1 4343" in flat
|
||||
@@ -547,33 +543,59 @@ async def test_nio_binding_rejects_port_out_of_range(compute_project, manager):
|
||||
# Generic GNS3_UNIX_SOCKET_NIO knob on plain VendorDockerVM
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_env_unix_socket_nio_parsing(compute_project, manager):
|
||||
|
||||
vm = VendorDockerVM("vendor-1", str(uuid.uuid4()), compute_project, manager, "vendor:latest",
|
||||
console_type="docker_exec",
|
||||
environment="GNS3_SKIP_INIT=1\nGNS3_UNIX_SOCKET_NIO=1\nGNS3_UNIX_SOCKET_DIR=/var/run/socks")
|
||||
vm = VendorDockerVM(
|
||||
"vendor-1",
|
||||
str(uuid.uuid4()),
|
||||
compute_project,
|
||||
manager,
|
||||
"vendor:latest",
|
||||
console_type="docker_exec",
|
||||
environment="GNS3_SKIP_INIT=1\nGNS3_UNIX_SOCKET_NIO=1\nGNS3_UNIX_SOCKET_DIR=/var/run/socks",
|
||||
)
|
||||
assert vm._unix_socket_nio is True
|
||||
assert vm._unix_socket_dir == "/var/run/socks"
|
||||
|
||||
# invalid dirs are rejected, keeping the default
|
||||
vm = VendorDockerVM("vendor-1", str(uuid.uuid4()), compute_project, manager, "vendor:latest",
|
||||
console_type="docker_exec",
|
||||
environment="GNS3_SKIP_INIT=1\nGNS3_UNIX_SOCKET_NIO=yes\nGNS3_UNIX_SOCKET_DIR=../../etc")
|
||||
vm = VendorDockerVM(
|
||||
"vendor-1",
|
||||
str(uuid.uuid4()),
|
||||
compute_project,
|
||||
manager,
|
||||
"vendor:latest",
|
||||
console_type="docker_exec",
|
||||
environment="GNS3_SKIP_INIT=1\nGNS3_UNIX_SOCKET_NIO=yes\nGNS3_UNIX_SOCKET_DIR=../../etc",
|
||||
)
|
||||
assert vm._unix_socket_nio is True
|
||||
assert vm._unix_socket_dir == "/tmp"
|
||||
|
||||
# off by default / explicit off
|
||||
vm = VendorDockerVM("vendor-1", str(uuid.uuid4()), compute_project, manager, "vendor:latest",
|
||||
console_type="docker_exec", environment="GNS3_SKIP_INIT=1")
|
||||
vm = VendorDockerVM(
|
||||
"vendor-1",
|
||||
str(uuid.uuid4()),
|
||||
compute_project,
|
||||
manager,
|
||||
"vendor:latest",
|
||||
console_type="docker_exec",
|
||||
environment="GNS3_SKIP_INIT=1",
|
||||
)
|
||||
assert vm._unix_socket_nio is False
|
||||
|
||||
|
||||
def test_unix_socket_dir_bound_from_runtime_dir(compute_project, manager):
|
||||
|
||||
vm = VendorDockerVM("vendor-1", str(uuid.uuid4()), compute_project, manager, "vendor:latest",
|
||||
console_type="docker_exec",
|
||||
environment="GNS3_SKIP_INIT=1\nGNS3_UNIX_SOCKET_NIO=1",
|
||||
extra_volumes=[])
|
||||
vm = VendorDockerVM(
|
||||
"vendor-1",
|
||||
str(uuid.uuid4()),
|
||||
compute_project,
|
||||
manager,
|
||||
"vendor:latest",
|
||||
console_type="docker_exec",
|
||||
environment="GNS3_SKIP_INIT=1\nGNS3_UNIX_SOCKET_NIO=1",
|
||||
extra_volumes=[],
|
||||
)
|
||||
# the socket directory is an ephemeral per-node directory from the
|
||||
# runtime dir, not a volume: writable by the (unprivileged) agent and
|
||||
# short enough for AF_UNIX
|
||||
@@ -584,10 +606,16 @@ def test_unix_socket_dir_bound_from_runtime_dir(compute_project, manager):
|
||||
assert socket_binds[0]["Source"] == _wiring_dir(vm)
|
||||
|
||||
# a socket dir already covered by a persisted volume gets no extra bind
|
||||
vm = VendorDockerVM("vendor-1", str(uuid.uuid4()), compute_project, manager, "vendor:latest",
|
||||
console_type="docker_exec",
|
||||
environment="GNS3_SKIP_INIT=1\nGNS3_UNIX_SOCKET_NIO=1",
|
||||
extra_volumes=["/tmp"])
|
||||
vm = VendorDockerVM(
|
||||
"vendor-1",
|
||||
str(uuid.uuid4()),
|
||||
compute_project,
|
||||
manager,
|
||||
"vendor:latest",
|
||||
console_type="docker_exec",
|
||||
environment="GNS3_SKIP_INIT=1\nGNS3_UNIX_SOCKET_NIO=1",
|
||||
extra_volumes=["/tmp"],
|
||||
)
|
||||
binds = vm._mount_binds({"Config": {"Volumes": {}}})
|
||||
assert not any(b.get("Source") == _wiring_dir(vm) for b in binds)
|
||||
assert any(b.get("Target") == "/tmp" for b in binds)
|
||||
@@ -596,9 +624,15 @@ def test_unix_socket_dir_bound_from_runtime_dir(compute_project, manager):
|
||||
@pytest.mark.asyncio
|
||||
async def test_generic_unix_socket_dir_honored_in_wiring(compute_project, manager):
|
||||
|
||||
vm = VendorDockerVM("vendor-1", str(uuid.uuid4()), compute_project, manager, "vendor:latest",
|
||||
console_type="docker_exec",
|
||||
environment="GNS3_SKIP_INIT=1\nGNS3_UNIX_SOCKET_NIO=1\nGNS3_UNIX_SOCKET_DIR=/var/run/socks")
|
||||
vm = VendorDockerVM(
|
||||
"vendor-1",
|
||||
str(uuid.uuid4()),
|
||||
compute_project,
|
||||
manager,
|
||||
"vendor:latest",
|
||||
console_type="docker_exec",
|
||||
environment="GNS3_SKIP_INIT=1\nGNS3_UNIX_SOCKET_NIO=1\nGNS3_UNIX_SOCKET_DIR=/var/run/socks",
|
||||
)
|
||||
_mock_wiring(vm)
|
||||
wiring_dir = _wiring_dir(vm)
|
||||
os.makedirs(wiring_dir, exist_ok=True)
|
||||
@@ -659,10 +693,19 @@ async def test_tap_wired_vendor_keeps_carrier_and_monitor(compute_project, manag
|
||||
behavior: the guards only apply to unix-socket NIO.
|
||||
"""
|
||||
|
||||
vm = VendorDockerVM("vendor-1", str(uuid.uuid4()), compute_project, manager, "vendor:latest",
|
||||
console_type="docker_exec", environment="GNS3_SKIP_INIT=1")
|
||||
with patch.object(DockerVM, "_set_adapter_carrier", new=AsyncioMagicMock()) as carrier, \
|
||||
patch.object(DockerVM, "_start_interface_monitor", new=AsyncioMagicMock()) as monitor:
|
||||
vm = VendorDockerVM(
|
||||
"vendor-1",
|
||||
str(uuid.uuid4()),
|
||||
compute_project,
|
||||
manager,
|
||||
"vendor:latest",
|
||||
console_type="docker_exec",
|
||||
environment="GNS3_SKIP_INIT=1",
|
||||
)
|
||||
with (
|
||||
patch.object(DockerVM, "_set_adapter_carrier", new=AsyncioMagicMock()) as carrier,
|
||||
patch.object(DockerVM, "_start_interface_monitor", new=AsyncioMagicMock()) as monitor,
|
||||
):
|
||||
await vm._set_adapter_carrier(0, True)
|
||||
await vm._start_interface_monitor()
|
||||
# the base-class attribute is replaced by a plain mock (no descriptor
|
||||
|
||||
@@ -50,6 +50,7 @@ from gns3server.compute.docker.docker_error import DockerError, DockerHttp404Err
|
||||
# Helpers / fixtures
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def _create_response(vm, entrypoint=None, volumes=None):
|
||||
"""Build the Docker /containers/create response (with image info merged)."""
|
||||
return {
|
||||
@@ -71,13 +72,18 @@ async def manager(port_manager):
|
||||
return m
|
||||
|
||||
|
||||
def _make_vm(compute_project, manager, environment=None, console_type="docker_exec",
|
||||
extra_volumes=None, adapters=4):
|
||||
def _make_vm(compute_project, manager, environment=None, console_type="docker_exec", extra_volumes=None, adapters=4):
|
||||
"""Build a VendorDockerVM with a fake cid (no create() called)."""
|
||||
vm = VendorDockerVM(
|
||||
"srlinux-1", str(uuid.uuid4()), compute_project, manager, "srlinux:latest",
|
||||
console_type=console_type, environment=environment,
|
||||
extra_volumes=extra_volumes or [], adapters=adapters,
|
||||
"srlinux-1",
|
||||
str(uuid.uuid4()),
|
||||
compute_project,
|
||||
manager,
|
||||
"srlinux:latest",
|
||||
console_type=console_type,
|
||||
environment=environment,
|
||||
extra_volumes=extra_volumes or [],
|
||||
adapters=adapters,
|
||||
)
|
||||
vm._cid = "e90e34656842"
|
||||
# Interface monitoring belongs to DockerVM and is covered by its focused
|
||||
@@ -91,6 +97,7 @@ def _make_vm(compute_project, manager, environment=None, console_type="docker_ex
|
||||
# Factory selection
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_factory_selects_vendor_when_docker_exec(manager):
|
||||
|
||||
assert manager._select_node_class(console_type="docker_exec") is VendorDockerVM
|
||||
@@ -123,12 +130,10 @@ async def test_create_node_sets_node_class(manager, compute_project, monkeypatch
|
||||
fake_super_create_node,
|
||||
)
|
||||
|
||||
await manager.create_node("v", compute_project.id, str(uuid.uuid4()),
|
||||
"srlinux:latest", console_type="docker_exec")
|
||||
await manager.create_node("v", compute_project.id, str(uuid.uuid4()), "srlinux:latest", console_type="docker_exec")
|
||||
assert captured["cls"] is VendorDockerVM
|
||||
|
||||
await manager.create_node("v", compute_project.id, str(uuid.uuid4()),
|
||||
"ubuntu:latest", console_type="telnet")
|
||||
await manager.create_node("v", compute_project.id, str(uuid.uuid4()), "ubuntu:latest", console_type="telnet")
|
||||
assert captured["cls"] is DockerVM
|
||||
|
||||
|
||||
@@ -136,6 +141,7 @@ async def test_create_node_sets_node_class(manager, compute_project, monkeypatch
|
||||
# GNS3_* env parsing
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_env_skip_init_true(compute_project, manager):
|
||||
|
||||
vm = _make_vm(compute_project, manager, environment="GNS3_SKIP_INIT=1")
|
||||
@@ -150,24 +156,23 @@ def test_env_skip_init_absent_defaults_true(compute_project, manager):
|
||||
|
||||
def test_env_interface_names(compute_project, manager):
|
||||
|
||||
vm = _make_vm(compute_project, manager,
|
||||
environment="GNS3_INTERFACE_NAMES=mgmt0,e1-1,e1-2,e1-3")
|
||||
vm = _make_vm(compute_project, manager, environment="GNS3_INTERFACE_NAMES=mgmt0,e1-1,e1-2,e1-3")
|
||||
assert vm._interface_names == ["mgmt0", "e1-1", "e1-2", "e1-3"]
|
||||
|
||||
|
||||
def test_env_console_cmd(compute_project, manager):
|
||||
|
||||
vm = _make_vm(compute_project, manager,
|
||||
environment="GNS3_CONSOLE_CMD=/opt/srlinux/bin/sr_cli")
|
||||
vm = _make_vm(compute_project, manager, environment="GNS3_CONSOLE_CMD=/opt/srlinux/bin/sr_cli")
|
||||
assert vm._console_cmd == "/opt/srlinux/bin/sr_cli"
|
||||
|
||||
|
||||
def test_env_multiple_lines(compute_project, manager):
|
||||
|
||||
vm = _make_vm(compute_project, manager,
|
||||
environment=("GNS3_SKIP_INIT=1\n"
|
||||
"GNS3_INTERFACE_NAMES=mgmt0,e1-1\n"
|
||||
"GNS3_CONSOLE_CMD=/opt/srlinux/bin/sr_cli\n"))
|
||||
vm = _make_vm(
|
||||
compute_project,
|
||||
manager,
|
||||
environment=("GNS3_SKIP_INIT=1\nGNS3_INTERFACE_NAMES=mgmt0,e1-1\nGNS3_CONSOLE_CMD=/opt/srlinux/bin/sr_cli\n"),
|
||||
)
|
||||
assert vm._gns3_init is False
|
||||
assert vm._interface_names == ["mgmt0", "e1-1"]
|
||||
assert vm._console_cmd == "/opt/srlinux/bin/sr_cli"
|
||||
@@ -183,18 +188,22 @@ def test_env_console_cmd_default_none(compute_project, manager):
|
||||
# create() — init.sh skip, GNS3_MAX_ETHERNET, /etc/network drop
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_create_skip_init_omits_init_sh(compute_project, manager):
|
||||
|
||||
response = _create_response(None, entrypoint=["/init"])
|
||||
with asyncio_patch("gns3server.compute.docker.Docker.list_images",
|
||||
return_value=[{"image": "srlinux"}]):
|
||||
with asyncio_patch("gns3server.compute.docker.Docker.query",
|
||||
return_value=response) as mock:
|
||||
vm = VendorDockerVM("srlinux-1", str(uuid.uuid4()), compute_project,
|
||||
manager, "srlinux:latest",
|
||||
console_type="docker_exec",
|
||||
environment="GNS3_SKIP_INIT=1")
|
||||
with asyncio_patch("gns3server.compute.docker.Docker.list_images", return_value=[{"image": "srlinux"}]):
|
||||
with asyncio_patch("gns3server.compute.docker.Docker.query", return_value=response) as mock:
|
||||
vm = VendorDockerVM(
|
||||
"srlinux-1",
|
||||
str(uuid.uuid4()),
|
||||
compute_project,
|
||||
manager,
|
||||
"srlinux:latest",
|
||||
console_type="docker_exec",
|
||||
environment="GNS3_SKIP_INIT=1",
|
||||
)
|
||||
await vm.create()
|
||||
# the Entrypoint must NOT contain /gns3/init.sh
|
||||
sent = mock.call_args.kwargs["data"]
|
||||
@@ -206,13 +215,11 @@ async def test_create_skip_init_omits_init_sh(compute_project, manager):
|
||||
async def test_create_without_skip_init_prepends_init_sh(compute_project, manager):
|
||||
|
||||
response = _create_response(None, entrypoint=["/init"])
|
||||
with asyncio_patch("gns3server.compute.docker.Docker.list_images",
|
||||
return_value=[{"image": "srlinux"}]):
|
||||
with asyncio_patch("gns3server.compute.docker.Docker.query",
|
||||
return_value=response) as mock:
|
||||
vm = VendorDockerVM("srlinux-1", str(uuid.uuid4()), compute_project,
|
||||
manager, "srlinux:latest",
|
||||
console_type="docker_exec")
|
||||
with asyncio_patch("gns3server.compute.docker.Docker.list_images", return_value=[{"image": "srlinux"}]):
|
||||
with asyncio_patch("gns3server.compute.docker.Docker.query", return_value=response) as mock:
|
||||
vm = VendorDockerVM(
|
||||
"srlinux-1", str(uuid.uuid4()), compute_project, manager, "srlinux:latest", console_type="docker_exec"
|
||||
)
|
||||
await vm.create()
|
||||
sent = mock.call_args.kwargs["data"]
|
||||
# init.sh IS prepended when not skipping
|
||||
@@ -223,14 +230,18 @@ async def test_create_without_skip_init_prepends_init_sh(compute_project, manage
|
||||
async def test_create_interface_names_sets_max_ethernet(compute_project, manager):
|
||||
|
||||
response = _create_response(None)
|
||||
with asyncio_patch("gns3server.compute.docker.Docker.list_images",
|
||||
return_value=[{"image": "srlinux"}]):
|
||||
with asyncio_patch("gns3server.compute.docker.Docker.query",
|
||||
return_value=response) as mock:
|
||||
vm = VendorDockerVM("srlinux-1", str(uuid.uuid4()), compute_project,
|
||||
manager, "srlinux:latest", adapters=4,
|
||||
console_type="docker_exec",
|
||||
environment="GNS3_SKIP_INIT=1\nGNS3_INTERFACE_NAMES=mgmt0,e1-1,e1-2,e1-3")
|
||||
with asyncio_patch("gns3server.compute.docker.Docker.list_images", return_value=[{"image": "srlinux"}]):
|
||||
with asyncio_patch("gns3server.compute.docker.Docker.query", return_value=response) as mock:
|
||||
vm = VendorDockerVM(
|
||||
"srlinux-1",
|
||||
str(uuid.uuid4()),
|
||||
compute_project,
|
||||
manager,
|
||||
"srlinux:latest",
|
||||
adapters=4,
|
||||
console_type="docker_exec",
|
||||
environment="GNS3_SKIP_INIT=1\nGNS3_INTERFACE_NAMES=mgmt0,e1-1,e1-2,e1-3",
|
||||
)
|
||||
await vm.create()
|
||||
sent = mock.call_args.kwargs["data"]
|
||||
# last interface (adapter index 3) should be e1-3, not eth3
|
||||
@@ -244,17 +255,19 @@ async def test_create_drops_etc_network_for_skip_init(compute_project, manager):
|
||||
seed_proc = MagicMock()
|
||||
seed_proc.communicate = AsyncioMagicMock(return_value=(b"seedcid", b""))
|
||||
seed_proc.returncode = 0
|
||||
with asyncio_patch("gns3server.compute.docker.Docker.list_images",
|
||||
return_value=[{"image": "srlinux"}]):
|
||||
with asyncio_patch("gns3server.compute.docker.Docker.query",
|
||||
return_value=response) as mock:
|
||||
with patch("asyncio.subprocess.create_subprocess_exec",
|
||||
return_value=seed_proc):
|
||||
vm = VendorDockerVM("srlinux-1", str(uuid.uuid4()), compute_project,
|
||||
manager, "srlinux:latest",
|
||||
console_type="docker_exec",
|
||||
environment="GNS3_SKIP_INIT=1",
|
||||
extra_volumes=["/etc/opt/srlinux"])
|
||||
with asyncio_patch("gns3server.compute.docker.Docker.list_images", return_value=[{"image": "srlinux"}]):
|
||||
with asyncio_patch("gns3server.compute.docker.Docker.query", return_value=response) as mock:
|
||||
with patch("asyncio.subprocess.create_subprocess_exec", return_value=seed_proc):
|
||||
vm = VendorDockerVM(
|
||||
"srlinux-1",
|
||||
str(uuid.uuid4()),
|
||||
compute_project,
|
||||
manager,
|
||||
"srlinux:latest",
|
||||
console_type="docker_exec",
|
||||
environment="GNS3_SKIP_INIT=1",
|
||||
extra_volumes=["/etc/opt/srlinux"],
|
||||
)
|
||||
await vm.create()
|
||||
sent = mock.call_args.kwargs["data"]
|
||||
targets = [m["Target"] for m in sent["HostConfig"]["Mounts"]]
|
||||
@@ -277,13 +290,11 @@ async def test_create_drops_etc_network_for_skip_init(compute_project, manager):
|
||||
async def test_create_keeps_etc_network_without_skip_init(compute_project, manager):
|
||||
|
||||
response = _create_response(None)
|
||||
with asyncio_patch("gns3server.compute.docker.Docker.list_images",
|
||||
return_value=[{"image": "srlinux"}]):
|
||||
with asyncio_patch("gns3server.compute.docker.Docker.query",
|
||||
return_value=response) as mock:
|
||||
vm = VendorDockerVM("srlinux-1", str(uuid.uuid4()), compute_project,
|
||||
manager, "srlinux:latest",
|
||||
console_type="docker_exec")
|
||||
with asyncio_patch("gns3server.compute.docker.Docker.list_images", return_value=[{"image": "srlinux"}]):
|
||||
with asyncio_patch("gns3server.compute.docker.Docker.query", return_value=response) as mock:
|
||||
vm = VendorDockerVM(
|
||||
"srlinux-1", str(uuid.uuid4()), compute_project, manager, "srlinux:latest", console_type="docker_exec"
|
||||
)
|
||||
await vm.create()
|
||||
sent = mock.call_args.kwargs["data"]
|
||||
targets = [m["Target"] for m in sent["HostConfig"]["Mounts"]]
|
||||
@@ -294,11 +305,11 @@ async def test_create_keeps_etc_network_without_skip_init(compute_project, manag
|
||||
# Interface renaming (_get_container_ifname / move_to_ns)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_move_to_ns_uses_renamed_interface(compute_project, manager):
|
||||
|
||||
vm = _make_vm(compute_project, manager,
|
||||
environment="GNS3_SKIP_INIT=1\nGNS3_INTERFACE_NAMES=mgmt0,e1-1,e1-2,e1-3")
|
||||
vm = _make_vm(compute_project, manager, environment="GNS3_SKIP_INIT=1\nGNS3_INTERFACE_NAMES=mgmt0,e1-1,e1-2,e1-3")
|
||||
vm._ubridge_hypervisor = MagicMock()
|
||||
vm._namespace = 42
|
||||
nio = manager.create_nio({"type": "nio_udp", "lport": 4242, "rport": 4343, "rhost": "127.0.0.1"})
|
||||
@@ -325,6 +336,7 @@ async def test_move_to_ns_falls_back_to_eth(compute_project, manager):
|
||||
# start() — docker_exec console dispatch + volume bridge + permission fix
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_start_docker_exec_dispatches_console(compute_project, manager):
|
||||
|
||||
@@ -373,6 +385,7 @@ async def test_start_without_skip_init_skips_vendor_passes(compute_project, mana
|
||||
# _fix_permissions — container-side, skips dead containers, targets volume paths
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_fix_permissions_skips_dead_container(compute_project, manager):
|
||||
|
||||
@@ -411,8 +424,7 @@ async def test_fix_permissions_targets_volume_paths(compute_project, manager):
|
||||
proc.stderr = MagicMock()
|
||||
proc.stderr.read = AsyncioMagicMock(return_value=b"")
|
||||
|
||||
with patch("asyncio.subprocess.create_subprocess_exec",
|
||||
return_value=proc) as mock_exec:
|
||||
with patch("asyncio.subprocess.create_subprocess_exec", return_value=proc) as mock_exec:
|
||||
await vm._fix_permissions()
|
||||
# one exec per volume
|
||||
assert mock_exec.call_count == 2
|
||||
@@ -422,13 +434,14 @@ async def test_fix_permissions_targets_volume_paths(compute_project, manager):
|
||||
script = call_obj.args[-1] # last positional arg is the sh -c script
|
||||
assert "/gns3volumes" not in script
|
||||
assert '"/etc/opt/srlinux"' in script or '"/var/log/srlinux"' in script
|
||||
assert 'chown' in script
|
||||
assert "chown" in script
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# _prepare_volumes — host-side seeding (docker create + cp + rm)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def _seed_proc(stdout=b"seedcid\n", returncode=0):
|
||||
proc = MagicMock()
|
||||
proc.communicate = AsyncioMagicMock(return_value=(stdout, b""))
|
||||
@@ -439,12 +452,10 @@ def _seed_proc(stdout=b"seedcid\n", returncode=0):
|
||||
@pytest.mark.asyncio
|
||||
async def test_prepare_volumes_seeds_unmarked_volume(compute_project, manager):
|
||||
|
||||
vm = _make_vm(compute_project, manager, environment="GNS3_SKIP_INIT=1",
|
||||
extra_volumes=["/etc/opt/srlinux"])
|
||||
vm = _make_vm(compute_project, manager, environment="GNS3_SKIP_INIT=1", extra_volumes=["/etc/opt/srlinux"])
|
||||
image_info = {"Config": {"Volumes": {}}}
|
||||
|
||||
with patch("asyncio.subprocess.create_subprocess_exec",
|
||||
return_value=_seed_proc()) as mock_exec:
|
||||
with patch("asyncio.subprocess.create_subprocess_exec", return_value=_seed_proc()) as mock_exec:
|
||||
await vm._prepare_volumes(image_info)
|
||||
# docker create + docker cp + docker rm
|
||||
assert mock_exec.call_count == 3
|
||||
@@ -465,8 +476,7 @@ async def test_prepare_volumes_never_overwrites_marked_volume(compute_project, m
|
||||
"""Regression guard: a volume that ever started (marker present) holds the
|
||||
node's saved configuration — re-seeding would reset it to factory."""
|
||||
|
||||
vm = _make_vm(compute_project, manager, environment="GNS3_SKIP_INIT=1",
|
||||
extra_volumes=["/etc/opt/srlinux"])
|
||||
vm = _make_vm(compute_project, manager, environment="GNS3_SKIP_INIT=1", extra_volumes=["/etc/opt/srlinux"])
|
||||
host_dir = os.path.join(vm.working_dir, "etc", "opt", "srlinux")
|
||||
os.makedirs(host_dir, exist_ok=True)
|
||||
marker = os.path.join(host_dir, ".gns3_perms")
|
||||
@@ -475,8 +485,7 @@ async def test_prepare_volumes_never_overwrites_marked_volume(compute_project, m
|
||||
with open(saved, "w") as f:
|
||||
f.write('{"user": "config"}')
|
||||
|
||||
with patch("asyncio.subprocess.create_subprocess_exec",
|
||||
return_value=_seed_proc()) as mock_exec:
|
||||
with patch("asyncio.subprocess.create_subprocess_exec", return_value=_seed_proc()) as mock_exec:
|
||||
await vm._prepare_volumes({"Config": {"Volumes": {}}})
|
||||
mock_exec.assert_not_called()
|
||||
with open(saved) as f:
|
||||
@@ -488,8 +497,7 @@ async def test_prepare_volumes_tolerates_missing_image_path(compute_project, man
|
||||
"""A volume path the image does not contain (e.g. XRd's /xr-storage-shadow)
|
||||
starts empty — cp fails, the marker is still written, no raise."""
|
||||
|
||||
vm = _make_vm(compute_project, manager, environment="GNS3_SKIP_INIT=1",
|
||||
extra_volumes=["/xr-storage-shadow"])
|
||||
vm = _make_vm(compute_project, manager, environment="GNS3_SKIP_INIT=1", extra_volumes=["/xr-storage-shadow"])
|
||||
calls = {"n": 0}
|
||||
|
||||
def proc_factory(*args, **kwargs):
|
||||
@@ -500,8 +508,7 @@ async def test_prepare_volumes_tolerates_missing_image_path(compute_project, man
|
||||
calls["n"] += 1
|
||||
return proc
|
||||
|
||||
with patch("asyncio.subprocess.create_subprocess_exec",
|
||||
side_effect=proc_factory):
|
||||
with patch("asyncio.subprocess.create_subprocess_exec", side_effect=proc_factory):
|
||||
await vm._prepare_volumes({"Config": {"Volumes": {}}})
|
||||
assert calls["n"] == 3 # rm still ran (finally path)
|
||||
host_dir = os.path.join(vm.working_dir, "xr-storage-shadow")
|
||||
@@ -512,8 +519,7 @@ async def test_prepare_volumes_tolerates_missing_image_path(compute_project, man
|
||||
async def test_prepare_volumes_skips_without_skip_init(compute_project, manager):
|
||||
|
||||
vm = _make_vm(compute_project, manager) # no SKIP_INIT
|
||||
with patch("asyncio.subprocess.create_subprocess_exec",
|
||||
return_value=_seed_proc()) as mock_exec:
|
||||
with patch("asyncio.subprocess.create_subprocess_exec", return_value=_seed_proc()) as mock_exec:
|
||||
await vm._prepare_volumes({"Config": {"Volumes": {"/etc/opt/srlinux": None}}})
|
||||
mock_exec.assert_not_called()
|
||||
|
||||
@@ -523,8 +529,7 @@ async def test_prepare_volumes_raises_when_seed_container_fails(compute_project,
|
||||
"""If `docker create` itself fails, creation must abort loudly instead of
|
||||
binding an empty directory over the NOS's config path."""
|
||||
|
||||
vm = _make_vm(compute_project, manager, environment="GNS3_SKIP_INIT=1",
|
||||
extra_volumes=["/etc/opt/srlinux"])
|
||||
vm = _make_vm(compute_project, manager, environment="GNS3_SKIP_INIT=1", extra_volumes=["/etc/opt/srlinux"])
|
||||
proc = _seed_proc(stdout=b"", returncode=1)
|
||||
|
||||
with patch("asyncio.subprocess.create_subprocess_exec", return_value=proc):
|
||||
@@ -536,6 +541,7 @@ async def test_prepare_volumes_raises_when_seed_container_fails(compute_project,
|
||||
# _cleanup_console_resources
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_cleanup_console_resources_closes_writer(compute_project, manager):
|
||||
|
||||
vm = _make_vm(compute_project, manager)
|
||||
@@ -558,11 +564,15 @@ def test_cleanup_console_resources_no_writer(compute_project, manager):
|
||||
# _LazyExecTelnetServer — upstream aliveness + reconnect/recreate logic
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def _make_lazy_server(compute_project, manager, environment="GNS3_CONSOLE_CMD=/opt/srlinux/bin/sr_cli"):
|
||||
"""Build a _LazyExecTelnetServer with _create_exec mocked out (no docker)."""
|
||||
vm = _make_vm(compute_project, manager, environment=environment)
|
||||
srv = _LazyExecTelnetServer(
|
||||
vm, manager, "e90e34656842", "/opt/srlinux/bin/sr_cli",
|
||||
vm,
|
||||
manager,
|
||||
"e90e34656842",
|
||||
"/opt/srlinux/bin/sr_cli",
|
||||
allow_resize=vm._console_resize,
|
||||
)
|
||||
srv._create_exec = AsyncioMagicMock()
|
||||
@@ -655,7 +665,8 @@ async def test_client_naws_ignored_when_resize_disabled(compute_project, manager
|
||||
geometry (paging CLIs need the tall default for concurrent netmiko)."""
|
||||
|
||||
srv = _make_lazy_server(
|
||||
compute_project, manager,
|
||||
compute_project,
|
||||
manager,
|
||||
environment="GNS3_CONSOLE_RESIZE=0",
|
||||
)
|
||||
assert srv._allow_resize is False
|
||||
@@ -784,6 +795,7 @@ async def test_create_exec_cmd_has_no_while_true(compute_project, manager):
|
||||
# Container termination (graceful stop for vendor NOS)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_terminate_container_graceful_stop(compute_project, manager):
|
||||
"""With graceful=True (explicit user stop) vendor containers are SIGTERMed
|
||||
@@ -797,8 +809,7 @@ async def test_terminate_container_graceful_stop(compute_project, manager):
|
||||
|
||||
await vm._terminate_container(graceful=True)
|
||||
|
||||
manager.http_query.assert_called_once_with(
|
||||
"POST", "containers/e90e34656842/stop", params={"t": 60}, timeout=90)
|
||||
manager.http_query.assert_called_once_with("POST", "containers/e90e34656842/stop", params={"t": 60}, timeout=90)
|
||||
manager.query.assert_not_called() # no kill on the graceful path
|
||||
|
||||
|
||||
@@ -826,8 +837,7 @@ async def test_terminate_container_already_stopped_is_silent(compute_project, ma
|
||||
from gns3server.compute.docker.docker_error import DockerHttp304Error
|
||||
|
||||
vm = _make_vm(compute_project, manager)
|
||||
manager.http_query = AsyncioMagicMock(
|
||||
side_effect=DockerHttp304Error("Docker has returned an error: 304"))
|
||||
manager.http_query = AsyncioMagicMock(side_effect=DockerHttp304Error("Docker has returned an error: 304"))
|
||||
await vm._terminate_container(graceful=True) # must not raise
|
||||
|
||||
|
||||
@@ -840,13 +850,9 @@ async def test_stop_uses_graceful_termination(compute_project, manager):
|
||||
vm = _make_vm(compute_project, manager)
|
||||
with patch.object(DockerVM, "_clean_servers", new=AsyncioMagicMock()):
|
||||
with patch.object(DockerVM, "_stop_ubridge", new=AsyncioMagicMock()):
|
||||
with patch.object(
|
||||
DockerVM, "_get_container_state", new=AsyncioMagicMock(return_value="running")
|
||||
):
|
||||
with patch.object(DockerVM, "_get_container_state", new=AsyncioMagicMock(return_value="running")):
|
||||
vm._permissions_fixed = True
|
||||
with patch.object(
|
||||
VendorDockerVM, "_terminate_container", new=AsyncioMagicMock()
|
||||
) as mock_term:
|
||||
with patch.object(VendorDockerVM, "_terminate_container", new=AsyncioMagicMock()) as mock_term:
|
||||
await vm.stop()
|
||||
mock_term.assert_called_once_with(graceful=False)
|
||||
|
||||
@@ -880,8 +886,7 @@ async def test_terminate_container_uses_env_timeout(compute_project, manager):
|
||||
|
||||
await vm._terminate_container(graceful=True)
|
||||
|
||||
manager.http_query.assert_called_once_with(
|
||||
"POST", "containers/e90e34656842/stop", params={"t": 120}, timeout=150)
|
||||
manager.http_query.assert_called_once_with("POST", "containers/e90e34656842/stop", params={"t": 120}, timeout=150)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@@ -890,13 +895,11 @@ async def test_create_reparse_refreshes_env_knobs(compute_project, manager):
|
||||
not on the next project reload: create() re-parses the vendor knobs."""
|
||||
|
||||
response = _create_response(None, entrypoint=["/init"])
|
||||
vm = _make_vm(compute_project, manager,
|
||||
environment="GNS3_SKIP_INIT=1\nGNS3_STOP_TIMEOUT=120")
|
||||
vm = _make_vm(compute_project, manager, environment="GNS3_SKIP_INIT=1\nGNS3_STOP_TIMEOUT=120")
|
||||
assert vm._gns3_init is False and vm._stop_timeout == 120
|
||||
|
||||
vm._environment = "GNS3_STOP_TIMEOUT=5" # knob removed + value changed
|
||||
with asyncio_patch("gns3server.compute.docker.Docker.list_images",
|
||||
return_value=[{"image": "srlinux"}]):
|
||||
with asyncio_patch("gns3server.compute.docker.Docker.list_images", return_value=[{"image": "srlinux"}]):
|
||||
with asyncio_patch("gns3server.compute.docker.Docker.query", return_value=response):
|
||||
await vm.create()
|
||||
|
||||
|
||||
Reference in new issue
Block a user