Advanced network configuration

This commit is contained in:
Sergey Vasilenko 2013-10-03 19:12:37 +04:00
parent 2fde90dd4f
commit 6ae4854eb4
22 changed files with 1357 additions and 37 deletions

View File

@ -1,4 +1,3 @@
#
# cidr_to_netmask.rb
#
@ -20,7 +19,7 @@ EOS
) do |arguments|
if arguments.size != 1
raise(Puppet::ParseError, "cidr_to_netmask(): Wrong number of arguments " +
"given (#{arguments.size} for 1)")
"given (#{arguments.size} for 1)")
end
masklen = prepare_cidr(arguments[0])[1]

View File

@ -0,0 +1,11 @@
require 'yaml'
require 'json'
Puppet::Parser::Functions::newfunction(:debug__dump_to_file, :doc => <<-EOS
debug output to file
EOS
) do |argv|
File.open(argv[0], 'w'){ |file| file.write argv[1].to_yaml() }
end
# vim: set ts=2 sw=2 et :

View File

@ -0,0 +1,258 @@
require 'ipaddr'
require 'forwardable'
require 'puppet/parser'
require 'puppet/parser/templatewrapper'
require 'puppet/resource/type_collection_helper'
require 'puppet/util/methodhelper'
begin
require 'puppet/parser/functions/lib/l23network_scheme.rb'
rescue LoadError => e
# puppet apply does not add module lib directories to the $LOAD_PATH (See
# #4248). It should (in the future) but for the time being we need to be
# defensive which is what this rescue block is doing.
rb_file = File.join(File.dirname(__FILE__),'lib','l23network_scheme.rb')
load rb_file if File.exists?(rb_file) or raise e
end
module L23network
def self.sanitize_transformation(trans)
action = trans[:action].downcase()
# Setup defaults
rv = case action
when "add-br" then {
:name => nil,
#:stp_enable => true,
:skip_existing => true
}
when "add-port" then {
:name => nil,
:bridge => nil,
#:type => "internal",
:tag => 0,
:trunks => [],
:port_properties => [],
:interface_properties => [],
:skip_existing => true
}
when "add-bond" then {
:name => nil,
:bridge => nil,
:interfaces => [],
:tag => 0,
:trunks => [],
:properties => [],
#:port_properties => [],
#:interface_properties => [],
:skip_existing => true
}
when "add-patch" then {
:name => "unnamed", # calculated later
:peers => [nil, nil],
:bridges => [],
:tags => [0, 0],
:trunks => [],
}
else
raise(Puppet::ParseError, "Unknown transformation: '#{action}'.")
end
# replace defaults to real parameters
rv[:action] = action
rv.each do |k,v|
if trans[k]
rv[k] = trans[k]
end
end
# Check for incorrect parameters
if not rv[:name].is_a? String
raise(Puppet::ParseError, "Unnamed transformation: '#{action}'.")
end
name = rv[:name]
if not rv[:bridge].is_a? String and not ["add-patch", "add-br"].index(action)
raise(Puppet::ParseError, "Undefined bridge for transformation '#{action}' with name '#{name}'.")
end
if action == "add-patch"
if not rv[:bridges].is_a? Array and rv[:bridges].size() != 2
raise(Puppet::ParseError, "Transformation patch have wrong 'bridges' parameter.")
end
name = "patch__#{rv[:bridges][0]}__#{rv[:bridges][1]}"
if not rv[:peers].is_a? Array and rv[:peers].size() != 2
raise(Puppet::ParseError, "Transformation patch '#{name}' have wrong 'peers' parameter.")
end
rv[:name] = name
end
if action == "add-bond"
if not rv[:interfaces].is_a? Array or rv[:interfaces].size() != 2
raise(Puppet::ParseError, "Transformation bond '#{name}' have wrong 'interfaces' parameter.")
end
# rv[:interfaces].each do |i|
# if
# end
end
return rv
end
end
Puppet::Parser::Functions::newfunction(:generate_network_config, :type => :rvalue, :doc => <<-EOS
This function get Hash of network interfaces and endpoints configuration
and realized it.
EOS
) do |argv|
def default_netmask()
"/24"
end
def create_endpoint()
{
:properties => {},
:IP => []
}
end
if argv.size != 0
raise(Puppet::ParseError, "generate_network_config(): Wrong number of arguments.")
end
config_hash = L23network::Scheme.get()
if config_hash.nil?
raise(Puppet::ParseError, "get_network_role_property(...): You must call prepare_network_config(...) first!")
end
# define internal puppet parameters for creating resources
res_factory = {
:br => { :name_of_resource => 'l23network::l2::bridge' },
:port => { :name_of_resource => 'l23network::l2::port' },
:bond => { :name_of_resource => 'l23network::l2::bond' },
:patch => { :name_of_resource => 'l23network::l2::patch' },
:ifconfig=> { :name_of_resource => 'l23network::l3::ifconfig' }
}
res_factory.each do |k, v|
if v[:name_of_resource].index('::')
# operate by Define
res_factory[k][:resource] = lookuptype(v[:name_of_resource].downcase()) # may be find_definition(k.downcase())
res_factory[k][:type_of_resource] = :define
else
# operate by custom Type
res_factory[k][:resource] = Puppet::Type.type(v[:name_of_resource].to_sym())
res_factory[k][:type_of_resource] = :type
end
end
# collect interfaces and endpoints
endpoints = {}
born_ports = []
config_hash[:interfaces].each do |int_name, int_properties|
int_name = int_name.to_sym()
endpoints[int_name] = create_endpoint()
born_ports.insert(-1, int_name)
end
config_hash[:endpoints].each do |e_name, e_properties|
e_name = e_name.to_sym()
if not endpoints[e_name]
endpoints[e_name] = create_endpoint()
end
e_properties.each do |k,v|
if k.to_sym() == :IP
if !(v.is_a?(Array) || ['none','dhcp',nil].include?(v))
raise(Puppet::ParseError, "generate_network_config(): IP field for endpoint '#{e_name}' must be array of IP addresses, 'dhcp' or 'none'.")
elsif ['none','dhcp',nil].include?(v)
endpoints[e_name][:IP].insert(-1, v ? v : 'none')
else
v.each do |ip|
begin
iip = IPAddr.new(ip)
endpoints[e_name][:IP].insert(-1, ip)
rescue
raise(Puppet::ParseError, "generate_network_config(): IP address '#{ip}' for endpoint '#{e_name}' wrong!.")
end
end
end
else
endpoints[e_name][:properties][k.to_sym()] = v
end
end
end
# execute transformations
# todo: if provider="lnx" execute transformations for LNX bridges
transformation_success = []
previous = nil
config_hash[:transformations].each do |t|
action = t[:action].strip()
if action.start_with?('add-')
action = t[:action][4..-1].to_sym()
else
action = t[:action].to_sym()
end
trans = L23network.sanitize_transformation(t)
resource = res_factory[action][:resource]
p_resource = Puppet::Parser::Resource.new(
res_factory[action][:name_of_resource],
trans[:name],
:scope => self,
:source => resource
)
trans.select{|k,v| k != :action}.each do |k,v|
p_resource.set_parameter(k,v)
end
p_resource.set_parameter(:require, [previous]) if previous
resource.instantiate_resource(self, p_resource)
compiler.add_resource(self, p_resource)
transformation_success.insert(-1, "#{t[:action].strip()}(#{trans[:name]})")
born_ports.insert(-1, trans[:name].to_sym()) if action != :patch
previous = p_resource.to_s
end
# check for all in endpoints are in interfaces or born by transformation
config_hash[:endpoints].each do |e_name, e_properties|
if not born_ports.index(e_name.to_sym())
raise(Puppet::ParseError, "generate_network_config(): Endpoint '#{e_name}' not found in interfaces or transformations result.")
end
end
# execute interfaces and endpoints
# may be in future we will move interfaces before transformations
endpoints.each do |endpoint_name, endpoint_body|
# create resource
resource = res_factory[:ifconfig][:resource]
p_resource = Puppet::Parser::Resource.new(
res_factory[:ifconfig][:name_of_resource],
endpoint_name,
:scope => self,
:source => resource
)
p_resource.set_parameter(:interface, endpoint_name)
# set ipaddresses
if endpoint_body[:IP].empty?
p_resource.set_parameter(:ipaddr, 'none')
elsif ['none','dhcp'].index(endpoint_body[:IP][0])
p_resource.set_parameter(:ipaddr, endpoint_body[:IP][0])
else
ipaddrs = []
endpoint_body[:IP].each do |i|
if i =~ /\/\d+$/
ipaddrs.insert(-1, i)
else
ipaddrs.insert(-1, "#{i}#{default_netmask()}")
end
end
p_resource.set_parameter(:ipaddr, ipaddrs)
end
#set another (see L23network::l3::ifconfig DOC) parametres
endpoint_body[:properties].each do |k,v|
p_resource.set_parameter(k,v)
end
p_resource.set_parameter(:require, [previous]) if previous
resource.instantiate_resource(self, p_resource)
compiler.add_resource(self, p_resource)
transformation_success.insert(-1, "endpoint(#{endpoint_name})")
previous = p_resource.to_s
end
return transformation_success.join(" -> ")
end
# vim: set ts=2 sw=2 et :

