Mohammed Morsi
2009-May-19 20:09 UTC
[Ovirt-devel] [PATCH server] added ovirt vnc proxy server, to proxy vnc request to managed vms
run on startup by default like the other ovirt services
---
conf/ovirt-vnc-proxy | 49 ++++++++
installer/modules/ovirt/manifests/ovirt.pp | 1 +
ovirt-server.spec.in | 5 +
src/vnc-proxy/vnc-proxy.rb | 167 ++++++++++++++++++++++++++++
4 files changed, 222 insertions(+), 0 deletions(-)
create mode 100755 conf/ovirt-vnc-proxy
create mode 100755 src/vnc-proxy/vnc-proxy.rb
diff --git a/conf/ovirt-vnc-proxy b/conf/ovirt-vnc-proxy
new file mode 100755
index 0000000..b3f565f
--- /dev/null
+++ b/conf/ovirt-vnc-proxy
@@ -0,0 +1,49 @@
+#!/bin/bash
+#
+#
+# ovirt-vnc-proxy startup script for ovirt-vnc-proxy
+#
+# chkconfig: - 97 03
+# description: ovirt-vnc-proxy proxies vnc requests to ovirt
+# managed vms.
+#
+
+DAEMON=/usr/share/ovirt-server/vnc-proxy/vnc-proxy.rb
+
+. /etc/init.d/functions
+
+start() {
+ echo -n "Starting ovirt-vnc-proxy: "
+ daemon $DAEMON
+ RETVAL=$?
+ echo
+}
+
+stop() {
+ echo -n "Shutting down ovirt-vnc-proxy: "
+ killproc vnc-proxy.rb
+ RETVAL=$?
+ echo
+}
+
+case "$1" in
+ start)
+ start
+ ;;
+ stop)
+ stop
+ ;;
+ restart)
+ stop
+ start
+ ;;
+ status)
+ status $DAEMON
+ RETVAL=$?
+ ;;
+ *)
+ echo "Usage: ovirt-vnc-proxy {start|stop|restart|status}"
+ exit 1
+ ;;
+esac
+exit $RETVAL
diff --git a/installer/modules/ovirt/manifests/ovirt.pp
b/installer/modules/ovirt/manifests/ovirt.pp
index 03a93a7..d953ebe 100644
--- a/installer/modules/ovirt/manifests/ovirt.pp
+++ b/installer/modules/ovirt/manifests/ovirt.pp
@@ -197,6 +197,7 @@ class ovirt::setup {
firewall_rule{"http": destination_port => "80"}
firewall_rule {"https": destination_port => '443'}
firewall_rule {"host-browser": destination_port =>
'12120'}
+ firewall_rule {"vnc-proxy": destination_port =>
'5900'}
firewall_rule {"qpidd": destination_port =>
'5672'}
firewall_rule {"collectd": destination_port =>
'25826', protocol => 'udp'}
firewall_rule {"ntpd": destination_port => '123',
protocol => 'udp'}
diff --git a/ovirt-server.spec.in b/ovirt-server.spec.in
index b4f7454..ad8777b 100644
--- a/ovirt-server.spec.in
+++ b/ovirt-server.spec.in
@@ -104,6 +104,7 @@ touch
%{buildroot}%{_localstatedir}/log/%{name}/db-omatic.log
%{__install} -Dp -m0755 %{pbuild}/conf/ovirt-mongrel-rails.sysconf
%{buildroot}%{_sysconfdir}/sysconfig/ovirt-mongrel-rails
%{__install} -Dp -m0755 %{pbuild}/conf/ovirt-rails.sysconf
%{buildroot}%{_sysconfdir}/sysconfig/ovirt-rails
%{__install} -Dp -m0755 %{pbuild}/conf/ovirt-taskomatic
%{buildroot}%{_initrddir}
+%{__install} -Dp -m0755 %{pbuild}/conf/ovirt-vnc-proxy
%{buildroot}%{_initrddir}
# copy over all of the src directory...
%{__cp} -a %{pbuild}/src/* %{buildroot}%{app_root}
@@ -180,6 +181,7 @@ fi
%daemon_chkconfig_post -d ovirt-host-collect
%daemon_chkconfig_post -d ovirt-mongrel-rails
%daemon_chkconfig_post -d ovirt-taskomatic
+%daemon_chkconfig_post -d ovirt-vnc-proxy
%preun
if [ "$1" = 0 ] ; then
@@ -188,11 +190,13 @@ if [ "$1" = 0 ] ; then
/sbin/service ovirt-host-collect stop > /dev/null 2>&1
/sbin/service ovirt-mongrel-rails stop > /dev/null 2>&1
/sbin/service ovirt-taskomatic stop > /dev/null 2>&1
+ /sbin/service ovirt-vnc-proxy stop > /dev/null 2>&1
/sbin/chkconfig --del ovirt-host-browser
/sbin/chkconfig --del ovirt-db-omatic
/sbin/chkconfig --del ovirt-host-collect
/sbin/chkconfig --del ovirt-mongrel-rails
/sbin/chkconfig --del ovirt-taskomatic
+ /sbin/chkconfig --del ovirt-vnc-proxy
fi
%files
@@ -206,6 +210,7 @@ fi
%{_initrddir}/ovirt-host-collect
%{_initrddir}/ovirt-mongrel-rails
%{_initrddir}/ovirt-taskomatic
+%{_initrddir}/ovirt-vnc-proxy
%{_sysconfdir}/cron.d/%{name}
%config(noreplace) %{_sysconfdir}/sysconfig/ovirt-mongrel-rails
%config(noreplace) %{_sysconfdir}/sysconfig/ovirt-rails
diff --git a/src/vnc-proxy/vnc-proxy.rb b/src/vnc-proxy/vnc-proxy.rb
new file mode 100755
index 0000000..5a1c35e
--- /dev/null
+++ b/src/vnc-proxy/vnc-proxy.rb
@@ -0,0 +1,167 @@
+#!/usr/bin/ruby
+#
+# vnc-proxy.rb
+# ovirt vnc proxy server, relays ovirt encoded
+# vnc requests to correct node
+# Copyright (C) 2009 Red Hat, Inc.
+# Written by Mohammed Morsi <mmorsi at redhat.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA 02110-1301, USA. A copy of the GNU General Public License is
+# also available at http://www.gnu.org/copyleft/gpl.html.
+
+$: << File.join(File.dirname(__FILE__), "../dutils")
+
+require 'dutils'
+require 'daemons'
+include Daemonize
+
+###########
+
+DEFAULT_VNC_PROXY_PORT = 5900
+VM_NAME_MAX_LEN = 250
+VNC_DATA_MAX_LEN = 800000
+
+###########
+
+# clone of the taskomatic / dbomatic logger;
+# TODO move all of these seperate implementations into a single dutils module
+class Logger
+ def format_message(severity, timestamp, progname, msg)
+ "#{severity} #{timestamp} (#{$$}) #{msg}\n"
+ end
+end
+
+$logfile = '/var/log/ovirt-server/vnc-proxy.log'
+
+###########
+
+
+class VncProxy
+
+ # initialize vnc proxy
+ def initialize()
+ super()
+ do_daemon = true
+ port = DEFAULT_VNC_PROXY_PORT
+
+ opts = OptionParser.new do |opts|
+ opts.on("-h", "--help", "Print help
message") do
+ puts opts
+ exit
+ end
+ opts.on("-n", "--nodaemon", "Run
interactively (useful for debugging)") do |n|
+ do_daemon = false
+ end
+ opts.on("-p", "--port", "Port to listen
on") do |n|
+ port = n.to_i
+ end
+ end
+ begin
+ opts.parse!(ARGV)
+ rescue OptionParser::InvalidOption
+ puts opts
+ exit
+ end
+
+ if do_daemon
+ # same issues as w/ dbomatic / taskomatic
+ pwd = Dir.pwd
+ daemonize
+ Dir.chdir(pwd)
+ @logger = Logger.new($logfile)
+ else
+ @logger = Logger.new(STDERR)
+ end
+
+ begin
+ @server = TCPServer.open(port)
+ rescue Exception => ex
+ @logger.error "Error in vnc-proxy: #{ex}"
+ @logger.error ex.backtrace
+
+ # reraise ex, if we can't bind
+ # to port server should die
+ raise ex
+ end
+
+ @logger.info "vnc-proxy started."
+ end
+
+ # run vnc proxy
+ def run
+ continue = true
+ while(continue) do
+ begin
+ Thread.start(@server.accept) do |client|
+ begin
+ @logger.info "client accepted"
+
+ # first msg will be the vm description
+ vm_description = client.recv(VM_NAME_MAX_LEN).to_s
+ @logger.info "vm received: " + vm_description +
";"
+
+ # lookup vm
+ vm = Vm.find(:first, :conditions => [ "description =
?", vm_description ])
+ if vm && vm.state == "running"
+ # connect to node
+ @logger.info "connecting to node " + vm.host.hostname
+ ":" + vm.vnc_port.to_s
+ node_socket = TCPSocket.open(vm.host.hostname, vm.vnc_port)
+
+ # begin new thread to process server->client messages
+ Thread.start do
+ @logger.debug "listening for server->client data"
+ while(true)do
+ node_data = node_socket.recv VNC_DATA_MAX_LEN
+ break if node_data.size <= 0
+ client.write node_data
+ end
+ end
+
+ # process client -> server messages
+ @logger.debug "listening for client->server data"
+ while(true) do
+ client_data = client.recv VNC_DATA_MAX_LEN
+ break if client_data.size <= 0
+ node_socket.write client_data
+ end
+
+ node_socket.close
+ @logger.info "node connection terminated"
+ end
+ rescue Exception => ex
+ @logger.error "Error w/ vnc-proxy client (non-fatal)
#{ex}"
+ end
+
+ client.close
+ @logger.info "client connection terminated"
+ end
+
+ rescue Exception => ex
+ continue = false
+ @logger.error "terminating vnc proxy server #{ex}"
+ end
+ end
+
+ @server.close
+ @logger.info "server terminated"
+ end
+end
+
+def main()
+ vncproxy = VncProxy.new
+ vncproxy.run
+end
+
+main()
--
1.6.0.6
Mohammed Morsi
2009-May-19 20:09 UTC
[Ovirt-devel] [PATCH viewer] connect to the ovirt-vnc-proxy server to access a vm's vnc
---
Makefile.am | 2 +-
internal.h | 30 ++++++
main.c | 27 +++++-
tunnel.c | 319 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 373 insertions(+), 5 deletions(-)
create mode 100644 tunnel.c
diff --git a/Makefile.am b/Makefile.am
index 1247729..b5a3e01 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -18,7 +18,7 @@
bin_PROGRAMS = ovirt-viewer
-ovirt_viewer_SOURCES = main.c wui_thread.c internal.h
+ovirt_viewer_SOURCES = main.c wui_thread.c tunnel.c internal.h
ovirt_viewer_CFLAGS = $(OVIRT_VIEWER_CFLAGS)
-DCAINFO='"$(CAINFO)"'
ovirt_viewer_LDADD = $(OVIRT_VIEWER_LIBS)
diff --git a/internal.h b/internal.h
index 04512f9..80e675e 100644
--- a/internal.h
+++ b/internal.h
@@ -39,6 +39,21 @@ extern gboolean debug;
} \
} while (0)
+/* Verbose messages are always compiled in, but have to
+ * be turned on using the --verbose command line switch.
+ */
+extern gboolean verbose;
+
+#define VERBOSE(fs,...) \
+ do { \
+ if (verbose) { \
+ fprintf (stderr, "%s:%d: [thread %p] ", __FILE__, __LINE__, \
+ g_thread_self ()); \
+ fprintf (stderr, (fs), ## __VA_ARGS__); \
+ fprintf (stderr, "\n"); \
+ } \
+ } while (0)
+
/* String equality tests, suggested by Jim Meyering. */
#define STREQ(a,b) (strcmp((a),(b)) == 0)
#define STRCASEEQ(a,b) (strcasecmp((a),(b)) == 0)
@@ -53,6 +68,12 @@ extern gboolean debug;
extern const char *cainfo;
extern gboolean check_cert;
+/* server we're connecting to */
+extern const char* hostname;
+
+/* vm currently in focus */
+extern struct vm* vm_in_focus;
+
/* Communications between the main thread and the WUI thread. For
* an explanation of the threading model, please see the comment in
* main().
@@ -144,6 +165,15 @@ extern gboolean wui_thread_has_valid_vmlist (void);
*/
extern gboolean wui_thread_is_busy (void);
+
+/* Communications between the main thread and the tunnel thread.*/
+extern void start_tunnel (void);
+extern void stop_tunnel (void);
+
+/* port which local tunnel is listening on */
+extern int tunnel_port;
+
+
/* Returns true if the main vm list contains a
* running vm w/ the same name as specified one
*/
diff --git a/main.c b/main.c
index 356d379..e5a9e4b 100644
--- a/main.c
+++ b/main.c
@@ -55,6 +55,8 @@
gboolean debug = 0;
+gboolean verbose = 0;
+
/* Usually /etc/pki/tls/certs/ca-bundle.crt unless overridden during
* configure or on the command line.
*/
@@ -68,6 +70,10 @@ gboolean check_cert = FALSE; // do we want this enabled by
default ?
*/
static GSList *vmlist = NULL;
+/* internal.h shared constructs */
+const char* hostname;
+struct vm* vm_in_focus;
+
/* Private functions. */
static void start_ui (void);
static GtkWidget *menu_item_new (int which_menu);
@@ -198,6 +204,8 @@ static const GOptionEntry options[] = {
"check the SSL certificate of the server", NULL },
{ "debug", 'd', 0, G_OPTION_ARG_NONE, &debug,
"turn on debugging messages", NULL },
+ { "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose,
+ "turn on verbose messages", NULL },
{ "version", 'V', 0, G_OPTION_ARG_NONE, &print_version,
"display version and exit", NULL },
{ NULL, 0, 0, G_OPTION_ARG_NONE, NULL, NULL, NULL }
@@ -225,6 +233,10 @@ main (int argc, char *argv[])
* processed in the main thread - see:
* http://mail.gnome.org/archives/gtk-app-devel-list/2007-March/msg00232.html
*
+ * A tunnel thread is also started to locally listen for vnc packets
+ * and make them proxyable, adding the vm name, before forwarding onto
+ * the server
+ *
* Note that under Win32 you must confine all Gtk/Gdk interactions
* to a single thread - see:
* http://developer.gimp.org/api/2.0/gdk/gdk-Threads.html
@@ -267,6 +279,7 @@ main (int argc, char *argv[])
gtk_main ();
stop_wui_thread ();
+ stop_tunnel();
exit (0);
}
@@ -510,6 +523,7 @@ help_about (GtkWidget *menu)
const char *authors[] = {
"Richard W.M. Jones <rjones at redhat.com>",
"Daniel P. Berrange <berrange at redhat.com>",
+ "Mohammed Morsi <mmorsi at redhat.com>",
NULL
};
@@ -557,7 +571,7 @@ connect_to_wui_on_enter (GtkWidget *widget, gpointer data)
static void
connect_to_wui (GtkWidget *widget, gpointer data)
{
- const char *hostname;
+ //const char *hostname;
char *uri;
int len;
@@ -570,6 +584,7 @@ connect_to_wui (GtkWidget *widget, gpointer data)
snprintf (uri, len, HTTPS "://%s/ovirt", hostname);
wui_thread_send_connect (uri);
+ start_tunnel();
}
static void
@@ -606,7 +621,7 @@ connect_to_vm (GtkWidget *widget, gpointer _vm)
int i, uuidlen, len, fd;
GtkWidget *child;
const char *label;
- const char* hostname;
+ //const char* hostname;
char *label2;
char new_title[97]; // 47 chars for title + 50 for vm name
@@ -637,9 +652,13 @@ connect_to_vm (GtkWidget *widget, gpointer _vm)
return;
}
+ // FIXME on notebook tab switch, change vm_in_focus
+ vm_in_focus = vm;
+
/* This VM isn't in the notebook already, so create a new console. */
- hostname = gtk_entry_get_text (GTK_ENTRY (ca_hostname));
- fd = viewer_open_vnc_socket(hostname, vm->forward_vnc_port);
+ //hostname = gtk_entry_get_text (GTK_ENTRY (ca_hostname));
+ DEBUG ("connecting to local tunnel on port %i", tunnel_port);
+ fd = viewer_open_vnc_socket("127.0.0.1", tunnel_port);
if (fd == -1) return; /* We've already given an error. */
child = vnc_display_new ();
diff --git a/tunnel.c b/tunnel.c
new file mode 100644
index 0000000..a03ef0b
--- /dev/null
+++ b/tunnel.c
@@ -0,0 +1,319 @@
+/* ovirt viewer console application
+ * Copyright (C) 2008 Red Hat Inc.
+ * Written by Mohammed Morsi <mmorsi at redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* ovirt-viewer starts listening on network port to
+ * encapsulate vnc packets including the vm's name
+ * so as to be able to be proxied.
+ *
+ * This operation takes place in another thread
+ * which can be started/stopped by calling
+ * start_tunnel / stop_tunnel.
+ *
+ * An additional connection thread is created and maintained
+ * internally for each vm / vnc connection open in ovirt-viewer
+ * establishing a connection w/ the ovirt server.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <assert.h>
+#include <string.h>
+
+#include <glib.h>
+
+#include "internal.h"
+
+/* constants */
+
+// port to try to listen on, if we can't, increment until we find one we
can
+const int PORT_RANGE_START = 5600;
+
+// max length of a vm name
+const int VM_NAME_MAX_LEN = 250;
+
+// max length of vnc data
+const int VNC_DATA_MAX_LEN = 800000;
+
+// port which to connect to on ovirt server
+const int OVIRT_SERVER_PORT = 5900;
+
+/* Private thread functions */
+static gpointer tunnel_thread(gpointer data);
+static gpointer client_server_thread(gpointer data);
+static gpointer server_client_thread(gpointer data);
+
+/* Other private functions */
+static void close_socket(gpointer _socket, gpointer data);
+static void wait_for_thread(gpointer _thread, gpointer data);
+
+/* tunnel and main threads */
+static GThread *tunnel_gthread = NULL;
+static GThread *main_gthread = NULL;
+
+/* list of communication threads */
+static GSList *communication_threads = NULL;
+
+/* list of sockets */
+static GSList *sockets = NULL;
+
+/* thread termination flag */
+static gboolean run_tunnel = FALSE;
+
+/* internal.h shared constructs */
+int tunnel_port;
+
+/////////////////
+
+/** public implementations **/
+
+/* start tunnel thread */
+void
+start_tunnel(void)
+{
+ GError *error = NULL;
+
+ DEBUG ("starting the tunnel thread");
+
+ assert (tunnel_gthread == NULL);
+
+ run_tunnel = TRUE;
+
+ main_gthread = g_thread_self ();
+
+ tunnel_gthread = g_thread_create (tunnel_thread, NULL, TRUE, &error);
+ if (error) {
+ g_print ("%s\n", error->message);
+ g_error_free (error);
+ exit (1);
+ }
+};
+
+/* stop tunnel thread */
+void
+stop_tunnel(void)
+{
+ if(!run_tunnel)
+ return;
+
+ DEBUG ("stopping the tunnel thread");
+
+ assert (tunnel_gthread != NULL);
+ ASSERT_IS_MAIN_THREAD ();
+
+ run_tunnel = FALSE;
+
+ g_slist_foreach(sockets, close_socket, NULL);
+
+ (void) g_thread_join (tunnel_gthread);
+ tunnel_gthread = NULL;
+};
+
+/////////////////
+
+/** private implementations **/
+
+/* the tunnel thread */
+static gpointer
+tunnel_thread (gpointer _data)
+{
+ //char vm_data[VM_NAME_MAX_LEN];
+ int local_server_socketfd, ovirt_server_socket, client_socketfd;
+ unsigned int local_server_len, client_len, ovirt_server_len;
+
+ struct sockaddr_in local_server_address;
+ struct sockaddr_in ovirt_server_address;
+ struct sockaddr_in client_address;
+
+ GThread *client_server_gthread = NULL;
+ GThread *server_client_gthread = NULL;
+
+ int sockets_param[2];
+ int * c_socket;
+
+ DEBUG ("tunnel thread starting up");
+
+ // ovirt server address
+ ovirt_server_address.sin_family = PF_INET;
+ ovirt_server_address.sin_addr.s_addr = inet_addr(hostname);
+ ovirt_server_address.sin_port = htons(OVIRT_SERVER_PORT);
+ ovirt_server_len = sizeof(ovirt_server_address);
+
+ // create local net socket
+ local_server_socketfd = socket(PF_INET, SOCK_STREAM, 0);
+ c_socket = malloc(sizeof(int)); *c_socket = local_server_socketfd;
+ sockets = g_slist_prepend(sockets, c_socket);
+
+ // local server address
+ tunnel_port = PORT_RANGE_START;
+ local_server_address.sin_family = PF_INET;
+ local_server_address.sin_addr.s_addr = inet_addr("127.0.0.1");
+ local_server_address.sin_port = htons(tunnel_port);
+ local_server_len = sizeof(local_server_address);
+
+ // increment ports until one is available
+ while(bind(local_server_socketfd, (struct
sockaddr*)&local_server_address, local_server_len) < 0){
+ tunnel_port += 1;
+ local_server_address.sin_port += htons(tunnel_port);
+ }
+
+ DEBUG ("tunnel bound to local port %i", tunnel_port);
+
+ // increase client buffer size?
+ listen(local_server_socketfd, 5);
+
+ while(run_tunnel) {
+ // accept a client connection
+ DEBUG("tunnel accepting");
+ client_len = sizeof(client_address);
+ client_socketfd = accept(local_server_socketfd, (struct
sockaddr*)&client_address, &client_len);
+ if(client_socketfd < 0){
+ DEBUG("tunnel accept failed");
+ break;
+ }
+ // TODO check accept return value for err
+ c_socket = malloc(sizeof(int)); *c_socket = client_socketfd;
+ sockets = g_slist_prepend(sockets, c_socket);
+
+ DEBUG ("client connected to tunnel");
+
+ // establish connection w/ ovirt server
+ ovirt_server_socket = socket(PF_INET, SOCK_STREAM, 0);
+ c_socket = malloc(sizeof(int)); *c_socket = ovirt_server_socket;
+ sockets = g_slist_prepend(sockets, c_socket);
+ DEBUG ("connecting to ovirt server %s on %i", hostname,
OVIRT_SERVER_PORT);
+ if(connect(ovirt_server_socket, (struct
sockaddr*)&ovirt_server_address, ovirt_server_len) < 0){
+ DEBUG ("could not connect to ovirt server");
+ break;
+ //return NULL;
+ }
+ DEBUG ("connected to ovirt server");
+
+ sockets_param[0] = ovirt_server_socket;
+ sockets_param[1] = client_socketfd;
+
+ // launch thread for client -> server traffic
+ client_server_gthread = g_thread_create (client_server_thread,
+ &sockets_param, TRUE, NULL);
+
+ // launch thread for server -> client traffic
+ server_client_gthread = g_thread_create (server_client_thread,
+ &sockets_param, TRUE, NULL);
+
+ communication_threads = g_slist_prepend(communication_threads,
client_server_gthread);
+ communication_threads = g_slist_prepend(communication_threads,
server_client_gthread);
+
+ // send target vm for this session
+ //strcpy(vm_data, vm_in_focus->description);
+ DEBUG ("sending vm %s", vm_in_focus->description);
+ write(ovirt_server_socket, vm_in_focus->description,
strlen(vm_in_focus->description));
+
+ }
+
+ DEBUG("terminating tunnel thread");
+
+ // wait for connection threads to finish
+ g_slist_foreach(communication_threads, wait_for_thread, NULL);
+
+ DEBUG ("tunnel thread completed");
+ return NULL;
+};
+
+/* the tunnel thread */
+static gpointer
+client_server_thread (gpointer _data){
+ int nbytes;
+ char vnc_data[VNC_DATA_MAX_LEN];
+
+ int ovirt_server_socket = ((int*)_data)[0],
+ client_socket = ((int*)_data)[1];
+
+ DEBUG ("client/server thread starting up");
+
+ while(run_tunnel){
+ VERBOSE( "accepting client data");
+
+ // grab vnc data
+ nbytes = read(client_socket, vnc_data, VNC_DATA_MAX_LEN);
+ if(nbytes <= 0){
+ DEBUG ( "error reading data from client" );
+ break;
+ }
+ VERBOSE ("read %i bytes from client", nbytes);
+
+ // send network_data onto server
+ nbytes = write(ovirt_server_socket, vnc_data, nbytes);
+ if(nbytes <= 0){
+ DEBUG ( "error writing data to server" );
+ break;
+ }
+ VERBOSE ("wrote %i bytes to server", nbytes);
+ }
+
+ DEBUG ("client/server thread completed");
+ return NULL;
+};
+
+/* the server thread */
+static gpointer
+server_client_thread (gpointer _data){
+ char vnc_data[VNC_DATA_MAX_LEN];
+
+ int ovirt_server_socket = ((int*)_data)[0],
+ client_socket = ((int*)_data)[1];
+
+ int nbytes;
+
+ DEBUG ("server/client thread starting up");
+
+ while(run_tunnel){
+ // grab vnc data
+ nbytes = read(ovirt_server_socket, vnc_data, VNC_DATA_MAX_LEN);
+ if(nbytes <= 0){
+ DEBUG ( "error reading data from server" );
+ break;
+ }
+ VERBOSE ("read %i bytes from server", nbytes);
+
+ // send network_data onto client
+ nbytes = write(client_socket, vnc_data, nbytes);
+ if(nbytes <= 0){
+ DEBUG ( "error writing data to client" );
+ break;
+ }
+ VERBOSE ("wrote %i bytes to client", nbytes);
+ }
+
+ DEBUG ("server/client thread completed");
+ return NULL;
+};
+
+static void close_socket(gpointer _socket, gpointer data){
+ shutdown(*(int*) _socket, 2);
+ close(*(int*) _socket);
+ free((int*) _socket);
+};
+
+static void wait_for_thread(gpointer _thread, gpointer data){
+ g_thread_join((GThread*)_thread);
+};
--
1.6.0.6
Jason Guiditta
2009-May-20 18:20 UTC
[Ovirt-devel] Re: [PATCH server] added ovirt vnc proxy server, to proxy vnc request to managed vms
ACK, this works well for me. Huge improvement with that refactor, IMO. 2 minor nits/suggestions, inline, no resend needed. On Tue, 2009-05-19 at 16:09 -0400, Mohammed Morsi wrote:> run on startup by default like the other ovirt services > --- > conf/ovirt-vnc-proxy | 49 ++++++++ > installer/modules/ovirt/manifests/ovirt.pp | 1 + > ovirt-server.spec.in | 5 + > src/vnc-proxy/vnc-proxy.rb | 167 ++++++++++++++++++++++++++++ > 4 files changed, 222 insertions(+), 0 deletions(-) > create mode 100755 conf/ovirt-vnc-proxy > create mode 100755 src/vnc-proxy/vnc-proxy.rb > > diff --git a/conf/ovirt-vnc-proxy b/conf/ovirt-vnc-proxy > new file mode 100755 > index 0000000..b3f565f > --- /dev/null > +++ b/conf/ovirt-vnc-proxy > @@ -0,0 +1,49 @@ > +#!/bin/bash > +# > +# > +# ovirt-vnc-proxy startup script for ovirt-vnc-proxy > +# > +# chkconfig: - 97 03 > +# description: ovirt-vnc-proxy proxies vnc requests to ovirt > +# managed vms. > +#Minor note - in db-omatic, we have the following, which should probably be here too: [ -r /etc/sysconfig/ovirt-rails ] && . /etc/sysconfig/ovirt-rails export RAILS_ENV="${RAILS_ENV:-production}"> + > +DAEMON=/usr/share/ovirt-server/vnc-proxy/vnc-proxy.rb > + > +. /etc/init.d/functions > + > +start() { > + echo -n "Starting ovirt-vnc-proxy: " > + daemon $DAEMON > + RETVAL=$? > + echo > +} > + > +stop() { > + echo -n "Shutting down ovirt-vnc-proxy: " > + killproc vnc-proxy.rb > + RETVAL=$? > + echo > +} > + > +case "$1" in > + start) > + start > + ;; > + stop) > + stop > + ;; > + restart) > + stop > + start > + ;; > + status) > + status $DAEMON > + RETVAL=$? > + ;; > + *) > + echo "Usage: ovirt-vnc-proxy {start|stop|restart|status}" > + exit 1 > + ;; > +esac > +exit $RETVAL > diff --git a/installer/modules/ovirt/manifests/ovirt.pp b/installer/modules/ovirt/manifests/ovirt.pp > index 03a93a7..d953ebe 100644 > --- a/installer/modules/ovirt/manifests/ovirt.pp > +++ b/installer/modules/ovirt/manifests/ovirt.pp > @@ -197,6 +197,7 @@ class ovirt::setup { > firewall_rule{"http": destination_port => "80"} > firewall_rule {"https": destination_port => '443'} > firewall_rule {"host-browser": destination_port => '12120'} > + firewall_rule {"vnc-proxy": destination_port => '5900'} > firewall_rule {"qpidd": destination_port => '5672'} > firewall_rule {"collectd": destination_port => '25826', protocol => 'udp'} > firewall_rule {"ntpd": destination_port => '123', protocol => 'udp'} > diff --git a/ovirt-server.spec.in b/ovirt-server.spec.in > index b4f7454..ad8777b 100644 > --- a/ovirt-server.spec.in > +++ b/ovirt-server.spec.in > @@ -104,6 +104,7 @@ touch %{buildroot}%{_localstatedir}/log/%{name}/db-omatic.log > %{__install} -Dp -m0755 %{pbuild}/conf/ovirt-mongrel-rails.sysconf %{buildroot}%{_sysconfdir}/sysconfig/ovirt-mongrel-rails > %{__install} -Dp -m0755 %{pbuild}/conf/ovirt-rails.sysconf %{buildroot}%{_sysconfdir}/sysconfig/ovirt-rails > %{__install} -Dp -m0755 %{pbuild}/conf/ovirt-taskomatic %{buildroot}%{_initrddir} > +%{__install} -Dp -m0755 %{pbuild}/conf/ovirt-vnc-proxy %{buildroot}%{_initrddir} > > # copy over all of the src directory... > %{__cp} -a %{pbuild}/src/* %{buildroot}%{app_root} > @@ -180,6 +181,7 @@ fi > %daemon_chkconfig_post -d ovirt-host-collect > %daemon_chkconfig_post -d ovirt-mongrel-rails > %daemon_chkconfig_post -d ovirt-taskomatic > +%daemon_chkconfig_post -d ovirt-vnc-proxy > > %preun > if [ "$1" = 0 ] ; then > @@ -188,11 +190,13 @@ if [ "$1" = 0 ] ; then > /sbin/service ovirt-host-collect stop > /dev/null 2>&1 > /sbin/service ovirt-mongrel-rails stop > /dev/null 2>&1 > /sbin/service ovirt-taskomatic stop > /dev/null 2>&1 > + /sbin/service ovirt-vnc-proxy stop > /dev/null 2>&1 > /sbin/chkconfig --del ovirt-host-browser > /sbin/chkconfig --del ovirt-db-omatic > /sbin/chkconfig --del ovirt-host-collect > /sbin/chkconfig --del ovirt-mongrel-rails > /sbin/chkconfig --del ovirt-taskomatic > + /sbin/chkconfig --del ovirt-vnc-proxy > fi > > %files > @@ -206,6 +210,7 @@ fi > %{_initrddir}/ovirt-host-collect > %{_initrddir}/ovirt-mongrel-rails > %{_initrddir}/ovirt-taskomatic > +%{_initrddir}/ovirt-vnc-proxy > %{_sysconfdir}/cron.d/%{name} > %config(noreplace) %{_sysconfdir}/sysconfig/ovirt-mongrel-rails > %config(noreplace) %{_sysconfdir}/sysconfig/ovirt-rails > diff --git a/src/vnc-proxy/vnc-proxy.rb b/src/vnc-proxy/vnc-proxy.rb > new file mode 100755 > index 0000000..5a1c35e > --- /dev/null > +++ b/src/vnc-proxy/vnc-proxy.rb > @@ -0,0 +1,167 @@ > +#!/usr/bin/ruby > +# > +# vnc-proxy.rb > +# ovirt vnc proxy server, relays ovirt encoded > +# vnc requests to correct node > +# Copyright (C) 2009 Red Hat, Inc. > +# Written by Mohammed Morsi <mmorsi at redhat.com> > +# > +# This program is free software; you can redistribute it and/or modify > +# it under the terms of the GNU General Public License as published by > +# the Free Software Foundation; version 2 of the License. > +# > +# This program is distributed in the hope that it will be useful, > +# but WITHOUT ANY WARRANTY; without even the implied warranty of > +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > +# GNU General Public License for more details. > +# > +# You should have received a copy of the GNU General Public License > +# along with this program; if not, write to the Free Software > +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, > +# MA 02110-1301, USA. A copy of the GNU General Public License is > +# also available at http://www.gnu.org/copyleft/gpl.html. > + > +$: << File.join(File.dirname(__FILE__), "../dutils") > + > +require 'dutils' > +require 'daemons' > +include Daemonize > + > +########### > + > +DEFAULT_VNC_PROXY_PORT = 5900 > +VM_NAME_MAX_LEN = 250 > +VNC_DATA_MAX_LEN = 800000 > + > +########### > + > +# clone of the taskomatic / dbomatic logger; > +# TODO move all of these seperate implementations into a single dutils module > +class Logger > + def format_message(severity, timestamp, progname, msg) > + "#{severity} #{timestamp} (#{$$}) #{msg}\n" > + end > +end > + > +$logfile = '/var/log/ovirt-server/vnc-proxy.log' > + > +########### > + > + > +class VncProxy > + > + # initialize vnc proxy > + def initialize() > + super()You can drop this call since VncProxy doesnt extend anything right now> + do_daemon = true > + port = DEFAULT_VNC_PROXY_PORT > + > + opts = OptionParser.new do |opts| > + opts.on("-h", "--help", "Print help message") do > + puts opts > + exit > + end > + opts.on("-n", "--nodaemon", "Run interactively (useful for debugging)") do |n| > + do_daemon = false > + end > + opts.on("-p", "--port", "Port to listen on") do |n| > + port = n.to_i > + end > + end > + begin > + opts.parse!(ARGV) > + rescue OptionParser::InvalidOption > + puts opts > + exit > + end > + > + if do_daemon > + # same issues as w/ dbomatic / taskomatic > + pwd = Dir.pwd > + daemonize > + Dir.chdir(pwd) > + @logger = Logger.new($logfile) > + else > + @logger = Logger.new(STDERR) > + end > + > + begin > + @server = TCPServer.open(port) > + rescue Exception => ex > + @logger.error "Error in vnc-proxy: #{ex}" > + @logger.error ex.backtrace > + > + # reraise ex, if we can't bind > + # to port server should die > + raise ex > + end > + > + @logger.info "vnc-proxy started." > + end > + > + # run vnc proxy > + def run > + continue = true > + while(continue) do > + begin > + Thread.start(@server.accept) do |client| > + begin > + @logger.info "client accepted" > + > + # first msg will be the vm description > + vm_description = client.recv(VM_NAME_MAX_LEN).to_s > + @logger.info "vm received: " + vm_description + ";" > + > + # lookup vm > + vm = Vm.find(:first, :conditions => [ "description = ?", vm_description ]) > + if vm && vm.state == "running" > + # connect to node > + @logger.info "connecting to node " + vm.host.hostname + ":" + vm.vnc_port.to_s > + node_socket = TCPSocket.open(vm.host.hostname, vm.vnc_port) > + > + # begin new thread to process server->client messages > + Thread.start do > + @logger.debug "listening for server->client data" > + while(true)do > + node_data = node_socket.recv VNC_DATA_MAX_LEN > + break if node_data.size <= 0 > + client.write node_data > + end > + end > + > + # process client -> server messages > + @logger.debug "listening for client->server data" > + while(true) do > + client_data = client.recv VNC_DATA_MAX_LEN > + break if client_data.size <= 0 > + node_socket.write client_data > + end > + > + node_socket.close > + @logger.info "node connection terminated" > + end > + rescue Exception => ex > + @logger.error "Error w/ vnc-proxy client (non-fatal) #{ex}" > + end > + > + client.close > + @logger.info "client connection terminated" > + end > + > + rescue Exception => ex > + continue = false > + @logger.error "terminating vnc proxy server #{ex}" > + end > + end > + > + @server.close > + @logger.info "server terminated" > + end > +end > + > +def main() > + vncproxy = VncProxy.new > + vncproxy.run > +end > + > +main()