Suppport for bonded interfaces
This separates the interface parameter from the port name. The port name is the new namevar and interface can be an array of interfaces for bonding. The default value ensures that this is mostly backwards compatible. The only incompatibility is if the interface parameter was explicitly set. In this case the new port parameter has to be used instead. The bonding configuration can be set with the bond_mode, lacp and lacp_time parameters. Change-Id: I0364ce46dce4b87a457d2265518fa968a48e3529
This commit is contained in:
parent
17b62e56e0
commit
035ef34f7c
@ -1,21 +1,117 @@
|
||||
require 'puppet'
|
||||
|
||||
UUID_RE = /[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89aAbB][a-f0-9]{3}-[a-f0-9]{12}/
|
||||
|
||||
Puppet::Type.type(:vs_port).provide(:ovs) do
|
||||
desc 'Openvswitch port manipulation'
|
||||
|
||||
has_feature :bonding
|
||||
|
||||
commands :vsctl => 'ovs-vsctl'
|
||||
|
||||
def exists?
|
||||
vsctl('list-ports', @resource[:bridge]).include? @resource[:interface]
|
||||
vsctl('list-ports', @resource[:bridge]).include? @resource[:port]
|
||||
rescue Puppet::ExecutionFailure => e
|
||||
return false
|
||||
end
|
||||
|
||||
def create
|
||||
vsctl('add-port', @resource[:bridge], @resource[:interface])
|
||||
# create with first interface, other interfaces will be added later when synchronizing properties
|
||||
vsctl('--', '--id=@iface0', 'create', 'Interface', "name=#{@resource[:interface][0]}", '--', 'add-port', @resource[:bridge], @resource[:port], 'interfaces=@iface0')
|
||||
|
||||
# synchronize properties
|
||||
# Only sync those properties actually supported by the provider. This
|
||||
# allows this provider to be used as a base class for providers not
|
||||
# supporting all properties.
|
||||
sync_properties = []
|
||||
if self.bonding?
|
||||
sync_properties += [:interface,
|
||||
:bond_mode,
|
||||
:lacp,
|
||||
:lacp_time,
|
||||
]
|
||||
end
|
||||
for prop_name in sync_properties
|
||||
property = @resource.property(prop_name)
|
||||
property.sync unless property.safe_insync?(property.retrieve)
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
vsctl('del-port', @resource[:bridge], @resource[:interface])
|
||||
vsctl('del-port', @resource[:bridge], @resource[:port])
|
||||
end
|
||||
|
||||
def interface
|
||||
get_port_interface_column('name')
|
||||
end
|
||||
|
||||
def interface=(value)
|
||||
# find interfaces we want to keep on the port
|
||||
keep = @resource.property(:interface).retrieve() & value
|
||||
keep_uids = keep.map { |iface| vsctl('get', 'Interface', iface, '_uuid').strip }
|
||||
new = value - keep
|
||||
args = ['--'] + new.each_with_index.map { |iface, i| ["--id=@#{i+1}", 'create', 'Interface', "name=#{iface}", '--'] }
|
||||
ifaces = (1..new.length).map { |i| "@#{i}" } + keep_uids
|
||||
args += ['set', 'Port', @resource[:port], "interfaces=#{ifaces.join(',')}"]
|
||||
vsctl(*args)
|
||||
end
|
||||
|
||||
def bond_mode
|
||||
get_port_column('bond_mode')
|
||||
end
|
||||
|
||||
def bond_mode=(value)
|
||||
set_port_column('bond_mode', value)
|
||||
end
|
||||
|
||||
def lacp
|
||||
get_port_column('lacp')
|
||||
end
|
||||
|
||||
def lacp=(value)
|
||||
set_port_column('lacp', value)
|
||||
end
|
||||
|
||||
def lacp_time
|
||||
get_port_column('other_config:lacp-time')
|
||||
end
|
||||
|
||||
def lacp_time=(value)
|
||||
set_port_column('other_config:lacp-time', value)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def port_column_command(command, column, value=nil)
|
||||
if value
|
||||
vsctl(command, 'Port', @resource[:port], column, value)
|
||||
else
|
||||
vsctl('--if-exists', command, 'Port', @resource[:port], column)
|
||||
end
|
||||
end
|
||||
|
||||
def get_port_column(column)
|
||||
value = port_column_command('get', column).strip
|
||||
if value == '[]' then '' else value end
|
||||
end
|
||||
|
||||
def set_port_column(column, value)
|
||||
if ! value or value.empty?
|
||||
# columns with maps need special handling, single map entries
|
||||
# can be removed with the remove command
|
||||
column, key = column.split(':')
|
||||
if ! key
|
||||
port_column_command('clear', column)
|
||||
else
|
||||
port_column_command('remove', [column, key])
|
||||
end
|
||||
else
|
||||
port_column_command('set', "#{column}=#{value}")
|
||||
end
|
||||
end
|
||||
|
||||
def get_port_interface_column(column)
|
||||
uuids = get_port_column('interfaces').scan(UUID_RE)
|
||||
uuids.map!{|id| vsctl('get', 'Interface', id, column).strip.tr('"', '')}
|
||||
end
|
||||
end
|
||||
|
@ -21,6 +21,15 @@ Puppet::Type.type(:vs_port).provide(:ovs_redhat, :parent => :ovs) do
|
||||
commands :ifup => 'ifup'
|
||||
commands :vsctl => 'ovs-vsctl'
|
||||
|
||||
def initialize(value={})
|
||||
super(value)
|
||||
# Set interface property although it's not really
|
||||
# supported on this provider. This ensures that all
|
||||
# methodes inherited from the ovs provider work as
|
||||
# expected.
|
||||
@resource[:interface] = @resource[:port]
|
||||
end
|
||||
|
||||
def create
|
||||
unless vsctl('list-ports',
|
||||
@resource[:bridge]).include? @resource[:interface]
|
||||
|
@ -3,16 +3,35 @@ require 'puppet'
|
||||
Puppet::Type.newtype(:vs_port) do
|
||||
desc 'A Virtual Switch Port'
|
||||
|
||||
feature :bonding, "The provider supports bonded interfaces"
|
||||
|
||||
ensurable
|
||||
|
||||
newparam(:interface, :namevar => true) do
|
||||
desc 'The interface to attach to the bridge'
|
||||
newparam(:port, :namevar => true) do
|
||||
desc 'Name of the port.'
|
||||
|
||||
validate do |value|
|
||||
if !value.is_a?(String)
|
||||
raise ArgumentError, "Invalid port name #{value}. Requires a String, not a #{value.class}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
newproperty(:interface, :array_matching => :all, :required_features => :bonding) do
|
||||
desc 'The interfaces to attach to the bridge. Defaults to the interface with the same name as the port.'
|
||||
|
||||
defaultto { @resource[:port] }
|
||||
|
||||
validate do |value|
|
||||
if !value.is_a?(String)
|
||||
raise ArgumentError, "Invalid interface #{value}. Requires a String, not a #{value.class}"
|
||||
end
|
||||
end
|
||||
|
||||
# order of interfaces does not matter
|
||||
def insync?(is)
|
||||
is.sort == should.sort
|
||||
end
|
||||
end
|
||||
|
||||
newparam(:bridge) do
|
||||
@ -25,6 +44,44 @@ Puppet::Type.newtype(:vs_port) do
|
||||
end
|
||||
end
|
||||
|
||||
newproperty(:bond_mode, :required_features => :bonding) do
|
||||
desc "Interface bonding mode for this port.
|
||||
|
||||
Possible values are 'active-backup', 'balance-tcp' or 'balance-slb'.
|
||||
By default no bonding mode is set (bond_mode='')."
|
||||
|
||||
defaultto ""
|
||||
|
||||
newvalues(:"active-backup", :"balance-tcp", :"balance-slb", "")
|
||||
end
|
||||
|
||||
newproperty(:lacp, :required_features => :bonding) do
|
||||
desc "LACP configuration for this port.
|
||||
|
||||
Possible values are 'active', 'passive' or 'off'. The default is
|
||||
'off'."
|
||||
|
||||
defaultto :off
|
||||
|
||||
newvalues(:active, :passive, :off)
|
||||
end
|
||||
|
||||
newproperty(:lacp_time, :required_features => :bonding) do
|
||||
desc "The LACP timing which should be used on this Port.
|
||||
|
||||
Possible values are 'slow' and 'fast'. The default is 'slow'.
|
||||
|
||||
When configured to be fast LACP heartbeats are requested at a rate of
|
||||
once per second causing connectivity problems to be detected more quickly.
|
||||
In slow mode, heartbeats are requested at a rate of once every 30 seconds."
|
||||
|
||||
# Default to the empty string which is equivalent to slow as this is also
|
||||
# the OVS default. This avoids setting a useless property on non bonded ports.
|
||||
defaultto ""
|
||||
|
||||
newvalues(:fast, :slow, "")
|
||||
end
|
||||
|
||||
autorequire(:vs_bridge) do
|
||||
self[:bridge] if self[:bridge]
|
||||
end
|
||||
|
Loading…
x
Reference in New Issue
Block a user