mirror of
https://github.com/GNS3/gns3-server.git
synced 2026-10-13 05:55:35 +03:00
Kernel eligibility becomes "both endpoints on one compute, each able to anchor": Docker always (adapters are born as veth pairs), QEMU when that compute's uBridge can create the persistent TAPs its adapters anchor on. The question is asked per compute, because the link needs both ends anchored -- so a mixed or half-upgraded deployment degrades to the relay instead of failing an attach. probe_tap_support() creates and deletes one throwaway persistent TAP through a scratch uBridge, cached by binary identity like the tc report; /capabilities exposes `ubridge_tap` (absent = unknown, which keeps links on the relay). The compute route for a QEMU adapter NIO took UDPNIO only, so a kernel link was rejected with 422: its body and response now accept Union[UDPNIO, BridgeNIO] exactly like Docker's. Stale "Docker-to-Docker" wording in the config, link and MCP descriptions now talks about anchors, and docs/features/qemu-kernel-datapath.md documents the datapath -- verified end-to-end on a real instance: 32/32 kernel-datapath checks and 29/29 on the relay variant.
135 lines
5.1 KiB
Python
135 lines
5.1 KiB
Python
#
|
|
# Copyright (C) 2020 GNS3 Technologies Inc.
|
|
#
|
|
# This program is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
import sys
|
|
|
|
import psutil
|
|
import pytest
|
|
from fastapi import FastAPI, status
|
|
from httpx import AsyncClient
|
|
|
|
from gns3server.utils.path import get_default_project_directory
|
|
from gns3server.version import __version__
|
|
|
|
pytestmark = pytest.mark.asyncio
|
|
|
|
|
|
async def _no_tap_probe():
|
|
"""The tap probe spawns uBridge — stub it out for payload assertions."""
|
|
|
|
return None
|
|
|
|
|
|
class TestCapabilitiesRoutes:
|
|
async def test_get(self, app: FastAPI, compute_client: AsyncClient, windows_platform, monkeypatch) -> None:
|
|
|
|
async def no_probe():
|
|
return None
|
|
|
|
monkeypatch.setattr("gns3server.api.routes.compute.capabilities.probe_tc_capabilities", no_probe)
|
|
monkeypatch.setattr("gns3server.api.routes.compute.capabilities.probe_tap_support", _no_tap_probe)
|
|
response = await compute_client.get(app.url_path_for("compute:get_capabilities"))
|
|
assert response.status_code == status.HTTP_200_OK
|
|
assert response.json() == {
|
|
"node_types": [
|
|
"cloud",
|
|
"ethernet_hub",
|
|
"ethernet_switch",
|
|
"nat",
|
|
"vpcs",
|
|
"virtualbox",
|
|
"dynamips",
|
|
"frame_relay_switch",
|
|
"atm_switch",
|
|
"qemu",
|
|
"vmware",
|
|
"docker",
|
|
"iou",
|
|
],
|
|
"version": __version__,
|
|
"platform": sys.platform,
|
|
"cpus": psutil.cpu_count(logical=True),
|
|
"memory": psutil.virtual_memory().total,
|
|
"disk_size": psutil.disk_usage(get_default_project_directory()).total,
|
|
"ubridge_tc": None,
|
|
"ubridge_tap": None,
|
|
}
|
|
|
|
async def test_get_on_gns3vm(self, app: FastAPI, compute_client: AsyncClient, on_gns3vm, monkeypatch) -> None:
|
|
|
|
async def no_probe():
|
|
return None
|
|
|
|
monkeypatch.setattr("gns3server.api.routes.compute.capabilities.probe_tc_capabilities", no_probe)
|
|
monkeypatch.setattr("gns3server.api.routes.compute.capabilities.probe_tap_support", _no_tap_probe)
|
|
response = await compute_client.get(app.url_path_for("compute:get_capabilities"))
|
|
assert response.status_code == status.HTTP_200_OK
|
|
assert response.json() == {
|
|
"node_types": [
|
|
"cloud",
|
|
"ethernet_hub",
|
|
"ethernet_switch",
|
|
"nat",
|
|
"vpcs",
|
|
"virtualbox",
|
|
"dynamips",
|
|
"frame_relay_switch",
|
|
"atm_switch",
|
|
"qemu",
|
|
"vmware",
|
|
"docker",
|
|
"iou",
|
|
],
|
|
"version": __version__,
|
|
"platform": sys.platform,
|
|
"cpus": psutil.cpu_count(logical=True),
|
|
"memory": psutil.virtual_memory().total,
|
|
"disk_size": psutil.disk_usage(get_default_project_directory()).total,
|
|
"ubridge_tc": None,
|
|
"ubridge_tap": None,
|
|
}
|
|
|
|
async def test_get_reports_ubridge_tc(self, app: FastAPI, compute_client: AsyncClient, monkeypatch) -> None:
|
|
|
|
async def fake_probe():
|
|
return {"netem": "delay,rate", "ebpf": "1", "cbpf": "0", "ebpf_modes": "nth,quota,window,flow"}
|
|
|
|
monkeypatch.setattr("gns3server.api.routes.compute.capabilities.probe_tc_capabilities", fake_probe)
|
|
monkeypatch.setattr("gns3server.api.routes.compute.capabilities.probe_tap_support", _no_tap_probe)
|
|
response = await compute_client.get(app.url_path_for("compute:get_capabilities"))
|
|
assert response.status_code == status.HTTP_200_OK
|
|
assert response.json()["ubridge_tc"] == {
|
|
"netem": ["delay", "rate"],
|
|
"ebpf": True,
|
|
"ebpf_modes": ["nth", "quota", "window", "flow"],
|
|
"cbpf": False,
|
|
}
|
|
|
|
async def test_get_reports_ubridge_tap(self, app: FastAPI, compute_client: AsyncClient, monkeypatch) -> None:
|
|
"""
|
|
The tap capability is what makes a QEMU node kernel-eligible, so it
|
|
travels with the other compute capabilities.
|
|
"""
|
|
|
|
async def fake_tap_probe():
|
|
return True
|
|
|
|
monkeypatch.setattr("gns3server.api.routes.compute.capabilities.probe_tap_support", fake_tap_probe)
|
|
response = await compute_client.get(app.url_path_for("compute:get_capabilities"))
|
|
assert response.status_code == status.HTTP_200_OK
|
|
assert response.json()["ubridge_tap"] is True
|