From c57b0cbb5365d3a5c8bc35235ccd0a5eda7ebd54 Mon Sep 17 00:00:00 2001 From: grossmj Date: Mon, 16 Jan 2023 18:04:46 +0800 Subject: [PATCH] Find Dynamips version before hypervisor launch and do not require Dynamips v0.2.23 --- gns3server/compute/dynamips/__init__.py | 44 +++++++++++++++++------ gns3server/compute/dynamips/hypervisor.py | 9 +++-- gns3server/utils/asyncio/__init__.py | 2 +- 3 files changed, 41 insertions(+), 14 deletions(-) diff --git a/gns3server/compute/dynamips/__init__.py b/gns3server/compute/dynamips/__init__.py index 11efc428..73e896e7 100644 --- a/gns3server/compute/dynamips/__init__.py +++ b/gns3server/compute/dynamips/__init__.py @@ -28,13 +28,14 @@ import time import asyncio import tempfile import logging +import subprocess import glob import re log = logging.getLogger(__name__) from gns3server.utils.interfaces import interfaces, is_interface_up -from gns3server.utils.asyncio import wait_run_in_executor +from gns3server.utils.asyncio import wait_run_in_executor, subprocess_check_output from gns3server.utils import parse_version from uuid import uuid4 from ..base_manager import BaseManager @@ -263,6 +264,25 @@ class Dynamips(BaseManager): self._dynamips_path = dynamips_path return dynamips_path + @staticmethod + async def dynamips_version(dynamips_path): + """ + Gets the Dynamips version + + :param dynamips_path: path to Dynamips executable. + """ + + try: + output = await subprocess_check_output(dynamips_path) + match = re.search(r"Cisco Router Simulation Platform \(version\s+([\d.]+)", output) + if match: + version = match.group(1) + return version + else: + raise DynamipsError("Could not determine the Dynamips version for {}".format(dynamips_path)) + except (OSError, subprocess.SubprocessError) as e: + raise DynamipsError("Error while looking for the Dynamips version: {}".format(e)) + async def start_new_hypervisor(self, working_dir=None): """ Creates a new Dynamips process and start it. @@ -278,14 +298,21 @@ class Dynamips(BaseManager): if not working_dir: working_dir = tempfile.gettempdir() + server_config = self.config.get_section_config("Server") + server_host = server_config.get("host") + bind_console_host = False + + dynamips_version = await self.dynamips_version(self.dynamips_path) + if parse_version(dynamips_version) < parse_version('0.2.11'): + raise DynamipsError("Dynamips version must be >= 0.2.11, detected version is {}".format(dynamips_version)) + if not sys.platform.startswith("win"): # Hypervisor should always listen to 127.0.0.1 # See https://github.com/GNS3/dynamips/issues/62 # This was fixed in Dynamips v0.2.23 which hasn't been built for Windows - server_host = "127.0.0.1" - else: - server_config = self.config.get_section_config("Server") - server_host = server_config.get("host") + if parse_version(dynamips_version) >= parse_version('0.2.23'): + server_host = "127.0.0.1" + bind_console_host = True try: info = socket.getaddrinfo(server_host, 0, socket.AF_UNSPEC, socket.SOCK_STREAM, 0, socket.AI_PASSIVE) @@ -302,17 +329,12 @@ class Dynamips(BaseManager): raise DynamipsError("Could not find free port for the Dynamips hypervisor: {}".format(e)) port_manager = PortManager.instance() - hypervisor = Hypervisor(self._dynamips_path, working_dir, server_host, port, port_manager.console_host) + hypervisor = Hypervisor(self._dynamips_path, working_dir, server_host, port, port_manager.console_host, bind_console_host) log.info("Creating new hypervisor {}:{} with working directory {}".format(hypervisor.host, hypervisor.port, working_dir)) await hypervisor.start() log.info("Hypervisor {}:{} has successfully started".format(hypervisor.host, hypervisor.port)) await hypervisor.connect() - if parse_version(hypervisor.version) < parse_version('0.2.11'): - raise DynamipsError("Dynamips version must be >= 0.2.11, detected version is {}".format(hypervisor.version)) - if not sys.platform.startswith("win") and parse_version(hypervisor.version) < parse_version('0.2.23'): - raise DynamipsError("Dynamips version must be >= 0.2.23 on Linux/macOS, detected version is {}".format(hypervisor.version)) - return hypervisor async def ghost_ios_support(self, vm): diff --git a/gns3server/compute/dynamips/hypervisor.py b/gns3server/compute/dynamips/hypervisor.py index 03e21933..9ce1d464 100644 --- a/gns3server/compute/dynamips/hypervisor.py +++ b/gns3server/compute/dynamips/hypervisor.py @@ -46,7 +46,7 @@ class Hypervisor(DynamipsHypervisor): _instance_count = 1 - def __init__(self, path, working_dir, host, port, console_host): + def __init__(self, path, working_dir, host, port, console_host, bind_console_host=False): super().__init__(working_dir, host, port) @@ -55,6 +55,7 @@ class Hypervisor(DynamipsHypervisor): Hypervisor._instance_count += 1 self._console_host = console_host + self._bind_console_host = bind_console_host self._path = path self._command = [] self._process = None @@ -204,8 +205,12 @@ class Hypervisor(DynamipsHypervisor): command = [self._path] command.extend(["-N1"]) # use instance IDs for filenames command.extend(["-l", "dynamips_i{}_log.txt".format(self._id)]) # log file - if not sys.platform.startswith("win"): + + if self._bind_console_host: + # support was added in Dynamips version 0.2.23 command.extend(["-H", "{}:{}".format(self._host, self._port), "--console-binding-addr", self._console_host]) + elif self._console_host != "0.0.0.0" and self._console_host != "::": + command.extend(["-H", "{}:{}".format(self._host, self._port)]) else: command.extend(["-H", str(self._port)]) diff --git a/gns3server/utils/asyncio/__init__.py b/gns3server/utils/asyncio/__init__.py index f6599abc..d459cb18 100644 --- a/gns3server/utils/asyncio/__init__.py +++ b/gns3server/utils/asyncio/__init__.py @@ -73,7 +73,7 @@ async def subprocess_check_output(*args, cwd=None, env=None, stderr=False): proc = await asyncio.create_subprocess_exec(*args, stderr=asyncio.subprocess.PIPE, cwd=cwd, env=env) output = await proc.stderr.read() else: - proc = await asyncio.create_subprocess_exec(*args, stdout=asyncio.subprocess.PIPE, cwd=cwd, env=env) + proc = await asyncio.create_subprocess_exec(*args, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.DEVNULL, cwd=cwd, env=env) output = await proc.stdout.read() if output is None: return ""