diff options
Diffstat (limited to 'lib/puppet')
| -rwxr-xr-x | lib/puppet/provider/parsedfile.rb | 14 | ||||
| -rwxr-xr-x | lib/puppet/provider/port/parsed.rb | 242 | ||||
| -rw-r--r-- | lib/puppet/type/pfile/target.rb | 1 | ||||
| -rwxr-xr-x | lib/puppet/type/port.rb | 17 | ||||
| -rw-r--r-- | lib/puppet/util.rb | 2 | ||||
| -rw-r--r-- | lib/puppet/util/fileparsing.rb | 37 |
6 files changed, 199 insertions, 114 deletions
diff --git a/lib/puppet/provider/parsedfile.rb b/lib/puppet/provider/parsedfile.rb index 5539679d0..40b977314 100755 --- a/lib/puppet/provider/parsedfile.rb +++ b/lib/puppet/provider/parsedfile.rb @@ -13,7 +13,7 @@ class Puppet::Provider::ParsedFile < Puppet::Provider extend Puppet::Util::FileParsing class << self - attr_accessor :default_target + attr_accessor :default_target, :target end attr_accessor :state_hash @@ -97,6 +97,8 @@ class Puppet::Provider::ParsedFile < Puppet::Provider @records = [] @target_objects = {} + @target = nil + # Default to flat files @filetype = Puppet::FileType.filetype(:flat) super @@ -210,7 +212,15 @@ class Puppet::Provider::ParsedFile < Puppet::Provider # there is no file return [] else - self.parse(text) + # Set the target, for logging. + old = @target + begin + @target = path + self.parse(text) + ensure + @target = old + end + end end diff --git a/lib/puppet/provider/port/parsed.rb b/lib/puppet/provider/port/parsed.rb index d65a0777f..510b461df 100755 --- a/lib/puppet/provider/port/parsed.rb +++ b/lib/puppet/provider/port/parsed.rb @@ -1,135 +1,169 @@ require 'puppet/provider/parsedfile' -Puppet::Type.type(:port).provide :parsed, :parent => Puppet::Provider::ParsedFile do - - @filetype = Puppet::FileType.filetype(:flat) - @path = "/etc/services" - - # Parse a services file - # - # This method also stores existing comments, and it stores all port - # info in order, mostly so that comments are retained in the order - # they were written and in proximity to the same ports. - def self.parse(text) - count = 0 - instances = [] - namehash = {} # For merging - text.chomp.split("\n").each { |line| - hash = {} - case line - when /^#/, /^\s*$/: - # add comments and blank lines to the list as they are - instances << line - else - if line.sub!(/^(\S+)\s+(\d+)\/(\w+)\s*/, '') - hash[:name] = $1 - hash[:number] = $2 - hash[:protocols] = [$3] - - unless line == "" - line.sub!(/^([^#]+)\s*/) do |value| - aliases = $1 - - # Remove any trailing whitespace - aliases.strip! - unless aliases =~ /^\s*$/ - hash[:alias] = aliases.split(/\s+/) - end - - "" - end - - line.sub!(/^\s*#\s*(.+)$/) do |value| - desc = $1 - unless desc =~ /^\s*$/ - hash[:description] = desc.sub(/\s*$/, '') - end - - "" - end - end - else - if line =~ /^\s+\d+/ and - Facter["operatingsystem"].value == "Darwin" - #Puppet.notice "Skipping wonky OS X port entry %s" % - # line.inspect - next - end - raise Puppet::Error, "Could not match '%s'" % line - end - - # If there's already a service with this name, then check - # to see if the only difference is the proto; if so, just - # add our proto and walk away - if obj = namehash[hash[:name]] - if portmerge(obj, hash) - next - end - end +services = nil +case Facter.value(:operatingsystem) +when "Solaris": services = "/etc/inet/services" +else + services = "/etc/services" +end - instances << hash - namehash[hash[:name]] = hash +Puppet::Type.type(:port).provide(:parsed, + :parent => Puppet::Provider::ParsedFile, + :default_target => services, + :filetype => :flat +) do + text_line :comment, :match => /^\s*#/ + text_line :blank, :match => /^\s*$/ + + # We're cheating horribly here -- we don't support ddp, because it assigns + # the same number to already-used names, and the same name to different + # numbers. + text_line :ddp, :match => /^\S+\s+\d+\/ddp/ + + # Also, just ignore the lines on OS X that don't have service names. + text_line :funky_darwin, :match => /^\s+\d+\// + + # We have to manually parse the line, since it's so darn complicated. + record_line :parsed, :fields => %w{name port protocols alias} do |line| + if line =~ /\/ddp/ + raise "missed ddp in %s" % line + end + # The record might contain multiple port lines separated by \n. + hashes = line.split("\n").collect { |l| parse_port(l) } - count += 1 - end - } + # It's easy if there's just one hash. + if hashes.length == 1 + return hashes.shift + end - return instances + # Else, merge the two records into one. + return port_merge(*hashes) end - def self.portmerge(base, hash) - unless base.has_key?(:protocols) - return false + # Override how we split into lines, so that we always treat both protocol + # lines as a single line. This drastically simplifies merging the two lines + # into one record. + def self.lines(text) + names = {} + lines = [] + + # We organize by number, because that's apparently how the ports work. + # You'll never be able to use Puppet to manage multiple entries + # with the same name but different numbers, though. + text.split("\n").each do |line| + if line =~ /^([-\w]+)\s+(\d+)\/[^d]/ # We want to skip ddp proto stuff + names[$1] ||= [] + names[$1] << line + lines << [:special, $1] + else + lines << line + end end - # This method is only called from parsing, so we only worry - # about 'is' values. - proto = base[:protocols] + # Now, return each line in order, but join the ones with the same name + lines.collect do |line| + if line.is_a?(Array) + name = line[1] + if names[name] + t = names[name].join("\n") + names.delete(name) + t + end + else + line + end + end.reject { |l| l.nil? } + end + + # Parse a single port line, returning a hash. + def self.parse_port(line) + hash = {} + if line.sub!(/^(\S+)\s+(\d+)\/(\w+)\s*/, '') + hash[:name] = $1 + hash[:number] = $2 + hash[:protocols] = [$3] + + unless line == "" + line.sub!(/^([^#]+)\s*/) do |value| + aliases = $1 + + # Remove any trailing whitespace + aliases.strip! + unless aliases =~ /^\s*$/ + hash[:alias] = aliases.split(/\s+/) + end - if proto.nil? or proto == :absent - # We are an unitialized object; we've got 'should' - # values but no 'is' values - return false - end + "" + end - # If this is happening, our object exists - base[:ensure] = :present + line.sub!(/^\s*#\s*(.+)$/) do |value| + desc = $1 + unless desc =~ /^\s*$/ + hash[:description] = desc.sub(/\s*$/, '') + end - if hash[:protocols] - # The protocol can be a symbol, so... - if proto.is_a?(Symbol) - proto = [] + "" + end end - # Check to see if it already includes our proto - unless proto.include?(hash[:protocols]) - # We are missing their proto - proto += hash[:protocols] - base[:protocols] = proto + else + if line =~ /^\s+\d+/ and + Facter["operatingsystem"].value == "Darwin" + #Puppet.notice "Skipping wonky OS X port entry %s" % + # line.inspect + next end + Puppet.notice "Ignoring unparseable line '%s' in %s" % [line, self.target] + end + + if hash.empty? + return nil + else + return hash end + end - if hash.include?(:description) and ! base.include?(:description) - base[:description] = hash[:description] + # Merge two records into one. + def self.port_merge(one, two) + keys = [one.keys, two.keys].flatten.uniq + + # We'll be returning the 'one' hash. so make any necessary modifications + # to it. + keys.each do |key| + # The easy case + if one[key] == two[key] + next + elsif one[key] and ! two[key] + next + elsif ! one[key] and two[key] + one[key] = two[key] + elsif one[key].is_a?(Array) and two[key].is_a?(Array) + one[key] = [one[key], two[key]].flatten.uniq + else + # Keep the info from the first hash, so don't do anything + #Puppet.notice "Cannot merge %s in %s with %s" % + # [key, one.inspect, two.inspect] + end end - return true + return one end # Convert the current object into one or more services entry. - def self.to_record(hash) + def self.to_line(hash) + unless hash[:record_type] == :parsed + return super + end + + # Strangely, most sites seem to use tabs as separators. hash[:protocols].collect { |proto| - str = "%s\t%s/%s" % [hash[:name], hash[:number], proto] + str = "%s\t\t%s/%s" % [hash[:name], hash[:number], proto] if value = hash[:alias] and value != :absent - str += "\t%s" % value.join(" ") - else - str += "\t" + str += "\t\t%s" % value.join(" ") end if value = hash[:description] and value != :absent str += "\t# %s" % value - else - str += "\t" end str }.join("\n") diff --git a/lib/puppet/type/pfile/target.rb b/lib/puppet/type/pfile/target.rb index 8c22b10b9..a7a219c0c 100644 --- a/lib/puppet/type/pfile/target.rb +++ b/lib/puppet/type/pfile/target.rb @@ -25,6 +25,7 @@ module Puppet # Create our link. def mklink + notice @is.inspect target = self.should # Clean up any existing objects. diff --git a/lib/puppet/type/port.rb b/lib/puppet/type/port.rb index 3e73e625f..c1eed299b 100755 --- a/lib/puppet/type/port.rb +++ b/lib/puppet/type/port.rb @@ -1,7 +1,7 @@ require 'puppet/type/parsedtype' module Puppet - newtype(:port, Puppet::Type::ParsedType) do + newtype(:port) do @doc = "Installs and manages port entries. For most systems, these entries will just be in /etc/services, but some systems (notably OS X) will have different solutions." @@ -55,7 +55,6 @@ module Puppet newstate(:description) do desc "The port description." - isoptional end newstate(:alias) do @@ -64,8 +63,6 @@ module Puppet one of the metaparams; using this state to set aliases will make those aliases available in your Puppet scripts and also on disk." - isoptional - # We actually want to return the whole array here, not just the first # value. def should @@ -99,6 +96,18 @@ module Puppet end end + newstate(:target) do + desc "The file in which to store service information. Only used by + those providers that write to disk (i.e., not NetInfo)." + + defaultto { if @parent.class.defaultprovider.ancestors.include?(Puppet::Provider::ParsedFile) + @parent.class.defaultprovider.default_target + else + nil + end + } + end + newparam(:name) do desc "The port name." diff --git a/lib/puppet/util.rb b/lib/puppet/util.rb index b0fb51e98..0d409e430 100644 --- a/lib/puppet/util.rb +++ b/lib/puppet/util.rb @@ -25,7 +25,7 @@ module Util # Change the process to a different user def self.chuser if Facter["operatingsystem"].value == "Darwin" - $stderr.puts "Ruby on darwin is broken; puppetmaster must run as root" + $stderr.puts "Ruby on darwin is broken; puppetmaster will not set its UID to 'puppet' and must run as root" return end if group = Puppet[:group] diff --git a/lib/puppet/util/fileparsing.rb b/lib/puppet/util/fileparsing.rb index 91a70c214..4056f2fff 100644 --- a/lib/puppet/util/fileparsing.rb +++ b/lib/puppet/util/fileparsing.rb @@ -54,8 +54,30 @@ module Puppet::Util::FileParsing # Try to match a record. def handle_record_line(line, hash) - if hash[:match] - raise "No provision yet for matching whole lines" + if method = hash[:method] + if ret = send(method, line.dup) + ret[:record_type] = hash[:name] + return ret + else + return nil + end + elsif regex = hash[:match] + raise "Cannot use matches to handle records yet" + # In this case, we try to match the whole line and then use the + # match captures to get our fields. + if match = regex.match(line) + fields = [] + ignore = hash[:ignore] || [] + p match.captures + match.captures.each_with_index do |value, i| + fields << value unless ignore.include? i + end + p fields + nil + else + Puppet.info "Did not match %s" % line + nil + end else ret = {} sep = hash[:separator] @@ -124,7 +146,7 @@ module Puppet::Util::FileParsing end # Define a new type of record. These lines get split into hashes. - def record_line(name, options) + def record_line(name, options, &block) unless options.include?(:fields) raise ArgumentError, "Must include a list of fields" end @@ -148,6 +170,15 @@ module Puppet::Util::FileParsing options[:joiner] = " " end + if block_given? + method = "handle_record_line_%s" % name + if respond_to?(method) + raise "Already have a method defined for this record" + end + meta_def(method, &block) + options[:method] = method + end + new_line_type(name, :record, options) end |
