mirror of
https://github.com/GNS3/gns3-server
synced 2024-11-28 11:18:11 +00:00
Merge pull request #895 from GNS3/qemu_more_adapters
Allow up to 275 adapters for qemu
This commit is contained in:
commit
aee7208f7b
@ -23,12 +23,13 @@ order to run a QEMU VM.
|
|||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
import math
|
||||||
import shutil
|
import shutil
|
||||||
import subprocess
|
|
||||||
import shlex
|
import shlex
|
||||||
import asyncio
|
import asyncio
|
||||||
import socket
|
import socket
|
||||||
import gns3server
|
import gns3server
|
||||||
|
import subprocess
|
||||||
|
|
||||||
from gns3server.utils import parse_version
|
from gns3server.utils import parse_version
|
||||||
from .qemu_error import QemuError
|
from .qemu_error import QemuError
|
||||||
@ -1446,6 +1447,20 @@ class QemuVM(BaseNode):
|
|||||||
# this is a patched Qemu if version is below 1.1.0
|
# this is a patched Qemu if version is below 1.1.0
|
||||||
patched_qemu = True
|
patched_qemu = True
|
||||||
|
|
||||||
|
# Each 32 PCI device we need to add a PCI bridge with max 9 bridges
|
||||||
|
pci_devices = 4 + len(self._ethernet_adapters) # 4 PCI devices are use by default by qemu
|
||||||
|
bridge_id = 0
|
||||||
|
for bridge_id in range(1, math.floor(pci_devices / 32) + 1):
|
||||||
|
network_options.extend(["-device", "i82801b11-bridge,id=dmi_pci_bridge{bridge_id}".format(bridge_id=bridge_id)])
|
||||||
|
network_options.extend(["-device", "pci-bridge,id=pci-bridge{bridge_id},bus=dmi_pci_bridge{bridge_id},chassis_nr=0x1,addr=0x{bridge_id},shpc=off".format(bridge_id=bridge_id)])
|
||||||
|
|
||||||
|
if bridge_id > 1:
|
||||||
|
qemu_version = yield from self.manager.get_qemu_version(self.qemu_path)
|
||||||
|
if qemu_version and parse_version(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 + bridge_id # Bridge consume PCI ports
|
||||||
|
|
||||||
for adapter_number, adapter in enumerate(self._ethernet_adapters):
|
for adapter_number, adapter in enumerate(self._ethernet_adapters):
|
||||||
mac = int_to_macaddress(macaddress_to_int(self._mac_address) + adapter_number)
|
mac = int_to_macaddress(macaddress_to_int(self._mac_address) + adapter_number)
|
||||||
|
|
||||||
@ -1483,8 +1498,14 @@ class QemuVM(BaseNode):
|
|||||||
|
|
||||||
else:
|
else:
|
||||||
# newer QEMU networking syntax
|
# newer QEMU networking syntax
|
||||||
|
device_string = "{},mac={}".format(self._adapter_type, mac)
|
||||||
|
bridge_id = math.floor(pci_device_id / 32)
|
||||||
|
if bridge_id > 0:
|
||||||
|
addr = pci_device_id % 32
|
||||||
|
device_string = "{},bus=pci-bridge{bridge_id},addr=0x{addr:02x}".format(device_string, bridge_id=bridge_id, addr=addr)
|
||||||
|
pci_device_id += 1
|
||||||
if nio:
|
if nio:
|
||||||
network_options.extend(["-device", "{},mac={},netdev=gns3-{}".format(self._adapter_type, mac, adapter_number)])
|
network_options.extend(["-device", "{},netdev=gns3-{}".format(device_string, adapter_number)])
|
||||||
if isinstance(nio, NIOUDP):
|
if isinstance(nio, NIOUDP):
|
||||||
network_options.extend(["-netdev", "socket,id=gns3-{},udp={}:{},localaddr={}:{}".format(adapter_number,
|
network_options.extend(["-netdev", "socket,id=gns3-{},udp={}:{},localaddr={}:{}".format(adapter_number,
|
||||||
nio.rhost,
|
nio.rhost,
|
||||||
@ -1494,7 +1515,7 @@ class QemuVM(BaseNode):
|
|||||||
elif isinstance(nio, NIOTAP):
|
elif isinstance(nio, NIOTAP):
|
||||||
network_options.extend(["-netdev", "tap,id=gns3-{},ifname={},script=no,downscript=no".format(adapter_number, nio.tap_device)])
|
network_options.extend(["-netdev", "tap,id=gns3-{},ifname={},script=no,downscript=no".format(adapter_number, nio.tap_device)])
|
||||||
else:
|
else:
|
||||||
network_options.extend(["-device", "{},mac={}".format(self._adapter_type, mac)])
|
network_options.extend(["-device", device_string])
|
||||||
|
|
||||||
return network_options
|
return network_options
|
||||||
|
|
||||||
|
@ -147,7 +147,7 @@ QEMU_CREATE_SCHEMA = {
|
|||||||
"description": "Number of adapters",
|
"description": "Number of adapters",
|
||||||
"type": ["integer", "null"],
|
"type": ["integer", "null"],
|
||||||
"minimum": 0,
|
"minimum": 0,
|
||||||
"maximum": 32,
|
"maximum": 275,
|
||||||
},
|
},
|
||||||
"adapter_type": {
|
"adapter_type": {
|
||||||
"description": "QEMU adapter type",
|
"description": "QEMU adapter type",
|
||||||
@ -332,7 +332,7 @@ QEMU_UPDATE_SCHEMA = {
|
|||||||
"description": "Number of adapters",
|
"description": "Number of adapters",
|
||||||
"type": ["integer", "null"],
|
"type": ["integer", "null"],
|
||||||
"minimum": 0,
|
"minimum": 0,
|
||||||
"maximum": 32,
|
"maximum": 275,
|
||||||
},
|
},
|
||||||
"adapter_type": {
|
"adapter_type": {
|
||||||
"description": "QEMU adapter type",
|
"description": "QEMU adapter type",
|
||||||
@ -520,7 +520,7 @@ QEMU_OBJECT_SCHEMA = {
|
|||||||
"description": "Number of adapters",
|
"description": "Number of adapters",
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"minimum": 0,
|
"minimum": 0,
|
||||||
"maximum": 32,
|
"maximum": 275,
|
||||||
},
|
},
|
||||||
"adapter_type": {
|
"adapter_type": {
|
||||||
"description": "QEMU adapter type",
|
"description": "QEMU adapter type",
|
||||||
|
@ -588,7 +588,7 @@ def test_build_command_two_adapters_mac_address(vm, loop, fake_qemu_binary, port
|
|||||||
vm.adapters = 2
|
vm.adapters = 2
|
||||||
vm.mac_address = "00:00:ab:0e:0f:09"
|
vm.mac_address = "00:00:ab:0e:0f:09"
|
||||||
mac_0 = vm._mac_address
|
mac_0 = vm._mac_address
|
||||||
mac_1 = int_to_macaddress(macaddress_to_int(vm._mac_address))
|
mac_1 = int_to_macaddress(macaddress_to_int(vm._mac_address) + 1)
|
||||||
assert mac_0[:8] == "00:00:ab"
|
assert mac_0[:8] == "00:00:ab"
|
||||||
with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()) as process:
|
with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()) as process:
|
||||||
cmd = loop.run_until_complete(asyncio.async(vm._build_command()))
|
cmd = loop.run_until_complete(asyncio.async(vm._build_command()))
|
||||||
@ -605,7 +605,49 @@ def test_build_command_two_adapters_mac_address(vm, loop, fake_qemu_binary, port
|
|||||||
assert "e1000,mac={}".format(mac_1) in cmd
|
assert "e1000,mac={}".format(mac_1) in cmd
|
||||||
|
|
||||||
|
|
||||||
|
def test_build_command_large_number_of_adapters(vm, loop, fake_qemu_binary, port_manager):
|
||||||
|
"""
|
||||||
|
When we have more than 28 interface we need to add a pci bridge for
|
||||||
|
additionnal interfaces
|
||||||
|
"""
|
||||||
|
|
||||||
|
# It's supported only with Qemu 2.4 and later
|
||||||
|
vm.manager.get_qemu_version = AsyncioMagicMock(return_value="2.4.0")
|
||||||
|
|
||||||
|
vm.adapters = 100
|
||||||
|
vm.mac_address = "00:00:ab:0e:0f:09"
|
||||||
|
mac_0 = vm._mac_address
|
||||||
|
mac_1 = int_to_macaddress(macaddress_to_int(vm._mac_address) + 1)
|
||||||
|
assert mac_0[:8] == "00:00:ab"
|
||||||
|
with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()) as process:
|
||||||
|
cmd = loop.run_until_complete(asyncio.async(vm._build_command()))
|
||||||
|
|
||||||
|
assert "e1000,mac={}".format(mac_0) in cmd
|
||||||
|
assert "e1000,mac={}".format(mac_1) in cmd
|
||||||
|
assert "pci-bridge,id=pci-bridge0,bus=dmi_pci_bridge0,chassis_nr=0x1,addr=0x0,shpc=off" not in cmd
|
||||||
|
assert "pci-bridge,id=pci-bridge1,bus=dmi_pci_bridge1,chassis_nr=0x1,addr=0x1,shpc=off" in cmd
|
||||||
|
assert "pci-bridge,id=pci-bridge2,bus=dmi_pci_bridge2,chassis_nr=0x1,addr=0x2,shpc=off" in cmd
|
||||||
|
assert "i82801b11-bridge,id=dmi_pci_bridge1" in cmd
|
||||||
|
|
||||||
|
mac_29 = int_to_macaddress(macaddress_to_int(vm._mac_address) + 29)
|
||||||
|
assert "e1000,mac={},bus=pci-bridge1,addr=0x04".format(mac_29) in cmd
|
||||||
|
mac_30 = int_to_macaddress(macaddress_to_int(vm._mac_address) + 30)
|
||||||
|
assert "e1000,mac={},bus=pci-bridge1,addr=0x05".format(mac_30) in cmd
|
||||||
|
mac_74 = int_to_macaddress(macaddress_to_int(vm._mac_address) + 74)
|
||||||
|
assert "e1000,mac={},bus=pci-bridge2,addr=0x11".format(mac_74) in cmd
|
||||||
|
|
||||||
|
# Qemu < 2.4 doesn't support large number of adapters
|
||||||
|
vm.manager.get_qemu_version = AsyncioMagicMock(return_value="2.0.0")
|
||||||
|
with pytest.raises(QemuError):
|
||||||
|
with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()) as process:
|
||||||
|
cmd = loop.run_until_complete(asyncio.async(vm._build_command()))
|
||||||
|
vm.adapters = 5
|
||||||
|
with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()) as process:
|
||||||
|
cmd = loop.run_until_complete(asyncio.async(vm._build_command()))
|
||||||
|
|
||||||
# Windows accept this kind of mistake
|
# Windows accept this kind of mistake
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows")
|
@pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows")
|
||||||
def test_build_command_with_invalid_options(vm, loop, fake_qemu_binary):
|
def test_build_command_with_invalid_options(vm, loop, fake_qemu_binary):
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user