[Ovirt-devel] [PATCH] ovirt-identify-node now submits all extended CPU details.

Darryl L. Pierce dpierce at redhat.com
Tue Jul 8 14:06:03 UTC 2008


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




More information about the ovirt-devel mailing list