[PATCH] [Kimchi 0/4] Fix issues on web serial console

This patchset contains some issues found in the web serial console code as well as some small improvements. Jose Ricardo Ziviani (4): Move unix socket files from /tmp to /run/wok Improve log messages printed by the serial console Make serial console timeout configurable Check if guest is listening to serial before connecting to it config.py.in | 3 ++ i18n.py | 2 + model/vms.py | 26 ++++++++-- serialconsole.py | 115 ++++++++++++++++++++++++++++++--------------- ui/serial/html/serial.html | 4 -- 5 files changed, 105 insertions(+), 45 deletions(-) -- 1.9.1

- /run is a temporary fs (tmpfs) more adequate to store this kind of file. Signed-off-by: Jose Ricardo Ziviani <joserz@linux.vnet.ibm.com> --- i18n.py | 1 + model/vms.py | 11 ++++++++++- serialconsole.py | 5 +++-- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/i18n.py b/i18n.py index 707b74b..e219857 100644 --- a/i18n.py +++ b/i18n.py @@ -135,6 +135,7 @@ messages = { "KCHVM0078E": _("Memory or Maximum Memory value is higher than amount supported by the host: %(memHost)sMiB."), "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."), "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 195c879..cd77575 100644 --- a/model/vms.py +++ b/model/vms.py @@ -1467,8 +1467,17 @@ class VMModel(object): if not self._vm_check_serial(name): raise OperationFailed("KCHVM0076E", {'name': name}) + if not os.path.isdir(serialconsole.BASE_DIRECTORY): + try: + os.mkdir(serialconsole.BASE_DIRECTORY) + + except OSError as e: + wok_log.error(e.message) + raise OperationFailed("KCHVM0081E") + websocket.add_proxy_token(name.encode('utf-8')+'-console', - '/tmp/%s' % name.encode('utf-8'), True) + os.path.join(serialconsole.BASE_DIRECTORY, + name.encode('utf-8')), True) try: self._serial_procs.append( diff --git a/serialconsole.py b/serialconsole.py index 1eb48b1..47f4c2d 100644 --- a/serialconsole.py +++ b/serialconsole.py @@ -35,6 +35,7 @@ from wok.plugins.kimchi import model SOCKET_QUEUE_BACKLOG = 0 DEFAULT_TIMEOUT = 120 # seconds CTRL_Q = '\x11' +BASE_DIRECTORY = '/run/wok' class SocketServer(Process): @@ -61,13 +62,13 @@ class SocketServer(Process): def __init__(self, guest_name, URI): """Constructs a unix socket server. - Listens to connections on /tmp/<guest name>. + Listens to connections on /run/wok/<guest name>. """ Process.__init__(self) self._guest_name = guest_name self._uri = URI - self._server_addr = '/tmp/%s' % guest_name + self._server_addr = os.path.join(BASE_DIRECTORY, guest_name) if os.path.exists(self._server_addr): wok_log.error('Cannot connect to %s due to an existing ' 'connection', guest_name) -- 1.9.1

- /run is a temporary fs (tmpfs) more adequate to store this kind of file.
Signed-off-by: Jose Ricardo Ziviani <joserz@linux.vnet.ibm.com> --- i18n.py | 1 + model/vms.py | 11 ++++++++++- serialconsole.py | 5 +++-- 3 files changed, 14 insertions(+), 3 deletions(-)
diff --git a/i18n.py b/i18n.py index 707b74b..e219857 100644 --- a/i18n.py +++ b/i18n.py @@ -135,6 +135,7 @@ messages = { "KCHVM0078E": _("Memory or Maximum Memory value is higher than amount supported by the host: %(memHost)sMiB."), "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."), It would be nice to not hardcode the path here. Better use %(dir)s and
On 09-03-2016 16:04, Jose Ricardo Ziviani wrote: pass BASE_DIRECTORY in raise command.
"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 195c879..cd77575 100644 --- a/model/vms.py +++ b/model/vms.py @@ -1467,8 +1467,17 @@ class VMModel(object): if not self._vm_check_serial(name): raise OperationFailed("KCHVM0076E", {'name': name})
+ if not os.path.isdir(serialconsole.BASE_DIRECTORY): + try: + os.mkdir(serialconsole.BASE_DIRECTORY) + + except OSError as e: + wok_log.error(e.message) + raise OperationFailed("KCHVM0081E") + websocket.add_proxy_token(name.encode('utf-8')+'-console', - '/tmp/%s' % name.encode('utf-8'), True) + os.path.join(serialconsole.BASE_DIRECTORY, + name.encode('utf-8')), True)
try: self._serial_procs.append( diff --git a/serialconsole.py b/serialconsole.py index 1eb48b1..47f4c2d 100644 --- a/serialconsole.py +++ b/serialconsole.py @@ -35,6 +35,7 @@ from wok.plugins.kimchi import model SOCKET_QUEUE_BACKLOG = 0 DEFAULT_TIMEOUT = 120 # seconds CTRL_Q = '\x11' +BASE_DIRECTORY = '/run/wok'
class SocketServer(Process): @@ -61,13 +62,13 @@ class SocketServer(Process): def __init__(self, guest_name, URI): """Constructs a unix socket server.
- Listens to connections on /tmp/<guest name>. + Listens to connections on /run/wok/<guest name>. """ Process.__init__(self)
self._guest_name = guest_name self._uri = URI - self._server_addr = '/tmp/%s' % guest_name + self._server_addr = os.path.join(BASE_DIRECTORY, guest_name) if os.path.exists(self._server_addr): wok_log.error('Cannot connect to %s due to an existing ' 'connection', guest_name)
-- Lucio Correia Software Engineer IBM LTC Brazil

On 03/09/2016 04:04 PM, Jose Ricardo Ziviani wrote:
- /run is a temporary fs (tmpfs) more adequate to store this kind of file.
Signed-off-by: Jose Ricardo Ziviani <joserz@linux.vnet.ibm.com> --- i18n.py | 1 + model/vms.py | 11 ++++++++++- serialconsole.py | 5 +++-- 3 files changed, 14 insertions(+), 3 deletions(-)
diff --git a/i18n.py b/i18n.py index 707b74b..e219857 100644 --- a/i18n.py +++ b/i18n.py @@ -135,6 +135,7 @@ messages = { "KCHVM0078E": _("Memory or Maximum Memory value is higher than amount supported by the host: %(memHost)sMiB."), "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."),
If you created it on run time, when uninstalling Kimchi it will remain in the system.
"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 195c879..cd77575 100644 --- a/model/vms.py +++ b/model/vms.py @@ -1467,8 +1467,17 @@ class VMModel(object): if not self._vm_check_serial(name): raise OperationFailed("KCHVM0076E", {'name': name})
+ if not os.path.isdir(serialconsole.BASE_DIRECTORY): + try: + os.mkdir(serialconsole.BASE_DIRECTORY) + + except OSError as e:
+ wok_log.error(e.message) + raise OperationFailed("KCHVM0081E")
Every exception raised is always logged into the log file so you don't need to do that.
+ websocket.add_proxy_token(name.encode('utf-8')+'-console', - '/tmp/%s' % name.encode('utf-8'), True) + os.path.join(serialconsole.BASE_DIRECTORY, + name.encode('utf-8')), True)
try: self._serial_procs.append( diff --git a/serialconsole.py b/serialconsole.py index 1eb48b1..47f4c2d 100644 --- a/serialconsole.py +++ b/serialconsole.py @@ -35,6 +35,7 @@ from wok.plugins.kimchi import model SOCKET_QUEUE_BACKLOG = 0 DEFAULT_TIMEOUT = 120 # seconds CTRL_Q = '\x11' +BASE_DIRECTORY = '/run/wok'
class SocketServer(Process): @@ -61,13 +62,13 @@ class SocketServer(Process): def __init__(self, guest_name, URI): """Constructs a unix socket server.
- Listens to connections on /tmp/<guest name>. + Listens to connections on /run/wok/<guest name>. """ Process.__init__(self)
self._guest_name = guest_name self._uri = URI - self._server_addr = '/tmp/%s' % guest_name + self._server_addr = os.path.join(BASE_DIRECTORY, guest_name) if os.path.exists(self._server_addr): wok_log.error('Cannot connect to %s due to an existing ' 'connection', guest_name)

On 03/10/2016 03:16 PM, Aline Manera wrote:
On 03/09/2016 04:04 PM, Jose Ricardo Ziviani wrote:
- /run is a temporary fs (tmpfs) more adequate to store this kind of file.
Signed-off-by: Jose Ricardo Ziviani <joserz@linux.vnet.ibm.com> --- i18n.py | 1 + model/vms.py | 11 ++++++++++- serialconsole.py | 5 +++-- 3 files changed, 14 insertions(+), 3 deletions(-)
diff --git a/i18n.py b/i18n.py index 707b74b..e219857 100644 --- a/i18n.py +++ b/i18n.py @@ -135,6 +135,7 @@ messages = { "KCHVM0078E": _("Memory or Maximum Memory value is higher than amount supported by the host: %(memHost)sMiB."), "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."),
If you created it on run time, when uninstalling Kimchi it will remain in the system.
"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 195c879..cd77575 100644 --- a/model/vms.py +++ b/model/vms.py @@ -1467,8 +1467,17 @@ class VMModel(object): if not self._vm_check_serial(name): raise OperationFailed("KCHVM0076E", {'name': name})
+ if not os.path.isdir(serialconsole.BASE_DIRECTORY): + try: + os.mkdir(serialconsole.BASE_DIRECTORY) + + except OSError as e:
+ wok_log.error(e.message) + raise OperationFailed("KCHVM0081E")
Every exception raised is always logged into the log file so you don't need to do that.
If I understood it right, the wok_log.error() is logging the exact OSError message into wok_log/terminal for debugging, while the OperationFailed is throwing an user-friendly error message to be shown by the UI. Perhaps it's a matter of opinion. In my opinion this is a valid case to have both.
+ websocket.add_proxy_token(name.encode('utf-8')+'-console', - '/tmp/%s' % name.encode('utf-8'), True) + os.path.join(serialconsole.BASE_DIRECTORY, + name.encode('utf-8')), True)
try: self._serial_procs.append( diff --git a/serialconsole.py b/serialconsole.py index 1eb48b1..47f4c2d 100644 --- a/serialconsole.py +++ b/serialconsole.py @@ -35,6 +35,7 @@ from wok.plugins.kimchi import model SOCKET_QUEUE_BACKLOG = 0 DEFAULT_TIMEOUT = 120 # seconds CTRL_Q = '\x11' +BASE_DIRECTORY = '/run/wok'
class SocketServer(Process): @@ -61,13 +62,13 @@ class SocketServer(Process): def __init__(self, guest_name, URI): """Constructs a unix socket server.
- Listens to connections on /tmp/<guest name>. + Listens to connections on /run/wok/<guest name>. """ Process.__init__(self)
self._guest_name = guest_name self._uri = URI - self._server_addr = '/tmp/%s' % guest_name + self._server_addr = os.path.join(BASE_DIRECTORY, guest_name) if os.path.exists(self._server_addr): wok_log.error('Cannot connect to %s due to an existing ' 'connection', guest_name)
_______________________________________________ Kimchi-devel mailing list Kimchi-devel@ovirt.org http://lists.ovirt.org/mailman/listinfo/kimchi-devel

On 03/10/2016 03:21 PM, Daniel Henrique Barboza wrote:
On 03/10/2016 03:16 PM, Aline Manera wrote:
On 03/09/2016 04:04 PM, Jose Ricardo Ziviani wrote:
- /run is a temporary fs (tmpfs) more adequate to store this kind of file.
Signed-off-by: Jose Ricardo Ziviani <joserz@linux.vnet.ibm.com> --- i18n.py | 1 + model/vms.py | 11 ++++++++++- serialconsole.py | 5 +++-- 3 files changed, 14 insertions(+), 3 deletions(-)
diff --git a/i18n.py b/i18n.py index 707b74b..e219857 100644 --- a/i18n.py +++ b/i18n.py @@ -135,6 +135,7 @@ messages = { "KCHVM0078E": _("Memory or Maximum Memory value is higher than amount supported by the host: %(memHost)sMiB."), "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."),
If you created it on run time, when uninstalling Kimchi it will remain in the system.
"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 195c879..cd77575 100644 --- a/model/vms.py +++ b/model/vms.py @@ -1467,8 +1467,17 @@ class VMModel(object): if not self._vm_check_serial(name): raise OperationFailed("KCHVM0076E", {'name': name})
+ if not os.path.isdir(serialconsole.BASE_DIRECTORY): + try: + os.mkdir(serialconsole.BASE_DIRECTORY) + + except OSError as e:
+ wok_log.error(e.message) + raise OperationFailed("KCHVM0081E")
Every exception raised is always logged into the log file so you don't need to do that.
If I understood it right, the wok_log.error() is logging the exact OSError message into wok_log/terminal for debugging, while the OperationFailed is throwing an user-friendly error message to be shown by the UI.
Perhaps it's a matter of opinion. In my opinion this is a valid case to have both.
My point is: when calling any WokException the error message will be sent out as the request response AND be logged into wok_log/terminal for debugging. You can check wok/src/wok/exception.py to confirm it. So in the above case, 2 messages will be logged in the wok_log/terminal which does not make sense IMO.
+ websocket.add_proxy_token(name.encode('utf-8')+'-console', - '/tmp/%s' % name.encode('utf-8'), True) + os.path.join(serialconsole.BASE_DIRECTORY, + name.encode('utf-8')), True)
try: self._serial_procs.append( diff --git a/serialconsole.py b/serialconsole.py index 1eb48b1..47f4c2d 100644 --- a/serialconsole.py +++ b/serialconsole.py @@ -35,6 +35,7 @@ from wok.plugins.kimchi import model SOCKET_QUEUE_BACKLOG = 0 DEFAULT_TIMEOUT = 120 # seconds CTRL_Q = '\x11' +BASE_DIRECTORY = '/run/wok'
class SocketServer(Process): @@ -61,13 +62,13 @@ class SocketServer(Process): def __init__(self, guest_name, URI): """Constructs a unix socket server.
- Listens to connections on /tmp/<guest name>. + Listens to connections on /run/wok/<guest name>. """ Process.__init__(self)
self._guest_name = guest_name self._uri = URI - self._server_addr = '/tmp/%s' % guest_name + self._server_addr = os.path.join(BASE_DIRECTORY, guest_name) if os.path.exists(self._server_addr): wok_log.error('Cannot connect to %s due to an existing ' 'connection', guest_name)
_______________________________________________ Kimchi-devel mailing list Kimchi-devel@ovirt.org http://lists.ovirt.org/mailman/listinfo/kimchi-devel
_______________________________________________ Kimchi-devel mailing list Kimchi-devel@ovirt.org http://lists.ovirt.org/mailman/listinfo/kimchi-devel

On 03/11/2016 10:43 AM, Aline Manera wrote:
On 03/10/2016 03:21 PM, Daniel Henrique Barboza wrote:
On 03/10/2016 03:16 PM, Aline Manera wrote:
On 03/09/2016 04:04 PM, Jose Ricardo Ziviani wrote:
- /run is a temporary fs (tmpfs) more adequate to store this kind of file.
Signed-off-by: Jose Ricardo Ziviani <joserz@linux.vnet.ibm.com> --- i18n.py | 1 + model/vms.py | 11 ++++++++++- serialconsole.py | 5 +++-- 3 files changed, 14 insertions(+), 3 deletions(-)
diff --git a/i18n.py b/i18n.py index 707b74b..e219857 100644 --- a/i18n.py +++ b/i18n.py @@ -135,6 +135,7 @@ messages = { "KCHVM0078E": _("Memory or Maximum Memory value is higher than amount supported by the host: %(memHost)sMiB."), "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."),
If you created it on run time, when uninstalling Kimchi it will remain in the system.
"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 195c879..cd77575 100644 --- a/model/vms.py +++ b/model/vms.py @@ -1467,8 +1467,17 @@ class VMModel(object): if not self._vm_check_serial(name): raise OperationFailed("KCHVM0076E", {'name': name})
+ if not os.path.isdir(serialconsole.BASE_DIRECTORY): + try: + os.mkdir(serialconsole.BASE_DIRECTORY) + + except OSError as e:
+ wok_log.error(e.message) + raise OperationFailed("KCHVM0081E")
Every exception raised is always logged into the log file so you don't need to do that.
If I understood it right, the wok_log.error() is logging the exact OSError message into wok_log/terminal for debugging, while the OperationFailed is throwing an user-friendly error message to be shown by the UI.
Perhaps it's a matter of opinion. In my opinion this is a valid case to have both.
My point is: when calling any WokException the error message will be sent out as the request response AND be logged into wok_log/terminal for debugging. You can check wok/src/wok/exception.py to confirm it.
So in the above case, 2 messages will be logged in the wok_log/terminal which does not make sense IMO.
I see you point. Problem is that using only OperationFailed() in the code above we'll lose debug information because the exception message won't be logged anywhere (KCHVM0081E isn't receiving the error message as argument). If you want to avoid flooding the log with messages while not losing debug information, KCHVM0081E will need to be changed to pass along the OSError message to the UI too. Daniel
+ websocket.add_proxy_token(name.encode('utf-8')+'-console', - '/tmp/%s' % name.encode('utf-8'), True) + os.path.join(serialconsole.BASE_DIRECTORY, + name.encode('utf-8')), True)
try: self._serial_procs.append( diff --git a/serialconsole.py b/serialconsole.py index 1eb48b1..47f4c2d 100644 --- a/serialconsole.py +++ b/serialconsole.py @@ -35,6 +35,7 @@ from wok.plugins.kimchi import model SOCKET_QUEUE_BACKLOG = 0 DEFAULT_TIMEOUT = 120 # seconds CTRL_Q = '\x11' +BASE_DIRECTORY = '/run/wok'
class SocketServer(Process): @@ -61,13 +62,13 @@ class SocketServer(Process): def __init__(self, guest_name, URI): """Constructs a unix socket server.
- Listens to connections on /tmp/<guest name>. + Listens to connections on /run/wok/<guest name>. """ Process.__init__(self)
self._guest_name = guest_name self._uri = URI - self._server_addr = '/tmp/%s' % guest_name + self._server_addr = os.path.join(BASE_DIRECTORY, guest_name) if os.path.exists(self._server_addr): wok_log.error('Cannot connect to %s due to an existing ' 'connection', guest_name)
_______________________________________________ Kimchi-devel mailing list Kimchi-devel@ovirt.org http://lists.ovirt.org/mailman/listinfo/kimchi-devel
_______________________________________________ Kimchi-devel mailing list Kimchi-devel@ovirt.org http://lists.ovirt.org/mailman/listinfo/kimchi-devel

On 03/11/2016 10:50 AM, Daniel Henrique Barboza wrote:
On 03/11/2016 10:43 AM, Aline Manera wrote:
On 03/10/2016 03:21 PM, Daniel Henrique Barboza wrote:
On 03/10/2016 03:16 PM, Aline Manera wrote:
On 03/09/2016 04:04 PM, Jose Ricardo Ziviani wrote:
- /run is a temporary fs (tmpfs) more adequate to store this kind of file.
Signed-off-by: Jose Ricardo Ziviani <joserz@linux.vnet.ibm.com> --- i18n.py | 1 + model/vms.py | 11 ++++++++++- serialconsole.py | 5 +++-- 3 files changed, 14 insertions(+), 3 deletions(-)
diff --git a/i18n.py b/i18n.py index 707b74b..e219857 100644 --- a/i18n.py +++ b/i18n.py @@ -135,6 +135,7 @@ messages = { "KCHVM0078E": _("Memory or Maximum Memory value is higher than amount supported by the host: %(memHost)sMiB."), "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."),
If you created it on run time, when uninstalling Kimchi it will remain in the system.
"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 195c879..cd77575 100644 --- a/model/vms.py +++ b/model/vms.py @@ -1467,8 +1467,17 @@ class VMModel(object): if not self._vm_check_serial(name): raise OperationFailed("KCHVM0076E", {'name': name})
+ if not os.path.isdir(serialconsole.BASE_DIRECTORY): + try: + os.mkdir(serialconsole.BASE_DIRECTORY) + + except OSError as e:
+ wok_log.error(e.message) + raise OperationFailed("KCHVM0081E")
Every exception raised is always logged into the log file so you don't need to do that.
If I understood it right, the wok_log.error() is logging the exact OSError message into wok_log/terminal for debugging, while the OperationFailed is throwing an user-friendly error message to be shown by the UI.
Perhaps it's a matter of opinion. In my opinion this is a valid case to have both.
My point is: when calling any WokException the error message will be sent out as the request response AND be logged into wok_log/terminal for debugging. You can check wok/src/wok/exception.py to confirm it.
So in the above case, 2 messages will be logged in the wok_log/terminal which does not make sense IMO.
I see you point. Problem is that using only OperationFailed() in the code above we'll lose debug information because the exception message won't be logged anywhere (KCHVM0081E isn't receiving the error message as argument).
If you want to avoid flooding the log with messages while not losing debug information, KCHVM0081E will need to be changed to pass along the OSError message to the UI too.
Oh! I got your point now! I thought the OperationFailed was also returning the the OSError message. So I agree the OperationFailed should also return the OSError message to UI.
Daniel
+ websocket.add_proxy_token(name.encode('utf-8')+'-console', - '/tmp/%s' % name.encode('utf-8'), True) + os.path.join(serialconsole.BASE_DIRECTORY, + name.encode('utf-8')), True)
try: self._serial_procs.append( diff --git a/serialconsole.py b/serialconsole.py index 1eb48b1..47f4c2d 100644 --- a/serialconsole.py +++ b/serialconsole.py @@ -35,6 +35,7 @@ from wok.plugins.kimchi import model SOCKET_QUEUE_BACKLOG = 0 DEFAULT_TIMEOUT = 120 # seconds CTRL_Q = '\x11' +BASE_DIRECTORY = '/run/wok'
class SocketServer(Process): @@ -61,13 +62,13 @@ class SocketServer(Process): def __init__(self, guest_name, URI): """Constructs a unix socket server.
- Listens to connections on /tmp/<guest name>. + Listens to connections on /run/wok/<guest name>. """ Process.__init__(self)
self._guest_name = guest_name self._uri = URI - self._server_addr = '/tmp/%s' % guest_name + self._server_addr = os.path.join(BASE_DIRECTORY, guest_name) if os.path.exists(self._server_addr): wok_log.error('Cannot connect to %s due to an existing ' 'connection', guest_name)
_______________________________________________ Kimchi-devel mailing list Kimchi-devel@ovirt.org http://lists.ovirt.org/mailman/listinfo/kimchi-devel
_______________________________________________ Kimchi-devel mailing list Kimchi-devel@ovirt.org http://lists.ovirt.org/mailman/listinfo/kimchi-devel

- Include the process name in the log to highlight messages from different process and thus make is easier to debug. Signed-off-by: Jose Ricardo Ziviani <joserz@linux.vnet.ibm.com> --- serialconsole.py | 56 ++++++++++++++++++++++++++------------------------------ 1 file changed, 26 insertions(+), 30 deletions(-) diff --git a/serialconsole.py b/serialconsole.py index 47f4c2d..74f6b8a 100644 --- a/serialconsole.py +++ b/serialconsole.py @@ -27,9 +27,8 @@ import time from multiprocessing import Process - -from wok.utils import wok_log from wok.plugins.kimchi import model +from wok.utils import wok_log SOCKET_QUEUE_BACKLOG = 0 @@ -70,8 +69,6 @@ class SocketServer(Process): self._uri = URI self._server_addr = os.path.join(BASE_DIRECTORY, guest_name) if os.path.exists(self._server_addr): - wok_log.error('Cannot connect to %s due to an existing ' - 'connection', guest_name) raise RuntimeError('There is an existing connection to %s' % guest_name) @@ -82,7 +79,8 @@ class SocketServer(Process): 1) self._socket.bind(self._server_addr) self._socket.listen(SOCKET_QUEUE_BACKLOG) - wok_log.info('socket server to guest %s created', guest_name) + wok_log.info('[%s] socket server to guest %s created', self.name, + guest_name) def run(self): """Implements customized run method from Process. @@ -98,7 +96,8 @@ class SocketServer(Process): data = stream.recv(1024) except Exception as e: - wok_log.info('Error when reading from console: %s', e.message) + wok_log.info('[%s] Error when reading from console: %s', + self.name, e.message) return # return if no data received or client socket(opaque) is not valid @@ -125,11 +124,11 @@ class SocketServer(Process): """ libvirt.virEventRegisterDefaultImpl() try: - guest = LibvirtGuest(self._guest_name, self._uri) + guest = LibvirtGuest(self._guest_name, self._uri, self.name) except Exception as e: - wok_log.error('Cannot open the guest %s due to %s', - self._guest_name, e.message) + wok_log.error('[%s] Cannot open the guest %s due to %s', + self.name, self._guest_name, e.message) self._socket.close() sys.exit(1) @@ -147,8 +146,8 @@ class SocketServer(Process): pass finally: - wok_log.info("Shutting down the socket server to %s console", - self._guest_name) + wok_log.info("[%s] Shutting down the socket server to %s console", + self.name, self._guest_name) self._socket.close() if os.path.exists(self._server_addr): os.unlink(self._server_addr) @@ -157,7 +156,8 @@ class SocketServer(Process): console.eventRemoveCallback() except Exception as e: - wok_log.info('Callback is probably removed: %s', e.message) + wok_log.info('[%s] Callback is probably removed: %s', + self.name, e.message) guest.close() @@ -171,8 +171,7 @@ class SocketServer(Process): """ client, client_addr = self._socket.accept() client.settimeout(DEFAULT_TIMEOUT) - wok_log.info('Client %s connected to %s', - str(client_addr), + wok_log.info('[%s] Client connected to %s', self.name, self._guest_name) # register the callback to receive any data from the console @@ -191,9 +190,8 @@ class SocketServer(Process): data = client.recv(1024) except Exception as e: - wok_log.info('Client %s disconnected from %s: %s', - str(client_addr), - self._guest_name, + wok_log.info('[%s] Client %s disconnected from %s: %s', + self.name, str(client_addr), self._guest_name, e.message) break @@ -206,8 +204,8 @@ class SocketServer(Process): console.send(data) except: - wok_log.info('Console of %s is not accessible', - self._guest_name) + wok_log.info('[%s] Console of %s is not accessible', + self.name, self._guest_name) break # clear used resources when the connection is closed and, if possible, @@ -222,26 +220,25 @@ class SocketServer(Process): class LibvirtGuest(object): - def __init__(self, guest_name, uri): + def __init__(self, guest_name, uri, process_name): """ Constructs a guest object that opens a connection to libvirt and searchs for a particular guest, provided by the caller. """ + self._proc_name = process_name try: libvirt = model.libvirtconnection.LibvirtConnection(uri) self._guest = model.vms.VMModel.get_vm(guest_name, libvirt) except Exception as e: - wok_log.error('Cannot open guest %s: %s', guest_name, e.message) + wok_log.error('[%s] Cannot open guest %s: %s', self._proc_name, + guest_name, e.message) raise self._libvirt = libvirt.get() self._name = guest_name self._stream = None - def get_name(self): - return self._name - def is_running(self): """ Checks if this guest is currently in a running state. @@ -258,8 +255,8 @@ class LibvirtGuest(object): # guest must be in a running state to get its console counter = 10 while not self.is_running(): - wok_log.info('Guest %s is not running, waiting for it', - self._name) + wok_log.info('[%s] Guest %s is not running, waiting for it', + self._proc_name, self._name) counter -= 1 if counter <= 0: @@ -269,8 +266,8 @@ class LibvirtGuest(object): # attach a stream in the guest console so we can read from/write to it if self._stream is None: - wok_log.info('Opening the console for guest %s', - self._name) + wok_log.info('[%s] Opening the console for guest %s', + self._proc_name, self._name) self._stream = self._libvirt.newStream(libvirt.VIR_STREAM_NONBLOCK) self._guest.openConsole(None, self._stream, @@ -295,8 +292,7 @@ def main(guest_name, URI): server = SocketServer(guest_name, URI='qemu:///system') except Exception as e: - wok_log.error('Cannot create the socket server for %s due to %s', - guest_name, e.message) + wok_log.error('Cannot create the socket server: %s', e.message) raise server.start() -- 1.9.1

