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(a)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):