mirror of
https://github.com/GNS3/gns3-server
synced 2024-11-24 01:08:19 +00:00
commit
11a9451098
2
.github/workflows/testing.yml
vendored
2
.github/workflows/testing.yml
vendored
@ -18,7 +18,7 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
os: ["ubuntu-latest"]
|
||||
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
|
||||
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]
|
||||
#include:
|
||||
# only test with Python 3.10 on Windows
|
||||
# - os: windows-latest
|
||||
|
10
CHANGELOG
10
CHANGELOG
@ -1,5 +1,15 @@
|
||||
# Change Log
|
||||
|
||||
## 2.2.51 07/11/2024
|
||||
|
||||
* Catch error when cannot resize Docker container TTY.
|
||||
* Do not use "ide" if there is a disk image and no interface type has been explicitly configured.
|
||||
* Use @locking when sending uBridge commands. Ref https://github.com/GNS3/gns3-gui/issues/3651
|
||||
* Fix run Docker containers with user namespaces enabled. Fixes #2414
|
||||
* Python 3.13 support
|
||||
* Upgrade dependencies
|
||||
* Fix errors in init.sh. Fixes #2431
|
||||
|
||||
## 2.2.50 21/10/2024
|
||||
|
||||
* Bundle web-ui v2.2.50
|
||||
|
52
gns3server/appliances/nixos.gns3a
Normal file
52
gns3server/appliances/nixos.gns3a
Normal file
@ -0,0 +1,52 @@
|
||||
{
|
||||
"appliance_id": "00714342-14b2-4281-aa20-9043ca8dc26e",
|
||||
"name": "NixOS",
|
||||
"category": "guest",
|
||||
"description": "NixOS QEMU Appliance for images created with nixos-generator. Automatically sets hostname based on vmname.",
|
||||
"vendor_name": "NixOS",
|
||||
"vendor_url": "https://nixos.org/",
|
||||
"vendor_logo_url": "https://avatars.githubusercontent.com/u/487568",
|
||||
"documentation_url": "https://github.com/ob7/gns3-nixos-appliance",
|
||||
"product_name": "NixOS",
|
||||
"product_url": "https://github.com/NixOS/nixpkgs",
|
||||
"registry_version": 4,
|
||||
"status": "experimental",
|
||||
"availability": "free",
|
||||
"maintainer": "ob7dev",
|
||||
"maintainer_email": "dev@ob7.us",
|
||||
"usage": "For custom NixOS images, create qcow2 VM with: nixos-generate -f qcow -c ./server.nix Import it into GNS3 as image. VM name is passed into QEMU guest with Advanced Options field entry: -fw_cfg name=opt/vm_hostname,string=%vm-name%",
|
||||
"symbol": ":/symbols/affinity/circle/gray/template.svg",
|
||||
"first_port_name": "eth0",
|
||||
"port_name_format": "eth{0}",
|
||||
"qemu": {
|
||||
"adapter_type": "e1000",
|
||||
"adapters": 4,
|
||||
"ram": 256,
|
||||
"cpus": 1,
|
||||
"hda_disk_interface": "ide",
|
||||
"arch": "x86_64",
|
||||
"console_type": "telnet",
|
||||
"kvm": "allow",
|
||||
"options": "-fw_cfg name=opt/vm_hostname,string=%vm-name%",
|
||||
"on_close": "power_off"
|
||||
},
|
||||
"images": [
|
||||
{
|
||||
"filename": "nixos-24-11.qcow2",
|
||||
"version": "24.11",
|
||||
"md5sum": "2459f05136836dd430402d75cba0f205",
|
||||
"download_url": "https://github.com/nix-community/nixos-generators",
|
||||
"filesize": 1749483520,
|
||||
"download_url": "https://f.ob7.us/gns3/",
|
||||
"direct_download_url": "http://ob7.us/nixos-24-11.qcow2"
|
||||
}
|
||||
],
|
||||
"versions": [
|
||||
{
|
||||
"name": "24.11",
|
||||
"images": {
|
||||
"hda_disk_image": "nixos-24-11.qcow2"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
@ -406,13 +406,11 @@ class BaseNode:
|
||||
Stops the telnet proxy.
|
||||
"""
|
||||
|
||||
if self._wrapper_telnet_server:
|
||||
if self._wrap_console_writer:
|
||||
self._wrap_console_writer.close()
|
||||
if sys.version_info >= (3, 7, 0):
|
||||
try:
|
||||
await self._wrap_console_writer.wait_closed()
|
||||
except ConnectionResetError:
|
||||
pass
|
||||
await self._wrap_console_writer.wait_closed()
|
||||
self._wrap_console_writer = None
|
||||
if self._wrapper_telnet_server:
|
||||
self._wrapper_telnet_server.close()
|
||||
await self._wrapper_telnet_server.wait_closed()
|
||||
self._wrapper_telnet_server = None
|
||||
@ -662,6 +660,7 @@ class BaseNode:
|
||||
path = shutil.which(path)
|
||||
return path
|
||||
|
||||
@locking
|
||||
async def _ubridge_send(self, command):
|
||||
"""
|
||||
Sends a command to uBridge hypervisor.
|
||||
|
@ -383,8 +383,8 @@ class DockerVM(BaseNode):
|
||||
"CapAdd": ["ALL"],
|
||||
"Privileged": True,
|
||||
"Binds": self._mount_binds(image_infos),
|
||||
"UsernsMode": "host",
|
||||
},
|
||||
"UsernsMode": "host",
|
||||
"Volumes": {},
|
||||
"Env": ["container=docker"], # Systemd compliant: https://github.com/GNS3/gns3-server/issues/573
|
||||
"Cmd": [],
|
||||
@ -710,7 +710,10 @@ class DockerVM(BaseNode):
|
||||
"""
|
||||
|
||||
# resize the container TTY.
|
||||
await self._manager.query("POST", "containers/{}/resize?h={}&w={}".format(self._cid, rows, columns))
|
||||
try:
|
||||
await self._manager.query("POST", "containers/{}/resize?h={}&w={}".format(self._cid, rows, columns))
|
||||
except DockerError as e:
|
||||
log.warning(f"Could not resize the container TTY: {e}")
|
||||
|
||||
async def _start_console(self):
|
||||
"""
|
||||
|
@ -25,7 +25,7 @@ PATH=/gns3/bin:/tmp/gns3/bin:/sbin:$PATH
|
||||
# bootstrap busybox commands
|
||||
if [ ! -d /tmp/gns3/bin ]; then
|
||||
busybox mkdir -p /tmp/gns3/bin
|
||||
for applet in `busybox --list-full`
|
||||
for applet in `busybox --list`
|
||||
do
|
||||
ln -s /gns3/bin/busybox "/tmp/gns3/bin/$applet"
|
||||
done
|
||||
|
@ -1915,11 +1915,6 @@ class QemuVM(BaseNode):
|
||||
continue
|
||||
|
||||
interface = getattr(self, "hd{}_disk_interface".format(drive))
|
||||
# fail-safe: use "ide" if there is a disk image and no interface type has been explicitly configured
|
||||
if interface == "none":
|
||||
interface = "ide"
|
||||
setattr(self, "hd{}_disk_interface".format(drive), interface)
|
||||
|
||||
disk_name = "hd" + drive
|
||||
if not os.path.isfile(disk_image) or not os.path.exists(disk_image):
|
||||
if os.path.islink(disk_image):
|
||||
|
@ -57,7 +57,7 @@ class CrashReport:
|
||||
Report crash to a third party service
|
||||
"""
|
||||
|
||||
DSN = "https://decb06a5d7bda07bef342a41c5c9d6e1@o19455.ingest.us.sentry.io/38482"
|
||||
DSN = "https://088679fcf3aa35f775356982a80fe37c@o19455.ingest.us.sentry.io/38482"
|
||||
_instance = None
|
||||
|
||||
def __init__(self):
|
||||
|
@ -23,8 +23,8 @@
|
||||
# or negative for a release candidate or beta (after the base version
|
||||
# number has been incremented)
|
||||
|
||||
__version__ = "2.2.50"
|
||||
__version_info__ = (2, 2, 50, 0)
|
||||
__version__ = "2.2.51"
|
||||
__version_info__ = (2, 2, 51, 0)
|
||||
|
||||
if "dev" in __version__:
|
||||
try:
|
||||
|
@ -1,13 +1,13 @@
|
||||
jsonschema>=4.23,<4.24
|
||||
aiohttp>=3.10.3,<3.11
|
||||
aiohttp>=3.10.10,<3.11
|
||||
aiohttp-cors>=0.7.0,<0.8
|
||||
aiofiles>=24.1.0,<25.0
|
||||
Jinja2>=3.1.4,<3.2
|
||||
sentry-sdk==2.12,<2.13 # optional dependency
|
||||
psutil==6.0.0
|
||||
sentry-sdk>=2.17,<2.18 # optional dependency
|
||||
psutil>=6.1.0
|
||||
async-timeout>=4.0.3,<4.1
|
||||
distro>=1.9.0
|
||||
py-cpuinfo>=9.0.0,<10.0
|
||||
platformdirs>=2.4.0
|
||||
importlib-resources>=1.3; python_version < '3.9'
|
||||
truststore>=0.9.1; python_version >= '3.10'
|
||||
truststore>=0.10.0; python_version >= '3.10'
|
||||
|
8
setup.py
8
setup.py
@ -16,9 +16,6 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import sys
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
|
||||
from setuptools import setup, find_packages
|
||||
from setuptools.command.test import test as TestCommand
|
||||
@ -67,8 +64,8 @@ setup(
|
||||
include_package_data=True,
|
||||
zip_safe=False,
|
||||
platforms="any",
|
||||
python_requires='>=3.8',
|
||||
setup_requires=["setuptools>=17.1"],
|
||||
python_requires=">=3.8",
|
||||
setup_requires=["setuptools>=61.0"],
|
||||
classifiers=[
|
||||
"Development Status :: 5 - Production/Stable",
|
||||
"Environment :: Console",
|
||||
@ -86,6 +83,7 @@ setup(
|
||||
"Programming Language :: Python :: 3.10",
|
||||
"Programming Language :: Python :: 3.11",
|
||||
"Programming Language :: Python :: 3.12",
|
||||
"Programming Language :: Python :: 3.13",
|
||||
"Programming Language :: Python :: Implementation :: CPython",
|
||||
],
|
||||
)
|
||||
|
@ -107,9 +107,9 @@ async def test_create(compute_project, manager):
|
||||
"{}:/gns3:ro".format(Docker.resources_path()),
|
||||
"{}:/gns3volumes/etc/network".format(os.path.join(vm.working_dir, "etc", "network"))
|
||||
],
|
||||
"Privileged": True
|
||||
"Privileged": True,
|
||||
"UsernsMode": "host"
|
||||
},
|
||||
"UsernsMode": "host",
|
||||
"Volumes": {},
|
||||
"NetworkDisabled": True,
|
||||
"Hostname": "test",
|
||||
@ -146,9 +146,9 @@ async def test_create_with_tag(compute_project, manager):
|
||||
"{}:/gns3:ro".format(Docker.resources_path()),
|
||||
"{}:/gns3volumes/etc/network".format(os.path.join(vm.working_dir, "etc", "network"))
|
||||
],
|
||||
"Privileged": True
|
||||
"Privileged": True,
|
||||
"UsernsMode": "host"
|
||||
},
|
||||
"UsernsMode": "host",
|
||||
"Volumes": {},
|
||||
"NetworkDisabled": True,
|
||||
"Hostname": "test",
|
||||
@ -189,9 +189,9 @@ async def test_create_vnc(compute_project, manager):
|
||||
"{}:/gns3volumes/etc/network".format(os.path.join(vm.working_dir, "etc", "network")),
|
||||
"/tmp/.X11-unix/X{0}:/tmp/.X11-unix/X{0}:ro".format(vm._display)
|
||||
],
|
||||
"Privileged": True
|
||||
"Privileged": True,
|
||||
"UsernsMode": "host"
|
||||
},
|
||||
"UsernsMode": "host",
|
||||
"Volumes": {},
|
||||
"NetworkDisabled": True,
|
||||
"Hostname": "test",
|
||||
@ -319,9 +319,9 @@ async def test_create_start_cmd(compute_project, manager):
|
||||
"{}:/gns3:ro".format(Docker.resources_path()),
|
||||
"{}:/gns3volumes/etc/network".format(os.path.join(vm.working_dir, "etc", "network"))
|
||||
],
|
||||
"Privileged": True
|
||||
"Privileged": True,
|
||||
"UsernsMode": "host"
|
||||
},
|
||||
"UsernsMode": "host",
|
||||
"Volumes": {},
|
||||
"Entrypoint": ["/gns3/init.sh"],
|
||||
"Cmd": ["/bin/ls"],
|
||||
@ -418,9 +418,9 @@ async def test_create_image_not_available(compute_project, manager):
|
||||
"{}:/gns3:ro".format(Docker.resources_path()),
|
||||
"{}:/gns3volumes/etc/network".format(os.path.join(vm.working_dir, "etc", "network"))
|
||||
],
|
||||
"Privileged": True
|
||||
"Privileged": True,
|
||||
"UsernsMode": "host"
|
||||
},
|
||||
"UsernsMode": "host",
|
||||
"Volumes": {},
|
||||
"NetworkDisabled": True,
|
||||
"Hostname": "test",
|
||||
@ -462,9 +462,9 @@ async def test_create_with_user(compute_project, manager):
|
||||
"{}:/gns3:ro".format(Docker.resources_path()),
|
||||
"{}:/gns3volumes/etc/network".format(os.path.join(vm.working_dir, "etc", "network"))
|
||||
],
|
||||
"Privileged": True
|
||||
"Privileged": True,
|
||||
"UsernsMode": "host"
|
||||
},
|
||||
"UsernsMode": "host",
|
||||
"Volumes": {},
|
||||
"NetworkDisabled": True,
|
||||
"Hostname": "test",
|
||||
@ -546,9 +546,9 @@ async def test_create_with_extra_volumes_duplicate_1_image(compute_project, mana
|
||||
"{}:/gns3volumes/etc/network".format(os.path.join(vm.working_dir, "etc", "network")),
|
||||
"{}:/gns3volumes/vol/1".format(os.path.join(vm.working_dir, "vol", "1")),
|
||||
],
|
||||
"Privileged": True
|
||||
"Privileged": True,
|
||||
"UsernsMode": "host"
|
||||
},
|
||||
"UsernsMode": "host",
|
||||
"Volumes": {},
|
||||
"NetworkDisabled": True,
|
||||
"Hostname": "test",
|
||||
@ -586,9 +586,9 @@ async def test_create_with_extra_volumes_duplicate_2_user(compute_project, manag
|
||||
"{}:/gns3volumes/etc/network".format(os.path.join(vm.working_dir, "etc", "network")),
|
||||
"{}:/gns3volumes/vol/1".format(os.path.join(vm.working_dir, "vol", "1")),
|
||||
],
|
||||
"Privileged": True
|
||||
"Privileged": True,
|
||||
"UsernsMode": "host"
|
||||
},
|
||||
"UsernsMode": "host",
|
||||
"Volumes": {},
|
||||
"NetworkDisabled": True,
|
||||
"Hostname": "test",
|
||||
@ -626,9 +626,9 @@ async def test_create_with_extra_volumes_duplicate_3_subdir(compute_project, man
|
||||
"{}:/gns3volumes/etc/network".format(os.path.join(vm.working_dir, "etc", "network")),
|
||||
"{}:/gns3volumes/vol".format(os.path.join(vm.working_dir, "vol")),
|
||||
],
|
||||
"Privileged": True
|
||||
"Privileged": True,
|
||||
"UsernsMode": "host"
|
||||
},
|
||||
"UsernsMode": "host",
|
||||
"Volumes": {},
|
||||
"NetworkDisabled": True,
|
||||
"Hostname": "test",
|
||||
@ -666,9 +666,9 @@ async def test_create_with_extra_volumes_duplicate_4_backslash(compute_project,
|
||||
"{}:/gns3volumes/etc/network".format(os.path.join(vm.working_dir, "etc", "network")),
|
||||
"{}:/gns3volumes/vol".format(os.path.join(vm.working_dir, "vol")),
|
||||
],
|
||||
"Privileged": True
|
||||
"Privileged": True,
|
||||
"UsernsMode": "host"
|
||||
},
|
||||
"UsernsMode": "host",
|
||||
"Volumes": {},
|
||||
"NetworkDisabled": True,
|
||||
"Hostname": "test",
|
||||
@ -705,9 +705,9 @@ async def test_create_with_extra_volumes_duplicate_5_subdir_issue_1595(compute_p
|
||||
"{}:/gns3:ro".format(Docker.resources_path()),
|
||||
"{}:/gns3volumes/etc".format(os.path.join(vm.working_dir, "etc")),
|
||||
],
|
||||
"Privileged": True
|
||||
"Privileged": True,
|
||||
"UsernsMode": "host"
|
||||
},
|
||||
"UsernsMode": "host",
|
||||
"Volumes": {},
|
||||
"NetworkDisabled": True,
|
||||
"Hostname": "test",
|
||||
@ -744,9 +744,9 @@ async def test_create_with_extra_volumes_duplicate_6_subdir_issue_1595(compute_p
|
||||
"{}:/gns3:ro".format(Docker.resources_path()),
|
||||
"{}:/gns3volumes/etc".format(os.path.join(vm.working_dir, "etc")),
|
||||
],
|
||||
"Privileged": True
|
||||
"Privileged": True,
|
||||
"UsernsMode": "host"
|
||||
},
|
||||
"UsernsMode": "host",
|
||||
"Volumes": {},
|
||||
"NetworkDisabled": True,
|
||||
"Hostname": "test",
|
||||
@ -791,9 +791,9 @@ async def test_create_with_extra_volumes(compute_project, manager):
|
||||
"{}:/gns3volumes/vol/1".format(os.path.join(vm.working_dir, "vol", "1")),
|
||||
"{}:/gns3volumes/vol/2".format(os.path.join(vm.working_dir, "vol", "2")),
|
||||
],
|
||||
"Privileged": True
|
||||
"Privileged": True,
|
||||
"UsernsMode": "host"
|
||||
},
|
||||
"UsernsMode": "host",
|
||||
"Volumes": {},
|
||||
"NetworkDisabled": True,
|
||||
"Hostname": "test",
|
||||
@ -1040,9 +1040,9 @@ async def test_update(vm):
|
||||
"{}:/gns3:ro".format(Docker.resources_path()),
|
||||
"{}:/gns3volumes/etc/network".format(os.path.join(vm.working_dir, "etc", "network"))
|
||||
],
|
||||
"Privileged": True
|
||||
"Privileged": True,
|
||||
"UsernsMode": "host"
|
||||
},
|
||||
"UsernsMode": "host",
|
||||
"Volumes": {},
|
||||
"NetworkDisabled": True,
|
||||
"Hostname": "test",
|
||||
@ -1109,9 +1109,9 @@ async def test_update_running(vm):
|
||||
"{}:/gns3:ro".format(Docker.resources_path()),
|
||||
"{}:/gns3volumes/etc/network".format(os.path.join(vm.working_dir, "etc", "network"))
|
||||
],
|
||||
"Privileged": True
|
||||
"Privileged": True,
|
||||
"UsernsMode": "host"
|
||||
},
|
||||
"UsernsMode": "host",
|
||||
"Volumes": {},
|
||||
"NetworkDisabled": True,
|
||||
"Hostname": "test",
|
||||
|
@ -353,16 +353,23 @@ async def test_set_platform(compute_project, manager):
|
||||
async def test_disk_options(vm, tmpdir, fake_qemu_img_binary):
|
||||
|
||||
vm._hda_disk_image = str(tmpdir / "test.qcow2")
|
||||
vm._hda_disk_interface = "ide"
|
||||
vm._hdb_disk_image = str(tmpdir / "test2.qcow2")
|
||||
open(vm._hda_disk_image, "w+").close()
|
||||
open(vm._hdb_disk_image, "w+").close()
|
||||
|
||||
with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM._find_disk_file_format", return_value="qcow2"):
|
||||
with (asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM._find_disk_file_format", return_value="qcow2")):
|
||||
with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()) as process:
|
||||
options = await vm._disk_options()
|
||||
assert process.called
|
||||
args, kwargs = process.call_args
|
||||
assert args == (fake_qemu_img_binary, "create", "-o", "backing_file={}".format(vm._hda_disk_image), "-F", "qcow2", "-f", "qcow2", os.path.join(vm.working_dir, "hda_disk.qcow2"))
|
||||
assert args == (fake_qemu_img_binary, "create", "-o", "backing_file={}".format(vm._hda_disk_image), "-F", "qcow2", "-f", "qcow2", os.path.join(vm.working_dir, "hda_disk.qcow2")) or \
|
||||
args == (fake_qemu_img_binary, "create", "-o", "backing_file={}".format(vm._hdb_disk_image), "-F", "qcow2", "-f", "qcow2", os.path.join(vm.working_dir, "hdb_disk.qcow2"))
|
||||
|
||||
assert options == ['-drive', 'file=' + os.path.join(vm.working_dir, "hda_disk.qcow2") + ',if=ide,index=0,media=disk,id=drive0']
|
||||
assert options == [
|
||||
'-drive', 'file=' + os.path.join(vm.working_dir, "hda_disk.qcow2") + ',if=ide,index=0,media=disk,id=drive0',
|
||||
'-drive', 'file=' + os.path.join(vm.working_dir, "hdb_disk.qcow2") + ',if=none,index=1,media=disk,id=drive1',
|
||||
]
|
||||
|
||||
|
||||
async def test_cdrom_option(vm, tmpdir, fake_qemu_img_binary):
|
||||
@ -440,9 +447,13 @@ async def test_tpm_option(vm, tmpdir, fake_qemu_img_binary):
|
||||
async def test_disk_options_multiple_disk(vm, tmpdir, fake_qemu_img_binary):
|
||||
|
||||
vm._hda_disk_image = str(tmpdir / "test0.qcow2")
|
||||
vm._hda_disk_interface = "ide"
|
||||
vm._hdb_disk_image = str(tmpdir / "test1.qcow2")
|
||||
vm._hdb_disk_interface = "ide"
|
||||
vm._hdc_disk_image = str(tmpdir / "test2.qcow2")
|
||||
vm._hdc_disk_interface = "ide"
|
||||
vm._hdd_disk_image = str(tmpdir / "test3.qcow2")
|
||||
vm._hdd_disk_interface = "ide"
|
||||
open(vm._hda_disk_image, "w+").close()
|
||||
open(vm._hdb_disk_image, "w+").close()
|
||||
open(vm._hdc_disk_image, "w+").close()
|
||||
|
Loading…
Reference in New Issue
Block a user