Reviewed-By: Lucio Correia <luciojhc@linux.vnet.ibm.com> On 09-03-2016 16:04, Jose Ricardo Ziviani wrote:
- Include the process name in the log to highlight messages from different process and thus make is easier to debug.
Signed-off-by: Jose Ricardo Ziviani <joserz@linux.vnet.ibm.com> --- serialconsole.py | 56 ++++++++++++++++++++++++++------------------------------ 1 file changed, 26 insertions(+), 30 deletions(-)
diff --git a/serialconsole.py b/serialconsole.py index 47f4c2d..74f6b8a 100644 --- a/serialconsole.py +++ b/serialconsole.py @@ -27,9 +27,8 @@ import time
from multiprocessing import Process
- -from wok.utils import wok_log from wok.plugins.kimchi import model +from wok.utils import wok_log
SOCKET_QUEUE_BACKLOG = 0 @@ -70,8 +69,6 @@ class SocketServer(Process): self._uri = URI self._server_addr = os.path.join(BASE_DIRECTORY, guest_name) if os.path.exists(self._server_addr): - wok_log.error('Cannot connect to %s due to an existing ' - 'connection', guest_name) raise RuntimeError('There is an existing connection to %s' % guest_name)
@@ -82,7 +79,8 @@ class SocketServer(Process): 1) self._socket.bind(self._server_addr) self._socket.listen(SOCKET_QUEUE_BACKLOG) - wok_log.info('socket server to guest %s created', guest_name) + wok_log.info('[%s] socket server to guest %s created', self.name, + guest_name)
def run(self): """Implements customized run method from Process. @@ -98,7 +96,8 @@ class SocketServer(Process): data = stream.recv(1024)
except Exception as e: - wok_log.info('Error when reading from console: %s', e.message) + wok_log.info('[%s] Error when reading from console: %s', + self.name, e.message) return
# return if no data received or client socket(opaque) is not valid @@ -125,11 +124,11 @@ class SocketServer(Process): """ libvirt.virEventRegisterDefaultImpl() try: - guest = LibvirtGuest(self._guest_name, self._uri) + guest = LibvirtGuest(self._guest_name, self._uri, self.name)
except Exception as e: - wok_log.error('Cannot open the guest %s due to %s', - self._guest_name, e.message) + wok_log.error('[%s] Cannot open the guest %s due to %s', + self.name, self._guest_name, e.message) self._socket.close() sys.exit(1)
@@ -147,8 +146,8 @@ class SocketServer(Process): pass
finally: - wok_log.info("Shutting down the socket server to %s console", - self._guest_name) + wok_log.info("[%s] Shutting down the socket server to %s console", + self.name, self._guest_name) self._socket.close() if os.path.exists(self._server_addr): os.unlink(self._server_addr) @@ -157,7 +156,8 @@ class SocketServer(Process): console.eventRemoveCallback()
except Exception as e: - wok_log.info('Callback is probably removed: %s', e.message) + wok_log.info('[%s] Callback is probably removed: %s', + self.name, e.message)
guest.close()
@@ -171,8 +171,7 @@ class SocketServer(Process): """ client, client_addr = self._socket.accept() client.settimeout(DEFAULT_TIMEOUT) - wok_log.info('Client %s connected to %s', - str(client_addr), + wok_log.info('[%s] Client connected to %s', self.name, self._guest_name)
# register the callback to receive any data from the console @@ -191,9 +190,8 @@ class SocketServer(Process): data = client.recv(1024)
except Exception as e: - wok_log.info('Client %s disconnected from %s: %s', - str(client_addr), - self._guest_name, + wok_log.info('[%s] Client %s disconnected from %s: %s', + self.name, str(client_addr), self._guest_name, e.message) break
@@ -206,8 +204,8 @@ class SocketServer(Process): console.send(data)
except: - wok_log.info('Console of %s is not accessible', - self._guest_name) + wok_log.info('[%s] Console of %s is not accessible', + self.name, self._guest_name) break
# clear used resources when the connection is closed and, if possible, @@ -222,26 +220,25 @@ class SocketServer(Process):
class LibvirtGuest(object):
- def __init__(self, guest_name, uri): + def __init__(self, guest_name, uri, process_name): """ Constructs a guest object that opens a connection to libvirt and searchs for a particular guest, provided by the caller. """ + self._proc_name = process_name try: libvirt = model.libvirtconnection.LibvirtConnection(uri) self._guest = model.vms.VMModel.get_vm(guest_name, libvirt)
except Exception as e: - wok_log.error('Cannot open guest %s: %s', guest_name, e.message) + wok_log.error('[%s] Cannot open guest %s: %s', self._proc_name, + guest_name, e.message) raise
self._libvirt = libvirt.get() self._name = guest_name self._stream = None
- def get_name(self): - return self._name - def is_running(self): """ Checks if this guest is currently in a running state. @@ -258,8 +255,8 @@ class LibvirtGuest(object): # guest must be in a running state to get its console counter = 10 while not self.is_running(): - wok_log.info('Guest %s is not running, waiting for it', - self._name) + wok_log.info('[%s] Guest %s is not running, waiting for it', + self._proc_name, self._name)
counter -= 1 if counter <= 0: @@ -269,8 +266,8 @@ class LibvirtGuest(object):
# attach a stream in the guest console so we can read from/write to it if self._stream is None: - wok_log.info('Opening the console for guest %s', - self._name) + wok_log.info('[%s] Opening the console for guest %s', + self._proc_name, self._name) self._stream = self._libvirt.newStream(libvirt.VIR_STREAM_NONBLOCK) self._guest.openConsole(None, self._stream, @@ -295,8 +292,7 @@ def main(guest_name, URI): server = SocketServer(guest_name, URI='qemu:///system')
except Exception as e: - wok_log.error('Cannot create the socket server for %s due to %s', - guest_name, e.message) + wok_log.error('Cannot create the socket server: %s', e.message) raise
server.start()
-- Lucio Correia Software Engineer IBM LTC Brazil

