summaryrefslogtreecommitdiffstats
path: root/nova/vnc
diff options
context:
space:
mode:
authorAnthony Young <sleepsonthefloor@gmail.com>2011-03-23 01:57:38 -0700
committerAnthony Young <sleepsonthefloor@gmail.com>2011-03-23 01:57:38 -0700
commit9c75878e5f6f1b90695e725d7bc8e6e9002cabbb (patch)
tree336171b047ed498fc285bc89a0860615c227adee /nova/vnc
parent8048fb9902d80c6a14786f89e672ebff8407d0dd (diff)
downloadnova-9c75878e5f6f1b90695e725d7bc8e6e9002cabbb.tar.gz
nova-9c75878e5f6f1b90695e725d7bc8e6e9002cabbb.tar.xz
nova-9c75878e5f6f1b90695e725d7bc8e6e9002cabbb.zip
separating out components of vnc console
Diffstat (limited to 'nova/vnc')
-rw-r--r--nova/vnc/__init__.py0
-rw-r--r--nova/vnc/auth.py83
-rw-r--r--nova/vnc/proxy.py111
3 files changed, 194 insertions, 0 deletions
diff --git a/nova/vnc/__init__.py b/nova/vnc/__init__.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/nova/vnc/__init__.py
diff --git a/nova/vnc/auth.py b/nova/vnc/auth.py
new file mode 100644
index 000000000..2596bdd24
--- /dev/null
+++ b/nova/vnc/auth.py
@@ -0,0 +1,83 @@
+#!/usr/bin/env python
+# pylint: disable-msg=C0103
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2010 United States Government as represented by the
+# Administrator of the National Aeronautics and Space Administration.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Auth Components for VNC Console"""
+
+import time
+from webob import Request
+from nova import flags
+from nova import log as logging
+from nova import rpc
+from nova import utils
+from nova import wsgi
+
+
+class NovaAuthMiddleware(object):
+ """Implementation of Middleware to Handle Nova Auth"""
+
+ def __init__(self, app):
+ self.app = app
+ self.register_listeners()
+
+ def __call__(self, environ, start_response):
+ req = Request(environ)
+
+ if req.path == '/data':
+ token = req.params.get('token')
+ if not token in self.tokens:
+ start_response('403 Forbidden',
+ [('content-type', 'text/html')])
+ return 'Not Authorized'
+
+ environ['vnc_host'] = self.tokens[token]['args']['host']
+ environ['vnc_port'] = int(self.tokens[token]['args']['port'])
+
+ resp = req.get_response(self.app)
+ return resp(environ, start_response)
+
+ def register_listeners(self):
+ middleware = self
+ middleware.tokens = {}
+
+ class Callback:
+ def __call__(self, data, message):
+ if data['method'] == 'authorize_vnc_console':
+ middleware.tokens[data['args']['token']] = \
+ {'args': data['args'], 'last_activity_at': time.time()}
+
+ def delete_expired_tokens():
+ now = time.time()
+ to_delete = []
+ for k, v in middleware.tokens.items():
+ if now - v['last_activity_at'] > 600:
+ to_delete.append(k)
+
+ for k in to_delete:
+ del middleware.tokens[k]
+
+ conn = rpc.Connection.instance(new=True)
+ consumer = rpc.TopicConsumer(
+ connection=conn,
+ topic=FLAGS.vnc_console_proxy_topic)
+ consumer.register_callback(Callback())
+
+ utils.LoopingCall(consumer.fetch, auto_ack=True,
+ enable_callbacks=True).start(0.1)
+ utils.LoopingCall(delete_expired_tokens).start(1)
diff --git a/nova/vnc/proxy.py b/nova/vnc/proxy.py
new file mode 100644
index 000000000..3f218e744
--- /dev/null
+++ b/nova/vnc/proxy.py
@@ -0,0 +1,111 @@
+#!/usr/bin/env python
+# pylint: disable-msg=C0103
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2010 United States Government as represented by the
+# Administrator of the National Aeronautics and Space Administration.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Eventlet WSGI Services to proxy VNC. No nova deps."""
+
+from base64 import b64encode, b64decode
+import eventlet
+from eventlet import wsgi
+from eventlet import websocket
+import os
+from webob import Request
+import webob
+
+
+class WebsocketVNCProxy(object):
+ """Class to proxy from websocket to vnc server"""
+
+ def __init__(self, wwwroot):
+ self.wwwroot = wwwroot
+
+ def sock2ws(self, source, dest):
+ try:
+ while True:
+ d = source.recv(32384)
+ if d == '':
+ break
+ d = b64encode(d)
+ dest.send(d)
+ except:
+ source.close()
+ dest.close()
+
+ def ws2sock(self, source, dest):
+ try:
+ while True:
+ d = source.wait()
+ if d is None:
+ break
+ d = b64decode(d)
+ dest.sendall(d)
+ except:
+ source.close()
+ dest.close()
+
+ def proxy_connection(self, environ, start_response):
+ @websocket.WebSocketWSGI
+ def _handle(client):
+ server = eventlet.connect((client.environ['vnc_host'],
+ client.environ['vnc_port']))
+ t1 = eventlet.spawn(self.ws2sock, client, server)
+ t2 = eventlet.spawn(self.sock2ws, server, client)
+ t1.wait()
+ t2.wait()
+ _handle(environ, start_response)
+
+ def serve(self, environ, start_response):
+ req = Request(environ)
+ if req.path == '/data':
+ return self.proxy_connection(environ, start_response)
+ else:
+ if req.path == '/':
+ fname = '/vnc_auto.html'
+ else:
+ fname = req.path
+
+ fname = self.wwwroot + fname
+
+ base, ext = os.path.splitext(fname)
+ if ext == '.js':
+ mimetype = 'application/javascript'
+ elif ext == '.css':
+ mimetype = 'text/css'
+ elif ext in ['.svg', '.jpg', '.png', '.gif']:
+ mimetype = 'image'
+ else:
+ mimetype = 'text/html'
+
+ start_response('200 OK', [('content-type', mimetype)])
+ return open(os.path.join(fname)).read()
+
+
+class DebugMiddleware(object):
+ """Debug middleware. Skip auth, get vnc port and host from query string"""
+
+ def __init__(self, app):
+ self.app = app
+
+ def __call__(self, environ, start_response):
+ req = Request(environ)
+ if req.path == '/data':
+ environ['vnc_host'] = req.params.get('host')
+ environ['vnc_port'] = int(req.params.get('port'))
+ resp = req.get_response(self.app)
+ return resp(environ, start_response)