fuel-library/deployment/puppet/l23network/lib/puppet/type/l23_stored_config.rb

409 lines
12 KiB
Ruby

# type for managing persistent interface config options
# Inspired by puppet-network module. Adrien, thanks.
require 'ipaddr'
Puppet::Type.newtype(:l23_stored_config) do
@doc = "Manage lines in interface config file"
desc @doc
feature :provider_options, <<-EOD
The provider can accept a hash of arbitrary options. The semantics of
these options will depend on the provider.
EOD
ensurable
newparam(:name) do
isnamevar
desc "The name of the physical or logical network device"
end
newproperty(:method) do
desc "The method for determining an IP address for the interface"
# static -- assign IP address in config
# manual -- UP interface without IP address
newvalues(:static, :absent, :manual, :dhcp, :loopback, :none, :undef, :nil)
aliasvalue(:none, :manual)
aliasvalue(:undef, :absent)
aliasvalue(:nil, :absent)
defaultto :manual
end
# newproperty(:port_type) do
# desc "port_type fake RO property"
# end
newproperty(:if_type) do
desc "Device type. Service property, shouldn't be setting by puppet"
newvalues(:ethernet, :bridge, :bond, :patch, :vport)
end
newproperty(:if_provider) do
desc "Device provider. Service property, shouldn't be setting by puppet"
end
newproperty(:bridge, :array_matching => :all) do
# Array_matching for this property required for very complicated cases
# ex. patchcord for connectind two bridges or bridge and network namesspace
desc "Name of bridge, including this port"
newvalues(/^[\w+\-]+$/, :none, :undef, :nil, :absent)
aliasvalue(:none, :absent)
aliasvalue(:undef, :absent)
aliasvalue(:nil, :absent)
defaultto :absent
end
newproperty(:jacks, :array_matching => :all) do
desc "Name of jacks for patchcord"
newvalues(/^[\w+\-]+$/)
end
newproperty(:bridge_ports, :array_matching => :all) do
desc "Ports, member of bridge, service property, do not use directly."
end
newproperty(:bridge_stp) do
desc "Whether stp enable"
newvalues(:true, :absent, :yes, :on, :false, :no, :off)
aliasvalue(:yes, :true)
aliasvalue(:on, :true)
aliasvalue(:no, :false)
aliasvalue(:off, :false)
defaultto :absent
end
newproperty(:onboot) do
desc "Whether to bring the interface up on boot"
newvalues(:true, :yes, :on, :false, :no, :off)
aliasvalue(:yes, :true)
aliasvalue(:on, :true)
aliasvalue(:no, :false)
aliasvalue(:off, :false)
defaultto :true
def insync?(value)
value.to_s.downcase == should.to_s.downcase
end
end
newproperty(:mtu) do
desc "The Maximum Transmission Unit size to use for the interface"
newvalues(/^\d+$/, :absent, :none, :undef, :nil)
aliasvalue(:none, :absent)
aliasvalue(:undef, :absent)
aliasvalue(:nil, :absent)
aliasvalue(0, :absent)
defaultto :absent # MTU value should be undefined by default, because some network resources (bridges, subinterfaces)
validate do |value| # inherits it from a parent interface
# Intel 82598 & 82599 chips support MTUs up to 16110; is there any
# hardware in the wild that supports larger frames?
#
# It appears loopback devices routinely have large MTU values; Eg. 65536
#
# Frames small than 64bytes are discarded as runts. Smallest valid MTU
# is 42 with a 802.1q header and 46 without.
min_mtu = 42
max_mtu = 65536
if ! (value.to_s == 'absent' or (min_mtu .. max_mtu).include?(value.to_i))
raise ArgumentError, "'#{value}' is not a valid mtu (must be a positive integer in range (#{min_mtu} .. #{max_mtu})"
end
end
munge do |val|
((val == :absent) ? :absent : val.to_i)
end
end
newproperty(:vlan_dev) do
desc "802.1q vlan base device"
end
newproperty(:vlan_id) do
desc "802.1q vlan ID"
newvalues(/^\d+$/, :absent, :none, :undef, :nil)
aliasvalue(:none, :absent)
aliasvalue(:undef, :absent)
aliasvalue(:nil, :absent)
aliasvalue(0, :absent)
defaultto :absent
validate do |val|
min_vid = 0
max_vid = 4094
if ! (val.to_s == 'absent' or (min_vid .. max_vid).include?(val.to_i))
raise ArgumentError, "'#{val}' is not a valid 802.1q NALN_ID (must be a integer value in range (#{min_vid} .. #{max_vid})"
end
end
munge do |val|
((val == :absent or val.to_s == '0') ? :absent : val.to_i)
end
end
newproperty(:vlan_mode) do
desc "802.1q vlan interface naming model"
#newvalues(:ethernet, :bridge, :bond)
#defaultto :ethernet
end
newproperty(:ipaddr) do
desc "Primary IP address for interface"
newvalues(/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\/\d{1,2}$/, :absent, :none, :undef, :nil, :dhcp)
aliasvalue(:none, :absent)
aliasvalue(:undef, :absent)
aliasvalue(:nil, :absent)
defaultto :absent
end
newproperty(:ipaddr_aliases, :array_matching => :all) do
desc "Additional IP addresses for interface"
newvalues(/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\/\d{1,2}$/, :absent, :none, :undef, :nil)
aliasvalue(:none, :absent)
aliasvalue(:undef, :absent)
aliasvalue(:nil, :absent)
defaultto :absent
def is_absent(value)
if value.is_a?(Hash) or value.is_a?(Array)
( value.empty? ? nil : value )
else
( ['', 'nil', 'none', 'absent'].include?(value.to_s.downcase) ? nil : value.to_s )
end
end
def should_to_s(value)
(is_absent(value).nil? ? '' : "#{value.join(':')}")
end
def is_to_s(value)
(is_absent(value).nil? ? '' : "#{value.join(':')}")
end
def insync?(value)
val = (is_absent(value).nil? ? '' : should_to_s(value.sort) )
shou = (is_absent(value).nil? ? '' : should_to_s(should.sort) )
val == shou
end
end
newproperty(:gateway) do
desc "Default gateway"
newvalues(/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/, :absent, :none, :undef, :nil)
aliasvalue(:none, :absent)
aliasvalue(:undef, :absent)
aliasvalue(:nil, :absent)
defaultto :absent
end
newproperty(:gateway_metric) do
desc "Default gateway metric"
newvalues(/^\d+$/, :absent, :none, :undef, :nil)
aliasvalue(:none, :absent)
aliasvalue(:undef, :absent)
aliasvalue(:nil, :absent)
aliasvalue(0, :absent)
defaultto :absent
validate do |val|
min_metric = 0
max_metric = 65535
if ! (val.to_s == 'absent' or (min_metric .. max_metric).include?(val.to_i))
raise ArgumentError, "'#{val}' is not a valid metric (must be a integer value in range (#{min_metric} .. #{max_metric})"
end
end
munge do |val|
((val == :absent) ? :absent : val.to_i)
end
end
newproperty(:delay_while_up) do
desc "Delay while interface stay UP"
newvalues(/^\d+$/, :absent, :none, :undef, :nil)
aliasvalue(:none, :absent)
aliasvalue(:undef, :absent)
aliasvalue(:nil, :absent)
aliasvalue(0, :absent)
defaultto :absent
munge do |val|
((val == :absent) ? :absent : [val.to_i])
end
end
newproperty(:bond_master) do
desc "bond name for bonded interface"
newvalues(/^\w[\w+\-]*\w$/, :none, :undef, :nil, :absent)
aliasvalue(:none, :absent)
aliasvalue(:undef, :absent)
aliasvalue(:nil, :absent)
defaultto :absent
end
newproperty(:bond_slaves, :array_matching => :all) do
desc "slave ports for bond interface"
newvalues(/^\w[\w+\-\.]*\w$/, :false, :none, :undef, :nil, :absent)
#aliasvalue(:absent, :none) # none is a valid config value
aliasvalue(:false, :none)
aliasvalue(:undef, :absent)
aliasvalue(:nil, :absent)
defaultto :absent
end
newproperty(:bond_mode)
newproperty(:bond_miimon)
newproperty(:bond_lacp)
newproperty(:bond_lacp_rate)
newproperty(:bond_xmit_hash_policy)
newproperty(:bond_updelay) do
newvalues(/^\d+$/)
end
newproperty(:bond_downdelay) do
newvalues(/^\d+$/)
end
newproperty(:bond_ad_select) do
validate do |val|
allowed_values = ['0','1','2','stable','bandwidth','count']
if ! allowed_values.include? val.to_s
raise ArgumentError, "'#{val}' is not a valid bond_ad_select. Only #{allowed_values.join(', ')} allowed.)"
end
end
end
# # `:options` provides an arbitrary passthrough for provider properties, so
# # that provider specific behavior doesn't clutter up the main type but still
# # allows for more powerful actions to be taken.
# newproperty(:options, :required_features => :provider_options) do
# desc "Provider specific options to be passed to the provider"
# def is_to_s(hash = @is)
# hash.keys.sort.map {|key| "#{key} => #{hash[key]}"}.join(", ")
# end
# def should_to_s(hash = @should)
# hash.keys.sort.map {|key| "#{key} => #{hash[key]}"}.join(", ")
# end
# defaultto {}
# validate do |value|
# raise ArgumentError, "#{self.class} requires a hash for the options property" unless value.is_a? Hash
# #provider.validate
# end
# end
newproperty(:routes) do
desc "routes, corresponded to this interface. This-a R/O property, that autofill from L3_route resource"
#defaultto {} # no default value should be!!!
validate do |val|
if ! val.is_a? Hash
fail("routes should be a hash!")
end
end
munge do |value|
(value.empty? ? nil : L23network.reccursive_sanitize_hash(value))
end
def should_to_s(value)
"\n#{value.to_yaml.gsub('!ruby/sym','')}\n"
end
def is_to_s(value)
"\n#{value.to_yaml.gsub('!ruby/sym','')}\n"
end
end
newproperty(:ethtool) do
desc "ethtool addition configuration for this interface"
#defaultto {} # no default value should be!!!
validate do |val|
if ! val.is_a? Hash
fail("ethtool commands should be a hash!")
end
end
munge do |value|
(value.empty? ? nil : L23network.reccursive_sanitize_hash(value))
end
def should_to_s(value)
"\n#{value.to_yaml.gsub('!ruby/sym','')}\n"
end
def is_to_s(value)
"\n#{value.to_yaml.gsub('!ruby/sym','')}\n"
end
end
newproperty(:vendor_specific) do
desc "Hash of vendor specific properties"
#defaultto {} # no default value should be!!!
# provider-specific properties, can be validating only by provider.
validate do |val|
if ! val.is_a? Hash
fail("Vendor_specific should be a hash!")
end
end
munge do |value|
(value.empty? ? nil : L23network.reccursive_sanitize_hash(value))
end
def should_to_s(value)
"\n#{value.to_yaml.gsub('!ruby/sym ','')}\n"
end
def is_to_s(value)
"\n#{value.to_yaml.gsub('!ruby/sym ','')}\n"
end
def insync?(value)
should_to_s(value) == should_to_s(should)
end
end
def generate
# if_type = :ethernet is the same as if_type = nil
if (!([:absent, :none, :nil, :undef] & self[:bridge]).any? and ([:ethernet, :bond].include? self[:if_type] or self[:if_type].nil?))
self[:bridge].each do |bridge|
br = self.catalog.resource('L23_stored_config', bridge)
fail("Stored_config resource for bridge '#{bridge}' not found for port '#{self[:name]}'!") if ! br
br[:bridge_ports] ||= []
ports = br[:bridge_ports]
return if ! ports.is_a? Array
if ! ports.include? self[:name]
ports << self[:name].to_s
br[:bridge_ports] = ports.reject{|a| a=='none'}.sort
end
end
end
# find routes, that should be applied while this interface UP
if !['', 'none', 'absent'].include?(self[:ipaddr].to_s.downcase)
l3_routes = self.catalog.resources.reject{|nnn| nnn.ref.split('[')[0]!='L3_route'}
my_network = IPAddr.new(self[:ipaddr].to_s.downcase) # only primary IP ADDR use !!!
my_route = {}
l3_routes.each do |rou|
if my_network.include? rou[:gateway]
#self[:routes] = {} if self[:routes].nil?
my_route[rou[:name]] = {
:gateway => rou[:gateway],
:destination => rou[:destination]
}
my_route[rou[:name]][:metric] = rou[:metric] if !['', 'absent'].include? rou[:metric].to_s.downcase
debug("+++My route: #{my_route}")
end
end
if ! my_route.empty?
if self[:routes].nil?
self[:routes] = my_route
else
self[:routes].merge!(my_route)
end
end
end
nil
end
end
# vim: set ts=2 sw=2 et :