fuel-astute/spec/unit/cobbler_manager_spec.rb

313 lines
9.3 KiB
Ruby

# Copyright 2014 Mirantis, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
require File.join(File.dirname(__FILE__), '../spec_helper')
describe Astute::CobblerManager do
include SpecHelpers
let(:data) do
{
"engine"=>{
"url"=>"http://localhost/cobbler_api",
"username"=>"cobbler",
"password"=>"cobbler",
"master_ip"=>"127.0.0.1",
},
"task_uuid"=>"a5c44b9a-285a-4a0c-ae65-2ed6b3d250f4",
"nodes" => [
{
'uid' => '1',
'profile' => 'centos-x86_64',
"slave_name"=>"controller-1",
"admin_ip" =>'1.2.3.5',
'power_type' => 'ssh',
'power_user' => 'root',
'power_pass' => '/root/.ssh/bootstrap.rsa',
'power-address' => '1.2.3.5',
'hostname' => 'name.domain.tld',
'name_servers' => '1.2.3.4 1.2.3.100',
'name_servers_search' => 'some.domain.tld domain.tld',
'netboot_enabled' => '1',
'ks_meta' => {
'repo_setup' => {
'repos' => [
{
"type" => "deb",
"name" => "repo1",
"uri" => "ip_address:port/patch",
"suite" => "param1",
"section" => "param2",
"priority" => 1001
},
{
"type" => "rpm",
"name" => "repo2",
"uri" => "ip_address:port/patch",
"priority" => 1
}
]
}
},
'interfaces' => {
'eth0' => {
'mac_address' => '00:00:00:00:00:00',
'static' => '1',
'netmask' => '255.255.255.0',
'ip_address' => '1.2.3.5',
'dns_name' => 'node.mirantis.net',
},
'eth1' => {
'mac_address' => '00:00:00:00:00:01',
'static' => '0',
'netmask' => '255.255.255.0',
'ip_address' => '1.2.3.6',
}
},
'interfaces_extra' => {
'eth0' => {
'peerdns' => 'no',
'onboot' => 'yes',
},
'eth1' => {
'peerdns' => 'no',
'onboot' => 'yes',
}
}
}
]
}
end
let(:reporter) do
reporter = mock('reporter')
reporter.stub_everything
reporter
end
let(:engine) do
cobbler = mock('cobbler')
cobbler.stub_everything
cobbler
end
before(:each) do
Astute::Provision::Cobbler.stubs(:new).returns(engine)
end
let(:cobbler_manager) { Astute::CobblerManager.new(data['engine'], reporter) }
describe '#add_nodes' do
before(:each) do
cobbler_manager.stubs(:sleep)
end
it 'should sync engine status after end' do
engine.stubs(:item_from_hash)
cobbler_manager.expects(:sync).once
cobbler_manager.add_nodes(data['nodes'])
end
end #'add_nodes'
describe '#edit_nodes' do
before(:each) do
cobbler_manager.stubs(:sleep)
end
it 'should edit nodes' do
cobbler_manager.stubs(:sync)
engine.expects(:item_from_hash).with(
'system',
'controller-1',
{'profile' => Astute.config.bootstrap_profile},
:item_preremove => false)
cobbler_manager.edit_nodes(
data['nodes'],
{'profile' => Astute.config.bootstrap_profile}
)
end
it 'should sync at the end of the call' do
engine.stubs(:item_from_hash)
cobbler_manager.expects(:sync)
cobbler_manager.edit_nodes(
data['nodes'],
{'profile' => Astute.config.bootstrap_profile}
)
end
it 'should sync after an error' do
engine.stubs(:item_from_hash).raises(RuntimeError)
cobbler_manager.expects(:sync)
expect{ cobbler_manager.edit_nodes(
data['nodes'],
{'profile' => Astute.config.bootstrap_profile}
)}.to raise_error(RuntimeError)
end
end #'edit_nodes'
describe '#remove_nodes' do
nodes = [
{"slave_name" => "node-1"},
{"slave_name" => "node-2"},
{"slave_name" => "node-3"},
]
system_exists_return_seq = [true, false, true, false, true, false]
system_remove_with_seq = ["node-1", "node-2", "node-3"]
before(:each) do
cobbler_manager.stubs(:sleep)
end
it 'should try to remove nodes using cobbler engine' do
engine.stubs(:system_exists?).returns(*system_exists_return_seq)
engine.expects(:remove_system).returns(true).times(3)
engine.expects(:sync).once
cobbler_manager.remove_nodes(nodes)
end
it 'should try to remove nodes three times before giving up' do
engine.stubs(:system_exists?).returns(true)
engine.expects(:remove_system).times(9)
engine.expects(:sync).once
cobbler_manager.remove_nodes(nodes)
end
it 'should not try to remove nodes if they do not exist' do
engine.stubs(:system_exists?).returns(false)
engine.expects(:remove_system).never
engine.expects(:sync).once
cobbler_manager.remove_nodes(nodes)
end
it 'should try to remove uniq list of nodes' do
nodes << {"slave_name" => "node-1"}
engine.stubs(:system_exists?).returns(*system_exists_return_seq)
engine.expects(:remove_system).times(3)
engine.expects(:sync).once
cobbler_manager.remove_nodes(nodes)
end
end
describe '#reboot_nodes' do
before(:each) do
cobbler_manager.stubs(:sleep)
end
it 'should reboot nodes' do
engine.expects(:power_reboot)
cobbler_manager.reboot_nodes(data['nodes'])
end
context 'splay' do
around(:each) do |example|
old_iops_value = Astute.config.iops
old_splay_factor_value = Astute.config.splay_factor
example.run
Astute.config.iops = old_iops_value
Astute.config.splay_factor = old_splay_factor_value
end
it 'should delay between nodes reboot' do
engine.stubs(:power_reboot)
cobbler_manager.expects(:calculate_splay_between_nodes).returns(5).once
cobbler_manager.expects(:sleep).with(5).once
cobbler_manager.reboot_nodes(data['nodes'])
end
it 'use formula (node + 1) / iops * splay_factor / node' do
Astute.config.iops = 100
Astute.config.splay_factor = 100
engine.stubs(:power_reboot)
cobbler_manager.expects(:sleep).with(2.0).once
cobbler_manager.reboot_nodes(data['nodes'])
end
end #'splay'
end #'reboot_nodes'
describe '#netboot_nodes' do
it 'should netboot nodes' do
cobbler_manager.stubs(:sync)
engine.expects(:netboot).with('controller-1', false)
cobbler_manager.netboot_nodes(data['nodes'], false)
end
it 'should sync at the end of the call' do
engine.stubs(:netboot)
cobbler_manager.expects(:sync)
cobbler_manager.netboot_nodes(data['nodes'], false)
end
it 'should sync after an error' do
engine.stubs(:netboot).raises(RuntimeError)
cobbler_manager.expects(:sync)
expect{cobbler_manager.netboot_nodes(data['nodes'], false)}
.to raise_error(RuntimeError)
end
end #'edit_nodes'
describe '#get_existent_nodes' do
it 'should return existent nodes' do
engine.expects(:system_exists?).with('controller-1').returns(true)
expect(cobbler_manager.get_existent_nodes(data['nodes']))
.to eql(data['nodes'])
end
it 'should not return non existent nodes' do
engine.expects(:system_exists?).with('controller-1').returns(false)
expect(cobbler_manager.get_existent_nodes(data['nodes']))
.to eql([])
end
end #'get_existent_nodes'
describe '#get_mac_duplicate_names' do
it 'should return cobbler names of those systems that have at least one matching mac address' do
engine.expects(:system_by_mac).with('00:00:00:00:00:00').returns({'name' => 'node-XXX'})
engine.expects(:system_by_mac).with('00:00:00:00:00:01').returns(nil)
expect(cobbler_manager.get_mac_duplicate_names(data['nodes']))
.to eql(['node-XXX'])
end
it 'should return uniq list of cobbler names of those systems that have matching mac addresses' do
engine.expects(:system_by_mac).with('00:00:00:00:00:00').returns({'name' => 'node-XXX'})
engine.expects(:system_by_mac).with('00:00:00:00:00:01').returns({'name' => 'node-XXX'})
expect(cobbler_manager.get_mac_duplicate_names(data['nodes']))
.to eql(['node-XXX'])
end
it 'should not return nodes that have not matching mac addresses' do
engine.expects(:system_by_mac).with('00:00:00:00:00:00').returns(nil)
engine.expects(:system_by_mac).with('00:00:00:00:00:01').returns(nil)
expect(cobbler_manager.get_mac_duplicate_names(data['nodes']))
.to eql([])
end
end #'get_existent_nodes'
end