Ovs2Lnx patch config saving for CentOS7/RHEL7

*Implement ovs2lnx patch config saving
*Implement ovs bridge config saving (remove dirty hacks)
*Test coverage

Change-Id: I9f50a8ea3a7b4d7d03585375ac47069c6408c92a
Closes-bug: #1513134
This commit is contained in:
Stanislav Makar 2015-11-04 16:48:55 +00:00
parent f93b87f54d
commit da32b883b3
12 changed files with 439 additions and 31 deletions

View File

@ -129,6 +129,11 @@ class Puppet::Provider::L23_stored_config_centos < Puppet::Provider::L23_stored_
hash.delete('PREFIX')
end
hash.delete('DEVICETYPE') if hash['DEVICETYPE']
# Do extra actions if ovs2lnx patch cord
hash = self.parse_patch_bridges(hash) if ( hash.has_key?('BRIDGE') and hash.has_key?('OVS_BRIDGE') )
hash = self.parse_bond_opts(hash) if ( hash.has_key?('TYPE') and hash['TYPE'] =~ %r{Bond} )
props = self.mangle_properties(hash)
@ -143,6 +148,11 @@ class Puppet::Provider::L23_stored_config_centos < Puppet::Provider::L23_stored_
raise Puppet::Error, "self.check_if_provider(if_data) Should be implemented in more specific class."
end
def self.parse_patch_bridges(hash)
# Do nothing, this is only needed for ovs provider
hash
end
def self.parse_bond_opts(hash)
if hash.has_key?('BONDING_OPTS')
bonding_opts_line = hash['BONDING_OPTS'].gsub('"', '').split(' ')
@ -207,7 +217,8 @@ class Puppet::Provider::L23_stored_config_centos < Puppet::Provider::L23_stored_
end
def self.mangle__method(val)
(['manual', 'static'].include? val.to_s.downcase) ? :none : val.to_sym
val = 'manual' if val.to_s == 'none'
val.to_s.downcase
end
def self.mangle__ethtool(val)
@ -267,10 +278,7 @@ class Puppet::Provider::L23_stored_config_centos < Puppet::Provider::L23_stored_
property_mappings.keys.select{|v| ! properties_fake.include?(v)}.each do |type_name|
val = provider.send(type_name)
if val.is_a?(Array)
val.select { |x| x.to_s != 'absent' or x.to_s != '' }
val = false if val.empty?
end
next if ( val.is_a?(Array) and val.reject{ |x| x.to_s == 'absent' }.empty? )
if val and val.to_s != 'absent'
props[type_name] = val
end
@ -283,14 +291,15 @@ class Puppet::Provider::L23_stored_config_centos < Puppet::Provider::L23_stored_
props[:slave] = 'yes'
end
# Do extra actions if ovs2lnx patch cord
props = self.format_patch_bridges(props) if ( props[:if_type].to_s == 'vport' and props[:bridge].size > 1 )
props = self.format_bond_opts(props) if props.has_key?(:bond_mode)
debug("format_file('#{filename}')::properties: #{props.inspect}")
pairs = self.unmangle_properties(provider, props)
if pairs['TYPE'] == :OVSBridge
pairs['DEVICETYPE'] = 'ovs'
end
pairs['DEVICETYPE'] = 'ovs' if pairs['TYPE'].to_s =~ /OVS/
if pairs['ROUTES']
route_filename = "#{self.script_directory}/route-#{provider.name}"
@ -331,6 +340,11 @@ class Puppet::Provider::L23_stored_config_centos < Puppet::Provider::L23_stored_
self.write_file file, content
end
def self.format_patch_bridges(props)
# Do nothing, this is only needed for ovs provider
props
end
def self.format_bond_opts(props)
bond_options = []
bond_properties = property_mappings.select { |k, v| k.to_s =~ %r{bond_.*} and !([:bond_master].include?(k)) }

View File

