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