[Kimchi-devel] [PATCH] [Kimchi 4/4] Check if guest is listening to serial before connecting to it

Aline Manera alinefm at linux.vnet.ibm.com
Fri Mar 11 13:49:57 UTC 2016


Reviewed-by: Aline Manera <alinefm at linux.vnet.ibm.com>

On 03/09/2016 04:04 PM, Jose Ricardo Ziviani wrote:
>   - 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 at 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));




More information about the Kimchi-devel mailing list