1 From 8e14e2e258a8f2f7189ed37c6337c41fbff0362a Mon Sep 17 00:00:00 2001
2 From: Al Bailey <al.bailey@windriver.com>
3 Date: Mon, 6 Jun 2016 17:13:09 -0400
4 Subject: [PATCH] puppet-network Kilo quilt changes
7 .../lib/puppet/provider/network_config/redhat.rb | 39 ++-
8 .../lib/puppet/provider/network_config/wrlinux.rb | 296 +++++++++++++++++++++
9 .../lib/puppet/provider/network_route/wrlinux.rb | 109 ++++++++
10 .../network/lib/puppet/type/network_config.rb | 4 +
11 packstack/puppet/modules/network/manifests/bond.pp | 22 ++
12 .../puppet/modules/network/manifests/bond/setup.pp | 2 +
13 .../modules/network/manifests/bond/wrlinux.pp | 56 ++++
14 7 files changed, 521 insertions(+), 7 deletions(-)
15 create mode 100644 packstack/puppet/modules/network/lib/puppet/provider/network_config/wrlinux.rb
16 create mode 100644 packstack/puppet/modules/network/lib/puppet/provider/network_route/wrlinux.rb
17 create mode 100644 packstack/puppet/modules/network/manifests/bond/wrlinux.pp
19 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
20 index 4b6de7e..758f387 100644
21 --- a/packstack/puppet/modules/network/lib/puppet/provider/network_config/redhat.rb
22 +++ b/packstack/puppet/modules/network/lib/puppet/provider/network_config/redhat.rb
23 @@ -19,7 +19,12 @@ Puppet::Type.type(:network_config).provide(:redhat) do
24 has_feature :provider_options
26 # @return [String] The path to network-script directory on redhat systems
27 - SCRIPT_DIRECTORY = "/etc/sysconfig/network-scripts"
28 + # SCRIPT_DIRECTORY = "/etc/sysconfig/network-scripts"
29 + # WRS: Generate temporary copies. It will get compared to files under
30 + # /etc/sysconfig/network-scripts afterward. Only config that have changed
31 + # will get replaced. Don't let puppet directly manage them, else it will
32 + # trigger un-wanted networking actions (like up/down).
33 + SCRIPT_DIRECTORY = "/var/run/network-scripts.puppet"
35 # The valid vlan ID range is 0-4095; 4096 is out of range
36 VLAN_RANGE_REGEX = %r[\d{1,3}|40[0-9][0-5]]
37 @@ -35,6 +40,7 @@ Puppet::Type.type(:network_config).provide(:redhat) do
39 :hotplug => 'HOTPLUG',
41 + :gateway => 'GATEWAY',
44 # Map provider instances to files based on their name
45 @@ -60,8 +66,14 @@ Puppet::Type.type(:network_config).provide(:redhat) do
46 # RedhatProvider.target_files
47 # # => ['/etc/sysconfig/network-scripts/ifcfg-eth0', '/etc/sysconfig/network-scripts/ifcfg-eth1']
48 def self.target_files(script_dir = SCRIPT_DIRECTORY)
49 - entries = Dir.entries(script_dir).select {|entry| entry.match SCRIPT_REGEX}
50 - entries.map {|entry| File.join(SCRIPT_DIRECTORY, entry)}
52 + if Dir.exists?(SCRIPT_DIRECTORY)
53 + Dir.foreach(SCRIPT_DIRECTORY) do |item|
54 + next if not item.match SCRIPT_REGEX
61 # Convert a redhat network script into a hash
62 @@ -184,6 +196,8 @@ Puppet::Type.type(:network_config).provide(:redhat) do
65 def self.format_file(filename, providers)
66 + Dir.mkdir(SCRIPT_DIRECTORY) unless File.exists?(SCRIPT_DIRECTORY)
68 if providers.length == 0
70 elsif providers.length > 1
71 @@ -193,11 +207,11 @@ Puppet::Type.type(:network_config).provide(:redhat) do
72 provider = providers[0]
75 - # Map everything to a flat hash
76 - props = (provider.options || {})
77 + props = provider.options if provider.options && provider.options != :absent
79 + # Map everything to a flat hash
80 NAME_MAPPINGS.keys.each do |type_name|
81 - if (val = provider.send(type_name))
82 + if (val = provider.send(type_name)) && val != :absent
83 props[type_name] = val
86 @@ -214,11 +228,11 @@ Puppet::Type.type(:network_config).provide(:redhat) do
87 str << %{#{key}=#{val}\n}
90 + content.prepend(header)
94 def self.unmunge(props)
98 [:onboot, :hotplug].each do |bool_property|
99 @@ -245,6 +259,17 @@ Puppet::Type.type(:network_config).provide(:redhat) do
105 +# HEADER: This file is is being managed by puppet. Changes to
106 +# HEADER: interfaces that are not being managed by puppet will persist;
107 +# HEADER: however changes to interfaces that are being managed by puppet will
108 +# HEADER: be overwritten. In addition, file order is NOT guaranteed.
109 +# HEADER: Last generated at: #{Time.now}
114 def self.post_flush_hook(filename)
115 File.chmod(0644, filename)
117 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
119 index 0000000..44c645a
121 +++ b/packstack/puppet/modules/network/lib/puppet/provider/network_config/wrlinux.rb
123 +require 'puppetx/filemapper'
125 +Puppet::Type.type(:network_config).provide(:wrlinux) do
126 + # Wind River Linux network_config interfaces provider.
128 + # This provider uses the filemapper mixin to map the interfaces file to a
129 + # collection of network_config providers, and back.
131 + include PuppetX::FileMapper
133 + desc "Wind River interfaces style provider"
135 + confine :osfamily => :wrlinux
136 + defaultfor :osfamily => :wrlinux
138 + has_feature :provider_options
139 + has_feature :hotpluggable
142 + '/var/run/interfaces.puppet'
145 + def self.target_files
146 + ['/var/run/interfaces.puppet']
149 + class MalformedInterfacesError < Puppet::Error
150 + def initialize(msg = nil)
151 + msg = 'Malformed wrlinux interfaces file; cannot instantiate network_config resources' if msg.nil?
156 + def self.raise_malformed
158 + raise MalformedInterfacesError
166 + attr_accessor :onboot, :hotplug
169 + # These fields are going to get rearranged to resolve issue 16
170 + # https://github.com/adrienthebo/puppet-network/issues/16
171 + attr_accessor :ipaddress, :netmask, :family, :method, :mtu
174 + attr_reader :options
176 + def initialize(name)
179 + @options = Hash.new {|hash, key| hash[key] = []}
185 + :onboot => @onboot,
186 + :hotplug => @hotplug,
187 + :ipaddress => @ipaddress,
188 + :netmask => @netmask,
189 + :family => @family,
190 + :method => @method,
192 + :options => squeeze_options
195 + h.inject({}) do |hash, (key, val)|
196 + hash[key] = val unless val.nil?
201 + def squeeze_options
202 + @options.inject({}) do |hash, (key, value)|
204 + hash[key] = value.pop
219 + # @return [Array<Instance>] All class instances
226 + if all_instances[name]
227 + obj = all_instances[name]
229 + obj = self.new(name)
230 + all_instances[name] = obj
238 + def self.parse_file(filename, contents)
239 + # Debian has a very irregular format for the interfaces file. The
240 + # parse_file method is somewhat derived from the ifup executable
241 + # supplied in the debian ifupdown package. The source can be found at
242 + # http://packages.debian.org/squeeze/ifupdown
245 + # The debian interfaces implementation requires global state while parsing
246 + # the file; namely, the stanza being parsed as well as the interface being
249 + current_interface = nil
251 + lines = contents.split("\n")
252 + # TODO Join lines that end with a backslash
254 + # Iterate over all lines and determine what attributes they create
255 + lines.each do |line|
257 + # Strip off any trailing comments
258 + line.sub!(/#.*$/, '')
262 + # Ignore comments and blank lines
265 + when /^auto|^allow-auto/
266 + # Parse out any auto sections
267 + interfaces = line.split(' ')
268 + interfaces.delete_at(0)
270 + interfaces.each do |name|
271 + Instance[name].onboot = true
274 + # Reset the current parse state
275 + current_interface = nil
277 + when /^allow-hotplug/
278 + # parse out allow-hotplug lines
280 + interfaces = line.split(' ')
281 + interfaces.delete_at(0)
283 + interfaces.each do |name|
284 + Instance[name].hotplug = true
287 + # Don't reset Reset the current parse state
290 + # Format of the iface line:
292 + # iface <iface> <family> <method>
293 + # zero or more options for <iface>
295 + if match = line.match(/^iface\s+(\S+)\s+(\S+)\s+(\S+)/)
300 + # If an iface block for this interface has been seen, the file is
302 + raise_malformed if Instance[name] and Instance[name].family
305 + current_interface = name
307 + # This is done automatically
308 + #Instance[name].name = name
309 + Instance[name].family = family
310 + Instance[name].method = method
313 + # If we match on a string with a leading iface, but it isn't in the
314 + # expected format, malformed blar blar
321 + raise Puppet::DevError, "Debian interfaces mapping parsing not implemented."
325 + # We're currently examining a line that is within a mapping or iface
326 + # stanza, so we need to validate the line and add the options it
327 + # specifies to the known state of the interface.
331 + if match = line.match(/(\S+)\s+(\S.*)/)
332 + # If we're parsing an iface stanza, then we should receive a set of
333 + # lines that contain two or more space delimited strings. Append
334 + # them as options to the iface in an array.
339 + name = current_interface
342 + when 'address'; Instance[name].ipaddress = val
343 + when 'netmask'; Instance[name].netmask = val
344 + when 'mtu'; Instance[name].mtu = val
345 + else Instance[name].options[key] << val
351 + raise Puppet::DevError, "Debian interfaces mapping parsing not implemented."
358 + Instance.all_instances.map {|name, instance| instance.to_hash }
361 + # Generate an array of sections
362 + def self.format_file(filename, providers)
366 + # Add onboot interfaces
367 + if (auto_interfaces = providers.select {|provider| provider.onboot == true })
369 + stanza << "auto " + auto_interfaces.map(&:name).sort.join(" ")
370 + contents << stanza.join("\n")
373 + # Build iface stanzas
374 + providers.sort_by(&:name).each do |provider|
375 + # TODO add validation method
376 + raise Puppet::Error, "#{provider.name} does not have a method." if provider.method.nil?
377 + raise Puppet::Error, "#{provider.name} does not have a family." if provider.family.nil?
380 + stanza << %{iface #{provider.name} #{provider.family} #{provider.method}}
383 + [:ipaddress, 'address'],
384 + [:netmask, 'netmask'],
386 + ].each do |(property, section)|
387 + stanza << " #{section} #{provider.send property}" if provider.send(property) and provider.send(property) != :absent
390 + if provider.options and provider.options != :absent
391 + provider.options.each_pair do |key, val|
392 + if val.is_a? String
393 + stanza << " #{key} #{val}"
394 + elsif val.is_a? Array
395 + val.each { |entry| stanza << " #{key} #{entry}" }
397 + raise Puppet::Error, "#{self} options key #{key} expects a String or Array, got #{val.class}"
402 + contents << stanza.join("\n")
405 + contents.map {|line| line + "\n\n"}.join
410 +# HEADER: This file is is being managed by puppet. Changes to
411 +# HEADER: interfaces that are not being managed by puppet will persist;
412 +# HEADER: however changes to interfaces that are being managed by puppet will
413 +# HEADER: be overwritten. In addition, file order is NOT guaranteed.
414 +# HEADER: Last generated at: #{Time.now}
419 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
421 index 0000000..d3fa7b5
423 +++ b/packstack/puppet/modules/network/lib/puppet/provider/network_route/wrlinux.rb
426 +require 'puppetx/filemapper'
428 +Puppet::Type.type(:network_route).provide(:wrlinux) do
429 + # Wind River Linux network_route routes provider.
431 + # This provider uses the filemapper mixin to map the routes file to a
432 + # collection of network_route providers, and back.
434 + include PuppetX::FileMapper
436 + desc "Wind River routes style provider"
438 + confine :osfamily => :wrlinux
440 + # $ dpkg -S /etc/network/if-up.d/20static-routes
441 + # ifupdown-extra: /etc/network/if-up.d/20static-routes
442 + confine :exists => '/etc/network/if-up.d/20static-routes'
444 + defaultfor :osfamily => :wrlinux
446 + has_feature :provider_options
449 + '/etc/network/routes'
452 + def self.target_files
453 + ['/etc/network/routes']
456 + class MalformedRoutesError < Puppet::Error
457 + def initialize(msg = nil)
458 + msg = 'Malformed wrlinux routes file; cannot instantiate network_route resources' if msg.nil?
463 + def self.raise_malformed
465 + raise MalformedRoutesError
468 + def self.parse_file(filename, contents)
469 + # Build out an empty hash for new routes for storing their configs.
470 + route_hash = Hash.new do |hash, key|
472 + hash[key][:name] = key
476 + lines = contents.split("\n")
477 + lines.each do |line|
478 + # Strip off any trailing comments
479 + line.sub!(/#.*$/, '')
481 + if line =~ /^\s*#|^\s*$/
482 + # Ignore comments and blank lines
486 + route = line.split(' ', 5)
488 + if route.length < 4
492 + # use the CIDR version of the target as :name
493 + cidr_target = "#{route[0]}/#{IPAddr.new(route[1]).to_i.to_s(2).count('1')}"
495 + route_hash[cidr_target][:network] = route[0]
496 + route_hash[cidr_target][:netmask] = route[1]
497 + route_hash[cidr_target][:gateway] = route[2]
498 + route_hash[cidr_target][:interface] = route[3]
499 + route_hash[cidr_target][:options] = route[4] if route[4]
505 + # Generate an array of sections
506 + def self.format_file(filename, providers)
511 + providers.sort_by(&:name).each do |provider|
512 + raise Puppet::Error, "#{provider.name} is missing the required parameter 'network'." if provider.network.nil?
513 + raise Puppet::Error, "#{provider.name} is missing the required parameter 'netmask'." if provider.netmask.nil?
514 + raise Puppet::Error, "#{provider.name} is missing the required parameter 'gateway'." if provider.gateway.nil?
515 + raise Puppet::Error, "#{provider.name} is missing the required parameter 'interface'." if provider.interface.nil?
517 + contents << "#{provider.network} #{provider.netmask} #{provider.gateway} #{provider.interface} #{provider.options}\n"
525 +# HEADER: This file is is being managed by puppet. Changes to
526 +# HEADER: routes that are not being managed by puppet will persist;
527 +# HEADER: however changes to routes that are being managed by puppet will
528 +# HEADER: be overwritten. In addition, file order is NOT guaranteed.
529 +# HEADER: Last generated at: #{Time.now}
534 diff --git a/packstack/puppet/modules/network/lib/puppet/type/network_config.rb b/packstack/puppet/modules/network/lib/puppet/type/network_config.rb
535 index a50a0df..1297ad7 100644
536 --- a/packstack/puppet/modules/network/lib/puppet/type/network_config.rb
537 +++ b/packstack/puppet/modules/network/lib/puppet/type/network_config.rb
538 @@ -95,6 +95,10 @@ Puppet::Type.newtype(:network_config) do
542 + newproperty(:gateway) do
543 + desc 'The IP address of the network router or gateway device (if any)'
546 # `:options` provides an arbitrary passthrough for provider properties, so
547 # that provider specific behavior doesn't clutter up the main type but still
548 # allows for more powerful actions to be taken.
549 diff --git a/packstack/puppet/modules/network/manifests/bond.pp b/packstack/puppet/modules/network/manifests/bond.pp
550 index d6d98ce..26ca104 100644
551 --- a/packstack/puppet/modules/network/manifests/bond.pp
552 +++ b/packstack/puppet/modules/network/manifests/bond.pp
553 @@ -188,6 +188,28 @@ define network::bond(
554 require => Kmod::Alias[$name],
558 + network::bond::wrlinux { $name:
561 + ipaddress => $ipaddress,
562 + netmask => $netmask,
569 + downdelay => $downdelay,
570 + updelay => $updelay,
571 + lacp_rate => $lacp_rate,
572 + primary => $primary,
573 + primary_reselect => $primary_reselect,
574 + xmit_hash_policy => $xmit_hash_policy,
576 + require => Kmod::Alias[$name],
580 network::bond::redhat { $name:
582 diff --git a/packstack/puppet/modules/network/manifests/bond/setup.pp b/packstack/puppet/modules/network/manifests/bond/setup.pp
583 index abe1252..0a30767 100644
584 --- a/packstack/puppet/modules/network/manifests/bond/setup.pp
585 +++ b/packstack/puppet/modules/network/manifests/bond/setup.pp
586 @@ -10,5 +10,7 @@ class network::bond::setup {
594 diff --git a/packstack/puppet/modules/network/manifests/bond/wrlinux.pp b/packstack/puppet/modules/network/manifests/bond/wrlinux.pp
596 index 0000000..e240341
598 +++ b/packstack/puppet/modules/network/manifests/bond/wrlinux.pp
600 +# = Define: network::bond::wrlinux
602 +# Instantiate bonded interfaces on Debian based systems.
606 +# * Debian Network Bonding http://wiki.wrlinux.org/Bonding
607 +define network::bond::wrlinux(
610 + $ipaddress = undef,
618 + $downdelay = undef,
620 + $lacp_rate = undef,
622 + $primary_reselect = undef,
623 + $xmit_hash_policy = undef,
627 + 'bond-slaves' => join($slaves, ' '),
628 + 'bond-mode' => $mode,
629 + 'bond-miimon' => $miimon,
630 + 'bond-downdelay' => $downdelay,
631 + 'bond-updelay' => $updelay,
632 + 'bond-lacp-rate' => $lacp_rate,
633 + 'bond-primary' => $primary,
634 + 'bond-primary-reselect' => $primary_reselect,
635 + 'bond-xmit-hash-policy' => $xmit_hash_policy,
638 + $opts = compact_hash($raw)
640 + network_config { $name:
642 + ipaddress => $ipaddress,
643 + netmask => $netmask,
650 + network_config { $slaves:
652 + reconfigure => true,
653 + before => Network_config[$name],