[Ovirt-devel] [PATCH server] Added the ability for NICs to be bonded for load balancing and failover.

Darryl L. Pierce dpierce at redhat.com
Fri Sep 19 20:34:18 UTC 2008


You will need to perform a migration after applying this patch to get the new
tables.

Additionally, this patch includes a configuration controller for the server.
When the node requests its configuration, the controller generates on on the
fly based on details in the database

Signed-off-by: Darryl L. Pierce <dpierce at redhat.com>
---
 src/app/controllers/managed_node_controller.rb     |   67 +++++
 src/app/controllers/nic_controller.rb              |    1 +
 src/app/models/bonding.rb                          |   46 +++
 src/app/models/bonding_type.rb                     |   26 ++
 src/app/models/boot_type.rb                        |   21 ++
 src/app/models/host.rb                             |    9 +-
 src/app/models/nic.rb                              |    3 +
 src/config/environment.rb                          |    2 +
 src/db/migrate/019_create_bonding_types.rb         |   43 +++
 src/db/migrate/020_create_bondings.rb              |   47 +++
 src/db/migrate/021_create_bondings_nics.rb         |   40 +++
 src/db/migrate/022_create_boot_types.rb            |   56 ++++
 src/dutils/active_record_env.rb                    |    1 +
 src/host-browser/host-browser.rb                   |   14 +-
 src/host-browser/test-host-browser-identify.rb     |  283 ------------------
 src/lib/managed_node_configuration.rb              |   98 +++++--
 src/test/fixtures/bonding_types.yml                |   15 +
 src/test/fixtures/bondings.yml                     |   10 +
 src/test/fixtures/bondings_nics.yml                |    7 +
 src/test/fixtures/boot_types.yml                   |   11 +
 src/test/fixtures/hosts.yml                        |   42 +++-
 src/test/fixtures/nics.yml                         |   52 +++-
 .../functional/managed_node_configuration_test.rb  |  129 ++++++---
 .../functional/managed_node_controller_test.rb     |   63 ++++
 src/test/functional/nic_controller_test.rb         |   43 ---
 src/test/unit/bonding_test.rb                      |   79 +++++
 src/test/unit/bonding_type_test.rb                 |   34 +++
 src/test/unit/boot_type_test.rb                    |   26 ++
 .../unit/host_browser_awaken_test.rb}              |    8 +-
 src/test/unit/host_browser_identify_test.rb        |  304 ++++++++++++++++++++
 30 files changed, 1169 insertions(+), 411 deletions(-)
 create mode 100644 src/app/controllers/managed_node_controller.rb
 create mode 100644 src/app/models/bonding.rb
 create mode 100644 src/app/models/bonding_type.rb
 create mode 100644 src/app/models/boot_type.rb
 create mode 100644 src/db/migrate/019_create_bonding_types.rb
 create mode 100644 src/db/migrate/020_create_bondings.rb
 create mode 100644 src/db/migrate/021_create_bondings_nics.rb
 create mode 100644 src/db/migrate/022_create_boot_types.rb
 delete mode 100755 src/host-browser/test-host-browser-identify.rb
 create mode 100644 src/test/fixtures/bonding_types.yml
 create mode 100644 src/test/fixtures/bondings.yml
 create mode 100644 src/test/fixtures/bondings_nics.yml
 create mode 100644 src/test/fixtures/boot_types.yml
 create mode 100644 src/test/functional/managed_node_controller_test.rb
 create mode 100644 src/test/unit/bonding_test.rb
 create mode 100644 src/test/unit/bonding_type_test.rb
 create mode 100644 src/test/unit/boot_type_test.rb
 rename src/{host-browser/test-host-browser-awake.rb => test/unit/host_browser_awaken_test.rb} (93%)
 mode change 100755 => 100644
 create mode 100644 src/test/unit/host_browser_identify_test.rb

diff --git a/src/app/controllers/managed_node_controller.rb b/src/app/controllers/managed_node_controller.rb
new file mode 100644
index 0000000..1bf6169
--- /dev/null
+++ b/src/app/controllers/managed_node_controller.rb
@@ -0,0 +1,67 @@
+#
+# 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.
+
+# +ManagedNodeController+ provides methods for interacting over HTTP with a
+# managed node.
+#
+class ManagedNodeController < ApplicationController
+  before_filter :load_host, :only => :config
+  before_filter :load_macs, :only => :config
+
+  def is_logged_in
+    # this overrides the default which is to force the client to log in
+  end
+
+  # Generates a configuration file for the managed node.
+  #
+  def config
+    context = ManagedNodeConfiguration.generate(@host, @macs)
+
+    send_data("#{context}", :type => 'text', :disposition => 'inline')
+  end
+
+  def error
+    send_data("#{flash[:message]}", :type => 'text', :disposition => 'inline')
+  end
+
+  private
+
+  def load_host
+    @host = Host.find_by_hostname(params[:host])
+
+    unless @host
+      flash[:message] = 'Missing or invalid hostname specified.'
+      redirect_to :action => :error
+    end
+  end
+
+  def load_macs
+    @macs = Hash.new
+    mac_text = params[:macs]
+
+    mac_text.scan(/([^\,]+)\,?/).each do |line|
+      key, value = line.first.split("=")
+      @macs[key] = value
+    end
+
+    if @macs.empty?
+      flash[:message] = 'No MAC address mappings provided.'
+      redirect_to :action => :error
+    end
+  end
+end
diff --git a/src/app/controllers/nic_controller.rb b/src/app/controllers/nic_controller.rb
index 85d4315..b3473ac 100644
--- a/src/app/controllers/nic_controller.rb
+++ b/src/app/controllers/nic_controller.rb
@@ -46,6 +46,7 @@ class NicController < ApplicationController
   end
 
   private
+
   #filter methods
   def pre_new
     flash[:notice] = 'Network Interfaces may not be edited via the web UI'
diff --git a/src/app/models/bonding.rb b/src/app/models/bonding.rb
new file mode 100644
index 0000000..941e2cd
--- /dev/null
+++ b/src/app/models/bonding.rb
@@ -0,0 +1,46 @@
+#
+# 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.
+
+# A +Bonding+ represents a bonded interface on a node. It is associated with
+# one (though really should be at least two) +Nic+ instances.
+#
+# The +name+ is the human-readable name used in the oVirt server.
+#
+# The +interface_name+ defines the name by which the bonded interface is
+# addressed on the node.
+#
+# The +arp_ping_address+ and +arp_interval+ are the ip address and interval
+# settings used for those nodes that require them for monitoring a bonded
+# interface. They can be ignored if not used.
+#
+class Bonding < ActiveRecord::Base
+  validates_presence_of :name,
+    :message => 'A name is required.'
+
+  validates_presence_of :interface_name,
+    :message => 'An interface name is required.'
+
+  belongs_to :host
+  belongs_to :bonding_type
+
+  has_and_belongs_to_many :nics,
+    :join_table  => 'bondings_nics',
+    :foreign_key => :bonding_id
+
+end
diff --git a/src/app/models/bonding_type.rb b/src/app/models/bonding_type.rb
new file mode 100644
index 0000000..e0d2193
--- /dev/null
+++ b/src/app/models/bonding_type.rb
@@ -0,0 +1,26 @@
+#
+# 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.
+
+# A +BondingType+ represents a mode in which two or more network interfaces
+# can be bound together.
+#
+class BondingType < ActiveRecord::Base
+  validates_presence_of :label
+  validates_presence_of :mode
+end
diff --git a/src/app/models/boot_type.rb b/src/app/models/boot_type.rb
new file mode 100644
index 0000000..8ffbe67
--- /dev/null
+++ b/src/app/models/boot_type.rb
@@ -0,0 +1,21 @@
+#
+# 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.
+
+class BootType < ActiveRecord::Base
+end
diff --git a/src/app/models/host.rb b/src/app/models/host.rb
index 3fc3abb..de5c5ee 100644
--- a/src/app/models/host.rb
+++ b/src/app/models/host.rb
@@ -21,15 +21,16 @@ require 'util/ovirt'
 
 class Host < ActiveRecord::Base
   belongs_to :hardware_pool
