Darryl L. Pierce
2008-Jul-05 13:59 UTC
[Ovirt-devel] [PATCH] ovirt-identify-node now submits all extended CPU details.
From: Darryl Pierce <dpierce at redhat.com> Signed-off-by: Darryl L. Pierce <dpierce at redhat.com> --- ovirt-managed-node/src/ovirt-identify-node.c | 420 +++++++++++++++----- ovirt-managed-node/src/ovirt-identify-node.h | 56 +++ wui/src/app/models/cpu.rb | 24 ++ wui/src/app/models/host.rb | 21 +- wui/src/db/migrate/002_create_hosts.rb | 2 - wui/src/db/migrate/010_create_cpus.rb | 26 ++ wui/src/dutils/active_record_env.rb | 5 +- wui/src/host-browser/host-browser.rb | 103 +++++- wui/src/host-browser/test-host-browser-awake.rb | 94 +++++ wui/src/host-browser/test-host-browser-awaken.rb | 94 ----- wui/src/host-browser/test-host-browser-identify.rb | 345 ++++++++++------ wui/src/test/fixtures/cpus.yml | 21 + wui/src/test/fixtures/hosts.yml | 26 +- wui/src/test/unit/cpu_test.rb | 8 + 14 files changed, 875 insertions(+), 370 deletions(-) create mode 100644 ovirt-managed-node/src/ovirt-identify-node.h create mode 100644 wui/src/app/models/cpu.rb create mode 100644 wui/src/db/migrate/010_create_cpus.rb create mode 100755 wui/src/host-browser/test-host-browser-awake.rb delete mode 100755 wui/src/host-browser/test-host-browser-awaken.rb create mode 100644 wui/src/test/fixtures/cpus.yml create mode 100644 wui/src/test/unit/cpu_test.rb diff --git a/ovirt-managed-node/src/ovirt-identify-node.c b/ovirt-managed-node/src/ovirt-identify-node.c index 819f700..f114d81 100644 --- a/ovirt-managed-node/src/ovirt-identify-node.c +++ b/ovirt-managed-node/src/ovirt-identify-node.c @@ -31,31 +31,7 @@ #include <sys/types.h> #include <sys/socket.h> -int config(int argc,char** argv); -void usage(void); - -int start_conversation(void); -int send_details(void); -int end_conversation(void); - -int send_text(char* text); -int get_text(const char *const expected); -int create_connection(void); - -int debug = 1; -int verbose = 1; -int testing = 0; - -#define BUFFER_LENGTH 128 - -char arch[BUFFER_LENGTH]; -char uuid[VIR_UUID_BUFLEN]; -char memsize[BUFFER_LENGTH]; -char numcpus[BUFFER_LENGTH]; -char cpuspeed[BUFFER_LENGTH]; -char *hostname; -int hostport = -1; -int socketfd; +#include "ovirt-identify-node.h" int main(int argc,char** argv) { @@ -63,9 +39,11 @@ int main(int argc,char** argv) virConnectPtr connection; virNodeInfo info; + fprintf(stdout,"Sending managed node details to server.\n"); + if(!config(argc,argv)) { - fprintf(stdout,"Connecting to libvirt.\n"); + if(verbose) fprintf(stdout,"Connecting to libvirt.\n"); connection = virConnectOpenReadOnly(testing ? "test:///default" : NULL); @@ -73,42 +51,68 @@ int main(int argc,char** argv) if(connection) { - if(debug) fprintf(stdout,"Getting hostname: %s\n", uuid); + if(verbose) fprintf(stdout,"Getting hostname: %s\n", uuid); if(!strlen(uuid)) gethostname(uuid,sizeof uuid); - if(debug) fprintf(stdout,"Retrieving node information.\n"); + 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); - snprintf(numcpus, BUFFER_LENGTH, "%d", info.cpus); - snprintf(cpuspeed, BUFFER_LENGTH, "%d", info.mhz); - if(debug) + cpu_info = NULL; + + if(!get_cpu_info()) { - fprintf(stdout,"Node Info:\n"); - fprintf(stdout," UUID: %s\n", uuid); - fprintf(stdout," Arch: %s\n", arch); - fprintf(stdout," Memory: %s\n", memsize); - fprintf(stdout," # CPUs: %s\n", numcpus); - fprintf(stdout,"CPU Speed: %s\n", cpuspeed); + if(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; + } } - - if(debug) fprintf(stdout, "Retrieved node information.\n"); - - if(!start_conversation() && !send_details() && !end_conversation()) + else { - result = 0; + if(verbose) fprintf(stderr,"Failed to get CPU info.\n"); } } else { - if(debug) fprintf(stderr,"Failed to get node info.\n"); + if(verbose) fprintf(stderr,"Failed to get node info.\n"); } } else { - if(debug) fprintf(stderr,"Could not connect to libvirt.\n"); + if(verbose) fprintf(stderr,"Could not connect to libvirt.\n"); } } else @@ -181,35 +185,35 @@ int start_conversation(void) { if(debug || verbose) fprintf(stdout,"Connected.\n"); - if (!get_text("HELLO?\n")) + if (!get_text("HELLO?")) { - if(debug) fprintf(stdout,"Checking for handshake.\n"); + if(verbose) fprintf(stdout,"Checking for handshake.\n"); - if(!send_text("HELLO!\n")) + if(!send_text("HELLO!")) { - if(debug) fprintf(stdout,"Handshake received. Starting conversation.\n"); + if(verbose) fprintf(stdout,"Handshake received. Starting conversation.\n"); - if(!get_text("MODE?\n")) + if(!get_text("MODE?")) { - if(debug) fprintf(stdout,"Shifting to IDENTIFY mode.\n"); + if(verbose) fprintf(stdout,"Shifting to IDENTIFY mode.\n"); - if(!send_text("IDENTIFY\n")) result = 0; + if(!send_text("IDENTIFY")) result = 0; } else { - if(debug) fprintf(stderr,"Was not asked for a mode.\n"); + if(verbose) fprintf(stderr,"Was not asked for a mode.\n"); } } } else { - if(debug) fprintf(stderr,"Did not receive a proper handshake.\n"); + if(verbose) fprintf(stderr,"Did not receive a proper handshake.\n"); } } else { - if(debug) fprintf(stderr,"Did not get a connection.\n"); + if(verbose) fprintf(stderr,"Did not get a connection.\n"); } if(debug) fprintf(stdout,"start_conversation: result=%d\n", result); @@ -223,19 +227,15 @@ int send_value(char* label,char* value) int result = 1; char expected[BUFFER_LENGTH]; - snprintf(buffer,BUFFER_LENGTH,"%s=%s\n", label, value); + snprintf(buffer,BUFFER_LENGTH,"%s=%s", label, value); if(!send_text(buffer)) { - snprintf(expected, BUFFER_LENGTH, "ACK %s\n", label); + snprintf(expected, BUFFER_LENGTH, "ACK %s", label); - if(debug) fprintf(stdout,"Expecting \"%s\"\n", expected); - - if (!get_text(expected)) - { - result = 0; - } + if(verbose) fprintf(stdout,"Expecting \"%s\"\n", expected); + result = get_text(expected); } return result; @@ -245,40 +245,238 @@ int send_details(void) { int result = 1; - fprintf(stdout,"Sending node details.\n"); + if(verbose) fprintf(stdout,"Sending node details.\n"); - if (!get_text("INFO?\n")) + if (!get_text("INFO?")) { if((!send_value("ARCH", arch)) && (!send_value("UUID", uuid)) && - (!send_value("NUMCPUS", numcpus)) && - (!send_value("CPUSPEED", cpuspeed)) && - (!send_value("MEMSIZE", memsize))) + (!send_value("MEMSIZE", memsize)) && + (!send_cpu_details())) { - if(!send_text("ENDINFO\n")) result = 0; + if(!send_text("ENDINFO")) result = 0; } } else { - if(debug) fprintf(stdout,"Was not interrogated for hardware info.\n"); + 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; - fprintf(stdout,"Ending conversation.\n"); + if(debug || verbose) fprintf(stdout,"Ending conversation.\n"); - send_text("ENDINFO\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; @@ -303,9 +501,10 @@ int send_text(char* text) int result = 1; int sent; - if(debug || verbose) fprintf(stdout,"\"%s\" -> %s:%d\n", text, hostname, hostport); + if(verbose) fprintf(stdout,"Sending: \"%s\"\n", text); sent = safewrite(socketfd, text, strlen(text)); + sent += safewrite(socketfd, "\n", 1); if(sent >= 0) { @@ -317,35 +516,46 @@ int send_text(char* text) return result; } -int saferead(int fd, void *buf, size_t count) +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(len_left > 0) { - bytes = read(fd, buf+offset, len_left); - fprintf(stderr,"After read, bytes is %ld\n",bytes); - if (bytes < 0) { - if (errno == EINTR) { - continue; - } - else { - offset = -1; - break; - } - } - else if (bytes == 0) { - // reached EOF; break out of here - break; - } - - offset += bytes; - len_left -= bytes; + + 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; @@ -353,22 +563,22 @@ int saferead(int fd, void *buf, size_t count) int get_text(const char *const expected) { + int result = 1; int received; char buffer[BUFFER_LENGTH]; + bzero(buffer,BUFFER_LENGTH); - if(debug) fprintf(stdout, "Looking to receive %s\n", expected); + if(verbose) fprintf(stdout, "Looking to receive %s\n", expected); - received = saferead(socketfd, buffer, strlen(expected)); + received = saferead(socketfd, buffer, BUFFER_LENGTH); buffer[received - 1] = 0; - if(debug) fprintf(stdout,"Received \"%s\": size=%d (trimmed ending carriage return)\n", buffer, received); + if(verbose) fprintf(stdout,"Received \"%s\": size=%d (trimmed ending carriage return)\n", buffer, received); - if (strncmp(expected, buffer, strlen(expected)) != 0) { - return 0; - } + result = strcmp(expected,buffer); - return 1; + return result; } int create_connection(void) @@ -379,7 +589,7 @@ int create_connection(void) char port[6]; struct addrinfo* rptr; - if(debug) fprintf(stdout,"Creating the socket connection.\n"); + if(verbose) fprintf(stdout,"Creating the socket connection.\n"); memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = AF_UNSPEC; @@ -387,13 +597,13 @@ int create_connection(void) hints.ai_flags = 0; hints.ai_protocol = 0; - if(debug) fprintf(stdout,"Searching for host candidates.\n"); + if(verbose) fprintf(stdout,"Searching for host candidates.\n"); snprintf(port, 6, "%d", hostport); if(!getaddrinfo(hostname, port, &hints, &results)) { - if(debug) fprintf(stdout,"Got address information. Searching for a proper entry.\n"); + if(verbose) fprintf(stdout,"Got address information. Searching for a proper entry.\n"); for(rptr = results; rptr != NULL; rptr = rptr->ai_next) { @@ -416,13 +626,13 @@ int create_connection(void) } // invalid connection, so close it - if(debug) fprintf(stdout, "Invalid connection.\n"); + if(verbose) fprintf(stdout, "Invalid connection.\n"); close(socketfd); } if(rptr == NULL) { - if(debug) fprintf(stdout,"Unable to connect to server %s:%d\n", hostname, hostport); + if(verbose) fprintf(stdout,"Unable to connect to server %s:%d\n", hostname, hostport); } else { @@ -434,7 +644,7 @@ int create_connection(void) } else { - if(debug) fprintf(stderr,"No hosts found. Exiting...\n"); + if(verbose) fprintf(stderr,"No hosts found. Exiting...\n"); } if(debug) fprintf(stdout, "create_connection: result=%d\n", result); diff --git a/ovirt-managed-node/src/ovirt-identify-node.h b/ovirt-managed-node/src/ovirt-identify-node.h new file mode 100644 index 0000000..1cb1526 --- /dev/null +++ b/ovirt-managed-node/src/ovirt-identify-node.h @@ -0,0 +1,56 @@ +#ifndef __OVIRT_IDENTIFY_NODE_H +#define __OVIRT_IDENTIFY_NODE_H + +#define BUFFER_LENGTH 128 +#define CPU_FLAGS_BUFFER_LENGTH 256 + +typedef struct _cpu_info { + char cpu_num[BUFFER_LENGTH]; + char core_num[BUFFER_LENGTH]; + char number_of_cores[BUFFER_LENGTH]; + char vendor[BUFFER_LENGTH]; + char model[BUFFER_LENGTH]; + char family[BUFFER_LENGTH]; + char cpuid_level[BUFFER_LENGTH]; + char speed[BUFFER_LENGTH]; + char cache[BUFFER_LENGTH]; + char flags[CPU_FLAGS_BUFFER_LENGTH]; + struct _cpu_info* next; +} t_cpu_info; + +#define COPY_VALUE_TO_BUFFER(value,buffer,length) \ + snprintf(buffer,length,"%s",value) + +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); +int create_connection(void); + +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; + +#endif diff --git a/wui/src/app/models/cpu.rb b/wui/src/app/models/cpu.rb new file mode 100644 index 0000000..1a7d3cf --- /dev/null +++ b/wui/src/app/models/cpu.rb @@ -0,0 +1,24 @@ +# +# 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. + +# +Cpu+ represents the details for a single CPU on a managed node. +# +class Cpu < ActiveRecord::Base + belongs_to :host +end diff --git a/wui/src/app/models/host.rb b/wui/src/app/models/host.rb index 6917226..3413370 100644 --- a/wui/src/app/models/host.rb +++ b/wui/src/app/models/host.rb @@ -1,4 +1,4 @@ -# +# # Copyright (C) 2008 Red Hat, Inc. # Written by Scott Seago <sseago at redhat.com> # @@ -21,8 +21,11 @@ require 'util/ovirt' class Host < ActiveRecord::Base belongs_to :hardware_pool + + has_many :cpus has_many :nics, :dependent => :destroy has_many :vms, :dependent => :nullify do + def consuming_resources find(:all, :conditions=>{:state=>Vm::RUNNING_STATES}) end @@ -32,7 +35,7 @@ class Host < ActiveRecord::Base find(:all, :conditions=>{:state=>Task::STATE_QUEUED}) end def pending_clear_tasks - find(:all, :conditions=>{:state=>Task::WORKING_STATES, + find(:all, :conditions=>{:state=>Task::WORKING_STATES, :action=>HostTask::ACTION_CLEAR_VMS}) end end @@ -42,12 +45,18 @@ class Host < ActiveRecord::Base STATE_UNAVAILABLE = "unavailable" STATE_AVAILABLE = "available" STATES = [STATE_UNAVAILABLE, STATE_AVAILABLE] + + KVM_HYPERVISOR_TYPE = "KVM" + HYPERVISOR_TYPES = [KVM_HYPERVISOR_TYPE] + def memory_in_mb kb_to_mb(memory) end + def memory_in_mb=(mem) self[:memory]=(mb_to_kb(mem)) end + def status_str "#{state} (#{disabled? ? 'disabled':'enabled'})" end @@ -61,4 +70,12 @@ class Host < ActiveRecord::Base not(disabled? and vms.consuming_resources.empty?) and tasks.pending_clear_tasks.empty? end + + def num_cpus + return cpu_details.size + end + + def cpu_speed + "FIX ME!" + end end diff --git a/wui/src/db/migrate/002_create_hosts.rb b/wui/src/db/migrate/002_create_hosts.rb index cef7996..442e674 100644 --- a/wui/src/db/migrate/002_create_hosts.rb +++ b/wui/src/db/migrate/002_create_hosts.rb @@ -23,8 +23,6 @@ class CreateHosts < ActiveRecord::Migration t.string :uuid t.string :hypervisor_type t.string :hostname - t.integer :num_cpus - t.integer :cpu_speed t.string :arch t.integer :memory t.integer :is_disabled diff --git a/wui/src/db/migrate/010_create_cpus.rb b/wui/src/db/migrate/010_create_cpus.rb new file mode 100644 index 0000000..381bf19 --- /dev/null +++ b/wui/src/db/migrate/010_create_cpus.rb @@ -0,0 +1,26 @@ +class CreateCpus < ActiveRecord::Migration + def self.up + create_table :cpus do |t| + t.integer :host_id + t.integer :cpu_number + t.integer :core_number + t.integer :number_of_cores + t.string :vendor, :limit => 128 + t.integer :model + t.integer :family + t.integer :cpuid_level + t.float :speed + t.string :cache + t.string :flags + + t.timestamps + end + + execute "alter table cpus add constraint fk_host_cpus + foreign key (host_id) references hosts(id)" + end + + def self.down + drop_table :cpus + end +end diff --git a/wui/src/dutils/active_record_env.rb b/wui/src/dutils/active_record_env.rb index b0aef04..72feb89 100644 --- a/wui/src/dutils/active_record_env.rb +++ b/wui/src/dutils/active_record_env.rb @@ -1,5 +1,5 @@ #!/usr/bin/ruby -# +# # Copyright (C) 2008 Red Hat, Inc. # Written by Scott Seago <sseago at redhat.com> # @@ -23,7 +23,7 @@ $: << File.join(File.dirname(__FILE__), "../vendor/plugins/betternestedset/lib") require 'rubygems' -gem 'activeldap' +gem 'activeldap' require 'active_ldap' require 'active_support' @@ -58,6 +58,7 @@ require 'models/quota.rb' require 'models/hardware_pool.rb' require 'models/host.rb' +require 'models/cpu.rb' require 'models/nic.rb' require 'models/vm_resource_pool.rb' diff --git a/wui/src/host-browser/host-browser.rb b/wui/src/host-browser/host-browser.rb index 691e4ed..e66493d 100755 --- a/wui/src/host-browser/host-browser.rb +++ b/wui/src/host-browser/host-browser.rb @@ -19,6 +19,7 @@ # also available at http://www.gnu.org/copyleft/gpl.html. $: << File.join(File.dirname(__FILE__), "../dutils") +$: << File.join(File.dirname(__FILE__), "../") require 'rubygems' require 'libvirt' @@ -73,16 +74,61 @@ class HostBrowser # def get_remote_info puts "#{@log_prefix} Begin remote info collection" unless defined?(TESTING) - result = {} + result = Hash.new result['HOSTNAME'] = @session.peeraddr[2] result['IPADDR'] = @session.peeraddr[3] + @session.write("INFO?\n") loop do info = @session.readline.chomp + puts "Received info='#{info}'" + break if info == "ENDINFO" + # if we got the start of a CPU details marker, then process it + if info == "CPU" + cpu = get_cpu_info + cpu_info = result['CPUINFO'] + + if(cpu_info == nil) + cpu_info = Array.new + result['CPUINFO'] = cpu_info + end + + cpu_info << cpu + + else + + 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 + end + + return result + end + + # Extracts CPU details from the managed node. + # + def get_cpu_info + puts "Begin receiving CPU details" + + result = Hash.new + + @session.write("CPUINFO?\n") + + loop do + info = @session.readline.chomp + + break if info == "ENDCPU" + raise Exception.new("ERRINFO! Excepted key=value : #{info}\n") unless info =~ /[\w]+[\s]*=[\w]/ key, value = info.split("=") @@ -93,6 +139,8 @@ class HostBrowser @session.write("ACK #{key}\n") end + @session.write("ACK CPU\n"); + return result end @@ -100,10 +148,24 @@ class HostBrowser # def write_host_info(host_info) ensure_present(host_info,'HOSTNAME') - ensure_present(host_info,'NUMCPUS') - ensure_present(host_info,'CPUSPEED') ensure_present(host_info,'ARCH') ensure_present(host_info,'MEMSIZE') + ensure_present(host_info,'CPUINFO') + + cpu_info = host_info['CPUINFO'] + + cpu_info.each do |cpu| + ensure_present(cpu,'CPUNUM') + ensure_present(cpu,'CORENUM') + ensure_present(cpu,'NUMCORES') + ensure_present(cpu,'VENDOR') + ensure_present(cpu,'MODEL') + ensure_present(cpu,'FAMILY') + ensure_present(cpu,'CPUIDLVL') + ensure_present(cpu,'SPEED') + ensure_present(cpu,'CACHE') + ensure_present(cpu,'FLAGS') + end puts "Searching for existing host record..." unless defined?(TESTING) host = Host.find(:first, :conditions => ["hostname = ?", host_info['HOSTNAME']]) @@ -112,31 +174,49 @@ class HostBrowser begin puts "Creating a new record for #{host_info['HOSTNAME']}..." unless defined?(TESTING) - Host.new( + host = Host.create( "uuid" => host_info['UUID'], "hostname" => host_info['HOSTNAME'], "hypervisor_type" => host_info['HYPERVISOR_TYPE'], - "num_cpus" => host_info['NUMCPUS'], - "cpu_speed" => host_info['CPUSPEED'], "arch" => host_info['ARCH'], "memory_in_mb" => host_info['MEMSIZE'], "is_disabled" => 0, "hardware_pool" => HardwarePool.get_default_pool, # Let host-status mark it available when it # successfully connects to it via libvirt. - "state" => Host::STATE_UNAVAILABLE).save + "state" => Host::STATE_UNAVAILABLE) + host.save! rescue Exception => error puts "Error while creating record: #{error.message}" unless defined?(TESTING) end else host.uuid = host_info['UUID'] host.hostname = host_info['HOSTNAME'] - host.num_cpus = host_info['NUMCPUS'] - host.cpu_speed = host_info['CPUSPEED'] host.arch = host_info['ARCH'] host.memory_in_mb = host_info['MEMSIZE'] end + # delete an existing CPUs and create new ones based on the data + puts "Deleting any existing CPUs" + Cpu.delete_all(['host_id = ?', host.id]) + + puts "Saving new CPU records" + cpu_info.collect do |cpu| + detail = Cpu.new( + "cpu_number" => cpu['CPUNUM'], + "core_number" => cpu['CORENUM]'], + "number_of_cores" => cpu['NUMCORES'], + "vendor" => cpu['VENDOR'], + "model" => cpu['MODEL'], + "family" => cpu['FAMILY'], + "cpuid_level" => cpu['CPUIDLVL'], + "speed" => cpu['SPEED'], + "cache" => cpu['CACHE'], + "flags" => cpu['FLAGS']) + + host.cpus << detail + end + return host end @@ -181,8 +261,8 @@ class HostBrowser # Private method to ensure that a required field is present. # - def ensure_present(host_info,key) - raise Exception.new("ERROR! Missing '#{key}'...") if host_info[key] == nil + def ensure_present(info,key) + raise Exception.new("ERROR! Missing '#{key}'...") if info[key] == nil end # Executes an external program to support the keytab function. @@ -226,7 +306,6 @@ def entry_point(server) end unless defined?(TESTING) - # The main entry point. # unless ARGV[0] == "-n" diff --git a/wui/src/host-browser/test-host-browser-awake.rb b/wui/src/host-browser/test-host-browser-awake.rb new file mode 100755 index 0000000..02e9146 --- /dev/null +++ b/wui/src/host-browser/test-host-browser-awake.rb @@ -0,0 +1,94 @@ +#!/usr/bin/ruby -Wall +# +# Copyright (C) 2008 Red Hat, Inc. +# Written by Darryl L. Pierce <dpierce at redhat.com> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. A copy of the GNU General Public License is +# also available at http://www.gnu.org/copyleft/gpl.html. + +require File.dirname(__FILE__) + '/../test/test_helper' +require 'test/unit' +require 'flexmock/test_unit' + +TESTING=true + +require 'host-browser' + +# +TestHostBrowserAwaken+ +class TestHostBrowserAwaken < Test::Unit::TestCase + + def setup + @session = flexmock('session') + @session.should_receive(:peeraddr).at_least.once.returns { [nil,nil,nil,"192.168.2.255"] } + + @krb5 = flexmock('krb5') + + @browser = HostBrowser.new(@session) + @browser.logfile = './unit-test.log' + @browser.keytab_dir = '/var/temp/' + end + + # Ensures that the server raises an exception when it receives an + # improper handshake response. + # + def test_begin_conversation_with_improper_response_to_greeting + @session.should_receive(:write).with("HELLO?\n").once().returns { |greeting| greeting.length } + @session.should_receive(:readline).once().returns { "SUP?" } + + assert_raise(Exception) { @browser.begin_conversation } + end + + # Ensures the server accepts a proper response from the remote system. + # + def test_begin_conversation + @session.should_receive(:write).with("HELLO?\n").once().returns { |greeting| greeting.length } + @session.should_receive(:readline).once().returns { "HELLO!\n" } + + assert_nothing_raised(Exception) { @browser.begin_conversation } + end + + # Ensures that the server is satisfied if the remote system is + # making a wakeup call. + # + def test_get_mode_with_awaken_request + @session.should_receive(:write).with("MODE?\n").once().returns { |request| request.length } + @session.should_receive(:readline).once().returns { "AWAKEN\n" } + + result = @browser.get_mode() + + assert_equal "AWAKEN", result, "method did not return the right value" + end + + # Ensures the host browser generates a keytab as expected. + # + def test_create_keytab + @krb5.should_receive(:get_default_realm).once().returns { "ovirt-test-realm" } + servername = `hostname -f`.chomp + @session.should_receive(:write).with("KTAB http://#{servername}/ipa/config/127.0.0.1-libvirt.tab\n").once().returns { |request| request.length } + @session.should_receive(:readline).once().returns { "ACK\n" } + + assert_nothing_raised(Exception) { @browser.create_keytab('localhost','127.0.0.1', at krb5) } + end + + # Ensures that, if a keytab is present and a key version number available, + # the server ends the conversation by returning the key version number. + # + def test_end_conversation + @session.should_receive(:write).with("BYE\n").once().returns { |request| request.length } + + assert_nothing_raised(Exception) { @browser.end_conversation } + end + +end diff --git a/wui/src/host-browser/test-host-browser-awaken.rb b/wui/src/host-browser/test-host-browser-awaken.rb deleted file mode 100755 index a5ca2e7..0000000 --- a/wui/src/host-browser/test-host-browser-awaken.rb +++ /dev/null @@ -1,94 +0,0 @@ -#!/usr/bin/ruby -Wall -# -# Copyright (C) 2008 Red Hat, Inc. -# Written by Darryl L. Pierce <dpierce at redhat.com> -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; version 2 of the License. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301, USA. A copy of the GNU General Public License is -# also available at http://www.gnu.org/copyleft/gpl.html. - -require File.dirname(__FILE__) + '/../test/test_helper' -require 'test/unit' -require 'flexmock/test_unit' - -TESTING=true - -require 'host-browser' - -# +TestHostBrowserAwaken+ -class TestHostBrowserAwaken < Test::Unit::TestCase - - def setup - @session = flexmock('session') - @session.should_receive(:peeraddr).at_least.once.returns { [nil,nil,nil,"192.168.2.255"] } - - @krb5 = flexmock('krb5') - - @browser = HostBrowser.new(@session) - @browser.logfile = './unit-test.log' - @browser.keytab_dir = '/var/temp/' - end - - # Ensures that the server raises an exception when it receives an - # improper handshake response. - # - def test_begin_conversation_with_improper_response_to_greeting - @session.should_receive(:write).with("HELLO?\n").once().returns { |greeting| greeting.length } - @session.should_receive(:readline).once().returns { "SUP?" } - - assert_raise(Exception) { @browser.begin_conversation } - end - - # Ensures the server accepts a proper response from the remote system. - # - def test_begin_conversation - @session.should_receive(:write).with("HELLO?\n").once().returns { |greeting| greeting.length } - @session.should_receive(:readline).once().returns { "HELLO!\n" } - - assert_nothing_raised(Exception) { @browser.begin_conversation } - end - - # Ensures that the server is satisfied if the remote system is - # making a wakeup call. - # - def test_get_mode_with_awaken_request - @session.should_receive(:write).with("MODE?\n").once().returns { |request| request.length } - @session.should_receive(:readline).once().returns { "AWAKEN\n" } - - result = @browser.get_mode() - - assert_equal "AWAKEN", result, "method did not return the right value" - end - - # Ensures the host browser generates a keytab as expected. - # - def test_create_keytab - @krb5.should_receive(:get_default_realm).once().returns { "ovirt-test-realm" } - servername = `hostname -f`.chomp - @session.should_receive(:write).with("KTAB http://#{servername}/config/127.0.0.1-libvirt.tab\n").once().returns { |request| request.length } - @session.should_receive(:readline).once().returns { "ACK\n" } - - assert_nothing_raised(Exception) { @browser.create_keytab('localhost','127.0.0.1', at krb5) } - end - - # Ensures that, if a keytab is present and a key version number available, - # the server ends the conversation by returning the key version number. - # - def test_end_conversation - @session.should_receive(:write).with("BYE\n").once().returns { |request| request.length } - - assert_nothing_raised(Exception) { @browser.end_conversation } - end - -end diff --git a/wui/src/host-browser/test-host-browser-identify.rb b/wui/src/host-browser/test-host-browser-identify.rb index 8f215e9..4197e19 100755 --- a/wui/src/host-browser/test-host-browser-identify.rb +++ b/wui/src/host-browser/test-host-browser-identify.rb @@ -27,136 +27,219 @@ TESTING=true require 'host-browser' class TestHostBrowser < Test::Unit::TestCase - - def setup - @session = flexmock('session') - @session.should_receive(:peeraddr).at_least.once.returns { [nil,nil,nil,"192.168.2.255"] } - - @browser = HostBrowser.new(@session) - @browser.logfile = './unit-test.log' - - # default host info - @host_info = {} - @host_info['UUID'] = 'node1' - @host_info['IPADDR'] = '192.168.2.2' - @host_info['HOSTNAME'] = 'node1.ovirt.redhat.com' - @host_info['NUMCPUS'] = '3' - @host_info['CPUSPEED'] = '3' - @host_info['ARCH'] = 'x86_64' - @host_info['MEMSIZE'] = '16384' - @host_info['DISABLED'] = '0' - end - - # Ensures that the server is satisfied if the remote system is - # making a wakeup call. - # - def test_get_mode_with_awaken_request - @session.should_receive(:write).with("MODE?\n").once().returns { |request| request.length } - @session.should_receive(:readline).once().returns { "IDENTIFY\n" } - - result = @browser.get_mode() - - assert_equal "IDENTIFY", result, "method did not return the right value" - end - - # Ensures that, if an info field is missing a key, the server raises - # an exception. - # - def test_get_info_with_missing_key - @session.should_receive(:write).with("INFO?\n").once().returns { |request| request.length } - @session.should_receive(:readline).once().returns { "=value1\n" } - - assert_raise(Exception) { @browser.get_remote_info } - end - - # Ensures that, if an info field is missing a value, the server raises - # an exception. - # - def test_get_info_with_missing_value - @session.should_receive(:write).with("INFO?\n").once().returns { |request| request.length } - @session.should_receive(:readline).once().returns { "key1=\n" } - - assert_raise(Exception) { @browser.get_remote_info } - end - - # Ensures that, if the server gets a poorly formed ending statement, it - # raises an exception. - # - def test_get_info_with_invalid_end - @session.should_receive(:write).with("INFO?\n").once().returns { |request| request.length } - @session.should_receive(:readline).once().returns { "key1=value1\n" } - @session.should_receive(:write).with("ACK key1\n").once().returns { |request| request.length } - @session.should_receive(:readline).once().returns { "ENDIFNO\n" } - - assert_raise(Exception) { @browser.get_remote_info } - end - - # Ensures that a well-formed transaction works as expected. - # - def test_get_info - @session.should_receive(:write).with("INFO?\n").once().returns { |request| request.length } - @session.should_receive(:readline).once().returns { "key1=value1\n" } - @session.should_receive(:write).with("ACK key1\n").once().returns { |request| request.length } - @session.should_receive(:readline).once().returns { "key2=value2\n" } - @session.should_receive(:write).with("ACK key2\n").once().returns { |request| request.length } - @session.should_receive(:readline).once().returns { "ENDINFO\n" } - - info = @browser.get_remote_info - - assert_equal 4,info.keys.size, "Should contain two keys" - assert info.include?("IPADDR") - assert info.include?("HOSTNAME") - assert info.include?("key1") - assert info.include?("key2") - end - - # Ensures that the server is fine when no UUID is present. - # - def test_write_host_info_with_missing_uuid - @host_info['UUID'] = nil - - assert_nothing_raised { @browser.write_host_info(@host_info) } - end - - # Ensures that, if the hostname is missing, the server - # raises an exception. - # - def test_write_host_info_with_missing_hostname - @host_info['HOSTNAME'] = nil - - assert_raise(Exception) { @browser.write_host_info(@host_info) } - end - - # Ensures that, if the number of CPUs is missing, the server raises an exception. - # - def test_write_host_info_with_missing_numcpus - @host_info['NUMCPUS'] = nil - - assert_raise(Exception) { @browser.write_host_info(@host_info) } - end - - # Ensures that, if the CPU speed is missing, the server raises an exception. - # - def test_write_host_info_with_missing_cpuspeed - @host_info['CPUSPEED'] = nil - - assert_raise(Exception) { @browser.write_host_info(@host_info) } - end - - # Ensures that, if the architecture is missing, the server raises an exception. - # - def test_write_host_info_with_missing_arch - @host_info['ARCH'] = nil - - assert_raise(Exception) { @browser.write_host_info(@host_info) } - end - - # Ensures that, if the memory size is missing, the server raises an exception. - # - def test_write_host_info_info_with_missing_memsize - @host_info['MEMSIZE'] = nil - - assert_raise(Exception) { @browser.write_host_info(@host_info) } - end + def setup + @session = flexmock('session') + @session.should_receive(:peeraddr).at_least.once.returns { [nil,nil,nil,"192.168.2.255"] } + + @browser = HostBrowser.new(@session) + @browser.logfile = './unit-test.log' + + # default host info + @host_info = {} + @host_info['UUID'] = 'node1' + @host_info['IPADDR'] = '192.168.2.2' + @host_info['HOSTNAME'] = 'node1.ovirt.redhat.com' + @host_info['ARCH'] = 'x86_64' + @host_info['MEMSIZE'] = '16384' + @host_info['DISABLED'] = '0' + + @host_info['NUMCPUS'] = '2' + + @host_info['CPUINFO'] = Array.new + @host_info['CPUINFO'][0] = {} + @host_info['CPUINFO'][0]['CPUNUM'] = '0' + @host_info['CPUINFO'][0]['CORENUM'] = '0' + @host_info['CPUINFO'][0]['NUMCORES'] = '2' + @host_info['CPUINFO'][0]['VENDOR'] = 'GenuineIntel' + @host_info['CPUINFO'][0]['MODEL'] = '15' + @host_info['CPUINFO'][0]['FAMILY'] = '6' + @host_info['CPUINFO'][0]['CPUIDLVL'] = '10' + @host_info['CPUINFO'][0]['SPEED'] = '3' + @host_info['CPUINFO'][0]['CACHE'] = '4096 kb' + @host_info['CPUINFO'][0]['FLAGS'] = 'fpu vme de pse tsc msr pae \ + 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['CPUINFO'][1] = {} + @host_info['CPUINFO'][1]['CPUNUM'] = '1' + @host_info['CPUINFO'][1]['CORENUM'] = '1' + @host_info['CPUINFO'][1]['NUMCORES'] = '2' + @host_info['CPUINFO'][1]['VENDOR'] = 'GenuineIntel' + @host_info['CPUINFO'][1]['MODEL'] = '15' + @host_info['CPUINFO'][1]['FAMILY'] = '6' + @host_info['CPUINFO'][1]['CPUIDLVL'] = '10' + @host_info['CPUINFO'][1]['SPEED'] = '3' + @host_info['CPUINFO'][1]['CACHE'] = '4096 kb' + @host_info['CPUINFO'][1]['FLAGS'] = 'fpu vme de pse tsc msr pae \ + 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' + end + + # Ensures that the server is satisfied if the remote system is + # making a wakeup call. + # + def test_get_mode_with_awaken_request + @session.should_receive(:write).with("MODE?\n").once().returns { |request| request.length } + @session.should_receive(:readline).once().returns { "IDENTIFY\n" } + + result = @browser.get_mode() + + assert_equal "IDENTIFY", result, "method did not return the right value" + end + + # Ensures that, if an info field is missing a key, the server raises + # an exception. + # + def test_get_info_with_missing_key + @session.should_receive(:write).with("INFO?\n").once().returns { |request| request.length } + @session.should_receive(:readline).once().returns { "=value1\n" } + + assert_raise(Exception) { @browser.get_remote_info } + end + + # Ensures that, if an info field is missing a value, the server raises + # an exception. + # + def test_get_info_with_missing_value + @session.should_receive(:write).with("INFO?\n").once().returns { |request| request.length } + @session.should_receive(:readline).once().returns { "key1=\n" } + + assert_raise(Exception) { @browser.get_remote_info } + end + + # Ensures that, if the server gets a poorly formed ending statement, it + # raises an exception. + # + def test_get_info_with_invalid_end + @session.should_receive(:write).with("INFO?\n").once().returns { |request| request.length } + @session.should_receive(:readline).once().returns { "key1=value1\n" } + @session.should_receive(:write).with("ACK key1\n").once().returns { |request| request.length } + @session.should_receive(:readline).once().returns { "ENDIFNO\n" } + + assert_raise(Exception) { @browser.get_remote_info } + end + + # Ensures that a well-formed transaction works as expected. + # + def test_get_info + @session.should_receive(:write).with("INFO?\n").once().returns { |request| request.length } + @session.should_receive(:readline).once().returns { "key1=value1\n" } + @session.should_receive(:write).with("ACK key1\n").once().returns { |request| request.length } + @session.should_receive(:readline).once().returns { "key2=value2\n" } + @session.should_receive(:write).with("ACK key2\n").once().returns { |request| request.length } + @session.should_receive(:readline).once().returns { "ENDINFO\n" } + + info = @browser.get_remote_info + + assert_equal 4,info.keys.size, "Should contain four keys" + assert info.include?("IPADDR") + assert info.include?("HOSTNAME") + assert info.include?("key1") + assert info.include?("key2") + end + + # Ensures that the server is fine when no UUID is present. + # + def test_write_host_info_with_missing_uuid + @host_info['UUID'] = nil + + assert_nothing_raised { @browser.write_host_info(@host_info) } + end + + # Ensures that, if the hostname is missing, the server + # raises an exception. + # + def test_write_host_info_with_missing_hostname + @host_info['HOSTNAME'] = nil + + assert_raise(Exception) { @browser.write_host_info(@host_info) } + end + + # Ensures that, if the architecture is missing, the server raises an + # exception. + # + def test_write_host_info_with_missing_arch + @host_info['ARCH'] = nil + + assert_raise(Exception) { @browser.write_host_info(@host_info) } + end + + # Ensures that, if the memory size is missing, the server raises an + # exception. + # + def test_write_host_info_info_with_missing_memsize + @host_info['MEMSIZE'] = nil + + assert_raise(Exception) { @browser.write_host_info(@host_info) } + end + + # Ensures that, if no cpu info was available, the server raises an + # exception. + # + def test_write_host_info_with_missing_cpuinfo + @host_info['CPUINFO'] = 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 + @session.should_receive(:write).with("INFO?\n").once().returns { |request| request.length } + @session.should_receive(:readline).once().returns { "CPU\n" } + @session.should_receive(:write).with("CPUINFO?\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 { "ENDCPU\n" } + @session.should_receive(:write).with("ACK CPU\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?("CPUINFO") + end + + # Ensures the browser can properly parse the CPU details of two CPUs. + # + def test_parse_cpu_info + @session.should_receive(:write).with("INFO?\n").once().returns { |request| request.length } + + # CPU 0 + @session.should_receive(:readline).once().returns { "CPU\n" } + @session.should_receive(:write).with("CPUINFO?\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 { "ENDCPU\n" } + @session.should_receive(:write).with("ACK CPU\n").once().returns { |request| request.length } + + # CPU 1 + @session.should_receive(:readline).once().returns { "CPU\n" } + @session.should_receive(:write).with("CPUINFO?\n").once().returns { |request| request.length } + @session.should_receive(:readline).once().returns { "key3=value3\n" } + @session.should_receive(:write).with("ACK key3\n").once().returns { |request| request.length } + @session.should_receive(:readline).once().returns { "key4=value4\n" } + @session.should_receive(:write).with("ACK key4\n").once().returns { |request| request.length } + @session.should_receive(:readline).once().returns { "ENDCPU\n" } + @session.should_receive(:write).with("ACK CPU\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?('CPUINFO') + assert_equal 2, info['CPUINFO'].size, "Should contain details for two CPUs" + assert_not_nil info['CPUINFO'][0]['key1'] + assert_not_nil info['CPUINFO'][0]['key2'] + assert_not_nil info['CPUINFO'][1]['key3'] + assert_not_nil info['CPUINFO'][1]['key4'] + end end diff --git a/wui/src/test/fixtures/cpus.yml b/wui/src/test/fixtures/cpus.yml new file mode 100644 index 0000000..5586303 --- /dev/null +++ b/wui/src/test/fixtures/cpus.yml @@ -0,0 +1,21 @@ +# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html + +one: + cpu_number: 1 + core_number: 1 + cpu_id_level: 1 + vendor: 1 + family: 1 + model: 1 + family: MyString + flags: MyString + +two: + cpu_number: 1 + core_number: 1 + cpu_id_level: 1 + vendor: 1 + family: 1 + model: 1 + family: MyString + flags: MyString diff --git a/wui/src/test/fixtures/hosts.yml b/wui/src/test/fixtures/hosts.yml index 966a829..6658fa7 100644 --- a/wui/src/test/fixtures/hosts.yml +++ b/wui/src/test/fixtures/hosts.yml @@ -2,8 +2,6 @@ one: id: 1 uuid: '1148fdf8-961d-11dc-9387-001558c41534' hostname: 'prod.corp.com' - num_cpus: 8 - cpu_speed: 4096 arch: 'i686' memory: 18384 is_disabled: 0 @@ -13,8 +11,6 @@ two: id: 2 uuid: '1f2a8694-961d-11dc-9387-001558c41534' hostname: 'myworkstation.dev.corp.com' - num_cpus: 4 - cpu_speed: 2048 arch: 'i386' memory: 2048 is_disabled: 0 @@ -24,52 +20,42 @@ three: id: 3 uuid: '58a85f44-75fd-4934-805f-88e45b40d4b4' hostname: 'macworkstation.foobar.com' - num_cpus: 8 - cpu_speed: 1024 arch: 'mips' memory: 2048 - is_disabled: 0 + is_disabled: 0 hypervisor_type: 'kvm' hardware_pool_id: 2 four: id: 4 uuid: '520bbb34-6515-490e-9d07-0c8b14f76805' hostname: 'fedoraworkstation.foobar.com' - num_cpus: 8 - cpu_speed: 3072 arch: 'i386' memory: 2048 - is_disabled: 1 + is_disabled: 1 hypervisor_type: 'kvm' hardware_pool_id: 2 five: id: 5 uuid: '2e422f66-324e-48d4-973f-0b91b33070f9' hostname: 'pipeline.foobar.com' - num_cpus: 32 - cpu_speed: 4096 arch: 'xeon' memory: 16384 - is_disabled: 0 + is_disabled: 0 hypervisor_type: 'kvm' hardware_pool_id: 2 six: id: 6 uuid: 'bb0ce7c7-f234-49ae-84b5-6f4fd8bddcaa' hostname: 'prod.foobar.com' - num_cpus: 32 - cpu_speed: 4096 arch: 'xeon' memory: 16384 - is_disabled: 0 + is_disabled: 0 hypervisor_type: 'kvm' hardware_pool_id: 2 seven: id: 7 uuid: '6ae3d22e-97e0-4d86-9712-5395b20a0f45' hostname: 'mystation.dev.corp.com' - num_cpus: 4 - cpu_speed: 3072 arch: 'i686' memory: 2048 is_disabled: 0 @@ -79,8 +65,6 @@ eight: id: 8 uuid: 'ec0d86de-657b-48f6-b7cc-e733a3f9a834' hostname: 'issue.qa.corp.com' - num_cpus: 8 - cpu_speed: 4096 arch: 'x86' memory: 4096 is_disabled: 0 @@ -90,8 +74,6 @@ nine: id: 9 uuid: '81c15560-dbf4-45f5-9b75-106cdbd63aeb' hostname: 'somehost' - num_cpus: 4 - cpu_speed: 4096 arch: 'x86' memory: 4096 is_disabled: 0 diff --git a/wui/src/test/unit/cpu_test.rb b/wui/src/test/unit/cpu_test.rb new file mode 100644 index 0000000..5b64ca7 --- /dev/null +++ b/wui/src/test/unit/cpu_test.rb @@ -0,0 +1,8 @@ +require File.dirname(__FILE__) + '/../test_helper' + +class CpuTest < ActiveSupport::TestCase + # Replace this with your real tests. + def test_truth + assert true + end +end -- 1.5.5.1
Darryl L. Pierce
2008-Jul-08 12:01 UTC
[Ovirt-devel] Re: ovirt-identify-node now submits all extended CPU details.
+++ Darryl L. Pierce [05/07/08 09:59 -0400]: Ditto on feedback here. I've got code queued up for NICs but don't want to base those changes on this codeset and any feedback I get. -- Darryl L. Pierce, Sr. Software Engineer Red Hat, Inc. - http://www.redhat.com/ oVirt - Virtual Machine Management - http://www.ovirt.org/ "What do you care what other people think, Mr. Feynman?" -------------- next part -------------- A non-text attachment was scrubbed... Name: not available Type: application/pgp-signature Size: 197 bytes Desc: not available URL: <http://listman.redhat.com/archives/ovirt-devel/attachments/20080708/f6f926e2/attachment.sig>
Darryl L. Pierce
2008-Jul-08 13:16 UTC
[Ovirt-devel] [PATCH] ovirt-identify-node now submits all extended CPU details.
From: Darryl Pierce <dpierce at redhat.com> Signed-off-by: Darryl Pierce <mcpierce at mcpierce-laptop.(none)> --- ovirt-managed-node/src/ovirt-identify-node.c | 420 +++++++++++++++----- ovirt-managed-node/src/ovirt-identify-node.h | 56 +++ wui/src/app/models/cpu.rb | 24 ++ wui/src/app/models/host.rb | 21 +- wui/src/db/migrate/002_create_hosts.rb | 2 - wui/src/db/migrate/010_create_cpus.rb | 26 ++ wui/src/dutils/active_record_env.rb | 5 +- wui/src/host-browser/host-browser.rb | 103 +++++- wui/src/host-browser/test-host-browser-awake.rb | 94 +++++ wui/src/host-browser/test-host-browser-awaken.rb | 94 ----- wui/src/host-browser/test-host-browser-identify.rb | 345 ++++++++++------ wui/src/test/fixtures/cpus.yml | 21 + wui/src/test/fixtures/hosts.yml | 24 +- wui/src/test/unit/cpu_test.rb | 8 + 14 files changed, 874 insertions(+), 369 deletions(-) create mode 100644 ovirt-managed-node/src/ovirt-identify-node.h create mode 100644 wui/src/app/models/cpu.rb create mode 100644 wui/src/db/migrate/010_create_cpus.rb create mode 100755 wui/src/host-browser/test-host-browser-awake.rb delete mode 100755 wui/src/host-browser/test-host-browser-awaken.rb create mode 100644 wui/src/test/fixtures/cpus.yml create mode 100644 wui/src/test/unit/cpu_test.rb diff --git a/ovirt-managed-node/src/ovirt-identify-node.c b/ovirt-managed-node/src/ovirt-identify-node.c index 819f700..f114d81 100644 --- a/ovirt-managed-node/src/ovirt-identify-node.c +++ b/ovirt-managed-node/src/ovirt-identify-node.c @@ -31,31 +31,7 @@ #include <sys/types.h> #include <sys/socket.h> -int config(int argc,char** argv); -void usage(void); - -int start_conversation(void); -int send_details(void); -int end_conversation(void); - -int send_text(char* text); -int get_text(const char *const expected); -int create_connection(void); - -int debug = 1; -int verbose = 1; -int testing = 0; - -#define BUFFER_LENGTH 128 - -char arch[BUFFER_LENGTH]; -char uuid[VIR_UUID_BUFLEN]; -char memsize[BUFFER_LENGTH]; -char numcpus[BUFFER_LENGTH]; -char cpuspeed[BUFFER_LENGTH]; -char *hostname; -int hostport = -1; -int socketfd; +#include "ovirt-identify-node.h" int main(int argc,char** argv) { @@ -63,9 +39,11 @@ int main(int argc,char** argv) virConnectPtr connection; virNodeInfo info; + fprintf(stdout,"Sending managed node details to server.\n"); + if(!config(argc,argv)) { - fprintf(stdout,"Connecting to libvirt.\n"); + if(verbose) fprintf(stdout,"Connecting to libvirt.\n"); connection = virConnectOpenReadOnly(testing ? "test:///default" : NULL); @@ -73,42 +51,68 @@ int main(int argc,char** argv) if(connection) { - if(debug) fprintf(stdout,"Getting hostname: %s\n", uuid); + if(verbose) fprintf(stdout,"Getting hostname: %s\n", uuid); if(!strlen(uuid)) gethostname(uuid,sizeof uuid); - if(debug) fprintf(stdout,"Retrieving node information.\n"); + 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); - snprintf(numcpus, BUFFER_LENGTH, "%d", info.cpus); - snprintf(cpuspeed, BUFFER_LENGTH, "%d", info.mhz); - if(debug) + cpu_info = NULL; + + if(!get_cpu_info()) { - fprintf(stdout,"Node Info:\n"); - fprintf(stdout," UUID: %s\n", uuid); - fprintf(stdout," Arch: %s\n", arch); - fprintf(stdout," Memory: %s\n", memsize); - fprintf(stdout," # CPUs: %s\n", numcpus); - fprintf(stdout,"CPU Speed: %s\n", cpuspeed); + if(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; + } } - - if(debug) fprintf(stdout, "Retrieved node information.\n"); - - if(!start_conversation() && !send_details() && !end_conversation()) + else { - result = 0; + if(verbose) fprintf(stderr,"Failed to get CPU info.\n"); } } else { - if(debug) fprintf(stderr,"Failed to get node info.\n"); + if(verbose) fprintf(stderr,"Failed to get node info.\n"); } } else { - if(debug) fprintf(stderr,"Could not connect to libvirt.\n"); + if(verbose) fprintf(stderr,"Could not connect to libvirt.\n"); } } else @@ -181,35 +185,35 @@ int start_conversation(void) { if(debug || verbose) fprintf(stdout,"Connected.\n"); - if (!get_text("HELLO?\n")) + if (!get_text("HELLO?")) { - if(debug) fprintf(stdout,"Checking for handshake.\n"); + if(verbose) fprintf(stdout,"Checking for handshake.\n"); - if(!send_text("HELLO!\n")) + if(!send_text("HELLO!")) { - if(debug) fprintf(stdout,"Handshake received. Starting conversation.\n"); + if(verbose) fprintf(stdout,"Handshake received. Starting conversation.\n"); - if(!get_text("MODE?\n")) + if(!get_text("MODE?")) { - if(debug) fprintf(stdout,"Shifting to IDENTIFY mode.\n"); + if(verbose) fprintf(stdout,"Shifting to IDENTIFY mode.\n"); - if(!send_text("IDENTIFY\n")) result = 0; + if(!send_text("IDENTIFY")) result = 0; } else { - if(debug) fprintf(stderr,"Was not asked for a mode.\n"); + if(verbose) fprintf(stderr,"Was not asked for a mode.\n"); } } } else { - if(debug) fprintf(stderr,"Did not receive a proper handshake.\n"); + if(verbose) fprintf(stderr,"Did not receive a proper handshake.\n"); } } else { - if(debug) fprintf(stderr,"Did not get a connection.\n"); + if(verbose) fprintf(stderr,"Did not get a connection.\n"); } if(debug) fprintf(stdout,"start_conversation: result=%d\n", result); @@ -223,19 +227,15 @@ int send_value(char* label,char* value) int result = 1; char expected[BUFFER_LENGTH]; - snprintf(buffer,BUFFER_LENGTH,"%s=%s\n", label, value); + snprintf(buffer,BUFFER_LENGTH,"%s=%s", label, value); if(!send_text(buffer)) { - snprintf(expected, BUFFER_LENGTH, "ACK %s\n", label); + snprintf(expected, BUFFER_LENGTH, "ACK %s", label); - if(debug) fprintf(stdout,"Expecting \"%s\"\n", expected); - - if (!get_text(expected)) - { - result = 0; - } + if(verbose) fprintf(stdout,"Expecting \"%s\"\n", expected); + result = get_text(expected); } return result; @@ -245,40 +245,238 @@ int send_details(void) { int result = 1; - fprintf(stdout,"Sending node details.\n"); + if(verbose) fprintf(stdout,"Sending node details.\n"); - if (!get_text("INFO?\n")) + if (!get_text("INFO?")) { if((!send_value("ARCH", arch)) && (!send_value("UUID", uuid)) && - (!send_value("NUMCPUS", numcpus)) && - (!send_value("CPUSPEED", cpuspeed)) && - (!send_value("MEMSIZE", memsize))) + (!send_value("MEMSIZE", memsize)) && + (!send_cpu_details())) { - if(!send_text("ENDINFO\n")) result = 0; + if(!send_text("ENDINFO")) result = 0; } } else { - if(debug) fprintf(stdout,"Was not interrogated for hardware info.\n"); + 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; - fprintf(stdout,"Ending conversation.\n"); + if(debug || verbose) fprintf(stdout,"Ending conversation.\n"); - send_text("ENDINFO\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; @@ -303,9 +501,10 @@ int send_text(char* text) int result = 1; int sent; - if(debug || verbose) fprintf(stdout,"\"%s\" -> %s:%d\n", text, hostname, hostport); + if(verbose) fprintf(stdout,"Sending: \"%s\"\n", text); sent = safewrite(socketfd, text, strlen(text)); + sent += safewrite(socketfd, "\n", 1); if(sent >= 0) { @@ -317,35 +516,46 @@ int send_text(char* text) return result; } -int saferead(int fd, void *buf, size_t count) +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(len_left > 0) { - bytes = read(fd, buf+offset, len_left); - fprintf(stderr,"After read, bytes is %ld\n",bytes); - if (bytes < 0) { - if (errno == EINTR) { - continue; - } - else { - offset = -1; - break; - } - } - else if (bytes == 0) { - // reached EOF; break out of here - break; - } - - offset += bytes; - len_left -= bytes; + + 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; @@ -353,22 +563,22 @@ int saferead(int fd, void *buf, size_t count) int get_text(const char *const expected) { + int result = 1; int received; char buffer[BUFFER_LENGTH]; + bzero(buffer,BUFFER_LENGTH); - if(debug) fprintf(stdout, "Looking to receive %s\n", expected); + if(verbose) fprintf(stdout, "Looking to receive %s\n", expected); - received = saferead(socketfd, buffer, strlen(expected)); + received = saferead(socketfd, buffer, BUFFER_LENGTH); buffer[received - 1] = 0; - if(debug) fprintf(stdout,"Received \"%s\": size=%d (trimmed ending carriage return)\n", buffer, received); + if(verbose) fprintf(stdout,"Received \"%s\": size=%d (trimmed ending carriage return)\n", buffer, received); - if (strncmp(expected, buffer, strlen(expected)) != 0) { - return 0; - } + result = strcmp(expected,buffer); - return 1; + return result; } int create_connection(void) @@ -379,7 +589,7 @@ int create_connection(void) char port[6]; struct addrinfo* rptr; - if(debug) fprintf(stdout,"Creating the socket connection.\n"); + if(verbose) fprintf(stdout,"Creating the socket connection.\n"); memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = AF_UNSPEC; @@ -387,13 +597,13 @@ int create_connection(void) hints.ai_flags = 0; hints.ai_protocol = 0; - if(debug) fprintf(stdout,"Searching for host candidates.\n"); + if(verbose) fprintf(stdout,"Searching for host candidates.\n"); snprintf(port, 6, "%d", hostport); if(!getaddrinfo(hostname, port, &hints, &results)) { - if(debug) fprintf(stdout,"Got address information. Searching for a proper entry.\n"); + if(verbose) fprintf(stdout,"Got address information. Searching for a proper entry.\n"); for(rptr = results; rptr != NULL; rptr = rptr->ai_next) { @@ -416,13 +626,13 @@ int create_connection(void) } // invalid connection, so close it - if(debug) fprintf(stdout, "Invalid connection.\n"); + if(verbose) fprintf(stdout, "Invalid connection.\n"); close(socketfd); } if(rptr == NULL) { - if(debug) fprintf(stdout,"Unable to connect to server %s:%d\n", hostname, hostport); + if(verbose) fprintf(stdout,"Unable to connect to server %s:%d\n", hostname, hostport); } else { @@ -434,7 +644,7 @@ int create_connection(void) } else { - if(debug) fprintf(stderr,"No hosts found. Exiting...\n"); + if(verbose) fprintf(stderr,"No hosts found. Exiting...\n"); } if(debug) fprintf(stdout, "create_connection: result=%d\n", result); diff --git a/ovirt-managed-node/src/ovirt-identify-node.h b/ovirt-managed-node/src/ovirt-identify-node.h new file mode 100644 index 0000000..1cb1526 --- /dev/null +++ b/ovirt-managed-node/src/ovirt-identify-node.h @@ -0,0 +1,56 @@ +#ifndef __OVIRT_IDENTIFY_NODE_H +#define __OVIRT_IDENTIFY_NODE_H + +#define BUFFER_LENGTH 128 +#define CPU_FLAGS_BUFFER_LENGTH 256 + +typedef struct _cpu_info { + char cpu_num[BUFFER_LENGTH]; + char core_num[BUFFER_LENGTH]; + char number_of_cores[BUFFER_LENGTH]; + char vendor[BUFFER_LENGTH]; + char model[BUFFER_LENGTH]; + char family[BUFFER_LENGTH]; + char cpuid_level[BUFFER_LENGTH]; + char speed[BUFFER_LENGTH]; + char cache[BUFFER_LENGTH]; + char flags[CPU_FLAGS_BUFFER_LENGTH]; + struct _cpu_info* next; +} t_cpu_info; + +#define COPY_VALUE_TO_BUFFER(value,buffer,length) \ + snprintf(buffer,length,"%s",value) + +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); +int create_connection(void); + +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; + +#endif diff --git a/wui/src/app/models/cpu.rb b/wui/src/app/models/cpu.rb new file mode 100644 index 0000000..1a7d3cf --- /dev/null +++ b/wui/src/app/models/cpu.rb @@ -0,0 +1,24 @@ +# +# 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. + +# +Cpu+ represents the details for a single CPU on a managed node. +# +class Cpu < ActiveRecord::Base + belongs_to :host +end diff --git a/wui/src/app/models/host.rb b/wui/src/app/models/host.rb index 6917226..3413370 100644 --- a/wui/src/app/models/host.rb +++ b/wui/src/app/models/host.rb @@ -1,4 +1,4 @@ -# +# # Copyright (C) 2008 Red Hat, Inc. # Written by Scott Seago <sseago at redhat.com> # @@ -21,8 +21,11 @@ require 'util/ovirt' class Host < ActiveRecord::Base belongs_to :hardware_pool + + has_many :cpus has_many :nics, :dependent => :destroy has_many :vms, :dependent => :nullify do + def consuming_resources find(:all, :conditions=>{:state=>Vm::RUNNING_STATES}) end @@ -32,7 +35,7 @@ class Host < ActiveRecord::Base find(:all, :conditions=>{:state=>Task::STATE_QUEUED}) end def pending_clear_tasks - find(:all, :conditions=>{:state=>Task::WORKING_STATES, + find(:all, :conditions=>{:state=>Task::WORKING_STATES, :action=>HostTask::ACTION_CLEAR_VMS}) end end @@ -42,12 +45,18 @@ class Host < ActiveRecord::Base STATE_UNAVAILABLE = "unavailable" STATE_AVAILABLE = "available" STATES = [STATE_UNAVAILABLE, STATE_AVAILABLE] + + KVM_HYPERVISOR_TYPE = "KVM" + HYPERVISOR_TYPES = [KVM_HYPERVISOR_TYPE] + def memory_in_mb kb_to_mb(memory) end + def memory_in_mb=(mem) self[:memory]=(mb_to_kb(mem)) end + def status_str "#{state} (#{disabled? ? 'disabled':'enabled'})" end @@ -61,4 +70,12 @@ class Host < ActiveRecord::Base not(disabled? and vms.consuming_resources.empty?) and tasks.pending_clear_tasks.empty? end + + def num_cpus + return cpu_details.size + end + + def cpu_speed + "FIX ME!" + end end diff --git a/wui/src/db/migrate/002_create_hosts.rb b/wui/src/db/migrate/002_create_hosts.rb index cef7996..442e674 100644 --- a/wui/src/db/migrate/002_create_hosts.rb +++ b/wui/src/db/migrate/002_create_hosts.rb @@ -23,8 +23,6 @@ class CreateHosts < ActiveRecord::Migration t.string :uuid t.string :hypervisor_type t.string :hostname - t.integer :num_cpus - t.integer :cpu_speed t.string :arch t.integer :memory t.integer :is_disabled diff --git a/wui/src/db/migrate/010_create_cpus.rb b/wui/src/db/migrate/010_create_cpus.rb new file mode 100644 index 0000000..381bf19 --- /dev/null +++ b/wui/src/db/migrate/010_create_cpus.rb @@ -0,0 +1,26 @@ +class CreateCpus < ActiveRecord::Migration + def self.up + create_table :cpus do |t| + t.integer :host_id + t.integer :cpu_number + t.integer :core_number + t.integer :number_of_cores + t.string :vendor, :limit => 128 + t.integer :model + t.integer :family + t.integer :cpuid_level + t.float :speed + t.string :cache + t.string :flags + + t.timestamps + end + + execute "alter table cpus add constraint fk_host_cpus + foreign key (host_id) references hosts(id)" + end + + def self.down + drop_table :cpus + end +end diff --git a/wui/src/dutils/active_record_env.rb b/wui/src/dutils/active_record_env.rb index b0aef04..72feb89 100644 --- a/wui/src/dutils/active_record_env.rb +++ b/wui/src/dutils/active_record_env.rb @@ -1,5 +1,5 @@ #!/usr/bin/ruby -# +# # Copyright (C) 2008 Red Hat, Inc. # Written by Scott Seago <sseago at redhat.com> # @@ -23,7 +23,7 @@ $: << File.join(File.dirname(__FILE__), "../vendor/plugins/betternestedset/lib") require 'rubygems' -gem 'activeldap' +gem 'activeldap' require 'active_ldap' require 'active_support' @@ -58,6 +58,7 @@ require 'models/quota.rb' require 'models/hardware_pool.rb' require 'models/host.rb' +require 'models/cpu.rb' require 'models/nic.rb' require 'models/vm_resource_pool.rb' diff --git a/wui/src/host-browser/host-browser.rb b/wui/src/host-browser/host-browser.rb index 691e4ed..e66493d 100755 --- a/wui/src/host-browser/host-browser.rb +++ b/wui/src/host-browser/host-browser.rb @@ -19,6 +19,7 @@ # also available at http://www.gnu.org/copyleft/gpl.html. $: << File.join(File.dirname(__FILE__), "../dutils") +$: << File.join(File.dirname(__FILE__), "../") require 'rubygems' require 'libvirt' @@ -73,16 +74,61 @@ class HostBrowser # def get_remote_info puts "#{@log_prefix} Begin remote info collection" unless defined?(TESTING) - result = {} + result = Hash.new result['HOSTNAME'] = @session.peeraddr[2] result['IPADDR'] = @session.peeraddr[3] + @session.write("INFO?\n") loop do info = @session.readline.chomp + puts "Received info='#{info}'" + break if info == "ENDINFO" + # if we got the start of a CPU details marker, then process it + if info == "CPU" + cpu = get_cpu_info + cpu_info = result['CPUINFO'] + + if(cpu_info == nil) + cpu_info = Array.new + result['CPUINFO'] = cpu_info + end + + cpu_info << cpu + + else + + 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 + end + + return result + end + + # Extracts CPU details from the managed node. + # + def get_cpu_info + puts "Begin receiving CPU details" + + result = Hash.new + + @session.write("CPUINFO?\n") + + loop do + info = @session.readline.chomp + + break if info == "ENDCPU" + raise Exception.new("ERRINFO! Excepted key=value : #{info}\n") unless info =~ /[\w]+[\s]*=[\w]/ key, value = info.split("=") @@ -93,6 +139,8 @@ class HostBrowser @session.write("ACK #{key}\n") end + @session.write("ACK CPU\n"); + return result end @@ -100,10 +148,24 @@ class HostBrowser # def write_host_info(host_info) ensure_present(host_info,'HOSTNAME') - ensure_present(host_info,'NUMCPUS') - ensure_present(host_info,'CPUSPEED') ensure_present(host_info,'ARCH') ensure_present(host_info,'MEMSIZE') + ensure_present(host_info,'CPUINFO') + + cpu_info = host_info['CPUINFO'] + + cpu_info.each do |cpu| + ensure_present(cpu,'CPUNUM') + ensure_present(cpu,'CORENUM') + ensure_present(cpu,'NUMCORES') + ensure_present(cpu,'VENDOR') + ensure_present(cpu,'MODEL') + ensure_present(cpu,'FAMILY') + ensure_present(cpu,'CPUIDLVL') + ensure_present(cpu,'SPEED') + ensure_present(cpu,'CACHE') + ensure_present(cpu,'FLAGS') + end puts "Searching for existing host record..." unless defined?(TESTING) host = Host.find(:first, :conditions => ["hostname = ?", host_info['HOSTNAME']]) @@ -112,31 +174,49 @@ class HostBrowser begin puts "Creating a new record for #{host_info['HOSTNAME']}..." unless defined?(TESTING) - Host.new( + host = Host.create( "uuid" => host_info['UUID'], "hostname" => host_info['HOSTNAME'], "hypervisor_type" => host_info['HYPERVISOR_TYPE'], - "num_cpus" => host_info['NUMCPUS'], - "cpu_speed" => host_info['CPUSPEED'], "arch" => host_info['ARCH'], "memory_in_mb" => host_info['MEMSIZE'], "is_disabled" => 0, "hardware_pool" => HardwarePool.get_default_pool, # Let host-status mark it available when it # successfully connects to it via libvirt. - "state" => Host::STATE_UNAVAILABLE).save + "state" => Host::STATE_UNAVAILABLE) + host.save! rescue Exception => error puts "Error while creating record: #{error.message}" unless defined?(TESTING) end else host.uuid = host_info['UUID'] host.hostname = host_info['HOSTNAME'] - host.num_cpus = host_info['NUMCPUS'] - host.cpu_speed = host_info['CPUSPEED'] host.arch = host_info['ARCH'] host.memory_in_mb = host_info['MEMSIZE'] end + # delete an existing CPUs and create new ones based on the data + puts "Deleting any existing CPUs" + Cpu.delete_all(['host_id = ?', host.id]) + + puts "Saving new CPU records" + cpu_info.collect do |cpu| + detail = Cpu.new( + "cpu_number" => cpu['CPUNUM'], + "core_number" => cpu['CORENUM]'], + "number_of_cores" => cpu['NUMCORES'], + "vendor" => cpu['VENDOR'], + "model" => cpu['MODEL'], + "family" => cpu['FAMILY'], + "cpuid_level" => cpu['CPUIDLVL'], + "speed" => cpu['SPEED'], + "cache" => cpu['CACHE'], + "flags" => cpu['FLAGS']) + + host.cpus << detail + end + return host end @@ -181,8 +261,8 @@ class HostBrowser # Private method to ensure that a required field is present. # - def ensure_present(host_info,key) - raise Exception.new("ERROR! Missing '#{key}'...") if host_info[key] == nil + def ensure_present(info,key) + raise Exception.new("ERROR! Missing '#{key}'...") if info[key] == nil end # Executes an external program to support the keytab function. @@ -226,7 +306,6 @@ def entry_point(server) end unless defined?(TESTING) - # The main entry point. # unless ARGV[0] == "-n" diff --git a/wui/src/host-browser/test-host-browser-awake.rb b/wui/src/host-browser/test-host-browser-awake.rb new file mode 100755 index 0000000..02e9146 --- /dev/null +++ b/wui/src/host-browser/test-host-browser-awake.rb @@ -0,0 +1,94 @@ +#!/usr/bin/ruby -Wall +# +# Copyright (C) 2008 Red Hat, Inc. +# Written by Darryl L. Pierce <dpierce at redhat.com> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. A copy of the GNU General Public License is +# also available at http://www.gnu.org/copyleft/gpl.html. + +require File.dirname(__FILE__) + '/../test/test_helper' +require 'test/unit' +require 'flexmock/test_unit' + +TESTING=true + +require 'host-browser' + +# +TestHostBrowserAwaken+ +class TestHostBrowserAwaken < Test::Unit::TestCase + + def setup + @session = flexmock('session') + @session.should_receive(:peeraddr).at_least.once.returns { [nil,nil,nil,"192.168.2.255"] } + + @krb5 = flexmock('krb5') + + @browser = HostBrowser.new(@session) + @browser.logfile = './unit-test.log' + @browser.keytab_dir = '/var/temp/' + end + + # Ensures that the server raises an exception when it receives an + # improper handshake response. + # + def test_begin_conversation_with_improper_response_to_greeting + @session.should_receive(:write).with("HELLO?\n").once().returns { |greeting| greeting.length } + @session.should_receive(:readline).once().returns { "SUP?" } + + assert_raise(Exception) { @browser.begin_conversation } + end + + # Ensures the server accepts a proper response from the remote system. + # + def test_begin_conversation + @session.should_receive(:write).with("HELLO?\n").once().returns { |greeting| greeting.length } + @session.should_receive(:readline).once().returns { "HELLO!\n" } + + assert_nothing_raised(Exception) { @browser.begin_conversation } + end + + # Ensures that the server is satisfied if the remote system is + # making a wakeup call. + # + def test_get_mode_with_awaken_request + @session.should_receive(:write).with("MODE?\n").once().returns { |request| request.length } + @session.should_receive(:readline).once().returns { "AWAKEN\n" } + + result = @browser.get_mode() + + assert_equal "AWAKEN", result, "method did not return the right value" + end + + # Ensures the host browser generates a keytab as expected. + # + def test_create_keytab + @krb5.should_receive(:get_default_realm).once().returns { "ovirt-test-realm" } + servername = `hostname -f`.chomp + @session.should_receive(:write).with("KTAB http://#{servername}/ipa/config/127.0.0.1-libvirt.tab\n").once().returns { |request| request.length } + @session.should_receive(:readline).once().returns { "ACK\n" } + + assert_nothing_raised(Exception) { @browser.create_keytab('localhost','127.0.0.1', at krb5) } + end + + # Ensures that, if a keytab is present and a key version number available, + # the server ends the conversation by returning the key version number. + # + def test_end_conversation + @session.should_receive(:write).with("BYE\n").once().returns { |request| request.length } + + assert_nothing_raised(Exception) { @browser.end_conversation } + end + +end diff --git a/wui/src/host-browser/test-host-browser-awaken.rb b/wui/src/host-browser/test-host-browser-awaken.rb deleted file mode 100755 index a5ca2e7..0000000 --- a/wui/src/host-browser/test-host-browser-awaken.rb +++ /dev/null @@ -1,94 +0,0 @@ -#!/usr/bin/ruby -Wall -# -# Copyright (C) 2008 Red Hat, Inc. -# Written by Darryl L. Pierce <dpierce at redhat.com> -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; version 2 of the License. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301, USA. A copy of the GNU General Public License is -# also available at http://www.gnu.org/copyleft/gpl.html. - -require File.dirname(__FILE__) + '/../test/test_helper' -require 'test/unit' -require 'flexmock/test_unit' - -TESTING=true - -require 'host-browser' - -# +TestHostBrowserAwaken+ -class TestHostBrowserAwaken < Test::Unit::TestCase - - def setup - @session = flexmock('session') - @session.should_receive(:peeraddr).at_least.once.returns { [nil,nil,nil,"192.168.2.255"] } - - @krb5 = flexmock('krb5') - - @browser = HostBrowser.new(@session) - @browser.logfile = './unit-test.log' - @browser.keytab_dir = '/var/temp/' - end - - # Ensures that the server raises an exception when it receives an - # improper handshake response. - # - def test_begin_conversation_with_improper_response_to_greeting - @session.should_receive(:write).with("HELLO?\n").once().returns { |greeting| greeting.length } - @session.should_receive(:readline).once().returns { "SUP?" } - - assert_raise(Exception) { @browser.begin_conversation } - end - - # Ensures the server accepts a proper response from the remote system. - # - def test_begin_conversation - @session.should_receive(:write).with("HELLO?\n").once().returns { |greeting| greeting.length } - @session.should_receive(:readline).once().returns { "HELLO!\n" } - - assert_nothing_raised(Exception) { @browser.begin_conversation } - end - - # Ensures that the server is satisfied if the remote system is - # making a wakeup call. - # - def test_get_mode_with_awaken_request - @session.should_receive(:write).with("MODE?\n").once().returns { |request| request.length } - @session.should_receive(:readline).once().returns { "AWAKEN\n" } - - result = @browser.get_mode() - - assert_equal "AWAKEN", result, "method did not return the right value" - end - - # Ensures the host browser generates a keytab as expected. - # - def test_create_keytab - @krb5.should_receive(:get_default_realm).once().returns { "ovirt-test-realm" } - servername = `hostname -f`.chomp - @session.should_receive(:write).with("KTAB http://#{servername}/config/127.0.0.1-libvirt.tab\n").once().returns { |request| request.length } - @session.should_receive(:readline).once().returns { "ACK\n" } - - assert_nothing_raised(Exception) { @browser.create_keytab('localhost','127.0.0.1', at krb5) } - end - - # Ensures that, if a keytab is present and a key version number available, - # the server ends the conversation by returning the key version number. - # - def test_end_conversation - @session.should_receive(:write).with("BYE\n").once().returns { |request| request.length } - - assert_nothing_raised(Exception) { @browser.end_conversation } - end - -end diff --git a/wui/src/host-browser/test-host-browser-identify.rb b/wui/src/host-browser/test-host-browser-identify.rb index 8f215e9..4197e19 100755 --- a/wui/src/host-browser/test-host-browser-identify.rb +++ b/wui/src/host-browser/test-host-browser-identify.rb @@ -27,136 +27,219 @@ TESTING=true require 'host-browser' class TestHostBrowser < Test::Unit::TestCase - - def setup - @session = flexmock('session') - @session.should_receive(:peeraddr).at_least.once.returns { [nil,nil,nil,"192.168.2.255"] } - - @browser = HostBrowser.new(@session) - @browser.logfile = './unit-test.log' - - # default host info - @host_info = {} - @host_info['UUID'] = 'node1' - @host_info['IPADDR'] = '192.168.2.2' - @host_info['HOSTNAME'] = 'node1.ovirt.redhat.com' - @host_info['NUMCPUS'] = '3' - @host_info['CPUSPEED'] = '3' - @host_info['ARCH'] = 'x86_64' - @host_info['MEMSIZE'] = '16384' - @host_info['DISABLED'] = '0' - end - - # Ensures that the server is satisfied if the remote system is - # making a wakeup call. - # - def test_get_mode_with_awaken_request - @session.should_receive(:write).with("MODE?\n").once().returns { |request| request.length } - @session.should_receive(:readline).once().returns { "IDENTIFY\n" } - - result = @browser.get_mode() - - assert_equal "IDENTIFY", result, "method did not return the right value" - end - - # Ensures that, if an info field is missing a key, the server raises - # an exception. - # - def test_get_info_with_missing_key - @session.should_receive(:write).with("INFO?\n").once().returns { |request| request.length } - @session.should_receive(:readline).once().returns { "=value1\n" } - - assert_raise(Exception) { @browser.get_remote_info } - end - - # Ensures that, if an info field is missing a value, the server raises - # an exception. - # - def test_get_info_with_missing_value - @session.should_receive(:write).with("INFO?\n").once().returns { |request| request.length } - @session.should_receive(:readline).once().returns { "key1=\n" } - - assert_raise(Exception) { @browser.get_remote_info } - end - - # Ensures that, if the server gets a poorly formed ending statement, it - # raises an exception. - # - def test_get_info_with_invalid_end - @session.should_receive(:write).with("INFO?\n").once().returns { |request| request.length } - @session.should_receive(:readline).once().returns { "key1=value1\n" } - @session.should_receive(:write).with("ACK key1\n").once().returns { |request| request.length } - @session.should_receive(:readline).once().returns { "ENDIFNO\n" } - - assert_raise(Exception) { @browser.get_remote_info } - end - - # Ensures that a well-formed transaction works as expected. - # - def test_get_info - @session.should_receive(:write).with("INFO?\n").once().returns { |request| request.length } - @session.should_receive(:readline).once().returns { "key1=value1\n" } - @session.should_receive(:write).with("ACK key1\n").once().returns { |request| request.length } - @session.should_receive(:readline).once().returns { "key2=value2\n" } - @session.should_receive(:write).with("ACK key2\n").once().returns { |request| request.length } - @session.should_receive(:readline).once().returns { "ENDINFO\n" } - - info = @browser.get_remote_info - - assert_equal 4,info.keys.size, "Should contain two keys" - assert info.include?("IPADDR") - assert info.include?("HOSTNAME") - assert info.include?("key1") - assert info.include?("key2") - end - - # Ensures that the server is fine when no UUID is present. - # - def test_write_host_info_with_missing_uuid - @host_info['UUID'] = nil - - assert_nothing_raised { @browser.write_host_info(@host_info) } - end - - # Ensures that, if the hostname is missing, the server - # raises an exception. - # - def test_write_host_info_with_missing_hostname - @host_info['HOSTNAME'] = nil - - assert_raise(Exception) { @browser.write_host_info(@host_info) } - end - - # Ensures that, if the number of CPUs is missing, the server raises an exception. - # - def test_write_host_info_with_missing_numcpus - @host_info['NUMCPUS'] = nil - - assert_raise(Exception) { @browser.write_host_info(@host_info) } - end - - # Ensures that, if the CPU speed is missing, the server raises an exception. - # - def test_write_host_info_with_missing_cpuspeed - @host_info['CPUSPEED'] = nil - - assert_raise(Exception) { @browser.write_host_info(@host_info) } - end - - # Ensures that, if the architecture is missing, the server raises an exception. - # - def test_write_host_info_with_missing_arch - @host_info['ARCH'] = nil - - assert_raise(Exception) { @browser.write_host_info(@host_info) } - end - - # Ensures that, if the memory size is missing, the server raises an exception. - # - def test_write_host_info_info_with_missing_memsize - @host_info['MEMSIZE'] = nil - - assert_raise(Exception) { @browser.write_host_info(@host_info) } - end + def setup + @session = flexmock('session') + @session.should_receive(:peeraddr).at_least.once.returns { [nil,nil,nil,"192.168.2.255"] } + + @browser = HostBrowser.new(@session) + @browser.logfile = './unit-test.log' + + # default host info + @host_info = {} + @host_info['UUID'] = 'node1' + @host_info['IPADDR'] = '192.168.2.2' + @host_info['HOSTNAME'] = 'node1.ovirt.redhat.com' + @host_info['ARCH'] = 'x86_64' + @host_info['MEMSIZE'] = '16384' + @host_info['DISABLED'] = '0' + + @host_info['NUMCPUS'] = '2' + + @host_info['CPUINFO'] = Array.new + @host_info['CPUINFO'][0] = {} + @host_info['CPUINFO'][0]['CPUNUM'] = '0' + @host_info['CPUINFO'][0]['CORENUM'] = '0' + @host_info['CPUINFO'][0]['NUMCORES'] = '2' + @host_info['CPUINFO'][0]['VENDOR'] = 'GenuineIntel' + @host_info['CPUINFO'][0]['MODEL'] = '15' + @host_info['CPUINFO'][0]['FAMILY'] = '6' + @host_info['CPUINFO'][0]['CPUIDLVL'] = '10' + @host_info['CPUINFO'][0]['SPEED'] = '3' + @host_info['CPUINFO'][0]['CACHE'] = '4096 kb' + @host_info['CPUINFO'][0]['FLAGS'] = 'fpu vme de pse tsc msr pae \ + 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['CPUINFO'][1] = {} + @host_info['CPUINFO'][1]['CPUNUM'] = '1' + @host_info['CPUINFO'][1]['CORENUM'] = '1' + @host_info['CPUINFO'][1]['NUMCORES'] = '2' + @host_info['CPUINFO'][1]['VENDOR'] = 'GenuineIntel' + @host_info['CPUINFO'][1]['MODEL'] = '15' + @host_info['CPUINFO'][1]['FAMILY'] = '6' + @host_info['CPUINFO'][1]['CPUIDLVL'] = '10' + @host_info['CPUINFO'][1]['SPEED'] = '3' + @host_info['CPUINFO'][1]['CACHE'] = '4096 kb' + @host_info['CPUINFO'][1]['FLAGS'] = 'fpu vme de pse tsc msr pae \ + 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' + end + + # Ensures that the server is satisfied if the remote system is + # making a wakeup call. + # + def test_get_mode_with_awaken_request + @session.should_receive(:write).with("MODE?\n").once().returns { |request| request.length } + @session.should_receive(:readline).once().returns { "IDENTIFY\n" } + + result = @browser.get_mode() + + assert_equal "IDENTIFY", result, "method did not return the right value" + end + + # Ensures that, if an info field is missing a key, the server raises + # an exception. + # + def test_get_info_with_missing_key + @session.should_receive(:write).with("INFO?\n").once().returns { |request| request.length } + @session.should_receive(:readline).once().returns { "=value1\n" } + + assert_raise(Exception) { @browser.get_remote_info } + end + + # Ensures that, if an info field is missing a value, the server raises + # an exception. + # + def test_get_info_with_missing_value + @session.should_receive(:write).with("INFO?\n").once().returns { |request| request.length } + @session.should_receive(:readline).once().returns { "key1=\n" } + + assert_raise(Exception) { @browser.get_remote_info } + end + + # Ensures that, if the server gets a poorly formed ending statement, it + # raises an exception. + # + def test_get_info_with_invalid_end + @session.should_receive(:write).with("INFO?\n").once().returns { |request| request.length } + @session.should_receive(:readline).once().returns { "key1=value1\n" } + @session.should_receive(:write).with("ACK key1\n").once().returns { |request| request.length } + @session.should_receive(:readline).once().returns { "ENDIFNO\n" } + + assert_raise(Exception) { @browser.get_remote_info } + end + + # Ensures that a well-formed transaction works as expected. + # + def test_get_info + @session.should_receive(:write).with("INFO?\n").once().returns { |request| request.length } + @session.should_receive(:readline).once().returns { "key1=value1\n" } + @session.should_receive(:write).with("ACK key1\n").once().returns { |request| request.length } + @session.should_receive(:readline).once().returns { "key2=value2\n" } + @session.should_receive(:write).with("ACK key2\n").once().returns { |request| request.length } + @session.should_receive(:readline).once().returns { "ENDINFO\n" } + + info = @browser.get_remote_info + + assert_equal 4,info.keys.size, "Should contain four keys" + assert info.include?("IPADDR") + assert info.include?("HOSTNAME") + assert info.include?("key1") + assert info.include?("key2") + end + + # Ensures that the server is fine when no UUID is present. + # + def test_write_host_info_with_missing_uuid + @host_info['UUID'] = nil + + assert_nothing_raised { @browser.write_host_info(@host_info) } + end + + # Ensures that, if the hostname is missing, the server + # raises an exception. + # + def test_write_host_info_with_missing_hostname + @host_info['HOSTNAME'] = nil + + assert_raise(Exception) { @browser.write_host_info(@host_info) } + end + + # Ensures that, if the architecture is missing, the server raises an + # exception. + # + def test_write_host_info_with_missing_arch + @host_info['ARCH'] = nil + + assert_raise(Exception) { @browser.write_host_info(@host_info) } + end + + # Ensures that, if the memory size is missing, the server raises an + # exception. + # + def test_write_host_info_info_with_missing_memsize + @host_info['MEMSIZE'] = nil + + assert_raise(Exception) { @browser.write_host_info(@host_info) } + end + + # Ensures that, if no cpu info was available, the server raises an + # exception. + # + def test_write_host_info_with_missing_cpuinfo + @host_info['CPUINFO'] = 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 + @session.should_receive(:write).with("INFO?\n").once().returns { |request| request.length } + @session.should_receive(:readline).once().returns { "CPU\n" } + @session.should_receive(:write).with("CPUINFO?\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 { "ENDCPU\n" } + @session.should_receive(:write).with("ACK CPU\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?("CPUINFO") + end + + # Ensures the browser can properly parse the CPU details of two CPUs. + # + def test_parse_cpu_info + @session.should_receive(:write).with("INFO?\n").once().returns { |request| request.length } + + # CPU 0 + @session.should_receive(:readline).once().returns { "CPU\n" } + @session.should_receive(:write).with("CPUINFO?\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 { "ENDCPU\n" } + @session.should_receive(:write).with("ACK CPU\n").once().returns { |request| request.length } + + # CPU 1 + @session.should_receive(:readline).once().returns { "CPU\n" } + @session.should_receive(:write).with("CPUINFO?\n").once().returns { |request| request.length } + @session.should_receive(:readline).once().returns { "key3=value3\n" } + @session.should_receive(:write).with("ACK key3\n").once().returns { |request| request.length } + @session.should_receive(:readline).once().returns { "key4=value4\n" } + @session.should_receive(:write).with("ACK key4\n").once().returns { |request| request.length } + @session.should_receive(:readline).once().returns { "ENDCPU\n" } + @session.should_receive(:write).with("ACK CPU\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?('CPUINFO') + assert_equal 2, info['CPUINFO'].size, "Should contain details for two CPUs" + assert_not_nil info['CPUINFO'][0]['key1'] + assert_not_nil info['CPUINFO'][0]['key2'] + assert_not_nil info['CPUINFO'][1]['key3'] + assert_not_nil info['CPUINFO'][1]['key4'] + end end diff --git a/wui/src/test/fixtures/cpus.yml b/wui/src/test/fixtures/cpus.yml new file mode 100644 index 0000000..5586303 --- /dev/null +++ b/wui/src/test/fixtures/cpus.yml @@ -0,0 +1,21 @@ +# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html + +one: + cpu_number: 1 + core_number: 1 + cpu_id_level: 1 + vendor: 1 + family: 1 + model: 1 + family: MyString + flags: MyString + +two: + cpu_number: 1 + core_number: 1 + cpu_id_level: 1 + vendor: 1 + family: 1 + model: 1 + family: MyString + flags: MyString diff --git a/wui/src/test/fixtures/hosts.yml b/wui/src/test/fixtures/hosts.yml index 3b93aea..f10a756 100644 --- a/wui/src/test/fixtures/hosts.yml +++ b/wui/src/test/fixtures/hosts.yml @@ -2,8 +2,6 @@ one: id: 1 uuid: '1148fdf8-961d-11dc-9387-001558c41534' hostname: 'prod.corp.com' - num_cpus: 8 - cpu_speed: 4096 arch: 'i686' memory: 18384 is_disabled: 0 @@ -13,8 +11,6 @@ two: id: 2 uuid: '1f2a8694-961d-11dc-9387-001558c41534' hostname: 'myworkstation.dev.corp.com' - num_cpus: 4 - cpu_speed: 2048 arch: 'i386' memory: 2048 is_disabled: 0 @@ -24,30 +20,24 @@ three: id: 3 uuid: '58a85f44-75fd-4934-805f-88e45b40d4b4' hostname: 'macworkstation.foobar.com' - num_cpus: 8 - cpu_speed: 1024 arch: 'mips' memory: 2048 - is_disabled: 0 + is_disabled: 0 hypervisor_type: 'kvm' hardware_pool_id: 3 four: id: 4 uuid: '520bbb34-6515-490e-9d07-0c8b14f76805' hostname: 'fedoraworkstation.foobar.com' - num_cpus: 8 - cpu_speed: 3072 arch: 'i386' memory: 2048 - is_disabled: 1 + is_disabled: 1 hypervisor_type: 'kvm' hardware_pool_id: 3 five: id: 5 uuid: '2e422f66-324e-48d4-973f-0b91b33070f9' hostname: 'pipeline.foobar.com' - num_cpus: 32 - cpu_speed: 4096 arch: 'xeon' memory: 1048576 is_disabled: 0 @@ -57,19 +47,15 @@ six: id: 6 uuid: 'bb0ce7c7-f234-49ae-84b5-6f4fd8bddcaa' hostname: 'prod.foobar.com' - num_cpus: 32 - cpu_speed: 4096 arch: 'xeon' memory: 16384 - is_disabled: 0 + is_disabled: 0 hypervisor_type: 'kvm' hardware_pool_id: 3 seven: id: 7 uuid: '6ae3d22e-97e0-4d86-9712-5395b20a0f45' hostname: 'mystation.dev.corp.com' - num_cpus: 4 - cpu_speed: 3072 arch: 'i686' memory: 2048 is_disabled: 0 @@ -79,8 +65,6 @@ eight: id: 8 uuid: 'ec0d86de-657b-48f6-b7cc-e733a3f9a834' hostname: 'issue.qa.corp.com' - num_cpus: 8 - cpu_speed: 4096 arch: 'x86' memory: 4096 is_disabled: 0 @@ -90,8 +74,6 @@ nine: id: 9 uuid: '81c15560-dbf4-45f5-9b75-106cdbd63aeb' hostname: 'somehost' - num_cpus: 4 - cpu_speed: 4096 arch: 'x86' memory: 4096 is_disabled: 0 diff --git a/wui/src/test/unit/cpu_test.rb b/wui/src/test/unit/cpu_test.rb new file mode 100644 index 0000000..5b64ca7 --- /dev/null +++ b/wui/src/test/unit/cpu_test.rb @@ -0,0 +1,8 @@ +require File.dirname(__FILE__) + '/../test_helper' + +class CpuTest < ActiveSupport::TestCase + # Replace this with your real tests. + def test_truth + assert true + end +end -- 1.5.5.1
Scott Seago
2008-Jul-08 13:26 UTC
[Ovirt-devel] [PATCH] ovirt-identify-node now submits all extended CPU details.
Darryl L. Pierce wrote:> diff --git a/wui/src/app/models/host.rb b/wui/src/app/models/host.rb > index 6917226..3413370 100644 > --- a/wui/src/app/models/host.rb > +++ b/wui/src/app/models/host.rb > @@ -21,8 +21,11 @@ require 'util/ovirt' > > class Host < ActiveRecord::Base > belongs_to :hardware_pool > + > + has_many :cpus > has_many :nics, :dependent => :destroy > has_many :vms, :dependent => :nullify do > + > def consuming_resources > find(:all, :conditions=>{:state=>Vm::RUNNING_STATES}) > end >Should probably add ':dependent => :destroy' here like we do for nics to handle automatic deletion of the CPU objects when the host is deleted.> @@ -42,12 +45,18 @@ class Host < ActiveRecord::Base > STATE_UNAVAILABLE = "unavailable" > STATE_AVAILABLE = "available" > STATES = [STATE_UNAVAILABLE, STATE_AVAILABLE] > + > + KVM_HYPERVISOR_TYPE = "KVM" > + HYPERVISOR_TYPES = [KVM_HYPERVISOR_TYPE] > + > def memory_in_mb > kb_to_mb(memory) > end > + > def memory_in_mb=(mem) > self[:memory]=(mb_to_kb(mem)) > end > + > def status_str > "#{state} (#{disabled? ? 'disabled':'enabled'})" > end > @@ -61,4 +70,12 @@ class Host < ActiveRecord::Base > not(disabled? and vms.consuming_resources.empty?) and > tasks.pending_clear_tasks.empty? > end > + > + def num_cpus > + return cpu_details.size > + end > + > + def cpu_speed > + "FIX ME!" > + end > end >I'm assuming cpu_speed above is so that the WUI view code doesn't break with this change. This is a question for Hugh or Tim -- have we determined what the UI should look like for this? Off the top of my head, we leave num_cpus in the host flexigrid tables and just remove cpu_speed. Then in the host detail pane we add a table (not a flexigrid) with the enumerated CPU details. Do we show all 10 attributes per CPU or just some of them?> diff --git a/wui/src/db/migrate/002_create_hosts.rb b/wui/src/db/migrate/002_create_hosts.rb > index cef7996..442e674 100644 > --- a/wui/src/db/migrate/002_create_hosts.rb > +++ b/wui/src/db/migrate/002_create_hosts.rb > @@ -23,8 +23,6 @@ class CreateHosts < ActiveRecord::Migration > t.string :uuid > t.string :hypervisor_type > t.string :hostname > - t.integer :num_cpus > - t.integer :cpu_speed > t.string :arch > t.integer :memory > t.integer :is_disabled >I think we had agreed that after the beta we'd track schema changes by adding new migrations rather than modifying existing ones. Hugh -- is this correct, or are we still doing it this way? If we are adding new migrations, then we'd need the above done as something like 11_remove_cpu_from_hosts.rb or whatever you want to call it. Another point we'll need to clarify -- if we're doing incremental migrations, do we have to handle existing data too? Or can we explicitly _not_ support live data migrations until later? OK, that's it for my comments here -- I'll let someone on the managed node side look at/test the back end bits. Scott
Darryl L. Pierce
2008-Jul-08 14:06 UTC
[Ovirt-devel] [PATCH] ovirt-identify-node now submits all extended CPU details.
From: Darryl Pierce <dpierce at redhat.com> Signed-off-by: Darryl L. Pierce <dpierce at redhat.com> --- ovirt-managed-node/src/ovirt-identify-node.c | 420 +++++++++++++++----- ovirt-managed-node/src/ovirt-identify-node.h | 56 +++ wui/src/app/models/cpu.rb | 24 ++ wui/src/app/models/host.rb | 23 +- wui/src/db/migrate/010_create_cpus.rb | 32 ++ wui/src/dutils/active_record_env.rb | 5 +- wui/src/host-browser/host-browser.rb | 103 +++++- wui/src/host-browser/test-host-browser-awake.rb | 94 +++++ wui/src/host-browser/test-host-browser-awaken.rb | 94 ----- wui/src/host-browser/test-host-browser-identify.rb | 345 ++++++++++------ wui/src/test/fixtures/cpus.yml | 21 + wui/src/test/fixtures/hosts.yml | 24 +- wui/src/test/unit/cpu_test.rb | 8 + 13 files changed, 881 insertions(+), 368 deletions(-) create mode 100644 ovirt-managed-node/src/ovirt-identify-node.h create mode 100644 wui/src/app/models/cpu.rb create mode 100644 wui/src/db/migrate/010_create_cpus.rb create mode 100755 wui/src/host-browser/test-host-browser-awake.rb delete mode 100755 wui/src/host-browser/test-host-browser-awaken.rb create mode 100644 wui/src/test/fixtures/cpus.yml create mode 100644 wui/src/test/unit/cpu_test.rb diff --git a/ovirt-managed-node/src/ovirt-identify-node.c b/ovirt-managed-node/src/ovirt-identify-node.c index 819f700..f114d81 100644 --- a/ovirt-managed-node/src/ovirt-identify-node.c +++ b/ovirt-managed-node/src/ovirt-identify-node.c @@ -31,31 +31,7 @@ #include <sys/types.h> #include <sys/socket.h> -int config(int argc,char** argv); -void usage(void); - -int start_conversation(void); -int send_details(void); -int end_conversation(void); - -int send_text(char* text); -int get_text(const char *const expected); -int create_connection(void); - -int debug = 1; -int verbose = 1; -int testing = 0; - -#define BUFFER_LENGTH 128 - -char arch[BUFFER_LENGTH]; -char uuid[VIR_UUID_BUFLEN]; -char memsize[BUFFER_LENGTH]; -char numcpus[BUFFER_LENGTH]; -char cpuspeed[BUFFER_LENGTH]; -char *hostname; -int hostport = -1; -int socketfd; +#include "ovirt-identify-node.h" int main(int argc,char** argv) { @@ -63,9 +39,11 @@ int main(int argc,char** argv) virConnectPtr connection; virNodeInfo info; + fprintf(stdout,"Sending managed node details to server.\n"); + if(!config(argc,argv)) { - fprintf(stdout,"Connecting to libvirt.\n"); + if(verbose) fprintf(stdout,"Connecting to libvirt.\n"); connection = virConnectOpenReadOnly(testing ? "test:///default" : NULL); @@ -73,42 +51,68 @@ int main(int argc,char** argv) if(connection) { - if(debug) fprintf(stdout,"Getting hostname: %s\n", uuid); + if(verbose) fprintf(stdout,"Getting hostname: %s\n", uuid); if(!strlen(uuid)) gethostname(uuid,sizeof uuid); - if(debug) fprintf(stdout,"Retrieving node information.\n"); + 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); - snprintf(numcpus, BUFFER_LENGTH, "%d", info.cpus); - snprintf(cpuspeed, BUFFER_LENGTH, "%d", info.mhz); - if(debug) + cpu_info = NULL; + + if(!get_cpu_info()) { - fprintf(stdout,"Node Info:\n"); - fprintf(stdout," UUID: %s\n", uuid); - fprintf(stdout," Arch: %s\n", arch); - fprintf(stdout," Memory: %s\n", memsize); - fprintf(stdout," # CPUs: %s\n", numcpus); - fprintf(stdout,"CPU Speed: %s\n", cpuspeed); + if(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; + } } - - if(debug) fprintf(stdout, "Retrieved node information.\n"); - - if(!start_conversation() && !send_details() && !end_conversation()) + else { - result = 0; + if(verbose) fprintf(stderr,"Failed to get CPU info.\n"); } } else { - if(debug) fprintf(stderr,"Failed to get node info.\n"); + if(verbose) fprintf(stderr,"Failed to get node info.\n"); } } else { - if(debug) fprintf(stderr,"Could not connect to libvirt.\n"); + if(verbose) fprintf(stderr,"Could not connect to libvirt.\n"); } } else @@ -181,35 +185,35 @@ int start_conversation(void) { if(debug || verbose) fprintf(stdout,"Connected.\n"); - if (!get_text("HELLO?\n")) + if (!get_text("HELLO?")) { - if(debug) fprintf(stdout,"Checking for handshake.\n"); + if(verbose) fprintf(stdout,"Checking for handshake.\n"); - if(!send_text("HELLO!\n")) + if(!send_text("HELLO!")) { - if(debug) fprintf(stdout,"Handshake received. Starting conversation.\n"); + if(verbose) fprintf(stdout,"Handshake received. Starting conversation.\n"); - if(!get_text("MODE?\n")) + if(!get_text("MODE?")) { - if(debug) fprintf(stdout,"Shifting to IDENTIFY mode.\n"); + if(verbose) fprintf(stdout,"Shifting to IDENTIFY mode.\n"); - if(!send_text("IDENTIFY\n")) result = 0; + if(!send_text("IDENTIFY")) result = 0; } else { - if(debug) fprintf(stderr,"Was not asked for a mode.\n"); + if(verbose) fprintf(stderr,"Was not asked for a mode.\n"); } } } else { - if(debug) fprintf(stderr,"Did not receive a proper handshake.\n"); + if(verbose) fprintf(stderr,"Did not receive a proper handshake.\n"); } } else { - if(debug) fprintf(stderr,"Did not get a connection.\n"); + if(verbose) fprintf(stderr,"Did not get a connection.\n"); } if(debug) fprintf(stdout,"start_conversation: result=%d\n", result); @@ -223,19 +227,15 @@ int send_value(char* label,char* value) int result = 1; char expected[BUFFER_LENGTH]; - snprintf(buffer,BUFFER_LENGTH,"%s=%s\n", label, value); + snprintf(buffer,BUFFER_LENGTH,"%s=%s", label, value); if(!send_text(buffer)) { - snprintf(expected, BUFFER_LENGTH, "ACK %s\n", label); + snprintf(expected, BUFFER_LENGTH, "ACK %s", label); - if(debug) fprintf(stdout,"Expecting \"%s\"\n", expected); - - if (!get_text(expected)) - { - result = 0; - } + if(verbose) fprintf(stdout,"Expecting \"%s\"\n", expected); + result = get_text(expected); } return result; @@ -245,40 +245,238 @@ int send_details(void) { int result = 1; - fprintf(stdout,"Sending node details.\n"); + if(verbose) fprintf(stdout,"Sending node details.\n"); - if (!get_text("INFO?\n")) + if (!get_text("INFO?")) { if((!send_value("ARCH", arch)) && (!send_value("UUID", uuid)) && - (!send_value("NUMCPUS", numcpus)) && - (!send_value("CPUSPEED", cpuspeed)) && - (!send_value("MEMSIZE", memsize))) + (!send_value("MEMSIZE", memsize)) && + (!send_cpu_details())) { - if(!send_text("ENDINFO\n")) result = 0; + if(!send_text("ENDINFO")) result = 0; } } else { - if(debug) fprintf(stdout,"Was not interrogated for hardware info.\n"); + 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; - fprintf(stdout,"Ending conversation.\n"); + if(debug || verbose) fprintf(stdout,"Ending conversation.\n"); - send_text("ENDINFO\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; @@ -303,9 +501,10 @@ int send_text(char* text) int result = 1; int sent; - if(debug || verbose) fprintf(stdout,"\"%s\" -> %s:%d\n", text, hostname, hostport); + if(verbose) fprintf(stdout,"Sending: \"%s\"\n", text); sent = safewrite(socketfd, text, strlen(text)); + sent += safewrite(socketfd, "\n", 1); if(sent >= 0) { @@ -317,35 +516,46 @@ int send_text(char* text) return result; } -int saferead(int fd, void *buf, size_t count) +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(len_left > 0) { - bytes = read(fd, buf+offset, len_left); - fprintf(stderr,"After read, bytes is %ld\n",bytes); - if (bytes < 0) { - if (errno == EINTR) { - continue; - } - else { - offset = -1; - break; - } - } - else if (bytes == 0) { - // reached EOF; break out of here - break; - } - - offset += bytes; - len_left -= bytes; + + 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; @@ -353,22 +563,22 @@ int saferead(int fd, void *buf, size_t count) int get_text(const char *const expected) { + int result = 1; int received; char buffer[BUFFER_LENGTH]; + bzero(buffer,BUFFER_LENGTH); - if(debug) fprintf(stdout, "Looking to receive %s\n", expected); + if(verbose) fprintf(stdout, "Looking to receive %s\n", expected); - received = saferead(socketfd, buffer, strlen(expected)); + received = saferead(socketfd, buffer, BUFFER_LENGTH); buffer[received - 1] = 0; - if(debug) fprintf(stdout,"Received \"%s\": size=%d (trimmed ending carriage return)\n", buffer, received); + if(verbose) fprintf(stdout,"Received \"%s\": size=%d (trimmed ending carriage return)\n", buffer, received); - if (strncmp(expected, buffer, strlen(expected)) != 0) { - return 0; - } + result = strcmp(expected,buffer); - return 1; + return result; } int create_connection(void) @@ -379,7 +589,7 @@ int create_connection(void) char port[6]; struct addrinfo* rptr; - if(debug) fprintf(stdout,"Creating the socket connection.\n"); + if(verbose) fprintf(stdout,"Creating the socket connection.\n"); memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = AF_UNSPEC; @@ -387,13 +597,13 @@ int create_connection(void) hints.ai_flags = 0; hints.ai_protocol = 0; - if(debug) fprintf(stdout,"Searching for host candidates.\n"); + if(verbose) fprintf(stdout,"Searching for host candidates.\n"); snprintf(port, 6, "%d", hostport); if(!getaddrinfo(hostname, port, &hints, &results)) { - if(debug) fprintf(stdout,"Got address information. Searching for a proper entry.\n"); + if(verbose) fprintf(stdout,"Got address information. Searching for a proper entry.\n"); for(rptr = results; rptr != NULL; rptr = rptr->ai_next) { @@ -416,13 +626,13 @@ int create_connection(void) } // invalid connection, so close it - if(debug) fprintf(stdout, "Invalid connection.\n"); + if(verbose) fprintf(stdout, "Invalid connection.\n"); close(socketfd); } if(rptr == NULL) { - if(debug) fprintf(stdout,"Unable to connect to server %s:%d\n", hostname, hostport); + if(verbose) fprintf(stdout,"Unable to connect to server %s:%d\n", hostname, hostport); } else { @@ -434,7 +644,7 @@ int create_connection(void) } else { - if(debug) fprintf(stderr,"No hosts found. Exiting...\n"); + if(verbose) fprintf(stderr,"No hosts found. Exiting...\n"); } if(debug) fprintf(stdout, "create_connection: result=%d\n", result); diff --git a/ovirt-managed-node/src/ovirt-identify-node.h b/ovirt-managed-node/src/ovirt-identify-node.h new file mode 100644 index 0000000..1cb1526 --- /dev/null +++ b/ovirt-managed-node/src/ovirt-identify-node.h @@ -0,0 +1,56 @@ +#ifndef __OVIRT_IDENTIFY_NODE_H +#define __OVIRT_IDENTIFY_NODE_H + +#define BUFFER_LENGTH 128 +#define CPU_FLAGS_BUFFER_LENGTH 256 + +typedef struct _cpu_info { + char cpu_num[BUFFER_LENGTH]; + char core_num[BUFFER_LENGTH]; + char number_of_cores[BUFFER_LENGTH]; + char vendor[BUFFER_LENGTH]; + char model[BUFFER_LENGTH]; + char family[BUFFER_LENGTH]; + char cpuid_level[BUFFER_LENGTH]; + char speed[BUFFER_LENGTH]; + char cache[BUFFER_LENGTH]; + char flags[CPU_FLAGS_BUFFER_LENGTH]; + struct _cpu_info* next; +} t_cpu_info; + +#define COPY_VALUE_TO_BUFFER(value,buffer,length) \ + snprintf(buffer,length,"%s",value) + +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); +int create_connection(void); + +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; + +#endif diff --git a/wui/src/app/models/cpu.rb b/wui/src/app/models/cpu.rb new file mode 100644 index 0000000..1a7d3cf --- /dev/null +++ b/wui/src/app/models/cpu.rb @@ -0,0 +1,24 @@ +# +# 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. + +# +Cpu+ represents the details for a single CPU on a managed node. +# +class Cpu < ActiveRecord::Base + belongs_to :host +end diff --git a/wui/src/app/models/host.rb b/wui/src/app/models/host.rb index 6917226..90e18d5 100644 --- a/wui/src/app/models/host.rb +++ b/wui/src/app/models/host.rb @@ -1,4 +1,4 @@ -# +# # Copyright (C) 2008 Red Hat, Inc. # Written by Scott Seago <sseago at redhat.com> # @@ -21,8 +21,11 @@ require 'util/ovirt' class Host < ActiveRecord::Base belongs_to :hardware_pool + + has_many :cpus, :dependent => :destroy has_many :nics, :dependent => :destroy - has_many :vms, :dependent => :nullify do + has_many :vms, :dependent => :nullify do + def consuming_resources find(:all, :conditions=>{:state=>Vm::RUNNING_STATES}) end @@ -32,7 +35,7 @@ class Host < ActiveRecord::Base find(:all, :conditions=>{:state=>Task::STATE_QUEUED}) end def pending_clear_tasks - find(:all, :conditions=>{:state=>Task::WORKING_STATES, + find(:all, :conditions=>{:state=>Task::WORKING_STATES, :action=>HostTask::ACTION_CLEAR_VMS}) end end @@ -42,12 +45,18 @@ class Host < ActiveRecord::Base STATE_UNAVAILABLE = "unavailable" STATE_AVAILABLE = "available" STATES = [STATE_UNAVAILABLE, STATE_AVAILABLE] + + KVM_HYPERVISOR_TYPE = "KVM" + HYPERVISOR_TYPES = [KVM_HYPERVISOR_TYPE] + def memory_in_mb kb_to_mb(memory) end + def memory_in_mb=(mem) self[:memory]=(mb_to_kb(mem)) end + def status_str "#{state} (#{disabled? ? 'disabled':'enabled'})" end @@ -61,4 +70,12 @@ class Host < ActiveRecord::Base not(disabled? and vms.consuming_resources.empty?) and tasks.pending_clear_tasks.empty? end + + def num_cpus + return cpu_details.size + end + + def cpu_speed + "FIX ME!" + end end diff --git a/wui/src/db/migrate/010_create_cpus.rb b/wui/src/db/migrate/010_create_cpus.rb new file mode 100644 index 0000000..bd17274 --- /dev/null +++ b/wui/src/db/migrate/010_create_cpus.rb @@ -0,0 +1,32 @@ +class CreateCpus < ActiveRecord::Migration + def self.up + create_table :cpus do |t| + t.integer :host_id + t.integer :cpu_number + t.integer :core_number + t.integer :number_of_cores + t.string :vendor, :limit => 128 + t.integer :model + t.integer :family + t.integer :cpuid_level + t.float :speed + t.string :cache + t.string :flags + + t.timestamps + end + + execute "alter table cpus add constraint fk_host_cpus + foreign key (host_id) references hosts(id)" + + remove_column :hosts, :cpu_speed + remove_column :hosts, :num_cpus + end + + def self.down + drop_table :cpus + + add_column :hosts, :cpu_speed, :integer + add_column :hosts, :num_cpus, :integer + end +end diff --git a/wui/src/dutils/active_record_env.rb b/wui/src/dutils/active_record_env.rb index b0aef04..72feb89 100644 --- a/wui/src/dutils/active_record_env.rb +++ b/wui/src/dutils/active_record_env.rb @@ -1,5 +1,5 @@ #!/usr/bin/ruby -# +# # Copyright (C) 2008 Red Hat, Inc. # Written by Scott Seago <sseago at redhat.com> # @@ -23,7 +23,7 @@ $: << File.join(File.dirname(__FILE__), "../vendor/plugins/betternestedset/lib") require 'rubygems' -gem 'activeldap' +gem 'activeldap' require 'active_ldap' require 'active_support' @@ -58,6 +58,7 @@ require 'models/quota.rb' require 'models/hardware_pool.rb' require 'models/host.rb' +require 'models/cpu.rb' require 'models/nic.rb' require 'models/vm_resource_pool.rb' diff --git a/wui/src/host-browser/host-browser.rb b/wui/src/host-browser/host-browser.rb index 691e4ed..e66493d 100755 --- a/wui/src/host-browser/host-browser.rb +++ b/wui/src/host-browser/host-browser.rb @@ -19,6 +19,7 @@ # also available at http://www.gnu.org/copyleft/gpl.html. $: << File.join(File.dirname(__FILE__), "../dutils") +$: << File.join(File.dirname(__FILE__), "../") require 'rubygems' require 'libvirt' @@ -73,16 +74,61 @@ class HostBrowser # def get_remote_info puts "#{@log_prefix} Begin remote info collection" unless defined?(TESTING) - result = {} + result = Hash.new result['HOSTNAME'] = @session.peeraddr[2] result['IPADDR'] = @session.peeraddr[3] + @session.write("INFO?\n") loop do info = @session.readline.chomp + puts "Received info='#{info}'" + break if info == "ENDINFO" + # if we got the start of a CPU details marker, then process it + if info == "CPU" + cpu = get_cpu_info + cpu_info = result['CPUINFO'] + + if(cpu_info == nil) + cpu_info = Array.new + result['CPUINFO'] = cpu_info + end + + cpu_info << cpu + + else + + 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 + end + + return result + end + + # Extracts CPU details from the managed node. + # + def get_cpu_info + puts "Begin receiving CPU details" + + result = Hash.new + + @session.write("CPUINFO?\n") + + loop do + info = @session.readline.chomp + + break if info == "ENDCPU" + raise Exception.new("ERRINFO! Excepted key=value : #{info}\n") unless info =~ /[\w]+[\s]*=[\w]/ key, value = info.split("=") @@ -93,6 +139,8 @@ class HostBrowser @session.write("ACK #{key}\n") end + @session.write("ACK CPU\n"); + return result end @@ -100,10 +148,24 @@ class HostBrowser # def write_host_info(host_info) ensure_present(host_info,'HOSTNAME') - ensure_present(host_info,'NUMCPUS') - ensure_present(host_info,'CPUSPEED') ensure_present(host_info,'ARCH') ensure_present(host_info,'MEMSIZE') + ensure_present(host_info,'CPUINFO') + + cpu_info = host_info['CPUINFO'] + + cpu_info.each do |cpu| + ensure_present(cpu,'CPUNUM') + ensure_present(cpu,'CORENUM') + ensure_present(cpu,'NUMCORES') + ensure_present(cpu,'VENDOR') + ensure_present(cpu,'MODEL') + ensure_present(cpu,'FAMILY') + ensure_present(cpu,'CPUIDLVL') + ensure_present(cpu,'SPEED') + ensure_present(cpu,'CACHE') + ensure_present(cpu,'FLAGS') + end puts "Searching for existing host record..." unless defined?(TESTING) host = Host.find(:first, :conditions => ["hostname = ?", host_info['HOSTNAME']]) @@ -112,31 +174,49 @@ class HostBrowser begin puts "Creating a new record for #{host_info['HOSTNAME']}..." unless defined?(TESTING) - Host.new( + host = Host.create( "uuid" => host_info['UUID'], "hostname" => host_info['HOSTNAME'], "hypervisor_type" => host_info['HYPERVISOR_TYPE'], - "num_cpus" => host_info['NUMCPUS'], - "cpu_speed" => host_info['CPUSPEED'], "arch" => host_info['ARCH'], "memory_in_mb" => host_info['MEMSIZE'], "is_disabled" => 0, "hardware_pool" => HardwarePool.get_default_pool, # Let host-status mark it available when it # successfully connects to it via libvirt. - "state" => Host::STATE_UNAVAILABLE).save + "state" => Host::STATE_UNAVAILABLE) + host.save! rescue Exception => error puts "Error while creating record: #{error.message}" unless defined?(TESTING) end else host.uuid = host_info['UUID'] host.hostname = host_info['HOSTNAME'] - host.num_cpus = host_info['NUMCPUS'] - host.cpu_speed = host_info['CPUSPEED'] host.arch = host_info['ARCH'] host.memory_in_mb = host_info['MEMSIZE'] end + # delete an existing CPUs and create new ones based on the data + puts "Deleting any existing CPUs" + Cpu.delete_all(['host_id = ?', host.id]) + + puts "Saving new CPU records" + cpu_info.collect do |cpu| + detail = Cpu.new( + "cpu_number" => cpu['CPUNUM'], + "core_number" => cpu['CORENUM]'], + "number_of_cores" => cpu['NUMCORES'], + "vendor" => cpu['VENDOR'], + "model" => cpu['MODEL'], + "family" => cpu['FAMILY'], + "cpuid_level" => cpu['CPUIDLVL'], + "speed" => cpu['SPEED'], + "cache" => cpu['CACHE'], + "flags" => cpu['FLAGS']) + + host.cpus << detail + end + return host end @@ -181,8 +261,8 @@ class HostBrowser # Private method to ensure that a required field is present. # - def ensure_present(host_info,key) - raise Exception.new("ERROR! Missing '#{key}'...") if host_info[key] == nil + def ensure_present(info,key) + raise Exception.new("ERROR! Missing '#{key}'...") if info[key] == nil end # Executes an external program to support the keytab function. @@ -226,7 +306,6 @@ def entry_point(server) end unless defined?(TESTING) - # The main entry point. # unless ARGV[0] == "-n" diff --git a/wui/src/host-browser/test-host-browser-awake.rb b/wui/src/host-browser/test-host-browser-awake.rb new file mode 100755 index 0000000..02e9146 --- /dev/null +++ b/wui/src/host-browser/test-host-browser-awake.rb @@ -0,0 +1,94 @@ +#!/usr/bin/ruby -Wall +# +# Copyright (C) 2008 Red Hat, Inc. +# Written by Darryl L. Pierce <dpierce at redhat.com> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. A copy of the GNU General Public License is +# also available at http://www.gnu.org/copyleft/gpl.html. + +require File.dirname(__FILE__) + '/../test/test_helper' +require 'test/unit' +require 'flexmock/test_unit' + +TESTING=true + +require 'host-browser' + +# +TestHostBrowserAwaken+ +class TestHostBrowserAwaken < Test::Unit::TestCase + + def setup + @session = flexmock('session') + @session.should_receive(:peeraddr).at_least.once.returns { [nil,nil,nil,"192.168.2.255"] } + + @krb5 = flexmock('krb5') + + @browser = HostBrowser.new(@session) + @browser.logfile = './unit-test.log' + @browser.keytab_dir = '/var/temp/' + end + + # Ensures that the server raises an exception when it receives an + # improper handshake response. + # + def test_begin_conversation_with_improper_response_to_greeting + @session.should_receive(:write).with("HELLO?\n").once().returns { |greeting| greeting.length } + @session.should_receive(:readline).once().returns { "SUP?" } + + assert_raise(Exception) { @browser.begin_conversation } + end + + # Ensures the server accepts a proper response from the remote system. + # + def test_begin_conversation + @session.should_receive(:write).with("HELLO?\n").once().returns { |greeting| greeting.length } + @session.should_receive(:readline).once().returns { "HELLO!\n" } + + assert_nothing_raised(Exception) { @browser.begin_conversation } + end + + # Ensures that the server is satisfied if the remote system is + # making a wakeup call. + # + def test_get_mode_with_awaken_request + @session.should_receive(:write).with("MODE?\n").once().returns { |request| request.length } + @session.should_receive(:readline).once().returns { "AWAKEN\n" } + + result = @browser.get_mode() + + assert_equal "AWAKEN", result, "method did not return the right value" + end + + # Ensures the host browser generates a keytab as expected. + # + def test_create_keytab + @krb5.should_receive(:get_default_realm).once().returns { "ovirt-test-realm" } + servername = `hostname -f`.chomp + @session.should_receive(:write).with("KTAB http://#{servername}/ipa/config/127.0.0.1-libvirt.tab\n").once().returns { |request| request.length } + @session.should_receive(:readline).once().returns { "ACK\n" } + + assert_nothing_raised(Exception) { @browser.create_keytab('localhost','127.0.0.1', at krb5) } + end + + # Ensures that, if a keytab is present and a key version number available, + # the server ends the conversation by returning the key version number. + # + def test_end_conversation + @session.should_receive(:write).with("BYE\n").once().returns { |request| request.length } + + assert_nothing_raised(Exception) { @browser.end_conversation } + end + +end diff --git a/wui/src/host-browser/test-host-browser-awaken.rb b/wui/src/host-browser/test-host-browser-awaken.rb deleted file mode 100755 index a5ca2e7..0000000 --- a/wui/src/host-browser/test-host-browser-awaken.rb +++ /dev/null @@ -1,94 +0,0 @@ -#!/usr/bin/ruby -Wall -# -# Copyright (C) 2008 Red Hat, Inc. -# Written by Darryl L. Pierce <dpierce at redhat.com> -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; version 2 of the License. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301, USA. A copy of the GNU General Public License is -# also available at http://www.gnu.org/copyleft/gpl.html. - -require File.dirname(__FILE__) + '/../test/test_helper' -require 'test/unit' -require 'flexmock/test_unit' - -TESTING=true - -require 'host-browser' - -# +TestHostBrowserAwaken+ -class TestHostBrowserAwaken < Test::Unit::TestCase - - def setup - @session = flexmock('session') - @session.should_receive(:peeraddr).at_least.once.returns { [nil,nil,nil,"192.168.2.255"] } - - @krb5 = flexmock('krb5') - - @browser = HostBrowser.new(@session) - @browser.logfile = './unit-test.log' - @browser.keytab_dir = '/var/temp/' - end - - # Ensures that the server raises an exception when it receives an - # improper handshake response. - # - def test_begin_conversation_with_improper_response_to_greeting - @session.should_receive(:write).with("HELLO?\n").once().returns { |greeting| greeting.length } - @session.should_receive(:readline).once().returns { "SUP?" } - - assert_raise(Exception) { @browser.begin_conversation } - end - - # Ensures the server accepts a proper response from the remote system. - # - def test_begin_conversation - @session.should_receive(:write).with("HELLO?\n").once().returns { |greeting| greeting.length } - @session.should_receive(:readline).once().returns { "HELLO!\n" } - - assert_nothing_raised(Exception) { @browser.begin_conversation } - end - - # Ensures that the server is satisfied if the remote system is - # making a wakeup call. - # - def test_get_mode_with_awaken_request - @session.should_receive(:write).with("MODE?\n").once().returns { |request| request.length } - @session.should_receive(:readline).once().returns { "AWAKEN\n" } - - result = @browser.get_mode() - - assert_equal "AWAKEN", result, "method did not return the right value" - end - - # Ensures the host browser generates a keytab as expected. - # - def test_create_keytab - @krb5.should_receive(:get_default_realm).once().returns { "ovirt-test-realm" } - servername = `hostname -f`.chomp - @session.should_receive(:write).with("KTAB http://#{servername}/config/127.0.0.1-libvirt.tab\n").once().returns { |request| request.length } - @session.should_receive(:readline).once().returns { "ACK\n" } - - assert_nothing_raised(Exception) { @browser.create_keytab('localhost','127.0.0.1', at krb5) } - end - - # Ensures that, if a keytab is present and a key version number available, - # the server ends the conversation by returning the key version number. - # - def test_end_conversation - @session.should_receive(:write).with("BYE\n").once().returns { |request| request.length } - - assert_nothing_raised(Exception) { @browser.end_conversation } - end - -end diff --git a/wui/src/host-browser/test-host-browser-identify.rb b/wui/src/host-browser/test-host-browser-identify.rb index 8f215e9..4197e19 100755 --- a/wui/src/host-browser/test-host-browser-identify.rb +++ b/wui/src/host-browser/test-host-browser-identify.rb @@ -27,136 +27,219 @@ TESTING=true require 'host-browser' class TestHostBrowser < Test::Unit::TestCase - - def setup - @session = flexmock('session') - @session.should_receive(:peeraddr).at_least.once.returns { [nil,nil,nil,"192.168.2.255"] } - - @browser = HostBrowser.new(@session) - @browser.logfile = './unit-test.log' - - # default host info - @host_info = {} - @host_info['UUID'] = 'node1' - @host_info['IPADDR'] = '192.168.2.2' - @host_info['HOSTNAME'] = 'node1.ovirt.redhat.com' - @host_info['NUMCPUS'] = '3' - @host_info['CPUSPEED'] = '3' - @host_info['ARCH'] = 'x86_64' - @host_info['MEMSIZE'] = '16384' - @host_info['DISABLED'] = '0' - end - - # Ensures that the server is satisfied if the remote system is - # making a wakeup call. - # - def test_get_mode_with_awaken_request - @session.should_receive(:write).with("MODE?\n").once().returns { |request| request.length } - @session.should_receive(:readline).once().returns { "IDENTIFY\n" } - - result = @browser.get_mode() - - assert_equal "IDENTIFY", result, "method did not return the right value" - end - - # Ensures that, if an info field is missing a key, the server raises - # an exception. - # - def test_get_info_with_missing_key - @session.should_receive(:write).with("INFO?\n").once().returns { |request| request.length } - @session.should_receive(:readline).once().returns { "=value1\n" } - - assert_raise(Exception) { @browser.get_remote_info } - end - - # Ensures that, if an info field is missing a value, the server raises - # an exception. - # - def test_get_info_with_missing_value - @session.should_receive(:write).with("INFO?\n").once().returns { |request| request.length } - @session.should_receive(:readline).once().returns { "key1=\n" } - - assert_raise(Exception) { @browser.get_remote_info } - end - - # Ensures that, if the server gets a poorly formed ending statement, it - # raises an exception. - # - def test_get_info_with_invalid_end - @session.should_receive(:write).with("INFO?\n").once().returns { |request| request.length } - @session.should_receive(:readline).once().returns { "key1=value1\n" } - @session.should_receive(:write).with("ACK key1\n").once().returns { |request| request.length } - @session.should_receive(:readline).once().returns { "ENDIFNO\n" } - - assert_raise(Exception) { @browser.get_remote_info } - end - - # Ensures that a well-formed transaction works as expected. - # - def test_get_info - @session.should_receive(:write).with("INFO?\n").once().returns { |request| request.length } - @session.should_receive(:readline).once().returns { "key1=value1\n" } - @session.should_receive(:write).with("ACK key1\n").once().returns { |request| request.length } - @session.should_receive(:readline).once().returns { "key2=value2\n" } - @session.should_receive(:write).with("ACK key2\n").once().returns { |request| request.length } - @session.should_receive(:readline).once().returns { "ENDINFO\n" } - - info = @browser.get_remote_info - - assert_equal 4,info.keys.size, "Should contain two keys" - assert info.include?("IPADDR") - assert info.include?("HOSTNAME") - assert info.include?("key1") - assert info.include?("key2") - end - - # Ensures that the server is fine when no UUID is present. - # - def test_write_host_info_with_missing_uuid - @host_info['UUID'] = nil - - assert_nothing_raised { @browser.write_host_info(@host_info) } - end - - # Ensures that, if the hostname is missing, the server - # raises an exception. - # - def test_write_host_info_with_missing_hostname - @host_info['HOSTNAME'] = nil - - assert_raise(Exception) { @browser.write_host_info(@host_info) } - end - - # Ensures that, if the number of CPUs is missing, the server raises an exception. - # - def test_write_host_info_with_missing_numcpus - @host_info['NUMCPUS'] = nil - - assert_raise(Exception) { @browser.write_host_info(@host_info) } - end - - # Ensures that, if the CPU speed is missing, the server raises an exception. - # - def test_write_host_info_with_missing_cpuspeed - @host_info['CPUSPEED'] = nil - - assert_raise(Exception) { @browser.write_host_info(@host_info) } - end - - # Ensures that, if the architecture is missing, the server raises an exception. - # - def test_write_host_info_with_missing_arch - @host_info['ARCH'] = nil - - assert_raise(Exception) { @browser.write_host_info(@host_info) } - end - - # Ensures that, if the memory size is missing, the server raises an exception. - # - def test_write_host_info_info_with_missing_memsize - @host_info['MEMSIZE'] = nil - - assert_raise(Exception) { @browser.write_host_info(@host_info) } - end + def setup + @session = flexmock('session') + @session.should_receive(:peeraddr).at_least.once.returns { [nil,nil,nil,"192.168.2.255"] } + + @browser = HostBrowser.new(@session) + @browser.logfile = './unit-test.log' + + # default host info + @host_info = {} + @host_info['UUID'] = 'node1' + @host_info['IPADDR'] = '192.168.2.2' + @host_info['HOSTNAME'] = 'node1.ovirt.redhat.com' + @host_info['ARCH'] = 'x86_64' + @host_info['MEMSIZE'] = '16384' + @host_info['DISABLED'] = '0' + + @host_info['NUMCPUS'] = '2' + + @host_info['CPUINFO'] = Array.new + @host_info['CPUINFO'][0] = {} + @host_info['CPUINFO'][0]['CPUNUM'] = '0' + @host_info['CPUINFO'][0]['CORENUM'] = '0' + @host_info['CPUINFO'][0]['NUMCORES'] = '2' + @host_info['CPUINFO'][0]['VENDOR'] = 'GenuineIntel' + @host_info['CPUINFO'][0]['MODEL'] = '15' + @host_info['CPUINFO'][0]['FAMILY'] = '6' + @host_info['CPUINFO'][0]['CPUIDLVL'] = '10' + @host_info['CPUINFO'][0]['SPEED'] = '3' + @host_info['CPUINFO'][0]['CACHE'] = '4096 kb' + @host_info['CPUINFO'][0]['FLAGS'] = 'fpu vme de pse tsc msr pae \ + 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['CPUINFO'][1] = {} + @host_info['CPUINFO'][1]['CPUNUM'] = '1' + @host_info['CPUINFO'][1]['CORENUM'] = '1' + @host_info['CPUINFO'][1]['NUMCORES'] = '2' + @host_info['CPUINFO'][1]['VENDOR'] = 'GenuineIntel' + @host_info['CPUINFO'][1]['MODEL'] = '15' + @host_info['CPUINFO'][1]['FAMILY'] = '6' + @host_info['CPUINFO'][1]['CPUIDLVL'] = '10' + @host_info['CPUINFO'][1]['SPEED'] = '3' + @host_info['CPUINFO'][1]['CACHE'] = '4096 kb' + @host_info['CPUINFO'][1]['FLAGS'] = 'fpu vme de pse tsc msr pae \ + 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' + end + + # Ensures that the server is satisfied if the remote system is + # making a wakeup call. + # + def test_get_mode_with_awaken_request + @session.should_receive(:write).with("MODE?\n").once().returns { |request| request.length } + @session.should_receive(:readline).once().returns { "IDENTIFY\n" } + + result = @browser.get_mode() + + assert_equal "IDENTIFY", result, "method did not return the right value" + end + + # Ensures that, if an info field is missing a key, the server raises + # an exception. + # + def test_get_info_with_missing_key + @session.should_receive(:write).with("INFO?\n").once().returns { |request| request.length } + @session.should_receive(:readline).once().returns { "=value1\n" } + + assert_raise(Exception) { @browser.get_remote_info } + end + + # Ensures that, if an info field is missing a value, the server raises + # an exception. + # + def test_get_info_with_missing_value + @session.should_receive(:write).with("INFO?\n").once().returns { |request| request.length } + @session.should_receive(:readline).once().returns { "key1=\n" } + + assert_raise(Exception) { @browser.get_remote_info } + end + + # Ensures that, if the server gets a poorly formed ending statement, it + # raises an exception. + # + def test_get_info_with_invalid_end + @session.should_receive(:write).with("INFO?\n").once().returns { |request| request.length } + @session.should_receive(:readline).once().returns { "key1=value1\n" } + @session.should_receive(:write).with("ACK key1\n").once().returns { |request| request.length } + @session.should_receive(:readline).once().returns { "ENDIFNO\n" } + + assert_raise(Exception) { @browser.get_remote_info } + end + + # Ensures that a well-formed transaction works as expected. + # + def test_get_info + @session.should_receive(:write).with("INFO?\n").once().returns { |request| request.length } + @session.should_receive(:readline).once().returns { "key1=value1\n" } + @session.should_receive(:write).with("ACK key1\n").once().returns { |request| request.length } + @session.should_receive(:readline).once().returns { "key2=value2\n" } + @session.should_receive(:write).with("ACK key2\n").once().returns { |request| request.length } + @session.should_receive(:readline).once().returns { "ENDINFO\n" } + + info = @browser.get_remote_info + + assert_equal 4,info.keys.size, "Should contain four keys" + assert info.include?("IPADDR") + assert info.include?("HOSTNAME") + assert info.include?("key1") + assert info.include?("key2") + end + + # Ensures that the server is fine when no UUID is present. + # + def test_write_host_info_with_missing_uuid + @host_info['UUID'] = nil + + assert_nothing_raised { @browser.write_host_info(@host_info) } + end + + # Ensures that, if the hostname is missing, the server + # raises an exception. + # + def test_write_host_info_with_missing_hostname + @host_info['HOSTNAME'] = nil + + assert_raise(Exception) { @browser.write_host_info(@host_info) } + end + + # Ensures that, if the architecture is missing, the server raises an + # exception. + # + def test_write_host_info_with_missing_arch + @host_info['ARCH'] = nil + + assert_raise(Exception) { @browser.write_host_info(@host_info) } + end + + # Ensures that, if the memory size is missing, the server raises an + # exception. + # + def test_write_host_info_info_with_missing_memsize + @host_info['MEMSIZE'] = nil + + assert_raise(Exception) { @browser.write_host_info(@host_info) } + end + + # Ensures that, if no cpu info was available, the server raises an + # exception. + # + def test_write_host_info_with_missing_cpuinfo + @host_info['CPUINFO'] = 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 + @session.should_receive(:write).with("INFO?\n").once().returns { |request| request.length } + @session.should_receive(:readline).once().returns { "CPU\n" } + @session.should_receive(:write).with("CPUINFO?\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 { "ENDCPU\n" } + @session.should_receive(:write).with("ACK CPU\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?("CPUINFO") + end + + # Ensures the browser can properly parse the CPU details of two CPUs. + # + def test_parse_cpu_info + @session.should_receive(:write).with("INFO?\n").once().returns { |request| request.length } + + # CPU 0 + @session.should_receive(:readline).once().returns { "CPU\n" } + @session.should_receive(:write).with("CPUINFO?\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 { "ENDCPU\n" } + @session.should_receive(:write).with("ACK CPU\n").once().returns { |request| request.length } + + # CPU 1 + @session.should_receive(:readline).once().returns { "CPU\n" } + @session.should_receive(:write).with("CPUINFO?\n").once().returns { |request| request.length } + @session.should_receive(:readline).once().returns { "key3=value3\n" } + @session.should_receive(:write).with("ACK key3\n").once().returns { |request| request.length } + @session.should_receive(:readline).once().returns { "key4=value4\n" } + @session.should_receive(:write).with("ACK key4\n").once().returns { |request| request.length } + @session.should_receive(:readline).once().returns { "ENDCPU\n" } + @session.should_receive(:write).with("ACK CPU\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?('CPUINFO') + assert_equal 2, info['CPUINFO'].size, "Should contain details for two CPUs" + assert_not_nil info['CPUINFO'][0]['key1'] + assert_not_nil info['CPUINFO'][0]['key2'] + assert_not_nil info['CPUINFO'][1]['key3'] + assert_not_nil info['CPUINFO'][1]['key4'] + end end diff --git a/wui/src/test/fixtures/cpus.yml b/wui/src/test/fixtures/cpus.yml new file mode 100644 index 0000000..5586303 --- /dev/null +++ b/wui/src/test/fixtures/cpus.yml @@ -0,0 +1,21 @@ +# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html + +one: + cpu_number: 1 + core_number: 1 + cpu_id_level: 1 + vendor: 1 + family: 1 + model: 1 + family: MyString + flags: MyString + +two: + cpu_number: 1 + core_number: 1 + cpu_id_level: 1 + vendor: 1 + family: 1 + model: 1 + family: MyString + flags: MyString diff --git a/wui/src/test/fixtures/hosts.yml b/wui/src/test/fixtures/hosts.yml index 3b93aea..f10a756 100644 --- a/wui/src/test/fixtures/hosts.yml +++ b/wui/src/test/fixtures/hosts.yml @@ -2,8 +2,6 @@ one: id: 1 uuid: '1148fdf8-961d-11dc-9387-001558c41534' hostname: 'prod.corp.com' - num_cpus: 8 - cpu_speed: 4096 arch: 'i686' memory: 18384 is_disabled: 0 @@ -13,8 +11,6 @@ two: id: 2 uuid: '1f2a8694-961d-11dc-9387-001558c41534' hostname: 'myworkstation.dev.corp.com' - num_cpus: 4 - cpu_speed: 2048 arch: 'i386' memory: 2048 is_disabled: 0 @@ -24,30 +20,24 @@ three: id: 3 uuid: '58a85f44-75fd-4934-805f-88e45b40d4b4' hostname: 'macworkstation.foobar.com' - num_cpus: 8 - cpu_speed: 1024 arch: 'mips' memory: 2048 - is_disabled: 0 + is_disabled: 0 hypervisor_type: 'kvm' hardware_pool_id: 3 four: id: 4 uuid: '520bbb34-6515-490e-9d07-0c8b14f76805' hostname: 'fedoraworkstation.foobar.com' - num_cpus: 8 - cpu_speed: 3072 arch: 'i386' memory: 2048 - is_disabled: 1 + is_disabled: 1 hypervisor_type: 'kvm' hardware_pool_id: 3 five: id: 5 uuid: '2e422f66-324e-48d4-973f-0b91b33070f9' hostname: 'pipeline.foobar.com' - num_cpus: 32 - cpu_speed: 4096 arch: 'xeon' memory: 1048576 is_disabled: 0 @@ -57,19 +47,15 @@ six: id: 6 uuid: 'bb0ce7c7-f234-49ae-84b5-6f4fd8bddcaa' hostname: 'prod.foobar.com' - num_cpus: 32 - cpu_speed: 4096 arch: 'xeon' memory: 16384 - is_disabled: 0 + is_disabled: 0 hypervisor_type: 'kvm' hardware_pool_id: 3 seven: id: 7 uuid: '6ae3d22e-97e0-4d86-9712-5395b20a0f45' hostname: 'mystation.dev.corp.com' - num_cpus: 4 - cpu_speed: 3072 arch: 'i686' memory: 2048 is_disabled: 0 @@ -79,8 +65,6 @@ eight: id: 8 uuid: 'ec0d86de-657b-48f6-b7cc-e733a3f9a834' hostname: 'issue.qa.corp.com' - num_cpus: 8 - cpu_speed: 4096 arch: 'x86' memory: 4096 is_disabled: 0 @@ -90,8 +74,6 @@ nine: id: 9 uuid: '81c15560-dbf4-45f5-9b75-106cdbd63aeb' hostname: 'somehost' - num_cpus: 4 - cpu_speed: 4096 arch: 'x86' memory: 4096 is_disabled: 0 diff --git a/wui/src/test/unit/cpu_test.rb b/wui/src/test/unit/cpu_test.rb new file mode 100644 index 0000000..5b64ca7 --- /dev/null +++ b/wui/src/test/unit/cpu_test.rb @@ -0,0 +1,8 @@ +require File.dirname(__FILE__) + '/../test_helper' + +class CpuTest < ActiveSupport::TestCase + # Replace this with your real tests. + def test_truth + assert true + end +end -- 1.5.5.1