diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/puppet/provider/interface/linux.rb | 284 | ||||
-rw-r--r-- | lib/puppet/provider/interface/sunos.rb | 99 | ||||
-rw-r--r-- | lib/puppet/type/interface.rb | 57 |
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$ |