+  belongs_to :bonding_type
 
-  has_many :cpus, :dependent => :destroy
-  has_many :nics, :dependent => :destroy
-  has_many :vms,  :dependent => :nullify do
+  has_many :cpus,     :dependent => :destroy
+  has_many :nics,     :dependent => :destroy
+  has_many :bondings, :dependent => :destroy
+  has_many :vms,      :dependent => :nullify
 
     def consuming_resources
       find(:all, :conditions=>{:state=>Vm::RUNNING_STATES})
     end
-  end
   has_many :tasks, :class_name => "HostTask", :dependent => :destroy, :order => "id DESC" do
     def queued
       find(:all, :conditions=>{:state=>Task::STATE_QUEUED})
diff --git a/src/app/models/nic.rb b/src/app/models/nic.rb
index c1fc542..baf7095 100644
--- a/src/app/models/nic.rb
+++ b/src/app/models/nic.rb
@@ -19,4 +19,7 @@
 
 class Nic < ActiveRecord::Base
   belongs_to :host
+  belongs_to :boot_type
+
+  has_and_belongs_to_many :bonding, :join_table => 'bondings_nics'
 end
diff --git a/src/config/environment.rb b/src/config/environment.rb
index ff6f6e8..3ec4379 100644
--- a/src/config/environment.rb
+++ b/src/config/environment.rb
@@ -80,3 +80,5 @@ end
 require 'gettext/rails'                                                                                                                                                     
 gem 'cobbler'
 require 'cobbler'
+
+MANAGED_NODE_CONFIGURATION_DIR = '/var/www/html/ovirt-cfgdb'
diff --git a/src/db/migrate/019_create_bonding_types.rb b/src/db/migrate/019_create_bonding_types.rb
new file mode 100644
index 0000000..bf2972f
--- /dev/null
+++ b/src/db/migrate/019_create_bonding_types.rb
@@ -0,0 +1,43 @@
+#
+# 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.
+
+class CreateBondingTypes < ActiveRecord::Migration
+  def self.up
+    create_table :bonding_types do |t|
+      t.string  :label, :null => false, :limit => 20
+      t.integer :mode,  :null => false
+    end
+
+    add_index :bonding_types, :label, :unique => true
+    add_index :bonding_types, :mode,  :unique => true
+
+    # The order of the records is not related to the mode value.
+    # Instead, they are ordered this way to ensure they're presented
+    # in this particular order when loaded.
+    #
+    BondingType.create :label => 'Load Balancing',   :mode => 2
+    BondingType.create :label => 'Failover',         :mode => 1
+    BondingType.create :label => 'Broadcast',        :mode => 3
+    BondingType.create :label => 'Link Aggregation', :mode => 4
+  end
+
+  def self.down
+    drop_table :bonding_types
+  end
+end
diff --git a/src/db/migrate/020_create_bondings.rb b/src/db/migrate/020_create_bondings.rb
new file mode 100644
index 0000000..e02adc3
--- /dev/null
+++ b/src/db/migrate/020_create_bondings.rb
@@ -0,0 +1,47 @@
+#
+# 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.
+
+class CreateBondings < ActiveRecord::Migration
+  def self.up
+    create_table :bondings do |t|
+      t.string  :name,            :null => false, :limit => 50
+      t.string  :interface_name,  :null => false, :limit => 20
+      t.integer :bonding_type_id, :null => false
+      t.integer :host_id,         :null => false
+      t.string  :ip_addr,         :null => true, :limit => 15
+      t.string  :netmask,         :null => true, :limit => 15
+      t.string  :broadcast,       :null => true, :limit => 15
+      t.string  :arp_ping_address,:null => true
+      t.integer :arp_interval,    :null => false, :default => 0
+
+      t.timestamps
+    end
+
+    add_index :bondings, [:interface_name, :host_id], :unique => true
+
+    execute 'alter table bondings add constraint fk_bonding_bonding_type
+             foreign key (bonding_type_id) references bonding_types(id)'
+    execute 'alter table bondings add constraint fk_bonding_host
+             foreign key (host_id) references hosts(id)'
+  end
+
+  def self.down
+    drop_table :bondings
+  end
+end
diff --git a/src/db/migrate/021_create_bondings_nics.rb b/src/db/migrate/021_create_bondings_nics.rb
new file mode 100644
index 0000000..1fe0789
--- /dev/null
+++ b/src/db/migrate/021_create_bondings_nics.rb
@@ -0,0 +1,40 @@
+#
+# 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.
+
+class CreateBondingsNics < ActiveRecord::Migration
+  def self.up
+    create_table :bondings_nics do |t|
+      t.integer :bonding_id, :null => false
+      t.integer :nic_id,     :null => false
+
+      t.timestamps
+    end
+
+    add_index :bondings_nics, [:bonding_id, :nic_id], :unique => true
+
+    execute 'alter table bondings_nics add constraint fk_bondings_nics_bonding
+             foreign key (bonding_id) references bondings(id)'
+    execute 'alter table bondings_nics add constraint fk_bondings_nics_nic
+             foreign key (nic_id) references nics(id)'
+  end
+
+  def self.down
+    drop_table :bondings_nics
+  end
+end
diff --git a/src/db/migrate/022_create_boot_types.rb b/src/db/migrate/022_create_boot_types.rb
new file mode 100644
index 0000000..e18d5c5
--- /dev/null
+++ b/src/db/migrate/022_create_boot_types.rb
@@ -0,0 +1,56 @@
+#
+# 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.
+
+class CreateBootTypes < ActiveRecord::Migration
+  def self.up
+    create_table :boot_types do |t|
+      t.string :label, :null => false, :limit => 25
+      t.string :proto, :null => false, :limit => 25
+
+      t.timestamps
+    end
+
+    add_index :boot_types, :label, :unique => true
+    add_index :boot_types, :proto, :unique => true
+
+    BootType.create(:label => 'Static IP', :proto => 'static')
+    BootType.create(:label => 'DHCP',      :proto => 'dhcp')
+    BootType.create(:label => 'BOOTP',     :proto => 'bootp')
+
+    add_column :nics, :boot_type_id, :integer, :null => true
+
+    execute 'alter table nics add constraint fk_nic_boot_type
+             foreign key (boot_type_id) references boot_types(id)'
+
+    boot_type = BootType.find_by_proto('static')
+
+    Nic.find(:all).each do |nic|
+      nic.boot_type_id = boot_type.id
+      nic.save!
+    end
+
+    change_column :nics, :boot_type_id, :integer, :null => false
+  end
+
+  def self.down
+    remove_column :nics, :boot_type_id
+
+    drop_table :boot_types
+  end
+end
diff --git a/src/dutils/active_record_env.rb b/src/dutils/active_record_env.rb
index c77f2d6..d0a005e 100644
--- a/src/dutils/active_record_env.rb
+++ b/src/dutils/active_record_env.rb
@@ -62,6 +62,7 @@ require 'models/directory_pool.rb'
 require 'models/smart_pool.rb'
 require 'models/host.rb'
 require 'models/cpu.rb'
