fuel-astute/spec/unit/cobbler_spec.rb

453 lines
16 KiB
Ruby

# Copyright 2013 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')
include Astute::Provision
describe Cobbler do
include SpecHelpers
it "should be able to be initialized with 'url'" do
host = "host.domain.tld"
port = "1234"
path = "/api"
username = 'user'
password = 'pass'
remote = mock()
tmp = XMLRPC::Client
XMLRPC::Client = mock() do
expects(:new).with(host, path, port).returns(remote)
end
Astute::Provision::Cobbler.new(
'url' => "http://#{host}:#{port}#{path}",
'username' => username,
'password' => password
)
XMLRPC::Client = tmp
end
it "should be able to be initialized with 'host', 'port', 'path'" do
username = 'user'
password = 'pass'
host = "host.domain.tld"
path = "/api"
port = "1234"
remote = mock()
tmp = XMLRPC::Client
XMLRPC::Client = mock() do
expects(:new).with(host, path, port).returns(remote)
end
Astute::Provision::Cobbler.new(
'host' => host,
'port' => port,
'path' => path,
'username' => username,
'password' => password
)
XMLRPC::Client = tmp
end
context "cobbler methods" do
before(:each) do
remote = mock() do
stubs(:call)
stubs(:call).with('login', 'cobbler', 'cobbler').returns('remotetoken')
end
@tmp = XMLRPC::Client
XMLRPC::Client = mock() do
stubs(:new).returns(remote)
end
end
let(:data) do
{
'profile' => 'centos-x86_64',
'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' => 'some_param=1 another_param=2',
'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
after(:each) do
XMLRPC::Client = @tmp
end
it "item_from_hash should remove item if 'item_preremove' is true" do
Astute::Provision::Cobbler.any_instance do
expects(:remove_item).with('system', 'name')
end
cobbler = Astute::Provision::Cobbler.new
cobbler.item_from_hash('system', 'name', {}, :item_preremove => true)
end
it "item_from_hash should create new item if it does not exist" do
cobbler = Astute::Provision::Cobbler.new
cobbler.remote.expects(:call).with('has_item', 'system', 'name').returns(false)
cobbler.remote.expects(:call).with('new_item', 'system', cobbler.token).returns('itemid')
cobbler.remote.expects(:call).with('modify_item', 'system', 'itemid', 'name', 'name', cobbler.token)
cobbler.item_from_hash('system', 'name', {}, :item_preremove => true)
end
it "item_from_hash should modify existent item if it does exist" do
cobbler = Astute::Provision::Cobbler.new
cobbler.remote.expects(:call).with('has_item', 'system', 'name').returns(true)
cobbler.remote.expects(:call).with('get_item_handle', 'system', 'name', cobbler.token).returns('itemid')
cobbler.item_from_hash('system', 'name', {}, :item_preremove => false)
end
it "item_from_hash should modify item with cobblerized data" do
cobblerized_data = Astute::Provision::Cobsh.new(data.merge({'what' => 'system', 'name' => 'name'})).cobblerized
cobbler = Astute::Provision::Cobbler.new
cobbler.stubs(:get_item_id).with('system', 'name').returns('itemid')
cobblerized_data.each do |opt, value|
next if opt == 'interfaces'
cobbler.remote.expects(:call).with(
'modify_item',
'system',
'itemid',
opt,
value,
'remotetoken'
)
end
cobbler.item_from_hash('system', 'name', data, :item_preremove => true)
end
it "item_from_hash should modify 'system' interfaces with cobblerized['interfaces']" do
cobblerized_data = Astute::Provision::Cobsh.new(data.merge({'what' => 'system', 'name' => 'name'})).cobblerized
cobbler = Astute::Provision::Cobbler.new
cobbler.stubs(:get_item_id).with('system', 'name').returns('itemid')
cobbler.remote.expects(:call).with(
'modify_system',
'itemid',
'modify_interface',
cobblerized_data['interfaces'],
'remotetoken')
cobbler.item_from_hash('system', 'name', data, :item_preremove => true)
end
it 'should generate token in every cobbler call where token need' do
remote = mock() do
stubs(:call).twice.with('sync', 'remotetoken')
expects(:call).twice.with('login', 'cobbler', 'cobbler').returns('remotetoken')
end
XMLRPC::Client = mock() do
stubs(:new).returns(remote)
end
cobbler = Astute::Provision::Cobbler.new
cobbler.sync
cobbler.sync
end
it 'items_by_criteria should return list of nodes that match passed criteria' do
cobbler = Astute::Provision::Cobbler.new
cobbler.remote.expects(:call).with('find_items', 'system', {'mac_address' => '00:00:00:00:00:00'})
.returns([{'name' => 'node-XXX'}])
expect(cobbler.items_by_criteria('system', {'mac_address' => '00:00:00:00:00:00'})).to eql([{'name' => 'node-XXX'}])
end
it 'system_by_mac should return node that match passed mac address' do
cobbler = Astute::Provision::Cobbler.new
cobbler.stubs(:items_by_criteria).with('system', {'mac_address' => '00:00:00:00:00:00'})
.returns([{'name' => 'node-XXX'}])
expect(cobbler.system_by_mac('00:00:00:00:00:00')).to eql({'name' => 'node-XXX'})
end
it 'should try sync several time before raise a exception (Net)' do
remote = mock() do
stubs(:call).with('sync', 'remotetoken')
.raises(Net::ReadTimeout)
.then.returns(nil)
stubs(:call).twice.with('login', 'cobbler', 'cobbler').returns('remotetoken')
end
XMLRPC::Client = mock() do
stubs(:new).returns(remote)
end
cobbler = Astute::Provision::Cobbler.new
cobbler.stubs(:sleep).with(10).times(1)
expect { cobbler.sync }.not_to raise_exception(Net::ReadTimeout)
end
it 'should try sync several time before raise a exception (XMLRPC)' do
remote = mock() do
stubs(:call).with('sync', 'remotetoken')
.raises(XMLRPC::FaultException.new("", ""))
.then.returns(nil)
stubs(:call).twice.with('login', 'cobbler', 'cobbler').returns('remotetoken')
end
XMLRPC::Client = mock() do
stubs(:new).returns(remote)
end
cobbler = Astute::Provision::Cobbler.new
cobbler.stubs(:sleep).with(10).times(1)
expect { cobbler.sync }.not_to raise_exception(XMLRPC::FaultException)
end
it 'should raise a exception if sync do not succeed after several(3) tries' do
remote = mock() do
stubs(:call).with('sync', 'remotetoken')
.raises(Net::ReadTimeout)
.then.raises(Net::ReadTimeout)
.then.raises(Net::ReadTimeout)
stubs(:call).times(3).with('login', 'cobbler', 'cobbler').returns('remotetoken')
end
XMLRPC::Client = mock() do
stubs(:new).returns(remote)
end
cobbler = Astute::Provision::Cobbler.new
cobbler.stubs(:sleep).with(10).times(2)
expect { cobbler.sync }.to raise_exception(Net::ReadTimeout)
end
end
end
describe Cobsh do
before(:each) do
@aliases = {
'ks_meta' => ['ksmeta'],
'mac_address' => ['mac'],
'ip_address' => ['ip'],
}
@fields = {
'system' => {
'fields' => [
'name', 'owners', 'profile', 'image', 'status', 'kernel_options',
'kernel_options_post', 'ks_meta', 'enable_gpxe', 'proxy',
'netboot_enabled', 'kickstart', 'comment', 'server',
'virt_path', 'virt_type', 'virt_cpus', 'virt_file_size',
'virt_disk_driver', 'virt_ram', 'virt_auto_boot', 'power_type',
'power_address', 'power_user', 'power_pass', 'power_id',
'hostname', 'gateway', 'name_servers', 'name_servers_search',
'ipv6_default_device', 'ipv6_autoconfiguration', 'mgmt_classes',
'mgmt_parameters', 'boot_files', 'fetchable_files',
'template_files', 'redhat_management_key', 'redhat_management_server',
'repos_enabled', 'ldap_enabled', 'ldap_type', 'monit_enabled',
],
'interfaces_fields' => [
'mac_address', 'mtu', 'ip_address', 'interface_type',
'interface_master', 'bonding_opts', 'bridge_opts',
'management', 'static', 'netmask', 'dhcp_tag', 'dns_name',
'static_routes', 'virt_bridge', 'ipv6_address', 'ipv6_secondaries',
'ipv6_mtu', 'ipv6_static_routes', 'ipv6_default_gateway'
],
'special' => ['interfaces', 'interfaces_extra']
},
'profile' => {
'fields' => [
'name', 'owners', 'distro', 'parent', 'enable_gpxe',
'enable_menu', 'kickstart', 'kernel_options', 'kernel_options_post',
'ks_meta', 'proxy', 'repos', 'comment', 'virt_auto_boot',
'virt_cpus', 'virt_file_size', 'virt_disk_driver',
'virt_ram', 'virt_type', 'virt_path', 'virt_bridge',
'dhcp_tag', 'server', 'name_servers', 'name_servers_search',
'mgmt_classes', 'mgmt_parameters', 'boot_files', 'fetchable_files',
'template_files', 'redhat_management_key', 'redhat_management_server'
]
},
'distro' => {
'fields' => ['name', 'owners', 'kernel', 'initrd', 'kernel_options',
'kernel_options_post', 'ks_meta', 'arch', 'breed',
'os_version', 'comment', 'mgmt_classes', 'boot_files',
'fetchable_files', 'template_files', 'redhat_management_key',
'redhat_management_server']
}
}
@minimal_data = {
'what' => 'system',
'name' => 'name',
}
end
it "should raise exception when init data do not contain 'what'" do
expect {
Astute::Provision::Cobsh.new({'what'=>'system'})
}.to raise_exception(/Cobbler hash must have 'name' key/)
end
it "should raise exception when init data do not contain 'name'" do
expect {
Astute::Provision::Cobsh.new({'name'=>'name'})
}.to raise_exception(/Cobbler hash must have 'what' key/)
end
it "should raise exception when init data 'what' is not supported" do
expect {
Astute::Provision::Cobsh.new({
'what' => 'unsupported',
'name'=>'name'
})
}.to raise_exception(/Unsupported 'what' value/)
end
it "should replace '-' into '_' in init data keys" do
data = @minimal_data.merge({'power_id' => 'blabla'})
expected = {
'name' => data['name'],
'power_id' => 'blabla'
}
Astute::Provision::Cobsh.new(data).cobblerized.should eql(expected)
end
it "returns cobblerizied data with aliased keys" do
@fields.each do |what, what_data|
@aliases.each do |main_alias, alias_list|
data = {'name' => 'name'}
expected = {'name' => 'name'}
if what_data['fields'].include? main_alias
data['what'] = what
alias_list.each do |a|
data2 = data.merge({a => 'blabla'})
expected2 = expected.merge({main_alias => 'blabla'})
Astute::Provision::Cobsh.new(data2).cobblerized.should eql(expected2)
end
elsif what_data.has_key? 'interfaces_fields' and what_data['interfaces_fields'].include? main_alias
data['what'] = what
alias_list.each do |a|
data2 = data.merge({
'interfaces' => {
'eth0' => {a => 'blabla'}
}
})
expected2 = expected.merge({
'interfaces' => {
"#{main_alias}-eth0" => 'blabla'
}
})
Astute::Provision::Cobsh.new(data2).cobblerized.should eql(expected2)
end
end
end
end
end
it "should raise exception when keys are duplicated in init data" do
data = @minimal_data.merge({'ks-meta' => 'ks-meta', 'ksmeta' => 'ksmeta'})
expect {
Astute::Provision::Cobsh.new(data).cobblerized
}.to raise_exception(/Wrong cobbler data: .* is duplicated/)
end
it "should cut out 'system' unsupported keys" do
data = {'what' => 'system'}
expected = {}
@fields['system']['fields'].each do |f|
data[f] = f
expected[f] = f
end
@fields['system']['interfaces_fields'].each do |f|
data['interfaces'] = {'eth0' => {}} unless data.has_key? 'interfaces'
data['interfaces']['eth0'][f] = f
expected['interfaces'] = {} unless expected.has_key? 'interfaces'
expected['interfaces']["#{f}-eth0"] = f
end
data['unsupported'] = 'unsupported'
Astute::Provision::Cobsh.new(data).cobblerized.should eql(expected)
end
it "should cut out 'profile' unsupported keys" do
data = {'what' => 'profile'}
expected = {}
@fields['profile']['fields'].each do |f|
data[f] = f
expected[f] = f
end
data['unsupported'] = 'unsupported'
Astute::Provision::Cobsh.new(data).cobblerized.should eql(expected)
end
it "should cut out 'distro' unsupported keys" do
data = {'what' => 'distro'}
expected = {}
@fields['distro']['fields'].each do |f|
data[f] = f
expected[f] = f
end
data['unsupported'] = 'unsupported'
Astute::Provision::Cobsh.new(data).cobblerized.should eql(expected)
end
it "should append extra interfaces data into ks_meta" do
RSpec::Matchers.define :ks_meta_equal? do |ks_meta|
match do |ks_meta_self|
equal = true
ks_meta_self.strip.split.each do |i|
equal = false unless ks_meta =~ /.*?#{i}.*?/
end
ks_meta.strip.split.each do |i|
equal = false unless ks_meta_self =~ /.*?#{i}.*?/
end
equal
end
end
data = @minimal_data.merge({
'interfaces_extra' => {
'eth0' => {
'blabla' => 'blabla',
'blabla2' => 'blabla2',
}
}
})
expected_ks_meta = "interface_extra_eth0_blabla=blabla interface_extra_eth0_blabla2=blabla2"
Astute::Provision::Cobsh.new(data).cobblerized['ks_meta'].should ks_meta_equal?(expected_ks_meta)
end
end