[Kimchi-devel] [project-kimchi] [PATCH 3/4] Use one weksockify instance as all vms' vnc proxy.

Mark Wu wudxw at linux.vnet.ibm.com
Wed Dec 18 08:51:20 UTC 2013


Currently, we start a new weksockify proxy on every vm's connect action.
It causes trouble to allow the vnc traffic in firewall because we choose
a random port for each vm. And it's also obversed that vnc connection fails
because of race between client and proxy server. This patch changes to use
websockify's target_cfg option instead of target_port. Then kimchi can
dynamically add proxy configuration for a vm. Basically, it gets the target
host/port by searching the token passed by the client on the connect request.
It adds token for vm when it's started and the token is kept until the vm is
stopped. After this change, we don't need do anything in kimchi because all things
are set up before connecting. So the vm's connect action is removed too.

Signed-off-by: Mark Wu <wudxw at linux.vnet.ibm.com>
---
 src/kimchi/model.py     | 18 +++++-------------
 src/kimchi/server.py    |  5 +++++
 src/kimchi/vnc.py       | 42 +++++++++++++++++++++++-------------------
 ui/js/src/kimchi.api.js | 14 +++++---------
 4 files changed, 38 insertions(+), 41 deletions(-)

diff --git a/src/kimchi/model.py b/src/kimchi/model.py
index 73c18ac..952a216 100644
--- a/src/kimchi/model.py
+++ b/src/kimchi/model.py
@@ -125,7 +125,6 @@ class Model(object):
         self.libvirt_uri = libvirt_uri or 'qemu:///system'
         self.conn = LibvirtConnection(self.libvirt_uri)
         self.objstore = ObjectStore(objstore_loc)
-        self.graphics_ports = {}
         self.next_taskid = 1
         self.stats = {}
         self.host_stats = defaultdict(int)
@@ -500,10 +499,7 @@ class Model(object):
         info = dom.info()
         state = Model.dom_state_map[info[0]]
         screenshot = None
-        graphics_type, _ = self._vm_get_graphics(name)
-        # 'port' must remain None until a connect call is issued
-        graphics_port = (self.graphics_ports.get(name, None) if state == 'running'
-                      else None)
+        graphics_type, graphics_port = self._vm_get_graphics(name)
         try:
             if state == 'running':
                 screenshot = self.vmscreenshot_lookup(name)
@@ -566,11 +562,15 @@ class Model(object):
     def vm_start(self, name):
         dom = self._get_vm(name)
         dom.create()
+        graphics, port = self._vm_get_graphics(name)
+        if graphics == "vnc" and port != None:
+            vnc.add_proxy_token(name, port)
 
     def vm_stop(self, name):
         if self._vm_exists(name):
             dom = self._get_vm(name)
             dom.destroy()
+            vnc.remove_proxy_token(name)
 
     def _vm_get_graphics(self, name):
         dom = self._get_vm(name)
@@ -589,14 +589,6 @@ class Model(object):
         graphics_type = None if graphics_type != "vnc" else graphics_type
         return graphics_type, port
 
-    def vm_connect(self, name):
-        graphics, port = self._vm_get_graphics(name)
-        if graphics == "vnc" and port != None:
-            port = vnc.new_ws_proxy(port)
-            self.graphics_ports[name] = port
-        else:
-            raise OperationFailed("Unable to find VNC port in %s" % name)
-
     def vms_create(self, params):
         conn = self.conn.get()
         t_name = template_name_from_uri(params['template'])
diff --git a/src/kimchi/server.py b/src/kimchi/server.py
index 6ff6fa0..9afe2ec 100644
--- a/src/kimchi/server.py
+++ b/src/kimchi/server.py
@@ -32,6 +32,7 @@ from kimchi import auth
 from kimchi import config
 from kimchi import model
 from kimchi import mockmodel
+from kimchi import vnc
 from kimchi.root import Root
 from kimchi.utils import import_class, get_enabled_plugins
 