View File

@ -0,0 +1,99 @@
require 'ipaddr'
begin
require 'puppet/parser/functions/lib/prepare_cidr.rb'
rescue LoadError => e
# puppet apply does not add module lib directories to the $LOAD_PATH (See
# #4248). It should (in the future) but for the time being we need to be
# defensive which is what this rescue block is doing.
rb_file = File.join(File.dirname(__FILE__),'lib','prepare_cidr.rb')
load rb_file if File.exists?(rb_file) or raise e
end
begin
require 'puppet/parser/functions/lib/l23network_scheme.rb'
rescue LoadError => e
# puppet apply does not add module lib directories to the $LOAD_PATH (See
# #4248). It should (in the future) but for the time being we need to be
# defensive which is what this rescue block is doing.
rb_file = File.join(File.dirname(__FILE__),'lib','l23network_scheme.rb')
load rb_file if File.exists?(rb_file) or raise e
end
Puppet::Parser::Functions::newfunction(:get_network_role_property, :type => :rvalue, :doc => <<-EOS
This function get get network the network_role name and mode --
and return information about network role.
ex: get_network_role_property('admin', 'interface')
You can use following modes:
interface -- network interface for the network_role
ipaddr -- IP address for the network_role
cidr -- CIDR-notated IP addr and mask for the network_role
netmask -- string, contains dotted nemmask
ipaddr_netmask_pair -- list of ipaddr and netmask
EOS
) do |argv|
if argv.size == 2
mode = argv[1].to_s().upcase()
else
raise(Puppet::ParseError, "get_network_role_property(...): Wrong number of arguments.")
end
cfg = L23network::Scheme.get()
File.open("/tmp/L23network_scheme.yaml", 'w'){ |file| file.write cfg.to_yaml() }
if cfg.nil?
raise(Puppet::ParseError, "get_network_role_property(...): You must call prepare_network_config(...) first!")
end
network_role = argv[0].to_sym()
if !cfg[:roles] || !cfg[:endpoints] || cfg[:roles].class.to_s() != "Hash" || cfg[:endpoints].class.to_s() != "Hash"
raise(Puppet::ParseError, "get_network_role_property(...): Invalid cfg_hash format.")
end
# search interface for role
interface = cfg[:roles][network_role]
if !interface
raise(Puppet::ParseError, "get_network_role_property(...): Undefined network_role '#{network_role}'.")
end
# get endpoint configuration hash for interface
ep = cfg[:endpoints][interface.to_sym()]
if !ep
raise(Puppet::ParseError, "get_network_role_property(...): Can't find interface '#{interface}' in endpoints for network_role '#{network_role}'.")
end
if mode == 'INTERFACE'
return interface.to_s
end
case ep[:IP].class().to_s()
when "Array"
ipaddr_cidr = ep[:IP][0] ? ep[:IP][0] : nil
when "String"
#raise(Puppet::ParseError, "get_network_role_property(cfg_hash, role_name): Can't determine dynamic or empty IP address for endpoint '#{interface}' (#{ep[:IP]}).")
ipaddr_cidr = nil
else
raise(Puppet::ParseError, "get_network_role_property(...): invalid IP address for endpoint '#{interface}'.")
end
if ipaddr_cidr == nil
return nil
end
case mode
when 'CIDR'
return ipaddr_cidr
when 'NETMASK'
return IPAddr.new('255.255.255.255').mask(prepare_cidr(ipaddr_cidr)[1]).to_s()
when 'IPADDR'
return prepare_cidr(ipaddr_cidr)[0].to_s()
when 'IPADDR_NETMASK_PAIR'
return prepare_cidr(ipaddr_cidr)[0].to_s(), IPAddr.new('255.255.255.255').mask(prepare_cidr(ipaddr_cidr)[1]).to_s()
end
end
# vim: set ts=2 sw=2 et :

