fuel-library/deployment/puppet/l23network/lib/puppet/provider/l23_stored_config_centos.rb

430 lines
13 KiB
Ruby

require 'puppetx/l23_ethtool_name_commands_mapping'
require File.join(File.dirname(__FILE__), 'l23_stored_config_base')
class Puppet::Provider::L23_stored_config_centos < Puppet::Provider::L23_stored_config_base
# @return [String] The path to network-script directory on redhat systems
def self.script_directory
'/etc/sysconfig/network-scripts'
end
def self.target_files(script_dir = nil)
provider = self.name.to_s
debug("Collecting target files for #{provider}")
entries = super
regoc_regex = %r{TYPE=OVS}
if provider =~ /ovs_/
entries = entries.reject { |entry| open(entry).grep(regoc_regex).empty? }
elsif provider =~ /lnx_/
entries = entries.select { |entry| open(entry).grep(regoc_regex).empty? }
end
entries
end
def self.property_mappings
{
:method => 'BOOTPROTO',
:ipaddr => 'IPADDR',
:name => 'DEVICE',
:onboot => 'ONBOOT',
:mtu => 'MTU',
:vlan_id => 'VLAN',
:vlan_dev => 'PHYSDEV',
:vlan_mode => 'VLAN_NAME_TYPE',
:if_type => 'TYPE',
:bridge => 'BRIDGE',
:prefix => 'PREFIX',
:gateway => 'GATEWAY',
:gateway_metric => 'METRIC',
:bond_master => 'MASTER',
:slave => 'SLAVE',
:bond_mode => 'mode',
:bond_miimon => 'miimon',
:bonding_opts => 'BONDING_OPTS',
:bond_lacp_rate => 'lacp_rate',
:bond_ad_select => 'ad_select',
:bond_xmit_hash_policy => 'xmit_hash_policy',
:bond_updelay => 'updelay',
:bond_downdelay => 'downdelay',
:ethtool => 'ETHTOOL_OPTS',
:routes => 'ROUTES',
}
end
def property_mappings
self.class.property_mappings
end
# In the interface config files those fields should be written as boolean
def self.boolean_properties
[
:hotplug,
:onboot,
:vlan_id,
]
end
def boolean_properties
self.class.boolean_properties
end
def self.properties_fake
[
:prefix,
:vlan_mode,
:vlan_dev,
:slave,
:bonding_opts,
:delay_while_up,
]
end
def properties_fake
self.class.properties_fake
end
def self.parse_file(filename, contents)
# WARNING!!!
# this implementation can parce only one interface per file file format
# Split up the file into lines
lines = contents.split("\n")
# Strip out all comments
lines.map! { |line| line.sub(/#.*$/, '') }
# Remove all blank lines
lines.reject! { |line| line.match(/^\s*$/) }
# initialize hash as predictible values
hash = {}
dirty_iface_name = nil
if (m = filename.match(%r/ifcfg-(\S+)$/))
# save iface name from file name. One will be used if iface name not defined inside config.
dirty_iface_name = m[1].strip
end
# Convert the data into key/value pairs
pair_regex = %r/^\s*(.+?)\s*=\s*(.*)\s*$/
lines.each do |line|
if (m = line.match(pair_regex))
key = m[1].strip
val = m[2].strip
hash[key] = val
else
raise Puppet::Error, %{#{filename} is malformed; "#{line}" did not match "#{pair_regex.to_s}"}
end
hash
end
route_filename = "#{self.script_directory}/route-#{dirty_iface_name}"
if File.exist?(route_filename)
route_file = open(route_filename, 'r')
rv = {}
while (line = route_file.gets)
rv[line.split(' ')[0]] = line
end
route_file.close
hash['ROUTES'] = rv
end
if hash.has_key?('IPADDR')
hash['IPADDR'] = "#{hash['IPADDR']}/#{hash['PREFIX']}"
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)
props.merge!({:family => :inet})
props.merge!({:provider => self.name})
debug("parse_file('#{filename}'): #{props.inspect}")
[props]
end
def self.check_if_provider(if_data)
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(' ')
pair_regex = %r/^\s*(.+?)\s*=\s*(.*)\s*$/
bonding_opts_line.each do | bond_opt |
if (bom = bond_opt.match(pair_regex))
hash[bom[1].strip] = bom[2].strip
else
raise Puppet::Error, %{#{filename} is malformed; "#{line}" did not match "#{pair_regex.to_s}"}
end
end
hash.delete('BONDING_OPTS')
end
hash
end
def self.mangle_properties(pairs)
props = {}
# Unquote all values
pairs.each_pair do |key, val|
next if ! (val.is_a? String or val.is_a? Symbol)
if (munged = val.to_s.gsub(/['"]/, ''))
pairs[key] = munged
end
end
# For each interface attribute that we recognize it, add the value to the
# hash with our expected label
property_mappings.each_pair do |type_name, in_config_name|
if (val = pairs[in_config_name])
# We've recognized a value that maps to an actual type property, delete
# it from the pairs and copy it as an actual property
pairs.delete(in_config_name)
mangle_method_name="mangle__#{type_name}"
if self.respond_to?(mangle_method_name)
rv = self.send(mangle_method_name, val)
else
rv = val
end
props[type_name] = rv if ! [nil, :absent].include? rv
end
end
boolean_properties.each do |bool_property|
if props[bool_property]
props[bool_property] = ! (props[bool_property] =~ /^\s*(yes|on)\s*$/i).nil?
else
props[bool_property] = :absent
end
end
props
end
def self.mangle__bridge(val)
Array(val)
end
def self.mangle__if_type(val)
val.to_s.downcase.to_sym
end
def self.mangle__method(val)
val = 'manual' if val.to_s == 'none'
val.to_s.downcase
end
def self.mangle__ethtool(val)
rv = {}
val.split(' ;').each do | section |
section_config = section.split(' ')
feature_params = {}
section_name = ''
L23network.ethtool_name_commands_mapping.each do |key, value |
section_name = key if value==section_config[0].split(' ')[0]
value.each { | k, v | section_name = key if v==section_config[0].split(' ')[0] } if value.is_a?(Hash)
end
next if section_name == ''
section_features = section_config.select {|k| k != section_config[0]}
section_features.each do | feature |
k,v = feature.split(' ')
tk = Hash[L23network.ethtool_name_commands_mapping[section_name].select { |key, value| value==k }]
next if tk == ''
feature_params[tk.keys.to_s] = ((v=='on' ? true : false))
end
rv[section_name] = feature_params
end
rv
end
def self.mangle__routes(data)
rv = {}
data.each_pair do |k,v|
gateway = v.match(/via ((?:[0-9]{1,3}\.){3}[0-9]{1,3}) /)[1]
route_params = { 'destination' => k ,
'gateway' => gateway
}
if v.include? 'metric'
metric = v.match(/metric (\d+)/)[1]
route_params['metric'] = metric
end
name = L23network.get_route_resource_name(k, metric)
rv[name] = route_params
end
return rv
end
###
# Hash to file
def self.format_file(filename, providers)
if providers.length == 0
return ""
elsif providers.length > 1
raise Puppet::DevError, "Unable to support multiple interfaces [#{providers.map(&:name).join(',')}] in a single file #{filename}"
end
content = []
provider = providers[0]
# Map everything to a flat hash
props = {}
property_mappings.keys.select{|v| ! properties_fake.include?(v)}.each do |type_name|
val = provider.send(type_name)
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
end
if props.has_key?(:ipaddr)
props[:ipaddr], props[:prefix] = props[:ipaddr].to_s.split('/')
end
if props.has_key?(:bond_master)
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)
pairs['DEVICETYPE'] = 'ovs' if pairs['TYPE'].to_s =~ /OVS/
if pairs['ROUTES']
route_filename = "#{self.script_directory}/route-#{provider.name}"
route_content = ""
pairs['ROUTES'].each do |route|
route_content << "#{route}\n"
end
self.write_file(route_filename, route_content)
pairs.delete('ROUTES')
end
# Delete default gateway from global network file
if pairs['GATEWAY']
self.remove_line_from_file('/etc/sysconfig/network', /GATEWAY.*/)
end
pairs.each_pair do |key, val|
content << "#{key}=#{val}" if ! val.nil?
end
debug("format_file('#{filename}')::content: #{content.inspect}")
content << ''
content.join("\n")
end
def self.read_file(file)
File.read file
end
def self.write_file(file, content)
File.open(file, 'w') do |fp|
fp.write content
end
end
def self.remove_line_from_file(file, remove)
content = self.read_file(file).split("\n").reject { |line| remove === line }.join("\n") + "\n"
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)) }
bond_properties.each do |param, transform |
if props.has_key?(param)
bond_options << "#{transform}=#{props[param]}"
props.delete(param)
end
end
props[:bonding_opts] = "\"#{bond_options.join(' ')}\""
props
end
def self.unmangle_properties(provider, props)
pairs = {}
boolean_properties.each do |bool_property|
if ! props[bool_property].nil?
props[bool_property] = (props[bool_property].to_s.downcase == 'true' || props[bool_property].integer?) ? 'yes' : 'no'
end
end
property_mappings.each_pair do |type_name, in_config_name|
if (val = props[type_name])
props.delete(type_name)
mangle_method_name="unmangle__#{type_name}"
if self.respond_to?(mangle_method_name)
rv = self.send(mangle_method_name, provider, val)
else
rv = val
end
pairs[in_config_name] = rv if ! ['', 'absent'].include? rv.to_s.downcase
end
end
pairs
end
def self.unmangle__bridge(provider, val)
val.join()
end
def self.unmangle__if_type(provider, val)
val.to_s.capitalize
end
def self.unmangle__method(provider, val)
(['manual', 'static'].include? val.to_s.downcase) ? 'none' : val
end
def self.unmangle__ipaddr(provider, val)
(val.to_s.downcase == 'dhcp') ? nil : val
end
def self.unmangle__ethtool(provider, val)
rv = ''
val.each do | section_name, features |
next if L23network.ethtool_name_commands_mapping[section_name].nil?
section_key = L23network.ethtool_name_commands_mapping[section_name]['__section_key_set__']
next if section_key.nil?
features.each do | feature, value |
next if L23network.ethtool_name_commands_mapping[section_name][feature].nil?
rv << " #{L23network.ethtool_name_commands_mapping[section_name][feature]} #{(value==true ? 'on' : 'off')} "
end
rv = "#{section_key} #{provider.name} #{rv};"
end
return "\"#{rv}\""
end
def self.unmangle__routes(provider, val)
# should generate set of lines:
# "10.109.55.0/24 via 192.168.1.54 dev br-storage"
return [] if ['', 'absent'].include? val.to_s
rv = []
val.each_pair do |name, route|
metric = (route['metric'].nil? ? '' : " metric #{route['metric']}")
rv << "#{route['destination']} via #{route['gateway']}#{metric} dev #{provider.name}"
end
return rv
end
end
# vim: set ts=2 sw=2 et :