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
-~----------~----~----~----~------~----~------~--~---

Reply via email to