From d00efbfb0fbcc662e21542983b5e83c521a03764 Mon Sep 17 00:00:00 2001 From: Vasil Rangelov Date: Fri, 1 Jan 2016 02:40:12 +0200 Subject: [PATCH] Adds a handler for getting the Qemu related capabilities of the server. Currently includes just a check for KVM architectures. --- gns3server/handlers/api/qemu_handler.py | 20 ++++++++++++- gns3server/modules/qemu/__init__.py | 38 ++++++++++++++++++++++--- gns3server/schemas/qemu.py | 30 +++++++++++++++++++ tests/modules/qemu/test_qemu_manager.py | 18 ++++++++++-- 4 files changed, 99 insertions(+), 7 deletions(-) diff --git a/gns3server/handlers/api/qemu_handler.py b/gns3server/handlers/api/qemu_handler.py index 5f8c40f8..c59b271d 100644 --- a/gns3server/handlers/api/qemu_handler.py +++ b/gns3server/handlers/api/qemu_handler.py @@ -25,7 +25,9 @@ from ...schemas.nio import NIO_SCHEMA from ...schemas.qemu import QEMU_CREATE_SCHEMA from ...schemas.qemu import QEMU_UPDATE_SCHEMA from ...schemas.qemu import QEMU_OBJECT_SCHEMA +from ...schemas.qemu import QEMU_BINARY_FILTER_SCHEMA from ...schemas.qemu import QEMU_BINARY_LIST_SCHEMA +from ...schemas.qemu import QEMU_CAPABILITY_LIST_SCHEMA from ...schemas.qemu import QEMU_IMAGE_CREATE_SCHEMA from ...schemas.vm import VM_LIST_IMAGES_SCHEMA from ...modules.qemu import Qemu @@ -300,10 +302,11 @@ class QEMUHandler: 404: "Instance doesn't exist" }, description="Get a list of available Qemu binaries", + input=QEMU_BINARY_FILTER_SCHEMA, output=QEMU_BINARY_LIST_SCHEMA) def list_binaries(request, response): - binaries = yield from Qemu.binary_list() + binaries = yield from Qemu.binary_list(request.json["archs"] if "archs" in request.json else None) response.json(binaries) @classmethod @@ -321,6 +324,21 @@ class QEMUHandler: binaries = yield from Qemu.img_binary_list() response.json(binaries) + @Route.get( + r"/qemu/capabilities", + status_codes={ + 200: "Success" + }, + description="Get a list of Qemu capabilities on this server", + output=QEMU_CAPABILITY_LIST_SCHEMA + ) + def get_capabilities(request, response): + capabilities = {} + kvms = Qemu.get_kvm_archs() + if kvms: + capabilities["kvm"] = kvms + response.json(capabilities) + @classmethod @Route.post( r"/qemu/img", diff --git a/gns3server/modules/qemu/__init__.py b/gns3server/modules/qemu/__init__.py index a0666444..45e633bb 100644 --- a/gns3server/modules/qemu/__init__.py +++ b/gns3server/modules/qemu/__init__.py @@ -21,6 +21,7 @@ Qemu server module. import asyncio import os +import platform import sys import re import subprocess @@ -38,6 +39,27 @@ class Qemu(BaseManager): _VM_CLASS = QemuVM + @staticmethod + def get_kvm_archs(): + """ + Gets a list of architectures for which KVM is available on this server. + + :returns: List of architectures for which KVM is available on this server. + """ + kvm = [] + x86_64_aliases = ["x86_64", "x86-64", "x64", "AMD64", "amd64", "Intel 64", "EM64T"] + i386_aliases = ["i386", "x86", "x32"] + if sys.platform.startswith("linux") and subprocess.call("kvm-ok") == 0: + arch = platform.machine() + if arch in x86_64_aliases: + kvm.append("x86_64") + kvm.append("i386") + elif arch in i386_aliases: + kvm.append("i386") + else: + kvm.append(platform.machine()) + return kvm + @staticmethod def paths_list(): """ @@ -82,7 +104,7 @@ class Qemu(BaseManager): return paths @staticmethod - def binary_list(): + def binary_list(archs=None): """ Gets QEMU binaries list available on the host. @@ -96,9 +118,17 @@ class Qemu(BaseManager): if (f.startswith("qemu-system") or f.startswith("qemu-kvm") or f == "qemu" or f == "qemu.exe") and \ os.access(os.path.join(path, f), os.X_OK) and \ os.path.isfile(os.path.join(path, f)): - qemu_path = os.path.join(path, f) - version = yield from Qemu.get_qemu_version(qemu_path) - qemus.append({"path": qemu_path, "version": version}) + if archs is not None: + for arch in archs: + if f.endswith(arch) or f.endswith("{}.exe".format(arch)) or f.endswith("{}w.exe".format(arch)): + qemu_path = os.path.join(path, f) + version = yield from Qemu.get_qemu_version(qemu_path) + qemus.append({"path": qemu_path, "version": version}) + else: + qemu_path = os.path.join(path, f) + version = yield from Qemu.get_qemu_version(qemu_path) + qemus.append({"path": qemu_path, "version": version}) + except OSError: continue diff --git a/gns3server/schemas/qemu.py b/gns3server/schemas/qemu.py index 43af3409..89362fbe 100644 --- a/gns3server/schemas/qemu.py +++ b/gns3server/schemas/qemu.py @@ -601,6 +601,21 @@ QEMU_OBJECT_SCHEMA = { "vm_directory"] } +QEMU_BINARY_FILTER_SCHEMA = { + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "Request validation for a list of qemu capabilities", + "properties": { + "archs": { + "description": "Architectures to filter binaries by", + "type": "array", + "items": { + "enum": QEMU_PLATFORMS + } + } + }, + "additionalProperties": False, +} + QEMU_BINARY_LIST_SCHEMA = { "$schema": "http://json-schema.org/draft-04/schema#", "description": "Request validation for a list of qemu binaries", @@ -626,6 +641,21 @@ QEMU_BINARY_LIST_SCHEMA = { "additionalProperties": False, } +QEMU_CAPABILITY_LIST_SCHEMA = { + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "Request validation for a list of qemu capabilities", + "properties": { + "kvm": { + "description": "Architectures that KVM is enabled for", + "type": "array", + "items": { + "enum": QEMU_PLATFORMS + } + } + }, + "additionalProperties": False, +} + QEMU_IMAGE_CREATE_SCHEMA = { "$schema": "http://json-schema.org/draft-04/schema#", "description": "Create a new qemu image. Options can be specific to a format. Read qemu-img manual for more information", diff --git a/tests/modules/qemu/test_qemu_manager.py b/tests/modules/qemu/test_qemu_manager.py index 36ede5bb..406bc296 100644 --- a/tests/modules/qemu/test_qemu_manager.py +++ b/tests/modules/qemu/test_qemu_manager.py @@ -58,18 +58,32 @@ def test_binary_list(loop): os.chmod(path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR) with asyncio_patch("gns3server.modules.qemu.subprocess_check_output", return_value="QEMU emulator version 2.2.0, Copyright (c) 2003-2008 Fabrice Bellard") as mock: - qemus = loop.run_until_complete(asyncio.async(Qemu.binary_list())) - if sys.platform.startswith("win"): version = "" else: version = "2.2.0" + qemus = loop.run_until_complete(asyncio.async(Qemu.binary_list())) + assert {"path": os.path.join(os.environ["PATH"], "qemu-system-x86"), "version": version} in qemus assert {"path": os.path.join(os.environ["PATH"], "qemu-kvm"), "version": version} in qemus assert {"path": os.path.join(os.environ["PATH"], "qemu-system-x42"), "version": version} in qemus assert {"path": os.path.join(os.environ["PATH"], "hello"), "version": version} not in qemus + qemus = loop.run_until_complete(asyncio.async(Qemu.binary_list(["x86"]))) + + assert {"path": os.path.join(os.environ["PATH"], "qemu-system-x86"), "version": version} in qemus + assert {"path": os.path.join(os.environ["PATH"], "qemu-kvm"), "version": version} not in qemus + assert {"path": os.path.join(os.environ["PATH"], "qemu-system-x42"), "version": version} not in qemus + assert {"path": os.path.join(os.environ["PATH"], "hello"), "version": version} not in qemus + + qemus = loop.run_until_complete(asyncio.async(Qemu.binary_list(["x86", "x42"]))) + + assert {"path": os.path.join(os.environ["PATH"], "qemu-system-x86"), "version": version} in qemus + assert {"path": os.path.join(os.environ["PATH"], "qemu-kvm"), "version": version} not in qemus + assert {"path": os.path.join(os.environ["PATH"], "qemu-system-x42"), "version": version} in qemus + assert {"path": os.path.join(os.environ["PATH"], "hello"), "version": version} not in qemus + def test_img_binary_list(loop):