summaryrefslogtreecommitdiffstats
path: root/lib/puppet
diff options
context:
space:
mode:
Diffstat (limited to 'lib/puppet')
-rwxr-xr-xlib/puppet/provider/parsedfile.rb14
-rwxr-xr-xlib/puppet/provider/port/parsed.rb242
-rw-r--r--lib/puppet/type/pfile/target.rb1
-rwxr-xr-xlib/puppet/type/port.rb17
-rw-r--r--lib/puppet/util.rb2
-rw-r--r--lib/puppet/util/fileparsing.rb37
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