+require 'models/boot_type.rb'
 require 'models/nic.rb'
 
 require 'models/vm_resource_pool.rb'
diff --git a/src/host-browser/host-browser.rb b/src/host-browser/host-browser.rb
index f7a4a02..3adea03 100755
--- a/src/host-browser/host-browser.rb
+++ b/src/host-browser/host-browser.rb
@@ -297,16 +297,18 @@ class HostBrowser
         end
 
         # iterate over any nics left and create new records for them.
+        boot_type = BootType.find_by_proto('dhcp')
 
         nic_info.collect do |nic|
             puts "Creating a new nic..."
             detail = Nic.new(
-                'mac'        => nic['MAC'],
-                'bandwidth'  => nic['BANDWIDTH'],
-                'usage_type' => 1,
-                'ip_addr'    => nic['IP_ADDRESS'],
-                'netmask'    => nic['NETMASK'],
-                'broadcast'  => nic['BROADCAST'])
+                'mac'          => nic['MAC'],
+                'bandwidth'    => nic['BANDWIDTH'],
+                'usage_type'   => 1,
+                'ip_addr'      => nic['IP_ADDRESS'],
+                'netmask'      => nic['NETMASK'],
+                'broadcast'    => nic['BROADCAST'],
+                'boot_type_id' => boot_type.id)
 
             host.nics << detail
         end
diff --git a/src/host-browser/test-host-browser-identify.rb b/src/host-browser/test-host-browser-identify.rb
deleted file mode 100755
index 7e672ce..0000000
--- a/src/host-browser/test-host-browser-identify.rb
+++ /dev/null
@@ -1,283 +0,0 @@
-#!/usr/bin/ruby -Wall
-#
-# Copyright (C) 2008 Red Hat, Inc.
-# Written by Darryl L. Pierce <dpierce at redhat.com>
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; version 2 of the License.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
-# MA  02110-1301, USA.  A copy of the GNU General Public License is
-# also available at http://www.gnu.org/copyleft/gpl.html.
-
-require File.dirname(__FILE__) + '/../test/test_helper'
-require 'test/unit'
-require 'flexmock/test_unit'
-
-TESTING=true
-
-require 'host-browser'
-
-class TestHostBrowser < Test::Unit::TestCase
-    def setup
-        @session = flexmock('session')
-        @session.should_receive(:peeraddr).at_least.once.returns { [nil,nil,nil,"192.168.2.255"] }
-
-        @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'] = 'prod.corp.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'
-
-        @host_info['NICINFO'] = Array.new
-        @host_info['NICINFO'][0] = {}
-        @host_info['NICINFO'][0]['MAC']       = '00:11:22:33:44:55'
-        @host_info['NICINFO'][0]['BANDWIDTH'] = '100'
-
-        @host_info['NICINFO'][1] = {}
-        @host_info['NICINFO'][1]['MAC']       = '00:77:11:77:19:65'
-        @host_info['NICINFO'][1]['BANDWIDTH'] = '100'
-    end
-
-    # Ensures that the server is satisfied if the remote system is
-    # 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 that, if no NIC info was available, the server raises an
-    # exception.
-    #
-    def test_write_host_info_with_missing_nicinfo
-        @host_info['NICINFO'] = nil
-
-        assert_raise(Exception) { @browser.write_host_info(@host_info) }
-    end
-
-    # Ensures the browser can properly parse the CPU details.
-    #
-    def test_parse_cpu_info
-        @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_with_two_entries
-        @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
-
-    # Ensures the browser can properly parse the details for a NIC.
-    #
-    def test_parse_nic_info
-        @session.should_receive(:write).with("INFO?\n").once().returns { |request| request.length }
-        @session.should_receive(:readline).once().returns { "NIC\n" }
-        @session.should_receive(:write).with("NICINFO?\n").once().returns { |request| request.length }
-        @session.should_receive(:readline).once().returns { "key1=value1\n" }
-        @session.should_receive(:write).with("ACK key1\n").once().returns { |request| request.length }
-        @session.should_receive(:readline).once().returns { "key2=value2\n" }
-        @session.should_receive(:write).with("ACK key2\n").once().returns { |request| request.length }
-        @session.should_receive(:readline).once().returns { "ENDNIC\n" }
-        @session.should_receive(:write).with("ACK NIC\n").once().returns { |request| request.length }
-        @session.should_receive(:readline).once().returns { "ENDINFO\n" }
-
-        info = @browser.get_remote_info
-
-        assert_equal 3,info.keys.size, "Should contain four keys"
-        assert info.include?("NICINFO")
-    end
-
-end
diff --git a/src/lib/managed_node_configuration.rb b/src/lib/managed_node_configuration.rb
index 386eb05..4ade235 100644
--- a/src/lib/managed_node_configuration.rb
+++ b/src/lib/managed_node_configuration.rb
@@ -2,24 +2,23 @@
 # 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 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.
+# 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.
+# 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.
 
 # +ManagedNodeConfiguration+ takes in the description for a managed node and,
-# from that, generates the configuration file that is consumed the next time
-# the managed node starts up.
+# from that, generates the configuration file that is consumed the next time the
+# managed node starts up.
 #
 
 require 'stringio'
@@ -30,18 +29,73 @@ class ManagedNodeConfiguration
   def self.generate(host, macs)
     result = StringIO.new
 
+    result.puts "#!/bin/bash"
+    result.puts "# THIS FILE IS GENERATED!"
+
+    # first process any bondings that're defined
+    unless host.bondings.empty?
+      result.puts "cat <<\EOF > /var/tmp/pre-config-script"
+      result.puts "#!/bin/bash"
+      result.puts "# THIS FILE IS GENERATED!"
+
+      host.bondings.each do |bonding|
+        result.puts "/sbin/modprobe bonding mode=#{bonding.bonding_type.mode}"
+      end
+
+      result.puts "EOF"
+    end
+
+    # now process the network interfaces  and bondings
+    result.puts "cat <<\EOF > /var/tmp/node-augtool"
+
+    host.bondings.each do |bonding|
+      result.puts "rm #{NIC_ENTRY_PREFIX}/ifcfg-#{bonding.interface_name}"
+      result.puts "set #{NIC_ENTRY_PREFIX}/ifcfg-#{bonding.interface_name}/DEVICE #{bonding.interface_name}"
+      result.puts "set #{NIC_ENTRY_PREFIX}/ifcfg-#{bonding.interface_name}/IPADDR #{bonding.ip_addr}" if bonding.ip_addr
+      result.puts "set #{NIC_ENTRY_PREFIX}/ifcfg-#{bonding.interface_name}/ONBOOT yes"
+
+      bonding.nics.each do |nic|
+        process_nic result, nic, macs, bonding
+      end
+    end
+
     host.nics.each do |nic|