View File

@ -0,0 +1,67 @@
module L23network
def self.process_array4keys(aa)
rv = []
aa.each do |v|
if v.is_a? Hash
rv.insert(-1, self.sanitize_keys_in_hash(v))
elsif v.is_a? Array
rv.insert(-1, self.process_array4keys(v))
else
rv.insert(-1, v)
end
end
return rv
end
def self.sanitize_keys_in_hash(hh)
rv = {}
hh.each do |k, v|
#info("xx>>#{k}--#{k.to_sym}<<")
if v.is_a? Hash
rv[k.to_sym] = self.sanitize_keys_in_hash(v)
elsif v.is_a? Array
rv[k.to_sym] = self.process_array4keys(v)
else
rv[k.to_sym] = v
end
end
return rv
end
def self.process_array4bool(aa)
rv = []
aa.each do |v|
if v.is_a? Hash
rv.insert(-1, self.sanitize_bool_in_hash(v))
elsif v.is_a? Array
rv.insert(-1, self.process_array4bool(v))
else
rv.insert(-1, v)
end
end
return rv
end
def self.sanitize_bool_in_hash(hh)
rv = {}
hh.each do |k, v|
if v.is_a? String or v.is_a? Symbol
rv[k] = case v.upcase()
when 'TRUE', :TRUE then true
when 'FALSE', :FALSE then false
when 'NONE', :NONE, 'NULL', :NULL, 'NIL', :NIL, 'NILL', :NILL then nil
else v
end
elsif v.is_a? Hash
rv[k] = self.sanitize_bool_in_hash(v)
elsif v.is_a? Array
rv[k] = self.process_array4bool(v)
else
rv[k] = v
end
end
return rv
end
end
# vim: set ts=2 sw=2 et :

View File

@ -0,0 +1,11 @@
module L23network
class Scheme
def self.set=(v)
@network_scheme_hash = v
end
def self.get
@network_scheme_hash
end
end
end
# vim: set ts=2 sw=2 et :

View File

@ -1,16 +1,17 @@
def prepare_cidr(cidr)
if ! cidr.is_a?(String)
if !cidr.is_a?(String)
raise(Puppet::ParseError, "Can't recognize IP address in non-string data.")
end
re_groups = /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\/(\d{1,2})$/.match(cidr)
if ! re_groups or re_groups[2].to_i > 32
if !re_groups or re_groups[2].to_i > 32
raise(Puppet::ParseError, "cidr_to_ipaddr(): Wrong CIDR: '#{cidr}'.")
end
end
for octet in re_groups[1].split('.')
raise(Puppet::ParseError, "cidr_to_ipaddr(): Wrong CIDR: '#{cidr}'.") if octet.to_i > 255
end
return re_groups[1], re_groups[2].to_i
end
# vim: set ts=2 sw=2 et :

View File

@ -0,0 +1,38 @@
begin
require 'puppet/parser/functions/lib/l23network_scheme.rb'
rescue LoadError => e
# puppet apply does not add module lib directories to the $LOAD_PATH (See
# #4248). It should (in the future) but for the time being we need to be
# defensive which is what this rescue block is doing.
rb_file = File.join(File.dirname(__FILE__),'lib','l23network_scheme.rb')
load rb_file if File.exists?(rb_file) or raise e
end
begin
require 'puppet/parser/functions/lib/hash_tools.rb'
rescue LoadError => e
# puppet apply does not add module lib directories to the $LOAD_PATH (See
# #4248). It should (in the future) but for the time being we need to be
# defensive which is what this rescue block is doing.
rb_file = File.join(File.dirname(__FILE__),'lib','hash_tools.rb')
load rb_file if File.exists?(rb_file) or raise e
end
module Puppet::Parser::Functions
newfunction(:prepare_network_config, :doc => <<-EOS
This function get Hash, and prepare it for using for network configuration.
You must call this function as early as possible. It do nothing, only stored protected
sanitized network config for usind later.
EOS
) do |argv|
if argv.size != 1
raise(Puppet::ParseError, "prepare_network_config(hash): Wrong number of arguments.")
end
cfg_hash = argv[0]
Puppet::Parser::Functions.autoloader.loadall
rv = L23network.sanitize_bool_in_hash(L23network.sanitize_keys_in_hash(cfg_hash))
L23network::Scheme.set=rv
return true
end
end
# vim: set ts=2 sw=2 et :

