Skip to content

Commit 53eac77

Browse files
authored
Merge pull request #2528 from tobiasstein/fix-pci-bridges
Fix adding pci_bridges to qemu vms
2 parents d9dcc27 + 2b96e54 commit 53eac77

File tree

2 files changed

+39
-59
lines changed

2 files changed

+39
-59
lines changed

gns3server/compute/qemu/qemu_vm.py

+8-9
Original file line numberDiff line numberDiff line change
@@ -2445,16 +2445,13 @@ async def _network_options(self):
24452445
) # we do not want any user networking back-end if no adapter is connected.
24462446

24472447
# Each 32 PCI device we need to add a PCI bridge with max 9 bridges
2448-
pci_devices = 4 + len(self._ethernet_adapters) # 4 PCI devices are use by default by qemu
2449-
pci_bridges = math.floor(pci_devices / 32)
2448+
# Reserve 32 devices on root pci_bridge,
2449+
# since the number of devices used by templates may differ significantly
2450+
# and pci_bridges also consume IDs.
2451+
# Move network devices to their own bridge
2452+
pci_devices_reserved = 32
24502453
pci_bridges_created = 0
2451-
if pci_bridges >= 1:
2452-
if self._qemu_version and parse_version(self._qemu_version) < parse_version("2.4.0"):
2453-
raise QemuError(
2454-
"Qemu version 2.4 or later is required to run this VM with a large number of network adapters"
2455-
)
2456-
2457-
pci_device_id = 4 + pci_bridges # Bridge consume PCI ports
2454+
pci_device_id = pci_devices_reserved
24582455
for adapter_number, adapter in enumerate(self._ethernet_adapters):
24592456
mac = int_to_macaddress(macaddress_to_int(self._mac_address) + adapter_number)
24602457

