Merge remote-tracking branch 'origin/2.2' into 3.0

# Conflicts:
#	Dockerfile
#	dev-requirements.txt
#	gns3server/compute/qemu/qemu_vm.py
#	gns3server/version.py
#	requirements.txt
This commit is contained in:
grossmj 2026-02-20 16:28:53 +08:00
commit c5b9f379fe
No known key found for this signature in database
GPG Key ID: 1E7DD6DBB53FF3D7
3 changed files with 28 additions and 8 deletions

View File

@ -78,8 +78,10 @@ class Docker(BaseManager):
stderr=asyncio.subprocess.DEVNULL
)
stdout, _ = await proc.communicate()
if proc.returncode == 1:
if proc.returncode == 1 or "static" in busybox_exec:
# ldd returns 1 if the file is not a dynamic executable
# on Alpine/musl, ldd returns 0 even for static binaries,
# so also trust binaries named busybox-static or busybox.static
log.info(f"Installing busybox from '{busybox_path}' to '{dst_busybox}'")
shutil.copy2(busybox_path, dst_busybox, follow_symlinks=True)
return

View File

@ -2438,13 +2438,31 @@ class QemuVM(BaseNode):
) # we do not want any user networking back-end if no adapter is connected.
# Each 32 PCI device we need to add a PCI bridge with max 9 bridges
# Reserve 32 devices on root pci_bridge,
# since the number of devices used by templates may differ significantly
# and pci_bridges also consume IDs.
# Move network devices to their own bridge
pci_devices_reserved = 32
# Count PCI devices: 4 default + disks (virtio/sata/nvme/scsi) + network adapters
pci_disk_devices = 0
for drive in ["a", "b", "c", "d"]:
# config disk replaces hdd when enabled
if drive == "d" and self._create_config_disk:
continue
if getattr(self, "_hd{}_disk_image".format(drive)):
interface = getattr(self, "_hd{}_disk_interface".format(drive))
# virtio, sata, nvme, and scsi each consume 1 PCI slot; ide and none don't
if interface not in ("none", "ide"):
pci_disk_devices += 1
# config disk (replaces hdd) also consumes a PCI slot if created
if self._create_config_disk and self.config_disk_image:
interface = self.hdd_disk_interface if self.hdd_disk_interface != "none" else self.hda_disk_interface
if interface not in ("none", "ide"):
pci_disk_devices += 1
pci_devices = 4 + pci_disk_devices + len(self._ethernet_adapters)
pci_bridges = math.floor(pci_devices / 32)
pci_bridges_created = 0
pci_device_id = pci_devices_reserved
if pci_bridges >= 1:
if self._qemu_version and parse_version(self._qemu_version) < parse_version("2.4.0"):
raise QemuError("Qemu version 2.4 or later is required to run this VM with a large number of network adapters")
pci_device_id = 4 + pci_bridges + pci_disk_devices # Bridges and disks consume PCI ports
for adapter_number, adapter in enumerate(self._ethernet_adapters):
mac = int_to_macaddress(macaddress_to_int(self._mac_address) + adapter_number)

View File

@ -261,7 +261,7 @@ 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", return_value="/usr/bin/busybox"):
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()