View File

@ -0,0 +1,27 @@
begin
require 'puppet/parser/functions/lib/hash_tools.rb'
rescue LoadError => e
# puppet apply does not add module lib directories to the $LOAD_PATH (See
# #4248). It should (in the future) but for the time being we need to be
# defensive which is what this rescue block is doing.
rb_file = File.join(File.dirname(__FILE__),'lib','hash_tools.rb')
load rb_file if File.exists?(rb_file) or raise e
end
module Puppet::Parser::Functions
newfunction(:sanitize_bool_in_hash, :type => :rvalue, :doc => <<-EOS
This function get Hash, recursive convert string implementation
of true, false, none, null, nil to Puppet/Ruby-specific
types.
EOS
) do |argv|
if argv.size != 1
raise(Puppet::ParseError, "sanitize_bool_in_hash(hash): Wrong number of arguments.")
end
return L23network.sanitize_bool_in_hash(argv[0])
end
end
# vim: set ts=2 sw=2 et :

View File

@ -4,21 +4,21 @@ Puppet::Type.type(:l2_ovs_bond).provide(:ovs) do
:appctl => "/usr/bin/ovs-appctl"
)
def _exists(bond)
def _exists?(bond)
begin
appctl('bond/show', bond)
return true
true
rescue Puppet::ExecutionFailure
return false
false
end
end
def exists?
_exists(@resource[:bond])
_exists?(@resource[:bond])
end
def create
if _exists(@resource[:bond])
if _exists?(@resource[:bond])
msg = "Bond '#{@resource[:bond]}' already exists"
if @resource[:skip_existing]
notice("#{msg}, skip creating.")
@ -27,15 +27,23 @@ Puppet::Type.type(:l2_ovs_bond).provide(:ovs) do
end
end
bond_create_cmd = ['add-bond', @resource[:bridge], @resource[:bond]] + @resource[:ports]
if ! @resource[:properties].empty?
bond_create_cmd += @resource[:properties]
bond_properties = @resource[:properties]
if @resource[:tag] > 0
bond_properties.insert(-1, "tag=#{@resource[:tag]}")
end
if not @resource[:trunks].empty?
bond_properties.insert(-1, "trunks=[#{@resource[:trunks].join(',')}]")
end
bond_create_cmd = ['add-bond', @resource[:bridge], @resource[:bond]] + @resource[:interfaces]
if ! bond_properties.empty?
bond_create_cmd += bond_properties
end
begin
vsctl(bond_create_cmd)
rescue Puppet::ExecutionFailure => error
notice(">>>#{bond_create_cmd.join(',')}<<<")
fail("Can't create bond '#{@resource[:bond]}' (ports: #{@resource[:ports].join(',')}) for bridge '#{@resource[:bridge]}'.\n#{error}")
fail("Can't create bond '#{@resource[:bond]}' (interfaces: #{@resource[:interfaces].join(',')}) for bridge '#{@resource[:bridge]}'.\n#{error}")
end
end

View File

@ -0,0 +1,90 @@
Puppet::Type.type(:l2_ovs_patch).provide(:ovs) do
optional_commands(
:vsctl => "/usr/bin/ovs-vsctl",
:appctl => "/usr/bin/ovs-appctl"
)
def get_names()
# result always contains array of two elements
# get_names()[i-1] always returns neighbor's name
#
rv = []
i = 0
for peer in @resource[:peers]
if peer == nil
rv.insert(-1, "#{@resource[:bridges][i]}--#{@resource[:bridges][i-1]}")
else
rv.insert(-1, peer)
end
i += 1
end
#todo: chect tags, trunks and bridge names
return rv
end
def _exists?(interface)
rv = true
begin
result = vsctl('get', 'interface', "#{interface}", 'type')
rv = false if result.strip() != 'patch'
rescue Puppet::ExecutionFailure
rv = false
end
return rv
end
def exists?
for name in get_names()
return false if not _exists?(name)
end
return true
end
def create()
names = get_names()
i = 0
for name in names
# tag and trunks for port
port_properties = [] #@resource[:port_properties]
tag = @resource[:tags][i]
if tag > 0
port_properties.insert(-1, "tag=#{tag}")
end
if not @resource[:trunks].empty?
port_properties.insert(-1, "trunks=[#{@resource[:trunks].join(',')}]")
end
#todo: kill before create if need
cmd = ['add-port', @resource[:bridges][i], name]
cmd.concat(port_properties)
cmd.concat(['--', 'set', 'interface', name, 'type=patch'])
begin
vsctl(cmd)
rescue Puppet::ExecutionFailure => errmsg
raise Puppet::ExecutionFailure, "Can't create patch '#{name}':\n#{errmsg}"
end
i += 1
end
i = 0
for name in names
begin
vsctl('set', 'interface', name, "options:peer=#{names[i-1]}")
rescue Puppet::ExecutionFailure => errmsg
raise Puppet::ExecutionFailure, "Can't connect patch '#{name}' to '#{names[i-1]}':\n#{errmsg}"
end
i += 1
end
end
def destroy()
names = get_names()
i = 0
for name in names
begin
vsctl('del-port', @resource[:bridges][i], name)
rescue Puppet::ExecutionFailure => error
raise Puppet::ExecutionFailure, "Can't remove patch '#{name}' from bridge '#{@resource[:bridges][i]}':\n#{error}"
end
i += 1
end
end
end

View File

