Nice. It'd be good to see some example files, though. On Oct 27, 2009, at 12:50 PM, Eric Sorenson wrote:
> > > Signed-off-by: Eric Sorenson <[email protected]> > --- > ext/regexp_nodes.rb | 199 ++++++++++++++++++++++++++++++++++++++++++ > +++++++++ > 1 files changed, 199 insertions(+), 0 deletions(-) > create mode 100644 ext/regexp_nodes.rb > > diff --git a/ext/regexp_nodes.rb b/ext/regexp_nodes.rb > new file mode 100644 > index 0000000..6a8e6da > --- /dev/null > +++ b/ext/regexp_nodes.rb > @@ -0,0 +1,199 @@ > +#!/usr/bin/env ruby > + > +# = Synopsis > +# This is an external node classifier script, after > +# http://reductivelabs.com/trac/puppet/wiki/ExternalNodes > +# > +# = Usage > +# regexp_nodes.rb <host> > +# > +# = Description > +# This classifier implements filesystem autoloading: > +# We look through classes/ and parameters/ subdirectories, looping > +# through each file we find there - the contents are a regexp-per- > line > +# which, if they match the hostname passed us as ARGV[0], will cause > +# a class or parameter named the same thing as the file to be set. > +# > +# = Author > +# Eric Sorenson <[email protected]> > + > + > +# we need yaml or there's not much point in going on > +require 'yaml' > + > +# Sets are like arrays but automatically de-duplicate elements > +require 'set' > + > +# set up some nice logging > +require 'logger' > +# XXX flip this for production vs local sandbox > +# $LOG = Logger.new("/var/puppet/log/extnodes.log") > +# $LOG.level = Logger::FATAL > +$LOG = Logger.new($stderr) > +$LOG.level = Logger::DEBUG > + > +# paths for files we use will be relative to this directory > +# XXX flip this for production vs local sandbox > +# WORKINGDIR = "/var/puppet/bin" > +WORKINGDIR = Dir.pwd > + > +# This class holds all the methods for creating and accessing the > properties > +# of an external node. There are really only two public methods: > initialize() > +# and a special version of to_yaml() > + > +class ExternalNode > + # Make these instance variables get/set-able with eponymous > methods > + attr_accessor :classes, :parameters, :hostname > + > + # initialize() takes three arguments: > + # hostname:: usually passed in via ARGV[0] but it could be > anything > + # classdir:: directory under WORKINGDIR to look for files named > after > + # classes > + # parameterdir:: directory under WORKINGDIR to look for > directories to set > + # parameters > + def initialize(hostname, classdir = 'classes/', parameterdir = > 'parameters/') > + # instance variables that contain the lists of classes and > parameters > + @hostname > + @classes = Set.new ["baseclass"] > + @parameters = Hash.new("unknown") # sets a default value > of "unknown" > + > + self.parse_argv(hostname) > + self.match_classes(WORKINGDIR + "/" + classdir) > + self.match_parameters(WORKINGDIR + "/" + parameterdir) > + end > + > + # private method called by initialize() which sanity-checks our > hostname. > + # good candidate for overriding in a subclass if you need > different checks > + def parse_argv(hostname) > + if hostname =~ /^([-\w]+?)\.([-\w\.]+)/ # non-greedy up > to the first . is hostname > + @hostname = $1 > + elsif hostname =~ /^([-\w]+)$/ # sometimes puppet's > @name is just a name > + @hostname = hostname > + else > + $LOG.fatal("didn't receive parsable hostname, got: > [#{hostname}]") > + exit(1) > + end > + end > + > + # to_yaml massages a copy of the object and outputs clean yaml > so we don't > + # feed weird things back to puppet []< > + def to_yaml > + classes = self.classes.to_a > + if self.parameters.empty? # otherwise to_yaml prints > "parameters: {}" > + parameters = nil > + else > + parameters = self.parameters > + end > + ({ 'classes' => classes, 'parameters' => parameters}).to_yaml > + end > + > + # Private method that expects an absolute path to a file and a > string to > + # match - it returns true if the string was matched by any of > the lines in > + # the file > + def matched_in_patternfile?(filepath, matchthis) > + > + patternlist = [] > + > + begin > + open(filepath).each { |l| > + pattern = %r{#{l.chomp!}} > + patternlist << pattern > + $LOG.debug("appending [#{pattern}] to patternlist > for [#{filepath}]") > + } > + rescue Exception > + $LOG.fatal("Problem reading #{filepath}: #{$!}") > + exit(1) > + end > + > + $LOG.debug("list of patterns for #{filepath}: > #{patternlist}") > + > + if matchthis =~ Regexp.union(patternlist) > + $LOG.debug("matched #{$~.to_s} in #{matchthis}, > returning true") > + return true > + > + else # hostname didn't match anything in patternlist > + $LOG.debug("#{matchthis} unmatched, returning false") > + return nil > + end > + > + end # def > + > + # private method - takes a path to look for files, iterates > through all > + # readable, regular files it finds, and matches this instance's > @hostname > + # against each line; if any match, the class will be set for > this node. > + def match_classes(fullpath) > + Dir.foreach(fullpath) do |patternfile| > + filepath = "#{fullpath}/#{patternfile}" > + next unless File.file?(filepath) and > + File.readable?(filepath) > + $LOG.debug("Attempting to match [...@hostname}] in > [#{filepath}]") > + if matched_in_patternfile?(filepath,@hostname) > + @classes << patternfile.to_s > + $LOG.debug("Appended #{patternfile.to_s} to classes > instance variable") > + end # if > + end # Dir.foreach > + end # def > + > + # Parameters are handled slightly differently; we make another > level of > + # directories to get the parameter name, then use the names of > the files > + # contained in there for the values of those parameters. > + # > + # ex: cat /var/puppet/bin/parameters/environment/production > + # ^prodweb > + # would set parameters["environment"] = "production" for > prodweb001 > + def match_parameters(fullpath) > + Dir.foreach(fullpath) do |parametername| > + > + filepath = "#{fullpath}/#{parametername}" > + next if File.basename(filepath) =~ /^\./ # skip > over dotfiles > + > + next unless File.directory?(filepath) and > + File.readable?(filepath) # skip over > non-directories > + > + $LOG.debug "Considering contents of #{filepath}" > + > + Dir.foreach("#{filepath}") do |patternfile| > + secondlevel = "#{filepath}/#{patternfile}" > + $LOG.debug "Found parameters patternfile at > #{secondlevel}" > + next unless File.file?(secondlevel) and > + File.readable?(secondlevel) > + $LOG.debug("Attempting to match [...@hostname}] in > [#{secondlevel}]") > + if matched_in_patternfile?(secondlevel, @hostname) > + @parameters[ parametername.to_s ] = > patternfile.to_s > + $LOG.debug("Set > @parameters[#{parametername.to_s}] = #{patternfile.to_s}") > + end # if > + end # Dir.foreach #{filepath} > + end # Dir.foreach #{fullpath} > + end # def > + > +end # Class > + > +# Logic for local hacks that don't fit neatly into the autoloading > model can > +# happen as we initialize a subclass > +class MyExternalNode < ExternalNode > + > + def initialize(hostname, classdir = 'classes/', parameterdir = > 'parameters/') > + > + super > + > + # Set "hostclass" parameter based on hostname, > + # stripped of leading environment prefix and numeric suffix > + if @hostname =~ /^(\w*?)-?(\D+)(\d{2,3})$/ > + match = Regexp.last_match > + > + hostclass = match[2] > + $LOG.debug("matched hostclass #{hostclass}") > + @parameters[ "hostclass" ] = hostclass > + else > + $LOG.debug("hostclass couldn't figure out class from > #...@hostname}") > + end # if > + end # def > + > +end # Class > + > + > +# Here we begin actual execution by calling methods defined above > + > +mynode = MyExternalNode.new(ARGV[0]) > + > +puts mynode.to_yaml > -- > 1.6.4.3 > > > > -- Men never do evil so completely and cheerfully as when they do it from a religious conviction. --Blaise Pascal --------------------------------------------------------------------- Luke Kanies | http://reductivelabs.com | http://madstop.com --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Puppet Developers" group. To post to this group, send email to [email protected] To unsubscribe from this group, send email to [email protected] For more options, visit this group at http://groups.google.com/group/puppet-dev?hl=en -~----------~----~----~----~------~----~------~--~---