-      iface_name = macs[nic.mac]
-
-      if iface_name
-        result.puts "rm #{NIC_ENTRY_PREFIX}/ifcfg-#{iface_name}"
-        result.puts "set #{NIC_ENTRY_PREFIX}/ifcfg-#{iface_name}/DEVICE #{iface_name}"
-        result.puts "set #{NIC_ENTRY_PREFIX}/ifcfg-#{iface_name}/IPADDR #{nic.ip_addr}"    if nic.ip_addr
-        result.puts "set #{NIC_ENTRY_PREFIX}/ifcfg-#{iface_name}/BOOTPROTO dhcp"           if nic.ip_addr == nil
-        result.puts "set #{NIC_ENTRY_PREFIX}/ifcfg-#{iface_name}/BRIDGE #{nic.bridge}"     if nic.bridge
+      # only process this nic if it doesn't have a bonding
+      if nic.bonding.empty?
+        process_nic result, nic, macs
       end
     end
 
+    result.puts "save"
+    result.puts "EOF"
+
     result.string
   end
+
+  private
+
+  def self.process_nic(result, nic, macs, bonding = nil)
+    iface_name = macs[nic.mac]
+
+    if iface_name
+      result.puts "rm #{NIC_ENTRY_PREFIX}/ifcfg-#{iface_name}"
+      result.puts "set #{NIC_ENTRY_PREFIX}/ifcfg-#{iface_name}/DEVICE #{iface_name}"
+
+      if bonding
+        result.puts "set #{NIC_ENTRY_PREFIX}/ifcfg-#{iface_name}/MASTER #{bonding.interface_name}"
+        result.puts "set #{NIC_ENTRY_PREFIX}/ifcfg-#{iface_name}/SLAVE yes"
+      else
+        result.puts "set #{NIC_ENTRY_PREFIX}/ifcfg-#{iface_name}/BOOTPROTO #{nic.boot_type.proto}"
+
+        if nic.boot_type.proto == 'static'
+          result.puts "set #{NIC_ENTRY_PREFIX}/ifcfg-#{iface_name}/IPADDR #{nic.ip_addr}"
+          result.puts "set #{NIC_ENTRY_PREFIX}/ifcfg-#{iface_name}/NETMASK #{nic.netmask}"
+          result.puts "set #{NIC_ENTRY_PREFIX}/ifcfg-#{iface_name}/BROADCAST #{nic.broadcast}"
+        end
+      end
+
+      result.puts "set #{NIC_ENTRY_PREFIX}/ifcfg-#{iface_name}/BRIDGE #{nic.bridge}"     if nic.bridge
+      result.puts "set #{NIC_ENTRY_PREFIX}/ifcfg-#{iface_name}/ONBOOT yes"
+    end
+  end
 end
diff --git a/src/test/fixtures/bonding_types.yml b/src/test/fixtures/bonding_types.yml
new file mode 100644
index 0000000..4bdeec5
--- /dev/null
+++ b/src/test/fixtures/bonding_types.yml
@@ -0,0 +1,15 @@
+load_balancing_bonding_type:
+    label: Load Balancing
+    mode: 2
+
+failover_bonding_type:
+    label: Failover
+    mode: 1
+
+broadcast_bonding_type:
+    label: Broadcast
+    mode: 3
+
+link_aggregation_bonding_type:
+    label: Link Aggregation
+    mode: 4
diff --git a/src/test/fixtures/bondings.yml b/src/test/fixtures/bondings.yml
new file mode 100644
index 0000000..c2a47b5
--- /dev/null
+++ b/src/test/fixtures/bondings.yml
@@ -0,0 +1,10 @@
+mailservers_managed_node_bonding:
+    name: Production Network
+    interface_name: bond0
+    bonding_type_id: <%= Fixtures.identify(:link_aggregation_bonding_type) %>
+    host_id: <%= Fixtures.identify(:mailservers_managed_node) %>
+    ip_addr: 172.31.0.15
+    netmask: 255.255.255.
+    broadcast: 172.31.0.255
+    arp_ping_address: 172.31.0.100
+    arp_interval: 0
diff --git a/src/test/fixtures/bondings_nics.yml b/src/test/fixtures/bondings_nics.yml
new file mode 100644
index 0000000..11a3d1a
--- /dev/null
+++ b/src/test/fixtures/bondings_nics.yml
@@ -0,0 +1,7 @@
+mailservers_managed_node_bonding_nic_1:
+    bonding_id: <%= Fixtures.identify(:mailservers_managed_node_bonding) %>
+    nic_id: <%= Fixtures.identify(:mailserver_nic_one) %>
+
+mailservers_managed_node_bonding_nic_2:
+    bonding_id: <%= Fixtures.identify(:mailservers_managed_node_bonding) %>
+    nic_id: <%= Fixtures.identify(:mailserver_nic_two) %>
diff --git a/src/test/fixtures/boot_types.yml b/src/test/fixtures/boot_types.yml
new file mode 100644
index 0000000..d3f31a2
--- /dev/null
+++ b/src/test/fixtures/boot_types.yml
@@ -0,0 +1,11 @@
+boot_type_static_ip:
+    label: Static IP
+    proto: static
+
+boot_type_dhcp:
+    label: DHCP
+    proto: dhcp
+
+boot_type_bootp:
+    label: BOOTP
+    proto: bootp
diff --git a/src/test/fixtures/hosts.yml b/src/test/fixtures/hosts.yml
index 44397da..5b8af15 100644
--- a/src/test/fixtures/hosts.yml
+++ b/src/test/fixtures/hosts.yml
@@ -1,4 +1,3 @@
-
 one:
   id: 1
   uuid: '1148fdf8-961d-11dc-9387-001558c41534'
@@ -90,12 +89,39 @@ ten:
  hypervisor_type: 'kvm'
  hardware_pool_id: 1
  state: "available"
+
 mailservers_managed_node:
-    id: 11
-    uuid:             '182a8596-961d-11dc-9387-001558c41534'
-    hostname:         'mail.mynetwork.com'
-    arch:             'i386'
-    memory:           16384
-    is_disabled:      0
-    hypervisor_type:  'kvm'
+    uuid: '182a8596-961d-11dc-9387-001558c41534'
+    hostname: 'mail.mynetwork.com'
+    arch: 'i386'
+    memory: 16384
+    is_disabled: 0
+    hypervisor_type: 'kvm'
+    hardware_pool_id: <%= Fixtures.identify(:prodops_pool) %>
+
+fileserver_managed_node:
+    uuid: '928ad8172-9723-11dc-9387-001558c41534'
+    hostname: 'files.mynetwork.com'
+    arch: 'x86_64'
+    memory: 32768
+    is_disabled: 0
+    hypervisor_type: 'kvm'
+    hardware_pool_id: <%= Fixtures.identify(:prodops_pool) %>
+
+ldapserver_managed_node:
+    uuid: '919ae372-9156-11dc-9387-001558c41534'
+    hostname: 'ldap.mynetwork.com'
+    arch: 'i386'
+    memory: 16384
+    is_disabled: 0
+    hypervisor_type: 'kvm'
+    hardware_pool_id: <%= Fixtures.identify(:prodops_pool) %>
+
+buildserver_managed_node:
+    uuid: '6293acd9-2784-11dc-9387-001558c41534'
+    hostname: 'build.mynetwork.com'
+    arch: 'x86_64'
+    memory: 65536
+    is_disabled: 0
+    hypervisor_type: 'kvm'
     hardware_pool_id: <%= Fixtures.identify(:prodops_pool) %>