@ -7,6 +7,7 @@ class Puppet::Provider::L23_stored_config_ovs_centos < Puppet::Provider::L23_sto
rv.merge!({
:devicetype => 'DEVICETYPE',
:bridge => 'OVS_BRIDGE',
:lnx_bridge => 'BRIDGE',
:bond_slaves => 'BOND_IFACES',
:bonding_opts => 'OVS_OPTIONS',
:bond_mode => 'bond_mode',
@ -24,21 +25,63 @@ class Puppet::Provider::L23_stored_config_ovs_centos < Puppet::Provider::L23_sto
def self.properties_fake
rv = super
rv.push(:devicetype)
rv.push(:lnx_bridge)
return rv
end
#Dirty hack which writes config files for OVS
#bridges into /tmp directory
def select_file
if name == 'br-prv' or name == 'br-floating'
"/tmp/ifcfg-#{name}"
else
"#{self.class.script_directory}/ifcfg-#{name}"
def self.get_catalog
return unless @all_providers
first_provider = @all_providers.first
return unless first_provider
class << first_provider
attr_reader :resource
end
first_provider_resource = first_provider.resource
first_provider_resource.catalog
end
def self.resource_in_catalog(type,title)
catalog = get_catalog
return unless catalog
catalog.resources.find do |res|
res.type == type.to_sym and res.name == title
end
end
def self.provider_of(title)
# This function is finding out the provider of bridge
found_resource = resource_in_catalog :l23_stored_config, title
return unless found_resource
found_resource.provider.class.name
end
def self.format_patch_bridges(props)
bridges = props[:bridge]
raise Puppet::Error, %{Patch #{props[:name]} has more than 2 bridges: #{bridges}. Patch can connect *ONLY* 2 bridges!} if bridges.size >2
lnx_bridge = []
ovs_bridge = []
bridges.each do |bridge|
bridge_provider = provider_of(bridge)
if bridge_provider.to_s =~ /lnx_centos/
lnx_bridge << bridge
elsif bridge_provider.to_s =~ /ovs_centos/
ovs_bridge << bridge
else
raise Puppet::Error, %{Patch #{props[:name]}: the bridge #{bridge} provider #{bridge_provider} is not supported!}
end
end
if lnx_bridge.size > ovs_bridge.size
provider_problem = lnx_bridge
elsif ovs_bridge.size > lnx_bridge.size
provider_problem = ovs_bridge
end
raise Puppet::Error, %{Patch #{props[:name]} has the same provider bridges: #{provider_problem} !} if provider_problem
props[:lnx_bridge] = lnx_bridge
props[:bridge] = ovs_bridge
props
end
def self.format_bond_opts(props)
props[:devicetype] = 'ovs'
bond_options = []
bond_properties = property_mappings.select { |k, v| k.to_s =~ %r{bond_.*} and !([:bond_slaves].include?(k)) }
bond_properties.each do |param, transform |
@ -51,6 +94,12 @@ class Puppet::Provider::L23_stored_config_ovs_centos < Puppet::Provider::L23_sto
props
end
def self.parse_patch_bridges(hash)
hash['OVS_BRIDGE'] = [hash['OVS_BRIDGE'], hash['BRIDGE']].join(' ')
hash.delete('BRIDGE')
hash
end
def self.parse_bond_opts(hash)
pair_regex = %r/^\s*(.+?)\s*=\s*(.*)\s*$/
if hash.has_key?('OVS_OPTIONS')
@ -68,11 +117,15 @@ class Puppet::Provider::L23_stored_config_ovs_centos < Puppet::Provider::L23_sto
end
def self.unmangle__if_type(provider, val)
"OVS#{val.to_s.capitalize}".to_sym
val = "OVS#{val.to_s.capitalize}".to_sym
val = 'OVSIntPort' if val.to_s == 'OVSVport'
val
end
def self.mangle__if_type(val)
val.gsub('OVS', '').downcase.to_sym
val = val.gsub('OVS', '').downcase.to_sym
val = :vport if val.to_s == 'intport'
val
end
def self.unmangle__bond_slaves(provider, val)
@ -83,18 +136,20 @@ class Puppet::Provider::L23_stored_config_ovs_centos < Puppet::Provider::L23_sto
val.split(' ')
end
#Dirty hack which deletes OVS bridges from patch OVS
#interfaces
def self.unmangle__bridge(provider, val)
if val.length == 2
val.delete('br-prv') if val.include?('br-prv')
val.delete('br-floating') if val.include?('br-floating')
val
end
def self.unmangle__lnx_bridge(provider, val)
val.join()
end
def self.unmangle__bridge(provider, val)
val.join()
end
def self.mangle__bridge(val)
val.split(' ')
end
end
# vim: set ts=2 sw=2 et :

View File

@ -402,7 +402,7 @@ class Puppet::Provider::L23_stored_config_ubuntu < Puppet::Provider::L23_stored_
property_mappings.reject{|k,v| (properties_fake.include?(k) or v.empty?)}.keys.each do |type_name|
next if props.has_key? type_name
val = provider.send(type_name)
val = false if ( val.is_a?(Array) and val.reject{ |x| x.to_s == 'absent' }.empty? )
next if ( val.is_a?(Array) and val.reject{ |x| x.to_s == 'absent' }.empty? )
if val and val.to_s != 'absent'
props[type_name] = val
end

View File

@ -0,0 +1,4 @@
BOOTPROTO=none
DEVICE=lnx-bridge
ONBOOT=yes
TYPE=Bridge

View File

@ -0,0 +1,6 @@
BOOTPROTO=none
DEVICE=ovs-bridge
ONBOOT=yes
MTU=9000
TYPE=OVSBridge
DEVICETYPE=ovs

View File

@ -0,0 +1,7 @@
BOOTPROTO=none
DEVICE=ovs2lnx-patch
ONBOOT=yes
TYPE=OVSIntPort
OVS_BRIDGE=ovs-br
BRIDGE=lnx-br
DEVICETYPE=ovs

View File

@ -74,7 +74,7 @@ describe Puppet::Type.type(:l23_stored_config).provider(:lnx_centos6) do
context "the method property" do
context 'when dhcp' do
let(:data) { subject.class.parse_file('eth0', fixture_data('ifcfg-eth0'))[0] }
it { expect(data[:method]).to eq :dhcp }
it { expect(data[:method]).to eq 'dhcp' }
end
end

View File

@ -0,0 +1,92 @@
require 'spec_helper'
require 'yaml'
describe Puppet::Type.type(:l23_stored_config).provider(:lnx_centos7) do
let(:facts) do
{
:osfamily => 'Redhat',
:operatingsystem => 'CentOS',
:l23_os => 'centos7',
}
end
let(:input_data) do
{
:ovs_bridge => {
:name => 'lnx-bridge',
:ensure => 'present',
:if_type => 'bridge',
:onboot => true,
:method => 'manual',
:provider => 'lnx_centos7',
},
}
end
let(:resources) do
resources = {}
input_data.each do |name, res|
resources.store name, Puppet::Type.type(:l23_stored_config).new(res)
end
return resources
end
let(:providers) do
providers = {}
resources.each do |name, resource|
provider = resource.provider
if ENV['SPEC_PUPPET_DEBUG']
class << provider
def debug(msg)
puts msg
end
end
end
provider.create
providers.store name, provider
end
providers
end
before(:each) do
puppet_debug_override()
end
def fixture_path
File.join(PROJECT_ROOT, 'spec', 'fixtures', 'provider', 'l23_stored_config', 'centos7_bridges')
end
def fixture_file(file)
File.join(fixture_path, file)
end
def fixture_data(file)
File.read(fixture_file(file))
end
context "OVS bridge" do
context 'format file' do
subject { providers[:ovs_bridge] }
let(:cfg_file) { subject.class.format_file('filepath', [subject]) }
it { expect(cfg_file).to match(%r{DEVICE=lnx-bridge}) }
it { expect(cfg_file).to match(%r{BOOTPROTO=none}) }
it { expect(cfg_file).to match(%r{ONBOOT=yes}) }
it { expect(cfg_file).to match(%r{TYPE=Bridge}) }
it { expect(cfg_file.split(/\n/).reject{|x| x=~/^\s*$/}.length). to eq(4) } # no more lines in the interface file
end
context "parse bridge data from fixture" do
let(:res) { subject.class.parse_file('lnx-bridge', fixture_data('ifcfg-lnx-bridge'))[0] }
it { expect(res[:name]).to eq 'lnx-bridge' }
it { expect(res[:if_type].to_s).to eq 'bridge' }
it { expect(res[:method].to_s).to eq 'manual' }
it { expect(res[:onboot]).to eq true }
it { expect(res[:provider]).to eq :lnx_centos7 }
end
end
end

View File

@ -74,7 +74,7 @@ describe Puppet::Type.type(:l23_stored_config).provider(:lnx_centos7) do
context "the method property" do
context 'when dhcp' do
let(:data) { subject.class.parse_file('eth0', fixture_data('ifcfg-eth0'))[0] }
it { expect(data[:method]).to eq :dhcp }
it { expect(data[:method]).to eq 'dhcp' }
end
end

View File

@ -74,7 +74,7 @@ describe Puppet::Type.type(:l23_stored_config).provider(:lnx_redhat7) do
context "the method property" do
context 'when dhcp' do
let(:data) { subject.class.parse_file('eth0', fixture_data('ifcfg-eth0'))[0] }
it { expect(data[:method]).to eq :dhcp }
it { expect(data[:method]).to eq 'dhcp' }
end
end

View File

@ -0,0 +1,96 @@
require 'spec_helper'
require 'yaml'
describe Puppet::Type.type(:l23_stored_config).provider(:ovs_centos7) do
let(:facts) do
{
:osfamily => 'Redhat',
:operatingsystem => 'CentOS',
:l23_os => 'centos7',
}
end
let(:input_data) do
{
:ovs_bridge => {
:name => 'ovs-bridge',
:ensure => 'present',
:if_type => 'bridge',
:mtu => '9000',
:onboot => true,
:method => 'manual',
:provider => 'ovs_centos7',
},
}
end
let(:resources) do
resources = {}
input_data.each do |name, res|
resources.store name, Puppet::Type.type(:l23_stored_config).new(res)
end
return resources
end
let(:providers) do
providers = {}
resources.each do |name, resource|
provider = resource.provider
if ENV['SPEC_PUPPET_DEBUG']
class << provider
def debug(msg)
puts msg
end
end
end
provider.create
providers.store name, provider
end
providers
end
before(:each) do
puppet_debug_override()
end
def fixture_path
File.join(PROJECT_ROOT, 'spec', 'fixtures', 'provider', 'l23_stored_config', 'centos7_bridges')
end
def fixture_file(file)
File.join(fixture_path, file)
end
def fixture_data(file)
File.read(fixture_file(file))
end
context "OVS bridge" do
context 'format file' do
subject { providers[:ovs_bridge] }
let(:cfg_file) { subject.class.format_file('filepath', [subject]) }
it { expect(cfg_file).to match(%r{DEVICE=ovs-bridge}) }
it { expect(cfg_file).to match(%r{BOOTPROTO=none}) }
it { expect(cfg_file).to match(%r{ONBOOT=yes}) }
it { expect(cfg_file).to match(%r{TYPE=OVSBridge}) }
it { expect(cfg_file).to match(%r{MTU=9000}) }
it { expect(cfg_file).to match(%r{DEVICETYPE=ovs}) }
it { expect(cfg_file.split(/\n/).reject{|x| x=~/^\s*$/}.length). to eq(6) } # no more lines in the interface file
end
context "parse bridge data from fixture" do
let(:res) { subject.class.parse_file('ovs-bridge', fixture_data('ifcfg-ovs-bridge'))[0] }
it { expect(res[:name]).to eq 'ovs-bridge' }
it { expect(res[:if_type].to_s).to eq 'bridge' }
it { expect(res[:method].to_s).to eq 'manual' }
it { expect(res[:mtu]).to eq '9000' }
it { expect(res[:onboot]).to eq true }
it { expect(res[:provider]).to eq :ovs_centos7 }
end
end
end

View File

@ -0,0 +1,134 @@
require 'spec_helper'
require 'yaml'
describe Puppet::Type.type(:l23_stored_config).provider(:ovs_centos7) do
let(:facts) do
{
:osfamily => 'Redhat',
:operatingsystem => 'CentOS',
:l23_os => 'centos7',
}
end
let(:input_data) do
{
:ovs2lnx_patch => {
:name => 'ovs2lnx-patch',
:ensure => 'present',
:if_type => 'vport',
:bridge => ['ovs-br', 'lnx-br'],
:onboot => true,
:method => 'manual',
:provider => 'ovs_centos7',
},
:ovs2lnx_bad_patch => {
:name => 'ovs2lnx-bpatch',
:ensure => 'present',
:if_type => 'vport',
:bridge => ['ovs-br', 'lnx-br', 'br-fake'],
:onboot => true,
:method => 'manual',
:provider => 'ovs_centos7',
},
:ovs2lnx_bad_patch2 => {
:name => 'ovs2lnx-bpatch2',
:ensure => 'present',
:if_type => 'vport',
:bridge => ['ovs-br1', 'ovs-br2'],
:onboot => true,
:method => 'manual',
:provider => 'ovs_centos7',
},
}
end
let(:resources) do
resources = {}
input_data.each do |name, res|
resources.store name, Puppet::Type.type(:l23_stored_config).new(res)
end
return resources
end
let(:providers) do
providers = {}
resources.each do |name, resource|
provider = resource.provider
if ENV['SPEC_PUPPET_DEBUG']
class << provider
def debug(msg)
puts msg
end
end
end
provider.create
providers.store name, provider
end
providers
end
before(:each) do
puppet_debug_override()
subject.class.stubs(:provider_of).with('ovs-br').returns('ovs_centos7')
subject.class.stubs(:provider_of).with('lnx-br').returns('lnx_centos7')
subject.class.stubs(:provider_of).with('ovs-br1').returns('ovs_centos7')
subject.class.stubs(:provider_of).with('ovs-br2').returns('ovs_centos7')
end
def fixture_path
File.join(PROJECT_ROOT, 'spec', 'fixtures', 'provider', 'l23_stored_config', 'centos7_patches')
end
def fixture_file(file)
File.join(fixture_path, file)
end
def fixture_data(file)
File.read(fixture_file(file))
end
context "ovs2lnx patch" do
context 'format patch file' do
subject { providers[:ovs2lnx_patch] }
let(:cfg_file) { subject.class.format_file('filepath', [subject]) }
it { expect(cfg_file).to match(%r{DEVICE=ovs2lnx-patch}) }
it { expect(cfg_file).to match(%r{BOOTPROTO=none}) }
it { expect(cfg_file).to match(%r{ONBOOT=yes}) }
it { expect(cfg_file).to match(%r{TYPE=OVSIntPort}) }
it { expect(cfg_file).to match(%r{BRIDGE=lnx-br}) }
it { expect(cfg_file).to match(%r{OVS_BRIDGE=ovs-br}) }
it { expect(cfg_file).to match(%r{DEVICETYPE=ovs}) }
it { expect(cfg_file.split(/\n/).reject{|x| x=~/^\s*$/}.length). to eq(7) } # no more lines in the interface file
end
context 'three bridges patch error' do
subject { providers[:ovs2lnx_bad_patch] }
it { expect{ subject.class.format_file('filepath', [subject])
}.to raise_error(Puppet::Error, %r{Patch\s+ovs2lnx-bpatch\s+has\s+more\s+than\s+2\s+bridges:\s+\["ovs-br",\s+"lnx-br",\s+"br-fake"].\s+Patch\s+can\s+connect\s+\*ONLY\*\s+2\s+bridges!}) }
end
context 'the same provider of bridges error' do
subject { providers[:ovs2lnx_bad_patch2] }
it { expect{ subject.class.format_file('filepath', [subject])
}.to raise_error(Puppet::Error, %r{Patch\s+ovs2lnx-bpatch2\s+has\s+the\s+same\s+provider\s+bridges:\s+\["ovs-br1",\s+"ovs-br2"\]\s+!}) }
end
context "parse patch data from fixture" do
let(:res) { subject.class.parse_file('ovs2lnx-patch', fixture_data('ifcfg-ovs2lnx-patch'))[0] }
it { expect(res[:name]).to eq 'ovs2lnx-patch' }
it { expect(res[:if_type].to_s).to eq 'vport' }
it { expect(res[:bridge]).to match_array(['lnx-br', 'ovs-br']) }
it { expect(res[:method].to_s).to eq 'manual' }
it { expect(res[:onboot]).to eq true }
it { expect(res[:provider].to_s).to eq 'ovs_centos7' }
end
end
end