1
0
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:
Jeremy Grossmann 2017-02-17 01:40:41 -08:00 committed by GitHub
commit aee7208f7b
3 changed files with 70 additions and 7 deletions

View File

@ -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

View File

@ -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",

View File

@ -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):