├── .gitignore
├── COPYING
├── README.markdown
├── files
└── network-restart.rb
├── lib
└── puppet
│ ├── provider
│ ├── network_config
│ │ ├── interfaces.rb
│ │ └── network_scripts.rb
│ ├── network_interface
│ │ └── ip.rb
│ └── network_route
│ │ ├── interfaces.rb
│ │ └── network_scripts.rb
│ └── type
│ ├── network_config.rb
│ ├── network_interface.rb
│ └── network_route.rb
├── manifests
└── init.pp.example
└── spec
└── unit
└── puppet
└── provider
├── network_config
└── network_scripts.rb
└── network_interface
└── ip.rb
/.gitignore:
--------------------------------------------------------------------------------
1 | *.swp
2 | .*.swp
3 |
--------------------------------------------------------------------------------
/COPYING:
--------------------------------------------------------------------------------
1 | Copyright 2011 William Van Hevelingen, Camille Meulien, Elie Bleton
2 |
3 | Licensed under the Apache License, Version 2.0 (the "License");
4 | you may not use this file except in compliance with the License.
5 | You may obtain a copy of the License at
6 |
7 | http://www.apache.org/licenses/LICENSE-2.0
8 |
9 | Unless required by applicable law or agreed to in writing, software
10 | distributed under the License is distributed on an "AS IS" BASIS,
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | See the License for the specific language governing permissions and
13 | limitations under the License.
14 |
--------------------------------------------------------------------------------
/README.markdown:
--------------------------------------------------------------------------------
1 | # puppet-network
2 |
3 | Network management for puppet
4 |
5 | ## Deprecation Warning
6 |
7 | This module is no longer being maintained or updated.
8 |
9 | It has been supercededd by Adrien Thebo's module which can be found here:
10 |
11 | http://forge.puppetlabs.com/adrien/network
12 |
13 | ## Overview
14 |
15 | This module provides types for network management :
16 |
17 | * Device configuration files using the `network_config` type
18 | * Live network management using the `network_interface` type
19 |
20 | Note: `network_interface` and `network_config` types are not dependant on each other in any way. `network_interface` is experimental.
21 |
22 | **Word of warning** : if you choose to go for automatic network reconfiguration and you inject a mistake in your configuration, you probably willl loose network connectivity on the configured system.
23 |
24 | Ensure that you have a fallback ready before trying puppet-network, like physical access, a remote KVM, or similar devices so that you can restore connectivity in the event of configuration errors.
25 |
26 | ## The 'network_config' type
27 |
28 | The `network_config` type is used to maintain persistent network configuration.
29 | Only redhat-derivatives (RHEL,Fedora,CentOS) are currently supported.
30 |
31 | ### Important notes
32 |
33 | #### 'Exclusive' mode by default
34 |
35 | `puppet-network` will remove any device that is not configured through puppet-network.
36 | This may look harsh to some, but the alternative yields greater problems (read below).
37 |
38 | If you want `puppet-network` to leave your existing ifcfg files be, set `exclusive => false` in any of the existing network_config resources.
39 |
40 | In non-exclusive mode, you get the freedom to handle ifcfg files the way you prefer. Be aware though, that *leaving behind unwanted devices can have very adverse effects* (broadcast issues, non-functionning bridges, defective bonding etc..) that won't be solved by rebooting the machine, probably requiring manual intervention to restore connectivity.
41 |
42 | #### 'service network restart' issues
43 |
44 | Phasing out a configuration is dangerous. `service network restart` will only shut down devices configured that are configured (ie with a matching file in `/etc/sysconfig/network-scripts`).
45 |
46 | This can yield to problematic roll-outs, such as removing bridge devices. This would leave behind live bridge configuration, preventing regular use of the formerly bridged interfaces.
47 |
48 | **Workarounds**:
49 |
50 | * use `network-restart.rb` script that comes with puppet-network. this will `service network stop` then proceed to remove anything left that looks like network-configuration, then run `service network start`. Please review code first, be `--sure`, and send feedback at heliostech if you encounter issues.
51 | * use `brctl`/`ifenslave`/`ip` etc manually (ie. roll your own 'network-restart.xx')
52 | * use puppet in offline mode, trigger a `service network stop` before applying configuration changes (puppet code left as an exercise ..), apply changes, then do `service network start`. (*not tested*)
53 | * send patches for network_interface puppet type that can do the `brctl` (and ifenslave etc..) lifting.
54 | * worst case scenario, reset your computer using any appropriate way
55 |
56 | ### Samples
57 |
58 | #### Static configuration
59 |
60 | network_config { "eth0":
61 | bootproto => "none",
62 | onboot => "yes",
63 | netmask => "255.255.255.0",
64 | broadcast => "192.168.56.255",
65 | ipaddr => "192.168.56.101",
66 | userctl => "no",
67 | hwaddr => "08:00:27:34:05:15",
68 | domain => "example.domain.com",
69 | nozeroconf => "yes",
70 | }
71 |
72 |
73 | You could also use `prefix => 24` instead of the `broadcast` parameter.
74 |
75 | #### DHCP
76 |
77 | network_config { "eth0":
78 | bootproto => "dhcp",
79 | onboot => "yes",
80 | }
81 |
82 |
83 | #### VLAN
84 |
85 | network_config { "eth0.2":
86 | vlan => "yes",
87 | }
88 |
89 |
90 | #### Bridges
91 |
92 | network_config { "eth0":
93 | bridge => "br0"
94 | }
95 |
96 | network_config { "br1":
97 | type => "Bridge",
98 | bootproto => "dhcp",
99 | stp => "on",
100 | }
101 |
102 |
103 | #### Bonding
104 |
105 | network_config { "bond0":
106 | type => "Bonding",
107 | bonding_module_opts => "mode=balance-rr miimon=100",
108 | }
109 |
110 | network_config { "eth0": master => "bond0", slave => "yes" }
111 | network_config { "eth2": master => "bond0", slave => "yes" }
112 | network_config { "eth3": master => "bond0", slave => "yes" }
113 |
114 |
115 | See [kernel documentation for bonding](http://www.kernel.org/doc/Documentation/networking/bonding.txt) for more information.
116 |
117 | ## The 'network_interface' type
118 |
119 | The `network_interface` maintains live state of the interface using the `ip` tool, likewise :
120 |
121 |
122 | network_interface { "eth0":
123 | state => "up",
124 | mtu => "1000",
125 | qlen => "1500",
126 | address => "aa:bb:cc:dd:ee:ff",
127 | broadcast => "ff:ff:ff:ff:ff:ff",
128 | }
129 |
130 |
131 | Source code
132 | -----------
133 |
134 | The source code for this module is available online at
135 | http://github.com/heliostech/puppet-network.git
136 |
137 | You can checkout the source code by installing the `git` distributed version
138 | control system and running:
139 |
140 | git clone git://github.com/heliostech/puppet-network.git
141 |
142 | Authors
143 | -------
144 |
145 | * William Van Hevelingen
146 | * Elie Bleton
147 | * Camille Meulien
--------------------------------------------------------------------------------
/files/network-restart.rb:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | ### network-restart.rb : thorough redhat network restarter
4 | ###
5 | ### This attempts to remove what's left behind by `service network stop`,
6 | ### which happens when you remove device configuration files.
7 | ###
8 | ### Camille Meulien
9 | ### Elie Bleton
10 |
11 | # Copyright 2011 Helios Technologies SARL
12 | #
13 | # Licensed under the Apache License, Version 2.0 (the "License");
14 | # you may not use this file except in compliance with the License.
15 | # You may obtain a copy of the License at
16 | #
17 | # http://www.apache.org/licenses/LICENSE-2.0
18 | #
19 | # Unless required by applicable law or agreed to in writing, software
20 | # distributed under the License is distributed on an "AS IS" BASIS,
21 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22 | # See the License for the specific language governing permissions and
23 | # limitations under the License.
24 |
25 | fail "usage: #{__FILE__} --sure" if ARGV.first != "--sure"
26 |
27 | def ifaces
28 | `ifconfig | grep -E '^[^ ]' | cut -d' ' -f1`.split.map { |d| d.chomp }
29 | end
30 |
31 | @cmds, @bridges, @bridge = [], {}, nil
32 |
33 | # Try to stop properly :)
34 | @cmds << "service network stop"
35 |
36 | # Remove interfaces
37 | ifaces.each { |iface| @cmds << "ip link set #{iface} down" }
38 |
39 | # Remove bridges
40 | `brctl show`.split("\n")[1..-1].each do |line|
41 | if line =~ /^([^\t]+)\t+[^\t]+\t+[^\t]+\t+([^\t]+)$/
42 | @bridge, iface = $1, $2
43 | @bridges[@bridge] = [ iface ]
44 | elsif line =~ /^ +([^ ]+)$/
45 | @bridges[@bridge] << (iface = $1)
46 | else
47 | STDERR.puts "brctl parse error:" + line
48 | end
49 | end
50 |
51 | @bridges.each_pair do |bridge, iflist|
52 | iflist.each { |iface| @cmds << "brctl delif #{bridge} #{iface}" }
53 | @cmds << "brctl delbr #{bridge}"
54 | end
55 |
56 | # Remove DHCP client
57 | daemons = ["dhclient", "udhcpcd", "dhcpcd"]
58 | daemons.each { |p| @cmds << "killall -KILL #{p}" if `ps x`.include?(p) }
59 |
60 | # Delete all links
61 | ifaces.each { |iface| @cmds << "ip link delete #{iface}" }
62 |
63 | # Remove bonding module
64 | @cmds << "rmmod bonding" if `lsmod | egrep '^bonding'`.chomp.empty?
65 |
66 | # Guess what ??
67 | @cmds << "service network restart"
68 |
69 | # Exec
70 | @cmds.each do |cmd|
71 | puts cmd
72 | system cmd
73 | end
74 |
--------------------------------------------------------------------------------
/lib/puppet/provider/network_config/interfaces.rb:
--------------------------------------------------------------------------------
1 | # This provider is in development and not ready for production
2 |
3 | Puppet::Type.type(:network_config).provide(:interfaces) do
4 |
5 | defaultfor :operatingsystem => [:ubuntu, :debian]
6 | # ... change this for interfaces
7 | # iface eth0-@map_value
8 | # key value
9 | # address 192.168.1.1
10 | # netmask 255.255.255.0
11 | #
12 | # lines beginning with the work "auto" ~ onboot => yes
13 | # record_line :parsed, :fields => %w{address netmask gateway broadcast family method},
14 | end
15 |
--------------------------------------------------------------------------------
/lib/puppet/provider/network_config/network_scripts.rb:
--------------------------------------------------------------------------------
1 | Puppet::Type.type(:network_config).provide(:network_scripts) do
2 | desc "Provider for configuration of network_scripts"
3 |
4 | defaultfor :operatingsystem => [:redhat, :fedora, :centos]
5 |
6 | @@exclusive = nil
7 | @@configured_devices = []
8 | @@config_dir = "/etc/sysconfig/network-scripts/"
9 | @@instance_count = 0
10 | @@total_resource_count = 0
11 |
12 | # checks for network-script existence and correctness
13 | def exists?
14 | @@instance_count += 1
15 | @config_file = "#{@@config_dir}ifcfg-#{@resource[:name]}"
16 |
17 | # do not check file contents if the purpose is to ensure the file isn't there
18 | return File.exists?(@config_file) if @resource[:ensure] == :absent
19 |
20 | # load puppet configuration (`should`)
21 | @memory_values = {}
22 |
23 | # Get any property set in the resource that isn't a metaparameter
24 | mp = Puppet::Type::metaparams << :ensure << :provider << :exclusive
25 | @resource.to_hash.delete_if { |k,v| mp.include? k }.each_pair { |k, v|
26 | @memory_values[k.to_s.upcase.to_sym] = v.to_s unless v.nil?
27 | }
28 |
29 | # handle the special hack with :exclusive
30 | @@exclusive = @resource.to_hash[:exclusive] if @@exclusive.nil?
31 |
32 | # load on-disk configuration (`is`)
33 | @disk_values = load_disk_config()
34 |
35 | # if this is the last file to be checked, trigger exclusivity enforcement
36 | enforce_exclusivity if (@@instance_count == @@total_resource_count)
37 |
38 | return @disk_values == @memory_values
39 | end
40 |
41 | def create
42 | File.open(@config_file.to_s, 'w') do |f|
43 | f.write("# Generated by puppet-network on #{Time.now.strftime("%F %T")}\n")
44 | @memory_values.each_pair do |k, v|
45 | # only quote values that include space or equal characters
46 | quote_value = v.include?(' ') or v.include?('=')
47 | vs = v.nil? ? k : quote_value ? "#{k}=\"#{v}\"" : "#{k}=#{v}"
48 | f.write("#{vs}\n")
49 | end
50 | end
51 | end
52 |
53 | def destroy
54 | if File.exists?(@config_file)
55 | Puppet.notice "Destroying #{@config_file}"
56 | File.unlink(@config_file)
57 | end
58 | end
59 |
60 | # Reads the content in the config file and returns a hash of keys & values
61 | def load_disk_config
62 | return nil unless File.exists?(@config_file)
63 |
64 | config_hash = {}
65 |
66 | File.readlines(@config_file).each do |line|
67 | next unless line =~ /^\s*([A-Za-z][^=]+)="?([^"]+)"?$/
68 | config_hash[$1.strip.upcase.to_sym] = $2.strip
69 | end
70 |
71 | Puppet.debug "Loaded file: #{@config_file}"
72 | return config_hash
73 | end
74 |
75 | #
76 | # `exclusive` related code
77 | #
78 |
79 | def initialize(args)
80 | super(args)
81 | @@configured_devices << "ifcfg-#{@resource[:device]}"
82 | @@total_resource_count += 1
83 | end
84 |
85 | def clear
86 | @@configured_devices = []
87 | @@instance_count = 0
88 | @@total_resource_count = 0
89 | @@exclusive = nil
90 | end
91 |
92 | # gets called once every network_config resource has been declared.
93 | def enforce_exclusivity
94 | unless @@exclusive == :false
95 | existing = Dir["#{@@config_dir}ifcfg-*"].map { |f| File.basename(f) }
96 | (existing - @@configured_devices).each do |parasite_file|
97 | Puppet.notice "puppet-network exclusive: removing #{parasite_file}"
98 | File.delete("#{@@config_dir}#{parasite_file}")
99 | end
100 | end
101 | clear()
102 | end
103 | end
104 |
--------------------------------------------------------------------------------
/lib/puppet/provider/network_interface/ip.rb:
--------------------------------------------------------------------------------
1 | Puppet::Type.type(:network_interface).provide(:ip) do
2 |
3 | # ip command is preferred over ifconfig
4 | commands :ip => "/sbin/ip", :vconfig => "/sbin/vconfig"
5 |
6 | # Uses the ip command to determine if the device exists
7 | def exists?
8 | # ip('link', 'list', @resource[:name])
9 | ip('addr', 'show', 'label', @resource[:device]).include?("inet")
10 | rescue Puppet::ExecutionFailure
11 | return false
12 | # raise Puppet::Error, "Network interface %s does not exist" % @resource[:name]
13 | end
14 |
15 | def create
16 | if @resource[:vlan] == :yes && ! ip('link', 'list').include?(@resource[:name].split(':').first)
17 | # Create vlan device
18 | vconfig('add', @resource[:device].split('.').first, @resource[:device].split('.').last)
19 | end
20 | unless self.netmask == @resource.should(:netmask) || self.broadcast == @resource.should(:broadcast) || self.ipaddr == @resource.should(:ipaddr)
21 | ip_addr_flush
22 | ip_addr_add
23 | end
24 | unless self.state == @resource.should(:state)
25 | self.state=(@resource.should(:state))
26 | end
27 | end
28 |
29 | def destroy
30 | ip_addr_flush
31 | if @resource[:vlan] == :yes
32 | # Test if no ip addresses are configured on this vlan device
33 | if ! ip('addr', 'show', @resource[:device].split(':').first).include?("inet")
34 | # Destroy vlan device
35 | vconfig('rem', @resource[:device].split(':').first)
36 | end
37 | end
38 | end
39 |
40 |
41 | # NETMASK
42 | def netmask
43 | lines = ip('addr', 'show', 'label', @resource[:device])
44 | lines.scan(/\s*inet (\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b)\/(\d+) b?r?d?\s*(\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b)?\s*scope (\w+) (\w+:?\d*)/)
45 | $2.nil? ? :absent : $2
46 | end
47 |
48 | def netmask=(value)
49 | ip_addr_flush
50 | ip_addr_add
51 | end
52 |
53 | # BROADCAST
54 | def broadcast
55 | lines = ip('addr', 'show', 'label', @resource[:device])
56 | lines.scan(/\s*inet (\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b)\/(\d+) b?r?d?\s*(\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b)?\s*scope (\w+) (\w+:?\d*)/)
57 | $3.nil? ? :absent : $3
58 | end
59 |
60 | def broadcast=(value)
61 | ip_addr_flush
62 | ip_addr_add
63 | end
64 |
65 | # IPADDR
66 | def ipaddr
67 | lines = ip('addr', 'show', 'label', @resource[:device])
68 | lines.scan(/\s*inet (\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b)\/(\d+) b?r?d?\s*(\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b)?\s*scope (\w+) (\w+:?\d*)/)
69 | $1.nil? ? :absent : $1
70 | end
71 |
72 | def ipaddr=(value)
73 | ip_addr_flush
74 | ip_addr_add
75 | end
76 |
77 |
78 | def ip_addr_flush
79 | ip('addr', 'flush', 'dev', @resource[:device], 'label', @resource[:device].sub(/:/, '\:'))
80 | end
81 |
82 | def ip_addr_add
83 | ip('addr', 'add', @resource[:ipaddr] + "/" + @resource[:netmask], 'broadcast', @resource[:broadcast], 'label', @resource[:device], 'dev', @resource[:device])
84 | end
85 |
86 | def device
87 | config_values[:dev]
88 | end
89 |
90 | # Ensurable/ensure adds unnecessary complexity to this provider
91 | # Network interfaces are up or down, present/absent are unnecessary
92 | def state
93 | lines = ip('link', 'list', @resource[:name])
94 | if lines.include?("UP")
95 | return "up"
96 | else
97 | return "down"
98 | end
99 | end
100 |
101 | # Set the interface's state
102 | # FIXME Facter bug #2211 prevents puppet from bringing up network devices
103 | def state=(value)
104 | ip('link', 'set', @resource[:name], value)
105 | end
106 |
107 | # Current state of the device via the ip command
108 | def state_values
109 | @values ||= read_ip_output
110 | end
111 |
112 | # Return the ip output of the device
113 | def ip_output
114 | ip('addr','show', 'dev', @resource[:name])
115 | end
116 |
117 | # FIXME Back Named Reference Captures are supported in Ruby 1.9.x
118 | def read_ip_output
119 | output = ip_output
120 | lines = output.split("\n")
121 | line1 = lines.shift
122 | line2 = lines.shift
123 | i=0
124 | j=0
125 | p=0
126 |
127 | # Append ipv6 lines into one string
128 | lines.each do |line|
129 | if line.include?("inet6")
130 | lines[p] = lines[p] + lines[p+1]
131 | lines.delete_at(p+1)
132 | else
133 | # move along, nothing to see here
134 | end
135 | p += 1
136 | end
137 |
138 | #FIXME This should capture 'NOARP' and 'MULTICAST'
139 | # Scan the first line of the ip command output
140 | line1.scan(/\d: (\w+): <(\w+),(\w+),(\w+),?(\w*)> mtu (\d+) qdisc (\w+) state (\w+)\s*\w* (\d+)*/)
141 | values = {
142 | "device" => $1,
143 | "mtu" => $6,
144 | "qdisc" => $7,
145 | "state" => $8,
146 | "qlen" => $9,
147 | }
148 |
149 | # Scan the second line of the ip command output
150 | line2.scan(/\s*link\/\w+ ((?:[0-9a-f]{2}[:-]){5}[0-9a-f]{2}) brd ((?:[0-9a-f]{2}[:-]){5}[0-9a-f]{2})/)
151 | values["address"] = $1
152 | values["broadcast"] = $2
153 |
154 | # Scan all the inet and inet6 entries
155 | lines.each do |line|
156 | if line.include?("inet6")
157 | line.scan(/\s*inet6 ((?>[0-9,a-f,A-F]*\:{1,2})+[0-9,a-f,A-F]{0,4})\/\w+ scope (\w+)\s*\w*\s*valid_lft (\w+) preferred_lft (\w+)/)
158 | values["inet6_#{j}"] = {
159 | "ip" => $1,
160 | "scope" => $2,
161 | "valid_lft" => $3,
162 | "preferred_lft" => $4,
163 | }
164 | j += 1
165 | else
166 | line.scan(/\s*inet (\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b)\/\d+ b?r?d?\s*(\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b)?\s*scope (\w+) (\w+:?\d*)/)
167 | values["inet_#{i}"] = {
168 | "ip" => $1,
169 | "brd" => $2,
170 | "scope" => $3,
171 | "dev" => $4,
172 | }
173 | i += 1
174 | end
175 | end
176 |
177 | return values
178 |
179 | end
180 |
181 | #FIXME Need to support multiple inet & inet6 hashes
182 | IP_ARGS = [ "qlen", "mtu", "address" ]
183 |
184 | IP_ARGS.each do |ip_arg|
185 | define_method(ip_arg.to_s.downcase) do
186 | state_values[ip_arg]
187 | end
188 |
189 | define_method("#{ip_arg}=".downcase) do |value|
190 | ip('link', 'set', "#{ip_arg}", value, 'dev', @resource[:name])
191 | state_values[ip_arg] = value
192 | end
193 | end
194 |
195 | end
196 |
--------------------------------------------------------------------------------
/lib/puppet/provider/network_route/interfaces.rb:
--------------------------------------------------------------------------------
1 | # This provider is in development and not ready for production
2 |
3 | Puppet::Type.type(:network_route).provide(:interfaces) do
4 |
5 | defaultfor :operatingsystem => [:ubuntu, :debian]
6 | # There are several ways to implement this
7 | # We can add a post-up/pre-down rules that call ip route
8 | # or we can scripts in /etc/network/if-up.d /etc/network/if-down.d
9 | end
10 |
--------------------------------------------------------------------------------
/lib/puppet/provider/network_route/network_scripts.rb:
--------------------------------------------------------------------------------
1 | Puppet::Type.type(:network_route).provide(:network_scripts) do
2 | desc "Provider for configuration of network_route"
3 |
4 | defaultfor :operatingsystem => [:redhat, :fedora, :centos]
5 |
6 | @@exclusive_routes= nil
7 | @@configured_routes = []
8 | @@config_dir_routes = "/etc/sysconfig/network-scripts/"
9 | @@instance_routes_count = 0
10 | @@total_resource_routes_count = 0
11 |
12 | # checks for network-script existence and correctness
13 | def exists?
14 | @@instance_routes_count += 1
15 | @config_file = "#{@@config_dir}route-#{@resource[:name]}"
16 |
17 | # do not check file contents if the purpose is to ensure the file isn't there
18 | return File.exists?(@config_file) if @resource[:ensure] == :absent
19 |
20 | # load puppet configuration (`should`)
21 | @memory_values = []
22 |
23 | for route in @resource[:routes]
24 | @memory_values.push(route.strip)
25 | end
26 |
27 | # handle the special hack with :exclusive
28 | @@exclusive_routes = @resource.to_hash[:exclusive] if @@exclusive_routes.nil?
29 |
30 | # load on-disk configuration (`is`)
31 | @disk_values = load_disk_config()
32 |
33 | # if this is the last file to be checked, trigger exclusivity enforcement
34 | enforce_exclusivity if (@@instance_routes_count == @@total_resource_routes_count)
35 |
36 | return @disk_values == @memory_values
37 | end
38 |
39 | def create
40 | File.open(@config_file.to_s, 'w') do |f|
41 | f.write("# Generated by puppet-network on #{Time.now.strftime("%F %T")}\n")
42 | for line in @memory_values
43 | f.write("#{line.to_s.strip}\n")
44 | end
45 | end
46 | end
47 |
48 | def destroy
49 | if File.exists?(@config_file)
50 | Puppet.notice "Destroying #{@config_file}"
51 | File.unlink(@config_file)
52 | end
53 | end
54 |
55 | # Reads the content in the config file and returns a hash of keys & values
56 | def load_disk_config
57 | return nil unless File.exists?(@config_file)
58 |
59 | config_array = []
60 |
61 | File.readlines(@config_file).each do |line|
62 | next if line =~ /^#.*$/
63 | config_array.push(line.to_s.strip)
64 | end
65 |
66 | Puppet.debug "Loaded file: #{@config_file}"
67 | return config_array
68 | end
69 |
70 | #
71 | # `exclusive` related code
72 | #
73 |
74 | def initialize(args)
75 | super(args)
76 | @@configured_routes << "route-#{@resource[:device]}"
77 | @@total_resource_routes_count += 1
78 | end
79 |
80 | def clear
81 | @@configured_routes = []
82 | @@instance_routes_count = 0
83 | @@total_resource_routes_count = 0
84 | @@exclusive_routes = nil
85 | end
86 |
87 | # gets called once every network_config resource has been declared.
88 | def enforce_exclusivity
89 | unless @@exclusive_routes == :false
90 | existing = Dir["#{@@config_dir}route-*"].map { |f| File.basename(f) }
91 | (existing - @@configured_routes).each do |parasite_file|
92 | Puppet.notice "puppet-network exclusive: removing #{parasite_file}"
93 | File.delete("#{@@config_dir}#{parasite_file}")
94 | end
95 | end
96 | clear()
97 | end
98 | end
99 |
--------------------------------------------------------------------------------
/lib/puppet/type/network_config.rb:
--------------------------------------------------------------------------------
1 | require 'puppet'
2 |
3 | module Puppet
4 |
5 | Puppet::Type.newtype(:network_config) do
6 | @doc = "The network configuration type"
7 |
8 | ensurable
9 |
10 | newparam(:exclusive) do
11 | d = "Enforces that no network configuration exists besides what puppet defines.\n"
12 | d << "Enabled by default, set it to false in any resource to disable globally."
13 | desc(d)
14 |
15 | newvalues(:true, :false)
16 | # this behaviorally defaults to true (see network_scripts.rb exists?()/initialize())
17 | # using defaultto(:true) would prevent users from setting this to false
18 | end
19 |
20 | newparam(:device) do
21 | isnamevar
22 | desc "The network device to be configured"
23 | end
24 |
25 | newparam(:bootproto) do
26 | desc "Boot priority for the network device"
27 | newvalues(:dhcp, :static, :none)
28 | defaultto(:dhcp)
29 | end
30 |
31 | newparam(:onboot) do
32 | desc "Start the network device on boot"
33 | newvalues(:yes, :no)
34 | defaultto(:yes)
35 | end
36 |
37 | newparam(:nozeroconf) do
38 | desc "Skip zeroconf (aka local-link) configuration"
39 | newvalues(:yes, :no)
40 | end
41 |
42 | newparam(:netmask) do
43 | desc "Configure the subnetmask of the device"
44 | end
45 |
46 | newparam(:prefix) do
47 | desc "Configure the network prefix, Has precedence over NETMASK on redhat."
48 | end
49 |
50 | newparam(:network) do
51 | desc "Configure the network of the device"
52 | end
53 |
54 | newparam(:broadcast) do
55 | desc "Configure the broadcast of the device"
56 | end
57 |
58 | newparam(:ipaddr) do
59 | desc "Configure the IP address of the device"
60 | end
61 |
62 | newparam(:gateway) do
63 | desc "Configure the Gateway of the device"
64 | end
65 |
66 | newparam(:hwaddr) do
67 | desc "Hardware address of the device"
68 | end
69 |
70 | newparam(:domain) do
71 | desc "Configure the domain of the device"
72 | end
73 |
74 | newparam(:bridge) do
75 | desc "The bridge in which the device is enslaved (if any)"
76 | end
77 |
78 | newparam(:stp) do
79 | desc "Enable STP (only applicable to type=Bridge devices)"
80 | end
81 |
82 | newparam(:delay) do
83 | desc "Configure forward delay (only applicable to type=Bridge devices)"
84 | end
85 |
86 | newparam(:peerdns) do
87 | desc "modify /etc/resolv.conf if peer uses msdns extension (PPP only) or
88 | DNS{1,2} are set, or if using dhclient. default to 'yes'."
89 | newvalues(:yes, :no)
90 | end
91 |
92 | newparam(:dns1) do
93 | desc "primary DNS server IPADDR"
94 | end
95 |
96 | newparam(:dns2) do
97 | desc "secondary DNS server IPADDR"
98 | end
99 |
100 | newparam(:type) do
101 | desc "Type of the device"
102 | newvalues(:Ethernet, :Bridge, :Bonding)
103 | end
104 |
105 | newparam(:vlan) do
106 | desc "Is the device VLAN tagged (802.1q)"
107 | newvalues(:yes, :no)
108 | end
109 |
110 | newparam(:userctl) do
111 | desc "Non root users are allowed to control device if set to yes"
112 | newvalues(:yes, :no)
113 | defaultto(:no)
114 | end
115 |
116 | newparam(:bonding_opts) do
117 | desc "Configures bonding parameter"
118 | end
119 |
120 | newparam(:master) do
121 | desc "Configures the bonding device to which the device is enslaved (set 'slave=>yes' too)"
122 | end
123 |
124 | newparam(:slave) do
125 | desc "Configures whether or not the device is enslaved to a bonding device"
126 | newvalues(:yes, :no)
127 | end
128 | end
129 | end
130 |
--------------------------------------------------------------------------------
/lib/puppet/type/network_interface.rb:
--------------------------------------------------------------------------------
1 | require 'puppet'
2 | require 'ipaddr'
3 |
4 | module Puppet
5 |
6 | Puppet::Type.newtype(:network_interface) do
7 | @doc = "The network managment configuration type"
8 |
9 | ensurable
10 |
11 | newparam(:device) do
12 | isnamevar
13 | desc "The network device to be configured"
14 | end
15 |
16 | newproperty(:state) do
17 | desc "state of the interface"
18 | newvalues(:up, :down)
19 | defaultto(:up)
20 | end
21 |
22 | newproperty(:inet) do
23 | desc "Configure the IPV4 address of the device"
24 | end
25 |
26 | newproperty(:inet6) do
27 | desc "Configure the IPV6 address of the device"
28 | end
29 |
30 | newproperty(:gateway) do
31 | desc "Configure the Gateway of the device"
32 | end
33 |
34 | newproperty(:address) do
35 | desc "Hardware address of the device"
36 | end
37 |
38 | newproperty(:arp) do
39 | desc "Arp"
40 | newvalues(:on, :off)
41 | end
42 |
43 | newproperty(:multicast) do
44 | desc "multicast"
45 | newvalues(:on, :off)
46 | end
47 |
48 | newproperty(:dynamic) do
49 | desc "dynamic"
50 | newvalues(:on, :off)
51 | end
52 |
53 | newproperty(:qlen) do
54 | desc "txquelen"
55 | end
56 |
57 | newproperty(:mtu) do
58 | desc "mtu"
59 | end
60 |
61 | newparam(:vlan) do
62 | desc "Is the device VLAN tagged (802.1q)"
63 | newvalues(:yes, :no)
64 | defaultto(:no)
65 | end
66 |
67 |
68 | newproperty(:ipaddr) do
69 | desc "Configure the IP address of the device"
70 | end
71 |
72 | newproperty(:netmask) do
73 | desc "Configure the subnetmask of the device"
74 |
75 | munge do |value|
76 | if value.match(/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/)
77 | IPAddr.new(value).to_i.to_s(2).count("1").to_s
78 | else
79 | super
80 | end
81 | end
82 | end
83 |
84 | newproperty(:broadcast) do
85 | desc "Configure the broadcast of the device"
86 | end
87 |
88 | end
89 | end
90 |
--------------------------------------------------------------------------------
/lib/puppet/type/network_route.rb:
--------------------------------------------------------------------------------
1 | require 'puppet'
2 |
3 | module Puppet
4 |
5 | Puppet::Type.newtype(:network_route) do
6 | @doc = "The route configuration type"
7 |
8 | ensurable
9 |
10 | newparam(:exclusive) do
11 | d = "Enforces that no route configuration exists besides what puppet defines.\n"
12 | d << "Enabled by default, set it to false in any resource to disable globally."
13 | desc(d)
14 |
15 | newvalues(:true, :false)
16 | # this behaviorally defaults to true (see network_scripts.rb exists?()/initialize())
17 | # using defaultto(:true) would prevent users from setting this to false
18 | end
19 |
20 | newparam(:device) do
21 | isnamevar
22 | desc "The network device for which route will be configured"
23 | end
24 |
25 | newparam(:routes) do
26 | desc "The routes to be configured"
27 | end
28 | end
29 | end
30 |
--------------------------------------------------------------------------------
/manifests/init.pp.example:
--------------------------------------------------------------------------------
1 |
2 | class puppet-network {
3 |
4 | network_config { "eth1":
5 | bootproto => static,
6 | onboot => no,
7 | netmask => "255.255.255.0",
8 | broadcast => "131.252.223.63",
9 | ipaddr => "131.252.214.173",
10 | gateway => "121.411.713.245",
11 | hwaddr => "FF:DD:CC:BB:AA",
12 | userctl => yes,
13 | domain => "example2.domain.com",
14 | ensure => present
15 | }
16 |
17 | network_config { "eth3":
18 | bootproto => "dhcp",
19 | onboot => "no",
20 | userctl => "yes",
21 | ensure => present
22 | }
23 |
24 | network_interface { "eth3":
25 | state => "up",
26 | mtu => "1111",
27 | qlen => "2000",
28 | address => "aa:bb:cc:dd:ee:ff",
29 | broadcast => "ff:ff:ff:ff:ff:ff",
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/spec/unit/puppet/provider/network_config/network_scripts.rb:
--------------------------------------------------------------------------------
1 | require 'puppet'
2 | require 'mocha'
3 | require '/etc/puppet/modules/puppet-network/lib/puppet/provider/network_config/network_scripts.rb'
4 |
5 | provider_class = Puppet::Type.type(:network_config).provider(:network_scripts)
6 |
7 | describe provider_class do
8 | before do
9 | @provider = provider_class.new
10 | end
11 | it "should read config file" do
12 | File.stubs(:exist?).returns true
13 | filemock = stub "Network File"
14 | File.stubs(:new).returns filemock
15 | filemock.stubs(:readlines).returns [
16 | "#this is a comment",
17 | "USRCTL=no",
18 | "IPADDR=127.0.0.1",
19 | "BOOTPROTO=dhcp\n",
20 | "ONBOOT=yes",
21 | ]
22 |
23 | @provider.read_config.should == {
24 | :USRCTL => "no",
25 | :IPADDR => "127.0.0.1",
26 | :BOOTPROTO => "dhcp",
27 | :ONBOOT => "yes",
28 | }
29 |
30 | end
31 |
32 | end
33 |
--------------------------------------------------------------------------------
/spec/unit/puppet/provider/network_interface/ip.rb:
--------------------------------------------------------------------------------
1 | require 'puppet'
2 | require 'ruby-debug'
3 | require 'mocha'
4 | require 'lib/puppet/provider/network_interface/ip.rb'
5 |
6 |
7 | provider_class = Puppet::Type.type(:network_interface).provider(:ip)
8 |
9 | describe provider_class do
10 |
11 | before do
12 | @resource = stub("resource", :name => "lo")
13 | @resource.stubs(:[]).with(:name).returns "lo"
14 | @resource.stubs(:[]).returns "lo"
15 | @provider = provider_class.new(@resource)
16 | end
17 |
18 | it "should parse ip link show output for loopback" do
19 | ip_output = <<-HEREDOC
20 | 1: lo: mtu 16436 qdisc noqueue state UNKNOWN
21 | link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
22 | inet 127.0.0.1/8 scope host lo
23 | inet6 ::1/128 scope host
24 | valid_lft forever preferred_lft forever
25 | HEREDOC
26 |
27 | @provider.stubs(:ip_output).returns(ip_output)
28 | hash = {
29 | "device" => "lo",
30 | #"dynamic" => "on",
31 | #"multicast" => "on",
32 | #"arp" => "on",
33 | "mtu" => "16436",
34 | "qdisc" => "noqueue",
35 | "state" => "UNKNOWN",
36 | "qlen" => nil,
37 | "address" => "00:00:00:00:00:00",
38 | "broadcast" => "00:00:00:00:00:00",
39 | "inet_0" => {
40 | "ip" => "127.0.0.1",
41 | "brd" => nil,
42 | "scope" => "host",
43 | "dev" => "lo",
44 | },
45 | "inet6_0" => {
46 | "ip" => "::1",
47 | "scope" => "host",
48 | "valid_lft" => "forever",
49 | "preferred_lft" => "forever",
50 | },
51 | }
52 | @provider.read_ip_output.should == hash
53 | end
54 |
55 | before do
56 | @resource = stub("resource", :name => "eth0")
57 | @resource.stubs(:[]).with(:name).returns "eth0"
58 | @resource.stubs(:[]).returns "eth0"
59 | @provider = provider_class.new(@resource)
60 | end
61 |
62 | it "should parse ip addr show output with an ipv4" do
63 | ip_output = <<-HEREDOC
64 | 2: eth0: mtu 1500 qdisc pfifo_fast state UP qlen 1000
65 | link/ether 08:00:27:6c:c7:59 brd ff:ff:ff:ff:ff:ff
66 | inet 192.168.56.101/24 brd 192.168.56.255 scope global eth0
67 | inet6 fe80::a00:27ff:fe6c:c759/64 scope link
68 | valid_lft forever preferred_lft forever
69 | HEREDOC
70 |
71 | @provider.stubs(:ip_output).returns(ip_output)
72 | hash = {
73 | "device" => "eth0",
74 | #"dynamic" => "on",
75 | #"multicast" => "on",
76 | #"arp" => "on",
77 | "mtu" => "1500",
78 | "qdisc" => "pfifo_fast",
79 | "state" => "UP",
80 | "qlen" => "1000",
81 | "address" => "08:00:27:6c:c7:59",
82 | "broadcast" => "ff:ff:ff:ff:ff:ff",
83 |
84 | "inet_0" => {
85 | "ip" => "192.168.56.101",
86 | "brd" => "192.168.56.255",
87 | "scope" => "global",
88 | "dev" => "eth0",
89 | },
90 | "inet6_0" => {
91 | "ip" => "fe80::a00:27ff:fe6c:c759",
92 | "scope" => "link",
93 | "valid_lft" => "forever",
94 | "preferred_lft" => "forever",
95 | }
96 | }
97 | @provider.read_ip_output.should == hash
98 |
99 | end
100 |
101 | it "should parse ip link show output with multiple ip addresses" do
102 | ip_output = <<-HEREDOC
103 | 2: eth0: mtu 1500 qdisc pfifo_fast state UNKNOWN qlen 1000
104 | link/ether 54:52:00:27:fd:71 brd ff:ff:ff:ff:ff:ff
105 | inet 131.252.208.54/24 brd 131.252.208.255 scope global eth0
106 | inet 131.252.208.79/32 brd 131.252.208.255 scope global eth0:2
107 | inet 131.252.208.98/32 brd 131.252.208.255 scope global eth0:3
108 | inet 131.252.208.61/32 brd 131.252.208.255 scope global eth0:12
109 | inet 131.252.208.66/32 brd 131.252.208.255 scope global eth0:13
110 | inet6 2610:10:20:208:5652:ff:fe27:fd71/64 scope global dynamic
111 | valid_lft 2591858sec preferred_lft 604658sec
112 | inet6 2610:10:20:208::66/64 scope global
113 | valid_lft forever preferred_lft forever
114 | inet6 2610:10:20:208::79/64 scope global
115 | valid_lft forever preferred_lft forever
116 | inet6 fe80::5652:ff:fe27:fd71/64 scope link
117 | valid_lft forever preferred_lft forever
118 | HEREDOC
119 | @provider.stubs(:ip_output).returns(ip_output)
120 | hash = {
121 | "device" => "eth0",
122 | #"dynamic" => "on",
123 | #"multicast" => "on",
124 | #"arp" => "on",
125 | "mtu" => "1500",
126 | "qdisc" => "pfifo_fast",
127 | "state" => "UNKNOWN",
128 | "qlen" => "1000",
129 | "address" => "54:52:00:27:fd:71",
130 | "broadcast" => "ff:ff:ff:ff:ff:ff",
131 | "inet_0" => {
132 | "ip" => "131.252.208.54",
133 | "brd" => "131.252.208.255",
134 | "scope" => "global",
135 | "dev" => "eth0",
136 | },
137 | "inet_1" => {
138 | "ip" => "131.252.208.79",
139 | "brd" => "131.252.208.255",
140 | "scope" => "global",
141 | "dev" => "eth0:2",
142 | },
143 | "inet_2" => {
144 | "ip" => "131.252.208.98",
145 | "brd" => "131.252.208.255",
146 | "scope" => "global",
147 | "dev" => "eth0:3",
148 | },
149 | "inet_3" => {
150 | "ip" => "131.252.208.61",
151 | "brd" => "131.252.208.255",
152 | "scope" => "global",
153 | "dev" => "eth0:12",
154 | },
155 | "inet_4" => {
156 | "ip" => "131.252.208.66",
157 | "brd" => "131.252.208.255",
158 | "scope" => "global",
159 | "dev" => "eth0:13",
160 | },
161 | "inet6_0" => {
162 | "ip" => "2610:10:20:208:5652:ff:fe27:fd71",
163 | "scope" => "global",
164 | "valid_lft" => "2591858sec",
165 | "preferred_lft" => "604658sec"
166 | },
167 | "inet6_1" => {
168 | "ip" => "2610:10:20:208::66",
169 | "scope" => "global",
170 | "valid_lft" => "forever",
171 | "preferred_lft" => "forever",
172 | },
173 | "inet6_2" => {
174 | "ip" => "2610:10:20:208::79",
175 | "scope" => "global",
176 | "valid_lft" => "forever",
177 | "preferred_lft" => "forever",
178 | },
179 | "inet6_3" => {
180 | "ip" => "fe80::5652:ff:fe27:fd71",
181 | "scope" => "link",
182 | "valid_lft" => "forever",
183 | "preferred_lft" => "forever",
184 | },
185 | }
186 | @provider.read_ip_output.should == hash
187 | end
188 |
189 | end
190 |
--------------------------------------------------------------------------------