diff --git a/src/test/fixtures/nics.yml b/src/test/fixtures/nics.yml
index 8726fb6..ccf71d2 100644
--- a/src/test/fixtures/nics.yml
+++ b/src/test/fixtures/nics.yml
@@ -5,6 +5,7 @@ one:
   usage_type: '1'
   bandwidth: 100
   host_id: 10
+  boot_type_id: <%= Fixtures.identify(:boot_type_static_ip) %>
 two:
   id: 2
   mac: 'AA:BB:CC:DD:EE:FF'
@@ -12,6 +13,7 @@ two:
   usage_type: '2'
   bandwidth: 1000
   host_id: 10
+  boot_type_id: <%= Fixtures.identify(:boot_type_static_ip) %>
 three:
   id: 3
   mac: '00:FF:11:EE:22:DD'
@@ -19,6 +21,7 @@ three:
   usage_type: '1'
   bandwidth: 10
   host_id: 10
+  boot_type_id: <%= Fixtures.identify(:boot_type_static_ip) %>
 four:
   id: 4
   mac: '00:FF:11:EE:22:DD'
@@ -26,11 +29,52 @@ four:
   usage_type: '1'
   bandwidth: 10
   host_id: 10
+  boot_type_id: <%= Fixtures.identify(:boot_type_static_ip) %>
+
 mailserver_nic_one:
-    id: 5
-    mac:        '00:11:22:33:44:55'
+    mac: '00:11:22:33:44:55'
     usage_type: '1'
-    bandwidth:  100
-    host_id:    <%= Fixtures.identify(:mailservers_managed_node) %>
+    bandwidth: 100
+    ip_addr: '172.31.0.15'
+    host_id: <%= Fixtures.identify(:mailservers_managed_node) %>
+    boot_type_id: <%= Fixtures.identify(:boot_type_static_ip) %>
 
+mailserver_nic_two:
+    mac: '22:11:33:66:44:55'
+    usage_type: '1'
+    bandwidth: 100
+    host_id: <%= Fixtures.identify(:mailservers_managed_node) %>
+    boot_type_id: <%= Fixtures.identify(:boot_type_dhcp) %>
 
+fileserver_nic_one:
+    mac: '00:99:00:99:13:07'
+    usage_type: '1'
+    bandwidth: 100
+    host_id: <%= Fixtures.identify(:fileserver_managed_node) %>
+    boot_type_id: <%= Fixtures.identify(:boot_type_dhcp) %>
+
+ldapserver_nic_one:
+    mac: '00:03:02:00:09:06'
+    usage_type: '1'
+    bandwidth: 100
+    bridge: 'ovirtbr0'
+    ip_addr: '172.31.0.25'
+    host_id: <%= Fixtures.identify(:ldapserver_managed_node) %>
+    boot_type_id: <%= Fixtures.identify(:boot_type_static_ip) %>
+
+buildserver_nic_one:
+    mac: '07:17:19:65:03:38'
+    usage_type: '1'
+    bandwidth: 100
+    host_id: <%= Fixtures.identify(:buildserver_managed_node) %>
+    boot_type_id: <%= Fixtures.identify(:boot_type_dhcp) %>
+
+buildserver_nic_two:
+    mac: '07:17:19:65:03:39'
+    usage_type: '1'
+    bandwidth: 100
+    ip_addr: '172.31.0.31'
+    netmask: '255.255.255.0'
+    broadcast: '172.31.0.255'
+    host_id: <%= Fixtures.identify(:buildserver_managed_node) %>
+    boot_type_id: <%= Fixtures.identify(:boot_type_static_ip) %>
diff --git a/src/test/functional/managed_node_configuration_test.rb b/src/test/functional/managed_node_configuration_test.rb
index edb9092..b5a7ec5 100644
--- a/src/test/functional/managed_node_configuration_test.rb
+++ b/src/test/functional/managed_node_configuration_test.rb
@@ -24,24 +24,40 @@ require 'managed_node_configuration'
 # Performs unit tests on the +ManagedNodeConfiguration+ class.
 #
 class ManagedNodeConfigurationTest < Test::Unit::TestCase
+  fixtures :bonding_types
+  fixtures :bondings
+  fixtures :bondings_nics
+  fixtures :boot_types
+  fixtures :hosts
+  fixtures :nics
+
   def setup
-    @host   = Host.new
-    @nic    = Nic.new(:mac => '00:11:22:33:44:55')
-    @host.nics << @nic
+    @host_with_dhcp_card = hosts(:fileserver_managed_node)
+    @host_with_ip_address = hosts(:ldapserver_managed_node)
+    @host_with_multiple_nics = hosts(:buildserver_managed_node)
+    @host_with_bondings = hosts(:mailservers_managed_node)
   end
 
   # Ensures that network interfaces uses DHCP when no IP address is specified.
   #
   def test_generate_with_no_ip_address
+    nic = @host_with_dhcp_card.nics.first
+
     expected = <<-HERE
+#!/bin/bash
+# THIS FILE IS GENERATED!
+cat <<\EOF > /var/tmp/node-augtool
 rm /files/etc/sysconfig/network-scripts/ifcfg-eth0
 set /files/etc/sysconfig/network-scripts/ifcfg-eth0/DEVICE eth0
-set /files/etc/sysconfig/network-scripts/ifcfg-eth0/BOOTPROTO dhcp
+set /files/etc/sysconfig/network-scripts/ifcfg-eth0/BOOTPROTO #{nic.boot_type.proto}
+set /files/etc/sysconfig/network-scripts/ifcfg-eth0/ONBOOT yes
+save
+EOF
     HERE
 
     result = ManagedNodeConfiguration.generate(
-      @host,
-      {'00:11:22:33:44:55' => 'eth0'}
+      @host_with_dhcp_card,
+      {"#{nic.mac}" => 'eth0'}
     )
 
     assert_equal expected, result
@@ -49,75 +65,112 @@ set /files/etc/sysconfig/network-scripts/ifcfg-eth0/BOOTPROTO dhcp
 
   # Ensures that network interfaces use the IP address when it's provided.
   #
-  def test_generate_with_ip_address
-    @nic.ip_addr = '192.168.2.1'
+  def test_generate_with_ip_address_and_bridge
+    nic = @host_with_ip_address.nics.first
 
     expected = <<-HERE
+#!/bin/bash
+# THIS FILE IS GENERATED!
+cat <<\EOF > /var/tmp/node-augtool
 rm /files/etc/sysconfig/network-scripts/ifcfg-eth0
 set /files/etc/sysconfig/network-scripts/ifcfg-eth0/DEVICE eth0
