Darryl L. Pierce
2008-Jul-11 19:28 UTC
[Ovirt-devel] [PATCH] Adding NIC details to ovirt identify stage.
This patch includes submitting to the server the MAC address and bandwidth, as determined via HAL, for each physical NIC in the machine. The source code for the ovirt-identify-node was split up as well to make it easier to review and manage. 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 | 278 +++++++++ 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 | 9 +- wui/src/host-browser/host-browser.rb | 84 +++- wui/src/host-browser/test-host-browser-identify.rb | 42 ++- 13 files changed, 1195 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..d18eb86 --- /dev/null +++ b/ovirt-managed-node/src/gather.c @@ -0,0 +1,278 @@ +/* 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; + + 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 + 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 3bb0f6d..2624e01 100755 --- a/ovirt-managed-node/src/scripts/ovirt-post +++ b/ovirt-managed-node/src/scripts/ovirt-post @@ -14,16 +14,11 @@ start() { echo -n $"Starting ovirt-post: " 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 success + echo } 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
Daniel P. Berrange
2008-Jul-11 19:46 UTC
[Ovirt-devel] [PATCH] Adding NIC details to ovirt identify stage.
On Fri, Jul 11, 2008 at 03:28:50PM -0400, Darryl L. Pierce wrote:> This patch includes submitting to the server the MAC address and bandwidth, as > determined via HAL, for each physical NIC in the machine.> +int get_cpu_info(void) > +{ > + int result = 1; > + FILE* inputfd; > + t_cpu_info* current = NULL; > + > + if(( inputfd = fopen("/proc/cpuinfo","rb")) != NULL)This is just plain wrong if running on Xen. This is why we provide the virNodeGetInfo API in libvirt, which knows whether to ask /proc/cpuinfo, or whether to query the hypervisor for the real info> + { > + 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 > + if(!strcmp(label,"core id")) > + { > + COPY_VALUE_TO_BUFFER(value,current->core_num,BUFFER_LENGTH); > + }This data is useless/wrong on Xen.> + else > + if(!strcmp(label,"cpu cores")) > + { > + COPY_VALUE_TO_BUFFER(value,current->number_of_cores,BUFFER_LENGTH); > + }This data is useless/wrong on Xen.> + 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); > + } > + elseThis data is useless/wrong if CPU frequency scaling is active - it is active by default> + 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); > + }Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|
Darryl L. Pierce
2008-Jul-16 21:12 UTC
[Ovirt-devel] [PATCH] Adding NIC details to ovirt identify stage.
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