
On 02/04/2016 05:47 PM, Jose Ricardo Ziviani wrote:
- This commit implements kimchi backend necessary to receive a web request to open the serial console for a particular guest, add the security token and starts the server.
Signed-off-by: Jose Ricardo Ziviani <joserz@linux.vnet.ibm.com> --- control/vms.py | 3 ++- i18n.py | 4 +++- model/vms.py | 37 ++++++++++++++++++++++++++++++++++++- websocket.py | 34 ++++++++++++++++++++++++++++++++-- 4 files changed, 73 insertions(+), 5 deletions(-)
diff --git a/control/vms.py b/control/vms.py index 96bdb20..e9f01e1 100644 --- a/control/vms.py +++ b/control/vms.py @@ -1,7 +1,7 @@ # # Project Kimchi # -# Copyright IBM, Corp. 2013-2015 +# Copyright IBM, Corp. 2013-2016 # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -55,6 +55,7 @@ class VM(Resource): 'password']) self.suspend = self.generate_action_handler('suspend') self.resume = self.generate_action_handler('resume') + self.serial = self.generate_action_handler('serial')
@property def data(self): diff --git a/i18n.py b/i18n.py index a575922..f84e12c 100644 --- a/i18n.py +++ b/i18n.py @@ -1,7 +1,7 @@ # # Project Kimchi # -# Copyright IBM, Corp. 2014-2015 +# Copyright IBM, Corp. 2014-2016 # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -131,6 +131,8 @@ messages = { "KCHVM0073E": _("Unable to update the following parameters while the VM is offline: %(params)s"), "KCHVM0074E": _("Unable to update the following parameters while the VM is online: %(params)s"), "KCHVM0075E": _("Cannot change VCPU value because '%(vm)s' has a topology defined - sockets: %(sockets)s, cores: %(cores)s, threads: %(threads)s."), + "KCHVM0076E": _("VM %(name)s must have serial and console defined to open a web serial console"), + "KCHVM0077E": _("Impossible to get the serial console of %(name)s"),
"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 c76641c..26b5cfb 100644 --- a/model/vms.py +++ b/model/vms.py @@ -1,7 +1,7 @@ # # Project Kimchi # -# Copyright IBM, Corp. 2014-2015 +# Copyright IBM, Corp. 2014-2016 # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -46,6 +46,7 @@ from wok.xmlutils.utils import dictize
from wok.plugins.kimchi import model from wok.plugins.kimchi import websocket +from wok.plugins.kimchi import serial_console from wok.plugins.kimchi.config import READONLY_POOL_TYPE, get_kimchi_version from wok.plugins.kimchi.kvmusertests import UserTests from wok.plugins.kimchi.model.config import CapabilitiesModel @@ -245,6 +246,7 @@ class VMModel(object): cls = import_class('plugins.kimchi.model.vmsnapshots.VMSnapshotsModel') self.vmsnapshots = cls(**kargs) self.stats = {} + self._serial_procs = []
def has_topology(self, dom): xml = dom.XMLDesc(0) @@ -1175,6 +1177,12 @@ class VMModel(object): else: memory = info[2] >> 10
+ # assure there is no zombie process left + for proc in self._serial_procs[:]: + if not proc.is_alive(): + proc.join(1) + self._serial_procs.remove(proc) + return {'name': name, 'state': state, 'stats': res, @@ -1359,6 +1367,20 @@ class VMModel(object): raise OperationFailed("KCHVM0022E", {'name': name, 'err': e.get_error_message()})
+ def _vm_check_serial(self, name): + dom = self.get_vm(name, self.conn) + xml = dom.XMLDesc(libvirt.VIR_DOMAIN_XML_SECURE) + + expr = "/domain/devices/serial/@type" + if not xpath_get_text(xml, expr): + return False + + expr = "/domain/devices/console/@type" + if not xpath_get_text(xml, expr): + return False + + return True + def _vm_get_graphics(self, name): dom = self.get_vm(name, self.conn) xml = dom.XMLDesc(libvirt.VIR_DOMAIN_XML_SECURE) @@ -1390,6 +1412,19 @@ class VMModel(object): return (graphics_type, graphics_listen, graphics_port, graphics_passwd, graphics_passwdValidTo)
+ def serial(self, name): + if not self._vm_check_serial(name): + raise OperationFailed("KCHVM0076E", {'name': name}) + + websocket.add_proxy_token(name.encode('utf-8')+'-console', + '/tmp/%s' % name.encode('utf-8')) + + try: + self._serial_procs.append( + serial_console.main(name.encode('utf-8'))) + except: + raise OperationFailed("KCHVM0077E", {'name': name}) + def connect(self, name): # (type, listen, port, passwd, passwdValidTo) graphics_port = self._vm_get_graphics(name)[2] diff --git a/websocket.py b/websocket.py index 4f94ab2..ab3ffb4 100644 --- a/websocket.py +++ b/websocket.py @@ -34,10 +34,32 @@ try: except ImportError: tokenFile = False
+try: + from websockify import ProxyRequestHandler as request_proxy +except: + from websockify import WebSocketProxy as request_proxy +
WS_TOKENS_DIR = os.path.join(PluginPaths('kimchi').state_dir, 'vnc-tokens')
+class custom_handler(request_proxy): + +
CustomHandler
def get_target(self, target_plugin, path): + target = super(custom_handler, self).get_target(target_plugin, path) + if target[0] == 'unix_socket': + try: + self.server.unix_target = target[1] + except: + self.unix_target = target[1] + else: + try: + self.server.unix_target = None + except: + self.unix_target = None + return target + + def new_ws_proxy(): try: os.makedirs(WS_TOKENS_DIR, mode=0755) @@ -64,7 +86,12 @@ def new_ws_proxy(): params['token_plugin'] = TokenFile(src=WS_TOKENS_DIR)
def start_proxy(): - server = WebSocketProxy(**params) + try: + server = WebSocketProxy(RequestHandlerClass=custom_handler, + **params) + except TypeError: + server = custom_handler(**params) + server.start_server()
proc = Process(target=start_proxy) @@ -82,7 +109,10 @@ def add_proxy_token(name, port): So remove it when needed as base64 can work well without it. """ name = base64.urlsafe_b64encode(name).rstrip('=') - f.write('%s: localhost:%s' % (name.encode('utf-8'), port))
+ if type(port) == str: + f.write('%s: unix_socket:%s' % (name.encode('utf-8'), port)) + else: + f.write('%s: localhost:%s' % (name.encode('utf-8'), port))
It is dangerous to trust the variable type to determine the connection type. I'd suggest to use a new variable to indicate that. So the developer can really know what is going on.
def remove_proxy_token(name):