-set /files/etc/sysconfig/network-scripts/ifcfg-eth0/IPADDR 192.168.2.1
+set /files/etc/sysconfig/network-scripts/ifcfg-eth0/BOOTPROTO #{nic.boot_type.proto}
+set /files/etc/sysconfig/network-scripts/ifcfg-eth0/IPADDR #{nic.ip_addr}
+set /files/etc/sysconfig/network-scripts/ifcfg-eth0/NETMASK #{nic.netmask}
+set /files/etc/sysconfig/network-scripts/ifcfg-eth0/BROADCAST #{nic.broadcast}
+set /files/etc/sysconfig/network-scripts/ifcfg-eth0/BRIDGE ovirtbr0
+set /files/etc/sysconfig/network-scripts/ifcfg-eth0/ONBOOT yes
+save
+EOF
     HERE
 
     result = ManagedNodeConfiguration.generate(
-      @host,
-      {'00:11:22:33:44:55' => 'eth0'}
+      @host_with_ip_address,
+      {"#{nic.mac}" => 'eth0'}
     )
 
     assert_equal expected, result
   end
 
-  # Ensures the bridge is added to the configuration if one is defined.
+  # Ensures that more than one NIC is successfully processed.
   #
-  def test_generate_with_bridge
-    @nic.bridge = 'ovirtbr0'
+  def test_generate_with_multiple_nics
+    nic1 = @host_with_multiple_nics.nics[0]
+    nic2 = @host_with_multiple_nics.nics[1]
 
     expected = <<-HERE
+#!/bin/bash
+# THIS FILE IS GENERATED!
+cat <<\EOF > /var/tmp/node-augtool
 rm /files/etc/sysconfig/network-scripts/ifcfg-eth0
 set /files/etc/sysconfig/network-scripts/ifcfg-eth0/DEVICE eth0
-set /files/etc/sysconfig/network-scripts/ifcfg-eth0/BOOTPROTO dhcp
-set /files/etc/sysconfig/network-scripts/ifcfg-eth0/BRIDGE ovirtbr0
+set /files/etc/sysconfig/network-scripts/ifcfg-eth0/BOOTPROTO #{nic1.boot_type.proto}
+set /files/etc/sysconfig/network-scripts/ifcfg-eth0/IPADDR #{nic1.ip_addr}
+set /files/etc/sysconfig/network-scripts/ifcfg-eth0/NETMASK #{nic1.netmask}
+set /files/etc/sysconfig/network-scripts/ifcfg-eth0/BROADCAST #{nic1.broadcast}
+set /files/etc/sysconfig/network-scripts/ifcfg-eth0/ONBOOT yes
+rm /files/etc/sysconfig/network-scripts/ifcfg-eth1
+set /files/etc/sysconfig/network-scripts/ifcfg-eth1/DEVICE eth1
+set /files/etc/sysconfig/network-scripts/ifcfg-eth1/BOOTPROTO #{nic2.boot_type.proto}
+set /files/etc/sysconfig/network-scripts/ifcfg-eth1/ONBOOT yes
+save
+EOF
     HERE
 
     result = ManagedNodeConfiguration.generate(
-      @host,
-      {'00:11:22:33:44:55' => 'eth0'}
-    )
+      @host_with_multiple_nics,
+      {
+        "#{nic1.mac}" => 'eth0',
+        "#{nic2.mac}" => 'eth1'
+      })
 
     assert_equal expected, result
   end
 
-  # Ensures that more than one NIC is successfully processed.
+  # Ensures that the bonding portion is created if the host has a bonded
+  # interface defined.
   #
-  def test_generate_with_multiple_nics
-    @host.nics << Nic.new(:mac => '11:22:33:44:55:66', :ip_addr => '172.31.0.15')
-    @host.nics << Nic.new(:mac => '22:33:44:55:66:77', :ip_addr => '172.31.0.100')
-    @host.nics << Nic.new(:mac => '33:44:55:66:77:88')
+  def test_generate_with_bonding
+    bonding = @host_with_bondings.bondings.first
 
+    nic1 = bonding.nics[0]
+    nic2 = bonding.nics[1]
 
     expected = <<-HERE
+#!/bin/bash
+# THIS FILE IS GENERATED!
+cat <<\EOF > /var/tmp/pre-config-script
+#!/bin/bash
+# THIS FILE IS GENERATED!
+/sbin/modprobe bonding mode=#{bonding.bonding_type.mode}
+EOF
+cat <<\EOF > /var/tmp/node-augtool
+rm /files/etc/sysconfig/network-scripts/ifcfg-#{bonding.interface_name}
+set /files/etc/sysconfig/network-scripts/ifcfg-#{bonding.interface_name}/DEVICE #{bonding.interface_name}
+set /files/etc/sysconfig/network-scripts/ifcfg-#{bonding.interface_name}/IPADDR 172.31.0.15
+set /files/etc/sysconfig/network-scripts/ifcfg-#{bonding.interface_name}/ONBOOT yes
 rm /files/etc/sysconfig/network-scripts/ifcfg-eth0
 set /files/etc/sysconfig/network-scripts/ifcfg-eth0/DEVICE eth0
-set /files/etc/sysconfig/network-scripts/ifcfg-eth0/BOOTPROTO dhcp
+set /files/etc/sysconfig/network-scripts/ifcfg-eth0/MASTER #{bonding.interface_name}
+set /files/etc/sysconfig/network-scripts/ifcfg-eth0/SLAVE yes
+set /files/etc/sysconfig/network-scripts/ifcfg-eth0/ONBOOT yes
 rm /files/etc/sysconfig/network-scripts/ifcfg-eth1
 set /files/etc/sysconfig/network-scripts/ifcfg-eth1/DEVICE eth1
-set /files/etc/sysconfig/network-scripts/ifcfg-eth1/IPADDR 172.31.0.15
-rm /files/etc/sysconfig/network-scripts/ifcfg-eth2
-set /files/etc/sysconfig/network-scripts/ifcfg-eth2/DEVICE eth2
-set /files/etc/sysconfig/network-scripts/ifcfg-eth2/IPADDR 172.31.0.100
-rm /files/etc/sysconfig/network-scripts/ifcfg-eth3
-set /files/etc/sysconfig/network-scripts/ifcfg-eth3/DEVICE eth3
-set /files/etc/sysconfig/network-scripts/ifcfg-eth3/BOOTPROTO dhcp
-    HERE
+set /files/etc/sysconfig/network-scripts/ifcfg-eth1/MASTER #{bonding.interface_name}
+set /files/etc/sysconfig/network-scripts/ifcfg-eth1/SLAVE yes
+set /files/etc/sysconfig/network-scripts/ifcfg-eth1/ONBOOT yes
+save
+EOF
+HERE
 
     result = ManagedNodeConfiguration.generate(
-      @host,
+      @host_with_bondings,
       {
-        '00:11:22:33:44:55' => 'eth0',
-        '11:22:33:44:55:66' => 'eth1',
-        '22:33:44:55:66:77' => 'eth2',
-        '33:44:55:66:77:88' => 'eth3'
+        "#{nic1.mac}" => 'eth0',
+        "#{nic2.mac}" => 'eth1'
       })
 
     assert_equal expected, result
   end
+
 end
