On 16-03-2016 17:25, Ramon Medeiros wrote:
On 03/16/2016 02:25 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(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 7abae66..013fdb7 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 %(dir)s
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 e86f8b8..03ffdae 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 349689f..d290798 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)
Using Thread is safe? No exception can be raise?
Libvirt recommends to run the eventloop in a thread, otherwise it is
almost impractical since virEventRunDefaultImpl will block from some
microseconds but time enough to make _is_vm_listening_serial fails.
In this case, even if virEventRunDefaultImpl blocks forever, or even if
it fails for any unexpected reason that forces the thread to keep alive,
the code
libvirt_loop.join(1)
will try to wait for it for 1 second, then will exit(1) the process.
Note that this code already runs in a separate process (it's a new
python interpretor, totally isolated from kimchi). It means that the
python interpretor will be killed with its threads together.
> + 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
> @@ -191,9 +228,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:
> @@ -283,14 +319,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)
> @@ -304,6 +340,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/<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));
--
Jose Ricardo Ziviani
-----------------------------
Software Engineer
Linux Technology Center - IBM