@ -16,16 +16,23 @@ Puppet::Type.type(:l2_ovs_port).provide(:ovs) do
rescue Puppet::ExecutionFailure
# pass
end
# tag and trunks for port
port_properties = @resource[:port_properties]
if @resource[:tag] > 0
port_properties.insert(-1, "tag=#{@resource[:tag]}")
end
if not @resource[:trunks].empty?
port_properties.insert(-1, "trunks=[#{@resource[:trunks].join(',')}]")
end
# Port create begins from definition brodge and port
cmd = [@resource[:bridge], @resource[:interface]]
# add port properties (k/w) to command line
if @resource[:port_properties]
for option in @resource[:port_properties]
cmd += [option]
if not port_properties.empty?
for option in port_properties
cmd.insert(-1, option)
end
end
# set interface type
#TODO: implement type=>patch sintax as type=>'patch:peer-name'
if @resource[:type] and @resource[:type].to_s != ''
tt = "type=" + @resource[:type].to_s
cmd += ['--', "set", "Interface", @resource[:interface], tt]

View File

@ -15,16 +15,16 @@ Puppet::Type.newtype(:l2_ovs_bond) do
end
end
newparam(:ports) do
desc "List of ports that will be added to the bond"
newparam(:interfaces) do
desc "List of interfaces that will be added to the bond"
#
validate do |val|
if not val.is_a?(Array)
fail("Ports parameter must be an array (not #{val.class}).")
fail("Interfaces parameter must be an array (not #{val.class}).")
end
for port in val
if not port =~ /^[a-z][0-9a-z\.\-\_]*[0-9a-z]$/
fail("Invalid port name: '#{port}'")
for ii in val
if not ii =~ /^[a-z][0-9a-z\.\-\_]*[0-9a-z]$/
fail("Invalid port name: '#{ii}'")
end
end
end
@ -50,6 +50,47 @@ Puppet::Type.newtype(:l2_ovs_bond) do
end
end
newparam(:tag) do
defaultto(0)
desc "802.1q tag"
validate do |val|
if not (val.is_a?(Integer) or val.is_a?(String))
fail("tag must be an integer (not #{val.class}).")
end
v = val.to_i
if v < 0 or v > 4094
fail("tag must be an integer in 2..4094 interval")
end
end
munge do |val|
val.to_i
end
end
newparam(:trunks) do
defaultto([])
desc "Array of trunks id, for configure port in trunk mode"
validate do |val|
if not (val.is_a?(Array) or val.is_a?(String) or val.is_a?(Integer)) # String need for array with one element. it's a puppet's feature
fail("trunks must be an array (not #{val.class}).")
end
end
munge do |val|
if val.is_a?(String)
[val.to_i]
elsif val.is_a?(Integer)
if val >= 0 and val < 4095
[val]
else
[]
end
else
val
end
end
end
autorequire(:l2_ovs_bridge) do
[self[:bridge]]
end

View File

@ -0,0 +1,97 @@
Puppet::Type.newtype(:l2_ovs_patch) do
@doc = "Manage a Open vSwitch patch between two bridges"
desc @doc
ensurable
newparam(:name) # workarround for following error:
# Error 400 on SERVER: Could not render to pson: undefined method `merge' for []:Array
# http://projects.puppetlabs.com/issues/5220
newparam(:bridges) do
desc "Array of bridges that will be connected"
#
validate do |val|
if not (val.is_a?(Array) and val.size() == 2)
fail("Must be an array of two bridge names")
end
if not (val[0].is_a?(String) and val[1].is_a?(String))
fail("Bridge names must have be a string.")
end
end
end
newparam(:peers) do
defaultto([nil,nil])
desc "List of names that will be used for naming patches at it's ends."
#
validate do |val|
if not (val.is_a?(Array) and val.size() == 2)
fail("Must be an array of two bridge names")
end
for i in val
if not (i.is_a?(String) or i == nil)
fail("Peer names must have be a string.")
end
end
end
end
# newparam(:skip_existing) do
# defaultto(false)
# desc "Allow to skip existing bond"
# end
newparam(:tags) do
defaultto([0,0])
desc "Array of 802.1q tag for ends."
#
validate do |val|
if not (val.is_a?(Array) and val.size() == 2)
fail("Must be an array of integers")
end
for i in val
if not i.is_a?(Integer)
fail("802.1q tags must have be a integer.")
end
if i < 0 or i > 4094
fail("Wrong 802.1q tag. Tag must be an integer in 2..4094 interval")
end
end
end
end
newparam(:trunks) do
defaultto([])
desc "Array of trunks id, for configure patch's ends as ports in trunk mode"
#
validate do |val|
if not (val.is_a?(Array) or val.is_a?(Integer)) # Integer need for array with one element. it's a puppet's feature
fail("Must be an array (not #{val.class}).")
end
if val.is_a?(Array)
for i in val
if not (i.to_i >= 0 and i.to_i <= 4094)
fail("Wrong trunk. Tag must be an integer in 2..4094 interval")
end
end
else
if not (val >= 0 and val <= 4094)
fail("Wrong trunk. Tag must be an integer in 2..4094 interval")
end
end
end
#
munge do |val|
if val.is_a?(Integer)
[val]
else
val
end
end
end
autorequire(:l2_ovs_bridge) do
self[:bridges]
end
end

View File

@ -44,6 +44,13 @@ Puppet::Type.newtype(:l2_ovs_port) do
fail("port_properties must be an array (not #{val.class}).")
end
end
munge do |val|
if val.is_a?(String)
[val]
else
val
end
end
end
newparam(:interface_properties) do
@ -54,6 +61,54 @@ Puppet::Type.newtype(:l2_ovs_port) do
fail("interface_properties must be an array (not #{val.class}).")
end
end
munge do |val|
if val.is_a?(String)
[val]
else
val
end
end
end
newparam(:tag) do
defaultto(0)
desc "802.1q tag"
validate do |val|
if not (val.is_a?(Integer) or val.is_a?(String))
fail("tag must be an integer (not #{val.class}).")
end
v = val.to_i
if v < 0 or v > 4094
fail("tag must be an integer in 2..4094 interval")
end
end
munge do |val|
val.to_i
end
end
newparam(:trunks) do
defaultto([])
desc "Array of trunks id, for configure port in trunk mode"
validate do |val|
if not (val.is_a?(Array) or val.is_a?(String) or val.is_a?(Integer)) # String need for array with one element. it's a puppet's feature
fail("trunks must be an array (not #{val.class}).")
end
end
munge do |val|
if val.is_a?(String)
[val.to_i]
elsif val.is_a?(Integer)
if val >= 0 and val < 4095
[val]
else
[]
end
else
val
end
end
end
autorequire(:l2_ovs_bridge) do

