1
0
mirror of https://github.com/GNS3/gns3-server synced 2024-11-28 11:18:11 +00:00

Fix bug and add optimizations when connecting and sending commands to QEMU monitor after starting a VM. Fixes #2336.

This commit is contained in:
grossmj 2018-03-23 00:07:32 +07:00
parent 135bbe8825
commit a40fdb3641
2 changed files with 63 additions and 9 deletions

View File

@ -90,7 +90,7 @@ class DynamipsHypervisor:
if not connection_success: if not connection_success:
raise DynamipsError("Couldn't connect to hypervisor on {}:{} :{}".format(host, self._port, last_exception)) raise DynamipsError("Couldn't connect to hypervisor on {}:{} :{}".format(host, self._port, last_exception))
else: else:
log.info("Connected to Dynamips hypervisor after {:.4f} seconds".format(time.time() - begin)) log.info("Connected to Dynamips hypervisor on {}:{} after {:.4f} seconds".format(host, self._port, time.time() - begin))
try: try:
version = yield from self.send("hypervisor version") version = yield from self.send("hypervisor version")

View File

@ -30,6 +30,7 @@ import asyncio
import socket import socket
import gns3server import gns3server
import subprocess import subprocess
import time
from gns3server.utils import parse_version from gns3server.utils import parse_version
from gns3server.utils.asyncio import subprocess_check_output, cancellable_wait_run_in_executor from gns3server.utils.asyncio import subprocess_check_output, cancellable_wait_run_in_executor
@ -956,6 +957,7 @@ class QemuVM(BaseNode):
self._hw_virtualization = True self._hw_virtualization = True
yield from self._start_ubridge() yield from self._start_ubridge()
set_link_commands = []
for adapter_number, adapter in enumerate(self._ethernet_adapters): for adapter_number, adapter in enumerate(self._ethernet_adapters):
nio = adapter.get_nio(0) nio = adapter.get_nio(0)
if nio: if nio:
@ -963,9 +965,10 @@ class QemuVM(BaseNode):
self._local_udp_tunnels[adapter_number][1], self._local_udp_tunnels[adapter_number][1],
nio) nio)
if nio.suspend: if nio.suspend:
yield from self._control_vm("set_link gns3-{} off".format(adapter_number)) set_link_commands.append("set_link gns3-{} off".format(adapter_number))
else: else:
yield from self._control_vm("set_link gns3-{} off".format(adapter_number)) set_link_commands.append("set_link gns3-{} off".format(adapter_number))
yield from self._control_vm_commands(set_link_commands)
try: try:
yield from self.start_wrap_console() yield from self.start_wrap_console()
@ -1020,6 +1023,37 @@ class QemuVM(BaseNode):
self._stop_cpulimit() self._stop_cpulimit()
yield from super().stop() yield from super().stop()
@asyncio.coroutine
def _open_qemu_monitor_connection_vm(self, timeout=10):
"""
Opens a connection to the QEMU monitor.
:param timeout: timeout to connect to the monitor TCP server
:returns: The reader returned is a StreamReader instance; the writer is a StreamWriter instance
"""
begin = time.time()
connection_success = False
last_exception = None
reader = writer = None
while time.time() - begin < timeout:
yield from asyncio.sleep(0.01)
try:
log.debug("Connecting to Qemu monitor on {}:{}".format(self._monitor_host, self._monitor))
reader, writer = yield from asyncio.open_connection(self._monitor_host, self._monitor)
except (asyncio.TimeoutError, OSError) as e:
last_exception = e
continue
connection_success = True
break
if not connection_success:
log.warning("Could not connect to QEMU monitor on {}:{}: {}".format(self._monitor_host, self._monitor,
last_exception))
else:
log.info("Connected to QEMU monitor on {}:{} after {:.4f} seconds".format(self._monitor_host, self._monitor, time.time() - begin))
return reader, writer
@asyncio.coroutine @asyncio.coroutine
def _control_vm(self, command, expected=None): def _control_vm(self, command, expected=None):
""" """
@ -1033,13 +1067,11 @@ class QemuVM(BaseNode):
result = None result = None
if self.is_running() and self._monitor: if self.is_running() and self._monitor:
log.debug("Execute QEMU monitor command: {}".format(command)) log.info("Execute QEMU monitor command: {}".format(command))
try: reader, writer = yield from self._open_qemu_monitor_connection_vm()
log.debug("Connecting to Qemu monitor on {}:{}".format(self._monitor_host, self._monitor)) if reader is None and writer is None:
reader, writer = yield from asyncio.open_connection(self._monitor_host, self._monitor)
except OSError as e:
log.warning("Could not connect to QEMU monitor on {}:{}: {}".format(self._monitor_host, self._monitor, e))
return result return result
try: try:
writer.write(command.encode('ascii') + b"\n") writer.write(command.encode('ascii') + b"\n")
except OSError as e: except OSError as e:
@ -1061,6 +1093,28 @@ class QemuVM(BaseNode):
writer.close() writer.close()
return result return result
@asyncio.coroutine
def _control_vm_commands(self, commands):
"""
Executes commands with QEMU monitor when this VM is running.
:param commands: a list of QEMU monitor commands (e.g. info status, stop etc.)
"""
if self.is_running() and self._monitor:
reader, writer = yield from self._open_qemu_monitor_connection_vm()
if reader is None and writer is None:
return
for command in commands:
log.info("Execute QEMU monitor command: {}".format(command))
try:
writer.write(command.encode('ascii') + b"\n")
except OSError as e:
log.warning("Could not write to QEMU monitor: {}".format(e))
writer.close()
@asyncio.coroutine @asyncio.coroutine
def close(self): def close(self):
""" """