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:
parent
45a48cfcc1
commit
cecf2f5014
@ -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={
|
||||||
|
@ -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
|
||||||
|
@ -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):
|
||||||
|
@ -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"]))
|
||||||
|
@ -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"
|
||||||
|
Loading…
Reference in New Issue
Block a user