@@ -2645,6 +2642,8 @@ async def _build_command(self):
26452642
"""
26462643

26472644
self._qemu_version = await self.manager.get_qemu_version(self.qemu_path)
2645+
if self._qemu_version and parse_version(self._qemu_version) < parse_version("2.4.0"):
2646+
raise QemuError("Qemu version 2.4 or later is required to run Qemu VMs")
26482647
vm_name = self._name.replace(",", ",,")
26492648
project_path = self.project.path.replace(",", ",,")
26502649
additional_options = self._options.strip()

tests/compute/qemu/test_qemu_vm.py

+31-50
Original file line numberDiff line numberDiff line change
@@ -570,7 +570,11 @@ async def test_build_command(vm, fake_qemu_binary):
570570
"-net",
571571
"none",
572572
"-device",
573-
"e1000,mac={},netdev=gns3-0".format(vm._mac_address),
573+
"i82801b11-bridge,id=dmi_pci_bridge1",
574+
"-device",
575+
"pci-bridge,id=pci-bridge1,bus=dmi_pci_bridge1,chassis_nr=0x1,addr=0x1,shpc=off",
576+
"-device",
577+
"e1000,mac={},bus=pci-bridge1,addr=0x00,netdev=gns3-0".format(vm._mac_address),
574578
"-netdev",
575579
"socket,id=gns3-0,udp=127.0.0.1:{},localaddr=127.0.0.1:{}".format(nio.rport, nio.lport),
576580
"-display",
@@ -593,40 +597,16 @@ async def test_build_command_manual_uuid(vm):
593597

594598

595599
@pytest.mark.asyncio
596-
async def test_build_command_kvm(linux_platform, vm, fake_qemu_binary):
600+
async def test_build_command_kvm_below_2_4(linux_platform, vm, fake_qemu_binary):
597601
"""
598602
Qemu 2.4 introduce an issue with KVM
599603
"""
600604

601605
vm.manager.get_qemu_version = AsyncioMagicMock(return_value="2.3.2")
602-
os.environ["DISPLAY"] = "0:0"
603606
with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM._run_with_hardware_acceleration", return_value=True):
604-
with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()) as process:
605-
cmd = await vm._build_command()
606-
nio = vm._local_udp_tunnels[0][0]
607-
assert cmd == [
608-
fake_qemu_binary,
609-
"-name",
610-
"test",
611-
"-m",
612-
"256M",
613-
"-smp",
614-
"cpus=1,maxcpus=1,sockets=1",
615-
"-enable-kvm",
616-
"-boot",
617-
"order=c",
618-
"-uuid",
619-
vm.id,
620-
"-serial",
621-
"telnet:127.0.0.1:{},server,nowait".format(vm._internal_console_port),
622-
"-net",
623-
"none",
624-
"-device",
625-
"e1000,mac={},netdev=gns3-0".format(vm._mac_address),
626-
"-netdev",
627-
"socket,id=gns3-0,udp=127.0.0.1:{},localaddr=127.0.0.1:{}".format(nio.rport, nio.lport),
628-
"-nographic"
629-
]
607+
with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()):
608+
with pytest.raises(QemuError):
609+
await vm._build_command()
630610

631611

632612
@pytest.mark.asyncio
@@ -661,7 +641,11 @@ async def test_build_command_kvm_2_4(linux_platform, vm, fake_qemu_binary):
661641
"-net",
662642
"none",
663643
"-device",
664-
"e1000,mac={},netdev=gns3-0".format(vm._mac_address),
644+
"i82801b11-bridge,id=dmi_pci_bridge1",
645+
"-device",
646+
"pci-bridge,id=pci-bridge1,bus=dmi_pci_bridge1,chassis_nr=0x1,addr=0x1,shpc=off",
647+
"-device",
648+
"e1000,mac={},bus=pci-bridge1,addr=0x00,netdev=gns3-0".format(vm._mac_address),
665649
"-netdev",
666650
"socket,id=gns3-0,udp=127.0.0.1:{},localaddr=127.0.0.1:{}".format(nio.rport, nio.lport),
667651
"-nographic"
@@ -705,11 +689,15 @@ async def test_build_command_two_adapters(vm, fake_qemu_binary):
705689
"-net",
706690
"none",
707691
"-device",
708-
"e1000,mac={},netdev=gns3-0".format(vm._mac_address),
692+
"i82801b11-bridge,id=dmi_pci_bridge1",
693+
"-device",
694+
"pci-bridge,id=pci-bridge1,bus=dmi_pci_bridge1,chassis_nr=0x1,addr=0x1,shpc=off",
695+
"-device",
696+
"e1000,mac={},bus=pci-bridge1,addr=0x00,netdev=gns3-0".format(vm._mac_address),
709697
"-netdev",
710698
"socket,id=gns3-0,udp=127.0.0.1:{},localaddr=127.0.0.1:{}".format(nio1.rport, nio1.lport),
711699
"-device",
712-
"e1000,mac={},netdev=gns3-1".format(int_to_macaddress(macaddress_to_int(vm._mac_address) + 1)),
700+
"e1000,mac={},bus=pci-bridge1,addr=0x01,netdev=gns3-1".format(int_to_macaddress(macaddress_to_int(vm._mac_address) + 1)),
713701
"-netdev",
714702
"socket,id=gns3-1,udp=127.0.0.1:{},localaddr=127.0.0.1:{}".format(nio2.rport, nio2.lport),
715703
"-nographic"
@@ -729,8 +717,8 @@ async def test_build_command_two_adapters_mac_address(vm):
729717
assert mac_0[:8] == "00:00:ab"
730718
with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()):
731719
cmd = await vm._build_command()
732-
assert "e1000,mac={},netdev=gns3-0".format(mac_0) in cmd
733-
assert "e1000,mac={},netdev=gns3-1".format(mac_1) in cmd
720+
assert "e1000,mac={},bus=pci-bridge1,addr=0x00,netdev=gns3-0".format(mac_0) in cmd
721+
assert "e1000,mac={},bus=pci-bridge1,addr=0x01,netdev=gns3-1".format(mac_1) in cmd
734722

735723
vm.mac_address = "00:42:ab:0e:0f:0a"
736724
mac_0 = vm._mac_address
@@ -739,8 +727,8 @@ async def test_build_command_two_adapters_mac_address(vm):
739727
with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()):
740728

741729
cmd = await vm._build_command()
742-
assert "e1000,mac={},netdev=gns3-0".format(mac_0) in cmd
743-
assert "e1000,mac={},netdev=gns3-1".format(mac_1) in cmd
730+
assert "e1000,mac={},bus=pci-bridge1,addr=0x00,netdev=gns3-0".format(mac_0) in cmd
731+
assert "e1000,mac={},bus=pci-bridge1,addr=0x01,netdev=gns3-1".format(mac_1) in cmd
744732

745733

746734
@pytest.mark.asyncio
@@ -762,28 +750,20 @@ async def test_build_command_large_number_of_adapters(vm):
762750
assert len([l for l in cmd if "e1000" in l ]) == 100
763751
assert len(vm._ethernet_adapters) == 100
764752

765-
assert "e1000,mac={},netdev=gns3-0".format(mac_0) in cmd
766-
assert "e1000,mac={},netdev=gns3-1".format(mac_1) in cmd
753+
assert "e1000,mac={},bus=pci-bridge1,addr=0x00,netdev=gns3-0".format(mac_0) in cmd
754+
assert "e1000,mac={},bus=pci-bridge1,addr=0x01,netdev=gns3-1".format(mac_1) in cmd
767755
assert "pci-bridge,id=pci-bridge0,bus=dmi_pci_bridge0,chassis_nr=0x1,addr=0x0,shpc=off" not in cmd
768756
assert "pci-bridge,id=pci-bridge1,bus=dmi_pci_bridge1,chassis_nr=0x1,addr=0x1,shpc=off" in cmd
769757
assert "pci-bridge,id=pci-bridge2,bus=dmi_pci_bridge2,chassis_nr=0x1,addr=0x2,shpc=off" in cmd
770758
assert "i82801b11-bridge,id=dmi_pci_bridge1" in cmd
771759

772760
mac_29 = int_to_macaddress(macaddress_to_int(vm._mac_address) + 29)
773-
assert "e1000,mac={},bus=pci-bridge1,addr=0x04,netdev=gns3-29".format(mac_29) in cmd
761+
assert "e1000,mac={},bus=pci-bridge1,addr=0x1d,netdev=gns3-29".format(mac_29) in cmd
774762
mac_30 = int_to_macaddress(macaddress_to_int(vm._mac_address) + 30)
775-
assert "e1000,mac={},bus=pci-bridge1,addr=0x05,netdev=gns3-30".format(mac_30) in cmd
763+
assert "e1000,mac={},bus=pci-bridge1,addr=0x1e,netdev=gns3-30".format(mac_30) in cmd
776764
mac_74 = int_to_macaddress(macaddress_to_int(vm._mac_address) + 74)
777-
assert "e1000,mac={},bus=pci-bridge2,addr=0x11,netdev=gns3-74".format(mac_74) in cmd
765+
assert "e1000,mac={},bus=pci-bridge3,addr=0x0a,netdev=gns3-74".format(mac_74) in cmd
778766

779-
# Qemu < 2.4 doesn't support large number of adapters
780-
vm.manager.get_qemu_version = AsyncioMagicMock(return_value="2.0.0")
781-
with pytest.raises(QemuError):
782-
with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()):
783-
await vm._build_command()
784-
vm.adapters = 5
785-
with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()):
786-
await vm._build_command()
787767

788768
@pytest.mark.asyncio
789769
async def test_build_command_with_virtio_net_pci_adapter(vm):
@@ -796,7 +776,8 @@ async def test_build_command_with_virtio_net_pci_adapter(vm):
796776
vm._adapter_type = "virtio-net-pci"
797777
with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()):
798778
cmd = await vm._build_command()
799-
assert "virtio-net-pci,mac=00:00:ab:0e:0f:09,speed=10000,duplex=full,netdev=gns3-0" in cmd
779+
print(cmd)
780+
assert "virtio-net-pci,mac=00:00:ab:0e:0f:09,speed=10000,duplex=full,bus=pci-bridge1,addr=0x00,netdev=gns3-0" in cmd
800781

801782

802783
@pytest.mark.asyncio

0 commit comments

Comments
 (0)