[Kimchi-devel] [PATCH v2] [Kimchi 4/4] Check if guest is listening to serial before connecting to it
Jose Ricardo Ziviani
joserz at linux.vnet.ibm.com
Wed Mar 16 20:44:21 UTC 2016
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 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 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
More information about the Kimchi-devel
mailing list