Darryl L. Pierce
2008-Sep-18 17:46 UTC
[Ovirt-devel] [PATCH server] Added the ability for NICs to be bonded for load balancing and failover.
From: Darryl Pierce <mcpierce at threshold.(none)> 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 Pierce <mcpierce at threshold.(none)> Signed-off-by: Darryl Pierce <mcpierce at mcpierce-laptop.(none)> --- src/app/controllers/managed_node_controller.rb | 67 +++++ src/app/controllers/nic_controller.rb | 5 +- 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 | 26 +- src/app/models/nic.rb | 3 + src/config/database.yml | 3 +- src/config/environment.rb | 2 + src/db/migrate/019_create_bonding_types.rb | 39 +++ 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 | 16 +- src/host-browser/test-host-browser-awake.rb | 94 ------ src/host-browser/test-host-browser-identify.rb | 283 ------------------ src/lib/managed_node_configuration.rb | 112 ++++++-- 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 | 44 +++- src/test/fixtures/nics.yml | 52 +++- .../functional/managed_node_configuration_test.rb | 151 +++++++---- .../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 | 8 + src/test/unit/host_browser_awaken_test.rb | 96 ++++++ src/test/unit/host_browser_identify_test.rb | 304 ++++++++++++++++++++ 32 files changed, 1271 insertions(+), 533 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-awake.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 create mode 100644 src/test/unit/host_browser_awaken_test.rb 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..c3133b8 --- /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..d5551d4 100644 --- a/src/app/controllers/nic_controller.rb +++ b/src/app/controllers/nic_controller.rb @@ -20,7 +20,7 @@ class NicController < ApplicationController # GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html) verify :method => :post, :only => [ :destroy, :create, :update ], - :redirect_to => { :controller => 'dashboard' } + :redirect_to => { :controller => 'dashboard' } def show set_perms(@perm_obj) @@ -44,8 +44,9 @@ class NicController < ApplicationController def destroy 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..9c5d94c --- /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..ba9eb2e 100644 --- a/src/app/models/host.rb +++ b/src/app/models/host.rb @@ -20,23 +20,25 @@ require 'util/ovirt' class Host < ActiveRecord::Base - belongs_to :hardware_pool + 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 + def consuming_resources + find(:all, :conditions=>{:state=>Vm::RUNNING_STATES}) end + has_many :tasks, :class_name => "HostTask", :dependent => :destroy, :order => "id DESC" do def queued find(:all, :conditions=>{:state=>Task::STATE_QUEUED}) end def pending_clear_tasks find(:all, :conditions=>{:state=>Task::WORKING_STATES, - :action=>HostTask::ACTION_CLEAR_VMS}) + :action=>HostTask::ACTION_CLEAR_VMS}) end end @@ -44,10 +46,10 @@ class Host < ActiveRecord::Base has_many :smart_pools, :through => :smart_pool_tags acts_as_xapian :texts => [ :hostname, :uuid, :hypervisor_type, :arch ], - :values => [ [ :created_at, 0, "created_at", :date ], - [ :updated_at, 1, "updated_at", :date ] ], - :terms => [ [ :hostname, 'H', "hostname" ], - [ :search_users, 'U', "search_users" ] ] + :values => [ [ :created_at, 0, "created_at", :date ], + [ :updated_at, 1, "updated_at", :date ] ], + :terms => [ [ :hostname, 'H', "hostname" ], + [ :search_users, 'U', "search_users" ] ] KVM_HYPERVISOR_TYPE = "KVM" diff --git a/src/app/models/nic.rb b/src/app/models/nic.rb index c1fc542..293c29a 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/database.yml b/src/config/database.yml index c60ee8f..b87fa3e 100644 --- a/src/config/database.yml +++ b/src/config/database.yml @@ -34,7 +34,8 @@ development: test: adapter: postgresql database: ovirt_test - username: postgres + username: ovirt + password: v23zj59an host: localhost production: diff --git a/src/config/environment.rb b/src/config/environment.rb index ff6f6e8..57fd461 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' \ No newline at end of file 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..c5d3171 --- /dev/null +++ b/src/db/migrate/019_create_bonding_types.rb @@ -0,0 +1,39 @@ +# +# 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 + + 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..c7155bd --- /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..32f7703 --- /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..dc62fbf --- /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..8b4f93d 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..." + 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-awake.rb b/src/host-browser/test-host-browser-awake.rb deleted file mode 100755 index 02e9146..0000000 --- a/src/host-browser/test-host-browser-awake.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}/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/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..d07d0b9 100644 --- a/src/lib/managed_node_configuration.rb +++ b/src/lib/managed_node_configuration.rb @@ -1,26 +1,25 @@ -# +# # 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. +# +# 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. # +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' @@ -29,19 +28,74 @@ class ManagedNodeConfiguration def self.generate(host, macs) result = StringIO.new - - 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 + + result.puts "#!/bin/bash" + result.puts "# THIS FILE IS GENERATED!" + + # first process any bondings that're defined + unless host.bondings.empty? + result.puts "echo \"" + 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 "\" > /var/tmp/pre-config-script" end + + # now process the network interfaces and bondings + result.puts "echo \"" + + 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| + # 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 "\" > /var/tmp/node-augtool" 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..ad70beb --- /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 \ No newline at end of file diff --git a/src/test/fixtures/bondings.yml b/src/test/fixtures/bondings.yml new file mode 100644 index 0000000..338fe35 --- /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 \ No newline at end of file diff --git a/src/test/fixtures/bondings_nics.yml b/src/test/fixtures/bondings_nics.yml new file mode 100644 index 0000000..7aa1d7a --- /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) %> \ No newline at end of file diff --git a/src/test/fixtures/boot_types.yml b/src/test/fixtures/boot_types.yml new file mode 100644 index 0000000..98ac400 --- /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 \ No newline at end of file diff --git a/src/test/fixtures/hosts.yml b/src/test/fixtures/hosts.yml index 44397da..0e20c93 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' @@ -41,7 +40,7 @@ five: hostname: 'pipeline.foobar.com' arch: 'xeon' memory: 1048576 - is_disabled: 0 + is_disabled: 0 hypervisor_type: 'kvm' hardware_pool_id: 3 six: @@ -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) %> \ No newline at end of file diff --git a/src/test/fixtures/nics.yml b/src/test/fixtures/nics.yml index 8726fb6..d1cdbdf 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) %> \ No newline at end of file diff --git a/src/test/functional/managed_node_configuration_test.rb b/src/test/functional/managed_node_configuration_test.rb index edb9092..388421e 100644 --- a/src/test/functional/managed_node_configuration_test.rb +++ b/src/test/functional/managed_node_configuration_test.rb @@ -15,7 +15,7 @@ # 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. +# also available at http://www.gnu.org/copyleft/gpl.html. require File.dirname(__FILE__) + '/../test_helper' require 'test/unit' @@ -24,100 +24,153 @@ 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 + def test_generate_with_no_ip_address + nic = @host_with_dhcp_card.nics.first + expected = <<-HERE +#!/bin/bash +# THIS FILE IS GENERATED! +echo " 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 +" > /var/tmp/node-augtool HERE result = ManagedNodeConfiguration.generate( - @host, - {'00:11:22:33:44:55' => 'eth0'} + @host_with_dhcp_card, + {"#{nic.mac}" => 'eth0'} ) assert_equal expected, result end - + # 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! +echo " 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 +" > /var/tmp/node-augtool 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! +echo " 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 +" > /var/tmp/node-augtool 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! +echo " +#!/bin/bash +# THIS FILE IS GENERATED! +/sbin/modprobe bonding mode=#{bonding.bonding_type.mode} +" > /var/tmp/pre-config-script +echo " +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 +" > /var/tmp/node-augtool +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 + +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..c03edd4 --- /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..ccc65d8 --- /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..6267052 --- /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..5dfaa68 --- /dev/null +++ b/src/test/unit/boot_type_test.rb @@ -0,0 +1,8 @@ +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/test/unit/host_browser_awaken_test.rb b/src/test/unit/host_browser_awaken_test.rb new file mode 100644 index 0000000..5340e01 --- /dev/null +++ b/src/test/unit/host_browser_awaken_test.rb @@ -0,0 +1,96 @@ +#!/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' + +TESTING=true + +require 'host-browser' + +# +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') + @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/src/test/unit/host_browser_identify_test.rb b/src/test/unit/host_browser_identify_test.rb new file mode 100644 index 0000000..e43507c --- /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
Darryl L. Pierce
2008-Sep-19 20:34 UTC
[Ovirt-devel] [PATCH server] Added the ability for NICs to be bonded for load balancing and failover.
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
Darryl L. Pierce
2008-Sep-19 21:10 UTC
[Ovirt-devel] [PATCH server] Added the ability for NICs to be bonded for load balancing and failover.
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 one 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/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 ++++++++++++++++++++ 29 files changed, 1168 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..5a3ad45 --- /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/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