diff --git a/src/test/functional/managed_node_controller_test.rb b/src/test/functional/managed_node_controller_test.rb
new file mode 100644
index 0000000..fe76746
--- /dev/null
+++ b/src/test/functional/managed_node_controller_test.rb
@@ -0,0 +1,63 @@
+#
+# 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_helper'
+
+class ManagedNodeControllerTest < ActionController::TestCase
+  fixtures :bonding_types
+  fixtures :bondings
+  fixtures :bondings_nics
+  fixtures :hosts
+  fixtures :nics
+
+  # Ensures that the request fails if it's missing a hostname, or if the
+  # hostname is invalid.
+  #
+  def test_config_with_invalid_hostname
+    get :config
+
+    assert_redirected_to :action => :error
+
+    get :config, {:host => 'invalid.prod.com'}
+
+    assert_redirected_to :action => :error
+  end
+
+  # Ensures the request fails if no mac addresses are supplied.
+  #
+  def test_config_without_macs
+    get :config, {:host => hosts(:mailservers_managed_node).hostname}
+
+    assert_redirected_to :action => :error
+  end
+
+  # Ensures the request succeeds if it is well-formed.
+  #
+  def test_config
+    get :config,
+      {
+      :host => hosts(:mailservers_managed_node).hostname,
+      :macs => {nics(:mailserver_nic_one).mac, 'eth0'}
+    }
+
+    assert_response :success
+    assert @response.body.length, "Did not get a response"
+  end
+
+end
diff --git a/src/test/functional/nic_controller_test.rb b/src/test/functional/nic_controller_test.rb
index b183c0d..74a80f4 100644
--- a/src/test/functional/nic_controller_test.rb
+++ b/src/test/functional/nic_controller_test.rb
@@ -30,8 +30,6 @@ class NicControllerTest < Test::Unit::TestCase
     @controller = NicController.new
     @request    = ActionController::TestRequest.new
     @response   = ActionController::TestResponse.new
-
-    @first_id = nics(:one).id
   end
 
   def test_show
@@ -51,45 +49,4 @@ class NicControllerTest < Test::Unit::TestCase
     assert_redirected_to :controller => 'host', :action => 'show', :id => 1
 
   end
-
-  def test_create
-    num_nics = Nic.count
-
-    post :create, :nic => {}
-
-    assert_response :redirect
-    assert_redirected_to :controller => 'dashboard'
-
-    assert_equal num_nics, Nic.count
-  end
-
-  def test_edit
-    get :edit, :id => @first_id
-
-    assert_response :redirect
-    assert_redirected_to :action => 'show', :id => @first_id
-
-    assert_not_nil assigns(:nic)
-    assert assigns(:nic).valid?
-  end
-
-  def test_update
-    post :update, :id => @first_id
-    assert_response :redirect
-    assert_redirected_to :action => 'show', :id => @first_id
-  end
-
-  def test_destroy
-    assert_nothing_raised {
-      Nic.find(@first_id)
-    }
-
-    post :destroy, :id => @first_id
-    assert_response :redirect
-    assert_redirected_to :action => 'show', :id => @first_id
-
-    assert_nothing_raised {
-      Nic.find(@first_id)
-    }
-  end
 end
diff --git a/src/test/unit/bonding_test.rb b/src/test/unit/bonding_test.rb
new file mode 100644
index 0000000..4bdb079
--- /dev/null
+++ b/src/test/unit/bonding_test.rb
@@ -0,0 +1,79 @@
+#
+# 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_helper'
+
+class BondingTest < ActiveSupport::TestCase
+  fixtures :bondings
+  fixtures :bonding_types
+  fixtures :bondings_nics
+  fixtures :hosts
+  fixtures :nics
+
+  def setup
+    @bonding = Bonding.new(
+      :name           => 'Bonding1',
+      :interface_name => 'bond0',
+      :type_id        => bonding_types(:failover_bonding_type),
+      :host_id        => hosts(:mailservers_managed_node))
+  end
+
+  # Ensures that the name is required.
+  #
+  def test_valid_fails_without_name
+    @bonding.name = ''
+
+    flunk 'Bondings having to have a name.' if @bonding.valid?
+  end
+
+  # Ensures that the interface name is required.
+  #
+  def test_valid_fails_without_interface_name
+    @bonding.interface_name = ''
+
+    flunk 'Bondings have to have an interface name.' if @bonding.valid?
+  end
+
+  # Ensures that the bonding type is required.
+  #
+  def test_valid_fails_without_type
+    @bonding.type_id = nil
+
+    flunk 'Bondings have to have a valid type.' if @bonding.valid?
+  end
+
+  # Ensures that a host is required
+  #
+  def test_valid_fails_without_host
+    @bonding.host_id = nil
+
+    flunk 'Bondings have to have a host.' if @bonding.valid?
+  end
+
+  # Ensure that retrieving a bonding returns its associated objects.
+  #
+  def test_find_all_for_host
+    result = Bonding.find_all_by_host_id(hosts(:mailservers_managed_node))
+
+    assert_equal 1, result.size, 'Did not find the right number of bondings.'
+    assert result[0].nics.empty? == false, 'Did not load any nics.'
+    assert_equal 2, result[0].nics.size, 'Did not load the right set of nics.'
+  end
+
+end
diff --git a/src/test/unit/bonding_type_test.rb b/src/test/unit/bonding_type_test.rb
new file mode 100644
index 0000000..5f2ccb2
--- /dev/null
+++ b/src/test/unit/bonding_type_test.rb
@@ -0,0 +1,34 @@
+#
+# 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_helper'
+
+class BondingTypeTest < ActiveSupport::TestCase
+  def setup
+    @bonding_type = BondingType.new(:label => 'test', :mode => 999)
+  end
+
+  # Ensures that bonding types have a unique mode.
+  #
+  def test_modes_must_be_unique
+    @bonding_type.mode = BondingType.find(:first).mode
+
+    assert @bonding_type.save == false, 'A bonding type with a duplicate mode should not save.'
+  end
+end
diff --git a/src/test/unit/boot_type_test.rb b/src/test/unit/boot_type_test.rb
new file mode 100644
index 0000000..a30e258
--- /dev/null
+++ b/src/test/unit/boot_type_test.rb
@@ -0,0 +1,26 @@
+# 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_helper'
+
+class BootTypeTest < ActiveSupport::TestCase
+  # Replace this with your real tests.
+  def test_truth
+    assert true
+  end
+end
diff --git a/src/host-browser/test-host-browser-awake.rb b/src/test/unit/host_browser_awaken_test.rb
old mode 100755
new mode 100644
similarity index 93%
rename from src/host-browser/test-host-browser-awake.rb
rename to src/test/unit/host_browser_awaken_test.rb
index 02e9146..5340e01
--- a/src/host-browser/test-host-browser-awake.rb
+++ b/src/test/unit/host_browser_awaken_test.rb
@@ -18,7 +18,7 @@
 # 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 File.dirname(__FILE__) + '/../test_helper'
 require 'test/unit'
 require 'flexmock/test_unit'
 
@@ -26,8 +26,10 @@ TESTING=true
 
 require 'host-browser'
 
-# +TestHostBrowserAwaken+
-class TestHostBrowserAwaken < Test::Unit::TestCase
+# +HostBrowserAwakenTest+ ensures that the host-browser daemon works correctly
+# during the identify phase of operation.
+#
+class HostBrowserAwakenTest < Test::Unit::TestCase
 
   def setup
     @session = flexmock('session')