View File

@ -10,8 +10,15 @@
# [*bridge*]
# Bridge that will contain this bond.
#
# [*ports*]
# List of ports in this bond.
# [*interfaces*]
# List of interfaces in this bond.
#
# [*tag*]
# Specify 802.1q tag for result bond. If need.
#
# [*trunks*]
# Specify array of 802.1q tags if need configure bond in trunk mode.
# Define trunks => [0] if you need pass only untagged traffic.
#
# [*skip_existing*]
# If this bond already exists it will be ignored without any errors.
@ -19,21 +26,34 @@
#
define l23network::l2::bond (
$bridge,
$ports,
$interfaces = undef,
$ports = undef, # deprecated, must be used interfaces
$bond = $name,
$properties = [],
$tag = 0,
$trunks = [],
$ensure = present,
$skip_existing = false,
$skip_existing = false
) {
if ! $::l23network::l2::use_ovs {
fail('You must enable Open vSwitch by setting the l23network::l2::use_ovs to true.')
}
if $interfaces {
$r_interfaces = $interfaces
} elsif $ports {
$r_interfaces = $ports
} else {
fail("You must specify 'interfaces' property for this bond.")
}
if ! defined (L2_ovs_bond["$bond"]) {
l2_ovs_bond { "$bond" :
ports => $ports,
interfaces => $r_interfaces,
ensure => $ensure,
bridge => $bridge,
tag => $tag,
trunks => $trunks,
properties => $properties,
skip_existing => $skip_existing,
}

View File

@ -0,0 +1,51 @@
# == Define: l23network::l2::patch
#
# Connect two open vSwitch bridges by virtual patch-cord.
#
# === Parameters
# [*bridges*]
# Bridges that will be connected.
#
# [*peers*]
# Patch port names for both bridges. must be array of two strings.
#
# [*tags*]
# Specify 802.1q tag for each end of patchcord. Must be array of 2 integers.
# Default [0,0] -- untagged
#
# [*trunks*]
# Specify array of 802.1q tags (identical for both ends) if need configure patch in trunk mode.
# Define trunks => [0] if you need pass only untagged traffic.
# by default -- undefined.
#
# [*skip_existing*]
# If this patch already exists it will be ignored without any errors.
# Must be true or false.
#
define l23network::l2::patch (
$bridges,
$peers = [undef,undef],
$tags = [0, 0],
$trunks = [],
$ensure = present,
) {
if ! $::l23network::l2::use_ovs {
fail('You must enable Open vSwitch by setting the l23network::l2::use_ovs to true.')
}
# Architecture limitation.
# We can't create more one patch between same bridges.
#$patch = "${bridges[0]}_${tags[0]}--${bridges[1]}_${tags[1]}"
$patch = "${bridges[0]}--${bridges[1]}"
if ! defined (L2_ovs_patch["$patch"]) {
l2_ovs_patch { "$patch" :
bridges => $bridges,
peers => $peers,
tags => $tags,
trunks => $trunks,
ensure => $ensure
}
Service<| title == 'openvswitch-service' |> -> L2_ovs_patch["$patch"]
}
}

View File

@ -17,6 +17,13 @@
# the port with default behavior.
# (see http://openvswitch.org/cgi-bin/ovsman.cgi?page=utilities%2Fovs-vsctl.8)
#
# [*tag*]
# Specify 802.1q tag for result bond. If need.
#
# [*trunks*]
# Specify array of 802.1q tags if need configure bond in trunk mode.
# Define trunks => [0] if you need pass only untagged traffic.
#
# [*skip_existing*]
# If this port already exists it will be ignored without any errors.
# Must be true or false.
@ -29,19 +36,23 @@ define l23network::l2::port (
$interface_properties = [],
$ensure = present,
$skip_existing = false,
$tag = 0,
$trunks = [],
) {
if ! $::l23network::l2::use_ovs {
fail('You must enable Open vSwitch by setting the l23network::l2::use_ovs to true.')
}
if ! defined (L2_ovs_port[$port]) {
l2_ovs_port { $port :
ensure => $ensure,
bridge => $bridge,
type => $type,
port_properties => $port_properties,
interface_properties => $interface_properties,
skip_existing => $skip_existing,
tag => $tag,
trunks => $trunks,
port_properties => $port_properties,
interface_properties => $interface_properties,
skip_existing => $skip_existing
}
Service<| title == 'openvswitch-service' |> -> L2_ovs_port[$port]
}

View File

@ -0,0 +1,185 @@
require 'spec_helper'
begin
require 'puppet/parser/functions/lib/l23network_scheme.rb'
rescue LoadError => e
# puppet apply does not add module lib directories to the $LOAD_PATH (See
# #4248). It should (in the future) but for the time being we need to be
# defensive which is what this rescue block is doing.
rb_file = File.join(File.dirname(__FILE__),'lib','l23network_scheme.rb')
load rb_file if File.exists?(rb_file) or raise e
end
describe 'generate_network_config' do
let(:scope) { PuppetlabsSpec::PuppetInternals.scope }
# before(:each) do
# L23network::Scheme.set = {
# :endpoints => {
# :eth0 => {:IP => 'dhcp'},
# :"br-ex" => {
# :gateway => '10.1.3.1',
# :IP => ['10.1.3.11/24'],
# },
# :"br-mgmt" => { :IP => ['10.20.1.11/25'] },
# :"br-storage" => { :IP => ['192.168.1.2/24'] },
# :"br-prv" => { :IP => 'none' },
# },
# :roles => {
# :management => 'br-mgmt',
# :private => 'br-prv',
# :ex => 'br-ex',
# :storage => 'br-storage',
# :admin => 'eth0',
# },
# }
# end
it 'should exist' do
Puppet::Parser::Functions.function('generate_network_config').should == 'function_generate_network_config'
end
# it 'should convert string-boolean values to boolean' do
# should run.with_params({
# :s_true => 'true',
# :s_false => 'false',
# :s_none => 'none',
# :s_null => 'null',
# :s_nil => 'nil',
# :s_nill => 'nill',
# }).and_return({
# :s_true => true,
# :s_false => false,
# :s_none => nil,
# :s_null => nil,
# :s_nil => nil,
# :s_nill => nil,
# })
# end
# it 'should convert UP-sace string-boolean values to boolean' do
# should run.with_params({
# :s_true => 'TRUE',
# :s_false => 'FALSE',
# :s_none => 'NONE',
# :s_null => 'NULL',
# :s_nil => 'NIL',
# :s_nill => 'NILL',
# }).and_return({
# :s_true => true,
# :s_false => false,
# :s_none => nil,
# :s_null => nil,
# :s_nil => nil,
# :s_nill => nil,
# })
# end
# it 'should convert reccursive hashes' do
# should run.with_params({
# :bool_hash => {
# :str => 'aaa',
# :int => 123,
# :array => [111,222,333],
# :hash => {
# :str => 'aaa',
# :int => 123,
# :array => [111,222,333],
# :a_sbool => ['true', 'nil', 'false'],
# :a_bool => [true, nil, false],
# :hash => {
# :str => 'aaa',
# :int => 123,
# :array => [111,222,333],
# :a_sbool => ['true', 'nil', 'false'],
# :a_bool => [true, nil, false],
# },
# },
# :a_sbool => ['true', 'nil', 'false'],
# :a_bool => [true, nil, false],
# },
# :bool_hash => {
# :t => true,
# :f => false,
# :n => nil
# },
# }).and_return({
# :bool_hash => {
# :str => 'aaa',
# :int => 123,
# :array => [111,222,333],
# :hash => {
# :str => 'aaa',
# :int => 123,
# :array => [111,222,333],
# :a_sbool => [true, nil, false],
# :a_bool => [true, nil, false],
# :hash => {
# :str => 'aaa',
# :int => 123,
# :array => [111,222,333],
# :a_sbool => [true, nil, false],
# :a_bool => [true, nil, false],
# },
# },
# :a_sbool => [true, nil, false],
# :a_bool => [true, nil, false],
# },
# :bool_hash => {
# :t => true,
# :f => false,
# :n => nil
# },
# })
# end
# it 'should convert array of hashes' do
# should run.with_params({ :array => [
# {:aaa=>1,"aaa"=>11, :bbb=>2,'bbb'=>12, :ccc=>3,'ccc'=>3},
# {:t=>'true','tt'=>'true', :f=>'false','ff'=>'false', :n=>'nil','nn'=>'nil'},
# {
# :s_true => 'true',
# :s_false => 'false',
# :s_none => 'none',
# :s_null => 'null',
# :s_nil => 'nil',
# :s_nill => 'nill',
# },
# {
# :s_true => 'TRUE',
# :s_false => 'FALSE',
# :s_none => 'NONE',
# :s_null => 'NULL',
# :s_nil => 'NIL',
# :s_nill => 'NILL',
# },
# ]}).and_return({ :array => [
# {:aaa=>1,"aaa"=>11, :bbb=>2,'bbb'=>12, :ccc=>3,'ccc'=>3},
# {:t=>true,'tt'=>true, :f=>false,'ff'=>false, :n=>nil,'nn'=>nil},
# {
# :s_true => true,
# :s_false => false,
# :s_none => nil,
# :s_null => nil,
# :s_nil => nil,
# :s_nill => nil,
# },
# {
# :s_true => true,
# :s_false => false,
# :s_none => nil,
# :s_null => nil,
# :s_nil => nil,
# :s_nill => nil,
# },
# ]})
# end
# it 'should throw an error' do
# lambda {
# scope.function_merge_arrays(['xxx'])
# }.should(raise_error(Puppet::ParseError))
# end
end
# vim: set ts=2 sw=2 et :

View File

@ -0,0 +1,78 @@
require 'spec_helper'
begin
require 'puppet/parser/functions/lib/l23network_scheme.rb'
rescue LoadError => e
# puppet apply does not add module lib directories to the $LOAD_PATH (See
# #4248). It should (in the future) but for the time being we need to be
# defensive which is what this rescue block is doing.
rb_file = File.join(File.dirname(__FILE__),'lib','l23network_scheme.rb')
load rb_file if File.exists?(rb_file) or raise e
end
describe 'get_network_role_property' do
let(:scope) { PuppetlabsSpec::PuppetInternals.scope }
before(:each) do
L23network::Scheme.set = {
:endpoints => {
:eth0 => {:IP => 'dhcp'},
:"br-ex" => {
:gateway => '10.1.3.1',
:IP => ['10.1.3.11/24'],
},
:"br-mgmt" => { :IP => ['10.20.1.11/25'] },
:"br-storage" => { :IP => ['192.168.1.2/24'] },
:"br-prv" => { :IP => 'none' },
},
:roles => {
:management => 'br-mgmt',
:private => 'br-prv',
:ex => 'br-ex',
:storage => 'br-storage',
:admin => 'eth0',
},
}
end
it 'should exist' do
Puppet::Parser::Functions.function('get_network_role_property').should == 'function_get_network_role_property'
end
it 'should return interface name for "private" network role' do
should run.with_params('private', 'interface').and_return('br-prv')
end
it 'should raise for non-existing role name' do
should run.with_params('not_exist', 'interface').and_raise_error(Puppet::ParseError)
end
it 'should return ip address for "management" network role' do
should run.with_params('management', 'ipaddr').and_return('10.20.1.11')
end
it 'should return cidr-notated ip address for "management" network role' do
should run.with_params('management', 'cidr').and_return('10.20.1.11/25')
end
it 'should return netmask for "management" network role' do
should run.with_params('management', 'netmask').and_return('255.255.255.128')
end
it 'should return ip address and netmask for "management" network role' do
should run.with_params('management', 'ipaddr_netmask_pair').and_return(['10.20.1.11','255.255.255.128'])
end
it 'should return NIL for "admin" network role' do
should run.with_params('admin', 'netmask').and_return(nil)
end
it 'should return NIL for "admin" network role' do
should run.with_params('admin', 'ipaddr').and_return(nil)
end
it 'should return NIL for "admin" network role' do
should run.with_params('admin', 'cidr').and_return(nil)
end
it 'should return NIL for "admin" network role' do
should run.with_params('admin', 'ipaddr_netmask_pair').and_return(nil)
end
end

View File

@ -0,0 +1,154 @@
require 'spec_helper'
describe 'sanitize_bool_in_hash' do
let(:scope) { PuppetlabsSpec::PuppetInternals.scope }
it 'should exist' do
Puppet::Parser::Functions.function('sanitize_bool_in_hash').should == 'function_sanitize_bool_in_hash'
end
it 'should convert string-boolean values to boolean' do
should run.with_params({
:s_true => 'true',
:s_false => 'false',
:s_none => 'none',
:s_null => 'null',
:s_nil => 'nil',
:s_nill => 'nill',
}).and_return({
:s_true => true,
:s_false => false,
:s_none => nil,
:s_null => nil,
:s_nil => nil,
:s_nill => nil,
})
end
it 'should convert UP-sace string-boolean values to boolean' do
should run.with_params({
:s_true => 'TRUE',
:s_false => 'FALSE',
:s_none => 'NONE',
:s_null => 'NULL',
:s_nil => 'NIL',
:s_nill => 'NILL',
}).and_return({
:s_true => true,
:s_false => false,
:s_none => nil,
:s_null => nil,
:s_nil => nil,
:s_nill => nil,
})
end
it 'should convert reccursive hashes' do
should run.with_params({
:bool_hash => {
:str => 'aaa',
:int => 123,
:array => [111,222,333],
:hash => {
:str => 'aaa',
:int => 123,
:array => [111,222,333],
:a_sbool => ['true', 'nil', 'false'],
:a_bool => [true, nil, false],
:hash => {
:str => 'aaa',
:int => 123,
:array => [111,222,333],
:a_sbool => ['true', 'nil', 'false'],
:a_bool => [true, nil, false],
},
},
:a_sbool => ['true', 'nil', 'false'],
:a_bool => [true, nil, false],
},
:bool_hash => {
:t => true,
:f => false,
:n => nil
},
}).and_return({
:bool_hash => {
:str => 'aaa',
:int => 123,
:array => [111,222,333],
:hash => {
:str => 'aaa',
:int => 123,
:array => [111,222,333],
:a_sbool => [true, nil, false],
:a_bool => [true, nil, false],
:hash => {
:str => 'aaa',
:int => 123,
:array => [111,222,333],
:a_sbool => [true, nil, false],
:a_bool => [true, nil, false],
},
},
:a_sbool => [true, nil, false],
:a_bool => [true, nil, false],
},
:bool_hash => {
:t => true,
:f => false,
:n => nil
},
})
end
it 'should convert array of hashes' do
should run.with_params({ :array => [
{:aaa=>1,"aaa"=>11, :bbb=>2,'bbb'=>12, :ccc=>3,'ccc'=>3},
{:t=>'true','tt'=>'true', :f=>'false','ff'=>'false', :n=>'nil','nn'=>'nil'},
{
:s_true => 'true',
:s_false => 'false',
:s_none => 'none',
:s_null => 'null',
:s_nil => 'nil',
:s_nill => 'nill',
},
{
:s_true => 'TRUE',
:s_false => 'FALSE',
:s_none => 'NONE',
:s_null => 'NULL',
:s_nil => 'NIL',
:s_nill => 'NILL',
},
]}).and_return({ :array => [
{:aaa=>1,"aaa"=>11, :bbb=>2,'bbb'=>12, :ccc=>3,'ccc'=>3},
{:t=>true,'tt'=>true, :f=>false,'ff'=>false, :n=>nil,'nn'=>nil},
{
:s_true => true,
:s_false => false,
:s_none => nil,
:s_null => nil,
:s_nil => nil,
:s_nill => nil,
},
{
:s_true => true,
:s_false => false,
:s_none => nil,
:s_null => nil,
:s_nil => nil,
:s_nill => nil,
},
]})
end
it 'should throw an error' do
lambda {
scope.function_merge_arrays(['xxx'])
}.should(raise_error(Puppet::ParseError))
end
end
# vim: set ts=2 sw=2 et :

View File

@ -21,7 +21,8 @@ stage {'glance-image':
require => Stage['main'],
}
# ANC: Advanced network configuration. Discovering.
#prepare_network_config(parsejson($::network_scheme))
if $nodes != undef {
$nodes_hash = parsejson($nodes)
@ -135,6 +136,17 @@ class node_netconfig (
l23network::l3::ifconfig {$fixed_interface: ipaddr=>'none' }
}
# ANC: Advanced network configuration. Creating resources.
#class advanced_node_netconfig {
# $sdn = generate_network_config()
# notify {"SDN: ${sdn}": }
#}
#
#class {'advanced_node_netconfig':
# stage => 'netconfig'
#}
case $::operatingsystem {
'redhat' : {
$queue_provider = 'qpid'