Reviewed-by: Aline Manera <alinefm@linux.vnet.ibm.com> On 03/09/2016 04:04 PM, Jose Ricardo Ziviani wrote:
- Include the process name in the log to highlight messages from different process and thus make is easier to debug.
Signed-off-by: Jose Ricardo Ziviani <joserz@linux.vnet.ibm.com> --- serialconsole.py | 56 ++++++++++++++++++++++++++------------------------------ 1 file changed, 26 insertions(+), 30 deletions(-)
diff --git a/serialconsole.py b/serialconsole.py index 47f4c2d..74f6b8a 100644 --- a/serialconsole.py +++ b/serialconsole.py @@ -27,9 +27,8 @@ import time
from multiprocessing import Process
- -from wok.utils import wok_log from wok.plugins.kimchi import model +from wok.utils import wok_log
SOCKET_QUEUE_BACKLOG = 0 @@ -70,8 +69,6 @@ class SocketServer(Process): self._uri = URI self._server_addr = os.path.join(BASE_DIRECTORY, guest_name) if os.path.exists(self._server_addr): - wok_log.error('Cannot connect to %s due to an existing ' - 'connection', guest_name) raise RuntimeError('There is an existing connection to %s' % guest_name)
@@ -82,7 +79,8 @@ class SocketServer(Process): 1) self._socket.bind(self._server_addr) self._socket.listen(SOCKET_QUEUE_BACKLOG) - wok_log.info('socket server to guest %s created', guest_name) + wok_log.info('[%s] socket server to guest %s created', self.name, + guest_name)
def run(self): """Implements customized run method from Process. @@ -98,7 +96,8 @@ class SocketServer(Process): data = stream.recv(1024)
except Exception as e: - wok_log.info('Error when reading from console: %s', e.message) + wok_log.info('[%s] Error when reading from console: %s', + self.name, e.message) return
# return if no data received or client socket(opaque) is not valid @@ -125,11 +124,11 @@ class SocketServer(Process): """ libvirt.virEventRegisterDefaultImpl() try: - guest = LibvirtGuest(self._guest_name, self._uri) + guest = LibvirtGuest(self._guest_name, self._uri, self.name)
except Exception as e: - wok_log.error('Cannot open the guest %s due to %s', - self._guest_name, e.message) + wok_log.error('[%s] Cannot open the guest %s due to %s', + self.name, self._guest_name, e.message) self._socket.close() sys.exit(1)
@@ -147,8 +146,8 @@ class SocketServer(Process): pass
finally: - wok_log.info("Shutting down the socket server to %s console", - self._guest_name) + wok_log.info("[%s] Shutting down the socket server to %s console", + self.name, self._guest_name) self._socket.close() if os.path.exists(self._server_addr): os.unlink(self._server_addr) @@ -157,7 +156,8 @@ class SocketServer(Process): console.eventRemoveCallback()
except Exception as e: - wok_log.info('Callback is probably removed: %s', e.message) + wok_log.info('[%s] Callback is probably removed: %s', + self.name, e.message)
guest.close()
@@ -171,8 +171,7 @@ class SocketServer(Process): """ client, client_addr = self._socket.accept() client.settimeout(DEFAULT_TIMEOUT) - wok_log.info('Client %s connected to %s', - str(client_addr), + wok_log.info('[%s] Client connected to %s', self.name, self._guest_name)
# register the callback to receive any data from the console @@ -191,9 +190,8 @@ class SocketServer(Process): data = client.recv(1024)
except Exception as e: - wok_log.info('Client %s disconnected from %s: %s', - str(client_addr), - self._guest_name, + wok_log.info('[%s] Client %s disconnected from %s: %s', + self.name, str(client_addr), self._guest_name, e.message) break
@@ -206,8 +204,8 @@ class SocketServer(Process): console.send(data)
except: - wok_log.info('Console of %s is not accessible', - self._guest_name) + wok_log.info('[%s] Console of %s is not accessible', + self.name, self._guest_name) break
# clear used resources when the connection is closed and, if possible, @@ -222,26 +220,25 @@ class SocketServer(Process):
class LibvirtGuest(object):
- def __init__(self, guest_name, uri): + def __init__(self, guest_name, uri, process_name): """ Constructs a guest object that opens a connection to libvirt and searchs for a particular guest, provided by the caller. """ + self._proc_name = process_name try: libvirt = model.libvirtconnection.LibvirtConnection(uri) self._guest = model.vms.VMModel.get_vm(guest_name, libvirt)
except Exception as e: - wok_log.error('Cannot open guest %s: %s', guest_name, e.message) + wok_log.error('[%s] Cannot open guest %s: %s', self._proc_name, + guest_name, e.message) raise
self._libvirt = libvirt.get() self._name = guest_name self._stream = None
- def get_name(self): - return self._name - def is_running(self): """ Checks if this guest is currently in a running state. @@ -258,8 +255,8 @@ class LibvirtGuest(object): # guest must be in a running state to get its console counter = 10 while not self.is_running(): - wok_log.info('Guest %s is not running, waiting for it', - self._name) + wok_log.info('[%s] Guest %s is not running, waiting for it', + self._proc_name, self._name)
counter -= 1 if counter <= 0: @@ -269,8 +266,8 @@ class LibvirtGuest(object):
# attach a stream in the guest console so we can read from/write to it if self._stream is None: - wok_log.info('Opening the console for guest %s', - self._name) + wok_log.info('[%s] Opening the console for guest %s', + self._proc_name, self._name) self._stream = self._libvirt.newStream(libvirt.VIR_STREAM_NONBLOCK) self._guest.openConsole(None, self._stream, @@ -295,8 +292,7 @@ def main(guest_name, URI): server = SocketServer(guest_name, URI='qemu:///system')
except Exception as e: - wok_log.error('Cannot create the socket server for %s due to %s', - guest_name, e.message) + wok_log.error('Cannot create the socket server: %s', e.message) raise
server.start()

