--- /dev/null
+From 8e14e2e258a8f2f7189ed37c6337c41fbff0362a Mon Sep 17 00:00:00 2001
+From: Al Bailey <al.bailey@windriver.com>
+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<Instance>] 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 <iface> <family> <method>
++ # zero or more options for <iface>
++
++ 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
+