Darryl L. Pierce
2008-Jul-21 13:25 UTC
[Ovirt-devel] [PATCH] Refactored the ovirt-identify codebase, and added NIC info during the identify phase.
Signed-off-by: Darryl L. Pierce <dpierce at redhat.com> --- ovirt-managed-node/ovirt-managed-node.spec | 2 + ovirt-managed-node/src/Makefile | 6 +- ovirt-managed-node/src/comm.c | 87 +++ ovirt-managed-node/src/debug.c | 48 ++ ovirt-managed-node/src/gather.c | 284 +++++++++ ovirt-managed-node/src/hal_support.c | 65 ++ ovirt-managed-node/src/main.c | 216 +++++++ ovirt-managed-node/src/ovirt-identify-node.c | 653 -------------------- ovirt-managed-node/src/ovirt-identify-node.h | 119 +++- ovirt-managed-node/src/protocol.c | 276 +++++++++ ovirt-managed-node/src/scripts/ovirt-post | 8 +- wui/src/host-browser/host-browser.rb | 84 +++- wui/src/host-browser/test-host-browser-identify.rb | 42 ++- 13 files changed, 1200 insertions(+), 690 deletions(-) create mode 100644 ovirt-managed-node/src/comm.c create mode 100644 ovirt-managed-node/src/debug.c create mode 100644 ovirt-managed-node/src/gather.c create mode 100644 ovirt-managed-node/src/hal_support.c create mode 100644 ovirt-managed-node/src/main.c delete mode 100644 ovirt-managed-node/src/ovirt-identify-node.c create mode 100644 ovirt-managed-node/src/protocol.c diff --git a/ovirt-managed-node/ovirt-managed-node.spec b/ovirt-managed-node/ovirt-managed-node.spec index 4e14b08..0cd3aa8 100644 --- a/ovirt-managed-node/ovirt-managed-node.spec +++ b/ovirt-managed-node/ovirt-managed-node.spec @@ -12,7 +12,9 @@ URL: http://www.ovirt.org/ Requires(post): /sbin/chkconfig Requires(preun): /sbin/chkconfig BuildRequires: libvirt-devel +BuildRequires: dbus-devel hal-devel Requires: libvirt +Requires: hal ExclusiveArch: %{ix86} x86_64 %define app_root %{_datadir}/%{name} diff --git a/ovirt-managed-node/src/Makefile b/ovirt-managed-node/src/Makefile index 1991e46..d5e4093 100644 --- a/ovirt-managed-node/src/Makefile +++ b/ovirt-managed-node/src/Makefile @@ -17,9 +17,9 @@ # also available at http://www.gnu.org/copyleft/gpl.html. CC=gcc -CFLAGS=-Wall -c -g -LFLAGS=-lvirt -OBJECTS=ovirt-identify-node.o +CFLAGS=-Wall -c -g $(shell pkg-config --cflags dbus-1) +LFLAGS=-lhal -lvirt +OBJECTS=comm.o gather.o hal_support.o main.o protocol.o TARGET=ovirt-identify-node SOURCES=\ ovirt-identify-node.c diff --git a/ovirt-managed-node/src/comm.c b/ovirt-managed-node/src/comm.c new file mode 100644 index 0000000..a63a58d --- /dev/null +++ b/ovirt-managed-node/src/comm.c @@ -0,0 +1,87 @@ +/* comm.c -- Contains communications routines. + * + * 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 "ovirt-identify-node.h" + +ssize_t saferead(int fd, char *buf, size_t count) +{ + ssize_t bytes,offset; + int len_left; + int done = 0; + + DEBUG("Begin saferead(%d, %p, %d)\n", fd, buf, count); + + offset = 0; + len_left = count; + + + while(!done) + { + DEBUG("Before read(%d,%p,%d)\n",fd,buf+offset,len_left); + + bytes = read(fd, buf+offset, len_left); + + DEBUG("After read: bytes=%d\n", bytes); + + if(bytes == 0) + { + done = 1; + } + else if(bytes > 0) + { + offset += bytes; + len_left -= bytes; + done = 1; + } + else if(errno == EINTR) + { + continue; + } + else + { + done = 1; + } + + DEBUG("End of decision loop: offset=%d, len_left=%dl, done=%d\n",offset, len_left, done); + } + + return offset; +} + +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; +} + diff --git a/ovirt-managed-node/src/debug.c b/ovirt-managed-node/src/debug.c new file mode 100644 index 0000000..807d88b --- /dev/null +++ b/ovirt-managed-node/src/debug.c @@ -0,0 +1,48 @@ +/* debug.c -- Debugging methods. + * + * 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 "ovirt-identify-node.h" + +void debug_cpu_info(void) +{ + fprintf(stdout,"Node Info:\n"); + fprintf(stdout," UUID: %s\n", uuid); + fprintf(stdout," Arch: %s\n", arch); + fprintf(stdout," Memory: %s\n", memsize); + + t_cpu_info* current = cpu_info; + while(current != NULL) + { + fprintf(stdout,"\n"); + fprintf(stdout," CPU Number: %s\n", current->cpu_num); + fprintf(stdout," Core Number: %s\n", current->core_num); + fprintf(stdout,"Number of Cores: %s\n", current->number_of_cores); + fprintf(stdout," Vendor: %s\n", current->vendor); + fprintf(stdout," Model: %s\n", current->model); + fprintf(stdout," Family: %s\n", current->family); + fprintf(stdout," CPUID Level: %s\n", current->cpuid_level); + fprintf(stdout," CPU Speed: %s\n", current->speed); + fprintf(stdout," Cache Size: %s\n", current->cache); + fprintf(stdout," CPU Flags: %s\n", current->flags); + + current = current->next; + } +} diff --git a/ovirt-managed-node/src/gather.c b/ovirt-managed-node/src/gather.c new file mode 100644 index 0000000..c7f6278 --- /dev/null +++ b/ovirt-managed-node/src/gather.c @@ -0,0 +1,284 @@ +/* gather.c -- Contains methods for collecting data about the system. + * + * 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 "ovirt-identify-node.h" + +int init_gather(void) +{ + int result = 1; + + hal_ctx = get_hal_ctx(); + + if(hal_ctx != NULL) + { + result = 0; + } + + return result; +} + +int get_uuid(void) +{ + const char* udi = "/org/freedesktop/Hal/devices/computer"; + const char* key = "system.hardware.uuid"; + + VERBOSE("Getting system UUID.\n"); + + int result = 1; + int type; + + type = libhal_device_get_property_type(hal_ctx,udi,key,&dbus_error); + + if(type == LIBHAL_PROPERTY_TYPE_STRING) + { + char* value; + + DEBUG("%s/%s=%d\n",udi,key,type); + + value = libhal_device_get_property_string(hal_ctx,udi,key,&dbus_error); + snprintf(uuid,BUFFER_LENGTH,"%s",value); + + DEBUG("UUID=%s\n",uuid); + + result = 0; + } + + return result; +} + +/* Creates a new instance of type t_cpu_info and places it into the + * linked list of CPUs. + */ +t_cpu_info* create_cpu_info(void) +{ + t_cpu_info* result = calloc(1,sizeof(t_cpu_info)); + bzero(result,sizeof(t_cpu_info)); + + strcpy(result->core_num,"0"); + strcpy(result->number_of_cores,"1"); + + return result; +} + +int get_cpu_info(void) +{ + int result = 1; + FILE* inputfd; + t_cpu_info* current = NULL; + + /* in order to support Xen, this data will need to be gathered + * from libvirt rather than directly from cpuinfo + */ + if(( inputfd = fopen("/proc/cpuinfo","rb")) != NULL) + { + VERBOSE("Parsing CPU information\n"); + do + { + char buffer[255]; + char label[BUFFER_LENGTH]; + char value[BUFFER_LENGTH]; + + fgets(buffer, 255, inputfd); + if(strlen(buffer) > 0) buffer[strlen(buffer) - 1] = '\0'; + + get_label_and_value(buffer, + label,BUFFER_LENGTH, + value,BUFFER_LENGTH); + + DEBUG("label=\"%s\", value=\"%s\"\n", label, value); + + if(strlen(label)) + { + if(!strcmp(label,"processor")) + { + VERBOSE("Starting new CPU\n"); + + t_cpu_info* last = current; + + current = create_cpu_info(); + if(last != NULL) + { + last->next = current; + } + else + { + cpu_info = current; + } + + COPY_VALUE_TO_BUFFER(value,current->cpu_num,BUFFER_LENGTH); + } + else + /* core id and number of cores is not correct on + * Xen machines + */ + if(!strcmp(label,"core id")) + { + COPY_VALUE_TO_BUFFER(value,current->core_num,BUFFER_LENGTH); + } + else + if(!strcmp(label,"cpu cores")) + { + COPY_VALUE_TO_BUFFER(value,current->number_of_cores,BUFFER_LENGTH); + } + else + if(!strcmp(label,"vendor_id")) + { + COPY_VALUE_TO_BUFFER(value,current->vendor,BUFFER_LENGTH); + } + else + if(!strcmp(label,"model")) + { + COPY_VALUE_TO_BUFFER(value,current->model,BUFFER_LENGTH); + } + else + if(!strcmp(label,"cpu family")) + { + COPY_VALUE_TO_BUFFER(value,current->family,BUFFER_LENGTH); + } + else + if(!strcmp(label,"cpuid level")) + { + COPY_VALUE_TO_BUFFER(value,current->cpuid_level,BUFFER_LENGTH); + } + else + if(!strcmp(label,"cpu MHz")) + { + COPY_VALUE_TO_BUFFER(value,current->speed,BUFFER_LENGTH); + } + else + if(!strcmp(label,"cache size")) + { + COPY_VALUE_TO_BUFFER(value,current->cache,BUFFER_LENGTH); + } + else + if(!strcmp(label,"flags")) + { + COPY_VALUE_TO_BUFFER(value,current->flags,BUFFER_LENGTH); + } + } + + } while(!feof(inputfd)); + + fclose(inputfd); + + result = 0; + } + else + { + VERBOSE("Unable to open /proc/cpuinfo\n"); + } + + return result; +} + +/* Creates a new instance of type t_nic_info. + */ +t_nic_info* create_nic_info(void) +{ + t_nic_info* result = calloc(1,sizeof(t_nic_info)); + bzero(result,sizeof(t_nic_info)); + + return result; +} + +/* Determines the speed of the network interface. + */ +void get_nic_data(char* nic,t_nic_info* nic_info) +{ + char* interface; + struct ifreq ifr; + int sockfd; + struct ethtool_cmd ecmd; + + interface = libhal_device_get_property_string(hal_ctx,nic,"net.interface", + &dbus_error); + bzero(&ifr,sizeof(struct ifreq)); + + sockfd = socket(AF_INET, SOCK_DGRAM, 0); + if(sockfd >= 0) + { + int bandwidth; + + ifr.ifr_addr.sa_family = AF_INET; + strncpy(ifr.ifr_name,interface,IFNAMSIZ-1); + + ioctl(sockfd, SIOCETHTOOL, &ifr); + close(sockfd); + + bandwidth = 10; + if (ecmd.supported & SUPPORTED_10000baseT_Full) + bandwidth = 10000; + else if (ecmd.supported & SUPPORTED_2500baseX_Full) + bandwidth = 2500; + else if (ecmd.supported & (SUPPORTED_1000baseT_Half|SUPPORTED_1000baseT_Full)) + bandwidth = 1000; + else if (ecmd.supported & (SUPPORTED_100baseT_Half|SUPPORTED_100baseT_Full)) + bandwidth = 100; + else if (ecmd.supported & (SUPPORTED_10baseT_Half|SUPPORTED_10baseT_Full)) + bandwidth = 10; + + snprintf(nic_info->bandwidth,BUFFER_LENGTH,"%d",bandwidth); + } +} + +int get_nic_info(void) +{ + int result = 0; + t_nic_info* current = NULL; + t_nic_info* last = NULL; + + char** nics; + int num_results; + int index; + + nics = libhal_find_device_by_capability(hal_ctx,"net", + &num_results,&dbus_error); + + DEBUG("Found %d NICs\n", num_results); + + for(index = 0;index < num_results;index++) + { + char* nic = nics[index]; + + VERBOSE("Starting new NIC.\n"); + + if(current != NULL) + { + last = current; + current = create_nic_info(); + last->next = current; + } + else + { + nic_info = current = create_nic_info(); + } + + snprintf(current->mac_address,BUFFER_LENGTH,"%s", + libhal_device_get_property_string(hal_ctx,nic,"net.address", + &dbus_error)); + get_nic_data(nic,current); + + DEBUG("NIC details: MAC:%s, speed:%s, IP:%s\n", + nic_info->mac_address, nic_info->bandwidth,nic_info->ip_address); + } + + return result; +} diff --git a/ovirt-managed-node/src/hal_support.c b/ovirt-managed-node/src/hal_support.c new file mode 100644 index 0000000..58ee29b --- /dev/null +++ b/ovirt-managed-node/src/hal_support.c @@ -0,0 +1,65 @@ +/* hal_support.c -- Interfaces with the HAL libraries. + * + * 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 "ovirt-identify-node.h" + +DBusConnection* dbus_connection; +DBusError dbus_error; + +LibHalContext* get_hal_ctx(void) +{ + LibHalContext* result = NULL; + LibHalContext* ctx; + + ctx = libhal_ctx_new(); + if(ctx != NULL) + { + dbus_error_init(&dbus_error); + dbus_connection = dbus_bus_get(DBUS_BUS_SYSTEM,&dbus_error); + + if(!dbus_error_is_set(&dbus_error)) + { + libhal_ctx_set_dbus_connection(ctx,dbus_connection); + + if(libhal_ctx_init(ctx,&dbus_error)) + { + result = ctx; + } + else + { + fprintf(stderr,"Failed to initial libhal context: %s : %s\n", + dbus_error.name, dbus_error.message); + } + } + else + { + fprintf(stderr,"Unable to connect to system bus: %s : %s\n", + dbus_error.name, dbus_error.message); + dbus_error_free(&dbus_error); + } + } + else + { + fprintf(stderr,"Unable to initialize HAL context.\n"); + } + + return result; +} diff --git a/ovirt-managed-node/src/main.c b/ovirt-managed-node/src/main.c new file mode 100644 index 0000000..29e08a5 --- /dev/null +++ b/ovirt-managed-node/src/main.c @@ -0,0 +1,216 @@ +/* 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 "ovirt-identify-node.h" + +int debug = 0; +int verbose = 0; +int testing = 0; + +char arch[BUFFER_LENGTH]; +char uuid[BUFFER_LENGTH]; +char memsize[BUFFER_LENGTH]; +char numcpus[BUFFER_LENGTH]; +char cpuspeed[BUFFER_LENGTH]; +char *hostname; +int hostport = -1; +int socketfd; +t_cpu_info* cpu_info; +t_nic_info* nic_info; + +LibHalContext* hal_ctx; + +int main(int argc,char** argv) +{ + int result = 1; + virConnectPtr connection; + virNodeInfo info; + + fprintf(stdout,"Sending managed node details to server.\n"); + + if(!config(argc,argv)) + { + VERBOSE("Connecting to libvirt.\n"); + + connection = virConnectOpenReadOnly(testing ? "test:///default" : NULL); + + DEBUG("connection=%p\n",connection); + + if(connection) + { + VERBOSE("Retrieving node information.\n"); + if(!virNodeGetInfo(connection,&info)) + { + snprintf(arch, BUFFER_LENGTH, "%s", info.model); + snprintf(memsize, BUFFER_LENGTH, "%ld", info.memory); + + cpu_info = NULL; + nic_info = NULL; + + if(!init_gather() && !get_uuid() && !get_cpu_info() && !get_nic_info()) + { + if(!start_conversation() && !send_details() && !end_conversation()) + { + fprintf(stdout,"Finished!\n"); + result = 0; + } + } + else + { + VERBOSE("Failed to get CPU info.\n"); + } + } + else + { + VERBOSE("Failed to get node info.\n"); + } + } + else + { + VERBOSE("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) + { + DEBUG("Processing argument: %c (optarg:%s)\n",option,optarg); + + switch(option) + { + case 's': 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"); +} + +void get_label_and_value(char* text, + char* label, size_t label_length, + char* value, size_t value_length) +{ + int offset = 0; + int which = 0; /* 0 = label, 1 = value */ + char* current = text; + + /* iterate through the text supplied and find where the + * label ends with a colon, then copy that into the supplied + * label buffer and trim any trailing spaces + */ + + while(current != NULL && *current != '\0') + { + /* if we're on the separator, then switch modes and reset + * the offset indicator, otherwise just process the character + */ + if(which == 0 && *current == ':') + { + which = 1; + offset = 0; + } + else + { + char* buffer = (which == 0 ? label : value); + int length = (which == 0 ? label_length : value_length); + + /* only copy if we're past the first character and it's not + * a space + */ + if((offset > 0 || (*current != 9 && *current != ' ')) && offset < (length - 1)) + { + buffer[offset++] = *current; + buffer[offset] = 0; + } + } + + current++; + } + + /* now trim all trailing spaces from the values */ + while(label[strlen(label) - 1 ] == 9) + label[strlen(label) - 1] = 0; + while(value[strlen(value) - 1] == 9) + value[strlen(value) - 1] = 0; +} + +int get_text(const char *const expected) +{ + int result = 1; + int received; + char buffer[BUFFER_LENGTH]; + bzero(buffer,BUFFER_LENGTH); + + VERBOSE( "Looking to receive %s\n", expected); + + received = saferead(socketfd, buffer, BUFFER_LENGTH); + + buffer[received - 1] = 0; + + VERBOSE("Received \"%s\": size=%d (trimmed ending carriage return)\n", buffer, received); + + result = strcmp(expected,buffer); + + return result; +} diff --git a/ovirt-managed-node/src/ovirt-identify-node.c b/ovirt-managed-node/src/ovirt-identify-node.c deleted file mode 100644 index f114d81..0000000 --- a/ovirt-managed-node/src/ovirt-identify-node.c +++ /dev/null @@ -1,653 +0,0 @@ -/* 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> - -#include "ovirt-identify-node.h" - -int main(int argc,char** argv) -{ - int result = 1; - virConnectPtr connection; - virNodeInfo info; - - fprintf(stdout,"Sending managed node details to server.\n"); - - if(!config(argc,argv)) - { - if(verbose) fprintf(stdout,"Connecting to libvirt.\n"); - - connection = virConnectOpenReadOnly(testing ? "test:///default" : NULL); - - if(debug) fprintf(stderr,"connection=%p\n",connection); - - if(connection) - { - if(verbose) fprintf(stdout,"Getting hostname: %s\n", uuid); - if(!strlen(uuid)) gethostname(uuid,sizeof uuid); - - if(verbose) fprintf(stdout,"Retrieving node information.\n"); - if(!virNodeGetInfo(connection,&info)) - { - snprintf(arch, BUFFER_LENGTH, "%s", info.model); - snprintf(memsize, BUFFER_LENGTH, "%ld", info.memory); - - cpu_info = NULL; - - if(!get_cpu_info()) - { - if(verbose) fprintf(stdout, "Getting CPU info.\n"); - - 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); - - t_cpu_info* current = cpu_info; - while(current != NULL) - { - fprintf(stdout,"\n"); - fprintf(stdout," CPU Number: %s\n", current->cpu_num); - fprintf(stdout," Core Number: %s\n", current->core_num); - fprintf(stdout,"Number of Cores: %s\n", current->number_of_cores); - fprintf(stdout," Vendor: %s\n", current->vendor); - fprintf(stdout," Model: %s\n", current->model); - fprintf(stdout," Family: %s\n", current->family); - fprintf(stdout," CPUID Level: %s\n", current->cpuid_level); - fprintf(stdout," CPU Speed: %s\n", current->speed); - fprintf(stdout," Cache Size: %s\n", current->cache); - fprintf(stdout," CPU Flags: %s\n", current->flags); - - current = current->next; - } - } - - if(verbose) fprintf(stdout, "Retrieved node information.\n"); - - if(!start_conversation() && !send_details() && !end_conversation()) - { - fprintf(stdout,"Finished!\n"); - result = 0; - } - } - else - { - if(verbose) fprintf(stderr,"Failed to get CPU info.\n"); - } - } - else - { - if(verbose) fprintf(stderr,"Failed to get node info.\n"); - } - } - else - { - if(verbose) 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:u:dvth")) != -1) - { - if(debug) fprintf(stdout,"Processing argument: %c (optarg:%s)\n",option,optarg); - - switch(option) - { - case 's': hostname = optarg; break; - case 'p': hostport = atoi(optarg); break; - case 'u': snprintf(uuid,VIR_UUID_BUFLEN,"%s",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"); - - if (!get_text("HELLO?")) - { - if(verbose) fprintf(stdout,"Checking for handshake.\n"); - - if(!send_text("HELLO!")) - { - if(verbose) fprintf(stdout,"Handshake received. Starting conversation.\n"); - - if(!get_text("MODE?")) - { - if(verbose) fprintf(stdout,"Shifting to IDENTIFY mode.\n"); - - if(!send_text("IDENTIFY")) result = 0; - } - else - { - if(verbose) fprintf(stderr,"Was not asked for a mode.\n"); - } - } - } - else - { - if(verbose) fprintf(stderr,"Did not receive a proper handshake.\n"); - } - } - - else - { - if(verbose) 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]; - int result = 1; - char expected[BUFFER_LENGTH]; - - snprintf(buffer,BUFFER_LENGTH,"%s=%s", label, value); - - if(!send_text(buffer)) - { - snprintf(expected, BUFFER_LENGTH, "ACK %s", label); - - if(verbose) fprintf(stdout,"Expecting \"%s\"\n", expected); - - result = get_text(expected); - } - - return result; -} - -int send_details(void) -{ - int result = 1; - - if(verbose) fprintf(stdout,"Sending node details.\n"); - - if (!get_text("INFO?")) - { - if((!send_value("ARCH", arch)) && - (!send_value("UUID", uuid)) && - (!send_value("MEMSIZE", memsize)) && - (!send_cpu_details())) - { - if(!send_text("ENDINFO")) result = 0; - } - } - else - { - if(verbose) fprintf(stdout,"Was not interrogated for hardware info.\n"); - } - - return result; -} - -int send_cpu_details(void) -{ - int result = 1; - t_cpu_info* current = cpu_info; - - while(current != NULL) - { - send_text("CPU"); - - if(!(get_text("CPUINFO?")) && - (!send_value("CPUNUM",current->cpu_num)) && - (!send_value("CORENUM",current->core_num)) && - (!send_value("NUMCORES",current->number_of_cores)) && - (!send_value("VENDOR",current->vendor)) && - (!send_value("MODEL",current->model)) && - (!send_value("FAMILY",current->family)) && - (!send_value("CPUIDLVL",current->cpuid_level)) && - (!send_value("SPEED",current->speed)) && - (!send_value("CACHE", current->cache)) && - (!send_value("FLAGS", current->flags))) - { - send_text("ENDCPU"); - result = get_text("ACK CPU"); - } - - current = current->next; - } - - - return result; -} - -int end_conversation(void) -{ - int result = 0; - - if(debug || verbose) fprintf(stdout,"Ending conversation.\n"); - - send_text("ENDINFO"); - - close(socketfd); - - return result; -} - -void get_label_and_value(char* text, - char* label, size_t label_length, - char* value, size_t value_length) -{ - int offset = 0; - int which = 0; /* 0 = label, 1 = value */ - char* current = text; - - /* iterate through the text supplied and find where the - * label ends with a colon, then copy that into the supplied - * label buffer and trim any trailing spaces - */ - - while(current != NULL && *current != '\0') - { - /* if we're on the separator, then switch modes and reset - * the offset indicator, otherwise just process the character - */ - if(which == 0 && *current == ':') - { - which = 1; - offset = 0; - } - else - { - char* buffer = (which == 0 ? label : value); - int length = (which == 0 ? label_length : value_length); - - /* only copy if we're past the first character and it's not - * a space - */ - if((offset > 0 || (*current != 9 && *current != ' ')) && offset < (length - 1)) - { - buffer[offset++] = *current; - buffer[offset] = 0; - } - } - - current++; - } - - /* now trim all trailing spaces from the values */ - while(label[strlen(label) - 1 ] == 9) - label[strlen(label) - 1] = 0; - while(value[strlen(value) - 1] == 9) - value[strlen(value) - 1] = 0; -} - -int get_cpu_info(void) -{ - int result = 1; - FILE* inputfd; - t_cpu_info* current = NULL; - - if(( inputfd = fopen("/proc/cpuinfo","rb")) != NULL) - { - if(verbose) fprintf(stdout,"Parsing CPU information\n"); - do - { - char buffer[255]; - char label[BUFFER_LENGTH]; - char value[BUFFER_LENGTH]; - - fgets(buffer, 255, inputfd); - if(strlen(buffer) > 0) buffer[strlen(buffer) - 1] = '\0'; - - get_label_and_value(buffer, - label,BUFFER_LENGTH, - value,BUFFER_LENGTH); - - if(debug) - fprintf(stdout,"label=\"%s\", value=\"%s\"\n", label, value); - - if(strlen(label)) - { - if(!strcmp(label,"processor")) - { - if(debug || verbose) - fprintf(stdout,"Starting new CPU\n"); - - t_cpu_info* last = current; - - current = create_cpu_info(); - if(last != NULL) - { - last->next = current; - } - else - { - cpu_info = current; - } - - COPY_VALUE_TO_BUFFER(value,current->cpu_num,BUFFER_LENGTH); - } - else - if(!strcmp(label,"core id")) - { - COPY_VALUE_TO_BUFFER(value,current->core_num,BUFFER_LENGTH); - } - else - if(!strcmp(label,"cpu cores")) - { - COPY_VALUE_TO_BUFFER(value,current->number_of_cores,BUFFER_LENGTH); - } - else - if(!strcmp(label,"vendor_id")) - { - COPY_VALUE_TO_BUFFER(value,current->vendor,BUFFER_LENGTH); - } - else - if(!strcmp(label,"model")) - { - COPY_VALUE_TO_BUFFER(value,current->model,BUFFER_LENGTH); - } - else - if(!strcmp(label,"cpu family")) - { - COPY_VALUE_TO_BUFFER(value,current->family,BUFFER_LENGTH); - } - else - if(!strcmp(label,"cpuid level")) - { - COPY_VALUE_TO_BUFFER(value,current->cpuid_level,BUFFER_LENGTH); - } - else - if(!strcmp(label,"cpu MHz")) - { - COPY_VALUE_TO_BUFFER(value,current->speed,BUFFER_LENGTH); - } - else - if(!strcmp(label,"cache size")) - { - COPY_VALUE_TO_BUFFER(value,current->cache,BUFFER_LENGTH); - } - else - if(!strcmp(label,"flags")) - { - COPY_VALUE_TO_BUFFER(value,current->flags,BUFFER_LENGTH); - } - } - - } while(!feof(inputfd)); - - fclose(inputfd); - - result = 0; - } - else - { - if(verbose) fprintf(stderr,"Unable to open /proc/cpuinfo\n"); - } - - return result; -} - -t_cpu_info* create_cpu_info(void) -{ - t_cpu_info* result = calloc(1,sizeof(t_cpu_info)); - bzero(result,sizeof(t_cpu_info)); - - strcpy(result->core_num,"0"); - strcpy(result->number_of_cores,"1"); - - - 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; - int sent; - - if(verbose) fprintf(stdout,"Sending: \"%s\"\n", text); - - sent = safewrite(socketfd, text, strlen(text)); - sent += safewrite(socketfd, "\n", 1); - - if(sent >= 0) - { - if(debug) fprintf(stdout,"Sent %d bytes total.\n", sent); - - result = 0; - } - - return result; -} - -int saferead(int fd, char *buf, size_t count) -{ - ssize_t bytes,offset; - int len_left; - int done = 0; - - if(debug) fprintf(stdout,"Begin saferead(%d, %p, %ld)\n", fd, buf, count); - - offset = 0; - len_left = count; - - - while(!done) - { - if(debug) fprintf(stdout,"Before read(%ld,%p,%ld)\n",fd,buf+offset,len_left); - - bytes = read(fd, buf+offset, len_left); - - if(debug) fprintf(stdout,"After read: bytes=%ld\n", bytes); - - if(bytes == 0) - { - done = 1; - } - else if(bytes > 0) - { - offset += bytes; - len_left -= bytes; - done = 1; - } - else if(errno == EINTR) - { - continue; - } - else - { - done = 1; - } - - if(debug) fprintf(stdout,"End of decision loop: offset=%ld, len_left=%dl, done=%d\n",offset, len_left, done); - } - - return offset; -} - -int get_text(const char *const expected) -{ - int result = 1; - int received; - char buffer[BUFFER_LENGTH]; - bzero(buffer,BUFFER_LENGTH); - - if(verbose) fprintf(stdout, "Looking to receive %s\n", expected); - - received = saferead(socketfd, buffer, BUFFER_LENGTH); - - buffer[received - 1] = 0; - - if(verbose) fprintf(stdout,"Received \"%s\": size=%d (trimmed ending carriage return)\n", buffer, received); - - result = strcmp(expected,buffer); - - return result; -} - -int create_connection(void) -{ - int result = 1; - struct addrinfo hints; - struct addrinfo* results; - char port[6]; - struct addrinfo* rptr; - - if(verbose) fprintf(stdout,"Creating the socket connection.\n"); - - 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(verbose) fprintf(stdout,"Searching for host candidates.\n"); - - snprintf(port, 6, "%d", hostport); - - if(!getaddrinfo(hostname, port, &hints, &results)) - { - if(verbose) fprintf(stdout,"Got address information. Searching for a proper entry.\n"); - - 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(verbose) fprintf(stdout, "Invalid connection.\n"); - close(socketfd); - } - - if(rptr == NULL) - { - if(verbose) fprintf(stdout,"Unable to connect to server %s:%d\n", hostname, hostport); - } - else - { - // success - result = 0; - } - - freeaddrinfo(results); - } - else - { - if(verbose) 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/src/ovirt-identify-node.h b/ovirt-managed-node/src/ovirt-identify-node.h index 1cb1526..fd1bc0a 100644 --- a/ovirt-managed-node/src/ovirt-identify-node.h +++ b/ovirt-managed-node/src/ovirt-identify-node.h @@ -1,6 +1,49 @@ +/* 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. + */ + #ifndef __OVIRT_IDENTIFY_NODE_H #define __OVIRT_IDENTIFY_NODE_H +#include <errno.h> +#include <getopt.h> +#include <netdb.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <arpa/inet.h> + +#include <hal/libhal.h> + +#include <libvirt/libvirt.h> + +#include <linux/ethtool.h> +#include <linux/sockios.h> +#include <linux/if.h> + +#include <netinet/in.h> + +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/socket.h> + #define BUFFER_LENGTH 128 #define CPU_FLAGS_BUFFER_LENGTH 256 @@ -18,39 +61,71 @@ typedef struct _cpu_info { struct _cpu_info* next; } t_cpu_info; -#define COPY_VALUE_TO_BUFFER(value,buffer,length) \ - snprintf(buffer,length,"%s",value) +typedef struct _nic_info { + char mac_address[BUFFER_LENGTH]; + char bandwidth[BUFFER_LENGTH]; + char ip_address[BUFFER_LENGTH]; + struct _nic_info* next; +} t_nic_info; int config(int argc,char** argv); void usage(void); -int start_conversation(void); -int send_details(void); -int send_cpu_details(void); -int end_conversation(void); - void get_label_and_value(char* text, char* label,size_t label_length, char* value,size_t value_length); -t_cpu_info* create_cpu_info(void); -int get_cpu_info(void); int send_text(char* text); int get_text(const char *const expected); + +/* comm.c */ +ssize_t saferead(int fd, char *buf, size_t count); +ssize_t safewrite(int fd, const void *buf, size_t count); + +/* debug.c */ +void debug_cpu_info(void); + +/* gather.c */ +int init_gather(void); +int get_uuid(void); +int get_cpu_info(void); +int get_nic_info(void); + +/* hal_support.c */ +LibHalContext* get_hal_ctx(void); + +/* protocol.c */ int create_connection(void); +int start_conversation(void); +int send_details(void); +int end_conversation(void); +int send_value(char* label,char* value); +int send_text(char* text); + +/* variables */ +extern int debug; +extern int verbose; +extern int testing; + +extern char arch[BUFFER_LENGTH]; +extern char uuid[BUFFER_LENGTH]; +extern char memsize[BUFFER_LENGTH]; +extern char numcpus[BUFFER_LENGTH]; +extern char cpuspeed[BUFFER_LENGTH]; +extern char *hostname; +extern int hostport; +extern int socketfd; +extern t_cpu_info* cpu_info; +extern t_nic_info* nic_info; -int debug = 0; -int verbose = 0; -int testing = 0; - -char arch[BUFFER_LENGTH]; -char uuid[VIR_UUID_BUFLEN]; -char memsize[BUFFER_LENGTH]; -char numcpus[BUFFER_LENGTH]; -char cpuspeed[BUFFER_LENGTH]; -char *hostname; -int hostport = -1; -int socketfd; -t_cpu_info* cpu_info; +extern DBusConnection* dbus_connection; +extern DBusError dbus_error; +extern LibHalContext* hal_ctx; + +/* macros */ +#define DEBUG(arg...) if(debug) fprintf(stderr, ##arg) +#define VERBOSE(arg...) if(verbose) fprintf(stdout, ##arg) +#define COPY_VALUE_TO_BUFFER(value,buffer,length) \ + snprintf(buffer,length,"%s",value) #endif diff --git a/ovirt-managed-node/src/protocol.c b/ovirt-managed-node/src/protocol.c new file mode 100644 index 0000000..111a1c1 --- /dev/null +++ b/ovirt-managed-node/src/protocol.c @@ -0,0 +1,276 @@ +/* protocol.c -- Manages the communication between the managed node and + * the oVirt 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. + */ + +#include "ovirt-identify-node.h" + +int create_connection(void) +{ + int result = 1; + struct addrinfo hints; + struct addrinfo* results; + char port[6]; + struct addrinfo* rptr; + + VERBOSE("Creating the socket connection.\n"); + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = 0; + hints.ai_protocol = 0; + + VERBOSE("Searching for host candidates.\n"); + + snprintf(port, 6, "%d", hostport); + + if(!getaddrinfo(hostname, port, &hints, &results)) + { + VERBOSE("Got address information. Searching for a proper entry.\n"); + + 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 + VERBOSE( "Invalid connection.\n"); + close(socketfd); + } + + if(rptr == NULL) + { + VERBOSE("Unable to connect to server %s:%d\n", hostname, hostport); + } + else + { + // success + result = 0; + } + + freeaddrinfo(results); + } + else + { + VERBOSE("No hosts found. Exiting...\n"); + } + + DEBUG( "create_connection: result=%d\n", result); + + return result; +} + +int start_conversation(void) +{ + int result = 1; + + VERBOSE("Starting conversation with %s:%d.\n",hostname,hostport); + + if(!create_connection()) + { + VERBOSE("Connected.\n"); + + if (!get_text("HELLO?")) + { + VERBOSE("Checking for handshake.\n"); + + if(!send_text("HELLO!")) + { + VERBOSE("Handshake received. Starting conversation.\n"); + + if(!get_text("MODE?")) + { + VERBOSE("Shifting to IDENTIFY mode.\n"); + + if(!send_text("IDENTIFY")) result = 0; + } + else + { + VERBOSE("Was not asked for a mode.\n"); + } + } + } + else + { + VERBOSE("Did not receive a proper handshake.\n"); + } + } + + else + { + VERBOSE("Did not get a connection.\n"); + } + + DEBUG("start_conversation: result=%d\n", result); + + return result; +} + +/* Transmits the CPU details to the server. + */ +int send_cpu_details(void) +{ + int result = 1; + t_cpu_info* current = cpu_info; + + while(current != NULL) + { + send_text("CPU"); + + if(!(get_text("CPUINFO?")) && + (!send_value("CPUNUM",current->cpu_num)) && + (!send_value("CORENUM",current->core_num)) && + (!send_value("NUMCORES",current->number_of_cores)) && + (!send_value("VENDOR",current->vendor)) && + (!send_value("MODEL",current->model)) && + (!send_value("FAMILY",current->family)) && + (!send_value("CPUIDLVL",current->cpuid_level)) && + (!send_value("SPEED",current->speed)) && + (!send_value("CACHE", current->cache)) && + (!send_value("FLAGS", current->flags))) + { + send_text("ENDCPU"); + result = get_text("ACK CPU"); + } + + current = current->next; + } + + + return result; +} + +/* Transmits the NIC details to the server. + */ +int send_nic_details(void) +{ + int result = 1; + t_nic_info* current = nic_info; + + while(current != NULL) + { + send_text("NIC"); + + if(!(get_text("NICINFO?")) && + (!send_value("MAC", current->mac_address)) && + (!send_value("BANDWIDTH",current->bandwidth))) + { + send_text("ENDNIC"); + result = get_text("ACK NIC"); + } + + current = current->next; + } + + return result; +} + +int send_details(void) +{ + int result = 1; + + VERBOSE("Sending node details.\n"); + + if (!get_text("INFO?")) + { + if((!send_value("ARCH", arch)) && + (!send_value("UUID", uuid)) && + (!send_value("MEMSIZE", memsize)) && + (!send_cpu_details() && !send_nic_details())) + { + if(!send_text("ENDINFO")) result = 0; + } + } + else + { + VERBOSE("Was not interrogated for hardware info.\n"); + } + + return result; +} + +int end_conversation(void) +{ + int result = 0; + + VERBOSE("Ending conversation.\n"); + + send_text("ENDINFO"); + + close(socketfd); + + return result; +} + +int send_value(char* label,char* value) +{ + char buffer[BUFFER_LENGTH]; + int result = 1; + char expected[BUFFER_LENGTH]; + + snprintf(buffer,BUFFER_LENGTH,"%s=%s", label, value); + + if(!send_text(buffer)) + { + snprintf(expected, BUFFER_LENGTH, "ACK %s", label); + + VERBOSE("Expecting \"%s\"\n", expected); + + result = get_text(expected); + } + + return result; +} + +int send_text(char* text) +{ + int result = 1; + int sent; + + VERBOSE("Sending: \"%s\"\n", text); + + sent = safewrite(socketfd, text, strlen(text)); + sent += safewrite(socketfd, "\n", 1); + + if(sent >= 0) + { + DEBUG("Sent %d bytes total.\n", sent); + + result = 0; + } + + return result; +} diff --git a/ovirt-managed-node/src/scripts/ovirt-post b/ovirt-managed-node/src/scripts/ovirt-post index f113b28..310a41c 100755 --- a/ovirt-managed-node/src/scripts/ovirt-post +++ b/ovirt-managed-node/src/scripts/ovirt-post @@ -12,14 +12,8 @@ start() { find_srv identify tcp - UUID=`hal-get-property --udi \ - /org/freedesktop/Hal/devices/computer --key system.hardware.uuid` - if [ -z $UUID ]; then - ovirt-identify-node -s $SRV_HOST -p $SRV_PORT - else - ovirt-identify-node -s $SRV_HOST -p $SRV_PORT -u $UUID - fi + ovirt-identify-node -s $SRV_HOST -p $SRV_PORT } case "$1" in diff --git a/wui/src/host-browser/host-browser.rb b/wui/src/host-browser/host-browser.rb index 624661d..d209bdb 100755 --- a/wui/src/host-browser/host-browser.rb +++ b/wui/src/host-browser/host-browser.rb @@ -87,8 +87,8 @@ class HostBrowser break if info == "ENDINFO" - # if we got the start of a CPU details marker, then process it - if info == "CPU" + case info + when "CPU" cpu = get_cpu_info cpu_info = result['CPUINFO'] @@ -98,7 +98,16 @@ class HostBrowser end cpu_info << cpu + when "NIC" + nic = get_nic_info + nic_info = result['NICINFO'] + if(nic_info == nil) + nic_info = Array.new + result['NICINFO'] = nic_info + end + + nic_info << nic else raise Exception.new("ERRINFO! Excepted key=value : #{info}\n") unless info =~ /[\w]+[\s]*=[\w]/ @@ -144,6 +153,35 @@ class HostBrowser return result end + # Extracts NIC details from the managed node. + # + def get_nic_info + puts "Begin receiving NIC details" + + result = Hash.new + + @session.write("NICINFO?\n") + + loop do + info = @session.readline.chomp + + break if info == "ENDNIC" + + raise Exception.new("ERRINFO! Excepted key=value : #{info}\n") unless info =~ /[\w]+[\s]*=[\w]/ + + key, value = info.split("=") + + puts "#{@log_prefix} ::Received - #{key}:#{value}" unless defined?(TESTING) + result[key] = value + + @session.write("ACK #{key}\n") + end + + @session.write("ACK NIC\n"); + + return result + end + # Writes the supplied host information to the database. # def write_host_info(host_info) @@ -151,8 +189,10 @@ class HostBrowser ensure_present(host_info,'ARCH') ensure_present(host_info,'MEMSIZE') ensure_present(host_info,'CPUINFO') + ensure_present(host_info,'NICINFO') cpu_info = host_info['CPUINFO'] + nic_info = host_info['NICINFO'] cpu_info.each do |cpu| ensure_present(cpu,'CPUNUM') @@ -216,7 +256,45 @@ class HostBrowser host.cpus << detail end - host.save! + # Update the NIC details for this host: + # -if the NIC exists, then update the IP address + # -if the NIC does not exist, create it + # -any nic not in this list is deleted + + puts "Updating NIC records for the node" + nics = Array.new + + host.nics.collect do |nic| + found = false + + nic_info.collect do |detail| + # if we have a match, then update the database and remove + # the received data to avoid creating a dupe later + if detail['MAC'] == nic.mac + nic_info.delete(detail) + end + end + + # if the record wasn't found, then remove it from the database + unless found + host.nics.delete(nic) + nic.destroy + end + end + + # iterate over any nics left and create new records for them. + + nic_info.collect do |nic| + puts "Creating a new nic..." + detail = Nic.new( + 'mac' => nic['MAC'], + 'bandwidth' => nic['BANDWIDTH'], + 'usage_type' => 1) + + host.nics << detail + end + + host.save! return host end diff --git a/wui/src/host-browser/test-host-browser-identify.rb b/wui/src/host-browser/test-host-browser-identify.rb index 4197e19..7e672ce 100755 --- a/wui/src/host-browser/test-host-browser-identify.rb +++ b/wui/src/host-browser/test-host-browser-identify.rb @@ -38,7 +38,7 @@ class TestHostBrowser < Test::Unit::TestCase @host_info = {} @host_info['UUID'] = 'node1' @host_info['IPADDR'] = '192.168.2.2' - @host_info['HOSTNAME'] = 'node1.ovirt.redhat.com' + @host_info['HOSTNAME'] = 'prod.corp.com' @host_info['ARCH'] = 'x86_64' @host_info['MEMSIZE'] = '16384' @host_info['DISABLED'] = '0' @@ -75,6 +75,15 @@ class TestHostBrowser < Test::Unit::TestCase mce cx8 apic mtrr pge mca cmov pat pse36 clflush dts acpi mmx \ fxsr sse sse2 ss ht tm pbe nx lm constant_tsc arch_perfmon pebs \ bts pni monitor ds_cpl vmx est tm2 ssse3 cx16 xtpr lahf_lm' + + @host_info['NICINFO'] = Array.new + @host_info['NICINFO'][0] = {} + @host_info['NICINFO'][0]['MAC'] = '00:11:22:33:44:55' + @host_info['NICINFO'][0]['BANDWIDTH'] = '100' + + @host_info['NICINFO'][1] = {} + @host_info['NICINFO'][1]['MAC'] = '00:77:11:77:19:65' + @host_info['NICINFO'][1]['BANDWIDTH'] = '100' end # Ensures that the server is satisfied if the remote system is @@ -184,6 +193,15 @@ class TestHostBrowser < Test::Unit::TestCase assert_raise(Exception) { @browser.write_host_info(@host_info) } end + # Ensures that, if no NIC info was available, the server raises an + # exception. + # + def test_write_host_info_with_missing_nicinfo + @host_info['NICINFO'] = nil + + assert_raise(Exception) { @browser.write_host_info(@host_info) } + end + # Ensures the browser can properly parse the CPU details. # def test_parse_cpu_info @@ -206,7 +224,7 @@ class TestHostBrowser < Test::Unit::TestCase # Ensures the browser can properly parse the CPU details of two CPUs. # - def test_parse_cpu_info + def test_parse_cpu_info_with_two_entries @session.should_receive(:write).with("INFO?\n").once().returns { |request| request.length } # CPU 0 @@ -242,4 +260,24 @@ class TestHostBrowser < Test::Unit::TestCase assert_not_nil info['CPUINFO'][1]['key4'] end + # Ensures the browser can properly parse the details for a NIC. + # + def test_parse_nic_info + @session.should_receive(:write).with("INFO?\n").once().returns { |request| request.length } + @session.should_receive(:readline).once().returns { "NIC\n" } + @session.should_receive(:write).with("NICINFO?\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 { "ENDNIC\n" } + @session.should_receive(:write).with("ACK NIC\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 four keys" + assert info.include?("NICINFO") + end + end -- 1.5.5.1
Darryl L. Pierce
2008-Jul-21 13:29 UTC
[Ovirt-devel] [PATCH] Refactored the ovirt-identify codebase, and added NIC info during the identify phase.
Signed-off-by: Darryl L. Pierce <dpierce at redhat.com> --- ovirt-managed-node/ovirt-managed-node.spec | 2 + ovirt-managed-node/src/Makefile | 6 +- ovirt-managed-node/src/comm.c | 87 +++ ovirt-managed-node/src/debug.c | 48 ++ ovirt-managed-node/src/gather.c | 284 +++++++++ ovirt-managed-node/src/hal_support.c | 65 ++ ovirt-managed-node/src/main.c | 216 +++++++ ovirt-managed-node/src/ovirt-identify-node.c | 653 -------------------- ovirt-managed-node/src/ovirt-identify-node.h | 119 +++- ovirt-managed-node/src/protocol.c | 276 +++++++++ ovirt-managed-node/src/scripts/ovirt-post | 8 +- wui/src/host-browser/host-browser.rb | 84 +++- wui/src/host-browser/test-host-browser-identify.rb | 42 ++- 13 files changed, 1200 insertions(+), 690 deletions(-) create mode 100644 ovirt-managed-node/src/comm.c create mode 100644 ovirt-managed-node/src/debug.c create mode 100644 ovirt-managed-node/src/gather.c create mode 100644 ovirt-managed-node/src/hal_support.c create mode 100644 ovirt-managed-node/src/main.c delete mode 100644 ovirt-managed-node/src/ovirt-identify-node.c create mode 100644 ovirt-managed-node/src/protocol.c diff --git a/ovirt-managed-node/ovirt-managed-node.spec b/ovirt-managed-node/ovirt-managed-node.spec index 4e14b08..0cd3aa8 100644 --- a/ovirt-managed-node/ovirt-managed-node.spec +++ b/ovirt-managed-node/ovirt-managed-node.spec @@ -12,7 +12,9 @@ URL: http://www.ovirt.org/ Requires(post): /sbin/chkconfig Requires(preun): /sbin/chkconfig BuildRequires: libvirt-devel +BuildRequires: dbus-devel hal-devel Requires: libvirt +Requires: hal ExclusiveArch: %{ix86} x86_64 %define app_root %{_datadir}/%{name} diff --git a/ovirt-managed-node/src/Makefile b/ovirt-managed-node/src/Makefile index 1991e46..d5e4093 100644 --- a/ovirt-managed-node/src/Makefile +++ b/ovirt-managed-node/src/Makefile @@ -17,9 +17,9 @@ # also available at http://www.gnu.org/copyleft/gpl.html. CC=gcc -CFLAGS=-Wall -c -g -LFLAGS=-lvirt -OBJECTS=ovirt-identify-node.o +CFLAGS=-Wall -c -g $(shell pkg-config --cflags dbus-1) +LFLAGS=-lhal -lvirt +OBJECTS=comm.o gather.o hal_support.o main.o protocol.o TARGET=ovirt-identify-node SOURCES=\ ovirt-identify-node.c diff --git a/ovirt-managed-node/src/comm.c b/ovirt-managed-node/src/comm.c new file mode 100644 index 0000000..a63a58d --- /dev/null +++ b/ovirt-managed-node/src/comm.c @@ -0,0 +1,87 @@ +/* comm.c -- Contains communications routines. + * + * 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 "ovirt-identify-node.h" + +ssize_t saferead(int fd, char *buf, size_t count) +{ + ssize_t bytes,offset; + int len_left; + int done = 0; + + DEBUG("Begin saferead(%d, %p, %d)\n", fd, buf, count); + + offset = 0; + len_left = count; + + + while(!done) + { + DEBUG("Before read(%d,%p,%d)\n",fd,buf+offset,len_left); + + bytes = read(fd, buf+offset, len_left); + + DEBUG("After read: bytes=%d\n", bytes); + + if(bytes == 0) + { + done = 1; + } + else if(bytes > 0) + { + offset += bytes; + len_left -= bytes; + done = 1; + } + else if(errno == EINTR) + { + continue; + } + else + { + done = 1; + } + + DEBUG("End of decision loop: offset=%d, len_left=%dl, done=%d\n",offset, len_left, done); + } + + return offset; +} + +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; +} + diff --git a/ovirt-managed-node/src/debug.c b/ovirt-managed-node/src/debug.c new file mode 100644 index 0000000..807d88b --- /dev/null +++ b/ovirt-managed-node/src/debug.c @@ -0,0 +1,48 @@ +/* debug.c -- Debugging methods. + * + * 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 "ovirt-identify-node.h" + +void debug_cpu_info(void) +{ + fprintf(stdout,"Node Info:\n"); + fprintf(stdout," UUID: %s\n", uuid); + fprintf(stdout," Arch: %s\n", arch); + fprintf(stdout," Memory: %s\n", memsize); + + t_cpu_info* current = cpu_info; + while(current != NULL) + { + fprintf(stdout,"\n"); + fprintf(stdout," CPU Number: %s\n", current->cpu_num); + fprintf(stdout," Core Number: %s\n", current->core_num); + fprintf(stdout,"Number of Cores: %s\n", current->number_of_cores); + fprintf(stdout," Vendor: %s\n", current->vendor); + fprintf(stdout," Model: %s\n", current->model); + fprintf(stdout," Family: %s\n", current->family); + fprintf(stdout," CPUID Level: %s\n", current->cpuid_level); + fprintf(stdout," CPU Speed: %s\n", current->speed); + fprintf(stdout," Cache Size: %s\n", current->cache); + fprintf(stdout," CPU Flags: %s\n", current->flags); + + current = current->next; + } +} diff --git a/ovirt-managed-node/src/gather.c b/ovirt-managed-node/src/gather.c new file mode 100644 index 0000000..c7f6278 --- /dev/null +++ b/ovirt-managed-node/src/gather.c @@ -0,0 +1,284 @@ +/* gather.c -- Contains methods for collecting data about the system. + * + * 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 "ovirt-identify-node.h" + +int init_gather(void) +{ + int result = 1; + + hal_ctx = get_hal_ctx(); + + if(hal_ctx != NULL) + { + result = 0; + } + + return result; +} + +int get_uuid(void) +{ + const char* udi = "/org/freedesktop/Hal/devices/computer"; + const char* key = "system.hardware.uuid"; + + VERBOSE("Getting system UUID.\n"); + + int result = 1; + int type; + + type = libhal_device_get_property_type(hal_ctx,udi,key,&dbus_error); + + if(type == LIBHAL_PROPERTY_TYPE_STRING) + { + char* value; + + DEBUG("%s/%s=%d\n",udi,key,type); + + value = libhal_device_get_property_string(hal_ctx,udi,key,&dbus_error); + snprintf(uuid,BUFFER_LENGTH,"%s",value); + + DEBUG("UUID=%s\n",uuid); + + result = 0; + } + + return result; +} + +/* Creates a new instance of type t_cpu_info and places it into the + * linked list of CPUs. + */ +t_cpu_info* create_cpu_info(void) +{ + t_cpu_info* result = calloc(1,sizeof(t_cpu_info)); + bzero(result,sizeof(t_cpu_info)); + + strcpy(result->core_num,"0"); + strcpy(result->number_of_cores,"1"); + + return result; +} + +int get_cpu_info(void) +{ + int result = 1; + FILE* inputfd; + t_cpu_info* current = NULL; + + /* in order to support Xen, this data will need to be gathered + * from libvirt rather than directly from cpuinfo + */ + if(( inputfd = fopen("/proc/cpuinfo","rb")) != NULL) + { + VERBOSE("Parsing CPU information\n"); + do + { + char buffer[255]; + char label[BUFFER_LENGTH]; + char value[BUFFER_LENGTH]; + + fgets(buffer, 255, inputfd); + if(strlen(buffer) > 0) buffer[strlen(buffer) - 1] = '\0'; + + get_label_and_value(buffer, + label,BUFFER_LENGTH, + value,BUFFER_LENGTH); + + DEBUG("label=\"%s\", value=\"%s\"\n", label, value); + + if(strlen(label)) + { + if(!strcmp(label,"processor")) + { + VERBOSE("Starting new CPU\n"); + + t_cpu_info* last = current; + + current = create_cpu_info(); + if(last != NULL) + { + last->next = current; + } + else + { + cpu_info = current; + } + + COPY_VALUE_TO_BUFFER(value,current->cpu_num,BUFFER_LENGTH); + } + else + /* core id and number of cores is not correct on + * Xen machines + */ + if(!strcmp(label,"core id")) + { + COPY_VALUE_TO_BUFFER(value,current->core_num,BUFFER_LENGTH); + } + else + if(!strcmp(label,"cpu cores")) + { + COPY_VALUE_TO_BUFFER(value,current->number_of_cores,BUFFER_LENGTH); + } + else + if(!strcmp(label,"vendor_id")) + { + COPY_VALUE_TO_BUFFER(value,current->vendor,BUFFER_LENGTH); + } + else + if(!strcmp(label,"model")) + { + COPY_VALUE_TO_BUFFER(value,current->model,BUFFER_LENGTH); + } + else + if(!strcmp(label,"cpu family")) + { + COPY_VALUE_TO_BUFFER(value,current->family,BUFFER_LENGTH); + } + else + if(!strcmp(label,"cpuid level")) + { + COPY_VALUE_TO_BUFFER(value,current->cpuid_level,BUFFER_LENGTH); + } + else + if(!strcmp(label,"cpu MHz")) + { + COPY_VALUE_TO_BUFFER(value,current->speed,BUFFER_LENGTH); + } + else + if(!strcmp(label,"cache size")) + { + COPY_VALUE_TO_BUFFER(value,current->cache,BUFFER_LENGTH); + } + else + if(!strcmp(label,"flags")) + { + COPY_VALUE_TO_BUFFER(value,current->flags,BUFFER_LENGTH); + } + } + + } while(!feof(inputfd)); + + fclose(inputfd); + + result = 0; + } + else + { + VERBOSE("Unable to open /proc/cpuinfo\n"); + } + + return result; +} + +/* Creates a new instance of type t_nic_info. + */ +t_nic_info* create_nic_info(void) +{ + t_nic_info* result = calloc(1,sizeof(t_nic_info)); + bzero(result,sizeof(t_nic_info)); + + return result; +} + +/* Determines the speed of the network interface. + */ +void get_nic_data(char* nic,t_nic_info* nic_info) +{ + char* interface; + struct ifreq ifr; + int sockfd; + struct ethtool_cmd ecmd; + + interface = libhal_device_get_property_string(hal_ctx,nic,"net.interface", + &dbus_error); + bzero(&ifr,sizeof(struct ifreq)); + + sockfd = socket(AF_INET, SOCK_DGRAM, 0); + if(sockfd >= 0) + { + int bandwidth; + + ifr.ifr_addr.sa_family = AF_INET; + strncpy(ifr.ifr_name,interface,IFNAMSIZ-1); + + ioctl(sockfd, SIOCETHTOOL, &ifr); + close(sockfd); + + bandwidth = 10; + if (ecmd.supported & SUPPORTED_10000baseT_Full) + bandwidth = 10000; + else if (ecmd.supported & SUPPORTED_2500baseX_Full) + bandwidth = 2500; + else if (ecmd.supported & (SUPPORTED_1000baseT_Half|SUPPORTED_1000baseT_Full)) + bandwidth = 1000; + else if (ecmd.supported & (SUPPORTED_100baseT_Half|SUPPORTED_100baseT_Full)) + bandwidth = 100; + else if (ecmd.supported & (SUPPORTED_10baseT_Half|SUPPORTED_10baseT_Full)) + bandwidth = 10; + + snprintf(nic_info->bandwidth,BUFFER_LENGTH,"%d",bandwidth); + } +} + +int get_nic_info(void) +{ + int result = 0; + t_nic_info* current = NULL; + t_nic_info* last = NULL; + + char** nics; + int num_results; + int index; + + nics = libhal_find_device_by_capability(hal_ctx,"net", + &num_results,&dbus_error); + + DEBUG("Found %d NICs\n", num_results); + + for(index = 0;index < num_results;index++) + { + char* nic = nics[index]; + + VERBOSE("Starting new NIC.\n"); + + if(current != NULL) + { + last = current; + current = create_nic_info(); + last->next = current; + } + else + { + nic_info = current = create_nic_info(); + } + + snprintf(current->mac_address,BUFFER_LENGTH,"%s", + libhal_device_get_property_string(hal_ctx,nic,"net.address", + &dbus_error)); + get_nic_data(nic,current); + + DEBUG("NIC details: MAC:%s, speed:%s, IP:%s\n", + nic_info->mac_address, nic_info->bandwidth,nic_info->ip_address); + } + + return result; +} diff --git a/ovirt-managed-node/src/hal_support.c b/ovirt-managed-node/src/hal_support.c new file mode 100644 index 0000000..58ee29b --- /dev/null +++ b/ovirt-managed-node/src/hal_support.c @@ -0,0 +1,65 @@ +/* hal_support.c -- Interfaces with the HAL libraries. + * + * 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 "ovirt-identify-node.h" + +DBusConnection* dbus_connection; +DBusError dbus_error; + +LibHalContext* get_hal_ctx(void) +{ + LibHalContext* result = NULL; + LibHalContext* ctx; + + ctx = libhal_ctx_new(); + if(ctx != NULL) + { + dbus_error_init(&dbus_error); + dbus_connection = dbus_bus_get(DBUS_BUS_SYSTEM,&dbus_error); + + if(!dbus_error_is_set(&dbus_error)) + { + libhal_ctx_set_dbus_connection(ctx,dbus_connection); + + if(libhal_ctx_init(ctx,&dbus_error)) + { + result = ctx; + } + else + { + fprintf(stderr,"Failed to initial libhal context: %s : %s\n", + dbus_error.name, dbus_error.message); + } + } + else + { + fprintf(stderr,"Unable to connect to system bus: %s : %s\n", + dbus_error.name, dbus_error.message); + dbus_error_free(&dbus_error); + } + } + else + { + fprintf(stderr,"Unable to initialize HAL context.\n"); + } + + return result; +} diff --git a/ovirt-managed-node/src/main.c b/ovirt-managed-node/src/main.c new file mode 100644 index 0000000..29e08a5 --- /dev/null +++ b/ovirt-managed-node/src/main.c @@ -0,0 +1,216 @@ +/* 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 "ovirt-identify-node.h" + +int debug = 0; +int verbose = 0; +int testing = 0; + +char arch[BUFFER_LENGTH]; +char uuid[BUFFER_LENGTH]; +char memsize[BUFFER_LENGTH]; +char numcpus[BUFFER_LENGTH]; +char cpuspeed[BUFFER_LENGTH]; +char *hostname; +int hostport = -1; +int socketfd; +t_cpu_info* cpu_info; +t_nic_info* nic_info; + +LibHalContext* hal_ctx; + +int main(int argc,char** argv) +{ + int result = 1; + virConnectPtr connection; + virNodeInfo info; + + fprintf(stdout,"Sending managed node details to server.\n"); + + if(!config(argc,argv)) + { + VERBOSE("Connecting to libvirt.\n"); + + connection = virConnectOpenReadOnly(testing ? "test:///default" : NULL); + + DEBUG("connection=%p\n",connection); + + if(connection) + { + VERBOSE("Retrieving node information.\n"); + if(!virNodeGetInfo(connection,&info)) + { + snprintf(arch, BUFFER_LENGTH, "%s", info.model); + snprintf(memsize, BUFFER_LENGTH, "%ld", info.memory); + + cpu_info = NULL; + nic_info = NULL; + + if(!init_gather() && !get_uuid() && !get_cpu_info() && !get_nic_info()) + { + if(!start_conversation() && !send_details() && !end_conversation()) + { + fprintf(stdout,"Finished!\n"); + result = 0; + } + } + else + { + VERBOSE("Failed to get CPU info.\n"); + } + } + else + { + VERBOSE("Failed to get node info.\n"); + } + } + else + { + VERBOSE("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) + { + DEBUG("Processing argument: %c (optarg:%s)\n",option,optarg); + + switch(option) + { + case 's': 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"); +} + +void get_label_and_value(char* text, + char* label, size_t label_length, + char* value, size_t value_length) +{ + int offset = 0; + int which = 0; /* 0 = label, 1 = value */ + char* current = text; + + /* iterate through the text supplied and find where the + * label ends with a colon, then copy that into the supplied + * label buffer and trim any trailing spaces + */ + + while(current != NULL && *current != '\0') + { + /* if we're on the separator, then switch modes and reset + * the offset indicator, otherwise just process the character + */ + if(which == 0 && *current == ':') + { + which = 1; + offset = 0; + } + else + { + char* buffer = (which == 0 ? label : value); + int length = (which == 0 ? label_length : value_length); + + /* only copy if we're past the first character and it's not + * a space + */ + if((offset > 0 || (*current != 9 && *current != ' ')) && offset < (length - 1)) + { + buffer[offset++] = *current; + buffer[offset] = 0; + } + } + + current++; + } + + /* now trim all trailing spaces from the values */ + while(label[strlen(label) - 1 ] == 9) + label[strlen(label) - 1] = 0; + while(value[strlen(value) - 1] == 9) + value[strlen(value) - 1] = 0; +} + +int get_text(const char *const expected) +{ + int result = 1; + int received; + char buffer[BUFFER_LENGTH]; + bzero(buffer,BUFFER_LENGTH); + + VERBOSE( "Looking to receive %s\n", expected); + + received = saferead(socketfd, buffer, BUFFER_LENGTH); + + buffer[received - 1] = 0; + + VERBOSE("Received \"%s\": size=%d (trimmed ending carriage return)\n", buffer, received); + + result = strcmp(expected,buffer); + + return result; +} diff --git a/ovirt-managed-node/src/ovirt-identify-node.c b/ovirt-managed-node/src/ovirt-identify-node.c deleted file mode 100644 index f114d81..0000000 --- a/ovirt-managed-node/src/ovirt-identify-node.c +++ /dev/null @@ -1,653 +0,0 @@ -/* 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> - -#include "ovirt-identify-node.h" - -int main(int argc,char** argv) -{ - int result = 1; - virConnectPtr connection; - virNodeInfo info; - - fprintf(stdout,"Sending managed node details to server.\n"); - - if(!config(argc,argv)) - { - if(verbose) fprintf(stdout,"Connecting to libvirt.\n"); - - connection = virConnectOpenReadOnly(testing ? "test:///default" : NULL); - - if(debug) fprintf(stderr,"connection=%p\n",connection); - - if(connection) - { - if(verbose) fprintf(stdout,"Getting hostname: %s\n", uuid); - if(!strlen(uuid)) gethostname(uuid,sizeof uuid); - - if(verbose) fprintf(stdout,"Retrieving node information.\n"); - if(!virNodeGetInfo(connection,&info)) - { - snprintf(arch, BUFFER_LENGTH, "%s", info.model); - snprintf(memsize, BUFFER_LENGTH, "%ld", info.memory); - - cpu_info = NULL; - - if(!get_cpu_info()) - { - if(verbose) fprintf(stdout, "Getting CPU info.\n"); - - 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); - - t_cpu_info* current = cpu_info; - while(current != NULL) - { - fprintf(stdout,"\n"); - fprintf(stdout," CPU Number: %s\n", current->cpu_num); - fprintf(stdout," Core Number: %s\n", current->core_num); - fprintf(stdout,"Number of Cores: %s\n", current->number_of_cores); - fprintf(stdout," Vendor: %s\n", current->vendor); - fprintf(stdout," Model: %s\n", current->model); - fprintf(stdout," Family: %s\n", current->family); - fprintf(stdout," CPUID Level: %s\n", current->cpuid_level); - fprintf(stdout," CPU Speed: %s\n", current->speed); - fprintf(stdout," Cache Size: %s\n", current->cache); - fprintf(stdout," CPU Flags: %s\n", current->flags); - - current = current->next; - } - } - - if(verbose) fprintf(stdout, "Retrieved node information.\n"); - - if(!start_conversation() && !send_details() && !end_conversation()) - { - fprintf(stdout,"Finished!\n"); - result = 0; - } - } - else - { - if(verbose) fprintf(stderr,"Failed to get CPU info.\n"); - } - } - else - { - if(verbose) fprintf(stderr,"Failed to get node info.\n"); - } - } - else - { - if(verbose) 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:u:dvth")) != -1) - { - if(debug) fprintf(stdout,"Processing argument: %c (optarg:%s)\n",option,optarg); - - switch(option) - { - case 's': hostname = optarg; break; - case 'p': hostport = atoi(optarg); break; - case 'u': snprintf(uuid,VIR_UUID_BUFLEN,"%s",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"); - - if (!get_text("HELLO?")) - { - if(verbose) fprintf(stdout,"Checking for handshake.\n"); - - if(!send_text("HELLO!")) - { - if(verbose) fprintf(stdout,"Handshake received. Starting conversation.\n"); - - if(!get_text("MODE?")) - { - if(verbose) fprintf(stdout,"Shifting to IDENTIFY mode.\n"); - - if(!send_text("IDENTIFY")) result = 0; - } - else - { - if(verbose) fprintf(stderr,"Was not asked for a mode.\n"); - } - } - } - else - { - if(verbose) fprintf(stderr,"Did not receive a proper handshake.\n"); - } - } - - else - { - if(verbose) 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]; - int result = 1; - char expected[BUFFER_LENGTH]; - - snprintf(buffer,BUFFER_LENGTH,"%s=%s", label, value); - - if(!send_text(buffer)) - { - snprintf(expected, BUFFER_LENGTH, "ACK %s", label); - - if(verbose) fprintf(stdout,"Expecting \"%s\"\n", expected); - - result = get_text(expected); - } - - return result; -} - -int send_details(void) -{ - int result = 1; - - if(verbose) fprintf(stdout,"Sending node details.\n"); - - if (!get_text("INFO?")) - { - if((!send_value("ARCH", arch)) && - (!send_value("UUID", uuid)) && - (!send_value("MEMSIZE", memsize)) && - (!send_cpu_details())) - { - if(!send_text("ENDINFO")) result = 0; - } - } - else - { - if(verbose) fprintf(stdout,"Was not interrogated for hardware info.\n"); - } - - return result; -} - -int send_cpu_details(void) -{ - int result = 1; - t_cpu_info* current = cpu_info; - - while(current != NULL) - { - send_text("CPU"); - - if(!(get_text("CPUINFO?")) && - (!send_value("CPUNUM",current->cpu_num)) && - (!send_value("CORENUM",current->core_num)) && - (!send_value("NUMCORES",current->number_of_cores)) && - (!send_value("VENDOR",current->vendor)) && - (!send_value("MODEL",current->model)) && - (!send_value("FAMILY",current->family)) && - (!send_value("CPUIDLVL",current->cpuid_level)) && - (!send_value("SPEED",current->speed)) && - (!send_value("CACHE", current->cache)) && - (!send_value("FLAGS", current->flags))) - { - send_text("ENDCPU"); - result = get_text("ACK CPU"); - } - - current = current->next; - } - - - return result; -} - -int end_conversation(void) -{ - int result = 0; - - if(debug || verbose) fprintf(stdout,"Ending conversation.\n"); - - send_text("ENDINFO"); - - close(socketfd); - - return result; -} - -void get_label_and_value(char* text, - char* label, size_t label_length, - char* value, size_t value_length) -{ - int offset = 0; - int which = 0; /* 0 = label, 1 = value */ - char* current = text; - - /* iterate through the text supplied and find where the - * label ends with a colon, then copy that into the supplied - * label buffer and trim any trailing spaces - */ - - while(current != NULL && *current != '\0') - { - /* if we're on the separator, then switch modes and reset - * the offset indicator, otherwise just process the character - */ - if(which == 0 && *current == ':') - { - which = 1; - offset = 0; - } - else - { - char* buffer = (which == 0 ? label : value); - int length = (which == 0 ? label_length : value_length); - - /* only copy if we're past the first character and it's not - * a space - */ - if((offset > 0 || (*current != 9 && *current != ' ')) && offset < (length - 1)) - { - buffer[offset++] = *current; - buffer[offset] = 0; - } - } - - current++; - } - - /* now trim all trailing spaces from the values */ - while(label[strlen(label) - 1 ] == 9) - label[strlen(label) - 1] = 0; - while(value[strlen(value) - 1] == 9) - value[strlen(value) - 1] = 0; -} - -int get_cpu_info(void) -{ - int result = 1; - FILE* inputfd; - t_cpu_info* current = NULL; - - if(( inputfd = fopen("/proc/cpuinfo","rb")) != NULL) - { - if(verbose) fprintf(stdout,"Parsing CPU information\n"); - do - { - char buffer[255]; - char label[BUFFER_LENGTH]; - char value[BUFFER_LENGTH]; - - fgets(buffer, 255, inputfd); - if(strlen(buffer) > 0) buffer[strlen(buffer) - 1] = '\0'; - - get_label_and_value(buffer, - label,BUFFER_LENGTH, - value,BUFFER_LENGTH); - - if(debug) - fprintf(stdout,"label=\"%s\", value=\"%s\"\n", label, value); - - if(strlen(label)) - { - if(!strcmp(label,"processor")) - { - if(debug || verbose) - fprintf(stdout,"Starting new CPU\n"); - - t_cpu_info* last = current; - - current = create_cpu_info(); - if(last != NULL) - { - last->next = current; - } - else - { - cpu_info = current; - } - - COPY_VALUE_TO_BUFFER(value,current->cpu_num,BUFFER_LENGTH); - } - else - if(!strcmp(label,"core id")) - { - COPY_VALUE_TO_BUFFER(value,current->core_num,BUFFER_LENGTH); - } - else - if(!strcmp(label,"cpu cores")) - { - COPY_VALUE_TO_BUFFER(value,current->number_of_cores,BUFFER_LENGTH); - } - else - if(!strcmp(label,"vendor_id")) - { - COPY_VALUE_TO_BUFFER(value,current->vendor,BUFFER_LENGTH); - } - else - if(!strcmp(label,"model")) - { - COPY_VALUE_TO_BUFFER(value,current->model,BUFFER_LENGTH); - } - else - if(!strcmp(label,"cpu family")) - { - COPY_VALUE_TO_BUFFER(value,current->family,BUFFER_LENGTH); - } - else - if(!strcmp(label,"cpuid level")) - { - COPY_VALUE_TO_BUFFER(value,current->cpuid_level,BUFFER_LENGTH); - } - else - if(!strcmp(label,"cpu MHz")) - { - COPY_VALUE_TO_BUFFER(value,current->speed,BUFFER_LENGTH); - } - else - if(!strcmp(label,"cache size")) - { - COPY_VALUE_TO_BUFFER(value,current->cache,BUFFER_LENGTH); - } - else - if(!strcmp(label,"flags")) - { - COPY_VALUE_TO_BUFFER(value,current->flags,BUFFER_LENGTH); - } - } - - } while(!feof(inputfd)); - - fclose(inputfd); - - result = 0; - } - else - { - if(verbose) fprintf(stderr,"Unable to open /proc/cpuinfo\n"); - } - - return result; -} - -t_cpu_info* create_cpu_info(void) -{ - t_cpu_info* result = calloc(1,sizeof(t_cpu_info)); - bzero(result,sizeof(t_cpu_info)); - - strcpy(result->core_num,"0"); - strcpy(result->number_of_cores,"1"); - - - 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; - int sent; - - if(verbose) fprintf(stdout,"Sending: \"%s\"\n", text); - - sent = safewrite(socketfd, text, strlen(text)); - sent += safewrite(socketfd, "\n", 1); - - if(sent >= 0) - { - if(debug) fprintf(stdout,"Sent %d bytes total.\n", sent); - - result = 0; - } - - return result; -} - -int saferead(int fd, char *buf, size_t count) -{ - ssize_t bytes,offset; - int len_left; - int done = 0; - - if(debug) fprintf(stdout,"Begin saferead(%d, %p, %ld)\n", fd, buf, count); - - offset = 0; - len_left = count; - - - while(!done) - { - if(debug) fprintf(stdout,"Before read(%ld,%p,%ld)\n",fd,buf+offset,len_left); - - bytes = read(fd, buf+offset, len_left); - - if(debug) fprintf(stdout,"After read: bytes=%ld\n", bytes); - - if(bytes == 0) - { - done = 1; - } - else if(bytes > 0) - { - offset += bytes; - len_left -= bytes; - done = 1; - } - else if(errno == EINTR) - { - continue; - } - else - { - done = 1; - } - - if(debug) fprintf(stdout,"End of decision loop: offset=%ld, len_left=%dl, done=%d\n",offset, len_left, done); - } - - return offset; -} - -int get_text(const char *const expected) -{ - int result = 1; - int received; - char buffer[BUFFER_LENGTH]; - bzero(buffer,BUFFER_LENGTH); - - if(verbose) fprintf(stdout, "Looking to receive %s\n", expected); - - received = saferead(socketfd, buffer, BUFFER_LENGTH); - - buffer[received - 1] = 0; - - if(verbose) fprintf(stdout,"Received \"%s\": size=%d (trimmed ending carriage return)\n", buffer, received); - - result = strcmp(expected,buffer); - - return result; -} - -int create_connection(void) -{ - int result = 1; - struct addrinfo hints; - struct addrinfo* results; - char port[6]; - struct addrinfo* rptr; - - if(verbose) fprintf(stdout,"Creating the socket connection.\n"); - - 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(verbose) fprintf(stdout,"Searching for host candidates.\n"); - - snprintf(port, 6, "%d", hostport); - - if(!getaddrinfo(hostname, port, &hints, &results)) - { - if(verbose) fprintf(stdout,"Got address information. Searching for a proper entry.\n"); - - 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(verbose) fprintf(stdout, "Invalid connection.\n"); - close(socketfd); - } - - if(rptr == NULL) - { - if(verbose) fprintf(stdout,"Unable to connect to server %s:%d\n", hostname, hostport); - } - else - { - // success - result = 0; - } - - freeaddrinfo(results); - } - else - { - if(verbose) 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/src/ovirt-identify-node.h b/ovirt-managed-node/src/ovirt-identify-node.h index 1cb1526..fd1bc0a 100644 --- a/ovirt-managed-node/src/ovirt-identify-node.h +++ b/ovirt-managed-node/src/ovirt-identify-node.h @@ -1,6 +1,49 @@ +/* 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. + */ + #ifndef __OVIRT_IDENTIFY_NODE_H #define __OVIRT_IDENTIFY_NODE_H +#include <errno.h> +#include <getopt.h> +#include <netdb.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <arpa/inet.h> + +#include <hal/libhal.h> + +#include <libvirt/libvirt.h> + +#include <linux/ethtool.h> +#include <linux/sockios.h> +#include <linux/if.h> + +#include <netinet/in.h> + +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/socket.h> + #define BUFFER_LENGTH 128 #define CPU_FLAGS_BUFFER_LENGTH 256 @@ -18,39 +61,71 @@ typedef struct _cpu_info { struct _cpu_info* next; } t_cpu_info; -#define COPY_VALUE_TO_BUFFER(value,buffer,length) \ - snprintf(buffer,length,"%s",value) +typedef struct _nic_info { + char mac_address[BUFFER_LENGTH]; + char bandwidth[BUFFER_LENGTH]; + char ip_address[BUFFER_LENGTH]; + struct _nic_info* next; +} t_nic_info; int config(int argc,char** argv); void usage(void); -int start_conversation(void); -int send_details(void); -int send_cpu_details(void); -int end_conversation(void); - void get_label_and_value(char* text, char* label,size_t label_length, char* value,size_t value_length); -t_cpu_info* create_cpu_info(void); -int get_cpu_info(void); int send_text(char* text); int get_text(const char *const expected); + +/* comm.c */ +ssize_t saferead(int fd, char *buf, size_t count); +ssize_t safewrite(int fd, const void *buf, size_t count); + +/* debug.c */ +void debug_cpu_info(void); + +/* gather.c */ +int init_gather(void); +int get_uuid(void); +int get_cpu_info(void); +int get_nic_info(void); + +/* hal_support.c */ +LibHalContext* get_hal_ctx(void); + +/* protocol.c */ int create_connection(void); +int start_conversation(void); +int send_details(void); +int end_conversation(void); +int send_value(char* label,char* value); +int send_text(char* text); + +/* variables */ +extern int debug; +extern int verbose; +extern int testing; + +extern char arch[BUFFER_LENGTH]; +extern char uuid[BUFFER_LENGTH]; +extern char memsize[BUFFER_LENGTH]; +extern char numcpus[BUFFER_LENGTH]; +extern char cpuspeed[BUFFER_LENGTH]; +extern char *hostname; +extern int hostport; +extern int socketfd; +extern t_cpu_info* cpu_info; +extern t_nic_info* nic_info; -int debug = 0; -int verbose = 0; -int testing = 0; - -char arch[BUFFER_LENGTH]; -char uuid[VIR_UUID_BUFLEN]; -char memsize[BUFFER_LENGTH]; -char numcpus[BUFFER_LENGTH]; -char cpuspeed[BUFFER_LENGTH]; -char *hostname; -int hostport = -1; -int socketfd; -t_cpu_info* cpu_info; +extern DBusConnection* dbus_connection; +extern DBusError dbus_error; +extern LibHalContext* hal_ctx; + +/* macros */ +#define DEBUG(arg...) if(debug) fprintf(stderr, ##arg) +#define VERBOSE(arg...) if(verbose) fprintf(stdout, ##arg) +#define COPY_VALUE_TO_BUFFER(value,buffer,length) \ + snprintf(buffer,length,"%s",value) #endif diff --git a/ovirt-managed-node/src/protocol.c b/ovirt-managed-node/src/protocol.c new file mode 100644 index 0000000..111a1c1 --- /dev/null +++ b/ovirt-managed-node/src/protocol.c @@ -0,0 +1,276 @@ +/* protocol.c -- Manages the communication between the managed node and + * the oVirt 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. + */ + +#include "ovirt-identify-node.h" + +int create_connection(void) +{ + int result = 1; + struct addrinfo hints; + struct addrinfo* results; + char port[6]; + struct addrinfo* rptr; + + VERBOSE("Creating the socket connection.\n"); + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = 0; + hints.ai_protocol = 0; + + VERBOSE("Searching for host candidates.\n"); + + snprintf(port, 6, "%d", hostport); + + if(!getaddrinfo(hostname, port, &hints, &results)) + { + VERBOSE("Got address information. Searching for a proper entry.\n"); + + 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 + VERBOSE( "Invalid connection.\n"); + close(socketfd); + } + + if(rptr == NULL) + { + VERBOSE("Unable to connect to server %s:%d\n", hostname, hostport); + } + else + { + // success + result = 0; + } + + freeaddrinfo(results); + } + else + { + VERBOSE("No hosts found. Exiting...\n"); + } + + DEBUG( "create_connection: result=%d\n", result); + + return result; +} + +int start_conversation(void) +{ + int result = 1; + + VERBOSE("Starting conversation with %s:%d.\n",hostname,hostport); + + if(!create_connection()) + { + VERBOSE("Connected.\n"); + + if (!get_text("HELLO?")) + { + VERBOSE("Checking for handshake.\n"); + + if(!send_text("HELLO!")) + { + VERBOSE("Handshake received. Starting conversation.\n"); + + if(!get_text("MODE?")) + { + VERBOSE("Shifting to IDENTIFY mode.\n"); + + if(!send_text("IDENTIFY")) result = 0; + } + else + { + VERBOSE("Was not asked for a mode.\n"); + } + } + } + else + { + VERBOSE("Did not receive a proper handshake.\n"); + } + } + + else + { + VERBOSE("Did not get a connection.\n"); + } + + DEBUG("start_conversation: result=%d\n", result); + + return result; +} + +/* Transmits the CPU details to the server. + */ +int send_cpu_details(void) +{ + int result = 1; + t_cpu_info* current = cpu_info; + + while(current != NULL) + { + send_text("CPU"); + + if(!(get_text("CPUINFO?")) && + (!send_value("CPUNUM",current->cpu_num)) && + (!send_value("CORENUM",current->core_num)) && + (!send_value("NUMCORES",current->number_of_cores)) && + (!send_value("VENDOR",current->vendor)) && + (!send_value("MODEL",current->model)) && + (!send_value("FAMILY",current->family)) && + (!send_value("CPUIDLVL",current->cpuid_level)) && + (!send_value("SPEED",current->speed)) && + (!send_value("CACHE", current->cache)) && + (!send_value("FLAGS", current->flags))) + { + send_text("ENDCPU"); + result = get_text("ACK CPU"); + } + + current = current->next; + } + + + return result; +} + +/* Transmits the NIC details to the server. + */ +int send_nic_details(void) +{ + int result = 1; + t_nic_info* current = nic_info; + + while(current != NULL) + { + send_text("NIC"); + + if(!(get_text("NICINFO?")) && + (!send_value("MAC", current->mac_address)) && + (!send_value("BANDWIDTH",current->bandwidth))) + { + send_text("ENDNIC"); + result = get_text("ACK NIC"); + } + + current = current->next; + } + + return result; +} + +int send_details(void) +{ + int result = 1; + + VERBOSE("Sending node details.\n"); + + if (!get_text("INFO?")) + { + if((!send_value("ARCH", arch)) && + (!send_value("UUID", uuid)) && + (!send_value("MEMSIZE", memsize)) && + (!send_cpu_details() && !send_nic_details())) + { + if(!send_text("ENDINFO")) result = 0; + } + } + else + { + VERBOSE("Was not interrogated for hardware info.\n"); + } + + return result; +} + +int end_conversation(void) +{ + int result = 0; + + VERBOSE("Ending conversation.\n"); + + send_text("ENDINFO"); + + close(socketfd); + + return result; +} + +int send_value(char* label,char* value) +{ + char buffer[BUFFER_LENGTH]; + int result = 1; + char expected[BUFFER_LENGTH]; + + snprintf(buffer,BUFFER_LENGTH,"%s=%s", label, value); + + if(!send_text(buffer)) + { + snprintf(expected, BUFFER_LENGTH, "ACK %s", label); + + VERBOSE("Expecting \"%s\"\n", expected); + + result = get_text(expected); + } + + return result; +} + +int send_text(char* text) +{ + int result = 1; + int sent; + + VERBOSE("Sending: \"%s\"\n", text); + + sent = safewrite(socketfd, text, strlen(text)); + sent += safewrite(socketfd, "\n", 1); + + if(sent >= 0) + { + DEBUG("Sent %d bytes total.\n", sent); + + result = 0; + } + + return result; +} diff --git a/ovirt-managed-node/src/scripts/ovirt-post b/ovirt-managed-node/src/scripts/ovirt-post index f113b28..310a41c 100755 --- a/ovirt-managed-node/src/scripts/ovirt-post +++ b/ovirt-managed-node/src/scripts/ovirt-post @@ -12,14 +12,8 @@ start() { find_srv identify tcp - UUID=`hal-get-property --udi \ - /org/freedesktop/Hal/devices/computer --key system.hardware.uuid` - if [ -z $UUID ]; then - ovirt-identify-node -s $SRV_HOST -p $SRV_PORT - else - ovirt-identify-node -s $SRV_HOST -p $SRV_PORT -u $UUID - fi + ovirt-identify-node -s $SRV_HOST -p $SRV_PORT } case "$1" in diff --git a/wui/src/host-browser/host-browser.rb b/wui/src/host-browser/host-browser.rb index 624661d..d209bdb 100755 --- a/wui/src/host-browser/host-browser.rb +++ b/wui/src/host-browser/host-browser.rb @@ -87,8 +87,8 @@ class HostBrowser break if info == "ENDINFO" - # if we got the start of a CPU details marker, then process it - if info == "CPU" + case info + when "CPU" cpu = get_cpu_info cpu_info = result['CPUINFO'] @@ -98,7 +98,16 @@ class HostBrowser end cpu_info << cpu + when "NIC" + nic = get_nic_info + nic_info = result['NICINFO'] + if(nic_info == nil) + nic_info = Array.new + result['NICINFO'] = nic_info + end + + nic_info << nic else raise Exception.new("ERRINFO! Excepted key=value : #{info}\n") unless info =~ /[\w]+[\s]*=[\w]/ @@ -144,6 +153,35 @@ class HostBrowser return result end + # Extracts NIC details from the managed node. + # + def get_nic_info + puts "Begin receiving NIC details" + + result = Hash.new + + @session.write("NICINFO?\n") + + loop do + info = @session.readline.chomp + + break if info == "ENDNIC" + + raise Exception.new("ERRINFO! Excepted key=value : #{info}\n") unless info =~ /[\w]+[\s]*=[\w]/ + + key, value = info.split("=") + + puts "#{@log_prefix} ::Received - #{key}:#{value}" unless defined?(TESTING) + result[key] = value + + @session.write("ACK #{key}\n") + end + + @session.write("ACK NIC\n"); + + return result + end + # Writes the supplied host information to the database. # def write_host_info(host_info) @@ -151,8 +189,10 @@ class HostBrowser ensure_present(host_info,'ARCH') ensure_present(host_info,'MEMSIZE') ensure_present(host_info,'CPUINFO') + ensure_present(host_info,'NICINFO') cpu_info = host_info['CPUINFO'] + nic_info = host_info['NICINFO'] cpu_info.each do |cpu| ensure_present(cpu,'CPUNUM') @@ -216,7 +256,45 @@ class HostBrowser host.cpus << detail end - host.save! + # Update the NIC details for this host: + # -if the NIC exists, then update the IP address + # -if the NIC does not exist, create it + # -any nic not in this list is deleted + + puts "Updating NIC records for the node" + nics = Array.new + + host.nics.collect do |nic| + found = false + + nic_info.collect do |detail| + # if we have a match, then update the database and remove + # the received data to avoid creating a dupe later + if detail['MAC'] == nic.mac + nic_info.delete(detail) + end + end + + # if the record wasn't found, then remove it from the database + unless found + host.nics.delete(nic) + nic.destroy + end + end + + # iterate over any nics left and create new records for them. + + nic_info.collect do |nic| + puts "Creating a new nic..." + detail = Nic.new( + 'mac' => nic['MAC'], + 'bandwidth' => nic['BANDWIDTH'], + 'usage_type' => 1) + + host.nics << detail + end + + host.save! return host end diff --git a/wui/src/host-browser/test-host-browser-identify.rb b/wui/src/host-browser/test-host-browser-identify.rb index 4197e19..7e672ce 100755 --- a/wui/src/host-browser/test-host-browser-identify.rb +++ b/wui/src/host-browser/test-host-browser-identify.rb @@ -38,7 +38,7 @@ class TestHostBrowser < Test::Unit::TestCase @host_info = {} @host_info['UUID'] = 'node1' @host_info['IPADDR'] = '192.168.2.2' - @host_info['HOSTNAME'] = 'node1.ovirt.redhat.com' + @host_info['HOSTNAME'] = 'prod.corp.com' @host_info['ARCH'] = 'x86_64' @host_info['MEMSIZE'] = '16384' @host_info['DISABLED'] = '0' @@ -75,6 +75,15 @@ class TestHostBrowser < Test::Unit::TestCase mce cx8 apic mtrr pge mca cmov pat pse36 clflush dts acpi mmx \ fxsr sse sse2 ss ht tm pbe nx lm constant_tsc arch_perfmon pebs \ bts pni monitor ds_cpl vmx est tm2 ssse3 cx16 xtpr lahf_lm' + + @host_info['NICINFO'] = Array.new + @host_info['NICINFO'][0] = {} + @host_info['NICINFO'][0]['MAC'] = '00:11:22:33:44:55' + @host_info['NICINFO'][0]['BANDWIDTH'] = '100' + + @host_info['NICINFO'][1] = {} + @host_info['NICINFO'][1]['MAC'] = '00:77:11:77:19:65' + @host_info['NICINFO'][1]['BANDWIDTH'] = '100' end # Ensures that the server is satisfied if the remote system is @@ -184,6 +193,15 @@ class TestHostBrowser < Test::Unit::TestCase assert_raise(Exception) { @browser.write_host_info(@host_info) } end + # Ensures that, if no NIC info was available, the server raises an + # exception. + # + def test_write_host_info_with_missing_nicinfo + @host_info['NICINFO'] = nil + + assert_raise(Exception) { @browser.write_host_info(@host_info) } + end + # Ensures the browser can properly parse the CPU details. # def test_parse_cpu_info @@ -206,7 +224,7 @@ class TestHostBrowser < Test::Unit::TestCase # Ensures the browser can properly parse the CPU details of two CPUs. # - def test_parse_cpu_info + def test_parse_cpu_info_with_two_entries @session.should_receive(:write).with("INFO?\n").once().returns { |request| request.length } # CPU 0 @@ -242,4 +260,24 @@ class TestHostBrowser < Test::Unit::TestCase assert_not_nil info['CPUINFO'][1]['key4'] end + # Ensures the browser can properly parse the details for a NIC. + # + def test_parse_nic_info + @session.should_receive(:write).with("INFO?\n").once().returns { |request| request.length } + @session.should_receive(:readline).once().returns { "NIC\n" } + @session.should_receive(:write).with("NICINFO?\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 { "ENDNIC\n" } + @session.should_receive(:write).with("ACK NIC\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 four keys" + assert info.include?("NICINFO") + end + end -- 1.5.5.1