[Ovirt-devel] [PATCH] The server was rewritten in Ruby. The managed node side was rewritten in Python.
Darryl L. Pierce
dpierce at redhat.com
Tue Jun 3 15:57:08 UTC 2008
---
ovirt-host-creator/common-pkgs.ks | 1 +
ovirt-host-creator/common-post.ks | 16 +-
ovirt-host-creator/identify.py | 103 +++++++++++
wui/src/host-browser/Makefile | 12 --
wui/src/host-browser/dbwriter.rb | 66 -------
wui/src/host-browser/host-browser.c | 286 -----------------------------
wui/src/host-browser/host-browser.rb | 218 ++++++++++++++++++++++
wui/src/host-browser/test-host-browser.rb | 220 ++++++++++++++++++++++
8 files changed, 550 insertions(+), 372 deletions(-)
create mode 100755 ovirt-host-creator/identify.py
delete mode 100644 wui/src/host-browser/Makefile
delete mode 100755 wui/src/host-browser/dbwriter.rb
delete mode 100644 wui/src/host-browser/host-browser.c
create mode 100755 wui/src/host-browser/host-browser.rb
create mode 100755 wui/src/host-browser/test-host-browser.rb
diff --git a/ovirt-host-creator/common-pkgs.ks b/ovirt-host-creator/common-pkgs.ks
index 618a73a..7433d97 100644
--- a/ovirt-host-creator/common-pkgs.ks
+++ b/ovirt-host-creator/common-pkgs.ks
@@ -8,6 +8,7 @@ chkconfig
rootfiles
dhclient
libvirt
+libvirt-python
openssh-clients
openssh-server
iscsi-initiator-utils
diff --git a/ovirt-host-creator/common-post.ks b/ovirt-host-creator/common-post.ks
index 088f920..46ad3c8 100644
--- a/ovirt-host-creator/common-post.ks
+++ b/ovirt-host-creator/common-post.ks
@@ -18,14 +18,14 @@ cat > /etc/init.d/ovirt-functions << \EOF
# -*-Shell-script-*-
find_srv() {
- local dnsreply
- dnsreply=$(dig +short -t srv _$1._$2.$(dnsdomainname))
- if [ $? -eq 0 ]; then
- set _ $dnsreply; shift
- SRV_HOST=$4; SRV_PORT=$3
- else
- SRV_HOST=; SRV_PORT=
- fi
+ local dnsreply
+ dnsreply=$(dig +short -t srv _$1._$2.$(dnsdomainname))
+ if [ $? -eq 0 ]; then
+ set _ $dnsreply; shift
+ SRV_HOST=$4; SRV_PORT=$3
+ else
+ SRV_HOST=; SRV_PORT=
+ fi
}
EOF
diff --git a/ovirt-host-creator/identify.py b/ovirt-host-creator/identify.py
new file mode 100755
index 0000000..a34ddf9
--- /dev/null
+++ b/ovirt-host-creator/identify.py
@@ -0,0 +1,103 @@
+#!/usr/bin/python -Wall
+#
+# Copyright (C) 2008 Red Hat, Inc.
+# Written by Darryl L. Pierce <dpierce 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.
+
+import socket
+import libvirt
+import sys
+import os
+
+class IdentifyNode:
+ """This class allows the managed node to connect to the WUI host
+ and notify it that the node is awake and ready to participate."""
+
+ def __init__(self, server_name, server_port):
+ conn = libvirt.openReadOnly(None)
+ info = conn.getInfo()
+ self.host_info = {
+ "UUID" : "foo",
+ "ARCH" : info[0],
+ "MEMSIZE" : "%d" % info[1],
+ "NUMCPUS" : "%d" % info[2],
+ "CPUSPEED" : "%d" % info[3],
+ "HOSTNAME" : conn.getHostname()
+ }
+
+ print(self.host_info)
+
+ self.server_name = server_name
+ self.server_port = int(server_port)
+
+ self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ self.socket.connect((self.server_name,self.server_port))
+ self.input = self.socket.makefile('rb', 0)
+ self.output = self.socket.makefile('wb', 0)
+
+ def start_conversation(self):
+ print("Connecting to server")
+
+ response = self.input.readline().strip()
+ if response == 'HELLO?':
+ self.output.write("HELLO!\n")
+ else:
+ raise TypeError, "Received invalid conversation starter: %s" % response
+
+ def send_host_info(self):
+ print("Starting information exchange...")
+
+ response = self.input.readline().strip()
+ if response == 'INFO?':
+ for name in self.host_info.keys():
+ self.send_host_info_element(name,self.host_info[name])
+ else:
+ raise TypeError, "Received invalid info marker: %s" % response
+
+ print("Ending information exchange...")
+ self.output.write("ENDINFO\n")
+ response = self.input.readline().strip()
+
+ if response[1:3] == 'KVNO':
+ self.keytab = response[:5]
+ else:
+ raise TypeError, "Did not receive a keytab response: '%s'" % response
+
+ def send_host_info_element(self,key,value):
+ print("Sending: " + key + "=" + value)
+ print(type(value))
+ self.output.write(key + "=" + value + "\n")
+ response = self.input.readline().strip()
+
+ if response != "ACK " + key:
+ raise TypeError, "Received bad acknolwedgement for field: %s" % key
+
+ def get_keytab(self,tabfile):
+ print("Retrieving keytab information: %s" % self.keytab)
+
+ def end_conversation(self):
+ print("Disconnecting from server")
+
+
+if __name__ == '__main__':
+
+ identifier = IdentifyNode(sys.argv[1], sys.argv[2])
+
+ identifier.start_conversation()
+ identifier.send_host_info()
+ identifier.get_keytab()
+ identifier.end_conversation()
diff --git a/wui/src/host-browser/Makefile b/wui/src/host-browser/Makefile
deleted file mode 100644
index 3029be9..0000000
--- a/wui/src/host-browser/Makefile
+++ /dev/null
@@ -1,12 +0,0 @@
-CC=gcc
-CFLAGS+=-g -Wall
-OBJS=host-browser.o
-LIBS=-lavahi-client
-
-all: host-browser
-
-host-browser: $(OBJS)
- $(CC) $(CFLAGS) -o host-browser $(OBJS) $(LIBS)
-
-clean:
- rm -f *.o *~ host-browser
diff --git a/wui/src/host-browser/dbwriter.rb b/wui/src/host-browser/dbwriter.rb
deleted file mode 100755
index 396ef60..0000000
--- a/wui/src/host-browser/dbwriter.rb
+++ /dev/null
@@ -1,66 +0,0 @@
-#!/usr/bin/ruby
-#
-# Copyright (C) 2008 Red Hat, Inc.
-# Written by Chris Lalancette <clalance 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 'rubygems'
-require 'libvirt'
-require 'dutils'
-
-if ARGV.length != 1
- exit
-end
-
-# connects to the db in here
-require 'dutils'
-
-# make sure we get our credentials up-front
-get_credentials
-
-begin
- conn = Libvirt::open("qemu+tcp://" + ARGV[0] + "/system")
- info = conn.node_get_info
- conn.close
-rescue
- # if we can't contact the host or get details for some reason, we just
- # don't do anything and don't add anything to the database
- puts "Failed connecting to host " + ARGV[0]
- exit
-end
-
-# we could destroy the credentials, but another process might be using them
-# (in particular, the taskomatic). Just leave them around, it shouldn't hurt
-
-
-# FIXME: we need a better way to get a UUID, rather than the hostname
-$host = Host.find(:first, :conditions => [ "uuid = ?", ARGV[0]])
-
-if $host == nil
- Host.new(
- "uuid" => ARGV[0],
- "hostname" => ARGV[0],
- "num_cpus" => info.cpus,
- "cpu_speed" => info.mhz,
- "arch" => info.model,
- "memory" => info.memory,
- "is_disabled" => 0,
- "hardware_pool" => HardwarePool.get_default_pool
- ).save
-end
diff --git a/wui/src/host-browser/host-browser.c b/wui/src/host-browser/host-browser.c
deleted file mode 100644
index 23af786..0000000
--- a/wui/src/host-browser/host-browser.c
+++ /dev/null
@@ -1,286 +0,0 @@
-/*
- * Copyright (C) 2008 Red Hat, Inc.
- * Written by Chris Lalancette <clalance 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <assert.h>
-#include <stdlib.h>
-#include <time.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/wait.h>
-#include <netdb.h>
-#include <arpa/inet.h>
-#include <string.h>
-#include <errno.h>
-#include <signal.h>
-#include <unistd.h>
-
-#include <avahi-client/client.h>
-#include <avahi-client/lookup.h>
-
-#include <avahi-common/simple-watch.h>
-#include <avahi-common/malloc.h>
-#include <avahi-common/error.h>
-
-#ifndef DBWRITER_PATH
-#define DBWRITER_PATH "./dbwriter.rb"
-#endif
-
-static AvahiSimplePoll *simple_poll = NULL;
-
-static void usage(void)
-{
- fprintf(stderr, "Usage: host-browser [OPTIONS]\n");
- fprintf(stderr, "OPTIONS:\n\n");
- fprintf(stderr, " -d\t\tRun in daemon mode (the default)\n");
- fprintf(stderr, " -h\t\tPrint this help message\n");
- fprintf(stderr, " -n\t\tRun in interactive (non-daemon) mode (useful for debugging)\n");
- exit(1);
-}
-
-static void sig_chld(int signo)
-{
- int status;
-
- if (waitpid(-1, &status, WNOHANG) < 0) {
- fprintf(stderr, "Error doing waitpid for child\n");
- return;
- }
-}
-
-// the function to make a daemon out of this program
-static int daemonize(void)
-{
- pid_t pid;
-
- if((pid=fork()) < 0){
- return -1;
- }
- else if (pid != 0){
- exit(0);
- }
-
- setsid();
-
- // umask(0);
-
- return 0;
-}
-
-static void resolve_callback(AvahiServiceResolver *r, AvahiIfIndex interface,
- AVAHI_GCC_UNUSED AvahiProtocol protocol,
- AvahiResolverEvent event, const char *name,
- const char *type, const char *domain,
- const char *host_name, const AvahiAddress *address,
- uint16_t port, AvahiStringList *txt,
- AvahiLookupResultFlags flags,
- AVAHI_GCC_UNUSED void* userdata)
-{
- assert(r);
-
- /* Called whenever a service has been resolved successfully or timed out */
-
- switch (event) {
- case AVAHI_RESOLVER_FAILURE:
- break;
-
- case AVAHI_RESOLVER_FOUND: {
- char a[AVAHI_ADDRESS_STR_MAX];
- in_addr_t remote;
- struct hostent *host;
- char *argv[3];
- pid_t pid;
- int ret;
- char *libvirt_hostname;
-
- avahi_address_snprint(a, sizeof(a), address);
-
- remote = inet_addr(a);
- host = gethostbyaddr(&remote, sizeof(remote), AF_INET);
- if (host == NULL) {
- // we failed to resolve the address to a hostname; we'll just try
- // with the IP address
- libvirt_hostname = a;
- }
- else {
- libvirt_hostname = host->h_name;
- }
-
- argv[0] = DBWRITER_PATH;
- argv[1] = libvirt_hostname;
- argv[2] = NULL;
-
- pid = fork();
-
- if (pid < 0) {
- fprintf(stderr, "Failed to fork: %s\n",strerror(errno));
- }
- else if (pid == 0) {
- // child
- ret = execv(DBWRITER_PATH, argv);
- if (ret < 0) {
- fprintf(stderr, "Failed to exec %s: %s\n",DBWRITER_PATH,strerror(errno));
- }
- }
- else {
- // parent, do nothing; we'll catch the child exits with SIGCHLD
- }
-
- break;
- }
- }
-
- avahi_service_resolver_free(r);
-}
-
-static void browse_callback(AvahiServiceBrowser *b, AvahiIfIndex interface,
- AvahiProtocol protocol, AvahiBrowserEvent event,
- const char *name, const char *type,
- const char *domain,
- AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
- void* userdata)
-{
- AvahiClient *c = userdata;
- assert(b);
-
- /* Called whenever a new services becomes available on the LAN or is removed from the LAN */
-
- switch (event) {
- case AVAHI_BROWSER_FAILURE:
-
- avahi_simple_poll_quit(simple_poll);
- return;
-
- case AVAHI_BROWSER_NEW:
- /* We ignore the returned resolver object. In the callback
- function we free it. If the server is terminated before
- the callback function is called the server will free
- the resolver for us. */
-
- if (!(avahi_service_resolver_new(c, interface, protocol, name, type, domain, AVAHI_PROTO_UNSPEC, 0, resolve_callback, c)))
- fprintf(stderr, "Failed to resolve service '%s': %s\n", name, avahi_strerror(avahi_client_errno(c)));
-
- break;
-
- case AVAHI_BROWSER_REMOVE:
- break;
-
- case AVAHI_BROWSER_ALL_FOR_NOW:
- case AVAHI_BROWSER_CACHE_EXHAUSTED:
- break;
- }
-}
-
-static void client_callback(AvahiClient *c, AvahiClientState state,
- AVAHI_GCC_UNUSED void * userdata)
-{
- assert(c);
-
- /* Called whenever the client or server state changes */
-
- if (state == AVAHI_CLIENT_FAILURE) {
- fprintf(stderr, "Server connection failure: %s\n", avahi_strerror(avahi_client_errno(c)));
- avahi_simple_poll_quit(simple_poll);
- }
-}
-
-int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char *argv[])
-{
- AvahiClient *client = NULL;
- AvahiServiceBrowser *sb = NULL;
- int error;
- int ret = 1;
- int daemon_mode = 1;
- int c;
- struct sigaction act;
-
- while ((c = getopt(argc, argv,":dhn")) != -1) {
- switch(c) {
- case 'd':
- daemon_mode = 1;
- break;
- case 'h':
- usage();
- break;
- case 'n':
- daemon_mode = 0;
- break;
- default:
- usage();
- break;
- }
- }
-
- if ((argc - optind) != 0) {
- usage();
- }
-
- if (daemon_mode) {
- daemonize();
- }
-
- act.sa_handler = sig_chld;
- sigemptyset(&act.sa_mask);
- act.sa_flags = SA_NOCLDSTOP;
- sigaction(SIGCHLD, &act, NULL);
-
- /* Allocate main loop object */
- if (!(simple_poll = avahi_simple_poll_new())) {
- fprintf(stderr, "Failed to create simple poll object.\n");
- goto fail;
- }
-
- /* Allocate a new client */
- client = avahi_client_new(avahi_simple_poll_get(simple_poll), 0, client_callback, NULL, &error);
-
- /* Check wether creating the client object succeeded */
- if (!client) {
- fprintf(stderr, "Failed to create client: %s\n", avahi_strerror(error));
- goto fail;
- }
-
- /* Create the service browser */
- if (!(sb = avahi_service_browser_new(client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, "_libvirt._tcp", NULL, 0, browse_callback, client))) {
- fprintf(stderr, "Failed to create service browser: %s\n", avahi_strerror(avahi_client_errno(client)));
- goto fail;
- }
-
- /* Run the main loop */
- avahi_simple_poll_loop(simple_poll);
-
- ret = 0;
-
-fail:
-
- /* Cleanup things */
- if (sb)
- avahi_service_browser_free(sb);
-
- if (client)
- avahi_client_free(client);
-
- if (simple_poll)
- avahi_simple_poll_free(simple_poll);
-
- return ret;
-}
diff --git a/wui/src/host-browser/host-browser.rb b/wui/src/host-browser/host-browser.rb
new file mode 100755
index 0000000..e2763a9
--- /dev/null
+++ b/wui/src/host-browser/host-browser.rb
@@ -0,0 +1,218 @@
+#!/usr/bin/ruby -Wall
+#
+# Copyright (C) 2008 Red Hat, Inc.
+# Written by Darryl L. Pierce <dpierce 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 'rubygems'
+require 'libvirt'
+require 'dutils'
+
+require 'socket'
+require 'krb5_auth'
+include Krb5Auth
+
+include Socket::Constants
+
+require 'dutils'
+
+# +HostBrowser+ communicates with the a managed node. It retrieves specific information
+# about the node and then updates the list of active nodes for the WUI.
+#
+class HostBrowser
+ attr_accessor :logfile
+ attr_accessor :keytab_dir
+ attr_accessor :keytab_filename
+
+ def initialize(session)
+ @session = session
+ @log_prefix = "[#{session.peeraddr[3]}] "
+ @logfile = '/var/log/ovirt-wui/host-browser.log'
+ @keytab_dir = '/usr/share/ipa/html/'
+ end
+
+ # Ensures the conversation starts properly.
+ #
+ def begin_conversation
+ puts "#{@log_prefix} Begin conversation"
+ @session.write("HELLO?\n")
+
+ response = @session.readline.chomp
+ raise Exception.new("received #{response}, expected HELLO!") unless response == "HELLO!"
+ end
+
+ # Requests node information from the remote system.
+ #
+ def get_remote_info
+ puts "#{@log_prefix} Begin remote info collection"
+ result = {}
+ result['IPADDR'] = @session.peeraddr[3]
+ @session.write("INFO?\n")
+
+ loop do
+ info = @session.readline.chomp
+
+ break if info == "ENDINFO"
+
+ raise Exception.new("ERRINFO! Excepted key=value : #{info}\n") unless info =~ /[\w]+[\s]*=[\w]/
+
+ key, value = info.split("=")
+
+ puts "#{@log_prefix} ::Received - #{key}:#{value}"
+ result[key] = value
+
+ @session.write("ACK #{key}\n")
+ end
+
+ return result
+ end
+
+ # Writes the supplied host information to the database.
+ #
+ def write_host_info(host_info)
+ ensure_present(host_info,'UUID')
+ ensure_present(host_info,'HOSTNAME')
+ ensure_present(host_info,'NUMCPUS')
+ ensure_present(host_info,'CPUSPEED')
+ ensure_present(host_info,'ARCH')
+ ensure_present(host_info,'MEMSIZE')
+
+ puts "Searching for existing host record..."
+ host = Host.find(:first, :conditions => ["uuid = ?", host_info['UUID']])
+
+ if host == nil
+ begin
+ puts "Creating a new record for #{host_info['HOSTNAME']}..."
+
+ Host.new(
+ "uuid" => host_info['UUID'],
+ "hostname" => host_info['HOSTNAME'],
+ "num_cpus" => host_info['NUMCPUS'],
+ "cpu_speed" => host_info['CPUSPEED'],
+ "arch" => host_info['ARCH'],
+ "memory" => host_info['MEMSIZE'],
+ "is_disabled" => 0,
+ "hardware_pool" => HardwarePool.get_default_pool).save
+ rescue Exception => error
+ puts "Error while creating record: #{error.message}"
+ end
+ end
+
+ return host
+ end
+
+ # Ends the conversation, notifying the user of the key version number.
+ #
+ def end_conversation(kvno)
+ puts "#{@log_prefix} Ending conversation"
+
+ @session.write("KVNO #{kvno}\n")
+
+ response = @session.readline.chomp
+
+ raise Exception.new("ERROR! Malformed response : expected ACK, got #{response}") unless response == "ACK"
+
+ @session.write("BYE\n");
+ @session.shutdown(2)
+ end
+
+ # Creates a keytab if one is needed, returning the filename.
+ #
+ def create_keytab(host_info, krb5_arg = nil)
+ krb5 = krb5_arg || Krb5.new
+
+ default_realm = krb5.get_default_realm
+ libvirt_princ = 'libvirt/' + host_info['HOSTNAME'] + '@' + default_realm
+ outfile = host_info['IPADDR'] + '-libvirt.tab'
+ @keytab_filename = @keytab_dir + outfile
+
+ # TODO need a way to test this portion
+ unless defined? TESTING
+ puts "Writing keytab file: #{@keytab_filename}"
+ kadmin_local('addprinc -randkey ' + libvirt_princ)
+ kadmin_local('ktadd -k ' + @keytab_filename + ' ' + libvirt_princ)
+
+ File.chmod(0644, at keytab_filename)
+ end
+
+ return @keytab_filename
+ end
+
+ private
+
+ # Private method to ensure that a required field is present.
+ #
+ def ensure_present(host_info,key)
+ raise Exception.new("ERROR! Missing '#{key}'...") if host_info[key] == nil
+ end
+
+ # Executes an external program to support the keytab function.
+ #
+ def kadmin_local(command)
+ system("/usr/kerberos/sbin/kadmin -q '" + command + "'")
+ end
+end
+
+def entry_point(server)
+ while(session = server.accept)
+ child = fork do
+ puts "Connected to #{session.peeraddr[3]}"
+
+ begin
+ browser = HostBrowser.new(session)
+
+ # redirect output to the logsg
+ STDOUT.reopen browser.logfile, 'a'
+ STDERR.reopen STDOUT
+
+ browser.begin_conversation
+ host_info = browser.get_remote_info
+ browser.write_host_info(host_info)
+ keytab = browser.create_keytab(host_info)
+ browser.end_conversation(keytab)
+ rescue Exception => error
+ session.write("ERROR #{error.message}\n")
+ puts "ERROR #{error.message}"
+ end
+
+ session.shutdown(2) unless session.closed?
+
+ puts "Disconnected from #{session.peeraddr[3]}"
+ end
+
+ Process.detach(child)
+ end
+end
+
+unless defined?(TESTING)
+ server = TCPServer.new("",12120)
+
+ # The main entry point.
+ #
+ unless ARGV[0] == "-n"
+ pid = fork do
+ # TODO need to pull the port from the SRV record
+ entry_point(server)
+ end
+
+ Process.detach(pid)
+ else
+ entry_point(server)
+ end
+end
diff --git a/wui/src/host-browser/test-host-browser.rb b/wui/src/host-browser/test-host-browser.rb
new file mode 100755
index 0000000..2a05181
--- /dev/null
+++ b/wui/src/host-browser/test-host-browser.rb
@@ -0,0 +1,220 @@
+#!/usr/bin/ruby -Wall
+#
+# Copyright (C) 2008 Red Hat, Inc.
+# Written by Darryl L. Pierce <dpierce 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.
+
+require File.dirname(__FILE__) + '/../test/test_helper'
+require 'test/unit'
+require 'flexmock/test_unit'
+
+TESTING=true
+
+require 'host-browser'
+
+class TestHostBrowser < Test::Unit::TestCase
+
+ def setup
+ @session = flexmock('session')
+ @session.should_receive(:peeraddr).at_least.once.returns { [nil,nil,nil,"192.168.2.255"] }
+
+ @krb5 = flexmock('krb5')
+
+ @browser = HostBrowser.new(@session)
+ @browser.logfile = './unit-test.log'
+ @browser.keytab_dir = '/var/temp/'
+
+ # default host info
+ @host_info = {}
+ @host_info['UUID'] = 'node1'
+ @host_info['IPADDR'] = '192.168.2.2'
+ @host_info['HOSTNAME'] = 'node1.ovirt.redhat.com'
+ @host_info['NUMCPUS'] = '3'
+ @host_info['CPUSPEED'] = '3'
+ @host_info['ARCH'] = 'x86_64'
+ @host_info['MEMSIZE'] = '16384'
+ @host_info['DISABLED'] = '0'
+ end
+
+ # Ensures that the server raises an exception when it receives an
+ # improper handshake response.
+ #
+ def test_begin_conversation_with_improper_response_to_greeting
+ @session.should_receive(:write).with("HELLO?\n").once().returns { |greeting| greeting.length }
+ @session.should_receive(:readline).once().returns { "SUP?" }
+
+ assert_raise(Exception) { @browser.begin_conversation }
+ end
+
+ # Ensures the server accepts a proper response from the remote system.
+ #
+ def test_begin_conversation
+ @session.should_receive(:write).with("HELLO?\n").once().returns { |greeting| greeting.length }
+ @session.should_receive(:readline).once().returns { "HELLO!\n" }
+
+ assert_nothing_raised(Exception) { @browser.begin_conversation }
+ end
+
+ # Ensures that the server raises an exception when it receives
+ # poorly formed data while exchanging system information.
+ #
+ def test_get_info_with_bad_handshake
+ @session.should_receive(:write).with("INFO?\n").once().returns { |request| request.length }
+ @session.should_receive(:readline).once().returns { "key1=value1\n" }
+ @session.should_receive(:write).with("ACK key1\n").once().returns { |request| request.length }
+ @session.should_receive(:readline).once().returns { "farkledina\n" }
+
+ assert_raise(Exception) { @browser.get_remote_info }
+ end
+
+ # Ensures that, if an info field is missing a key, the server raises
+ # an exception.
+ #
+ def test_get_info_with_missing_key
+ @session.should_receive(:write).with("INFO?\n").once().returns { |request| request.length }
+ @session.should_receive(:readline).once().returns { "=value1\n" }
+
+ assert_raise(Exception) { @browser.get_remote_info }
+ end
+
+ # Ensures that, if an info field is missing a value, the server raises
+ # an exception.
+ #
+ def test_get_info_with_missing_value
+ @session.should_receive(:write).with("INFO?\n").once().returns { |request| request.length }
+ @session.should_receive(:readline).once().returns { "key1=\n" }
+
+ assert_raise(Exception) { @browser.get_remote_info }
+ end
+
+ # Ensures that, if the server gets a poorly formed ending statement, it
+ # raises an exception.
+ #
+ def test_get_info_with_invalid_end
+ @session.should_receive(:write).with("INFO?\n").once().returns { |request| request.length }
+ @session.should_receive(:readline).once().returns { "key1=value1\n" }
+ @session.should_receive(:write).with("ACK key1\n").once().returns { |request| request.length }
+ @session.should_receive(:readline).once().returns { "ENDIFNO\n" }
+
+ assert_raise(Exception) { @browser.get_remote_info }
+ end
+
+ # Ensures that a well-formed transaction works as expected.
+ #
+ def test_get_info
+ @session.should_receive(:write).with("INFO?\n").once().returns { |request| request.length }
+ @session.should_receive(:readline).once().returns { "key1=value1\n" }
+ @session.should_receive(:write).with("ACK key1\n").once().returns { |request| request.length }
+ @session.should_receive(:readline).once().returns { "key2=value2\n" }
+ @session.should_receive(:write).with("ACK key2\n").once().returns { |request| request.length }
+ @session.should_receive(:readline).once().returns { "ENDINFO\n" }
+
+ info = @browser.get_remote_info
+
+ assert_equal 3,info.keys.size, "Should contain two keys"
+ assert info.include?("IPADDR")
+ assert info.include?("key1")
+ assert info.include?("key2")
+ end
+
+ # Ensures the host browser generates a keytab as expected.
+ #
+ def test_create_keytab
+ @krb5.should_receive(:get_default_realm).once().returns { "ovirt-test-realm" }
+
+ result = @browser.create_keytab(@host_info, at krb5)
+
+ assert_equal @browser.keytab_filename, result, "Should have returned the keytab filename"
+ end
+
+ # Ensures that, if no UUID is present, the server raises an exception.
+ #
+ def test_write_host_info_with_missing_uuid
+ @host_info['UUID'] = nil
+
+ assert_raise(Exception) { @browser.write_host_info(@host_info) }
+ end
+
+ # Ensures that, if the hostname is missing, the server
+ # raises an exception.
+ #
+ def test_write_host_info_with_missing_hostname
+ @host_info['HOSTNAME'] = nil
+
+ assert_raise(Exception) { @browser.write_host_info(@host_info) }
+ end
+
+ # Ensures that, if the number of CPUs is missing, the server raises an exception.
+ #
+ def test_write_host_info_with_missing_numcpus
+ @host_info['NUMCPUS'] = nil
+
+ assert_raise(Exception) { @browser.write_host_info(@host_info) }
+ end
+
+ # Ensures that, if the CPU speed is missing, the server raises an exception.
+ #
+ def test_write_host_info_with_missing_cpuspeed
+ @host_info['CPUSPEED'] = nil
+
+ assert_raise(Exception) { @browser.write_host_info(@host_info) }
+ end
+
+ # Ensures that, if the architecture is missing, the server raises an exception.
+ #
+ def test_write_host_info_with_missing_arch
+ @host_info['ARCH'] = nil
+
+ assert_raise(Exception) { @browser.write_host_info(@host_info) }
+ end
+
+ # Ensures that, if the memory size is missing, the server raises an exception.
+ #
+ def test_write_host_info_info_with_missing_memsize
+ @host_info['MEMSIZE'] = nil
+
+ assert_raise(Exception) { @browser.write_host_info(@host_info) }
+ end
+
+ # Ensures that the host information is properly moved to a persisted object
+ # and saved.
+ #
+ def test_write_host_info
+ result = @browser.write_host_info(@host_info)
+
+ assert result, "No persisted object returned"
+ assert_match @host_info['UUID'], result.uuid, "UUID was not persisted"
+ assert_match @host_info['HOSTNAME'], result.hostname, "Hostname was not persisted"
+ assert_match @host_info['NUMCPUS'], "#{result.num_cpus}", "Number of CPUs was not persisted"
+ assert_match @host_info['CPUSPEED'], "#{result.cpu_speed}", "CPU speed was not persisted"
+ assert_match @host_info['ARCH'], "#{result.arch}", "Architecture was not persisted"
+ assert_match @host_info['MEMSIZE'], "#{result.memory}", "Memory size was not persisted"
+ end
+
+ # Ensures that, if a keytab is present and a key version number available,
+ # the server ends the conversation by returning the key version number.
+ #
+ def test_end_conversation
+ @session.should_receive(:write).with("KVNO 12345\n").once().returns { |request| request.length }
+ @session.should_receive(:readline).once().returns { "ACK\n" }
+ @session.should_receive(:write).with("BYE\n").once().returns { |request| request.length }
+ @session.should_receive(:shutdown).with(2).once()
+
+ assert_nothing_raised(Exception) { @browser.end_conversation(12345) }
+ end
+
+end
--
1.5.5.1
More information about the ovirt-devel
mailing list