- Add a new parameter in config.py to make it easier for users to configure the serial console timeout. Signed-off-by: Jose Ricardo Ziviani <joserz@linux.vnet.ibm.com> --- config.py.in | 3 +++ serialconsole.py | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/config.py.in b/config.py.in index 3c1c13a..0035515 100644 --- a/config.py.in +++ b/config.py.in @@ -36,6 +36,9 @@ __release__ = "@kimchirelease@" # Storage pool constant for read-only pool types READONLY_POOL_TYPE = ['iscsi', 'scsi', 'mpath'] +# Serial console timeout (seconds) +SERIAL_CONSOLE_TIMEOUT = 120 + def get_kimchi_version(): return "-".join([__version__, __release__]) diff --git a/serialconsole.py b/serialconsole.py index 74f6b8a..6ace1a6 100644 --- a/serialconsole.py +++ b/serialconsole.py @@ -28,11 +28,11 @@ import time from multiprocessing import Process from wok.plugins.kimchi import model +from wok.plugins.kimchi.config import SERIAL_CONSOLE_TIMEOUT from wok.utils import wok_log SOCKET_QUEUE_BACKLOG = 0 -DEFAULT_TIMEOUT = 120 # seconds CTRL_Q = '\x11' BASE_DIRECTORY = '/run/wok' @@ -170,7 +170,7 @@ class SocketServer(Process): client console. """ client, client_addr = self._socket.accept() - client.settimeout(DEFAULT_TIMEOUT) + client.settimeout(SERIAL_CONSOLE_TIMEOUT) wok_log.info('[%s] Client connected to %s', self.name, self._guest_name) -- 1.9.1

