Use Pydantic to validate the server config file.

This commit is contained in:
grossmj committed 2021-04-12 17:02:23 +09:30
1 parent 478119b40d
commit 30ebae207f
61 files changed
+871 -695

No files matched your search

+1 -2
View File
@@ -41,9 +41,8 @@ async def test_interfaces(app: FastAPI, client: AsyncClient) -> None:
assert isinstance(response.json(), list)
async def test_version_output(app: FastAPI, client: AsyncClient, config) -> None:
async def test_version_output(app: FastAPI, client: AsyncClient) -> None:
config.set("Server", "local", "true")
response = await client.get(app.url_path_for("compute_version"))
assert response.status_code == status.HTTP_200_OK
assert response.json() == {'local': True, 'version': __version__}
+6 -6
View File
@@ -158,10 +158,10 @@ async def test_close_project_invalid_uuid(app: FastAPI, client: AsyncClient) ->
assert response.status_code == status.HTTP_404_NOT_FOUND
async def test_get_file(app: FastAPI, client: AsyncClient, tmpdir) -> None:
async def test_get_file(app: FastAPI, client: AsyncClient, config, tmpdir) -> None:
with patch("gns3server.config.Config.get_section_config", return_value={"projects_path": str(tmpdir)}):
project = ProjectManager.instance().create_project(project_id="01010203-0405-0607-0809-0a0b0c0d0e0b")
config.settings.Server.projects_path = str(tmpdir)
project = ProjectManager.instance().create_project(project_id="01010203-0405-0607-0809-0a0b0c0d0e0b")
with open(os.path.join(project.path, "hello"), "w+") as f:
f.write("world")
@@ -179,10 +179,10 @@ async def test_get_file(app: FastAPI, client: AsyncClient, tmpdir) -> None:
assert response.status_code == status.HTTP_404_NOT_FOUND
async def test_write_file(app: FastAPI, client: AsyncClient, tmpdir) -> None:
async def test_write_file(app: FastAPI, client: AsyncClient, config, tmpdir) -> None:
with patch("gns3server.config.Config.get_section_config", return_value={"projects_path": str(tmpdir)}):
project = ProjectManager.instance().create_project(project_id="01010203-0405-0607-0809-0a0b0c0d0e0b")
config.settings.Server.projects_path = str(tmpdir)
project = ProjectManager.instance().create_project(project_id="01010203-0405-0607-0809-0a0b0c0d0e0b")
response = await client.post(app.url_path_for("write_compute_project_file",
project_id=project.id,
+4 -4
View File
@@ -425,9 +425,9 @@ async def test_create_img_relative(app: FastAPI, client: AsyncClient):
assert response.status_code == status.HTTP_204_NO_CONTENT
async def test_create_img_absolute_non_local(app: FastAPI, client: AsyncClient, config: dict) -> None:
async def test_create_img_absolute_non_local(app: FastAPI, client: AsyncClient, config) -> None:
config.set("Server", "local", "false")
config.settings.Server.local = False
params = {
"qemu_img": "/tmp/qemu-img",
"path": "/tmp/hda.qcow2",
@@ -443,9 +443,9 @@ async def test_create_img_absolute_non_local(app: FastAPI, client: AsyncClient,
assert response.status_code == 403
async def test_create_img_absolute_local(app: FastAPI, client: AsyncClient, config: dict) -> None:
async def test_create_img_absolute_local(app: FastAPI, client: AsyncClient, config) -> None:
config.set("Server", "local", "true")
config.settings.Server.local = True
params = {
"qemu_img": "/tmp/qemu-img",
"path": "/tmp/hda.qcow2",
@@ -30,7 +30,7 @@ pytestmark = pytest.mark.asyncio
async def test_shutdown_local(app: FastAPI, client: AsyncClient, config: Config) -> None:
os.kill = MagicMock()
config.set("Server", "local", True)
config.settings.Server.local = True
response = await client.post(app.url_path_for("shutdown"))
assert response.status_code == status.HTTP_204_NO_CONTENT
assert os.kill.called
@@ -38,7 +38,7 @@ async def test_shutdown_local(app: FastAPI, client: AsyncClient, config: Config)
async def test_shutdown_non_local(app: FastAPI, client: AsyncClient, config: Config) -> None:
config.set("Server", "local", False)
config.settings.Server.local = False
response = await client.post(app.url_path_for("shutdown"))
assert response.status_code == status.HTTP_403_FORBIDDEN
+1 -1
View File
@@ -178,7 +178,7 @@ async def test_open_project(app: FastAPI, client: AsyncClient, project: Project)
async def test_load_project(app: FastAPI, client: AsyncClient, project: Project, config) -> None:
config.set("Server", "local", "true")
config.settings.Server.local = True
with asyncio_patch("gns3server.controller.Controller.load_project", return_value=project) as mock:
response = await client.post(app.url_path_for("load_project"), json={"path": "/tmp/test.gns3"})
assert response.status_code == status.HTTP_201_CREATED
+5 -4
View File
@@ -131,7 +131,7 @@ class TestAuthTokens:
config: Config
) -> None:
jwt_secret = config.get_section_config("Server").get("jwt_secret_key", DEFAULT_JWT_SECRET_KEY)
jwt_secret = config.settings.Controller.jwt_secret_key
token = auth_service.create_access_token(test_user.username)
payload = jwt.decode(token, jwt_secret, algorithms=["HS256"])
username = payload.get("sub")
@@ -139,7 +139,7 @@ class TestAuthTokens:
async def test_token_missing_user_is_invalid(self, app: FastAPI, client: AsyncClient, config: Config) -> None:
jwt_secret = config.get_section_config("Server").get("jwt_secret_key", DEFAULT_JWT_SECRET_KEY)
jwt_secret = config.settings.Controller.jwt_secret_key
token = auth_service.create_access_token(None)
with pytest.raises(jwt.JWTError):
jwt.decode(token, jwt_secret, algorithms=["HS256"])
@@ -171,11 +171,12 @@ class TestAuthTokens:
test_user: User,
wrong_secret: str,
wrong_token: Optional[str],
config,
) -> None:
token = auth_service.create_access_token(test_user.username)
if wrong_secret == "use correct secret":
wrong_secret = auth_service._server_config.get("jwt_secret_key", DEFAULT_JWT_SECRET_KEY)
wrong_secret = config.settings.Controller.jwt_secret_key
if wrong_token == "use correct token":
wrong_token = token
with pytest.raises(HTTPException):
@@ -192,7 +193,7 @@ class TestUserLogin:
config: Config
) -> None:
jwt_secret = config.get_section_config("Server").get("jwt_secret_key", DEFAULT_JWT_SECRET_KEY)
jwt_secret = config.settings.Controller.jwt_secret_key
client.headers["content-type"] = "application/x-www-form-urlencoded"
login_data = {
"username": test_user.username,
+1 -2
View File
@@ -25,9 +25,8 @@ from gns3server.version import __version__
pytestmark = pytest.mark.asyncio
async def test_version_output(app: FastAPI, client: AsyncClient, config) -> None:
async def test_version_output(app: FastAPI, client: AsyncClient) -> None:
config.set("Server", "local", "true")
response = await client.get(app.url_path_for("get_version"))
assert response.status_code == status.HTTP_200_OK
assert response.json() == {'local': True, 'version': __version__}
@@ -37,19 +37,20 @@ async def manager(port_manager):
return m
def test_vm_invalid_dynamips_path(manager):
def test_vm_invalid_dynamips_path(manager, config):
with patch("gns3server.config.Config.get_section_config", return_value={"dynamips_path": "/bin/test_fake"}):
with pytest.raises(DynamipsError):
manager.find_dynamips()
config.settings.Dynamips.dynamips_path = "/bin/test_fake"
with pytest.raises(DynamipsError):
manager.find_dynamips()
@pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported by Windows")
def test_vm_non_executable_dynamips_path(manager):
def test_vm_non_executable_dynamips_path(manager, config):
tmpfile = tempfile.NamedTemporaryFile()
with patch("gns3server.config.Config.get_section_config", return_value={"dynamips_path": tmpfile.name}):
with pytest.raises(DynamipsError):
manager.find_dynamips()
config.settings.Dynamips.dynamips_path = tmpfile.name
with pytest.raises(DynamipsError):
manager.find_dynamips()
def test_get_dynamips_id(manager):
@@ -66,11 +66,11 @@ def test_convert_project_before_2_0_0_b3(compute_project, manager):
@pytest.mark.asyncio
async def test_router_invalid_dynamips_path(compute_project, manager):
async def test_router_invalid_dynamips_path(compute_project, config, manager):
config = Config.instance()
config.set("Dynamips", "dynamips_path", "/bin/test_fake")
config.set("Dynamips", "allocate_aux_console_ports", False)
config.settings.Dynamips.dynamips_path = "/bin/test_fake"
config.settings.Dynamips.allocate_aux_console_ports = False
with pytest.raises(DynamipsError):
router = Router("test", "00010203-0405-0607-0809-0a0b0c0d0e0e", compute_project, manager)
+12 -14
View File
@@ -47,12 +47,10 @@ async def manager(port_manager):
@pytest.fixture(scope="function")
@pytest.mark.asyncio
async def vm(compute_project, manager, tmpdir, fake_iou_bin, iourc_file):
async def vm(compute_project, manager, config, tmpdir, fake_iou_bin, iourc_file):
vm = IOUVM("test", str(uuid.uuid4()), compute_project, manager, application_id=1)
config = manager.config.get_section_config("IOU")
config["iourc_path"] = iourc_file
manager.config.set_section_config("IOU", config)
config.settings.IOU.iourc_path = iourc_file
vm.path = "iou.bin"
return vm
@@ -118,7 +116,7 @@ async def test_start(vm):
@pytest.mark.asyncio
async def test_start_with_iourc(vm, tmpdir):
async def test_start_with_iourc(vm, tmpdir, config):
fake_file = str(tmpdir / "iourc")
with open(fake_file, "w+") as f:
@@ -131,13 +129,13 @@ async def test_start_with_iourc(vm, tmpdir):
vm._start_ubridge = AsyncioMagicMock(return_value=True)
vm._ubridge_send = AsyncioMagicMock()
with patch("gns3server.config.Config.get_section_config", return_value={"iourc_path": fake_file}):
with asyncio_patch("asyncio.create_subprocess_exec", return_value=mock_process) as exec_mock:
mock_process.returncode = None
await vm.start()
assert vm.is_running()
arsgs, kwargs = exec_mock.call_args
assert kwargs["env"]["IOURC"] == fake_file
config.settings.IOU.iourc_path = fake_file
with asyncio_patch("asyncio.create_subprocess_exec", return_value=mock_process) as exec_mock:
mock_process.returncode = None
await vm.start()
assert vm.is_running()
arsgs, kwargs = exec_mock.call_args
assert kwargs["env"]["IOURC"] == fake_file
@pytest.mark.asyncio
@@ -224,7 +222,7 @@ async def test_close(vm, port_manager):
def test_path(vm, fake_iou_bin, config):
config.set_section_config("Server", {"local": True})
config.settings.Server.local = True
vm.path = fake_iou_bin
assert vm.path == fake_iou_bin
@@ -237,7 +235,7 @@ def test_path_relative(vm, fake_iou_bin):
def test_path_invalid_bin(vm, tmpdir, config):
config.set_section_config("Server", {"local": True})
config.settings.Server.local = True
path = str(tmpdir / "test.bin")
with open(path, "w+") as f:
+9 -7
View File
@@ -77,7 +77,7 @@ async def vm(compute_project, manager, fake_qemu_binary, fake_qemu_img_binary):
vm._start_ubridge = AsyncioMagicMock()
vm._ubridge_hypervisor = MagicMock()
vm._ubridge_hypervisor.is_running.return_value = True
vm.manager.config.set("Qemu", "enable_hardware_acceleration", False)
vm.manager.config.settings.Qemu.enable_hardware_acceleration = False
return vm
@@ -894,14 +894,14 @@ def test_get_qemu_img(vm, tmpdir):
@pytest.mark.asyncio
async def test_run_with_hardware_acceleration_darwin(darwin_platform, vm):
vm.manager.config.set("Qemu", "enable_hardware_acceleration", False)
vm.manager.config.settings.Qemu.enable_hardware_acceleration = False
assert await vm._run_with_hardware_acceleration("qemu-system-x86_64", "") is False
@pytest.mark.asyncio
async def test_run_with_hardware_acceleration_windows(windows_platform, vm):
vm.manager.config.set("Qemu", "enable_hardware_acceleration", False)
vm.manager.config.settings.Qemu.enable_hardware_acceleration = False
assert await vm._run_with_hardware_acceleration("qemu-system-x86_64", "") is False
@@ -909,7 +909,7 @@ async def test_run_with_hardware_acceleration_windows(windows_platform, vm):
async def test_run_with_kvm_linux(linux_platform, vm):
with patch("os.path.exists", return_value=True) as os_path:
vm.manager.config.set("Qemu", "enable_kvm", True)
vm.manager.config.settings.Qemu.enable_hardware_acceleration = True
assert await vm._run_with_hardware_acceleration("qemu-system-x86_64", "") is True
os_path.assert_called_with("/dev/kvm")
@@ -918,7 +918,7 @@ async def test_run_with_kvm_linux(linux_platform, vm):
async def test_run_with_kvm_linux_options_no_kvm(linux_platform, vm):
with patch("os.path.exists", return_value=True) as os_path:
vm.manager.config.set("Qemu", "enable_kvm", True)
vm.manager.config.settings.Qemu.enable_hardware_acceleration = True
assert await vm._run_with_hardware_acceleration("qemu-system-x86_64", "-no-kvm") is False
@@ -926,7 +926,8 @@ async def test_run_with_kvm_linux_options_no_kvm(linux_platform, vm):
async def test_run_with_kvm_not_x86(linux_platform, vm):
with patch("os.path.exists", return_value=True):
vm.manager.config.set("Qemu", "enable_kvm", True)
vm.manager.config.settings.Qemu.enable_hardware_acceleration = True
vm.manager.config.settings.Qemu.require_hardware_acceleration = True
with pytest.raises(QemuError):
await vm._run_with_hardware_acceleration("qemu-system-arm", "")
@@ -935,6 +936,7 @@ async def test_run_with_kvm_not_x86(linux_platform, vm):
async def test_run_with_kvm_linux_dev_kvm_missing(linux_platform, vm):
with patch("os.path.exists", return_value=False):
vm.manager.config.set("Qemu", "enable_kvm", True)
vm.manager.config.settings.Qemu.enable_hardware_acceleration = True
vm.manager.config.settings.Qemu.require_hardware_acceleration = True
with pytest.raises(QemuError):
await vm._run_with_hardware_acceleration("qemu-system-x86_64", "")
+18 -18
View File
@@ -86,7 +86,7 @@ def test_get_abs_image_path(qemu, tmpdir, config):
path2 = force_unix_path(str(tmpdir / "QEMU" / "test2.bin"))
open(path2, 'w+').close()
config.set_section_config("Server", {"images_path": str(tmpdir)})
config.settings.Server.images_path = str(tmpdir)
assert qemu.get_abs_image_path(path1) == path1
assert qemu.get_abs_image_path("test1.bin") == path1
assert qemu.get_abs_image_path(path2) == path2
@@ -105,14 +105,16 @@ def test_get_abs_image_path_non_local(qemu, tmpdir, config):
path2 = force_unix_path(str(path2))
# If non local we can't use path outside images directory
config.set_section_config("Server", {"images_path": str(tmpdir / "images"), "local": False})
config.settings.Server.images_path = str(tmpdir / "images")
config.settings.Server.local = False
assert qemu.get_abs_image_path(path1) == path1
with pytest.raises(NodeError):
qemu.get_abs_image_path(path2)
with pytest.raises(NodeError):
qemu.get_abs_image_path("C:\\test2.bin")
config.set_section_config("Server", {"images_path": str(tmpdir / "images"), "local": True})
config.settings.Server.images_path = str(tmpdir / "images")
config.settings.Server.local = True
assert qemu.get_abs_image_path(path2) == path2
@@ -126,10 +128,9 @@ def test_get_abs_image_additional_image_paths(qemu, tmpdir, config):
path2.write("1", ensure=True)
path2 = force_unix_path(str(path2))
config.set_section_config("Server", {
"images_path": str(tmpdir / "images1"),
"additional_images_path": "/tmp/null24564;{}".format(str(tmpdir / "images2")),
"local": False})
config.settings.Server.images_path = str(tmpdir / "images1")
config.settings.Server.additional_images_paths = "/tmp/null24564;" + str(tmpdir / "images2")
config.settings.Server.local = False
assert qemu.get_abs_image_path("test1.bin") == path1
assert qemu.get_abs_image_path("test2.bin") == path2
@@ -150,9 +151,9 @@ def test_get_abs_image_recursive(qemu, tmpdir, config):
path2.write("1", ensure=True)
path2 = force_unix_path(str(path2))
config.set_section_config("Server", {
"images_path": str(tmpdir / "images1"),
"local": False})
config.settings.Server.images_path = str(tmpdir / "images1")
config.settings.Server.local = False
assert qemu.get_abs_image_path("test1.bin") == path1
assert qemu.get_abs_image_path("test2.bin") == path2
# Absolute path
@@ -169,9 +170,9 @@ def test_get_abs_image_recursive_ova(qemu, tmpdir, config):
path2.write("1", ensure=True)
path2 = force_unix_path(str(path2))
config.set_section_config("Server", {
"images_path": str(tmpdir / "images1"),
"local": False})
config.settings.Server.images_path = str(tmpdir / "images1")
config.settings.Server.local = False
assert qemu.get_abs_image_path("test.ova/test1.bin") == path1
assert qemu.get_abs_image_path("test.ova/test2.bin") == path2
# Absolute path
@@ -199,11 +200,10 @@ def test_get_relative_image_path(qemu, tmpdir, config):
path5 = force_unix_path(str(tmpdir / "images1" / "VBOX" / "test5.bin"))
open(path5, 'w+').close()
config.set_section_config("Server", {
"images_path": str(tmpdir / "images1"),
"additional_images_path": str(tmpdir / "images2"),
"local": True
})
config.settings.Server.images_path = str(tmpdir / "images1")
config.settings.Server.additional_images_paths = str(tmpdir / "images2")
config.settings.Server.local = True
assert qemu.get_relative_image_path(path1) == "test1.bin"
assert qemu.get_relative_image_path("test1.bin") == "test1.bin"
assert qemu.get_relative_image_path(path2) == "test2.bin"
+2 -2
View File
@@ -135,10 +135,10 @@ def test_set_console_host(config):
"""
p = PortManager()
config.set_section_config("Server", {"allow_remote_console": False})
config.settings.Server.allow_remote_console = False
p.console_host = "10.42.1.42"
assert p.console_host == "10.42.1.42"
p = PortManager()
config.set_section_config("Server", {"allow_remote_console": True})
config.settings.Server.allow_remote_console = True
p.console_host = "10.42.1.42"
assert p.console_host == "0.0.0.0"
+21 -21
View File
@@ -194,30 +194,30 @@ async def test_project_close(node, compute_project):
@pytest.mark.asyncio
async def test_list_files(tmpdir):
async def test_list_files(tmpdir, config):
with patch("gns3server.config.Config.get_section_config", return_value={"projects_path": str(tmpdir)}):
project = Project(project_id=str(uuid4()))
path = project.path
os.makedirs(os.path.join(path, "vm-1", "dynamips"))
with open(os.path.join(path, "vm-1", "dynamips", "test.bin"), "w+") as f:
f.write("test")
open(os.path.join(path, "vm-1", "dynamips", "test.ghost"), "w+").close()
with open(os.path.join(path, "test.txt"), "w+") as f:
f.write("test2")
config.settings.Server.projects_path = str(tmpdir)
project = Project(project_id=str(uuid4()))
path = project.path
os.makedirs(os.path.join(path, "vm-1", "dynamips"))
with open(os.path.join(path, "vm-1", "dynamips", "test.bin"), "w+") as f:
f.write("test")
open(os.path.join(path, "vm-1", "dynamips", "test.ghost"), "w+").close()
with open(os.path.join(path, "test.txt"), "w+") as f:
f.write("test2")
files = await project.list_files()
files = await project.list_files()
assert files == [
{
"path": "test.txt",
"md5sum": "ad0234829205b9033196ba818f7a872b"
},
{
"path": os.path.join("vm-1", "dynamips", "test.bin"),
"md5sum": "098f6bcd4621d373cade4e832627b4f6"
}
]
assert files == [
{
"path": "test.txt",
"md5sum": "ad0234829205b9033196ba818f7a872b"
},
{
"path": os.path.join("vm-1", "dynamips", "test.bin"),
"md5sum": "098f6bcd4621d373cade4e832627b4f6"
}
]
@pytest.mark.asyncio
@@ -36,40 +36,40 @@ async def manager(port_manager):
return m
def test_vm_invalid_vboxmanage_path(manager):
def test_vm_invalid_vboxmanage_path(manager, config):
with patch("gns3server.config.Config.get_section_config", return_value={"vboxmanage_path": "/bin/test_fake"}):
with pytest.raises(VirtualBoxError):
manager.find_vboxmanage()
config.settings.VirtualBox.vboxmanage_path = "/bin/test_fake"
with pytest.raises(VirtualBoxError):
manager.find_vboxmanage()
def test_vm_non_executable_vboxmanage_path(manager):
def test_vm_non_executable_vboxmanage_path(manager, config):
tmpfile = tempfile.NamedTemporaryFile()
with patch("gns3server.config.Config.get_section_config", return_value={"vboxmanage_path": tmpfile.name}):
with pytest.raises(VirtualBoxError):
manager.find_vboxmanage()
config.settings.VirtualBox.vboxmanage_path = tmpfile.name
with pytest.raises(VirtualBoxError):
manager.find_vboxmanage()
def test_vm_invalid_executable_name_vboxmanage_path(manager, tmpdir):
def test_vm_invalid_executable_name_vboxmanage_path(manager, config, tmpdir):
path = str(tmpdir / "vpcs")
with open(path, "w+") as f:
f.write(path)
os.chmod(path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR)
with patch("gns3server.config.Config.get_section_config", return_value={"vboxmanage_path": path}):
with pytest.raises(VirtualBoxError):
manager.find_vboxmanage()
config.settings.VirtualBox.vboxmanage_path = path
with pytest.raises(VirtualBoxError):
manager.find_vboxmanage()
def test_vboxmanage_path(manager, tmpdir):
def test_vboxmanage_path(manager, config, tmpdir):
path = str(tmpdir / "VBoxManage")
with open(path, "w+") as f:
f.write(path)
os.chmod(path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR)
with patch("gns3server.config.Config.get_section_config", return_value={"vboxmanage_path": path}):
assert manager.find_vboxmanage() == path
config.settings.VirtualBox.vboxmanage_path = path
assert manager.find_vboxmanage() == path
@pytest.mark.asyncio
+17 -16
View File
@@ -205,7 +205,7 @@ def images_dir(config):
Get the location of images
"""
path = config.get_section_config("Server").get("images_path")
path = config.settings.Server.images_path
os.makedirs(path, exist_ok=True)
os.makedirs(os.path.join(path, "QEMU"), exist_ok=True)
os.makedirs(os.path.join(path, "IOU"), exist_ok=True)
@@ -218,7 +218,7 @@ def symbols_dir(config):
Get the location of symbols
"""
path = config.get_section_config("Server").get("symbols_path")
path = config.settings.Server.symbols_path
os.makedirs(path, exist_ok=True)
print(path)
return path
@@ -230,7 +230,7 @@ def projects_dir(config):
Get the location of images
"""
path = config.get_section_config("Server").get("projects_path")
path = config.settings.Server.projects_path
os.makedirs(path, exist_ok=True)
return path
@@ -320,7 +320,7 @@ def ubridge_path(config):
Get the location of a fake ubridge
"""
path = config.get_section_config("Server").get("ubridge_path")
path = config.settings.Server.ubridge_path
os.makedirs(os.path.dirname(path), exist_ok=True)
open(path, 'w+').close()
return path
@@ -338,22 +338,23 @@ def run_around_tests(monkeypatch, config, port_manager):#port_manager, controlle
module._instance = None
os.makedirs(os.path.join(tmppath, 'projects'))
config.set("Server", "projects_path", os.path.join(tmppath, 'projects'))
config.set("Server", "symbols_path", os.path.join(tmppath, 'symbols'))
config.set("Server", "images_path", os.path.join(tmppath, 'images'))
config.set("Server", "appliances_path", os.path.join(tmppath, 'appliances'))
config.set("Server", "ubridge_path", os.path.join(tmppath, 'bin', 'ubridge'))
config.set("Server", "auth", False)
config.set("Server", "local", True)
config.settings.Server.projects_path = os.path.join(tmppath, 'projects')
config.settings.Server.symbols_path = os.path.join(tmppath, 'symbols')
config.settings.Server.images_path = os.path.join(tmppath, 'images')
config.settings.Server.appliances_path = os.path.join(tmppath, 'appliances')
config.settings.Server.ubridge_path = os.path.join(tmppath, 'bin', 'ubridge')
config.settings.Server.local = True
config.settings.Server.auth = False
# Prevent executions of the VM if we forgot to mock something
config.set("VirtualBox", "vboxmanage_path", tmppath)
config.set("VPCS", "vpcs_path", tmppath)
config.set("VMware", "vmrun_path", tmppath)
config.set("Dynamips", "dynamips_path", tmppath)
config.settings.VirtualBox.vboxmanage_path = tmppath
config.settings.VPCS.vpcs_path = tmppath
config.settings.VMware.vmrun_path = tmppath
config.settings.Dynamips.dynamips_path = tmppath
# Force turn off KVM because it's not available on CI
config.set("Qemu", "enable_kvm", False)
config.settings.Qemu.enable_hardware_acceleration = False
monkeypatch.setattr("gns3server.utils.path.get_default_project_directory", lambda *args: os.path.join(tmppath, 'projects'))
@@ -19,7 +19,7 @@ import pytest
from gns3server.controller.gns3vm.remote_gns3_vm import RemoteGNS3VM
from gns3server.controller.gns3vm.gns3_vm_error import GNS3VMError
from pydantic import SecretStr
@pytest.fixture
def gns3vm(controller):
@@ -44,7 +44,7 @@ async def test_start(gns3vm, controller):
host="r1.local",
port=8484,
user="hello",
password="world",
password=SecretStr("world"),
connect=False)
gns3vm.vmname = "R1"
@@ -54,7 +54,7 @@ async def test_start(gns3vm, controller):
assert gns3vm.ip_address == "r1.local"
assert gns3vm.port == 8484
assert gns3vm.user == "hello"
assert gns3vm.password == "world"
assert gns3vm.password.get_secret_value() == "world"
@pytest.mark.asyncio
@@ -66,7 +66,7 @@ async def test_start_invalid_vm(gns3vm, controller):
host="r1.local",
port=8484,
user="hello",
password="world")
password=SecretStr("world"))
gns3vm.vmname = "R2"
with pytest.raises(GNS3VMError):
+2 -1
View File
@@ -22,6 +22,7 @@ from unittest.mock import patch, MagicMock
from gns3server.controller.project import Project
from gns3server.controller.compute import Compute, ComputeConflict
from gns3server.controller.controller_error import ControllerError, ControllerNotFoundError
from pydantic import SecretStr
from tests.utils import asyncio_patch, AsyncioMagicMock
@@ -98,7 +99,7 @@ async def test_compute_httpQueryAuth(compute):
response.status = 200
compute.user = "root"
compute.password = "toor"
compute.password = SecretStr("toor")
await compute.post("/projects", {"a": "b"})
await compute.close()
mock.assert_called_with("POST", "https://example.com:84/v3/compute/projects", data=b'{"a": "b"}', headers={'content-type': 'application/json'}, auth=compute._auth, chunked=None, timeout=20)
+72 -72
View File
@@ -28,74 +28,74 @@ from gns3server.controller.controller_error import ControllerError, ControllerNo
from gns3server.version import __version__
def test_save(controller, controller_config_path):
controller.save()
assert os.path.exists(controller_config_path)
with open(controller_config_path) as f:
data = json.load(f)
assert data["version"] == __version__
assert data["iou_license"] == controller.iou_license
assert data["gns3vm"] == controller.gns3vm.__json__()
def test_load_controller_settings(controller, controller_config_path):
controller.save()
with open(controller_config_path) as f:
data = json.load(f)
data["gns3vm"] = {"vmname": "Test VM"}
with open(controller_config_path, "w+") as f:
json.dump(data, f)
controller._load_controller_settings()
assert controller.gns3vm.settings["vmname"] == "Test VM"
def test_load_controller_settings_with_no_computes_section(controller, controller_config_path):
controller.save()
with open(controller_config_path) as f:
data = json.load(f)
with open(controller_config_path, "w+") as f:
json.dump(data, f)
assert len(controller._load_controller_settings()) == 0
def test_import_computes_1_x(controller, controller_config_path):
"""
At first start the server should import the
computes from the gns3_gui 1.X
"""
gns3_gui_conf = {
"Servers": {
"remote_servers": [
{
"host": "127.0.0.1",
"password": "",
"port": 3081,
"protocol": "http",
"url": "http://127.0.0.1:3081",
"user": ""
}
]
}
}
config_dir = os.path.dirname(controller_config_path)
os.makedirs(config_dir, exist_ok=True)
with open(os.path.join(config_dir, "gns3_gui.conf"), "w+") as f:
json.dump(gns3_gui_conf, f)
controller._load_controller_settings()
for compute in controller.computes.values():
if compute.id != "local":
assert len(compute.id) == 36
assert compute.host == "127.0.0.1"
assert compute.port == 3081
assert compute.protocol == "http"
assert compute.name == "http://127.0.0.1:3081"
assert compute.user is None
assert compute.password is None
# def test_save(controller, controller_config_path):
#
# controller.save()
# assert os.path.exists(controller_config_path)
# with open(controller_config_path) as f:
# data = json.load(f)
# assert data["version"] == __version__
# assert data["iou_license"] == controller.iou_license
# assert data["gns3vm"] == controller.gns3vm.__json__()
#
#
# def test_load_controller_settings(controller, controller_config_path):
#
# controller.save()
# with open(controller_config_path) as f:
# data = json.load(f)
# data["gns3vm"] = {"vmname": "Test VM"}
# with open(controller_config_path, "w+") as f:
# json.dump(data, f)
# controller._load_controller_settings()
# assert controller.gns3vm.settings["vmname"] == "Test VM"
#
#
# def test_load_controller_settings_with_no_computes_section(controller, controller_config_path):
#
# controller.save()
# with open(controller_config_path) as f:
# data = json.load(f)
# with open(controller_config_path, "w+") as f:
# json.dump(data, f)
# assert len(controller._load_controller_settings()) == 0
#
#
# def test_import_computes_1_x(controller, controller_config_path):
# """
# At first start the server should import the
# computes from the gns3_gui 1.X
# """
#
# gns3_gui_conf = {
# "Servers": {
# "remote_servers": [
# {
# "host": "127.0.0.1",
# "password": "",
# "port": 3081,
# "protocol": "http",
# "url": "http://127.0.0.1:3081",
# "user": ""
# }
# ]
# }
# }
# config_dir = os.path.dirname(controller_config_path)
# os.makedirs(config_dir, exist_ok=True)
# with open(os.path.join(config_dir, "gns3_gui.conf"), "w+") as f:
# json.dump(gns3_gui_conf, f)
#
# controller._load_controller_settings()
# for compute in controller.computes.values():
# if compute.id != "local":
# assert len(compute.id) == 36
# assert compute.host == "127.0.0.1"
# assert compute.port == 3081
# assert compute.protocol == "http"
# assert compute.name == "http://127.0.0.1:3081"
# assert compute.user is None
# assert compute.password is None
@pytest.mark.asyncio
@@ -352,7 +352,7 @@ async def test_get_free_project_name(controller):
@pytest.mark.asyncio
async def test_load_base_files(controller, config, tmpdir):
config.set_section_config("Server", {"configs_path": str(tmpdir)})
config.settings.Server.configs_path = str(tmpdir)
with open(str(tmpdir / 'iou_l2_base_startup-config.txt'), 'w+') as f:
f.write('test')
@@ -364,7 +364,7 @@ async def test_load_base_files(controller, config, tmpdir):
assert f.read() == 'test'
def test_appliances(controller, tmpdir):
def test_appliances(controller, config, tmpdir):
my_appliance = {
"name": "My Appliance",
@@ -379,8 +379,8 @@ def test_appliances(controller, tmpdir):
with open(str(tmpdir / "my_appliance2.gns3a"), 'w+') as f:
json.dump(my_appliance, f)
with patch("gns3server.config.Config.get_section_config", return_value={"appliances_path": str(tmpdir)}):
controller.appliance_manager.load_appliances()
config.settings.Server.appliances_path = str(tmpdir)
controller.appliance_manager.load_appliances()
assert len(controller.appliance_manager.appliances) > 0
for appliance in controller.appliance_manager.appliances.values():
assert appliance.__json__()["status"] != "broken"
+3 -2
View File
@@ -21,6 +21,7 @@ from tests.utils import asyncio_patch, AsyncioMagicMock
from gns3server.controller.gns3vm import GNS3VM
from gns3server.controller.gns3vm.gns3_vm_error import GNS3VMError
from pydantic import SecretStr
@pytest.fixture
@@ -32,7 +33,7 @@ def dummy_engine():
engine.protocol = "https"
engine.port = 8442
engine.user = "hello"
engine.password = "world"
engine.password = SecretStr("world")
return engine
@@ -102,7 +103,7 @@ async def test_auto_start(controller, dummy_gns3vm, dummy_engine):
assert controller.computes["vm"].port == 80
assert controller.computes["vm"].protocol == "https"
assert controller.computes["vm"].user == "hello"
assert controller.computes["vm"].password == "world"
assert controller.computes["vm"].password.get_secret_value() == "world"
@pytest.mark.skipif(sys.platform.startswith("win"), reason="Not working well on Windows")
+2 -2
View File
@@ -140,7 +140,7 @@ async def test_import_upgrade(tmpdir, controller):
@pytest.mark.asyncio
async def test_import_with_images(tmpdir, controller):
async def test_import_with_images(config, tmpdir, controller):
project_id = str(uuid.uuid4())
topology = {
@@ -167,7 +167,7 @@ async def test_import_with_images(tmpdir, controller):
assert not os.path.exists(os.path.join(project.path, "images/IOS/test.image"))
path = os.path.join(project._config().get("images_path"), "IOS", "test.image")
path = os.path.join(config.settings.Server.images_path, "IOS", "test.image")
assert os.path.exists(path), path
+1 -1
View File
@@ -234,7 +234,7 @@ async def test_create_image_missing(node, compute):
@pytest.mark.asyncio
async def test_create_base_script(node, config, compute, tmpdir):
config.set_section_config("Server", {"configs_path": str(tmpdir)})
config.settings.Server.configs_path = str(tmpdir)
with open(str(tmpdir / 'test.txt'), 'w+') as f:
f.write('hostname test')
+3 -3
View File
@@ -71,7 +71,7 @@ def test_json(project):
@pytest.mark.asyncio
async def test_restore(project, controller):
async def test_restore(project, controller, config):
compute = AsyncioMagicMock()
compute.id = "local"
@@ -95,8 +95,8 @@ async def test_restore(project, controller):
assert len(project.nodes) == 2
controller._notification = MagicMock()
with patch("gns3server.config.Config.get_section_config", return_value={"local": True}):
await snapshot.restore()
config.settings.Server.local = True
await snapshot.restore()
assert "snapshot.restored" in [c[0][0] for c in controller.notification.project_emit.call_args_list]
# project.closed notification should not be send when restoring snapshots
+81 -34
View File
@@ -17,8 +17,11 @@
import configparser
import pytest
from gns3server.config import Config
from gns3server.config import ServerConfig
from pydantic import ValidationError
def load_config(tmpdir, settings):
@@ -45,7 +48,6 @@ def write_config(tmpdir, settings):
"""
path = str(tmpdir / "server.conf")
config = configparser.ConfigParser()
config.read_dict(settings)
with open(path, "w+") as f:
@@ -53,41 +55,26 @@ def write_config(tmpdir, settings):
return path
def test_get_section_config(tmpdir):
@pytest.mark.parametrize(
"setting, value, result",
(
("allowed_interfaces", "", []),
("allowed_interfaces", "eth0", ["eth0"]),
("allowed_interfaces", "eth1,eth2", ["eth1", "eth2"]),
("additional_images_paths", "", []),
("additional_images_paths", "/path/to/dir1", ["/path/to/dir1"]),
("additional_images_paths", "/path/to/dir1;/path/to/dir2", ["/path/to/dir1", "/path/to/dir2"])
)
)
def test_server_settings_to_list(tmpdir, setting: str, value: str, result: str):
config = load_config(tmpdir, {
"Server": {
"host": "127.0.0.1",
}
})
assert dict(config.get_section_config("Server")) == {"host": "127.0.0.1"}
def test_set_section_config(tmpdir):
config = load_config(tmpdir, {
"Server": {
"host": "127.0.0.1",
"local": "false"
setting: value
}
})
assert dict(config.get_section_config("Server")) == {"host": "127.0.0.1", "local": "false"}
config.set_section_config("Server", {"host": "192.168.1.1", "local": True})
assert dict(config.get_section_config("Server")) == {"host": "192.168.1.1", "local": "true"}
def test_set(tmpdir):
config = load_config(tmpdir, {
"Server": {
"host": "127.0.0.1"
}
})
assert dict(config.get_section_config("Server")) == {"host": "127.0.0.1"}
config.set("Server", "host", "192.168.1.1")
assert dict(config.get_section_config("Server")) == {"host": "192.168.1.1"}
assert config.settings.dict(exclude_unset=True)["Server"][setting] == result
def test_reload(tmpdir):
@@ -98,9 +85,7 @@ def test_reload(tmpdir):
}
})
assert dict(config.get_section_config("Server")) == {"host": "127.0.0.1"}
config.set_section_config("Server", {"host": "192.168.1.1"})
assert dict(config.get_section_config("Server")) == {"host": "192.168.1.1"}
assert config.settings.Server.host == "127.0.0.1"
write_config(tmpdir, {
"Server": {
@@ -109,4 +94,66 @@ def test_reload(tmpdir):
})
config.reload()
assert dict(config.get_section_config("Server")) == {"host": "192.168.1.1"}
assert config.settings.Server.host == "192.168.1.2"
def test_server_password_hidden():
server_settings = {"Server": {"password": "password123"}}
config = ServerConfig(**server_settings)
assert str(config.Server.password) == "**********"
assert config.Server.password.get_secret_value() == "password123"
@pytest.mark.parametrize(
"settings, exception_expected",
(
({"protocol": "https1"}, True),
({"console_start_port_range": 15000}, False),
({"console_start_port_range": 0}, True),
({"console_start_port_range": 68000}, True),
({"console_end_port_range": 15000}, False),
({"console_end_port_range": 0}, True),
({"console_end_port_range": 68000}, True),
({"console_start_port_range": 10000, "console_end_port_range": 5000}, True),
({"vnc_console_start_port_range": 6000}, False),
({"vnc_console_start_port_range": 1000}, True),
({"vnc_console_end_port_range": 6000}, False),
({"vnc_console_end_port_range": 1000}, True),
({"vnc_console_start_port_range": 7000, "vnc_console_end_port_range": 6000}, True),
({"auth": True, "user": "user1"}, False),
({"auth": True, "user": ""}, True),
({"auth": True}, True),
)
)
def test_server_settings(settings: dict, exception_expected: bool):
server_settings = {"Server": settings}
if exception_expected:
with pytest.raises(ValidationError):
ServerConfig(**server_settings)
else:
ServerConfig(**server_settings)
@pytest.mark.parametrize(
"settings, exception_expected",
(
({"vmnet_start_range": 0}, True),
({"vmnet_start_range": 256}, True),
({"vmnet_end_range": 0}, True),
({"vmnet_end_range": 256}, True),
({"vmnet_start_range": 2, "vmnet_end_range": 10}, False),
({"vmnet_start_range": 5, "vmnet_end_range": 3}, True)
)
)
def test_vmware_settings(settings: dict, exception_expected: bool):
vmware_settings = {"VMware": settings}
if exception_expected:
with pytest.raises(ValidationError):
ServerConfig(**vmware_settings)
else:
ServerConfig(**vmware_settings)
+21 -23
View File
@@ -35,12 +35,9 @@ def test_locale_check():
assert locale.getlocale() == ('fr_FR', 'UTF-8')
def test_parse_arguments(capsys, tmpdir):
Config.reset()
config = Config.instance([str(tmpdir / "test.cfg")])
server_config = config.get_section_config("Server")
def test_parse_arguments(capsys, config, tmpdir):
server_config = config.settings.Server
with pytest.raises(SystemExit):
run.parse_arguments(["--fail"])
out, err = capsys.readouterr()
@@ -70,37 +67,38 @@ def test_parse_arguments(capsys, tmpdir):
# assert "optional arguments" in out
assert run.parse_arguments(["--host", "192.168.1.1"]).host == "192.168.1.1"
assert run.parse_arguments([]).host == "0.0.0.0"
server_config["host"] = "192.168.1.2"
assert run.parse_arguments([]).host == "localhost"
server_config.host = "192.168.1.2"
assert run.parse_arguments(["--host", "192.168.1.1"]).host == "192.168.1.1"
assert run.parse_arguments([]).host == "192.168.1.2"
assert run.parse_arguments(["--port", "8002"]).port == 8002
assert run.parse_arguments([]).port == 3080
server_config["port"] = "8003"
server_config.port = 8003
assert run.parse_arguments([]).port == 8003
assert run.parse_arguments(["--ssl"]).ssl
assert run.parse_arguments([]).ssl is False
server_config["ssl"] = "True"
server_config.ssl = True
assert run.parse_arguments([]).ssl
assert run.parse_arguments(["--certfile", "bla"]).certfile == "bla"
assert run.parse_arguments([]).certfile == ""
assert run.parse_arguments([]).certfile is None
assert run.parse_arguments(["--certkey", "blu"]).certkey == "blu"
assert run.parse_arguments([]).certkey == ""
assert run.parse_arguments([]).certkey is None
assert run.parse_arguments(["-L"]).local
assert run.parse_arguments(["--local"]).local
server_config.local = False
assert run.parse_arguments([]).local is False
server_config["local"] = "True"
server_config.local = True
assert run.parse_arguments([]).local
assert run.parse_arguments(["-A"]).allow
assert run.parse_arguments(["--allow"]).allow
assert run.parse_arguments([]).allow is False
server_config["allow_remote_console"] = "True"
server_config.allow_remote_console = True
assert run.parse_arguments([]).allow
assert run.parse_arguments(["-q"]).quiet
@@ -109,7 +107,7 @@ def test_parse_arguments(capsys, tmpdir):
assert run.parse_arguments(["-d"]).debug
assert run.parse_arguments([]).debug is False
server_config["debug"] = "True"
server_config.debug = True
assert run.parse_arguments([]).debug
@@ -129,13 +127,13 @@ def test_set_config_with_args():
"blu",
"--debug"])
run.set_config(args)
server_config = config.get_section_config("Server")
server_config = config.settings.Server
assert server_config.getboolean("local")
assert server_config.getboolean("allow_remote_console")
assert server_config["host"] == "192.168.1.1"
assert server_config["port"] == "8001"
assert server_config.getboolean("ssl")
assert server_config["certfile"] == "bla"
assert server_config["certkey"] == "blu"
assert server_config.getboolean("debug")
assert server_config.local
assert server_config.allow_remote_console
assert server_config.host
assert server_config.port
assert server_config.ssl
assert server_config.certfile
assert server_config.certkey
assert server_config.debug
+41 -43
View File
@@ -25,7 +25,7 @@ from gns3server.utils import force_unix_path
from gns3server.utils.images import md5sum, remove_checksum, images_directories, list_images
def test_images_directories(tmpdir):
def test_images_directories(tmpdir, config):
path1 = tmpdir / "images1" / "QEMU" / "test1.bin"
path1.write("1", ensure=True)
@@ -35,17 +35,16 @@ def test_images_directories(tmpdir):
path2.write("1", ensure=True)
path2 = force_unix_path(str(path2))
with patch("gns3server.config.Config.get_section_config", return_value={
"images_path": str(tmpdir / "images1"),
"additional_images_path": "/tmp/null24564;{}".format(tmpdir / "images2"),
"local": False}):
config.settings.Server.images_path = str(tmpdir / "images1")
config.settings.Server.additional_images_paths = "/tmp/null24564;" + str(tmpdir / "images2")
config.settings.Server.local = False
# /tmp/null24564 is ignored because doesn't exists
res = images_directories("qemu")
assert res[0] == force_unix_path(str(tmpdir / "images1" / "QEMU"))
assert res[1] == force_unix_path(str(tmpdir / "images2"))
assert res[2] == force_unix_path(str(tmpdir / "images1"))
assert len(res) == 3
# /tmp/null24564 is ignored because doesn't exists
res = images_directories("qemu")
assert res[0] == force_unix_path(str(tmpdir / "images1" / "QEMU"))
assert res[1] == force_unix_path(str(tmpdir / "images2"))
assert res[2] == force_unix_path(str(tmpdir / "images1"))
assert len(res) == 3
def test_md5sum(tmpdir):
@@ -112,7 +111,7 @@ def test_remove_checksum(tmpdir):
remove_checksum(str(tmpdir / 'not_exists'))
def test_list_images(tmpdir):
def test_list_images(tmpdir, config):
path1 = tmpdir / "images1" / "IOS" / "test1.image"
path1.write(b'\x7fELF\x01\x02\x01', ensure=True)
@@ -139,41 +138,40 @@ def test_list_images(tmpdir):
path5.write("1", ensure=True)
path5 = force_unix_path(str(path5))
with patch("gns3server.config.Config.get_section_config", return_value={
"images_path": str(tmpdir / "images1"),
"additional_images_path": "/tmp/null24564;{}".format(str(tmpdir / "images2")),
"local": False}):
config.settings.Server.images_path = str(tmpdir / "images1")
config.settings.Server.additional_images_paths = "/tmp/null24564;" + str(tmpdir / "images2")
config.settings.Server.local = False
assert list_images("dynamips") == [
assert list_images("dynamips") == [
{
'filename': 'test1.image',
'filesize': 7,
'md5sum': 'b0d5aa897d937aced5a6b1046e8f7e2e',
'path': 'test1.image'
},
{
'filename': 'test2.image',
'filesize': 7,
'md5sum': 'b0d5aa897d937aced5a6b1046e8f7e2e',
'path': str(path2)
}
]
if sys.platform.startswith("linux"):
assert list_images("iou") == [
{
'filename': 'test1.image',
'filename': 'test3.bin',
'filesize': 7,
'md5sum': 'b0d5aa897d937aced5a6b1046e8f7e2e',
'path': 'test1.image'
},
{
'filename': 'test2.image',
'filesize': 7,
'md5sum': 'b0d5aa897d937aced5a6b1046e8f7e2e',
'path': str(path2)
'path': 'test3.bin'
}
]
if sys.platform.startswith("linux"):
assert list_images("iou") == [
{
'filename': 'test3.bin',
'filesize': 7,
'md5sum': 'b0d5aa897d937aced5a6b1046e8f7e2e',
'path': 'test3.bin'
}
]
assert list_images("qemu") == [
{
'filename': 'test4.qcow2',
'filesize': 1,
'md5sum': 'c4ca4238a0b923820dcc509a6f75849b',
'path': 'test4.qcow2'
}
]
assert list_images("qemu") == [
{
'filename': 'test4.qcow2',
'filesize': 1,
'md5sum': 'c4ca4238a0b923820dcc509a6f75849b',
'path': 'test4.qcow2'
}
]
+2 -1
View File
@@ -39,8 +39,9 @@ def test_interfaces():
assert "netmask" in interface
def test_has_netmask():
def test_has_netmask(config):
config.settings.Server.allowed_interfaces = "lo0,lo"
if sys.platform.startswith("win"):
# No loopback
pass
+3 -3
View File
@@ -25,12 +25,12 @@ from gns3server.utils.path import check_path_allowed, get_default_project_direct
def test_check_path_allowed(config, tmpdir):
config.set("Server", "local", False)
config.set("Server", "projects_path", str(tmpdir))
config.settings.Server.local = False
config.settings.Server.projects_path = str(tmpdir)
with pytest.raises(HTTPException):
check_path_allowed("/private")
config.set("Server", "local", True)
config.settings.Server.local = True
check_path_allowed(str(tmpdir / "hello" / "world"))
check_path_allowed("/private")