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

Royce Lv lvroyce at linux.vnet.ibm.com
Wed Jan 8 03:10:42 UTC 2014


Reviewed-by: Royce Lv<lvroyce at linux.vnet.ibm.com>
On 2014年01月07日 16:56, Mark Wu wrote:
> 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. It adds a map of target
> host/port and token pair to websockify's cfg on the connect request. With
> this change, all vnc traffic can be forwared in one tcp port, and therefore
> it's possible to add firewall rule for kimchi's vnc remote access.
>
> Signed-off-by: Mark Wu <wudxw at linux.vnet.ibm.com>
> ---
>   src/kimchi/model.py     | 14 ++++++--------
>   src/kimchi/server.py    |  5 +++++
>   src/kimchi/vnc.py       | 41 +++++++++++++++++++++++------------------
>   ui/js/src/kimchi.api.js |  6 ++++--
>   4 files changed, 38 insertions(+), 28 deletions(-)
>
> diff --git a/src/kimchi/model.py b/src/kimchi/model.py
> index a21fcf7..10e6c3f 100644
> --- a/src/kimchi/model.py
> +++ b/src/kimchi/model.py
> @@ -129,7 +129,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)
> @@ -504,10 +503,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)
> @@ -567,6 +563,8 @@ class Model(object):
>               with self.objstore as session:
>                   session.delete('vm', dom.UUIDString(), ignore_missing=True)
>
> +            vnc.remove_proxy_token(name)
> +
>       def vm_start(self, name):
>           dom = self._get_vm(name)
>           dom.create()
> @@ -596,10 +594,10 @@ class Model(object):
>       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
> +            vnc.add_proxy_token(name, port)
>           else:
> -            raise OperationFailed("Unable to find VNC port in %s" % name)
> +            raise OperationFailed("Only able to connect to running vm's vnc "
> +                                  "graphics.")
>
>       def vms_create(self, params):
>           conn = self.conn.get()
> diff --git a/src/kimchi/server.py b/src/kimchi/server.py
> index 1467c87..b820263 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 get_enabled_plugins, import_class
>
> @@ -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..dc70b46 100644
> --- a/src/kimchi/vnc.py
> +++ b/src/kimchi/vnc.py
> @@ -21,33 +21,38 @@
>   # 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 contextlib import closing
> +from kimchi.config import config
>
>
> -from kimchi.exception import OperationFailed
> +WS_TOKENS_DIR = '/var/lib/kimchi/vnc-tokens'
>
>
> -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")
> +def new_ws_proxy():
> +    try:
> +        os.makedirs(WS_TOKENS_DIR, mode=0755)
> +    except OSError as e:
> +        if e.errno == errno.EEXIST:
> +            pass
>
> -        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('novnc', '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..a4608ff 100644
> --- a/ui/js/src/kimchi.api.js
> +++ b/ui/js/src/kimchi.api.js
> @@ -291,17 +291,19 @@ var kimchi = {
>               dataType : 'json'
>           }).done(function(data, textStatus, xhr) {
>               http_port = data['http_port'];
> +            proxy_port = data['vnc_proxy_port'];
>               kimchi.requestJSON({
>                   url : "/vms/" + encodeURIComponent(vm) + "/connect",
>                   type : "POST",
>                   dataType : "json"
> -            }).done(function(data, textStatus, xhr) {
> +            }).done(function() {
>                   /**
>                    * 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;
> +                url += "/vnc_auto.html?port=" + proxy_port;
> +                url += "&path=?token=" + encodeURIComponent(vm);
>                   window.open(url);
>               });
>           }).error(function() {




More information about the Kimchi-devel mailing list