On 09-03-2016 16:04, Jose Ricardo Ziviani wrote:
- Add a new parameter in config.py to make it easier for users to configure the serial console timeout.
Better use kimchi.conf if user is supposed to change this config.
Signed-off-by: Jose Ricardo Ziviani <joserz@linux.vnet.ibm.com> --- config.py.in | 3 +++ serialconsole.py | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/config.py.in b/config.py.in index 3c1c13a..0035515 100644 --- a/config.py.in +++ b/config.py.in @@ -36,6 +36,9 @@ __release__ = "@kimchirelease@" # Storage pool constant for read-only pool types READONLY_POOL_TYPE = ['iscsi', 'scsi', 'mpath']
+# Serial console timeout (seconds) +SERIAL_CONSOLE_TIMEOUT = 120 +
def get_kimchi_version(): return "-".join([__version__, __release__]) diff --git a/serialconsole.py b/serialconsole.py index 74f6b8a..6ace1a6 100644 --- a/serialconsole.py +++ b/serialconsole.py @@ -28,11 +28,11 @@ import time from multiprocessing import Process
from wok.plugins.kimchi import model +from wok.plugins.kimchi.config import SERIAL_CONSOLE_TIMEOUT from wok.utils import wok_log
SOCKET_QUEUE_BACKLOG = 0 -DEFAULT_TIMEOUT = 120 # seconds CTRL_Q = '\x11' BASE_DIRECTORY = '/run/wok'
@@ -170,7 +170,7 @@ class SocketServer(Process): client console. """ client, client_addr = self._socket.accept() - client.settimeout(DEFAULT_TIMEOUT) + client.settimeout(SERIAL_CONSOLE_TIMEOUT) wok_log.info('[%s] Client connected to %s', self.name, self._guest_name)
-- Lucio Correia Software Engineer IBM LTC Brazil

On 03/10/2016 01:23 PM, Lucio Correia wrote:
On 09-03-2016 16:04, Jose Ricardo Ziviani wrote:
- Add a new parameter in config.py to make it easier for users to configure the serial console timeout.
Better use kimchi.conf if user is supposed to change this config.
Agree.
Signed-off-by: Jose Ricardo Ziviani <joserz@linux.vnet.ibm.com> --- config.py.in | 3 +++ serialconsole.py | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/config.py.in b/config.py.in index 3c1c13a..0035515 100644 --- a/config.py.in +++ b/config.py.in @@ -36,6 +36,9 @@ __release__ = "@kimchirelease@" # Storage pool constant for read-only pool types READONLY_POOL_TYPE = ['iscsi', 'scsi', 'mpath']
+# Serial console timeout (seconds) +SERIAL_CONSOLE_TIMEOUT = 120 +
def get_kimchi_version(): return "-".join([__version__, __release__]) diff --git a/serialconsole.py b/serialconsole.py index 74f6b8a..6ace1a6 100644 --- a/serialconsole.py +++ b/serialconsole.py @@ -28,11 +28,11 @@ import time from multiprocessing import Process
from wok.plugins.kimchi import model +from wok.plugins.kimchi.config import SERIAL_CONSOLE_TIMEOUT from wok.utils import wok_log
SOCKET_QUEUE_BACKLOG = 0 -DEFAULT_TIMEOUT = 120 # seconds CTRL_Q = '\x11' BASE_DIRECTORY = '/run/wok'
@@ -170,7 +170,7 @@ class SocketServer(Process): client console. """ client, client_addr = self._socket.accept() - client.settimeout(DEFAULT_TIMEOUT) + client.settimeout(SERIAL_CONSOLE_TIMEOUT) wok_log.info('[%s] Client connected to %s', self.name, self._guest_name)

- 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@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

Reviewed-By: Lucio Correia <luciojhc@linux.vnet.ibm.com> On 09-03-2016 16:04, 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@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));
-- Lucio Correia Software Engineer IBM LTC Brazil

Reviewed-by: Aline Manera <alinefm@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@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));
participants (4)
-
Aline Manera
-
Daniel Henrique Barboza
-
Jose Ricardo Ziviani
-
Lucio Correia