summaryrefslogtreecommitdiffstats
path: root/lib/puppet
diff options
context:
space:
mode:
authorluke <luke@980ebf18-57e1-0310-9a29-db15c13687c0>2007-07-31 16:35:24 +0000
committerluke <luke@980ebf18-57e1-0310-9a29-db15c13687c0>2007-07-31 16:35:24 +0000
commit52e9fa0e734c6b6a873952fded94071910c2d3ac (patch)
treef713eea9df6503c69005e6d06a8cfd31c679a2dc /lib/puppet
parent7547baf6424ccb5f172566b11b9e3d7fe324fe00 (diff)
downloadpuppet-52e9fa0e734c6b6a873952fded94071910c2d3ac.tar.gz
puppet-52e9fa0e734c6b6a873952fded94071910c2d3ac.tar.xz
puppet-52e9fa0e734c6b6a873952fded94071910c2d3ac.zip
Adding interface implementations, as written by Paul Rose
git-svn-id: https://reductivelabs.com/svn/puppet/trunk@2732 980ebf18-57e1-0310-9a29-db15c13687c0
Diffstat (limited to 'lib/puppet')
-rw-r--r--lib/puppet/provider/interface/linux.rb284
-rw-r--r--lib/puppet/provider/interface/sunos.rb99
-rw-r--r--lib/puppet/type/interface.rb57
3 files changed, 440 insertions, 0 deletions
diff --git a/lib/puppet/provider/interface/linux.rb b/lib/puppet/provider/interface/linux.rb
new file mode 100644
index 000000000..c1d7eb985
--- /dev/null
+++ b/lib/puppet/provider/interface/linux.rb
@@ -0,0 +1,284 @@
+require 'puppet/provider/parsedfile'
+require 'erb'
+
+Puppet::Type.type(:interface).provide(:linux) do
+
+ confine :kernel => "Linux"
+
+ # Create the setter/gettor methods to match the model.
+ mkmodelmethods
+
+ ALIAS_TEMPLATE = ERB.new <<-ALIAS
+DEVICE=<%= self.device %>
+ONBOOT=<%= self.on_boot %>
+BOOTPROTO=none
+IPADDR=<%= self.name %>
+NETMASK=255.255.255.255
+BROADCAST=
+ALIAS
+
+
+ LOOPBACK_TEMPLATE = ERB.new <<-LOOPBACKDUMMY
+DEVICE=<%= self.device %>
+ONBOOT=<%= self.on_boot %>
+BOOTPROTO=static
+IPADDR=<%= self.name %>
+NETMASK=255.255.255.255
+BROADCAST=
+LOOPBACKDUMMY
+
+ # maximum number of dummy interfaces
+ MAX_DUMMIES = 10
+
+ # maximum number of aliases per interface
+ MAX_ALIASES_PER_IFACE = 10
+
+ INTERFACE_DIR = "/etc/sysconfig/network-scripts"
+
+ @@dummies = []
+ @@aliases = Hash.new { |hash, key| hash[key] = [] }
+
+ # return the next avaliable dummy interface number, in the case where
+ # ifnum is not manually specified
+
+ def self.next_dummy
+
+ MAX_DUMMIES.times do |i|
+ unless @@dummies.include?(i.to_s)
+ @@dummies << i.to_s
+ return i.to_s
+ end
+ end
+
+ end
+
+ # return the next available alias on a given interface, in the case
+ # where ifnum if not manually specified
+
+ def self.next_alias(interface)
+
+ MAX_ALIASES_PER_IFACE.times do |i|
+ unless @@aliases[interface].include?(i.to_s)
+ @@aliases[interface] << i.to_s
+ return i.to_s
+ end
+ end
+
+ end
+
+ # calculate which dummy interfaces are currently already in
+ # use prior to needing to call self.next_dummy later on.
+
+ def self.prefetch
+
+ # parse all of the config files at once
+
+ Dir.glob("%s/ifcfg-*" % INTERFACE_DIR).each do |file|
+
+ record = parse(file)
+
+ # store the existing dummy interfaces
+
+ if record[:interface_type] == :dummy
+ @@dummies << record[:ifnum]
+ end
+
+ if record[:interface_type] == :alias
+ @@aliases[record[:interface]] << record[:ifnum]
+ end
+
+ end
+
+ end
+
+ def create
+
+ @model.class.validproperties.each do |property|
+ if value = @model.should(property)
+ @property_hash[property] = value
+ end
+ end
+ @property_hash[:name] = @model.name
+
+ return (@model.class.name.to_s + "_created").intern
+ end
+
+ def destroy
+ File.unlink(@model[:target])
+ end
+
+ def exists?
+ FileTest.exists?(@model[:target])
+ end
+
+ # generate the content for the interface file, so this is dependent
+ # on whether we are adding an alias to a real interface, or a loopback
+ # address (also dummy) on linux. For linux it's quite involved, and we
+ # will use an ERB template
+
+ def generate
+
+ # choose which template to use for the interface file, based on
+ # the interface type
+
+ case @model.should(:interface_type)
+ when :loopback
+
+ return LOOPBACK_TEMPLATE.result(binding)
+
+ when :alias
+
+ return ALIAS_TEMPLATE.result(binding)
+
+ end
+
+ end
+
+ # Where should the file be written out?
+ # This defaults to INTERFACE_DIR/ifcfg-<namevar>, but can have a
+ # more symbolic name by setting interface_desc in the type.
+
+ def file_path
+
+ @model[:interface_desc] ||= @model[:name]
+ return File.join(INTERFACE_DIR, "ifcfg-" + @model[:interface_desc])
+
+ end
+
+ # create the device name, so this based on the IP, and interface + type
+ def device
+
+ case @model.should(:interface_type)
+ when :loopback
+
+ @property_hash[:ifnum] ||= self.class.next_dummy
+ return "dummy" + @property_hash[:ifnum]
+
+ when :alias
+
+ @property_hash[:ifnum] ||= self.class.next_alias(@model[:interface])
+ return @model[:interface] + ":" + @property_hash[:ifnum]
+
+ end
+
+ end
+
+ # whether the device is to be brought up on boot or not. converts
+ # the true / false of the type, into yes / no values respectively
+ # writing out the ifcfg-* files
+
+ def on_boot
+
+ case @property_hash[:onboot].to_s
+ when "true"
+ return "yes"
+ when "false"
+ return "no"
+ else
+ return "neither"
+ end
+
+ end
+
+
+ # Write the new file out.
+ def flush
+ # Don't flush to disk if we're removing the config.
+ return if @model.should(:ensure) == :absent
+
+ @property_hash.each do |name, val|
+ if val == :absent
+ raise ArgumentError, "Propety %s must be provided" % val
+ end
+ end
+
+ File.open(@model[:target], "w") do |f|
+ f.puts generate()
+ end
+ end
+
+ # base the ifnum, for dummy / loopback interface in linux
+ # on the last octect of the IP address
+
+ # Parse the existing file.
+ def self.parse(file)
+
+ opts = {}
+ return opts unless FileTest.exists?(file)
+
+ File.open(file) do |f|
+ f.readlines.each do |line|
+ if line =~ /^(\w+)=(.+)$/
+ opts[$1.downcase.intern] = $2
+ end
+ end
+ end
+
+ # figure out the "real" device information
+
+ case opts[:device]
+ when /:/:
+
+ if opts[:device].include?(":")
+ opts[:interface], opts[:ifnum] = opts[:device].split(":")
+ end
+
+ opts[:interface_type] = :alias
+
+ when /^dummy/:
+
+ opts[:interface_type] = :loopback
+
+ opts[:interface] = "dummy"
+
+ # take the number of the dummy interface, as this is used
+ # when working out whether to call next_dummy when dynamically
+ # creating these
+
+ opts[:ifnum] = opts[:device].sub("dummy",'')
+
+ @@dummies << opts[:ifnum].to_s
+
+ else
+
+ opts[:interface_type] = :normal
+ opts[:interface] = opts[:device]
+
+ end
+
+ # translate whether we come up on boot to true/false
+
+ case opts[:onboot].downcase
+ when "yes":
+ opts[:onboot] = :true
+ when "no":
+ opts[:onboot] = :false
+ else
+ # this case should never happen, but just in case
+ opts[:onboot] = false
+ end
+
+
+ # Remove any attributes we don't want. These would be
+ # pretty easy to support.
+ [:bootproto, :broadcast, :netmask, :device].each do |opt|
+ if opts.include?(opt)
+ opts.delete(opt)
+ end
+ end
+
+ if opts.include?(:ipaddr)
+ opts[:name] = opts[:ipaddr]
+ opts.delete(:ipaddr)
+ end
+
+ return opts
+
+ end
+
+ def prefetch
+ @property_hash = self.class.parse(@model[:target])
+ end
+end
+
+# $Id$
diff --git a/lib/puppet/provider/interface/sunos.rb b/lib/puppet/provider/interface/sunos.rb
new file mode 100644
index 000000000..0290aa209
--- /dev/null
+++ b/lib/puppet/provider/interface/sunos.rb
@@ -0,0 +1,99 @@
+require 'puppet/provider/parsedfile'
+require 'erb'
+
+Puppet::Type.type(:interface).provide(:sunos,
+ :default_target => "/etc/hostname.lo0",
+ :parent => Puppet::Provider::ParsedFile,
+ :filetype => :flat
+) do
+
+ confine :kernel => "SunOS"
+
+ # Two types of lines:
+ # the first line does not start with 'addif'
+ # the rest do
+
+
+ record_line :sunos, :fields => %w{interface_type name ifopts onboot}, :rts => true, :absent => "", :block_eval => :instance do
+
+ # Parse our interface line
+ def process(line)
+ details = {:ensure => :present}
+
+ values = line.split(/\s+/)
+
+ # Are we the primary interface?
+ if values[0] == "addif"
+ details[:interface_type] = :alias
+ values.shift
+ else
+ details[:interface_type] = :normal
+ end
+
+ # Should the interface be up by default?
+ if values[-1] == "up"
+ details[:onboot] = :true
+ values.pop
+ else
+ details[:onboot] = :false
+ end
+
+ # Set the interface name.
+ details[:name] = values.shift
+
+ # Handle any interface options
+ unless values.empty?
+ details[:ifopts] = values.join(" ")
+ end
+
+ return details
+ end
+
+ # Turn our record into a line.
+ def to_line(details)
+ ret = []
+ if details[:interface_type] != :normal
+ ret << "addif"
+ end
+ ret << details[:name]
+
+ if details[:ifopts] and details[:ifopts] != :absent
+ if details[:ifopts].is_a?(Array)
+ ret << details[:ifopts].join(" ")
+ else
+ ret << details[:ifopts]
+ end
+ end
+
+ if details[:onboot] and details[:onboot] != :false
+ ret << "up"
+ end
+
+ return ret.join(" ")
+ end
+ end
+
+ def self.header
+ # over-write the default puppet behaviour of adding a header
+ # because on further investigation this breaks the solaris
+ # init scripts
+ %{}
+ end
+
+ def self.match(hash)
+ # see if we can match the has against an existing object
+ if model.find { |obj| obj.value(:name) == hash[:name] }
+ return obj
+ else
+ return false
+ end
+ end
+
+ # Where should the file be written out? Can be overridden by setting
+ # :target in the model.
+ def file_path
+ return File.join("/etc", "hostname." + @model[:interface])
+ end
+end
+
+# $Id$
diff --git a/lib/puppet/type/interface.rb b/lib/puppet/type/interface.rb
new file mode 100644
index 000000000..206776260
--- /dev/null
+++ b/lib/puppet/type/interface.rb
@@ -0,0 +1,57 @@
+Puppet::Type.newtype(:interface) do
+ require 'erb'
+
+ @doc = "Create configuration for IP address aliases and loopback addresses."
+
+ newparam(:name, :namevar => true) do
+ desc "The ipaddress to add to alias or loopback/dummy interface"
+ end
+
+ ensurable
+
+ newparam(:interface) do
+ desc "The interface the IP should be added to"
+ end
+
+ newproperty(:interface_type) do
+ desc "The interface type, loopback (also dummy) or alias"
+
+ newvalue(:loopback)
+ newvalue(:alias)
+ newvalue(:normal)
+
+ # Make dummy and loopback equivalent
+ aliasvalue(:dummy, :loopback)
+
+ defaultto :normal
+ end
+
+ newparam(:interface_desc) do
+ desc "On Linux, the description / symbolic name you wish to refer to the
+ interface by. When absent, Redhat Linux defaults to uses the namevar
+ which will be either the IP address, or hostname."
+ end
+
+ newproperty(:onboot) do
+ desc "Whether the interface should be configured to come up on boot"
+ newvalue(:true)
+ newvalue(:false)
+ end
+
+ newproperty(:ifnum) do
+ desc "If not automatically configuring the dummy interface or
+ and alias. This is use to force a given number to be used"
+ end
+
+ newproperty(:ifopts) do
+ desc "Interface options."
+ end
+
+ newparam(:target) do
+ desc "The path to the file this resource creates."
+
+ defaultto { @parent.provider.file_path }
+ end
+end
+
+# $Id$