diff --git a/src/test/unit/host_browser_identify_test.rb b/src/test/unit/host_browser_identify_test.rb
new file mode 100644
index 0000000..87c6f0b
--- /dev/null
+++ b/src/test/unit/host_browser_identify_test.rb
@@ -0,0 +1,304 @@
+#!/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_helper'
+
+require 'test/unit'
+require 'flexmock/test_unit'
+require 'dutils'
+
+TESTING=true
+
+require 'host-browser'
+
+# +HostBrowserIdentifyTest+ tests the host-browser server to ensure that it
+# works correctly during the identify mode of operation.
+#
+class HostBrowserIdentifyTest < Test::Unit::TestCase
+  def setup
+    @connection = flexmock('connection')
+    @connection.should_receive(:peeraddr).at_least.once.returns { [nil,nil,nil,"192.168.2.255"] }
+
+    @browser = HostBrowser.new(@connection)
+    @browser.logfile = './unit-test.log'
+
+    # default host info
+    @host_info = {}
+    @host_info['UUID']     = 'node1'
+    @host_info['IPADDR']   = '192.168.2.2'
+    @host_info['HOSTNAME'] = 'prod.corp.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'
+
+    @host_info['NICINFO'] = Array.new
+    @host_info['NICINFO'] << {
+      'MAC'        => '00:11:22:33:44:55',
+      'BANDWIDTH'  => '100',
+      'IFACE_NAME' => 'eth0'}
+
+    @host_info['NICINFO'] << {
+      'MAC'        => '00:77:11:77:19:65',
+      'BANDWIDTH'  => '100',
+      'IFACE_NAME' => 'eth01'}
+  end
+
+  # Ensures that the server is satisfied if the remote system is
+  # making a wakeup call.
+  #
+  def test_get_mode_with_awaken_request
+    @connection.should_receive(:write).with("MODE?\n").once().returns { |request| request.length }
+    @connection.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
+    @connection.should_receive(:write).with("INFO?\n").once().returns { |request| request.length }
+    @connection.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
+    @connection.should_receive(:write).with("INFO?\n").once().returns { |request| request.length }
+    @connection.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
+    @connection.should_receive(:write).with("INFO?\n").once().returns { |request| request.length }
+    @connection.should_receive(:readline).once().returns { "key1=value1\n" }
+    @connection.should_receive(:write).with("ACK key1\n").once().returns { |request| request.length }
+    @connection.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
+    @connection.should_receive(:write).with("INFO?\n").once().returns { |request| request.length }
+    @connection.should_receive(:readline).once().returns { "key1=value1\n" }
+    @connection.should_receive(:write).with("ACK key1\n").once().returns { |request| request.length }
+    @connection.should_receive(:readline).once().returns { "key2=value2\n" }
+    @connection.should_receive(:write).with("ACK key2\n").once().returns { |request| request.length }
+    @connection.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 that, if no NIC info was available, the server raises an
+  # exception.
+  #
+  def test_write_host_info_with_missing_nicinfo
+    @host_info['NICINFO'] = nil
+
+    assert_raise(Exception) { @browser.write_host_info(@host_info) }
+  end
+
+  # Ensures that, if a NIC is present that was already submitted, it
+  # doesn't get re-entered.
+  #
+  def test_write_host_info_with_duplicate_nic
+    # Values taken from the nics.yml fixture
+    @host_info['NICINFO'] << {
+      'MAC'        => '00:11:22:33:44:55',
+      'BANDWIDTH'  => '100',
+      'IFACE_NAME' => 'eth0'
+    }
+
+    assert_nothing_raised { @browser.write_host_info(@host_info) }
+    assert_equal 3, Host.find_by_hostname('prod.corp.com').nics.size, 'Expected three NICs.'
+  end
+
+  # Ensures the browser can properly parse the CPU details.
+  #
+  def test_parse_cpu_info
+    @connection.should_receive(:write).with("INFO?\n").once().returns { |request| request.length }
+    @connection.should_receive(:readline).once().returns { "CPU\n" }
+    @connection.should_receive(:write).with("CPUINFO?\n").once().returns { |request| request.length }
+    @connection.should_receive(:readline).once().returns { "key1=value1\n" }
+    @connection.should_receive(:write).with("ACK key1\n").once().returns { |request| request.length }
+    @connection.should_receive(:readline).once().returns { "key2=value2\n" }
+    @connection.should_receive(:write).with("ACK key2\n").once().returns { |request| request.length }
+    @connection.should_receive(:readline).once().returns { "ENDCPU\n" }
+    @connection.should_receive(:write).with("ACK CPU\n").once().returns { |request| request.length }
+    @connection.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_with_two_entries
+    @connection.should_receive(:write).with("INFO?\n").once().returns { |request| request.length }
+
+    # CPU 0
+    @connection.should_receive(:readline).once().returns { "CPU\n" }
+    @connection.should_receive(:write).with("CPUINFO?\n").once().returns { |request| request.length }
+    @connection.should_receive(:readline).once().returns { "key1=value1\n" }
+    @connection.should_receive(:write).with("ACK key1\n").once().returns { |request| request.length }
+    @connection.should_receive(:readline).once().returns { "key2=value2\n" }
+    @connection.should_receive(:write).with("ACK key2\n").once().returns { |request| request.length }
+    @connection.should_receive(:readline).once().returns { "ENDCPU\n" }
+    @connection.should_receive(:write).with("ACK CPU\n").once().returns { |request| request.length }
+
+    # CPU 1
+    @connection.should_receive(:readline).once().returns { "CPU\n" }
+    @connection.should_receive(:write).with("CPUINFO?\n").once().returns { |request| request.length }
+    @connection.should_receive(:readline).once().returns { "key3=value3\n" }
+    @connection.should_receive(:write).with("ACK key3\n").once().returns { |request| request.length }
+    @connection.should_receive(:readline).once().returns { "key4=value4\n" }
+    @connection.should_receive(:write).with("ACK key4\n").once().returns { |request| request.length }
+    @connection.should_receive(:readline).once().returns { "ENDCPU\n" }
+    @connection.should_receive(:write).with("ACK CPU\n").once().returns { |request| request.length }
+
+    @connection.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
+
+  # Ensures the browser can properly parse the details for a NIC.
+  #
+  def test_parse_nic_info
+    @connection.should_receive(:write).with("INFO?\n").once().returns { |request| request.length }
+    @connection.should_receive(:readline).once().returns { "NIC\n" }
+    @connection.should_receive(:write).with("NICINFO?\n").once().returns { |request| request.length }
+    @connection.should_receive(:readline).once().returns { "key1=value1\n" }
+    @connection.should_receive(:write).with("ACK key1\n").once().returns { |request| request.length }
+    @connection.should_receive(:readline).once().returns { "key2=value2\n" }
+    @connection.should_receive(:write).with("ACK key2\n").once().returns { |request| request.length }
+    @connection.should_receive(:readline).once().returns { "ENDNIC\n" }
+    @connection.should_receive(:write).with("ACK NIC\n").once().returns { |request| request.length }
+    @connection.should_receive(:readline).once().returns { "ENDINFO\n" }
+
+    info = @browser.get_remote_info
+
+    assert_equal 3,info.keys.size, "Should contain four keys"
+    assert info.include?("NICINFO")
+  end
+end
-- 
1.5.5.1




More information about the ovirt-devel mailing list