diff --git a/gns3server/compute/docker/__init__.py b/gns3server/compute/docker/__init__.py index 567d1ef32..82acba4b7 100644 --- a/gns3server/compute/docker/__init__.py +++ b/gns3server/compute/docker/__init__.py @@ -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 diff --git a/gns3server/compute/qemu/qemu_vm.py b/gns3server/compute/qemu/qemu_vm.py index cc666f030..f1e145696 100644 --- a/gns3server/compute/qemu/qemu_vm.py +++ b/gns3server/compute/qemu/qemu_vm.py @@ -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) diff --git a/tests/compute/docker/test_docker.py b/tests/compute/docker/test_docker.py index 917255f78..fd5cf81fd 100644 --- a/tests/compute/docker/test_docker.py +++ b/tests/compute/docker/test_docker.py @@ -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()