1
0
mirror of https://github.com/GNS3/gns3-server synced 2024-12-01 04:38:12 +00:00

Async qemu monitor reading

This commit is contained in:
Julien Duponchelle 2015-02-21 00:15:56 +01:00
parent 45a48cfcc1
commit cecf2f5014
5 changed files with 74 additions and 16 deletions

View File

@ -205,7 +205,7 @@ class QEMUHandler:
400: "Invalid request", 400: "Invalid request",
404: "Instance doesn't exist" 404: "Instance doesn't exist"
}, },
description="Reload a Qemu.instance") description="Suspend a Qemu.instance")
def suspend(request, response): def suspend(request, response):
qemu_manager = Qemu.instance() qemu_manager = Qemu.instance()
@ -213,6 +213,26 @@ class QEMUHandler:
yield from vm.suspend() yield from vm.suspend()
response.set_status(204) response.set_status(204)
@classmethod
@Route.post(
r"/projects/{project_id}/qemu/vms/{vm_id}/resume",
parameters={
"project_id": "UUID for the project",
"vm_id": "UUID for the instance",
},
status_codes={
204: "Instance resumed",
400: "Invalid request",
404: "Instance doesn't exist"
},
description="Resume a Qemu.instance")
def resume(request, response):
qemu_manager = Qemu.instance()
vm = qemu_manager.get_vm(request.match_info["vm_id"], project_id=request.match_info["project_id"])
yield from vm.resume()
response.set_status(204)
@Route.post( @Route.post(
r"/projects/{project_id}/qemu/vms/{vm_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio", r"/projects/{project_id}/qemu/vms/{vm_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio",
parameters={ parameters={

View File

@ -612,13 +612,12 @@ class QemuVM(BaseVM):
self._stop_cpulimit() self._stop_cpulimit()
@asyncio.coroutine @asyncio.coroutine
def _control_vm(self, command, expected=None, timeout=30): def _control_vm(self, command, expected=None):
""" """
Executes a command with QEMU monitor when this VM is running. Executes a command with QEMU monitor when this VM is running.
:param command: QEMU monitor command (e.g. info status, stop etc.) :param command: QEMU monitor command (e.g. info status, stop etc.)
:params expected: An array with the string attended (Default None) :params expected: An array with the string attended (Default None)
:param timeout: how long to wait for QEMU monitor
:returns: result of the command (Match object or None) :returns: result of the command (Match object or None)
""" """
@ -627,25 +626,29 @@ class QemuVM(BaseVM):
if self.is_running() and self._monitor: if self.is_running() and self._monitor:
log.debug("Execute QEMU monitor command: {}".format(command)) log.debug("Execute QEMU monitor command: {}".format(command))
try: try:
tn = telnetlib.Telnet(self._monitor_host, self._monitor, timeout=timeout) reader, writer = yield from asyncio.open_connection("127.0.0.1", self._monitor)
except OSError as e: except OSError as e:
log.warn("Could not connect to QEMU monitor: {}".format(e)) log.warn("Could not connect to QEMU monitor: {}".format(e))
return result return result
try: try:
tn.write(command.encode('ascii') + b"\n") writer.write(command.encode('ascii') + b"\n")
time.sleep(0.1)
except OSError as e: except OSError as e:
log.warn("Could not write to QEMU monitor: {}".format(e)) log.warn("Could not write to QEMU monitor: {}".format(e))
tn.close() writer.close()
return result return result
if expected: if expected:
try: try:
ind, match, dat = tn.expect(list=expected, timeout=timeout) while result is None:
if match: line = yield from reader.readline()
result = match if not line:
break
for expect in expected:
if expect in line:
result = line
break
except EOFError as e: except EOFError as e:
log.warn("Could not read from QEMU monitor: {}".format(e)) log.warn("Could not read from QEMU monitor: {}".format(e))
tn.close() writer.close()
return result return result
@asyncio.coroutine @asyncio.coroutine
@ -667,11 +670,7 @@ class QemuVM(BaseVM):
:returns: status (string) :returns: status (string)
""" """
result = None result = yield from self._control_vm("info status", [b"running", b"paused"])
match = yield from self._control_vm("info status", [b"running", b"paused"])
if match:
result = match.group(0).decode('ascii')
return result return result
@asyncio.coroutine @asyncio.coroutine

View File

@ -142,6 +142,8 @@ class Server:
@asyncio.coroutine @asyncio.coroutine
def start_shell(self): def start_shell(self):
from ptpython.repl import embed from ptpython.repl import embed
from gns3server.modules import Qemu
yield from embed(globals(), locals(), return_asyncio_coroutine=True, patch_stdout=True) yield from embed(globals(), locals(), return_asyncio_coroutine=True, patch_stdout=True)
def run(self): def run(self):

View File

@ -103,6 +103,13 @@ def test_qemu_suspend(server, vm):
assert response.status == 204 assert response.status == 204
def test_qemu_resume(server, vm):
with asyncio_patch("gns3server.modules.qemu.qemu_vm.QemuVM.resume", return_value=True) as mock:
response = server.post("/projects/{project_id}/qemu/vms/{vm_id}/resume".format(project_id=vm["project_id"], vm_id=vm["vm_id"]))
assert mock.called
assert response.status == 204
def test_qemu_delete(server, vm): def test_qemu_delete(server, vm):
with asyncio_patch("gns3server.modules.qemu.Qemu.delete_vm", return_value=True) as mock: with asyncio_patch("gns3server.modules.qemu.Qemu.delete_vm", return_value=True) as mock:
response = server.delete("/projects/{project_id}/qemu/vms/{vm_id}".format(project_id=vm["project_id"], vm_id=vm["vm_id"])) response = server.delete("/projects/{project_id}/qemu/vms/{vm_id}".format(project_id=vm["project_id"], vm_id=vm["vm_id"]))

View File

@ -203,3 +203,33 @@ def test_json(vm, project):
json = vm.__json__() json = vm.__json__()
assert json["name"] == vm.name assert json["name"] == vm.name
assert json["project_id"] == project.id assert json["project_id"] == project.id
def test_control_vm(vm, loop):
vm._process = MagicMock()
vm._monitor = 4242
reader = MagicMock()
writer = MagicMock()
with asyncio_patch("asyncio.open_connection", return_value=(reader, writer)) as open_connect:
res = loop.run_until_complete(asyncio.async(vm._control_vm("test")))
assert writer.write.called_with("test")
assert res is None
def test_control_vm_expect_text(vm, loop):
vm._process = MagicMock()
vm._monitor = 4242
reader = MagicMock()
writer = MagicMock()
with asyncio_patch("asyncio.open_connection", return_value=(reader, writer)) as open_connect:
future = asyncio.Future()
future.set_result("epic product")
reader.readline.return_value = future
res = loop.run_until_complete(asyncio.async(vm._control_vm("test", ["epic"])))
assert writer.write.called_with("test")
assert res == "epic product"