Darryl L. Pierce
2008-Jun-23 12:09 UTC
[Ovirt-devel] [PATCH] Rewriting the identify-node piece into two managed node units to submit a wake-up request and an identify request. The bulk of the ovirt-identify work is done via a C application.
Signed-off-by: Darryl L. Pierce <dpierce at redhat.com> --- build-all.sh | 1 + ovirt-managed-node/AUTHOR | 1 + ovirt-managed-node/ChangeLog | 2 + ovirt-managed-node/Makefile | 76 ++++ ovirt-managed-node/NEWS | 2 + ovirt-managed-node/README | 2 + ovirt-managed-node/ovirt-awake | 82 ++++ ovirt-managed-node/ovirt-identify | 46 ++ ovirt-managed-node/ovirt-identify-node.c | 438 ++++++++++++++++++++ ovirt-managed-node/ovirt-managed-node.spec | 45 ++ wui/src/host-browser/host-browser.rb | 106 +++-- wui/src/host-browser/test-host-browser-awaken.rb | 94 +++++ wui/src/host-browser/test-host-browser-identify.rb | 162 ++++++++ wui/src/host-browser/test-host-browser.rb | 204 --------- 14 files changed, 1011 insertions(+), 250 deletions(-) create mode 100644 ovirt-managed-node/AUTHOR create mode 100644 ovirt-managed-node/ChangeLog create mode 100644 ovirt-managed-node/Makefile create mode 100644 ovirt-managed-node/NEWS create mode 100644 ovirt-managed-node/README create mode 100644 ovirt-managed-node/ovirt-awake create mode 100644 ovirt-managed-node/ovirt-identify create mode 100644 ovirt-managed-node/ovirt-identify-node.c create mode 100644 ovirt-managed-node/ovirt-managed-node.spec create mode 100755 wui/src/host-browser/test-host-browser-awaken.rb create mode 100755 wui/src/host-browser/test-host-browser-identify.rb delete mode 100755 wui/src/host-browser/test-host-browser.rb diff --git a/build-all.sh b/build-all.sh index 8c3a7e9..d064f29 100755 --- a/build-all.sh +++ b/build-all.sh @@ -126,6 +126,7 @@ fi set -e # build ovirt-wui RPM +# also build the ovirt-managed-node RPM if [ $update_wui = 1 ]; then cd $BASE/wui rm -rf rpm-build diff --git a/ovirt-managed-node/AUTHOR b/ovirt-managed-node/AUTHOR new file mode 100644 index 0000000..d37a107 --- /dev/null +++ b/ovirt-managed-node/AUTHOR @@ -0,0 +1 @@ +Darryl L. Pierce <dpierce at redhat.com> diff --git a/ovirt-managed-node/ChangeLog b/ovirt-managed-node/ChangeLog new file mode 100644 index 0000000..61dc8cc --- /dev/null +++ b/ovirt-managed-node/ChangeLog @@ -0,0 +1,2 @@ +* Tue 17 Jun 2008 Darryl L. Pierce <dpierce at redhat.com> - 0.0.1-1 +- Created the initial RPM structure. diff --git a/ovirt-managed-node/Makefile b/ovirt-managed-node/Makefile new file mode 100644 index 0000000..564be1d --- /dev/null +++ b/ovirt-managed-node/Makefile @@ -0,0 +1,76 @@ +# 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. + +CC=gcc +CFLAGS=-Wall -c -g +LFLAGS=-lvirt +OBJECTS=ovirt-identify-node.o +TARGET=ovirt-identify-node +SPECFILE=ovirt-managed-node.spec +NAME=ovirt-managed-node +VERSION=0.1 +NV=$(NAME)-$(VERSION) +RPM_FLAGS=\ + --define "_topdir %(pwd)/rpm-build" \ + --define "_builddir %{_topdir}" \ + --define "_rpmdir %{_topdir}" \ + --define "_srcrpmdir %{_topdir}" \ + --define '_rpmfilename %%{NAME}-%%{VERSION}-%%{RELEASE}.%%{ARCH}.rpm' \ + --define "_specdir %{_topdir}" \ + --define "_sourcedir %{_topdir}" +SOURCES=\ + AUTHOR \ + ChangeLog \ + Makefile \ + NEWS \ + ovirt-awake \ + ovirt-identify \ + ovirt-identify-node.c \ + ovirt-managed-node.spec \ + README + + +ALL: $(TARGET) + +.c.o: + $(CC) $(CFLAGS) $< -o $@ + +clean: + rm -rf $(OBJECTS) $(TARGET) $(NV) rpm-build + +bumpgit: + -echo "$(VERSION) $(GITRELEASE)" > version + +bumprelease: + -echo "$(VERSION) $(NEWRELEASE)" > version + +setversion: + -echo "$(VERSION) 0" > version + +$(TARGET): $(OBJECTS) + $(CC) -o $@ $(OBJECTS) $(LFLAGS) + +tar: clean $(TARGET) + mkdir -p $(NV) + cp -a $(SOURCES) $(NV) + mkdir -p rpm-build + tar zcvf rpm-build/$(NV).tar $(NV) + rm -rf $(NV) + +rpms: tar + rpmbuild $(RPM_FLAGS) -ba $(SPECFILE) diff --git a/ovirt-managed-node/NEWS b/ovirt-managed-node/NEWS new file mode 100644 index 0000000..139597f --- /dev/null +++ b/ovirt-managed-node/NEWS @@ -0,0 +1,2 @@ + + diff --git a/ovirt-managed-node/README b/ovirt-managed-node/README new file mode 100644 index 0000000..139597f --- /dev/null +++ b/ovirt-managed-node/README @@ -0,0 +1,2 @@ + + diff --git a/ovirt-managed-node/ovirt-awake b/ovirt-managed-node/ovirt-awake new file mode 100644 index 0000000..f7f643b --- /dev/null +++ b/ovirt-managed-node/ovirt-awake @@ -0,0 +1,82 @@ +#!/bin/bash +# +# ovirt-awake Notifies the oVirt server that a managed node is +# starting up. +# +# 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. + +# Source function library +. /etc/init.d/ovirt-functions + +start () { + find-server identify tcp + + connect-to-server + + receive-text + + if [ $REPLY == "HELLO?" ]; then + echo "Starting wakeup conversation." + + send-text "HELLO!" + + read 0<&3 + + if [ $REPLY == "MODE?" ]; then + send-text "AWAKEN" + + receive-text + + KEYTAB=`echo $REPLY | awk '{ print $2 }'` + + if [ -n $KEYTAB ]; then + echo "Retrieving keytab: '$KEYTAB'" + + wget $KEYTAB --output-document=$KEYTAB_FILE + else + echo "No keytab to retrieve" + fi + else + echo "Did not get a mode request." + fi + else + echo "Did not get a proper startup marker." + fi + + echo "Disconnecting." + + disconnect-from-server +} + +case "$1" in + start) + KEYTAB_FILE=$2 + SRV_HOST=$3 + SRV_PORT=$4 + start + RETVAL=$? + ;; + + *) + echo "Usage: $0 start" + RETVAL=2 + ;; +esac + +exit $RETVAL diff --git a/ovirt-managed-node/ovirt-identify b/ovirt-managed-node/ovirt-identify new file mode 100644 index 0000000..53f21b0 --- /dev/null +++ b/ovirt-managed-node/ovirt-identify @@ -0,0 +1,46 @@ +#!/bin/bash +# +# ovirt-identify Submits managed node details to the central server. +# +# 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. + +# Source function library +. /etc/init.d/ovirt-functions + +start () { + find-server identify tcp + + /sbin/ovirt-identify-node -s $SRV_HOST -p $SRV_PORT +} + +case "$1" in + start) + SRV_HOST=$2 + SRV_PORT=$3 + start + RETVAL=$? + ;; + + *) + echo "Usage: $0 start" + RETVAL=2 + ;; +esac + +exit $RETVAL diff --git a/ovirt-managed-node/ovirt-identify-node.c b/ovirt-managed-node/ovirt-identify-node.c new file mode 100644 index 0000000..5a5d3c3 --- /dev/null +++ b/ovirt-managed-node/ovirt-identify-node.c @@ -0,0 +1,438 @@ +/* identify-node -- Main entry point for the identify-node utility. + * + * 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. + */ + +#include <errno.h> +#include <getopt.h> +#include <netdb.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <libvirt/libvirt.h> +#include <netinet/in.h> +#include <sys/types.h> +#include <sys/socket.h> + +int config(int argc,char** argv); +void usage(void); + +int start_conversation(void); +int send_details(void); +int end_conversation(void); + +int send_text(char* text); +int get_text(char* response,int maxlength); +int create_connection(void); + +int debug = 0; +int verbose = 0; +int testing = 0; + +#define BUFFER_LENGTH 128 + +char arch[BUFFER_LENGTH]; +char uuid[VIR_UUID_BUFLEN]; +char memsize[BUFFER_LENGTH]; +char numcpus[BUFFER_LENGTH]; +char cpuspeed[BUFFER_LENGTH]; +char hostname[256]; +int hostport = -1; +int socketfd; + +int main(int argc,char** argv) +{ + int result = 1; + + if(!config(argc,argv)) + { + fprintf(stdout,"Connecting to libvirt.\n"); + + virConnectPtr connection = virConnectOpenReadOnly(testing ? "test:///default" : NULL); + + if(debug) fprintf(stderr,"connection=%x\n",(unsigned int )connection); + + if(connection) + { + if(debug) fprintf(stdout,"Getting hostname: %s\n", uuid); + if(!strlen(uuid)) gethostname(uuid,sizeof uuid); + + virNodeInfo info; + + if(debug) fprintf(stdout,"Retrieving node information.\n"); + if(!virNodeGetInfo(connection,&info)) + { + sprintf(arch, "%s", info.model); + sprintf(memsize, "%ld", info.memory); + sprintf(numcpus, "%d", info.cpus); + sprintf(cpuspeed,"%d", info.mhz); + + if(debug) + { + fprintf(stdout,"Node Info:\n"); + fprintf(stdout," UUID: %s\n", uuid); + fprintf(stdout," Arch: %s\n", arch); + fprintf(stdout," Memory: %s\n", memsize); + fprintf(stdout," # CPUs: %s\n", numcpus); + fprintf(stdout,"CPU Speed: %s\n", cpuspeed); + } + + if(debug) fprintf(stdout, "Retrieved node information.\n"); + + if(!start_conversation() && !send_details() && !end_conversation()) + { + result = 0; + } + } + else + { + if(debug) fprintf(stderr,"Failed to get node info.\n"); + } + } + else + { + if(debug) fprintf(stderr,"Could not connect to libvirt.\n"); + } + } + else + { + usage(); + } + + return result; +} + +int config(int argc,char** argv) +{ + int result = 0; + int option; + + while((option = getopt(argc,argv,"s:p:dvth")) != -1) + { + if(debug) fprintf(stdout,"Processing argument: %c (optarg:%s)\n",option,optarg); + + switch(option) + { + case 's': strcpy(hostname,optarg); break; + case 'p': hostport = atoi(optarg); break; + case 't': testing = 1; break; + case 'd': debug = 1; break; + case 'v': verbose = 1; break; + case 'h': + // fall thru + default : result = 1; break; + } + } + + // verify that required options are provided + if(hostname == NULL || strlen(hostname) == 0) + { + fprintf(stderr,"ERROR: The server name is required. (-s [hostname])\n"); + result = 1; + } + + if(hostport <= 0) + { + fprintf(stderr,"ERROR: The server port is required. (-p [port])\n"); + result = 1; + } + + return result; +} + +void usage() +{ + fprintf(stdout,"\n"); + fprintf(stdout,"Usage: ovirt-identify [OPTION]\n"); + fprintf(stdout,"\n"); + fprintf(stdout,"\t-s [server]\t\tThe remote server's hostname.\n"); + fprintf(stdout,"\t-p [port]\t\tThe remote server's port.\n"); + fprintf(stdout,"\t-d\t\tDisplays debug information during execution.\n"); + fprintf(stdout,"\t-v\t\tDisplays verbose information during execution.\n"); + fprintf(stdout,"\t-h\t\tDisplays this help information and then exits.\n"); + fprintf(stdout,"\n"); +} + +int start_conversation(void) +{ + int result = 1; + + if(verbose || debug) fprintf(stdout,"Starting conversation with %s:%d.\n",hostname,hostport); + + if(!create_connection()) + { + if(debug || verbose) fprintf(stdout,"Connected.\n"); + + char buffer[BUFFER_LENGTH]; + + get_text(buffer,sizeof buffer); + + if(!strcmp(buffer,"HELLO?")) + { + if(debug) fprintf(stdout,"Checking for handshake.\n"); + + if(!send_text("HELLO!")) + { + if(debug) fprintf(stdout,"Handshake received. Starting conversation.\n"); + + get_text(buffer,sizeof buffer); + + if(!strcmp(buffer,"MODE?")) + { + if(debug) fprintf(stdout,"Shifting to IDENTIFY mode.\n"); + + if(!send_text("IDENTIFY")) result = 0; + } + else + { + if(debug) fprintf(stderr,"Was not asked for a mode.\n"); + } + } + } + else + { + if(debug) fprintf(stderr,"Did not receive a proper handshake.\n"); + } + } + + else + { + if(debug) fprintf(stderr,"Did not get a connection.\n"); + } + + if(debug) fprintf(stdout,"start_conversation: result=%d\n", result); + + return result; +} + +int send_value(char* label,char* value) +{ + char buffer[BUFFER_LENGTH]; + + bzero(buffer,sizeof buffer); + + sprintf(buffer,"%s=%s", label, value); + + int result = 1; + + if(!send_text(buffer)) + { + char expected[BUFFER_LENGTH]; + + bzero(expected,sizeof buffer); + bzero(buffer,sizeof buffer); + + get_text(buffer,sizeof buffer); + + sprintf(expected, "ACK %s", label); + + if(debug) fprintf(stdout,"Expecting \"%s\" : Received \"%s\"\n", expected, buffer); + + if(!strcmp(expected,buffer)) + { + result = 0; + } + } + + return result; +} + +int send_details(void) +{ + int result = 1; + + fprintf(stdout,"Sending node details.\n"); + + char buffer[BUFFER_LENGTH]; + + get_text(buffer,sizeof buffer); + + if(!strcmp(buffer,"INFO?")) + { + if((!send_value("ARCH", arch)) && + (!send_value("UUID", uuid)) && + (!send_value("NUMCPUS", numcpus)) && + (!send_value("CPUSPEED", cpuspeed)) && + (!send_value("MEMSIZE", memsize))) + { + if(!send_text("ENDINFO")) result = 0; + } + } + else + { + if(debug) fprintf(stdout,"Was not interrogated for hardware info.\n"); + } + + return result; +} + +int end_conversation(void) +{ + int result = 0; + + fprintf(stdout,"Ending conversation.\n"); + + send_text("ENDINFO"); + + close(socketfd); + + return result; +} + +ssize_t safewrite(int fd, const void *buf, size_t count) +{ + size_t nwritten = 0; + while (count > 0) { + ssize_t r = write(fd, buf, count); + + if (r < 0 && errno == EINTR) + continue; + if (r < 0) + return r; + if (r == 0) + return nwritten; + buf = (const char *)buf + r; + count -= r; + nwritten += r; + } + return nwritten; +} + +int send_text(char* text) +{ + int result = 1; + + if(debug || verbose) fprintf(stdout,"\"%s\" -> %s:%d\n", text, hostname, hostport); + + char buffer[strlen(text) + 2]; + + sprintf(buffer,"%s\n",text); + + // int sent = write(socketfd,buffer,strlen(buffer)); + + int sent = safewrite(socketfd, buffer, strlen(buffer)); + + if(sent >= 0) + { + if(debug) fprintf(stdout,"Sent %d bytes total.\n", sent); + + result = 0; + } + + return result; +} + +int saferead(int fd, void *buf, size_t count) +{ + if(debug) fprintf(stdout,"Begin saferead(%d, %x, %d)\n", fd, (unsigned int)buf, count); + + while (1) + { + ssize_t r = read (fd, buf, count); + if (r < 0 && errno == EINTR) continue; + return r; + } +} + +int get_text(char* response,int maxlength) +{ + if(debug) fprintf(stdout,"Reading up to %d bytes from socket.\n", maxlength); + // int received = read(socketfd,response,maxlength); + int received = saferead(socketfd,response,maxlength); + + response[received - 1] = 0; + + if(debug) fprintf(stdout,"Received \"%s\": size=%d (trimmed ending carriage return)\n", response, received); + + return received; +} + +int create_connection(void) +{ + int result = 1; + + if(debug) fprintf(stdout,"Creating the socket connection.\n"); + + struct addrinfo hints; + struct addrinfo* results; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = 0; + hints.ai_protocol = 0; + + if(debug) fprintf(stdout,"Searching for host candidates.\n"); + + char port[6]; + + sprintf(port,"%d", hostport); + + if(!getaddrinfo(hostname, port, &hints, &results)) + { + if(debug) fprintf(stdout,"Got address information. Searching for a proper entry.\n"); + struct addrinfo* rptr; + + for(rptr = results; rptr != NULL; rptr = rptr->ai_next) + { + if(debug) + { + fprintf(stdout,"Attempting connection: family=%d, socket type=%d, protocol=%d\n", + rptr->ai_family, rptr->ai_socktype, rptr->ai_protocol); + } + + socketfd = socket(rptr->ai_family, rptr->ai_socktype, rptr->ai_protocol); + + if(socketfd == -1) + { + continue; + } + + if(connect(socketfd, rptr->ai_addr, rptr->ai_addrlen) != -1) + { + break; + } + + // invalid connection, so close it + if(debug) fprintf(stdout, "Invalid connection.\n"); + close(socketfd); + } + + if(rptr == NULL) + { + if(debug) fprintf(stdout,"Unable to connect to server %s:%d\n", hostname, hostport); + } + else + { + // success + result = 0; + } + + freeaddrinfo(results); + } + else + { + if(debug) fprintf(stderr,"No hosts found. Exiting...\n"); + } + + if(debug) fprintf(stdout, "create_connection: result=%d\n", result); + + return result; +} diff --git a/ovirt-managed-node/ovirt-managed-node.spec b/ovirt-managed-node/ovirt-managed-node.spec new file mode 100644 index 0000000..7f33344 --- /dev/null +++ b/ovirt-managed-node/ovirt-managed-node.spec @@ -0,0 +1,45 @@ +Summary: The managed node demons for oVirt. +Name: ovirt-managed-node +Version: 0.1 +Release: 1%{?dist} +License: Fedora +Group: Applications/System +Source0: http://www.ovirt.org/sources/%{name}-%{version}.tar + +BuildRoot: %{_tmppath}/%{name}-%{version}-root +URL: http://www.ovirt.org/ +BuildRequires: libvirt-devel +Requires: libvirt +ExclusiveArch: %{ix86} x86_64 ia64 + + +%description +Provides a series of daemons and support utilities to allow an +oVirt managed node to interact with the oVirt server. + + +%prep +%setup + +rm -rf $RPM_BUILD_ROOT +mkdir -p $RPM_BUILD_ROOT + + +%build +make + +mkdir -p $RPM_BUILD_ROOT/sbin +cp ovirt-awake $RPM_BUILD_ROOT/sbin +cp ovirt-identify $RPM_BUILD_ROOT/sbin +cp ovirt-identify-node $RPM_BUILD_ROOT/sbin + + +%clean +rm -rf $RPM_BUILD_ROOT + +%files +%defattr(755,root,root) +%doc README NEWS AUTHOR ChangeLog +/sbin/ovirt-awake +/sbin/ovirt-identify +/sbin/ovirt-identify-node diff --git a/wui/src/host-browser/host-browser.rb b/wui/src/host-browser/host-browser.rb index e127ddb..3e242cf 100755 --- a/wui/src/host-browser/host-browser.rb +++ b/wui/src/host-browser/host-browser.rb @@ -1,5 +1,5 @@ #!/usr/bin/ruby -Wall -# +# # Copyright (C) 2008 Red Hat, Inc. # Written by Darryl L. Pierce <dpierce at redhat.com> # @@ -41,7 +41,7 @@ class HostBrowser attr_accessor :logfile attr_accessor :keytab_dir attr_accessor :keytab_filename - + def initialize(session) @session = session @log_prefix = "[#{session.peeraddr[3]}] " @@ -51,19 +51,31 @@ class HostBrowser # Ensures the conversation starts properly. # def begin_conversation - puts "#{@log_prefix} Begin conversation" + puts "#{@log_prefix} Begin conversation" unless defined?(TESTING) @session.write("HELLO?\n") response = @session.readline.chomp raise Exception.new("received #{response}, expected HELLO!") unless response == "HELLO!" end + # Retrieves the mode request from the remote system. + # + def get_mode + puts "#{@log_prefix} Determining the runtime mode." unless defined?(TESTING) + @session.write("MODE?\n") + response = @session.readline.chomp + puts "#{@log_prefix} MODE=#{response}" unless defined?(TESTING) + + response + end + # Requests node information from the remote system. # def get_remote_info - puts "#{@log_prefix} Begin remote info collection" + puts "#{@log_prefix} Begin remote info collection" unless defined?(TESTING) result = {} - result['IPADDR'] = @session.peeraddr[3] + result['HOSTNAME'] = @session.peeraddr[2] + result['IPADDR'] = @session.peeraddr[3] @session.write("INFO?\n") loop do @@ -75,9 +87,9 @@ class HostBrowser key, value = info.split("=") - puts "#{@log_prefix} ::Received - #{key}:#{value}" + puts "#{@log_prefix} ::Received - #{key}:#{value}" unless defined?(TESTING) result[key] = value - + @session.write("ACK #{key}\n") end @@ -94,13 +106,13 @@ class HostBrowser ensure_present(host_info,'ARCH') ensure_present(host_info,'MEMSIZE') - puts "Searching for existing host record..." + puts "Searching for existing host record..." unless defined?(TESTING) host = Host.find(:first, :conditions => ["uuid = ?", host_info['UUID']]) if host == nil begin - puts "Creating a new record for #{host_info['HOSTNAME']}..." - + puts "Creating a new record for #{host_info['HOSTNAME']}..." unless defined?(TESTING) + Host.new( "uuid" => host_info['UUID'], "hostname" => host_info['HOSTNAME'], @@ -115,7 +127,7 @@ class HostBrowser # successfully connects to it via libvirt. "state" => "unavailable").save rescue Exception => error - puts "Error while creating record: #{error.message}" + puts "Error while creating record: #{error.message}" unless defined?(TESTING) end else host.uuid = host_info['UUID'] @@ -125,45 +137,45 @@ class HostBrowser host.arch = host_info['ARCH'] host.memory_in_mb = host_info['MEMSIZE'] end - + return host end - # Ends the conversation, notifying the user of the key version number. - # - def end_conversation(ktab) - puts "#{@log_prefix} Ending conversation" - - @session.write("KTAB #{ktab}\n") - - response = @session.readline.chomp - - raise Exception.new("ERROR! Malformed response : expected ACK, got #{response}") unless response == "ACK" - - @session.write("BYE\n"); - end - # Creates a keytab if one is needed, returning the filename. # - def create_keytab(host_info, krb5_arg = nil) + def create_keytab(hostname, ipaddress, 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' + libvirt_princ = 'libvirt/' + hostname + '@' + default_realm + outfile = ipaddress + '-libvirt.tab' @keytab_filename = @keytab_dir + outfile # TODO need a way to test this portion unless defined? TESTING || File.exists?(@keytab_filename) # TODO replace with Kr5Auth when it supports admin actions - puts "Writing keytab file: #{@keytab_filename}" + puts "Writing keytab file: #{@keytab_filename}" unless defined?(TESTING) kadmin_local('addprinc -randkey ' + libvirt_princ) kadmin_local('ktadd -k ' + @keytab_filename + ' ' + libvirt_princ) File.chmod(0644, at keytab_filename) end - return outfile + hostname = `hostname -f`.chomp + + @session.write("KTAB http://#{hostname}/config/#{outfile}\n") + + response = @session.readline.chomp + + raise Exception.new("ERRINFO! No keytab acknowledgement") unless response == "ACK" + end + + # Ends the conversation, notifying the user of the key version number. + # + def end_conversation + puts "#{@log_prefix} Ending conversation" unless defined?(TESTING) + + @session.write("BYE\n"); end private @@ -185,35 +197,37 @@ def entry_point(server) while(session = server.accept) child = fork do remote = session.peeraddr[3] - - puts "Connected to #{remote}" + + puts "Connected to #{remote}" unless defined?(TESTING) # This is needed because we just forked a new process # which now needs its own connection to the database. database_connect - + begin browser = HostBrowser.new(session) 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) + case browser.get_mode + when "AWAKEN": browser.create_keytab(remote,session.peeraddr[3]) + when "IDENTIFY": browser.write_host_info(browser.get_remote_info) + end + + browser.end_conversation rescue Exception => error session.write("ERROR #{error.message}\n") - puts "ERROR #{error.message}" + puts "ERROR #{error.message}" unless defined?(TESTING) end - - puts "Disconnected from #{remote}" + + puts "Disconnected from #{remote}" unless defined?(TESTING) end - - Process.detach(child) - end + + Process.detach(child) + end end unless defined?(TESTING) - + # The main entry point. # unless ARGV[0] == "-n" diff --git a/wui/src/host-browser/test-host-browser-awaken.rb b/wui/src/host-browser/test-host-browser-awaken.rb new file mode 100755 index 0000000..a5ca2e7 --- /dev/null +++ b/wui/src/host-browser/test-host-browser-awaken.rb @@ -0,0 +1,94 @@ +#!/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' + +# +TestHostBrowserAwaken+ +class TestHostBrowserAwaken < 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/' + 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 is satisfied if the remote system is + # making a wakeup call. + # + def test_get_mode_with_awaken_request + @session.should_receive(:write).with("MODE?\n").once().returns { |request| request.length } + @session.should_receive(:readline).once().returns { "AWAKEN\n" } + + result = @browser.get_mode() + + assert_equal "AWAKEN", result, "method did not return the right value" + 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" } + servername = `hostname -f`.chomp + @session.should_receive(:write).with("KTAB http://#{servername}/config/127.0.0.1-libvirt.tab\n").once().returns { |request| request.length } + @session.should_receive(:readline).once().returns { "ACK\n" } + + assert_nothing_raised(Exception) { @browser.create_keytab('localhost','127.0.0.1', at krb5) } + 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("BYE\n").once().returns { |request| request.length } + + assert_nothing_raised(Exception) { @browser.end_conversation } + end + +end diff --git a/wui/src/host-browser/test-host-browser-identify.rb b/wui/src/host-browser/test-host-browser-identify.rb new file mode 100755 index 0000000..a70884d --- /dev/null +++ b/wui/src/host-browser/test-host-browser-identify.rb @@ -0,0 +1,162 @@ +#!/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"] } + + @browser = HostBrowser.new(@session) + @browser.logfile = './unit-test.log' + + # 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 is satisfied if the remote system is + # making a wakeup call. + # + def test_get_mode_with_awaken_request + @session.should_receive(:write).with("MODE?\n").once().returns { |request| request.length } + @session.should_receive(:readline).once().returns { "IDENTIFY\n" } + + result = @browser.get_mode() + + assert_equal "IDENTIFY", result, "method did not return the right value" + 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 4,info.keys.size, "Should contain two keys" + assert info.include?("IPADDR") + assert info.include?("HOSTNAME") + assert info.include?("key1") + assert info.include?("key2") + 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 + +end diff --git a/wui/src/host-browser/test-host-browser.rb b/wui/src/host-browser/test-host-browser.rb deleted file mode 100755 index 6f4c660..0000000 --- a/wui/src/host-browser/test-host-browser.rb +++ /dev/null @@ -1,204 +0,0 @@ -#!/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, 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("KTAB 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 } - - assert_nothing_raised(Exception) { @browser.end_conversation(12345) } - end - -end -- 1.5.5.1
Darryl L. Pierce
2008-Jun-24 02:17 UTC
[Ovirt-devel] [PATCH] Rewriting the identify-node piece into two managed node units to submit a wake-up request and an identify request. The bulk of the ovirt-identify work is done via a C application.
I'm having a trouble with getting the ovirt-managed-node RPM into the LiveCD image. If someone could take a few minutes, I'd appreciate some feedback on how to fix build-all.sh. Thanks. Signed-off-by: Darryl L. Pierce <dpierce at redhat.com> --- build-all.sh | 8 + ovirt-host-creator/common-pkgs.ks | 1 + ovirt-host-creator/version | 2 +- ovirt-managed-node/AUTHOR | 1 + ovirt-managed-node/ChangeLog | 2 + ovirt-managed-node/Makefile | 76 ++++ ovirt-managed-node/NEWS | 2 + ovirt-managed-node/README | 2 + ovirt-managed-node/ovirt-awake | 82 ++++ ovirt-managed-node/ovirt-identify | 46 ++ ovirt-managed-node/ovirt-identify-node.c | 438 ++++++++++++++++++++ ovirt-managed-node/ovirt-managed-node.spec | 45 ++ wui/src/host-browser/host-browser.rb | 106 +++-- wui/src/host-browser/test-host-browser-awaken.rb | 94 +++++ wui/src/host-browser/test-host-browser-identify.rb | 162 ++++++++ wui/src/host-browser/test-host-browser.rb | 204 --------- wui/version | 2 +- 17 files changed, 1021 insertions(+), 252 deletions(-) create mode 100644 ovirt-managed-node/AUTHOR create mode 100644 ovirt-managed-node/ChangeLog create mode 100644 ovirt-managed-node/Makefile create mode 100644 ovirt-managed-node/NEWS create mode 100644 ovirt-managed-node/README create mode 100644 ovirt-managed-node/ovirt-awake create mode 100644 ovirt-managed-node/ovirt-identify create mode 100644 ovirt-managed-node/ovirt-identify-node.c create mode 100644 ovirt-managed-node/ovirt-managed-node.spec create mode 100755 wui/src/host-browser/test-host-browser-awaken.rb create mode 100755 wui/src/host-browser/test-host-browser-identify.rb delete mode 100755 wui/src/host-browser/test-host-browser.rb diff --git a/build-all.sh b/build-all.sh index 8c3a7e9..3d2666c 100755 --- a/build-all.sh +++ b/build-all.sh @@ -126,13 +126,21 @@ fi set -e # build ovirt-wui RPM +# also build the ovirt-managed-node RPM if [ $update_wui = 1 ]; then + cd $BASE/ovirt-managed-node + rm -rf rpm-build + make rpms + rm -f $OVIRT/ovirt-managed-node*rpm + cp rpm-build/ovirt-managed-node*rpm + cd $BASE/wui rm -rf rpm-build bumpver make rpms rm -f $OVIRT/ovirt-wui*rpm cp rpm-build/ovirt-wui*rpm $OVIRT + cd $OVIRT createrepo . fi diff --git a/ovirt-host-creator/common-pkgs.ks b/ovirt-host-creator/common-pkgs.ks index 814f6a9..6fe4f26 100644 --- a/ovirt-host-creator/common-pkgs.ks +++ b/ovirt-host-creator/common-pkgs.ks @@ -26,6 +26,7 @@ augeas nc bind-utils syslinux +ovirt-managed-node -policycoreutils -audit-libs-python -hdparm diff --git a/ovirt-host-creator/version b/ovirt-host-creator/version index f644984..c5b5338 100644 --- a/ovirt-host-creator/version +++ b/ovirt-host-creator/version @@ -1 +1 @@ -0.91 1 +0.92 0.1.200806240052git63f6a5a diff --git a/ovirt-managed-node/AUTHOR b/ovirt-managed-node/AUTHOR new file mode 100644 index 0000000..d37a107 --- /dev/null +++ b/ovirt-managed-node/AUTHOR @@ -0,0 +1 @@ +Darryl L. Pierce <dpierce at redhat.com> diff --git a/ovirt-managed-node/ChangeLog b/ovirt-managed-node/ChangeLog new file mode 100644 index 0000000..61dc8cc --- /dev/null +++ b/ovirt-managed-node/ChangeLog @@ -0,0 +1,2 @@ +* Tue 17 Jun 2008 Darryl L. Pierce <dpierce at redhat.com> - 0.0.1-1 +- Created the initial RPM structure. diff --git a/ovirt-managed-node/Makefile b/ovirt-managed-node/Makefile new file mode 100644 index 0000000..564be1d --- /dev/null +++ b/ovirt-managed-node/Makefile @@ -0,0 +1,76 @@ +# 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. + +CC=gcc +CFLAGS=-Wall -c -g +LFLAGS=-lvirt +OBJECTS=ovirt-identify-node.o +TARGET=ovirt-identify-node +SPECFILE=ovirt-managed-node.spec +NAME=ovirt-managed-node +VERSION=0.1 +NV=$(NAME)-$(VERSION) +RPM_FLAGS=\ + --define "_topdir %(pwd)/rpm-build" \ + --define "_builddir %{_topdir}" \ + --define "_rpmdir %{_topdir}" \ + --define "_srcrpmdir %{_topdir}" \ + --define '_rpmfilename %%{NAME}-%%{VERSION}-%%{RELEASE}.%%{ARCH}.rpm' \ + --define "_specdir %{_topdir}" \ + --define "_sourcedir %{_topdir}" +SOURCES=\ + AUTHOR \ + ChangeLog \ + Makefile \ + NEWS \ + ovirt-awake \ + ovirt-identify \ + ovirt-identify-node.c \ + ovirt-managed-node.spec \ + README + + +ALL: $(TARGET) + +.c.o: + $(CC) $(CFLAGS) $< -o $@ + +clean: + rm -rf $(OBJECTS) $(TARGET) $(NV) rpm-build + +bumpgit: + -echo "$(VERSION) $(GITRELEASE)" > version + +bumprelease: + -echo "$(VERSION) $(NEWRELEASE)" > version + +setversion: + -echo "$(VERSION) 0" > version + +$(TARGET): $(OBJECTS) + $(CC) -o $@ $(OBJECTS) $(LFLAGS) + +tar: clean $(TARGET) + mkdir -p $(NV) + cp -a $(SOURCES) $(NV) + mkdir -p rpm-build + tar zcvf rpm-build/$(NV).tar $(NV) + rm -rf $(NV) + +rpms: tar + rpmbuild $(RPM_FLAGS) -ba $(SPECFILE) diff --git a/ovirt-managed-node/NEWS b/ovirt-managed-node/NEWS new file mode 100644 index 0000000..139597f --- /dev/null +++ b/ovirt-managed-node/NEWS @@ -0,0 +1,2 @@ + + diff --git a/ovirt-managed-node/README b/ovirt-managed-node/README new file mode 100644 index 0000000..139597f --- /dev/null +++ b/ovirt-managed-node/README @@ -0,0 +1,2 @@ + + diff --git a/ovirt-managed-node/ovirt-awake b/ovirt-managed-node/ovirt-awake new file mode 100644 index 0000000..f7f643b --- /dev/null +++ b/ovirt-managed-node/ovirt-awake @@ -0,0 +1,82 @@ +#!/bin/bash +# +# ovirt-awake Notifies the oVirt server that a managed node is +# starting up. +# +# 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. + +# Source function library +. /etc/init.d/ovirt-functions + +start () { + find-server identify tcp + + connect-to-server + + receive-text + + if [ $REPLY == "HELLO?" ]; then + echo "Starting wakeup conversation." + + send-text "HELLO!" + + read 0<&3 + + if [ $REPLY == "MODE?" ]; then + send-text "AWAKEN" + + receive-text + + KEYTAB=`echo $REPLY | awk '{ print $2 }'` + + if [ -n $KEYTAB ]; then + echo "Retrieving keytab: '$KEYTAB'" + + wget $KEYTAB --output-document=$KEYTAB_FILE + else + echo "No keytab to retrieve" + fi + else + echo "Did not get a mode request." + fi + else + echo "Did not get a proper startup marker." + fi + + echo "Disconnecting." + + disconnect-from-server +} + +case "$1" in + start) + KEYTAB_FILE=$2 + SRV_HOST=$3 + SRV_PORT=$4 + start + RETVAL=$? + ;; + + *) + echo "Usage: $0 start" + RETVAL=2 + ;; +esac + +exit $RETVAL diff --git a/ovirt-managed-node/ovirt-identify b/ovirt-managed-node/ovirt-identify new file mode 100644 index 0000000..53f21b0 --- /dev/null +++ b/ovirt-managed-node/ovirt-identify @@ -0,0 +1,46 @@ +#!/bin/bash +# +# ovirt-identify Submits managed node details to the central server. +# +# 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. + +# Source function library +. /etc/init.d/ovirt-functions + +start () { + find-server identify tcp + + /sbin/ovirt-identify-node -s $SRV_HOST -p $SRV_PORT +} + +case "$1" in + start) + SRV_HOST=$2 + SRV_PORT=$3 + start + RETVAL=$? + ;; + + *) + echo "Usage: $0 start" + RETVAL=2 + ;; +esac + +exit $RETVAL diff --git a/ovirt-managed-node/ovirt-identify-node.c b/ovirt-managed-node/ovirt-identify-node.c new file mode 100644 index 0000000..5a5d3c3 --- /dev/null +++ b/ovirt-managed-node/ovirt-identify-node.c @@ -0,0 +1,438 @@ +/* identify-node -- Main entry point for the identify-node utility. + * + * 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. + */ + +#include <errno.h> +#include <getopt.h> +#include <netdb.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <libvirt/libvirt.h> +#include <netinet/in.h> +#include <sys/types.h> +#include <sys/socket.h> + +int config(int argc,char** argv); +void usage(void); + +int start_conversation(void); +int send_details(void); +int end_conversation(void); + +int send_text(char* text); +int get_text(char* response,int maxlength); +int create_connection(void); + +int debug = 0; +int verbose = 0; +int testing = 0; + +#define BUFFER_LENGTH 128 + +char arch[BUFFER_LENGTH]; +char uuid[VIR_UUID_BUFLEN]; +char memsize[BUFFER_LENGTH]; +char numcpus[BUFFER_LENGTH]; +char cpuspeed[BUFFER_LENGTH]; +char hostname[256]; +int hostport = -1; +int socketfd; + +int main(int argc,char** argv) +{ + int result = 1; + + if(!config(argc,argv)) + { + fprintf(stdout,"Connecting to libvirt.\n"); + + virConnectPtr connection = virConnectOpenReadOnly(testing ? "test:///default" : NULL); + + if(debug) fprintf(stderr,"connection=%x\n",(unsigned int )connection); + + if(connection) + { + if(debug) fprintf(stdout,"Getting hostname: %s\n", uuid); + if(!strlen(uuid)) gethostname(uuid,sizeof uuid); + + virNodeInfo info; + + if(debug) fprintf(stdout,"Retrieving node information.\n"); + if(!virNodeGetInfo(connection,&info)) + { + sprintf(arch, "%s", info.model); + sprintf(memsize, "%ld", info.memory); + sprintf(numcpus, "%d", info.cpus); + sprintf(cpuspeed,"%d", info.mhz); + + if(debug) + { + fprintf(stdout,"Node Info:\n"); + fprintf(stdout," UUID: %s\n", uuid); + fprintf(stdout," Arch: %s\n", arch); + fprintf(stdout," Memory: %s\n", memsize); + fprintf(stdout," # CPUs: %s\n", numcpus); + fprintf(stdout,"CPU Speed: %s\n", cpuspeed); + } + + if(debug) fprintf(stdout, "Retrieved node information.\n"); + + if(!start_conversation() && !send_details() && !end_conversation()) + { + result = 0; + } + } + else + { + if(debug) fprintf(stderr,"Failed to get node info.\n"); + } + } + else + { + if(debug) fprintf(stderr,"Could not connect to libvirt.\n"); + } + } + else + { + usage(); + } + + return result; +} + +int config(int argc,char** argv) +{ + int result = 0; + int option; + + while((option = getopt(argc,argv,"s:p:dvth")) != -1) + { + if(debug) fprintf(stdout,"Processing argument: %c (optarg:%s)\n",option,optarg); + + switch(option) + { + case 's': strcpy(hostname,optarg); break; + case 'p': hostport = atoi(optarg); break; + case 't': testing = 1; break; + case 'd': debug = 1; break; + case 'v': verbose = 1; break; + case 'h': + // fall thru + default : result = 1; break; + } + } + + // verify that required options are provided + if(hostname == NULL || strlen(hostname) == 0) + { + fprintf(stderr,"ERROR: The server name is required. (-s [hostname])\n"); + result = 1; + } + + if(hostport <= 0) + { + fprintf(stderr,"ERROR: The server port is required. (-p [port])\n"); + result = 1; + } + + return result; +} + +void usage() +{ + fprintf(stdout,"\n"); + fprintf(stdout,"Usage: ovirt-identify [OPTION]\n"); + fprintf(stdout,"\n"); + fprintf(stdout,"\t-s [server]\t\tThe remote server's hostname.\n"); + fprintf(stdout,"\t-p [port]\t\tThe remote server's port.\n"); + fprintf(stdout,"\t-d\t\tDisplays debug information during execution.\n"); + fprintf(stdout,"\t-v\t\tDisplays verbose information during execution.\n"); + fprintf(stdout,"\t-h\t\tDisplays this help information and then exits.\n"); + fprintf(stdout,"\n"); +} + +int start_conversation(void) +{ + int result = 1; + + if(verbose || debug) fprintf(stdout,"Starting conversation with %s:%d.\n",hostname,hostport); + + if(!create_connection()) + { + if(debug || verbose) fprintf(stdout,"Connected.\n"); + + char buffer[BUFFER_LENGTH]; + + get_text(buffer,sizeof buffer); + + if(!strcmp(buffer,"HELLO?")) + { + if(debug) fprintf(stdout,"Checking for handshake.\n"); + + if(!send_text("HELLO!")) + { + if(debug) fprintf(stdout,"Handshake received. Starting conversation.\n"); + + get_text(buffer,sizeof buffer); + + if(!strcmp(buffer,"MODE?")) + { + if(debug) fprintf(stdout,"Shifting to IDENTIFY mode.\n"); + + if(!send_text("IDENTIFY")) result = 0; + } + else + { + if(debug) fprintf(stderr,"Was not asked for a mode.\n"); + } + } + } + else + { + if(debug) fprintf(stderr,"Did not receive a proper handshake.\n"); + } + } + + else + { + if(debug) fprintf(stderr,"Did not get a connection.\n"); + } + + if(debug) fprintf(stdout,"start_conversation: result=%d\n", result); + + return result; +} + +int send_value(char* label,char* value) +{ + char buffer[BUFFER_LENGTH]; + + bzero(buffer,sizeof buffer); + + sprintf(buffer,"%s=%s", label, value); + + int result = 1; + + if(!send_text(buffer)) + { + char expected[BUFFER_LENGTH]; + + bzero(expected,sizeof buffer); + bzero(buffer,sizeof buffer); + + get_text(buffer,sizeof buffer); + + sprintf(expected, "ACK %s", label); + + if(debug) fprintf(stdout,"Expecting \"%s\" : Received \"%s\"\n", expected, buffer); + + if(!strcmp(expected,buffer)) + { + result = 0; + } + } + + return result; +} + +int send_details(void) +{ + int result = 1; + + fprintf(stdout,"Sending node details.\n"); + + char buffer[BUFFER_LENGTH]; + + get_text(buffer,sizeof buffer); + + if(!strcmp(buffer,"INFO?")) + { + if((!send_value("ARCH", arch)) && + (!send_value("UUID", uuid)) && + (!send_value("NUMCPUS", numcpus)) && + (!send_value("CPUSPEED", cpuspeed)) && + (!send_value("MEMSIZE", memsize))) + { + if(!send_text("ENDINFO")) result = 0; + } + } + else + { + if(debug) fprintf(stdout,"Was not interrogated for hardware info.\n"); + } + + return result; +} + +int end_conversation(void) +{ + int result = 0; + + fprintf(stdout,"Ending conversation.\n"); + + send_text("ENDINFO"); + + close(socketfd); + + return result; +} + +ssize_t safewrite(int fd, const void *buf, size_t count) +{ + size_t nwritten = 0; + while (count > 0) { + ssize_t r = write(fd, buf, count); + + if (r < 0 && errno == EINTR) + continue; + if (r < 0) + return r; + if (r == 0) + return nwritten; + buf = (const char *)buf + r; + count -= r; + nwritten += r; + } + return nwritten; +} + +int send_text(char* text) +{ + int result = 1; + + if(debug || verbose) fprintf(stdout,"\"%s\" -> %s:%d\n", text, hostname, hostport); + + char buffer[strlen(text) + 2]; + + sprintf(buffer,"%s\n",text); + + // int sent = write(socketfd,buffer,strlen(buffer)); + + int sent = safewrite(socketfd, buffer, strlen(buffer)); + + if(sent >= 0) + { + if(debug) fprintf(stdout,"Sent %d bytes total.\n", sent); + + result = 0; + } + + return result; +} + +int saferead(int fd, void *buf, size_t count) +{ + if(debug) fprintf(stdout,"Begin saferead(%d, %x, %d)\n", fd, (unsigned int)buf, count); + + while (1) + { + ssize_t r = read (fd, buf, count); + if (r < 0 && errno == EINTR) continue; + return r; + } +} + +int get_text(char* response,int maxlength) +{ + if(debug) fprintf(stdout,"Reading up to %d bytes from socket.\n", maxlength); + // int received = read(socketfd,response,maxlength); + int received = saferead(socketfd,response,maxlength); + + response[received - 1] = 0; + + if(debug) fprintf(stdout,"Received \"%s\": size=%d (trimmed ending carriage return)\n", response, received); + + return received; +} + +int create_connection(void) +{ + int result = 1; + + if(debug) fprintf(stdout,"Creating the socket connection.\n"); + + struct addrinfo hints; + struct addrinfo* results; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = 0; + hints.ai_protocol = 0; + + if(debug) fprintf(stdout,"Searching for host candidates.\n"); + + char port[6]; + + sprintf(port,"%d", hostport); + + if(!getaddrinfo(hostname, port, &hints, &results)) + { + if(debug) fprintf(stdout,"Got address information. Searching for a proper entry.\n"); + struct addrinfo* rptr; + + for(rptr = results; rptr != NULL; rptr = rptr->ai_next) + { + if(debug) + { + fprintf(stdout,"Attempting connection: family=%d, socket type=%d, protocol=%d\n", + rptr->ai_family, rptr->ai_socktype, rptr->ai_protocol); + } + + socketfd = socket(rptr->ai_family, rptr->ai_socktype, rptr->ai_protocol); + + if(socketfd == -1) + { + continue; + } + + if(connect(socketfd, rptr->ai_addr, rptr->ai_addrlen) != -1) + { + break; + } + + // invalid connection, so close it + if(debug) fprintf(stdout, "Invalid connection.\n"); + close(socketfd); + } + + if(rptr == NULL) + { + if(debug) fprintf(stdout,"Unable to connect to server %s:%d\n", hostname, hostport); + } + else + { + // success + result = 0; + } + + freeaddrinfo(results); + } + else + { + if(debug) fprintf(stderr,"No hosts found. Exiting...\n"); + } + + if(debug) fprintf(stdout, "create_connection: result=%d\n", result); + + return result; +} diff --git a/ovirt-managed-node/ovirt-managed-node.spec b/ovirt-managed-node/ovirt-managed-node.spec new file mode 100644 index 0000000..7f33344 --- /dev/null +++ b/ovirt-managed-node/ovirt-managed-node.spec @@ -0,0 +1,45 @@ +Summary: The managed node demons for oVirt. +Name: ovirt-managed-node +Version: 0.1 +Release: 1%{?dist} +License: Fedora +Group: Applications/System +Source0: http://www.ovirt.org/sources/%{name}-%{version}.tar + +BuildRoot: %{_tmppath}/%{name}-%{version}-root +URL: http://www.ovirt.org/ +BuildRequires: libvirt-devel +Requires: libvirt +ExclusiveArch: %{ix86} x86_64 ia64 + + +%description +Provides a series of daemons and support utilities to allow an +oVirt managed node to interact with the oVirt server. + + +%prep +%setup + +rm -rf $RPM_BUILD_ROOT +mkdir -p $RPM_BUILD_ROOT + + +%build +make + +mkdir -p $RPM_BUILD_ROOT/sbin +cp ovirt-awake $RPM_BUILD_ROOT/sbin +cp ovirt-identify $RPM_BUILD_ROOT/sbin +cp ovirt-identify-node $RPM_BUILD_ROOT/sbin + + +%clean +rm -rf $RPM_BUILD_ROOT + +%files +%defattr(755,root,root) +%doc README NEWS AUTHOR ChangeLog +/sbin/ovirt-awake +/sbin/ovirt-identify +/sbin/ovirt-identify-node diff --git a/wui/src/host-browser/host-browser.rb b/wui/src/host-browser/host-browser.rb index e127ddb..3e242cf 100755 --- a/wui/src/host-browser/host-browser.rb +++ b/wui/src/host-browser/host-browser.rb @@ -1,5 +1,5 @@ #!/usr/bin/ruby -Wall -# +# # Copyright (C) 2008 Red Hat, Inc. # Written by Darryl L. Pierce <dpierce at redhat.com> # @@ -41,7 +41,7 @@ class HostBrowser attr_accessor :logfile attr_accessor :keytab_dir attr_accessor :keytab_filename - + def initialize(session) @session = session @log_prefix = "[#{session.peeraddr[3]}] " @@ -51,19 +51,31 @@ class HostBrowser # Ensures the conversation starts properly. # def begin_conversation - puts "#{@log_prefix} Begin conversation" + puts "#{@log_prefix} Begin conversation" unless defined?(TESTING) @session.write("HELLO?\n") response = @session.readline.chomp raise Exception.new("received #{response}, expected HELLO!") unless response == "HELLO!" end + # Retrieves the mode request from the remote system. + # + def get_mode + puts "#{@log_prefix} Determining the runtime mode." unless defined?(TESTING) + @session.write("MODE?\n") + response = @session.readline.chomp + puts "#{@log_prefix} MODE=#{response}" unless defined?(TESTING) + + response + end + # Requests node information from the remote system. # def get_remote_info - puts "#{@log_prefix} Begin remote info collection" + puts "#{@log_prefix} Begin remote info collection" unless defined?(TESTING) result = {} - result['IPADDR'] = @session.peeraddr[3] + result['HOSTNAME'] = @session.peeraddr[2] + result['IPADDR'] = @session.peeraddr[3] @session.write("INFO?\n") loop do @@ -75,9 +87,9 @@ class HostBrowser key, value = info.split("=") - puts "#{@log_prefix} ::Received - #{key}:#{value}" + puts "#{@log_prefix} ::Received - #{key}:#{value}" unless defined?(TESTING) result[key] = value - + @session.write("ACK #{key}\n") end @@ -94,13 +106,13 @@ class HostBrowser ensure_present(host_info,'ARCH') ensure_present(host_info,'MEMSIZE') - puts "Searching for existing host record..." + puts "Searching for existing host record..." unless defined?(TESTING) host = Host.find(:first, :conditions => ["uuid = ?", host_info['UUID']]) if host == nil begin - puts "Creating a new record for #{host_info['HOSTNAME']}..." - + puts "Creating a new record for #{host_info['HOSTNAME']}..." unless defined?(TESTING) + Host.new( "uuid" => host_info['UUID'], "hostname" => host_info['HOSTNAME'], @@ -115,7 +127,7 @@ class HostBrowser # successfully connects to it via libvirt. "state" => "unavailable").save rescue Exception => error - puts "Error while creating record: #{error.message}" + puts "Error while creating record: #{error.message}" unless defined?(TESTING) end else host.uuid = host_info['UUID'] @@ -125,45 +137,45 @@ class HostBrowser host.arch = host_info['ARCH'] host.memory_in_mb = host_info['MEMSIZE'] end - + return host end - # Ends the conversation, notifying the user of the key version number. - # - def end_conversation(ktab) - puts "#{@log_prefix} Ending conversation" - - @session.write("KTAB #{ktab}\n") - - response = @session.readline.chomp - - raise Exception.new("ERROR! Malformed response : expected ACK, got #{response}") unless response == "ACK" - - @session.write("BYE\n"); - end - # Creates a keytab if one is needed, returning the filename. # - def create_keytab(host_info, krb5_arg = nil) + def create_keytab(hostname, ipaddress, 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' + libvirt_princ = 'libvirt/' + hostname + '@' + default_realm + outfile = ipaddress + '-libvirt.tab' @keytab_filename = @keytab_dir + outfile # TODO need a way to test this portion unless defined? TESTING || File.exists?(@keytab_filename) # TODO replace with Kr5Auth when it supports admin actions - puts "Writing keytab file: #{@keytab_filename}" + puts "Writing keytab file: #{@keytab_filename}" unless defined?(TESTING) kadmin_local('addprinc -randkey ' + libvirt_princ) kadmin_local('ktadd -k ' + @keytab_filename + ' ' + libvirt_princ) File.chmod(0644, at keytab_filename) end - return outfile + hostname = `hostname -f`.chomp + + @session.write("KTAB http://#{hostname}/config/#{outfile}\n") + + response = @session.readline.chomp + + raise Exception.new("ERRINFO! No keytab acknowledgement") unless response == "ACK" + end + + # Ends the conversation, notifying the user of the key version number. + # + def end_conversation + puts "#{@log_prefix} Ending conversation" unless defined?(TESTING) + + @session.write("BYE\n"); end private @@ -185,35 +197,37 @@ def entry_point(server) while(session = server.accept) child = fork do remote = session.peeraddr[3] - - puts "Connected to #{remote}" + + puts "Connected to #{remote}" unless defined?(TESTING) # This is needed because we just forked a new process # which now needs its own connection to the database. database_connect - + begin browser = HostBrowser.new(session) 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) + case browser.get_mode + when "AWAKEN": browser.create_keytab(remote,session.peeraddr[3]) + when "IDENTIFY": browser.write_host_info(browser.get_remote_info) + end + + browser.end_conversation rescue Exception => error session.write("ERROR #{error.message}\n") - puts "ERROR #{error.message}" + puts "ERROR #{error.message}" unless defined?(TESTING) end - - puts "Disconnected from #{remote}" + + puts "Disconnected from #{remote}" unless defined?(TESTING) end - - Process.detach(child) - end + + Process.detach(child) + end end unless defined?(TESTING) - + # The main entry point. # unless ARGV[0] == "-n" diff --git a/wui/src/host-browser/test-host-browser-awaken.rb b/wui/src/host-browser/test-host-browser-awaken.rb new file mode 100755 index 0000000..a5ca2e7 --- /dev/null +++ b/wui/src/host-browser/test-host-browser-awaken.rb @@ -0,0 +1,94 @@ +#!/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' + +# +TestHostBrowserAwaken+ +class TestHostBrowserAwaken < 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/' + 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 is satisfied if the remote system is + # making a wakeup call. + # + def test_get_mode_with_awaken_request + @session.should_receive(:write).with("MODE?\n").once().returns { |request| request.length } + @session.should_receive(:readline).once().returns { "AWAKEN\n" } + + result = @browser.get_mode() + + assert_equal "AWAKEN", result, "method did not return the right value" + 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" } + servername = `hostname -f`.chomp + @session.should_receive(:write).with("KTAB http://#{servername}/config/127.0.0.1-libvirt.tab\n").once().returns { |request| request.length } + @session.should_receive(:readline).once().returns { "ACK\n" } + + assert_nothing_raised(Exception) { @browser.create_keytab('localhost','127.0.0.1', at krb5) } + 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("BYE\n").once().returns { |request| request.length } + + assert_nothing_raised(Exception) { @browser.end_conversation } + end + +end diff --git a/wui/src/host-browser/test-host-browser-identify.rb b/wui/src/host-browser/test-host-browser-identify.rb new file mode 100755 index 0000000..a70884d --- /dev/null +++ b/wui/src/host-browser/test-host-browser-identify.rb @@ -0,0 +1,162 @@ +#!/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"] } + + @browser = HostBrowser.new(@session) + @browser.logfile = './unit-test.log' + + # 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 is satisfied if the remote system is + # making a wakeup call. + # + def test_get_mode_with_awaken_request + @session.should_receive(:write).with("MODE?\n").once().returns { |request| request.length } + @session.should_receive(:readline).once().returns { "IDENTIFY\n" } + + result = @browser.get_mode() + + assert_equal "IDENTIFY", result, "method did not return the right value" + 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 4,info.keys.size, "Should contain two keys" + assert info.include?("IPADDR") + assert info.include?("HOSTNAME") + assert info.include?("key1") + assert info.include?("key2") + 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 + +end diff --git a/wui/src/host-browser/test-host-browser.rb b/wui/src/host-browser/test-host-browser.rb deleted file mode 100755 index 6f4c660..0000000 --- a/wui/src/host-browser/test-host-browser.rb +++ /dev/null @@ -1,204 +0,0 @@ -#!/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, 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("KTAB 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 } - - assert_nothing_raised(Exception) { @browser.end_conversation(12345) } - end - -end diff --git a/wui/version b/wui/version index f644984..538368d 100644 --- a/wui/version +++ b/wui/version @@ -1 +1 @@ -0.91 1 +0.92 0.1.200806240034git63f6a5a -- 1.5.5.1