From 8e14e2e258a8f2f7189ed37c6337c41fbff0362a Mon Sep 17 00:00:00 2001 From: Al Bailey Date: Mon, 6 Jun 2016 17:13:09 -0400 Subject: [PATCH] puppet-network Kilo quilt changes --- .../lib/puppet/provider/network_config/redhat.rb | 39 ++- .../lib/puppet/provider/network_config/wrlinux.rb | 296 +++++++++++++++++++++ .../lib/puppet/provider/network_route/wrlinux.rb | 109 ++++++++ .../network/lib/puppet/type/network_config.rb | 4 + packstack/puppet/modules/network/manifests/bond.pp | 22 ++ .../puppet/modules/network/manifests/bond/setup.pp | 2 + .../modules/network/manifests/bond/wrlinux.pp | 56 ++++ 7 files changed, 521 insertions(+), 7 deletions(-) create mode 100644 packstack/puppet/modules/network/lib/puppet/provider/network_config/wrlinux.rb create mode 100644 packstack/puppet/modules/network/lib/puppet/provider/network_route/wrlinux.rb create mode 100644 packstack/puppet/modules/network/manifests/bond/wrlinux.pp diff --git a/packstack/puppet/modules/network/lib/puppet/provider/network_config/redhat.rb b/packstack/puppet/modules/network/lib/puppet/provider/network_config/redhat.rb index 4b6de7e..758f387 100644 --- a/packstack/puppet/modules/network/lib/puppet/provider/network_config/redhat.rb +++ b/packstack/puppet/modules/network/lib/puppet/provider/network_config/redhat.rb @@ -19,7 +19,12 @@ Puppet::Type.type(:network_config).provide(:redhat) do has_feature :provider_options # @return [String] The path to network-script directory on redhat systems - SCRIPT_DIRECTORY = "/etc/sysconfig/network-scripts" + # SCRIPT_DIRECTORY = "/etc/sysconfig/network-scripts" + # WRS: Generate temporary copies. It will get compared to files under + # /etc/sysconfig/network-scripts afterward. Only config that have changed + # will get replaced. Don't let puppet directly manage them, else it will + # trigger un-wanted networking actions (like up/down). + SCRIPT_DIRECTORY = "/var/run/network-scripts.puppet" # The valid vlan ID range is 0-4095; 4096 is out of range VLAN_RANGE_REGEX = %r[\d{1,3}|40[0-9][0-5]] @@ -35,6 +40,7 @@ Puppet::Type.type(:network_config).provide(:redhat) do :name => 'DEVICE', :hotplug => 'HOTPLUG', :mtu => 'MTU', + :gateway => 'GATEWAY', } # Map provider instances to files based on their name @@ -60,8 +66,14 @@ Puppet::Type.type(:network_config).provide(:redhat) do # RedhatProvider.target_files # # => ['/etc/sysconfig/network-scripts/ifcfg-eth0', '/etc/sysconfig/network-scripts/ifcfg-eth1'] def self.target_files(script_dir = SCRIPT_DIRECTORY) - entries = Dir.entries(script_dir).select {|entry| entry.match SCRIPT_REGEX} - entries.map {|entry| File.join(SCRIPT_DIRECTORY, entry)} + entries = [] + if Dir.exists?(SCRIPT_DIRECTORY) + Dir.foreach(SCRIPT_DIRECTORY) do |item| + next if not item.match SCRIPT_REGEX + entries << item + end + end + entries end # Convert a redhat network script into a hash @@ -184,6 +196,8 @@ Puppet::Type.type(:network_config).provide(:redhat) do end def self.format_file(filename, providers) + Dir.mkdir(SCRIPT_DIRECTORY) unless File.exists?(SCRIPT_DIRECTORY) + if providers.length == 0 return "" elsif providers.length > 1 @@ -193,11 +207,11 @@ Puppet::Type.type(:network_config).provide(:redhat) do provider = providers[0] props = {} - # Map everything to a flat hash - props = (provider.options || {}) + props = provider.options if provider.options && provider.options != :absent + # Map everything to a flat hash NAME_MAPPINGS.keys.each do |type_name| - if (val = provider.send(type_name)) + if (val = provider.send(type_name)) && val != :absent props[type_name] = val end end @@ -214,11 +228,11 @@ Puppet::Type.type(:network_config).provide(:redhat) do str << %{#{key}=#{val}\n} end + content.prepend(header) content end def self.unmunge(props) - pairs = {} [:onboot, :hotplug].each do |bool_property| @@ -245,6 +259,17 @@ Puppet::Type.type(:network_config).provide(:redhat) do pairs end + def self.header + str = <<-HEADER +# HEADER: This file is is being managed by puppet. Changes to +# HEADER: interfaces that are not being managed by puppet will persist; +# HEADER: however changes to interfaces that are being managed by puppet will +# HEADER: be overwritten. In addition, file order is NOT guaranteed. +# HEADER: Last generated at: #{Time.now} +HEADER + str + end + def self.post_flush_hook(filename) File.chmod(0644, filename) end diff --git a/packstack/puppet/modules/network/lib/puppet/provider/network_config/wrlinux.rb b/packstack/puppet/modules/network/lib/puppet/provider/network_config/wrlinux.rb new file mode 100644 index 0000000..44c645a --- /dev/null +++ b/packstack/puppet/modules/network/lib/puppet/provider/network_config/wrlinux.rb @@ -0,0 +1,296 @@ +require 'puppetx/filemapper' + +Puppet::Type.type(:network_config).provide(:wrlinux) do + # Wind River Linux network_config interfaces provider. + # + # This provider uses the filemapper mixin to map the interfaces file to a + # collection of network_config providers, and back. + # + include PuppetX::FileMapper + + desc "Wind River interfaces style provider" + + confine :osfamily => :wrlinux + defaultfor :osfamily => :wrlinux + + has_feature :provider_options + has_feature :hotpluggable + + def select_file + '/var/run/interfaces.puppet' + end + + def self.target_files + ['/var/run/interfaces.puppet'] + end + + class MalformedInterfacesError < Puppet::Error + def initialize(msg = nil) + msg = 'Malformed wrlinux interfaces file; cannot instantiate network_config resources' if msg.nil? + super + end + end + + def self.raise_malformed + @failed = true + raise MalformedInterfacesError + end + + class Instance + + attr_reader :name + + # Booleans + attr_accessor :onboot, :hotplug + + + # These fields are going to get rearranged to resolve issue 16 + # https://github.com/adrienthebo/puppet-network/issues/16 + attr_accessor :ipaddress, :netmask, :family, :method, :mtu + + # Options hash + attr_reader :options + + def initialize(name) + @name = name + + @options = Hash.new {|hash, key| hash[key] = []} + end + + def to_hash + h = { + :name => @name, + :onboot => @onboot, + :hotplug => @hotplug, + :ipaddress => @ipaddress, + :netmask => @netmask, + :family => @family, + :method => @method, + :mtu => @mtu, + :options => squeeze_options + } + + h.inject({}) do |hash, (key, val)| + hash[key] = val unless val.nil? + hash + end + end + + def squeeze_options + @options.inject({}) do |hash, (key, value)| + if value.size <= 1 + hash[key] = value.pop + else + hash[key] = value + end + + hash + end + end + + class << self + + def reset! + @interfaces = {} + end + + # @return [Array] All class instances + def all_instances + @interfaces ||= {} + @interfaces + end + + def [](name) + if all_instances[name] + obj = all_instances[name] + else + obj = self.new(name) + all_instances[name] = obj + end + + obj + end + end + end + + def self.parse_file(filename, contents) + # Debian has a very irregular format for the interfaces file. The + # parse_file method is somewhat derived from the ifup executable + # supplied in the debian ifupdown package. The source can be found at + # http://packages.debian.org/squeeze/ifupdown + + + # The debian interfaces implementation requires global state while parsing + # the file; namely, the stanza being parsed as well as the interface being + # parsed. + status = :none + current_interface = nil + + lines = contents.split("\n") + # TODO Join lines that end with a backslash + + # Iterate over all lines and determine what attributes they create + lines.each do |line| + + # Strip off any trailing comments + line.sub!(/#.*$/, '') + + case line + when /^\s*#|^\s*$/ + # Ignore comments and blank lines + next + + when /^auto|^allow-auto/ + # Parse out any auto sections + interfaces = line.split(' ') + interfaces.delete_at(0) + + interfaces.each do |name| + Instance[name].onboot = true + end + + # Reset the current parse state + current_interface = nil + + when /^allow-hotplug/ + # parse out allow-hotplug lines + + interfaces = line.split(' ') + interfaces.delete_at(0) + + interfaces.each do |name| + Instance[name].hotplug = true + end + + # Don't reset Reset the current parse state + when /^iface/ + + # Format of the iface line: + # + # iface + # zero or more options for + + if match = line.match(/^iface\s+(\S+)\s+(\S+)\s+(\S+)/) + name = match[1] + family = match[2] + method = match[3] + + # If an iface block for this interface has been seen, the file is + # malformed. + raise_malformed if Instance[name] and Instance[name].family + + status = :iface + current_interface = name + + # This is done automatically + #Instance[name].name = name + Instance[name].family = family + Instance[name].method = method + + else + # If we match on a string with a leading iface, but it isn't in the + # expected format, malformed blar blar + raise_malformed + end + + when /^mapping/ + + # XXX dox + raise Puppet::DevError, "Debian interfaces mapping parsing not implemented." + status = :mapping + + else + # We're currently examining a line that is within a mapping or iface + # stanza, so we need to validate the line and add the options it + # specifies to the known state of the interface. + + case status + when :iface + if match = line.match(/(\S+)\s+(\S.*)/) + # If we're parsing an iface stanza, then we should receive a set of + # lines that contain two or more space delimited strings. Append + # them as options to the iface in an array. + + key = match[1] + val = match[2] + + name = current_interface + + case key + when 'address'; Instance[name].ipaddress = val + when 'netmask'; Instance[name].netmask = val + when 'mtu'; Instance[name].mtu = val + else Instance[name].options[key] << val + end + else + raise_malformed + end + when :mapping + raise Puppet::DevError, "Debian interfaces mapping parsing not implemented." + when :none + raise_malformed + end + end + end + + Instance.all_instances.map {|name, instance| instance.to_hash } + end + + # Generate an array of sections + def self.format_file(filename, providers) + contents = [] + contents << header + + # Add onboot interfaces + if (auto_interfaces = providers.select {|provider| provider.onboot == true }) + stanza = [] + stanza << "auto " + auto_interfaces.map(&:name).sort.join(" ") + contents << stanza.join("\n") + end + + # Build iface stanzas + providers.sort_by(&:name).each do |provider| + # TODO add validation method + raise Puppet::Error, "#{provider.name} does not have a method." if provider.method.nil? + raise Puppet::Error, "#{provider.name} does not have a family." if provider.family.nil? + + stanza = [] + stanza << %{iface #{provider.name} #{provider.family} #{provider.method}} + + [ + [:ipaddress, 'address'], + [:netmask, 'netmask'], + [:mtu, 'mtu'], + ].each do |(property, section)| + stanza << " #{section} #{provider.send property}" if provider.send(property) and provider.send(property) != :absent + end + + if provider.options and provider.options != :absent + provider.options.each_pair do |key, val| + if val.is_a? String + stanza << " #{key} #{val}" + elsif val.is_a? Array + val.each { |entry| stanza << " #{key} #{entry}" } + else + raise Puppet::Error, "#{self} options key #{key} expects a String or Array, got #{val.class}" + end + end + end + + contents << stanza.join("\n") + end + + contents.map {|line| line + "\n\n"}.join + end + + def self.header + str = <<-HEADER +# HEADER: This file is is being managed by puppet. Changes to +# HEADER: interfaces that are not being managed by puppet will persist; +# HEADER: however changes to interfaces that are being managed by puppet will +# HEADER: be overwritten. In addition, file order is NOT guaranteed. +# HEADER: Last generated at: #{Time.now} +HEADER + str + end +end diff --git a/packstack/puppet/modules/network/lib/puppet/provider/network_route/wrlinux.rb b/packstack/puppet/modules/network/lib/puppet/provider/network_route/wrlinux.rb new file mode 100644 index 0000000..d3fa7b5 --- /dev/null +++ b/packstack/puppet/modules/network/lib/puppet/provider/network_route/wrlinux.rb @@ -0,0 +1,109 @@ +require 'ipaddr' +require 'puppetx/filemapper' + +Puppet::Type.type(:network_route).provide(:wrlinux) do + # Wind River Linux network_route routes provider. + # + # This provider uses the filemapper mixin to map the routes file to a + # collection of network_route providers, and back. + # + include PuppetX::FileMapper + + desc "Wind River routes style provider" + + confine :osfamily => :wrlinux + + # $ dpkg -S /etc/network/if-up.d/20static-routes + # ifupdown-extra: /etc/network/if-up.d/20static-routes + confine :exists => '/etc/network/if-up.d/20static-routes' + + defaultfor :osfamily => :wrlinux + + has_feature :provider_options + + def select_file + '/etc/network/routes' + end + + def self.target_files + ['/etc/network/routes'] + end + + class MalformedRoutesError < Puppet::Error + def initialize(msg = nil) + msg = 'Malformed wrlinux routes file; cannot instantiate network_route resources' if msg.nil? + super + end + end + + def self.raise_malformed + @failed = true + raise MalformedRoutesError + end + + def self.parse_file(filename, contents) + # Build out an empty hash for new routes for storing their configs. + route_hash = Hash.new do |hash, key| + hash[key] = {} + hash[key][:name] = key + hash[key] + end + + lines = contents.split("\n") + lines.each do |line| + # Strip off any trailing comments + line.sub!(/#.*$/, '') + + if line =~ /^\s*#|^\s*$/ + # Ignore comments and blank lines + next + end + + route = line.split(' ', 5) + + if route.length < 4 + raise_malformed + end + + # use the CIDR version of the target as :name + cidr_target = "#{route[0]}/#{IPAddr.new(route[1]).to_i.to_s(2).count('1')}" + + route_hash[cidr_target][:network] = route[0] + route_hash[cidr_target][:netmask] = route[1] + route_hash[cidr_target][:gateway] = route[2] + route_hash[cidr_target][:interface] = route[3] + route_hash[cidr_target][:options] = route[4] if route[4] + end + + route_hash.values + end + + # Generate an array of sections + def self.format_file(filename, providers) + contents = [] + contents << header + + # Build routes + providers.sort_by(&:name).each do |provider| + raise Puppet::Error, "#{provider.name} is missing the required parameter 'network'." if provider.network.nil? + raise Puppet::Error, "#{provider.name} is missing the required parameter 'netmask'." if provider.netmask.nil? + raise Puppet::Error, "#{provider.name} is missing the required parameter 'gateway'." if provider.gateway.nil? + raise Puppet::Error, "#{provider.name} is missing the required parameter 'interface'." if provider.interface.nil? + + contents << "#{provider.network} #{provider.netmask} #{provider.gateway} #{provider.interface} #{provider.options}\n" + end + + contents.join + end + + def self.header + str = <<-HEADER +# HEADER: This file is is being managed by puppet. Changes to +# HEADER: routes that are not being managed by puppet will persist; +# HEADER: however changes to routes that are being managed by puppet will +# HEADER: be overwritten. In addition, file order is NOT guaranteed. +# HEADER: Last generated at: #{Time.now} +HEADER + str + end +end diff --git a/packstack/puppet/modules/network/lib/puppet/type/network_config.rb b/packstack/puppet/modules/network/lib/puppet/type/network_config.rb index a50a0df..1297ad7 100644 --- a/packstack/puppet/modules/network/lib/puppet/type/network_config.rb +++ b/packstack/puppet/modules/network/lib/puppet/type/network_config.rb @@ -95,6 +95,10 @@ Puppet::Type.newtype(:network_config) do defaultto :raw end + newproperty(:gateway) do + desc 'The IP address of the network router or gateway device (if any)' + 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. diff --git a/packstack/puppet/modules/network/manifests/bond.pp b/packstack/puppet/modules/network/manifests/bond.pp index d6d98ce..26ca104 100644 --- a/packstack/puppet/modules/network/manifests/bond.pp +++ b/packstack/puppet/modules/network/manifests/bond.pp @@ -188,6 +188,28 @@ define network::bond( require => Kmod::Alias[$name], } } + WRLinux: { + network::bond::wrlinux { $name: + slaves => $slaves, + ensure => $ensure, + ipaddress => $ipaddress, + netmask => $netmask, + method => $method, + family => $family, + onboot => $onboot, + + mode => $mode, + miimon => $miimon, + downdelay => $downdelay, + updelay => $updelay, + lacp_rate => $lacp_rate, + primary => $primary, + primary_reselect => $primary_reselect, + xmit_hash_policy => $xmit_hash_policy, + + require => Kmod::Alias[$name], + } + } RedHat: { network::bond::redhat { $name: ensure => $ensure, diff --git a/packstack/puppet/modules/network/manifests/bond/setup.pp b/packstack/puppet/modules/network/manifests/bond/setup.pp index abe1252..0a30767 100644 --- a/packstack/puppet/modules/network/manifests/bond/setup.pp +++ b/packstack/puppet/modules/network/manifests/bond/setup.pp @@ -10,5 +10,7 @@ class network::bond::setup { ensure => present, } } + WRLinux: { + } } } diff --git a/packstack/puppet/modules/network/manifests/bond/wrlinux.pp b/packstack/puppet/modules/network/manifests/bond/wrlinux.pp new file mode 100644 index 0000000..e240341 --- /dev/null +++ b/packstack/puppet/modules/network/manifests/bond/wrlinux.pp @@ -0,0 +1,56 @@ +# = Define: network::bond::wrlinux +# +# Instantiate bonded interfaces on Debian based systems. +# +# == See also +# +# * Debian Network Bonding http://wiki.wrlinux.org/Bonding +define network::bond::wrlinux( + $slaves, + $ensure = present, + $ipaddress = undef, + $netmask = undef, + $method = undef, + $family = undef, + $onboot = undef, + + $mode = undef, + $miimon = undef, + $downdelay = undef, + $updelay = undef, + $lacp_rate = undef, + $primary = undef, + $primary_reselect = undef, + $xmit_hash_policy = undef, +) { + + $raw = { + 'bond-slaves' => join($slaves, ' '), + 'bond-mode' => $mode, + 'bond-miimon' => $miimon, + 'bond-downdelay' => $downdelay, + 'bond-updelay' => $updelay, + 'bond-lacp-rate' => $lacp_rate, + 'bond-primary' => $primary, + 'bond-primary-reselect' => $primary_reselect, + 'bond-xmit-hash-policy' => $xmit_hash_policy, + } + + $opts = compact_hash($raw) + + network_config { $name: + ensure => $ensure, + ipaddress => $ipaddress, + netmask => $netmask, + family => $family, + method => $method, + onboot => $onboot, + options => $opts, + } + + network_config { $slaves: + ensure => absent, + reconfigure => true, + before => Network_config[$name], + } +} -- 1.8.3.1