@@ -188,6 +189,10 @@ class Server(object):
         else:
             model_instance = model.Model()
 
+        if isinstance(model_instance, model.Model):
+            vnc_ws_proxy = vnc.new_ws_proxy()
+            cherrypy.engine.subscribe('exit', vnc_ws_proxy.kill)
+
         self.app = cherrypy.tree.mount(Root(model_instance, dev_env),
                                        config=self.configObj)
         self._load_plugins()
diff --git a/src/kimchi/vnc.py b/src/kimchi/vnc.py
index 089d64a..e476002 100644
--- a/src/kimchi/vnc.py
+++ b/src/kimchi/vnc.py
@@ -21,33 +21,37 @@
 # License along with this library; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 
+import errno
 import os
-import socket
 import subprocess
-import sys
 
+from kimchi.config import config
 
-from contextlib import closing
 
+WS_TOKENS_DIR = '/var/lib/kimchi/vnc-tokens'
 
-from kimchi.exception import OperationFailed
 
+def new_ws_proxy():
+    try:
+        os.makedirs(WS_TOKENS_DIR, mode=0755)
+    except OSError as e:
+        if e.errno == errno.EEXIST:
+            pass
 
-def _getFreePort():
-    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-    with closing(sock):
-        try:
-            sock.bind(("0.0.0.0", 0))
-        except:
-            raise OperationFailed("Could not find a free port")
-
-        return sock.getsockname()[1]
-
-def new_ws_proxy(target_port):
-    src_port = _getFreePort()
     cmd = os.path.join(os.path.dirname(__file__), 'websockify.py')
-    args = ['python', cmd, str(src_port), '--timeout', '10',
-            '--idle-timeout', '10', 'localhost:%s' % target_port]
+    args = ['python', cmd, config.get('server', 'vnc_proxy_port'),
+            '--target-config', WS_TOKENS_DIR]
     p = subprocess.Popen(args, close_fds=True)
+    return p
+
+
+def add_proxy_token(name, port):
+    with open(os.path.join(WS_TOKENS_DIR, name), 'w') as f:
+        f.write('%s: localhost:%s' % (name.encode('utf-8'), port))
+
 
-    return src_port
+def remove_proxy_token(name):
+    try:
+        os.unlink(os.path.join(WS_TOKENS_DIR, name))
+    except OSError:
+        pass
diff --git a/ui/js/src/kimchi.api.js b/ui/js/src/kimchi.api.js
index fbcf4a2..83fb82e 100644
--- a/ui/js/src/kimchi.api.js
+++ b/ui/js/src/kimchi.api.js
@@ -291,19 +291,15 @@ var kimchi = {
             dataType : 'json'
         }).done(function(data, textStatus, xhr) {
             http_port = data['http_port'];
-            kimchi.requestJSON({
-                url : "/vms/" + encodeURIComponent(vm) + "/connect",
-                type : "POST",
-                dataType : "json"
-            }).done(function(data, textStatus, xhr) {
+            proxy_port = data['vnc_proxy_port'];
                 /**
                  * Due to problems with web sockets and self-signed
                  * certificates, for now we will always redirect to http
                  */
-                url = 'http://' + location.hostname + ':' + http_port;
-                url += "/vnc_auto.html?port=" + data.graphics.port;
-                window.open(url);
-            });
+            url = 'http://' + location.hostname + ':' + http_port;
+            url += "/vnc_auto.html?port=" + proxy_port;
+            url += "&path=?token=" + encodeURIComponent(vm);
+            window.open(url);
         }).error(function() {
             kimchi.message.error(i18n['msg.fail.get.config']);
         });
-- 
1.8.3.1

-- 
project-kimchi mailing list <project-kimchi at googlegroups.com>
https://groups.google.com/forum/#!forum/project-kimchi
--- 
You received this message because you are subscribed to the Google Groups "project-kimchi" group.
To unsubscribe from this group and stop receiving emails from it, send an email to project-kimchi+unsubscribe at googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.



More information about the Kimchi-devel mailing list