- This commit implements code that will check if the selected guest is
responding to data sent to its serial port before allowing client
connections to it. If it's not responding, an error message will be
displayed.
- There are 2 cases where the error message will be displayed:
1) guest is set up correctly but kernel is not sending boot msgs,
for example: I configured 'console=ttyS0' in my guest grub.conf
and I just booted it. I'll only be able to get the serial after
the kernel starts sending messages to ttyS0.
2) guest is not configured to use the serial.
Signed-off-by: Jose Ricardo Ziviani <joserz(a)linux.vnet.ibm.com>
---
i18n.py | 1 +
model/vms.py | 15 ++++++++++---
serialconsole.py | 55 +++++++++++++++++++++++++++++++++++++++++-----
tests/test_model.py | 4 ++--
ui/serial/html/serial.html | 4 ----
5 files changed, 65 insertions(+), 14 deletions(-)
diff --git a/i18n.py b/i18n.py
index e219857..6f46258 100644
--- a/i18n.py
+++ b/i18n.py
@@ -136,6 +136,7 @@ messages = {
"KCHVM0079E": _("Memory or Maximum Memory value is higher than maximum
amount recommended: %(value)sTiB"),
"KCHVM0080E": _("Cannot update Maximum Memory when guest is
running."),
"KCHVM0081E": _("Impossible to create /run/wok directory."),
+ "KCHVM0082E": _("Either the guest %(name)s did not start to listen to
the serial or it is not configured to use the serial console."),
"KCHVMHDEV0001E": _("VM %(vmid)s does not contain directly assigned
host device %(dev_name)s."),
"KCHVMHDEV0002E": _("The host device %(dev_name)s is not allowed to
directly assign to VM."),
diff --git a/model/vms.py b/model/vms.py
index cd77575..3bbed8d 100644
--- a/model/vms.py
+++ b/model/vms.py
@@ -1480,9 +1480,18 @@ class VMModel(object):
name.encode('utf-8')), True)
try:
- self._serial_procs.append(
- serialconsole.main(name.encode('utf-8'),
- self.conn.get().getURI()))
+ proc = serialconsole.main(name.encode('utf-8'),
+ self.conn.get().getURI())
+
+ proc.join(2)
+ if not proc.is_alive():
+ raise OperationFailed("KCHVM0082E", {'name': name})
+
+ self._serial_procs.append(proc)
+
+ except OperationFailed:
+ raise
+
except Exception as e:
wok_log.error(e.message)
raise OperationFailed("KCHVM0077E", {'name': name})
diff --git a/serialconsole.py b/serialconsole.py
index 6ace1a6..0c47c3e 100644
--- a/serialconsole.py
+++ b/serialconsole.py
@@ -87,6 +87,35 @@ class SocketServer(Process):
"""
self.listen()
+ def _is_vm_listening_serial(self, console):
+ """Checks if the guest is listening (reading/writing) to the
serial
+ console.
+ """
+ is_listening = []
+
+ def _test_output(stream, event, opaque):
+ is_listening.append(1)
+
+ def _event_loop():
+ while not is_listening:
+ libvirt.virEventRunDefaultImpl()
+
+ console.eventAddCallback(libvirt.VIR_STREAM_EVENT_READABLE,
+ _test_output,
+ None)
+ libvirt_loop = threading.Thread(target=_event_loop)
+ libvirt_loop.start()
+
+ console.send("\n")
+ libvirt_loop.join(1)
+
+ if not libvirt_loop.is_alive():
+ console.eventRemoveCallback()
+ return True
+
+ console.eventRemoveCallback()
+ return False
+
def _send_to_client(self, stream, event, opaque):
"""Handles libvirt stream readable events.
@@ -139,6 +168,14 @@ class SocketServer(Process):
console = None
try:
console = guest.get_console()
+ if console is None:
+ wok_log.error('[%s] Cannot get the console to %s',
+ self.name, self._guest_name)
+ return
+
+ if not self._is_vm_listening_serial(console):
+ sys.exit(1)
+
self._listen(guest, console)
# clear resources aquired when the process is killed
@@ -190,9 +227,8 @@ class SocketServer(Process):
data = client.recv(1024)
except Exception as e:
- wok_log.info('[%s] Client %s disconnected from %s: %s',
- self.name, str(client_addr), self._guest_name,
- e.message)
+ wok_log.info('[%s] Client disconnected from %s: %s',
+ self.name, self._guest_name, e.message)
break
if not data or data == CTRL_Q:
@@ -282,14 +318,14 @@ class LibvirtGuest(object):
# guest
-def main(guest_name, URI):
+def main(guest_name, URI='qemu:///system'):
"""Main entry point to create a socket server.
Starts a new socket server to listen messages to/from the guest.
"""
server = None
try:
- server = SocketServer(guest_name, URI='qemu:///system')
+ server = SocketServer(guest_name, URI)
except Exception as e:
wok_log.error('Cannot create the socket server: %s', e.message)
@@ -303,6 +339,15 @@ if __name__ == '__main__':
"""Executes a stand alone instance of the socket server.
This may be useful for testing/debugging.
+
+ In order to debug, add the path before importing kimchi/wok code:
+ sys.path.append('../../../')
+
+ start the server:
+ python serialconsole.py <guest_name>
+
+ and, on another terminal, run:
+ netcat -U /run/wok/<guest_name>
"""
argc = len(sys.argv)
if argc != 2:
diff --git a/tests/test_model.py b/tests/test_model.py
index 0229b3d..fc7ee10 100644
--- a/tests/test_model.py
+++ b/tests/test_model.py
@@ -341,8 +341,8 @@ class ModelTests(unittest.TestCase):
inst.vm_start('kimchi-serial')
rollback.prependDefer(inst.vm_poweroff, 'kimchi-serial')
- inst.vm_serial('kimchi-serial')
- self.assertTrue(os.path.exists('/tmp/kimchi-serial'))
+ with self.assertRaises(OperationFailed):
+ inst.vm_serial('kimchi-serial')
inst.template_delete('test')
diff --git a/ui/serial/html/serial.html b/ui/serial/html/serial.html
index 8010a59..e58a282 100644
--- a/ui/serial/html/serial.html
+++ b/ui/serial/html/serial.html
@@ -79,10 +79,6 @@
socket.send(window.btoa(data));
});
- socket.onopen = function() {
- socket.send(window.btoa('\n'));
- };
-
socket.onmessage = function(event) {
var message = event.data;
term.write(window.atob(message));
--
1.9.1