diff --git a/lib/puppet/error.rb b/lib/puppet/error.rb index d0a0c9cc5..43a8df0f8 100644 --- a/lib/puppet/error.rb +++ b/lib/puppet/error.rb @@ -1,43 +1,49 @@ module Puppet # :nodoc: - # The base class for all Puppet errors. We want to make it easy to add - # line and file information. This probably isn't necessary for all - # errors, but... + # The base class for all Puppet errors. It can wrap another exception class Error < RuntimeError - attr_accessor :line, :file - - def backtrace - if defined?(@backtrace) - return @backtrace - else - return super - end + attr_reader :original + def initialize(message, original=nil) + super(message) + @original = original end + end - def initialize(message, line = nil, file = nil) - @message = message + module ExternalFileError + # This module implements logging with a filename and line number. Use this + # for errors that need to report a location in a non-ruby file that we + # parse. + attr_accessor :line, :file - @line = line if line - @file = file if file + def initialize(message, file=nil, line=nil, original=nil) + super(message, original) + @file = file + @line = line end def to_s - str = nil - if self.file and self.line - str = "#{@message} at #{@file}:#{@line}" - elsif self.line - str = "#{@message} at line #{@line}" - elsif self.file - str = "#{@message} in #{self.file}" + msg = super + if @file and @line + "#{msg} at #{@file}:#{@line}" + elsif @line + "#{msg} at line #{@line}" + elsif @file + "#{msg} in #{@file}" else - str = @message.to_s + msg end - - str end end + class ParseError < Puppet::Error + include ExternalFileError + end + + class ResourceError < Puppet::Error + include ExternalFileError + end + # An error class for when I don't know what happened. Automatically # prints a stack trace when in debug mode. class DevError < Puppet::Error end end diff --git a/lib/puppet/network/rights.rb b/lib/puppet/network/rights.rb index 086ac74ed..dda2c395c 100755 --- a/lib/puppet/network/rights.rb +++ b/lib/puppet/network/rights.rb @@ -1,274 +1,273 @@ require 'puppet/network/authstore' require 'puppet/error' module Puppet::Network # this exception is thrown when a request is not authenticated class AuthorizationError < Puppet::Error; end # Define a set of rights and who has access to them. # There are two types of rights: # * named rights (ie a common string) # * path based rights (which are matched on a longest prefix basis) class Rights # We basically just proxy directly to our rights. Each Right stores # its own auth abilities. [:allow, :deny, :restrict_method, :restrict_environment, :restrict_authenticated].each do |method| define_method(method) do |name, *args| if obj = self[name] obj.send(method, *args) else raise ArgumentError, "Unknown right '#{name}'" end end end # Check that name is allowed or not def allowed?(name, *args) !is_forbidden_and_why?(name, :node => args[0], :ip => args[1]) end def is_request_forbidden_and_why?(indirection, method, key, params) methods_to_check = if method == :head # :head is ok if either :find or :save is ok. [:find, :save] else [method] end authorization_failure_exceptions = methods_to_check.map do |method| is_forbidden_and_why?("/#{indirection}/#{key}", params.merge({:method => method})) end if authorization_failure_exceptions.include? nil # One of the methods we checked is ok, therefore this request is ok. nil else # Just need to return any of the failure exceptions. authorization_failure_exceptions.first end end def is_forbidden_and_why?(name, args = {}) res = :nomatch right = @rights.find do |acl| found = false # an acl can return :dunno, which means "I'm not qualified to answer your question, # please ask someone else". This is used when for instance an acl matches, but not for the # current rest method, where we might think some other acl might be more specific. if match = acl.match?(name) args[:match] = match if (res = acl.allowed?(args[:node], args[:ip], args)) != :dunno # return early if we're allowed return nil if res # we matched, select this acl found = true end end found end # if we end here, then that means we either didn't match # or failed, in any case will throw an error to the outside world if name =~ /^\// or right # we're a patch ACL, let's fail msg = "#{(args[:node].nil? ? args[:ip] : "#{args[:node]}(#{args[:ip]})")} access to #{name} [#{args[:method]}]" msg += " authenticated " if args[:authenticated] - - error = AuthorizationError.new("Forbidden request: #{msg}") if right - error.file = right.file - error.line = right.line + msg += " at #{right.file}:#{right.line}" end + + error = AuthorizationError.new("Forbidden request: #{msg}") else # there were no rights allowing/denying name # if name is not a path, let's throw raise ArgumentError, "Unknown namespace right '#{name}'" end error end def initialize @rights = [] end def [](name) @rights.find { |acl| acl == name } end def include?(name) @rights.include?(name) end def each @rights.each { |r| yield r.name,r } end # Define a new right to which access can be provided. def newright(name, line=nil, file=nil) add_right( Right.new(name, line, file) ) end private def add_right(right) if right.acl_type == :name and include?(right.key) raise ArgumentError, "Right '%s' already exists" end @rights << right sort_rights right end def sort_rights @rights.sort! end # Retrieve a right by name. def right(name) self[name] end # A right. class Right < Puppet::Network::AuthStore include Puppet::FileCollection::Lookup attr_accessor :name, :key, :acl_type attr_accessor :methods, :environment, :authentication ALL = [:save, :destroy, :find, :search] Puppet::Util.logmethods(self, true) def initialize(name, line, file) @methods = [] @environment = [] @authentication = true # defaults to authenticated @name = name @line = line || 0 @file = file case name when Symbol @acl_type = :name @key = name when /^\[(.+)\]$/ @acl_type = :name @key = $1.intern if name.is_a?(String) when /^\// @acl_type = :regex @key = Regexp.new("^" + Regexp.escape(name)) @methods = ALL when /^~/ # this is a regex @acl_type = :regex @name = name.gsub(/^~\s+/,'') @key = Regexp.new(@name) @methods = ALL else raise ArgumentError, "Unknown right type '#{name}'" end super() end def to_s "access[#{@name}]" end # There's no real check to do at this point def valid? true end def regex? acl_type == :regex end # does this right is allowed for this triplet? # if this right is too restrictive (ie we don't match this access method) # then return :dunno so that upper layers have a chance to try another right # tailored to the given method def allowed?(name, ip, args = {}) return :dunno if acl_type == :regex and not @methods.include?(args[:method]) return :dunno if acl_type == :regex and @environment.size > 0 and not @environment.include?(args[:environment]) return :dunno if acl_type == :regex and (@authentication and not args[:authenticated]) begin # make sure any capture are replaced if needed interpolate(args[:match]) if acl_type == :regex and args[:match] res = super(name,ip) ensure reset_interpolation if acl_type == :regex end res end # restrict this right to some method only def restrict_method(m) m = m.intern if m.is_a?(String) raise ArgumentError, "'#{m}' is not an allowed value for method directive" unless ALL.include?(m) # if we were allowing all methods, then starts from scratch if @methods === ALL @methods = [] end raise ArgumentError, "'#{m}' is already in the '#{name}' ACL" if @methods.include?(m) @methods << m end def restrict_environment(env) env = Puppet::Node::Environment.new(env) raise ArgumentError, "'#{env}' is already in the '#{name}' ACL" if @environment.include?(env) @environment << env end def restrict_authenticated(authentication) case authentication when "yes", "on", "true", true authentication = true when "no", "off", "false", false, "all" ,"any", :all, :any authentication = false else raise ArgumentError, "'#{name}' incorrect authenticated value: #{authentication}" end @authentication = authentication end def match?(key) # if we are a namespace compare directly return self.key == namespace_to_key(key) if acl_type == :name # otherwise match with the regex self.key.match(key) end def namespace_to_key(key) key = key.intern if key.is_a?(String) key end # this is where all the magic happens. # we're sorting the rights array with this scheme: # * namespace rights are all in front # * regex path rights are then all queued in file order def <=>(rhs) # move namespace rights at front return self.acl_type == :name ? -1 : 1 if self.acl_type != rhs.acl_type # sort by creation order (ie first match appearing in the file will win) # that is don't sort, in which case the sort algorithm will order in the # natural array order (ie the creation order) 0 end def ==(name) return(acl_type == :name ? self.key == namespace_to_key(name) : self.name == name.gsub(/^~\s+/,'')) end end end end diff --git a/lib/puppet/parser/ast.rb b/lib/puppet/parser/ast.rb index 8918d22e9..b24ee41ae 100644 --- a/lib/puppet/parser/ast.rb +++ b/lib/puppet/parser/ast.rb @@ -1,142 +1,142 @@ # the parent class for all of our syntactical objects require 'puppet' require 'puppet/util/autoload' require 'puppet/file_collection/lookup' # The base class for all of the objects that make up the parse trees. # Handles things like file name, line #, and also does the initialization # for all of the parameters of all of the child objects. class Puppet::Parser::AST # Do this so I don't have to type the full path in all of the subclasses AST = Puppet::Parser::AST include Puppet::FileCollection::Lookup include Puppet::Util::Errors include Puppet::Util::MethodHelper include Puppet::Util::Docs attr_accessor :parent, :scope def inspect "( #{self.class} #{self.to_s} #{@children.inspect} )" end # don't fetch lexer comment by default def use_docs self.class.use_docs end # allow our subclass to specify they want documentation class << self attr_accessor :use_docs def associates_doc self.use_docs = true end end # Does this ast object set something? If so, it gets evaluated first. def self.settor? if defined?(@settor) @settor else false end end # Evaluate the current object. Just a stub method, since the subclass # should override this method. # of the contained children and evaluates them in turn, returning a # list of all of the collected values, rejecting nil values def evaluate(*options) raise Puppet::DevError, "Did not override #evaluate in #{self.class}" end # Throw a parse error. def parsefail(message) self.fail(Puppet::ParseError, message) end # Wrap a statemp in a reusable way so we always throw a parse error. def parsewrap exceptwrap :type => Puppet::ParseError do yield end end # The version of the evaluate method that should be called, because it # correctly handles errors. It is critical to use this method because # it can enable you to catch the error where it happens, rather than # much higher up the stack. def safeevaluate(*options) # We duplicate code here, rather than using exceptwrap, because this # is called so many times during parsing. begin return self.evaluate(*options) rescue Puppet::Error => detail raise adderrorcontext(detail) rescue => detail - error = Puppet::Error.new(detail.to_s) + error = Puppet::ParseError.new(detail.to_s, nil, nil, detail) # We can't use self.fail here because it always expects strings, # not exceptions. raise adderrorcontext(error, detail) end end # Initialize the object. Requires a hash as the argument, and # takes each of the parameters of the hash and calls the settor # method for them. This is probably pretty inefficient and should # likely be changed at some point. def initialize(args) set_options(args) end # evaluate ourselves, and match def evaluate_match(value, scope) obj = self.safeevaluate(scope) obj = obj.downcase if obj.respond_to?(:downcase) value = value.downcase if value.respond_to?(:downcase) obj = Puppet::Parser::Scope.number?(obj) || obj value = Puppet::Parser::Scope.number?(value) || value # "" == undef for case/selector/if obj == value or (obj == "" and value == :undef) or (obj == :undef and value == "") end end # And include all of the AST subclasses. require 'puppet/parser/ast/arithmetic_operator' require 'puppet/parser/ast/astarray' require 'puppet/parser/ast/asthash' require 'puppet/parser/ast/boolean_operator' require 'puppet/parser/ast/branch' require 'puppet/parser/ast/caseopt' require 'puppet/parser/ast/casestatement' require 'puppet/parser/ast/collection' require 'puppet/parser/ast/collexpr' require 'puppet/parser/ast/comparison_operator' require 'puppet/parser/ast/definition' require 'puppet/parser/ast/else' require 'puppet/parser/ast/function' require 'puppet/parser/ast/hostclass' require 'puppet/parser/ast/ifstatement' require 'puppet/parser/ast/in_operator' require 'puppet/parser/ast/leaf' require 'puppet/parser/ast/match_operator' require 'puppet/parser/ast/minus' require 'puppet/parser/ast/node' require 'puppet/parser/ast/nop' require 'puppet/parser/ast/not' require 'puppet/parser/ast/relationship' require 'puppet/parser/ast/resource' require 'puppet/parser/ast/resource_defaults' require 'puppet/parser/ast/resource_instance' require 'puppet/parser/ast/resource_override' require 'puppet/parser/ast/resource_reference' require 'puppet/parser/ast/resourceparam' require 'puppet/parser/ast/selector' require 'puppet/parser/ast/tag' require 'puppet/parser/ast/vardef' diff --git a/lib/puppet/parser/compiler.rb b/lib/puppet/parser/compiler.rb index 172ffe67a..85687559f 100644 --- a/lib/puppet/parser/compiler.rb +++ b/lib/puppet/parser/compiler.rb @@ -1,489 +1,489 @@ require 'puppet/node' require 'puppet/resource/catalog' require 'puppet/util/errors' require 'puppet/resource/type_collection_helper' # Maintain a graph of scopes, along with a bunch of data # about the individual catalog we're compiling. class Puppet::Parser::Compiler include Puppet::Util include Puppet::Util::Errors include Puppet::Resource::TypeCollectionHelper def self.compile(node) # We get these from the environment and only cache them in a thread # variable for the duration of the compilation. If nothing else is using # the thread, though, we can leave 'em hanging round with no ill effects, # and this is safer than cleaning them at the end and assuming that will # stick until the next entry to this function. Thread.current[:known_resource_types] = nil Thread.current[:env_module_directories] = nil # ...and we actually do the compile now we have caching ready. new(node).compile.to_resource rescue => detail message = "#{detail} on node #{node.name}" Puppet.log_exception(detail, message) - raise Puppet::Error, message + raise Puppet::Error, message, detail.backtrace end attr_reader :node, :facts, :collections, :catalog, :resources, :relationships, :topscope # Add a collection to the global list. def add_collection(coll) @collections << coll end def add_relationship(dep) @relationships << dep end # Store a resource override. def add_override(override) # If possible, merge the override in immediately. if resource = @catalog.resource(override.ref) resource.merge(override) else # Otherwise, store the override for later; these # get evaluated in Resource#finish. @resource_overrides[override.ref] << override end end # Store a resource in our resource table. def add_resource(scope, resource) @resources << resource # Note that this will fail if the resource is not unique. @catalog.add_resource(resource) if resource.type.to_s.downcase != "class" && resource[:stage] raise ArgumentError, "Only classes can set 'stage'; normal resources like #{resource} cannot change run stage" end # Stages should not be inside of classes. They are always a # top-level container, regardless of where they appear in the # manifest. return if resource.type.to_s.downcase == "stage" # This adds a resource to the class it lexically appears in in the # manifest. if resource.type.to_s.downcase != "class" return @catalog.add_edge(scope.resource, resource) end end # Do we use nodes found in the code, vs. the external node sources? def ast_nodes? known_resource_types.nodes? end # Store the fact that we've evaluated a class def add_class(name) @catalog.add_class(name) unless name == "" end # Return a list of all of the defined classes. def classlist @catalog.classes end # Compiler our catalog. This mostly revolves around finding and evaluating classes. # This is the main entry into our catalog. def compile # Set the client's parameters into the top scope. set_node_parameters create_settings_scope evaluate_main evaluate_ast_node evaluate_node_classes evaluate_generators finish fail_on_unevaluated @catalog end # LAK:FIXME There are no tests for this. def delete_collection(coll) @collections.delete(coll) if @collections.include?(coll) end # Return the node's environment. def environment unless defined?(@environment) @environment = (node.environment and node.environment != "") ? node.environment : nil end Puppet::Node::Environment.current = @environment @environment end # Evaluate all of the classes specified by the node. def evaluate_node_classes evaluate_classes(@node.classes, @node_scope || topscope) end # Evaluate each specified class in turn. If there are any classes we can't # find, raise an error. This method really just creates resource objects # that point back to the classes, and then the resources are themselves # evaluated later in the process. # # Sometimes we evaluate classes with a fully qualified name already, in which # case, we tell scope.find_hostclass we've pre-qualified the name so it # doesn't need to search it's namespaces again. This gets around a weird # edge case of duplicate class names, one at top scope and one nested in our # namespace and the wrong one (or both!) getting selected. See ticket #13349 # for more detail. --jeffweiss 26 apr 2012 def evaluate_classes(classes, scope, lazy_evaluate = true, fqname = false) raise Puppet::DevError, "No source for scope passed to evaluate_classes" unless scope.source class_parameters = nil # if we are a param class, save the classes hash # and transform classes to be the keys if classes.class == Hash class_parameters = classes classes = classes.keys end classes.each do |name| # If we can find the class, then make a resource that will evaluate it. if klass = scope.find_hostclass(name, :assume_fqname => fqname) # If parameters are passed, then attempt to create a duplicate resource # so the appropriate error is thrown. if class_parameters resource = klass.ensure_in_catalog(scope, class_parameters[name] || {}) else next if scope.class_scope(klass) resource = klass.ensure_in_catalog(scope) end # If they've disabled lazy evaluation (which the :include function does), # then evaluate our resource immediately. resource.evaluate unless lazy_evaluate else raise Puppet::Error, "Could not find class #{name} for #{node.name}" end end end def evaluate_relationships @relationships.each { |rel| rel.evaluate(catalog) } end # Return a resource by either its ref or its type and title. def findresource(*args) @catalog.resource(*args) end def initialize(node, options = {}) @node = node options.each do |param, value| begin send(param.to_s + "=", value) rescue NoMethodError raise ArgumentError, "Compiler objects do not accept #{param}" end end initvars end # Create a new scope, with either a specified parent scope or # using the top scope. def newscope(parent, options = {}) parent ||= topscope options[:compiler] = self scope = Puppet::Parser::Scope.new(options) scope.parent = parent scope end # Return any overrides for the given resource. def resource_overrides(resource) @resource_overrides[resource.ref] end private # If ast nodes are enabled, then see if we can find and evaluate one. def evaluate_ast_node return unless ast_nodes? # Now see if we can find the node. astnode = nil @node.names.each do |name| break if astnode = known_resource_types.node(name.to_s.downcase) end unless (astnode ||= known_resource_types.node("default")) raise Puppet::ParseError, "Could not find default node or by name with '#{node.names.join(", ")}'" end # Create a resource to model this node, and then add it to the list # of resources. resource = astnode.ensure_in_catalog(topscope) resource.evaluate @node_scope = topscope.class_scope(astnode) end # Evaluate our collections and return true if anything returned an object. # The 'true' is used to continue a loop, so it's important. def evaluate_collections return false if @collections.empty? found_something = false exceptwrap do # We have to iterate over a dup of the array because # collections can delete themselves from the list, which # changes its length and causes some collections to get missed. @collections.dup.each do |collection| found_something = true if collection.evaluate end end found_something end # Make sure all of our resources have been evaluated into native resources. # We return true if any resources have, so that we know to continue the # evaluate_generators loop. def evaluate_definitions exceptwrap do !unevaluated_resources.each { |resource| resource.evaluate }.empty? end end # Iterate over collections and resources until we're sure that the whole # compile is evaluated. This is necessary because both collections # and defined resources can generate new resources, which themselves could # be defined resources. def evaluate_generators count = 0 loop do done = true # Call collections first, then definitions. done = false if evaluate_collections done = false if evaluate_definitions break if done count += 1 if count > 1000 raise Puppet::ParseError, "Somehow looped more than 1000 times while evaluating host catalog" end end end # Find and evaluate our main object, if possible. def evaluate_main @main = known_resource_types.find_hostclass([""], "") || known_resource_types.add(Puppet::Resource::Type.new(:hostclass, "")) @topscope.source = @main @main_resource = Puppet::Parser::Resource.new("class", :main, :scope => @topscope, :source => @main) @topscope.resource = @main_resource add_resource(@topscope, @main_resource) @main_resource.evaluate end # Make sure the entire catalog is evaluated. def fail_on_unevaluated fail_on_unevaluated_overrides fail_on_unevaluated_resource_collections end # If there are any resource overrides remaining, then we could # not find the resource they were supposed to override, so we # want to throw an exception. def fail_on_unevaluated_overrides remaining = [] @resource_overrides.each do |name, overrides| remaining.concat overrides end unless remaining.empty? fail Puppet::ParseError, "Could not find resource(s) %s for overriding" % remaining.collect { |o| o.ref }.join(", ") end end # Make sure we don't have any remaining collections that specifically # look for resources, because we want to consider those to be # parse errors. def fail_on_unevaluated_resource_collections remaining = [] @collections.each do |coll| # We're only interested in the 'resource' collections, # which result from direct calls of 'realize'. Anything # else is allowed not to return resources. # Collect all of them, so we have a useful error. if r = coll.resources if r.is_a?(Array) remaining += r else remaining << r end end end raise Puppet::ParseError, "Failed to realize virtual resources #{remaining.join(', ')}" unless remaining.empty? end # Make sure all of our resources and such have done any last work # necessary. def finish evaluate_relationships resources.each do |resource| # Add in any resource overrides. if overrides = resource_overrides(resource) overrides.each do |over| resource.merge(over) end # Remove the overrides, so that the configuration knows there # are none left. overrides.clear end resource.finish if resource.respond_to?(:finish) end add_resource_metaparams end def add_resource_metaparams unless main = catalog.resource(:class, :main) raise "Couldn't find main" end names = [] Puppet::Type.eachmetaparam do |name| next if Puppet::Parser::Resource.relationship_parameter?(name) names << name end data = {} catalog.walk(main, :out) do |source, target| if source_data = data[source] || metaparams_as_data(source, names) # only store anything in the data hash if we've actually got # data data[source] ||= source_data source_data.each do |param, value| target[param] = value if target[param].nil? end data[target] = source_data.merge(metaparams_as_data(target, names)) end target.tag(*(source.tags)) end end def metaparams_as_data(resource, params) data = nil params.each do |param| unless resource[param].nil? # Because we could be creating a hash for every resource, # and we actually probably don't often have any data here at all, # we're optimizing a bit by only creating a hash if there's # any data to put in it. data ||= {} data[param] = resource[param] end end data end # Set up all of our internal variables. def initvars # The list of objects that will available for export. @exported_resources = {} # The list of overrides. This is used to cache overrides on objects # that don't exist yet. We store an array of each override. @resource_overrides = Hash.new do |overs, ref| overs[ref] = [] end # The list of collections that have been created. This is a global list, # but they each refer back to the scope that created them. @collections = [] # The list of relationships to evaluate. @relationships = [] # For maintaining the relationship between scopes and their resources. @catalog = Puppet::Resource::Catalog.new(@node.name) @catalog.version = known_resource_types.version @catalog.environment = @node.environment.to_s # Create our initial scope and a resource that will evaluate main. @topscope = Puppet::Parser::Scope.new(:compiler => self) @main_stage_resource = Puppet::Parser::Resource.new("stage", :main, :scope => @topscope) @catalog.add_resource(@main_stage_resource) # local resource array to maintain resource ordering @resources = [] # Make sure any external node classes are in our class list if @node.classes.class == Hash @catalog.add_class(*@node.classes.keys) else @catalog.add_class(*@node.classes) end end # Set the node's parameters into the top-scope as variables. def set_node_parameters node.parameters.each do |param, value| @topscope[param] = value end # These might be nil. catalog.client_version = node.parameters["clientversion"] catalog.server_version = node.parameters["serverversion"] end def create_settings_scope unless settings_type = environment.known_resource_types.hostclass("settings") settings_type = Puppet::Resource::Type.new :hostclass, "settings" environment.known_resource_types.add(settings_type) end settings_resource = Puppet::Parser::Resource.new("class", "settings", :scope => @topscope) @catalog.add_resource(settings_resource) settings_type.evaluate_code(settings_resource) scope = @topscope.class_scope(settings_type) Puppet.settings.each do |name, setting| next if name.to_s == "name" scope[name.to_s] = environment[name] end end # Return an array of all of the unevaluated resources. These will be definitions, # which need to get evaluated into native resources. def unevaluated_resources # The order of these is significant for speed due to short-circuting resources.reject { |resource| resource.evaluated? or resource.virtual? or resource.builtin_type? } end end diff --git a/lib/puppet/parser/parser.rb b/lib/puppet/parser/parser.rb index 828a9ddf7..b431b4144 100644 --- a/lib/puppet/parser/parser.rb +++ b/lib/puppet/parser/parser.rb @@ -1,2553 +1,2552 @@ # # DO NOT MODIFY!!!! # This file is automatically generated by Racc 1.4.8 # from Racc grammer file "". # require 'racc/parser.rb' require 'puppet' require 'puppet/util/loadedfile' require 'puppet/parser/lexer' require 'puppet/parser/ast' module Puppet - class ParseError < Puppet::Error; end class ImportError < Racc::ParseError; end class AlreadyImportedError < ImportError; end end module Puppet module Parser class Parser < Racc::Parser module_eval(<<'...end grammar.ra/module_eval...', 'grammar.ra', 806) # It got too annoying having code in a file that needs to be compiled. require 'puppet/parser/parser_support' # Make emacs happy # Local Variables: # mode: ruby # End: ...end grammar.ra/module_eval... ##### State transition tables begin ### clist = [ '9,13,167,168,103,155,372,103,249,105,173,344,90,250,373,142,146,333', '275,314,-183,-177,106,154,137,139,143,145,40,94,48,1,315,10,12,174,21', '29,35,332,44,49,2,9,13,15,102,138,141,34,272,148,149,132,133,135,136', '331,140,144,33,9,13,306,-184,134,8,98,275,300,99,40,352,48,1,369,10', '12,368,21,29,35,339,44,49,2,9,13,15,33,297,298,34,399,64,117,297,298', '29,222,9,13,49,33,9,13,15,161,72,8,34,53,-112,-96,40,54,48,1,345,10', '12,33,21,29,35,154,44,49,2,9,13,15,275,362,-178,34,330,64,160,94,179', '29,75,9,13,49,33,294,-179,15,342,178,8,34,53,293,183,40,54,48,1,-129', '10,12,33,21,29,35,179,44,49,2,9,13,15,336,72,98,34,178,99,160,132,133', '183,140,144,140,144,33,9,13,134,53,134,8,155,54,-196,33,205,206,208', '189,191,64,195,197,200,239,243,-177,207,247,154,9,13,15,-176,199,202', '244,397,64,249,179,337,29,222,250,303,49,33,9,13,15,178,190,194,34,327', '183,326,40,283,48,1,282,10,12,33,21,29,35,119,44,49,2,9,13,15,179,140', '144,34,318,64,-123,334,134,29,222,178,94,49,33,53,183,15,340,54,8,34', '275,300,154,40,15,48,1,310,10,12,33,21,29,35,309,44,49,2,9,13,15,307', '305,323,34,384,369,86,98,368,-130,99,303,142,146,33,84,85,307,292,356', '8,137,139,143,145,40,287,48,1,33,10,12,284,21,29,35,359,44,49,2,9,13', '15,33,138,141,34,349,148,149,132,133,135,136,33,140,144,33,9,13,248', '365,134,8,-130,-130,-130,-130,40,-122,48,1,232,10,12,366,21,29,35,225', '44,49,2,9,13,15,188,105,374,34,172,64,132,133,123,29,222,140,144,49', '33,9,13,15,134,316,8,34,377,275,276,40,378,48,1,117,10,12,33,21,29,35', '-182,44,49,2,9,13,15,-180,382,-178,34,395,64,53,-181,-179,29,129,301', '44,49,33,275,300,15,278,385,8,34,275,276,-176,40,-183,48,1,386,10,12', '33,21,29,35,86,44,49,2,9,13,15,123,296,-229,34,297,298,121,9,13,388', '119,72,117,78,33,-40,-40,-40,-40,391,8,59,60,61,57,40,-178,48,1,65,10', '12,393,21,29,35,303,44,49,2,64,-179,15,109,29,75,34,107,49,92,56,400', '15,68,9,13,34,33,72,83,78,401,,8,9,13,,33,72,,78,,,,9,13,65,,72,,78', '-38,-38,-38,-38,,65,64,,,,29,75,,,49,65,64,,15,68,29,75,34,,49,83,64', ',15,68,29,75,34,33,49,83,,,15,68,9,13,34,33,72,83,78,,,,9,13,,33,113', ',-196,,,,9,13,65,,72,,78,-44,-44,-44,-44,,112,64,,,,29,75,,,49,65,64', ',15,68,29,75,34,,49,83,64,,15,,29,75,34,33,49,,,,15,68,9,13,34,33,72', '83,78,,,,9,13,,33,72,,78,,,,9,13,65,,72,,78,59,60,61,57,,65,64,,,,29', '75,,,49,65,64,,15,68,29,75,34,,49,83,64,,15,68,29,75,34,33,49,83,,,15', '68,9,13,34,33,72,83,78,,,,9,13,,33,72,,78,,,,9,13,65,,72,,,,148,149', '132,133,65,64,,140,144,29,75,,,49,134,64,,15,68,29,75,34,,49,83,64,', '15,68,29,75,34,33,49,83,,,15,,9,13,34,33,72,,78,,,,9,13,,33,72,,78,', ',,9,13,65,,72,,78,,,,,,65,64,,,,29,75,,,49,65,64,,15,68,29,75,34,,49', '83,64,,15,68,29,75,34,33,49,83,9,13,15,68,72,,34,33,,83,9,13,,,72,152', '78,33,,,9,13,,,72,,78,,,,9,13,65,64,72,,78,29,222,,,49,65,64,,15,,29', '75,34,,49,65,64,,15,68,29,75,34,33,49,83,64,,15,68,29,75,34,33,49,83', ',,15,68,9,13,34,33,72,83,78,,,,9,13,,33,72,,78,,,,9,13,65,,72,,78,,', ',,,65,64,,,,29,75,,,49,65,64,,15,68,29,75,34,,49,83,64,,15,68,29,75', '34,33,49,83,,,15,68,9,13,34,33,72,83,78,,,,9,13,,33,72,,78,,,,9,13,65', ',72,,78,,,,,,65,64,,,,29,75,,,49,65,64,,15,68,29,75,34,,49,83,64,,15', '68,29,75,34,33,49,83,,,15,68,9,13,34,33,72,83,78,,,,9,13,,33,72,,78', ',,,9,13,65,,72,,78,,,,,,65,64,,,,29,75,,,49,65,64,,15,68,29,75,34,,49', '83,64,,15,68,29,75,34,33,49,83,,,15,68,9,13,34,33,72,83,78,,,,9,13,', '33,72,,78,148,149,132,133,,65,,140,144,,,,,,134,65,64,9,13,,29,75,,', '49,,64,,15,68,29,75,34,,49,83,,,15,68,,,34,33,,83,,,,,64,53,,33,29,129', ',44,49,9,13,,15,72,,78,34,,,9,13,,,72,,78,,33,,,,65,,,,,,,,,,65,64,', ',,29,75,,,49,,64,,15,68,29,75,34,,49,83,285,,15,68,,,34,33,,83,142,146', ',,,,,33,,137,139,143,145,,,,,,9,13,9,13,,,72,371,78,,,,,,138,141,,,148', '149,132,133,135,136,65,140,144,,,,9,13,134,64,72,64,78,29,222,29,75', '49,,49,,15,,15,68,34,,34,65,,83,9,13,,,72,33,78,33,64,,,,29,75,,,49', ',,,15,68,65,,34,9,13,83,,72,,78,,64,,33,,29,75,,,49,9,13,,15,68,65,223', '34,,,83,,,,,,64,,33,,29,75,,,49,,,,15,68,9,13,34,64,72,83,78,29,222', ',,49,,33,,15,,,,34,9,13,65,279,72,,78,,,,33,,,64,,,,29,75,,,49,65,226', ',15,68,,,34,,,83,64,,,,29,75,,33,49,,,,15,68,,,34,9,13,83,,72,152,78', ',,,33,,,,,,,9,13,,,72,65,78,,,,,,,,,,64,,,,29,75,65,,49,,,,15,68,,,34', '64,,83,,29,75,,,49,,33,,15,68,9,13,34,,72,83,78,,,,9,13,,33,72,,78,', ',,9,13,65,,72,,78,,,,,,65,64,,,,29,75,,,49,65,64,,15,68,29,75,34,,49', '83,64,,15,68,29,75,34,33,49,83,,,15,68,9,13,34,33,72,83,78,,,,9,13,', '33,72,,78,,,,,,65,,,,,,,,,,65,64,,,,29,75,,,49,,64,,15,68,29,75,34,', '49,83,,230,15,68,,,34,33,,83,142,146,,,,,147,33,,137,139,143,145,,,142', '146,,,,,,,,137,139,143,145,,,,,138,141,,,148,149,132,133,135,136,,140', '144,,,138,141,,134,148,149,132,133,135,136,229,140,144,,,,,,134,142', '146,,,,,,,,137,139,143,145,,,142,146,,,,,,,,137,139,143,145,,,,,138', '141,,,148,149,132,133,135,136,,140,144,,,138,141,,134,148,149,132,133', '135,136,,140,144,142,146,,,,134,,,,137,139,143,145,,,142,146,,,,,,,', '137,139,143,145,,,,,138,141,,,148,149,132,133,135,136,,140,144,,,138', '141,,134,148,149,132,133,135,136,,140,144,142,146,,,,134,,,,137,139', '143,145,,,142,146,,,,,,,,137,139,143,145,,,,,138,141,,,148,149,132,133', '135,136,,140,144,,,138,141,,134,148,149,132,133,135,136,,140,144,142', '146,,,,134,,,,137,139,143,145,,,142,146,,,,,,,,137,139,143,145,,,,,', '141,,,148,149,132,133,135,136,,140,144,,,,,,134,148,149,132,133,135', '136,,140,144,142,146,,,,134,,,,137,139,143,145,,,142,146,,,,,,,,137', '139,143,145,,,,,,,,,148,149,132,133,135,136,,140,144,,,138,141,,134', '148,149,132,133,135,136,,140,144,142,146,,,,134,,,,137,139,143,145,', ',142,146,,,,,,,,137,139,143,145,,,,,138,141,,,148,149,132,133,135,136', ',140,144,,,138,141,,134,148,149,132,133,135,136,,140,144,142,146,,,', '134,,,,137,139,143,145,,,142,146,,,,,,,,137,139,143,145,,,,,138,141', ',,148,149,132,133,135,136,,140,144,,,138,141,,134,148,149,132,133,135', '136,,140,144,142,146,,,,134,,,,137,139,143,145,,,142,146,,,,,,,254,137', '139,143,145,,,,,,,146,,148,149,132,133,135,136,137,140,144,,,138,141', '146,134,148,149,132,133,135,136,137,140,144,,,,,,134,,146,,148,149,132', '133,135,136,137,140,144,,,,,146,134,148,149,132,133,135,136,137,140', '144,,,,,,134,,,,148,149,132,133,135,136,,140,144,,,,,,134,148,149,132', '133,135,136,,140,144,205,206,208,189,191,134,195,197,200,201,203,,207', '209,,,,,,199,202,204,205,206,208,189,191,,195,197,200,201,215,,207,209', ',190,194,,,199,202,204,205,206,208,189,191,,195,197,200,201,215,,207', '209,,190,194,,,199,202,204,205,206,208,189,191,,195,197,200,201,203', ',207,209,,190,194,,,199,202,204,205,206,208,189,191,,195,197,200,201', '215,,207,209,,190,194,,,199,202,204,205,206,208,189,191,,195,197,200', '201,203,,207,209,,190,194,,,199,202,204,148,149,132,133,135,136,,140', '144,,,,,,134,190,194,148,149,132,133,135,136,,140,144,,,,,,134' ] racc_action_table = arr = ::Array.new(2463, nil) idx = 0 clist.each do |str| str.split(',', -1).each do |i| arr[idx] = i.to_i unless i.empty? idx += 1 end end clist = [ '0,0,88,88,71,75,321,23,123,23,95,286,14,123,321,88,88,251,227,227,14', '71,23,75,88,88,88,88,0,21,0,0,227,0,0,95,0,0,0,250,0,0,0,147,147,0,23', '88,88,0,147,88,88,88,88,88,88,249,88,88,0,230,230,210,247,88,0,21,280', '280,21,147,295,147,147,318,147,147,318,147,147,147,280,147,147,147,393', '393,147,21,210,210,147,393,230,246,295,295,230,230,78,78,230,147,225', '225,230,78,225,147,230,287,243,244,393,287,393,393,287,393,393,230,393', '393,393,243,393,393,393,248,248,393,312,312,242,393,248,225,78,174,299', '225,225,282,282,225,393,177,240,225,282,299,393,225,12,177,299,248,12', '248,248,239,248,248,225,248,248,248,179,248,248,248,121,121,248,271', '121,174,248,179,174,282,273,273,179,255,255,273,273,248,372,372,255', '1,273,248,129,1,129,174,121,121,121,121,121,121,121,121,121,121,121', '238,121,121,129,391,391,121,237,121,121,121,391,372,334,102,276,372', '372,334,236,372,121,223,223,372,102,121,121,372,235,102,233,391,156', '391,391,156,391,391,372,391,391,391,232,391,391,391,229,229,391,106', '256,256,391,229,223,226,252,256,223,223,106,173,223,391,22,106,223,281', '22,391,223,281,281,222,229,22,229,229,219,229,229,223,229,229,229,215', '229,229,229,344,344,229,212,198,231,229,344,366,10,173,366,316,173,193', '231,231,229,10,10,302,175,304,229,231,231,231,231,344,166,344,344,173', '344,344,158,344,344,344,308,344,344,344,292,292,344,144,231,231,344', '292,231,231,231,231,231,231,140,231,231,344,363,363,122,314,231,344', '316,316,316,316,292,315,292,292,118,292,292,317,292,292,292,110,292', '292,292,26,26,292,104,100,325,292,92,363,274,274,87,363,363,274,274', '363,292,58,58,363,274,228,292,363,328,228,228,26,329,26,26,82,26,26', '363,26,26,26,81,26,26,26,383,383,26,79,335,77,26,383,58,58,74,73,58', '58,187,58,58,26,187,187,58,153,348,26,58,153,153,69,383,67,383,383,354', '383,383,58,383,383,383,64,383,383,383,270,270,383,55,184,362,383,184', '184,50,40,40,364,48,40,45,40,383,4,4,4,4,369,383,5,5,5,5,270,36,270', '270,40,270,270,373,270,270,270,376,270,270,270,40,30,270,28,40,40,270', '24,40,20,2,396,40,40,284,284,40,270,284,40,284,398,,270,13,13,,40,13', ',13,,,,145,145,284,,145,,145,47,47,47,47,,13,284,,,,284,284,,,284,145', '13,,284,284,13,13,284,,13,284,145,,13,13,145,145,13,284,145,13,,,145', '145,309,309,145,13,309,145,309,,,,35,35,,145,35,,35,,,,44,44,309,,44', ',44,11,11,11,11,,35,309,,,,309,309,,,309,44,35,,309,309,35,35,309,,35', '309,44,,35,,44,44,35,309,44,,,,44,44,368,368,44,35,368,44,368,,,,143', '143,,44,143,,143,,,,142,142,368,,142,,142,16,16,16,16,,143,368,,,,368', '368,,,368,142,143,,368,368,143,143,368,,143,368,142,,143,143,142,142', '143,368,142,143,,,142,142,65,65,142,143,65,142,65,,,,141,141,,142,141', ',141,,,,310,310,65,,310,,,,259,259,259,259,141,65,,259,259,65,65,,,65', '259,141,,65,65,141,141,65,,141,65,310,,141,141,310,310,141,65,310,141', ',,310,,68,68,310,141,68,,68,,,,139,139,,310,139,,139,,,,293,293,68,', '293,,293,,,,,,139,68,,,,68,68,,,68,293,139,,68,68,139,139,68,,139,68', '293,,139,139,293,293,139,68,293,139,326,326,293,293,326,,293,139,,293', '72,72,,,72,72,72,293,,,277,277,,,277,,277,,,,138,138,72,326,138,,138', '326,326,,,326,277,72,,326,,72,72,326,,72,138,277,,72,72,277,277,72,326', '277,72,138,,277,277,138,138,277,72,138,277,,,138,138,294,294,138,277', '294,138,294,,,,137,137,,138,137,,137,,,,332,332,294,,332,,332,,,,,,137', '294,,,,294,294,,,294,332,137,,294,294,137,137,294,,137,294,332,,137', '137,332,332,137,294,332,137,,,332,332,331,331,332,137,331,332,331,,', ',83,83,,332,83,,83,,,,84,84,331,,84,,84,,,,,,83,331,,,,331,331,,,331', '84,83,,331,331,83,83,331,,83,331,84,,83,83,84,84,83,331,84,83,,,84,84', '85,85,84,83,85,84,85,,,,86,86,,84,86,,86,,,,136,136,85,,136,,136,,,', ',,86,85,,,,85,85,,,85,136,86,,85,85,86,86,85,,86,85,136,,86,86,136,136', '86,85,136,86,,,136,136,8,8,136,86,8,136,8,,,,90,90,,136,90,,90,258,258', '258,258,,8,,258,258,,,,,,258,90,8,91,91,,8,8,,,8,,90,,8,8,90,90,8,,90', '8,,,90,90,,,90,8,,90,,,,,91,91,,90,91,91,,91,91,167,167,,91,167,,167', '91,,,305,305,,,305,,305,,91,,,,167,,,,,,,,,,305,167,,,,167,167,,,167', ',305,,167,167,305,305,167,,305,167,165,,305,305,,,305,167,,305,165,165', ',,,,,305,,165,165,165,165,,,,,,319,319,103,103,,,103,319,103,,,,,,165', '165,,,165,165,165,165,165,165,103,165,165,,,,135,135,165,319,135,103', '135,319,319,103,103,319,,103,,319,,103,103,319,,103,135,,103,134,134', ',,134,319,134,103,135,,,,135,135,,,135,,,,135,135,134,,135,155,155,135', ',155,,155,,134,,135,,134,134,,,134,109,109,,134,134,155,109,134,,,134', ',,,,,155,,134,,155,155,,,155,,,,155,155,154,154,155,109,154,155,154', '109,109,,,109,,155,,109,,,,109,112,112,154,154,112,,112,,,,109,,,154', ',,,154,154,,,154,112,112,,154,154,,,154,,,154,112,,,,112,112,,154,112', ',,,112,112,,,112,113,113,112,,113,113,113,,,,112,,,,,,,133,133,,,133', '113,133,,,,,,,,,,113,,,,113,113,133,,113,,,,113,113,,,113,133,,113,', '133,133,,,133,,113,,133,133,132,132,133,,132,133,132,,,,117,117,,133', '117,,117,,,,148,148,132,,148,,148,,,,,,117,132,,,,132,132,,,132,148', '117,,132,132,117,117,132,,117,132,148,,117,117,148,148,117,132,148,117', ',,148,148,149,149,148,117,149,148,149,,,,146,146,,148,146,,146,,,,,', '149,,,,,,,,,,146,149,,,,149,149,,,149,,146,,149,149,146,146,149,,146', '149,,116,146,146,,,146,149,,146,116,116,,,,,66,146,,116,116,116,116', ',,66,66,,,,,,,,66,66,66,66,,,,,116,116,,,116,116,116,116,116,116,,116', '116,,,66,66,,116,66,66,66,66,66,66,114,66,66,,,,,,66,114,114,,,,,,,', '114,114,114,114,,,343,343,,,,,,,,343,343,343,343,,,,,114,114,,,114,114', '114,114,114,114,,114,114,,,343,343,,114,343,343,343,343,343,343,,343', '343,163,163,,,,343,,,,163,163,163,163,,,164,164,,,,,,,,164,164,164,164', ',,,,163,163,,,163,163,163,163,163,163,,163,163,,,164,164,,163,164,164', '164,164,164,164,,164,164,170,170,,,,164,,,,170,170,170,170,,,338,338', ',,,,,,,338,338,338,338,,,,,170,170,,,170,170,170,170,170,170,,170,170', ',,338,338,,170,338,338,338,338,338,338,,338,338,261,261,,,,338,,,,261', '261,261,261,,,350,350,,,,,,,,350,350,350,350,,,,,,261,,,261,261,261', '261,261,261,,261,261,,,,,,261,350,350,350,350,350,350,,350,350,351,351', ',,,350,,,,351,351,351,351,,,151,151,,,,,,,,151,151,151,151,,,,,,,,,351', '351,351,351,351,351,,351,351,,,151,151,,351,151,151,151,151,151,151', ',151,151,357,357,,,,151,,,,357,357,357,357,,,360,360,,,,,,,,360,360', '360,360,,,,,357,357,,,357,357,357,357,357,357,,357,357,,,360,360,,357', '360,360,360,360,360,360,,360,360,379,379,,,,360,,,,379,379,379,379,', ',380,380,,,,,,,,380,380,380,380,,,,,379,379,,,379,379,379,379,379,379', ',379,379,,,380,380,,379,380,380,380,380,380,380,,380,380,264,264,,,', '380,,,,264,264,264,264,,,131,131,,,,,,,131,131,131,131,131,,,,,,,268', ',264,264,264,264,264,264,268,264,264,,,131,131,262,264,131,131,131,131', '131,131,262,131,131,,,,,,131,,265,,268,268,268,268,268,268,265,268,268', ',,,,266,268,262,262,262,262,262,262,266,262,262,,,,,,262,,,,265,265', '265,265,265,265,,265,265,,,,,,265,266,266,266,266,266,266,,266,266,327', '327,327,327,327,266,327,327,327,327,327,,327,327,,,,,,327,327,327,307', '307,307,307,307,,307,307,307,307,307,,307,307,,327,327,,,307,307,307', '107,107,107,107,107,,107,107,107,107,107,,107,107,,307,307,,,107,107', '107,105,105,105,105,105,,105,105,105,105,105,,105,105,,107,107,,,105', '105,105,188,188,188,188,188,,188,188,188,188,188,,188,188,,105,105,', ',188,188,188,303,303,303,303,303,,303,303,303,303,303,,303,303,,188', '188,,,303,303,303,260,260,260,260,260,260,,260,260,,,,,,260,303,303', '269,269,269,269,269,269,,269,269,,,,,,269' ] racc_action_check = arr = ::Array.new(2463, nil) idx = 0 clist.each do |str| str.split(',', -1).each do |i| arr[idx] = i.to_i unless i.empty? idx += 1 end end racc_action_pointer = [ -2, 157, 482, nil, 428, 434, nil, nil, 1137, nil, 304, 561, 118, 538, -3, nil, 628, nil, nil, nil, 525, 27, 240, 1, 515, nil, 385, nil, 496, nil, 494, nil, nil, nil, nil, 605, 479, nil, nil, nil, 481, nil, nil, nil, 615, 482, nil, 494, 484, nil, 474, nil, nil, nil, nil, 452, nil, nil, 403, nil, nil, nil, nil, nil, 463, 729, 1685, 436, 796, 434, nil, -2, 869, 418, 417, -1, nil, 412, 98, 410, nil, 403, 413, 1013, 1023, 1070, 1080, 374, -2, nil, 1147, 1171, 394, nil, nil, -2, nil, nil, nil, nil, 383, nil, 201, 1296, 382, 2335, 237, 2313, nil, 1391, 371, nil, 1441, 1491, 1736, nil, 1670, 1565, 364, nil, nil, 170, 356, -27, nil, nil, nil, nil, nil, 190, nil, 2171, 1555, 1508, 1349, 1324, 1090, 946, 889, 806, 296, 739, 682, 672, 285, 548, 1632, 41, 1575, 1622, nil, 1991, nil, 444, 1421, 1374, 235, nil, 325, nil, nil, nil, nil, 1796, 1811, 1261, 293, 1213, nil, nil, 1856, nil, nil, 271, 137, 314, nil, 129, nil, 144, nil, nil, nil, nil, 431, nil, nil, 437, 2357, nil, nil, nil, nil, 303, nil, nil, nil, nil, 294, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 41, nil, 292, nil, nil, 245, nil, nil, nil, 279, nil, nil, 261, 231, nil, 102, 244, 7, 402, 256, 59, 299, 252, 201, nil, 220, 218, 195, 188, 138, 125, nil, 111, 101, 90, nil, 89, 41, 127, 42, 24, -8, 256, nil, nil, 125, 202, nil, 1103, 706, 2378, 1916, 2204, nil, 2156, 2222, 2237, nil, 2189, 2395, 471, 166, nil, 127, 341, nil, 219, 879, nil, nil, 57, 272, 141, nil, 528, nil, 3, 75, nil, nil, nil, nil, 342, 816, 936, 47, nil, nil, nil, 116, nil, nil, 309, 2379, 314, 1223, nil, 2291, 331, 595, 749, nil, 121, nil, 340, 350, 305, 370, 44, 1294, nil, -6, nil, nil, nil, 383, 859, 2269, 403, 407, nil, 1003, 956, nil, 189, 409, nil, nil, 1871, nil, nil, nil, nil, 1751, 299, nil, nil, nil, 443, nil, 1931, 1976, nil, nil, 453, nil, nil, 2036, nil, nil, 2051, nil, 469, 360, 476, nil, 278, nil, 662, 487, nil, nil, 188, 500, nil, nil, 500, nil, nil, 2096, 2111, nil, nil, 428, nil, nil, nil, nil, nil, nil, nil, 213, nil, 84, nil, nil, 518, nil, 528, nil, nil, nil ] racc_action_default = [ -205, -242, -74, -19, -8, -20, -9, -185, -242, -124, -222, -10, -197, -242, -240, -98, -242, -11, -176, -12, -242, -242, -242, -177, -39, -13, -1, -181, -242, -129, -41, -14, -2, -230, -96, -97, -42, -15, -3, -180, -242, -43, -16, -182, -242, -45, -17, -6, -242, -184, -242, -18, -7, -197, -196, -205, -75, -49, -242, -46, -47, -48, -142, -141, -222, -242, -242, -240, -242, -59, -66, -60, -242, -63, -61, -97, -64, -58, -242, -67, -62, -68, -65, -242, -242, -242, -242, -205, -242, -125, -242, -242, -242, -204, -202, -205, -198, -200, -201, -203, -242, -73, -205, -242, -77, -109, -205, -119, -4, -242, -53, -54, -242, -242, -242, -134, -242, -242, -189, -186, -187, -109, -242, -206, -207, -40, -44, -37, -39, -97, -38, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -159, -56, -225, -242, -242, -242, -242, -234, -242, -238, -237, -233, -152, -106, -108, -242, -205, -242, -127, -126, -107, -36, 402, -242, -242, -242, -216, -242, -222, -205, -90, -89, -80, -97, -242, -81, -83, -242, -119, -25, -29, -27, -113, -228, -35, -23, -110, -31, -242, -33, -32, -114, -21, -112, -34, -28, -26, -22, -30, -24, -242, -117, -228, -118, -120, -112, -183, -177, -179, -242, -178, -170, -97, -242, -171, -242, -52, -242, -242, -242, -242, -242, -242, -94, -92, -242, -228, -100, -101, -114, -103, -104, -99, -97, -34, -102, -105, -24, -242, -215, -213, -242, -228, -210, -162, -149, -148, -143, -150, -151, -154, -161, -156, -144, -160, -158, -155, -145, -157, -153, -5, -242, -133, -146, -147, -226, -227, -242, -223, -123, -242, -242, -242, -231, -242, -239, -242, -242, -218, -128, -199, -217, -242, -242, -242, -242, -78, -86, -85, -242, -227, -131, -228, -229, -242, -242, -79, -229, -242, -242, -242, -173, -228, -55, -227, -50, -223, -242, -137, -242, -164, -242, -168, -241, -188, -242, -95, -109, -242, -242, -191, -242, -242, -208, -229, -242, -132, -224, -57, -122, -130, -235, -232, -236, -242, -221, -220, -219, -242, -195, -87, -88, -84, -82, -242, -111, -71, -115, -121, -72, -116, -175, -227, -242, -242, -51, -137, -136, -242, -242, -165, -163, -242, -242, -69, -93, -228, -70, -190, -214, -212, -211, -209, -242, -193, -194, -76, -174, -172, -135, -138, -242, -169, -242, -91, -192, -242, -140, -242, -167, -139, -166 ] racc_goto_table = [ 23, 55, 26, 27, 111, 108, 115, 253, 157, 219, 271, 120, 87, 89, 193, 234, 39, 224, 32, 93, 241, 211, 100, 96, 184, 304, 23, 320, 210, 27, 236, 101, 367, 212, 104, 153, 58, 125, 319, 127, 358, 353, 39, 221, 308, 43, 126, 91, 169, 122, 312, 325, 118, 233, 286, 95, 175, 363, 23, 291, 251, 27, 24, 252, 110, 347, 187, 130, 328, 45, 125, 43, 171, 124, 39, 227, 228, 156, 20, 126, 389, 166, nil, nil, 335, nil, nil, nil, 24, nil, nil, 23, 317, nil, 27, 45, nil, nil, nil, nil, 130, 295, 211, 43, nil, 124, nil, 39, nil, 217, nil, 329, 27, 176, 302, nil, 370, 280, 281, nil, 128, 238, 18, 219, 27, 39, nil, 45, nil, nil, 322, 311, nil, nil, 354, nil, 43, 39, 263, nil, nil, nil, 267, nil, 364, nil, nil, 23, 18, nil, 27, nil, nil, 128, 43, 348, nil, nil, nil, nil, 45, nil, nil, 39, nil, nil, 43, 289, nil, nil, nil, 93, 93, nil, nil, 290, 41, nil, nil, nil, 18, 14, nil, nil, 288, nil, nil, nil, nil, nil, 246, nil, 43, nil, 313, 324, nil, nil, nil, nil, nil, nil, 41, nil, nil, nil, nil, 14, 394, 24, nil, nil, 341, 18, nil, nil, 45, 355, 381, 322, 375, 211, nil, 217, 180, 241, 27, nil, 180, 23, 217, 18, 27, 27, 41, nil, 376, nil, nil, 39, nil, nil, nil, 237, nil, 39, 39, nil, 23, 108, nil, 27, nil, nil, 396, nil, 398, nil, nil, nil, nil, nil, nil, 219, 39, nil, nil, 41, 43, 18, 23, 387, 392, 27, 43, 43, nil, nil, nil, 361, nil, nil, nil, nil, nil, nil, 39, 346, nil, nil, 216, 24, 23, 43, nil, 27, nil, 245, 45, nil, nil, 180, nil, nil, nil, nil, nil, nil, 39, nil, 24, nil, nil, nil, nil, 43, nil, 45, nil, 217, nil, nil, 27, 41, nil, nil, 238, nil, 14, 27, nil, nil, 24, nil, 390, 39, nil, 43, nil, 45, nil, nil, 39, nil, 23, 18, 383, 27, nil, nil, nil, 18, 18, nil, 24, nil, nil, nil, nil, nil, 39, 45, 108, 217, 43, nil, 27, nil, nil, nil, 18, 43, 217, nil, nil, 27, nil, nil, nil, 39, nil, nil, nil, 23, nil, nil, 27, nil, 39, 43, nil, 23, 18, 23, 27, 246, 27, nil, nil, 39, nil, 30, nil, nil, 216, 41, 24, 39, 43, 39, 14, 216, nil, 45, 18, nil, nil, 43, nil, nil, nil, 180, nil, nil, 41, nil, nil, 30, 43, 14, nil, nil, nil, nil, nil, nil, 43, nil, 43, nil, nil, 18, nil, nil, nil, 24, 41, nil, 237, nil, nil, 14, 45, 24, nil, 24, 36, nil, nil, 30, 45, nil, 45, nil, nil, nil, 18, nil, 41, nil, nil, nil, nil, 14, nil, nil, nil, nil, nil, nil, nil, nil, 36, nil, nil, 18, nil, nil, nil, nil, nil, nil, 30, nil, 18, nil, nil, nil, nil, nil, 216, nil, 245, 181, nil, 18, nil, 181, nil, nil, 218, nil, nil, 18, 36, 18, nil, nil, nil, nil, 41, nil, 240, nil, nil, 14, nil, nil, nil, nil, nil, nil, nil, nil, 159, nil, nil, nil, nil, nil, nil, nil, nil, nil, 216, nil, nil, 36, 30, nil, nil, nil, nil, 216, nil, nil, nil, nil, nil, 41, nil, nil, nil, nil, 14, 220, nil, 41, nil, 41, nil, nil, 14, nil, 14, nil, nil, 242, nil, nil, 181, 66, nil, nil, nil, nil, 88, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 36, nil, nil, nil, nil, nil, nil, nil, nil, nil, 114, nil, nil, nil, 116, nil, nil, nil, nil, nil, nil, 218, nil, nil, nil, nil, nil, 30, 218, nil, nil, nil, nil, nil, nil, 131, nil, nil, 150, nil, nil, nil, nil, nil, nil, nil, 30, nil, nil, nil, nil, nil, nil, 162, 163, 164, 165, nil, nil, nil, 170, nil, nil, nil, nil, nil, nil, nil, 30, nil, nil, nil, nil, nil, nil, nil, 220, nil, nil, nil, nil, nil, 36, 220, nil, nil, nil, 231, nil, nil, 30, nil, nil, nil, nil, nil, nil, 181, nil, nil, nil, 36, 255, 256, 257, 258, 259, 260, 261, 262, nil, 264, 265, 266, nil, 268, 269, 218, 273, 274, nil, nil, nil, 36, 240, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 159, nil, 88, nil, nil, nil, nil, 30, nil, nil, 36, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 218, nil, nil, nil, nil, nil, nil, nil, nil, 218, nil, 220, nil, nil, nil, nil, nil, nil, 242, nil, 30, nil, nil, nil, nil, nil, nil, nil, 30, nil, 30, nil, nil, nil, nil, nil, 36, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 220, nil, nil, nil, nil, nil, nil, nil, nil, 220, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 36, nil, nil, nil, nil, nil, nil, nil, 36, nil, 36, 338, nil, nil, nil, nil, nil, nil, 343, nil, nil, nil, nil, nil, nil, nil, nil, 350, 351, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 357, nil, nil, nil, 360, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 379, 380, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 114 ] racc_goto_check = [ 35, 40, 2, 36, 31, 4, 62, 83, 86, 69, 5, 73, 40, 60, 43, 53, 38, 71, 3, 65, 37, 56, 35, 79, 48, 44, 35, 67, 48, 36, 43, 6, 63, 45, 47, 29, 23, 8, 66, 22, 59, 49, 38, 70, 44, 39, 10, 23, 61, 75, 72, 42, 74, 41, 76, 77, 78, 33, 35, 80, 81, 36, 24, 82, 30, 84, 29, 6, 44, 28, 8, 39, 22, 3, 38, 29, 29, 85, 1, 10, 63, 75, nil, nil, 44, nil, nil, nil, 24, nil, nil, 35, 5, nil, 36, 28, nil, nil, nil, nil, 6, 48, 56, 39, nil, 3, nil, 38, nil, 35, nil, 5, 36, 3, 45, nil, 67, 29, 29, nil, 24, 35, 34, 69, 36, 38, nil, 28, nil, nil, 69, 71, nil, nil, 44, nil, 39, 38, 65, nil, nil, nil, 65, nil, 44, nil, nil, 35, 34, nil, 36, nil, nil, 24, 39, 5, nil, nil, nil, nil, 28, nil, nil, 38, nil, nil, 39, 60, nil, nil, nil, 65, 65, nil, nil, 79, 27, nil, nil, nil, 34, 55, nil, nil, 3, nil, nil, nil, nil, nil, 28, nil, 39, nil, 31, 73, nil, nil, nil, nil, nil, nil, 27, nil, nil, nil, nil, 55, 44, 24, nil, nil, 86, 34, nil, nil, 28, 56, 83, 69, 53, 56, nil, 35, 34, 37, 36, nil, 34, 35, 35, 34, 36, 36, 27, nil, 43, nil, nil, 38, nil, nil, nil, 34, nil, 38, 38, nil, 35, 4, nil, 36, nil, nil, 5, nil, 5, nil, nil, nil, nil, nil, nil, 69, 38, nil, nil, 27, 39, 34, 35, 71, 69, 36, 39, 39, nil, nil, nil, 31, nil, nil, nil, nil, nil, nil, 38, 40, nil, nil, 55, 24, 35, 39, nil, 36, nil, 27, 28, nil, nil, 34, nil, nil, nil, nil, nil, nil, 38, nil, 24, nil, nil, nil, nil, 39, nil, 28, nil, 35, nil, nil, 36, 27, nil, nil, 35, nil, 55, 36, nil, nil, 24, nil, 62, 38, nil, 39, nil, 28, nil, nil, 38, nil, 35, 34, 2, 36, nil, nil, nil, 34, 34, nil, 24, nil, nil, nil, nil, nil, 38, 28, 4, 35, 39, nil, 36, nil, nil, nil, 34, 39, 35, nil, nil, 36, nil, nil, nil, 38, nil, nil, nil, 35, nil, nil, 36, nil, 38, 39, nil, 35, 34, 35, 36, 28, 36, nil, nil, 38, nil, 25, nil, nil, 55, 27, 24, 38, 39, 38, 55, 55, nil, 28, 34, nil, nil, 39, nil, nil, nil, 34, nil, nil, 27, nil, nil, 25, 39, 55, nil, nil, nil, nil, nil, nil, 39, nil, 39, nil, nil, 34, nil, nil, nil, 24, 27, nil, 34, nil, nil, 55, 28, 24, nil, 24, 26, nil, nil, 25, 28, nil, 28, nil, nil, nil, 34, nil, 27, nil, nil, nil, nil, 55, nil, nil, nil, nil, nil, nil, nil, nil, 26, nil, nil, 34, nil, nil, nil, nil, nil, nil, 25, nil, 34, nil, nil, nil, nil, nil, 55, nil, 27, 25, nil, 34, nil, 25, nil, nil, 25, nil, nil, 34, 26, 34, nil, nil, nil, nil, 27, nil, 25, nil, nil, 55, nil, nil, nil, nil, nil, nil, nil, nil, 26, nil, nil, nil, nil, nil, nil, nil, nil, nil, 55, nil, nil, 26, 25, nil, nil, nil, nil, 55, nil, nil, nil, nil, nil, 27, nil, nil, nil, nil, 55, 26, nil, 27, nil, 27, nil, nil, 55, nil, 55, nil, nil, 26, nil, nil, 25, 32, nil, nil, nil, nil, 32, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 26, nil, nil, nil, nil, nil, nil, nil, nil, nil, 32, nil, nil, nil, 32, nil, nil, nil, nil, nil, nil, 25, nil, nil, nil, nil, nil, 25, 25, nil, nil, nil, nil, nil, nil, 32, nil, nil, 32, nil, nil, nil, nil, nil, nil, nil, 25, nil, nil, nil, nil, nil, nil, 32, 32, 32, 32, nil, nil, nil, 32, nil, nil, nil, nil, nil, nil, nil, 25, nil, nil, nil, nil, nil, nil, nil, 26, nil, nil, nil, nil, nil, 26, 26, nil, nil, nil, 32, nil, nil, 25, nil, nil, nil, nil, nil, nil, 25, nil, nil, nil, 26, 32, 32, 32, 32, 32, 32, 32, 32, nil, 32, 32, 32, nil, 32, 32, 25, 32, 32, nil, nil, nil, 26, 25, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 26, nil, 32, nil, nil, nil, nil, 25, nil, nil, 26, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 25, nil, nil, nil, nil, nil, nil, nil, nil, 25, nil, 26, nil, nil, nil, nil, nil, nil, 26, nil, 25, nil, nil, nil, nil, nil, nil, nil, 25, nil, 25, nil, nil, nil, nil, nil, 26, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 26, nil, nil, nil, nil, nil, nil, nil, nil, 26, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 26, nil, nil, nil, nil, nil, nil, nil, 26, nil, 26, 32, nil, nil, nil, nil, nil, nil, 32, nil, nil, nil, nil, nil, nil, nil, nil, 32, 32, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 32, nil, nil, nil, 32, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 32, 32, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 32 ] racc_goto_pointer = [ nil, 78, 2, 18, -21, -137, 9, nil, -21, nil, -12, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, -19, 31, 62, 401, 456, 176, 69, -37, 29, -31, 573, -255, 122, 0, 3, -101, 16, 45, 0, -68, -182, -91, -168, -74, nil, 11, -78, -258, nil, nil, nil, -106, nil, 181, -86, nil, nil, -267, 0, -40, -34, -286, nil, -2, -192, -203, nil, -100, -66, -92, -173, -37, 4, -6, -112, 34, -39, 2, -115, -63, -60, -116, -222, -1, -70, nil ] racc_goto_default = [ nil, nil, 270, 182, 38, nil, 47, 52, 4, 6, 11, 17, 19, 25, 31, 37, 42, 46, 51, 3, 5, 192, 16, nil, 70, 73, 77, 80, 82, nil, nil, 63, 151, 277, 69, 71, 74, 76, 79, 81, 50, nil, nil, nil, nil, nil, 22, nil, nil, 185, 299, 186, 177, nil, 235, 67, 196, 198, 213, 214, nil, nil, nil, nil, 62, 7, nil, nil, 321, 28, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 97, nil, nil, nil, nil, nil, nil, 158 ] racc_reduce_table = [ 0, 0, :racc_error, 1, 70, :_reduce_none, 1, 70, :_reduce_none, 1, 71, :_reduce_3, 2, 71, :_reduce_4, 1, 74, :_reduce_5, 1, 73, :_reduce_none, 1, 73, :_reduce_none, 1, 73, :_reduce_none, 1, 73, :_reduce_none, 1, 73, :_reduce_none, 1, 73, :_reduce_none, 1, 73, :_reduce_none, 1, 73, :_reduce_none, 1, 73, :_reduce_none, 1, 73, :_reduce_none, 1, 73, :_reduce_none, 1, 73, :_reduce_none, 1, 73, :_reduce_none, 1, 73, :_reduce_none, 1, 73, :_reduce_none, 1, 90, :_reduce_none, 1, 90, :_reduce_none, 1, 90, :_reduce_none, 1, 90, :_reduce_none, 1, 90, :_reduce_none, 1, 90, :_reduce_none, 1, 90, :_reduce_none, 1, 90, :_reduce_none, 1, 90, :_reduce_none, 1, 90, :_reduce_none, 1, 90, :_reduce_none, 1, 90, :_reduce_none, 1, 90, :_reduce_none, 1, 90, :_reduce_none, 1, 90, :_reduce_none, 3, 89, :_reduce_36, 3, 89, :_reduce_37, 1, 91, :_reduce_none, 1, 91, :_reduce_none, 1, 91, :_reduce_none, 1, 91, :_reduce_none, 1, 91, :_reduce_none, 1, 91, :_reduce_none, 1, 91, :_reduce_none, 1, 91, :_reduce_none, 1, 92, :_reduce_none, 1, 92, :_reduce_none, 1, 92, :_reduce_none, 1, 92, :_reduce_none, 4, 83, :_reduce_50, 5, 83, :_reduce_51, 3, 83, :_reduce_52, 2, 83, :_reduce_53, 1, 99, :_reduce_54, 3, 99, :_reduce_55, 1, 98, :_reduce_56, 3, 98, :_reduce_57, 1, 100, :_reduce_none, 1, 100, :_reduce_none, 1, 100, :_reduce_none, 1, 100, :_reduce_none, 1, 100, :_reduce_none, 1, 100, :_reduce_none, 1, 100, :_reduce_none, 1, 100, :_reduce_none, 1, 100, :_reduce_none, 1, 100, :_reduce_none, 1, 100, :_reduce_none, 5, 75, :_reduce_69, 5, 75, :_reduce_70, 5, 75, :_reduce_71, 5, 87, :_reduce_72, 2, 76, :_reduce_73, 1, 115, :_reduce_74, 2, 115, :_reduce_75, 6, 77, :_reduce_76, 2, 77, :_reduce_77, 3, 116, :_reduce_78, 3, 116, :_reduce_79, 1, 117, :_reduce_none, 1, 117, :_reduce_none, 3, 117, :_reduce_82, 1, 118, :_reduce_none, 3, 118, :_reduce_84, 1, 119, :_reduce_85, 1, 119, :_reduce_86, 3, 120, :_reduce_87, 3, 120, :_reduce_88, 1, 121, :_reduce_none, 1, 121, :_reduce_none, 4, 122, :_reduce_91, 1, 110, :_reduce_92, 3, 110, :_reduce_93, 0, 111, :_reduce_none, 1, 111, :_reduce_none, 1, 108, :_reduce_96, 1, 103, :_reduce_97, 1, 104, :_reduce_98, 1, 123, :_reduce_none, 1, 123, :_reduce_none, 1, 123, :_reduce_none, 1, 123, :_reduce_none, 1, 123, :_reduce_none, 1, 123, :_reduce_none, 1, 123, :_reduce_none, 3, 78, :_reduce_106, 3, 78, :_reduce_107, 3, 88, :_reduce_108, 0, 112, :_reduce_109, 1, 112, :_reduce_110, 3, 112, :_reduce_111, 1, 126, :_reduce_none, 1, 126, :_reduce_none, 1, 126, :_reduce_none, 3, 125, :_reduce_115, 3, 127, :_reduce_116, 1, 128, :_reduce_none, 1, 128, :_reduce_none, 0, 114, :_reduce_119, 1, 114, :_reduce_120, 3, 114, :_reduce_121, 4, 107, :_reduce_122, 3, 107, :_reduce_123, 1, 95, :_reduce_124, 2, 95, :_reduce_125, 2, 129, :_reduce_126, 1, 130, :_reduce_127, 2, 130, :_reduce_128, 1, 105, :_reduce_129, 4, 93, :_reduce_130, 4, 93, :_reduce_131, 5, 81, :_reduce_132, 4, 81, :_reduce_133, 2, 80, :_reduce_134, 5, 131, :_reduce_135, 4, 131, :_reduce_136, 0, 132, :_reduce_none, 2, 132, :_reduce_138, 4, 132, :_reduce_139, 3, 132, :_reduce_140, 1, 101, :_reduce_none, 1, 101, :_reduce_none, 3, 101, :_reduce_143, 3, 101, :_reduce_144, 3, 101, :_reduce_145, 3, 101, :_reduce_146, 3, 101, :_reduce_147, 3, 101, :_reduce_148, 3, 101, :_reduce_149, 3, 101, :_reduce_150, 3, 101, :_reduce_151, 2, 101, :_reduce_152, 3, 101, :_reduce_153, 3, 101, :_reduce_154, 3, 101, :_reduce_155, 3, 101, :_reduce_156, 3, 101, :_reduce_157, 3, 101, :_reduce_158, 2, 101, :_reduce_159, 3, 101, :_reduce_160, 3, 101, :_reduce_161, 3, 101, :_reduce_162, 5, 79, :_reduce_163, 1, 135, :_reduce_164, 2, 135, :_reduce_165, 5, 136, :_reduce_166, 4, 136, :_reduce_167, 1, 137, :_reduce_168, 3, 137, :_reduce_169, 3, 96, :_reduce_170, 1, 139, :_reduce_none, 4, 139, :_reduce_172, 1, 141, :_reduce_none, 3, 141, :_reduce_174, 3, 140, :_reduce_175, 1, 138, :_reduce_none, 1, 138, :_reduce_none, 1, 138, :_reduce_none, 1, 138, :_reduce_none, 1, 138, :_reduce_none, 1, 138, :_reduce_none, 1, 138, :_reduce_none, 1, 138, :_reduce_none, 1, 138, :_reduce_184, 1, 138, :_reduce_none, 1, 142, :_reduce_186, 1, 143, :_reduce_none, 3, 143, :_reduce_188, 2, 82, :_reduce_189, 6, 84, :_reduce_190, 5, 84, :_reduce_191, 7, 85, :_reduce_192, 6, 85, :_reduce_193, 6, 86, :_reduce_194, 5, 86, :_reduce_195, 1, 109, :_reduce_196, 1, 109, :_reduce_197, 1, 146, :_reduce_198, 3, 146, :_reduce_199, 1, 148, :_reduce_200, 1, 149, :_reduce_201, 1, 149, :_reduce_202, 1, 149, :_reduce_203, 1, 149, :_reduce_none, 0, 72, :_reduce_205, 0, 150, :_reduce_206, 1, 144, :_reduce_none, 3, 144, :_reduce_208, 4, 144, :_reduce_209, 1, 151, :_reduce_none, 3, 151, :_reduce_211, 3, 152, :_reduce_212, 1, 152, :_reduce_213, 3, 152, :_reduce_214, 1, 152, :_reduce_215, 1, 147, :_reduce_none, 2, 147, :_reduce_217, 1, 145, :_reduce_none, 2, 145, :_reduce_219, 1, 153, :_reduce_none, 1, 153, :_reduce_none, 1, 94, :_reduce_222, 3, 106, :_reduce_223, 4, 106, :_reduce_224, 2, 106, :_reduce_225, 1, 102, :_reduce_none, 1, 102, :_reduce_none, 0, 113, :_reduce_none, 1, 113, :_reduce_229, 1, 134, :_reduce_230, 3, 133, :_reduce_231, 4, 133, :_reduce_232, 2, 133, :_reduce_233, 1, 154, :_reduce_none, 3, 154, :_reduce_235, 3, 155, :_reduce_236, 1, 156, :_reduce_237, 1, 156, :_reduce_238, 4, 124, :_reduce_239, 1, 97, :_reduce_none, 4, 97, :_reduce_241 ] racc_reduce_n = 242 racc_shift_n = 402 racc_token_table = { false => 0, :error => 1, :STRING => 2, :DQPRE => 3, :DQMID => 4, :DQPOST => 5, :LBRACK => 6, :RBRACK => 7, :LBRACE => 8, :RBRACE => 9, :SYMBOL => 10, :FARROW => 11, :COMMA => 12, :TRUE => 13, :FALSE => 14, :EQUALS => 15, :APPENDS => 16, :LESSEQUAL => 17, :NOTEQUAL => 18, :DOT => 19, :COLON => 20, :LLCOLLECT => 21, :RRCOLLECT => 22, :QMARK => 23, :LPAREN => 24, :RPAREN => 25, :ISEQUAL => 26, :GREATEREQUAL => 27, :GREATERTHAN => 28, :LESSTHAN => 29, :IF => 30, :ELSE => 31, :IMPORT => 32, :DEFINE => 33, :ELSIF => 34, :VARIABLE => 35, :CLASS => 36, :INHERITS => 37, :NODE => 38, :BOOLEAN => 39, :NAME => 40, :SEMIC => 41, :CASE => 42, :DEFAULT => 43, :AT => 44, :LCOLLECT => 45, :RCOLLECT => 46, :CLASSREF => 47, :NOT => 48, :OR => 49, :AND => 50, :UNDEF => 51, :PARROW => 52, :PLUS => 53, :MINUS => 54, :TIMES => 55, :DIV => 56, :LSHIFT => 57, :RSHIFT => 58, :UMINUS => 59, :MATCH => 60, :NOMATCH => 61, :REGEX => 62, :IN_EDGE => 63, :OUT_EDGE => 64, :IN_EDGE_SUB => 65, :OUT_EDGE_SUB => 66, :IN => 67, :UNLESS => 68 } racc_nt_base = 69 racc_use_result_var = true Racc_arg = [ racc_action_table, racc_action_check, racc_action_default, racc_action_pointer, racc_goto_table, racc_goto_check, racc_goto_default, racc_goto_pointer, racc_nt_base, racc_reduce_table, racc_token_table, racc_shift_n, racc_reduce_n, racc_use_result_var ] Racc_token_to_s_table = [ "$end", "error", "STRING", "DQPRE", "DQMID", "DQPOST", "LBRACK", "RBRACK", "LBRACE", "RBRACE", "SYMBOL", "FARROW", "COMMA", "TRUE", "FALSE", "EQUALS", "APPENDS", "LESSEQUAL", "NOTEQUAL", "DOT", "COLON", "LLCOLLECT", "RRCOLLECT", "QMARK", "LPAREN", "RPAREN", "ISEQUAL", "GREATEREQUAL", "GREATERTHAN", "LESSTHAN", "IF", "ELSE", "IMPORT", "DEFINE", "ELSIF", "VARIABLE", "CLASS", "INHERITS", "NODE", "BOOLEAN", "NAME", "SEMIC", "CASE", "DEFAULT", "AT", "LCOLLECT", "RCOLLECT", "CLASSREF", "NOT", "OR", "AND", "UNDEF", "PARROW", "PLUS", "MINUS", "TIMES", "DIV", "LSHIFT", "RSHIFT", "UMINUS", "MATCH", "NOMATCH", "REGEX", "IN_EDGE", "OUT_EDGE", "IN_EDGE_SUB", "OUT_EDGE_SUB", "IN", "UNLESS", "$start", "program", "statements_and_declarations", "nil", "statement_or_declaration", "statements", "resource", "virtualresource", "collection", "assignment", "casestatement", "ifstatement_begin", "unlessstatement", "import", "fstatement", "definition", "hostclass", "nodedef", "resourceoverride", "append", "relationship", "keyword", "relationship_side", "edge", "resourceref", "variable", "quotedtext", "selector", "hasharrayaccesses", "expressions", "funcvalues", "rvalue", "expression", "comma", "name", "type", "boolean", "array", "funcrvalue", "undef", "classname", "resourceinstances", "endsemi", "params", "endcomma", "anyparams", "at", "collectrhand", "collstatements", "collstatement", "colljoin", "collexpr", "colllval", "resourceinst", "resourcename", "hasharrayaccess", "param", "param_name", "addparam", "anyparam", "dqrval", "dqtail", "ifstatement", "else", "hash", "regex", "caseopts", "caseopt", "casevalues", "selectlhand", "svalues", "selectval", "sintvalues", "string", "strings", "argumentlist", "classparent", "hostnames", "nodeparent", "nodename", "hostname", "nothing", "arguments", "argument", "classnameordefault", "hashpairs", "hashpair", "key" ] Racc_debug_parser = false ##### State transition tables end ##### # reduce 0 omitted # reduce 1 omitted # reduce 2 omitted module_eval(<<'.,.,', 'grammar.ra', 34) def _reduce_3(val, _values, result) result = ast AST::ASTArray, :children => (val[0] ? [val[0]] : []) result end .,., module_eval(<<'.,.,', 'grammar.ra', 37) def _reduce_4(val, _values, result) if val[1] val[0].push(val[1]) end result = val[0] result end .,., module_eval(<<'.,.,', 'grammar.ra', 46) def _reduce_5(val, _values, result) val[0].each do |stmt| if stmt.is_a?(AST::TopLevelConstruct) error "Classes, definitions, and nodes may only appear at toplevel or inside other classes", \ :line => stmt.context[:line], :file => stmt.context[:file] end end result = val[0] result end .,., # reduce 6 omitted # reduce 7 omitted # reduce 8 omitted # reduce 9 omitted # reduce 10 omitted # reduce 11 omitted # reduce 12 omitted # reduce 13 omitted # reduce 14 omitted # reduce 15 omitted # reduce 16 omitted # reduce 17 omitted # reduce 18 omitted # reduce 19 omitted # reduce 20 omitted # reduce 21 omitted # reduce 22 omitted # reduce 23 omitted # reduce 24 omitted # reduce 25 omitted # reduce 26 omitted # reduce 27 omitted # reduce 28 omitted # reduce 29 omitted # reduce 30 omitted # reduce 31 omitted # reduce 32 omitted # reduce 33 omitted # reduce 34 omitted # reduce 35 omitted module_eval(<<'.,.,', 'grammar.ra', 89) def _reduce_36(val, _values, result) result = AST::Relationship.new(val[0], val[2], val[1][:value], ast_context) result end .,., module_eval(<<'.,.,', 'grammar.ra', 92) def _reduce_37(val, _values, result) result = AST::Relationship.new(val[0], val[2], val[1][:value], ast_context) result end .,., # reduce 38 omitted # reduce 39 omitted # reduce 40 omitted # reduce 41 omitted # reduce 42 omitted # reduce 43 omitted # reduce 44 omitted # reduce 45 omitted # reduce 46 omitted # reduce 47 omitted # reduce 48 omitted # reduce 49 omitted module_eval(<<'.,.,', 'grammar.ra', 107) def _reduce_50(val, _values, result) result = ast AST::Function, :name => val[0][:value], :line => val[0][:line], :arguments => val[2], :ftype => :statement result end .,., module_eval(<<'.,.,', 'grammar.ra', 114) def _reduce_51(val, _values, result) result = ast AST::Function, :name => val[0][:value], :line => val[0][:line], :arguments => val[2], :ftype => :statement result end .,., module_eval(<<'.,.,', 'grammar.ra', 120) def _reduce_52(val, _values, result) result = ast AST::Function, :name => val[0][:value], :line => val[0][:line], :arguments => AST::ASTArray.new({}), :ftype => :statement result end .,., module_eval(<<'.,.,', 'grammar.ra', 127) def _reduce_53(val, _values, result) result = ast AST::Function, :name => val[0][:value], :line => val[0][:line], :arguments => val[1], :ftype => :statement result end .,., module_eval(<<'.,.,', 'grammar.ra', 134) def _reduce_54(val, _values, result) result = aryfy(val[0]) result end .,., module_eval(<<'.,.,', 'grammar.ra', 137) def _reduce_55(val, _values, result) val[0].push(val[2]) result = val[0] result end .,., module_eval(<<'.,.,', 'grammar.ra', 141) def _reduce_56(val, _values, result) result = aryfy(val[0]) result end .,., module_eval(<<'.,.,', 'grammar.ra', 142) def _reduce_57(val, _values, result) result = val[0].push(val[2]) result end .,., # reduce 58 omitted # reduce 59 omitted # reduce 60 omitted # reduce 61 omitted # reduce 62 omitted # reduce 63 omitted # reduce 64 omitted # reduce 65 omitted # reduce 66 omitted # reduce 67 omitted # reduce 68 omitted module_eval(<<'.,.,', 'grammar.ra', 157) def _reduce_69(val, _values, result) @lexer.commentpop result = ast(AST::Resource, :type => val[0], :instances => val[2]) result end .,., module_eval(<<'.,.,', 'grammar.ra', 160) def _reduce_70(val, _values, result) # This is a deprecated syntax. error "All resource specifications require names" result end .,., module_eval(<<'.,.,', 'grammar.ra', 163) def _reduce_71(val, _values, result) # a defaults setting for a type @lexer.commentpop result = ast(AST::ResourceDefaults, :type => val[0].value, :parameters => val[2]) result end .,., module_eval(<<'.,.,', 'grammar.ra', 170) def _reduce_72(val, _values, result) @lexer.commentpop result = ast AST::ResourceOverride, :object => val[0], :parameters => val[2] result end .,., module_eval(<<'.,.,', 'grammar.ra', 177) def _reduce_73(val, _values, result) type = val[0] if (type == :exported and ! Puppet[:storeconfigs]) Puppet.warning addcontext("You cannot collect without storeconfigs being set") end error "Defaults are not virtualizable" if val[1].is_a? AST::ResourceDefaults method = type.to_s + "=" # Just mark our resource as exported and pass it through. val[1].send(method, true) result = val[1] result end .,., module_eval(<<'.,.,', 'grammar.ra', 193) def _reduce_74(val, _values, result) result = :virtual result end .,., module_eval(<<'.,.,', 'grammar.ra', 194) def _reduce_75(val, _values, result) result = :exported result end .,., module_eval(<<'.,.,', 'grammar.ra', 199) def _reduce_76(val, _values, result) @lexer.commentpop type = val[0].value.downcase args = {:type => type} if val[1].is_a?(AST::CollExpr) args[:query] = val[1] args[:query].type = type args[:form] = args[:query].form else args[:form] = val[1] end if args[:form] == :exported and ! Puppet[:storeconfigs] Puppet.warning addcontext("You cannot collect exported resources without storeconfigs being set; the collection will be ignored") end args[:override] = val[3] result = ast AST::Collection, args result end .,., module_eval(<<'.,.,', 'grammar.ra', 217) def _reduce_77(val, _values, result) type = val[0].value.downcase args = {:type => type } if val[1].is_a?(AST::CollExpr) args[:query] = val[1] args[:query].type = type args[:form] = args[:query].form else args[:form] = val[1] end if args[:form] == :exported and ! Puppet[:storeconfigs] Puppet.warning addcontext("You cannot collect exported resources without storeconfigs being set; the collection will be ignored") end result = ast AST::Collection, args result end .,., module_eval(<<'.,.,', 'grammar.ra', 235) def _reduce_78(val, _values, result) if val[1] result = val[1] result.form = :virtual else result = :virtual end result end .,., module_eval(<<'.,.,', 'grammar.ra', 243) def _reduce_79(val, _values, result) if val[1] result = val[1] result.form = :exported else result = :exported end result end .,., # reduce 80 omitted # reduce 81 omitted module_eval(<<'.,.,', 'grammar.ra', 256) def _reduce_82(val, _values, result) result = ast AST::CollExpr, :test1 => val[0], :oper => val[1], :test2 => val[2] result end .,., # reduce 83 omitted module_eval(<<'.,.,', 'grammar.ra', 261) def _reduce_84(val, _values, result) result = val[1] result.parens = true result end .,., module_eval(<<'.,.,', 'grammar.ra', 265) def _reduce_85(val, _values, result) result=val[0][:value] result end .,., module_eval(<<'.,.,', 'grammar.ra', 266) def _reduce_86(val, _values, result) result=val[0][:value] result end .,., module_eval(<<'.,.,', 'grammar.ra', 269) def _reduce_87(val, _values, result) result = ast AST::CollExpr, :test1 => val[0], :oper => val[1][:value], :test2 => val[2] #result = ast AST::CollExpr #result.push *val result end .,., module_eval(<<'.,.,', 'grammar.ra', 274) def _reduce_88(val, _values, result) result = ast AST::CollExpr, :test1 => val[0], :oper => val[1][:value], :test2 => val[2] #result = ast AST::CollExpr #result.push *val result end .,., # reduce 89 omitted # reduce 90 omitted module_eval(<<'.,.,', 'grammar.ra', 283) def _reduce_91(val, _values, result) result = ast AST::ResourceInstance, :title => val[0], :parameters => val[2] result end .,., module_eval(<<'.,.,', 'grammar.ra', 286) def _reduce_92(val, _values, result) result = aryfy(val[0]) result end .,., module_eval(<<'.,.,', 'grammar.ra', 288) def _reduce_93(val, _values, result) val[0].push val[2] result = val[0] result end .,., # reduce 94 omitted # reduce 95 omitted module_eval(<<'.,.,', 'grammar.ra', 296) def _reduce_96(val, _values, result) result = ast AST::Undef, :value => :undef result end .,., module_eval(<<'.,.,', 'grammar.ra', 300) def _reduce_97(val, _values, result) result = ast AST::Name, :value => val[0][:value], :line => val[0][:line] result end .,., module_eval(<<'.,.,', 'grammar.ra', 304) def _reduce_98(val, _values, result) result = ast AST::Type, :value => val[0][:value], :line => val[0][:line] result end .,., # reduce 99 omitted # reduce 100 omitted # reduce 101 omitted # reduce 102 omitted # reduce 103 omitted # reduce 104 omitted # reduce 105 omitted module_eval(<<'.,.,', 'grammar.ra', 316) def _reduce_106(val, _values, result) raise Puppet::ParseError, "Cannot assign to variables in other namespaces" if val[0][:value] =~ /::/ # this is distinct from referencing a variable variable = ast AST::Name, :value => val[0][:value], :line => val[0][:line] result = ast AST::VarDef, :name => variable, :value => val[2], :line => val[0][:line] result end .,., module_eval(<<'.,.,', 'grammar.ra', 322) def _reduce_107(val, _values, result) result = ast AST::VarDef, :name => val[0], :value => val[2] result end .,., module_eval(<<'.,.,', 'grammar.ra', 326) def _reduce_108(val, _values, result) variable = ast AST::Name, :value => val[0][:value], :line => val[0][:line] result = ast AST::VarDef, :name => variable, :value => val[2], :append => true, :line => val[0][:line] result end .,., module_eval(<<'.,.,', 'grammar.ra', 332) def _reduce_109(val, _values, result) result = ast AST::ASTArray result end .,., module_eval(<<'.,.,', 'grammar.ra', 334) def _reduce_110(val, _values, result) result = aryfy(val[0]) result end .,., module_eval(<<'.,.,', 'grammar.ra', 336) def _reduce_111(val, _values, result) val[0].push(val[2]) result = val[0] result end .,., # reduce 112 omitted # reduce 113 omitted # reduce 114 omitted module_eval(<<'.,.,', 'grammar.ra', 345) def _reduce_115(val, _values, result) result = ast AST::ResourceParam, :param => val[0][:value], :line => val[0][:line], :value => val[2] result end .,., module_eval(<<'.,.,', 'grammar.ra', 352) def _reduce_116(val, _values, result) result = ast AST::ResourceParam, :param => val[0][:value], :line => val[0][:line], :value => val[2], :add => true result end .,., # reduce 117 omitted # reduce 118 omitted module_eval(<<'.,.,', 'grammar.ra', 361) def _reduce_119(val, _values, result) result = ast AST::ASTArray result end .,., module_eval(<<'.,.,', 'grammar.ra', 363) def _reduce_120(val, _values, result) result = aryfy(val[0]) result end .,., module_eval(<<'.,.,', 'grammar.ra', 365) def _reduce_121(val, _values, result) val[0].push(val[2]) result = val[0] result end .,., module_eval(<<'.,.,', 'grammar.ra', 371) def _reduce_122(val, _values, result) result = ast AST::Function, :name => val[0][:value], :line => val[0][:line], :arguments => val[2], :ftype => :rvalue result end .,., module_eval(<<'.,.,', 'grammar.ra', 376) def _reduce_123(val, _values, result) result = ast AST::Function, :name => val[0][:value], :line => val[0][:line], :arguments => AST::ASTArray.new({}), :ftype => :rvalue result end .,., module_eval(<<'.,.,', 'grammar.ra', 382) def _reduce_124(val, _values, result) result = ast AST::String, :value => val[0][:value], :line => val[0][:line] result end .,., module_eval(<<'.,.,', 'grammar.ra', 383) def _reduce_125(val, _values, result) result = ast AST::Concat, :value => [ast(AST::String,val[0])]+val[1], :line => val[0][:line] result end .,., module_eval(<<'.,.,', 'grammar.ra', 385) def _reduce_126(val, _values, result) result = [val[0]] + val[1] result end .,., module_eval(<<'.,.,', 'grammar.ra', 387) def _reduce_127(val, _values, result) result = [ast(AST::String,val[0])] result end .,., module_eval(<<'.,.,', 'grammar.ra', 388) def _reduce_128(val, _values, result) result = [ast(AST::String,val[0])] + val[1] result end .,., module_eval(<<'.,.,', 'grammar.ra', 391) def _reduce_129(val, _values, result) result = ast AST::Boolean, :value => val[0][:value], :line => val[0][:line] result end .,., module_eval(<<'.,.,', 'grammar.ra', 395) def _reduce_130(val, _values, result) Puppet.warning addcontext("Deprecation notice: Resource references should now be capitalized") result = ast AST::ResourceReference, :type => val[0][:value], :line => val[0][:line], :title => val[2] result end .,., module_eval(<<'.,.,', 'grammar.ra', 398) def _reduce_131(val, _values, result) result = ast AST::ResourceReference, :type => val[0].value, :title => val[2] result end .,., module_eval(<<'.,.,', 'grammar.ra', 402) def _reduce_132(val, _values, result) @lexer.commentpop args = { :test => ast(AST::Not, :value => val[1]), :statements => val[3] } result = ast AST::IfStatement, args result end .,., module_eval(<<'.,.,', 'grammar.ra', 411) def _reduce_133(val, _values, result) @lexer.commentpop args = { :test => ast(AST::Not, :value => val[1]), :statements => ast(AST::Nop) } result = ast AST::IfStatement, args result end .,., module_eval(<<'.,.,', 'grammar.ra', 420) def _reduce_134(val, _values, result) result = val[1] result end .,., module_eval(<<'.,.,', 'grammar.ra', 424) def _reduce_135(val, _values, result) @lexer.commentpop args = { :test => val[0], :statements => val[2] } args[:else] = val[4] if val[4] result = ast AST::IfStatement, args result end .,., module_eval(<<'.,.,', 'grammar.ra', 435) def _reduce_136(val, _values, result) @lexer.commentpop args = { :test => val[0], :statements => ast(AST::Nop) } args[:else] = val[3] if val[3] result = ast AST::IfStatement, args result end .,., # reduce 137 omitted module_eval(<<'.,.,', 'grammar.ra', 448) def _reduce_138(val, _values, result) result = ast AST::Else, :statements => val[1] result end .,., module_eval(<<'.,.,', 'grammar.ra', 451) def _reduce_139(val, _values, result) @lexer.commentpop result = ast AST::Else, :statements => val[2] result end .,., module_eval(<<'.,.,', 'grammar.ra', 455) def _reduce_140(val, _values, result) @lexer.commentpop result = ast AST::Else, :statements => ast(AST::Nop) result end .,., # reduce 141 omitted # reduce 142 omitted module_eval(<<'.,.,', 'grammar.ra', 474) def _reduce_143(val, _values, result) result = ast AST::InOperator, :lval => val[0], :rval => val[2] result end .,., module_eval(<<'.,.,', 'grammar.ra', 477) def _reduce_144(val, _values, result) result = ast AST::MatchOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] result end .,., module_eval(<<'.,.,', 'grammar.ra', 480) def _reduce_145(val, _values, result) result = ast AST::MatchOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] result end .,., module_eval(<<'.,.,', 'grammar.ra', 483) def _reduce_146(val, _values, result) result = ast AST::ArithmeticOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] result end .,., module_eval(<<'.,.,', 'grammar.ra', 486) def _reduce_147(val, _values, result) result = ast AST::ArithmeticOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] result end .,., module_eval(<<'.,.,', 'grammar.ra', 489) def _reduce_148(val, _values, result) result = ast AST::ArithmeticOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] result end .,., module_eval(<<'.,.,', 'grammar.ra', 492) def _reduce_149(val, _values, result) result = ast AST::ArithmeticOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] result end .,., module_eval(<<'.,.,', 'grammar.ra', 495) def _reduce_150(val, _values, result) result = ast AST::ArithmeticOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] result end .,., module_eval(<<'.,.,', 'grammar.ra', 498) def _reduce_151(val, _values, result) result = ast AST::ArithmeticOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] result end .,., module_eval(<<'.,.,', 'grammar.ra', 501) def _reduce_152(val, _values, result) result = ast AST::Minus, :value => val[1] result end .,., module_eval(<<'.,.,', 'grammar.ra', 504) def _reduce_153(val, _values, result) result = ast AST::ComparisonOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] result end .,., module_eval(<<'.,.,', 'grammar.ra', 507) def _reduce_154(val, _values, result) result = ast AST::ComparisonOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] result end .,., module_eval(<<'.,.,', 'grammar.ra', 510) def _reduce_155(val, _values, result) result = ast AST::ComparisonOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] result end .,., module_eval(<<'.,.,', 'grammar.ra', 513) def _reduce_156(val, _values, result) result = ast AST::ComparisonOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] result end .,., module_eval(<<'.,.,', 'grammar.ra', 516) def _reduce_157(val, _values, result) result = ast AST::ComparisonOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] result end .,., module_eval(<<'.,.,', 'grammar.ra', 519) def _reduce_158(val, _values, result) result = ast AST::ComparisonOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] result end .,., module_eval(<<'.,.,', 'grammar.ra', 522) def _reduce_159(val, _values, result) result = ast AST::Not, :value => val[1] result end .,., module_eval(<<'.,.,', 'grammar.ra', 525) def _reduce_160(val, _values, result) result = ast AST::BooleanOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] result end .,., module_eval(<<'.,.,', 'grammar.ra', 528) def _reduce_161(val, _values, result) result = ast AST::BooleanOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] result end .,., module_eval(<<'.,.,', 'grammar.ra', 531) def _reduce_162(val, _values, result) result = val[1] result end .,., module_eval(<<'.,.,', 'grammar.ra', 535) def _reduce_163(val, _values, result) @lexer.commentpop result = ast AST::CaseStatement, :test => val[1], :options => val[3] result end .,., module_eval(<<'.,.,', 'grammar.ra', 539) def _reduce_164(val, _values, result) result = aryfy(val[0]) result end .,., module_eval(<<'.,.,', 'grammar.ra', 541) def _reduce_165(val, _values, result) val[0].push val[1] result = val[0] result end .,., module_eval(<<'.,.,', 'grammar.ra', 546) def _reduce_166(val, _values, result) @lexer.commentpop result = ast AST::CaseOpt, :value => val[0], :statements => val[3] result end .,., module_eval(<<'.,.,', 'grammar.ra', 549) def _reduce_167(val, _values, result) @lexer.commentpop result = ast( AST::CaseOpt, :value => val[0], :statements => ast(AST::ASTArray) ) result end .,., module_eval(<<'.,.,', 'grammar.ra', 559) def _reduce_168(val, _values, result) result = aryfy(val[0]) result end .,., module_eval(<<'.,.,', 'grammar.ra', 561) def _reduce_169(val, _values, result) val[0].push(val[2]) result = val[0] result end .,., module_eval(<<'.,.,', 'grammar.ra', 566) def _reduce_170(val, _values, result) result = ast AST::Selector, :param => val[0], :values => val[2] result end .,., # reduce 171 omitted module_eval(<<'.,.,', 'grammar.ra', 571) def _reduce_172(val, _values, result) @lexer.commentpop result = val[1] result end .,., # reduce 173 omitted module_eval(<<'.,.,', 'grammar.ra', 577) def _reduce_174(val, _values, result) if val[0].instance_of?(AST::ASTArray) val[0].push(val[2]) result = val[0] else result = ast AST::ASTArray, :children => [val[0],val[2]] end result end .,., module_eval(<<'.,.,', 'grammar.ra', 586) def _reduce_175(val, _values, result) result = ast AST::ResourceParam, :param => val[0], :value => val[2] result end .,., # reduce 176 omitted # reduce 177 omitted # reduce 178 omitted # reduce 179 omitted # reduce 180 omitted # reduce 181 omitted # reduce 182 omitted # reduce 183 omitted module_eval(<<'.,.,', 'grammar.ra', 598) def _reduce_184(val, _values, result) result = ast AST::Default, :value => val[0][:value], :line => val[0][:line] result end .,., # reduce 185 omitted module_eval(<<'.,.,', 'grammar.ra', 603) def _reduce_186(val, _values, result) result = [val[0][:value]] result end .,., # reduce 187 omitted module_eval(<<'.,.,', 'grammar.ra', 605) def _reduce_188(val, _values, result) result = val[0] += val[2] result end .,., module_eval(<<'.,.,', 'grammar.ra', 608) def _reduce_189(val, _values, result) val[1].each do |file| import(file) end result = nil result end .,., module_eval(<<'.,.,', 'grammar.ra', 618) def _reduce_190(val, _values, result) @lexer.commentpop result = Puppet::Parser::AST::Definition.new(classname(val[1]), ast_context(true).merge(:arguments => val[2], :code => val[4], :line => val[0][:line])) @lexer.indefine = false #} | DEFINE NAME argumentlist parent LBRACE RBRACE { result end .,., module_eval(<<'.,.,', 'grammar.ra', 626) def _reduce_191(val, _values, result) @lexer.commentpop result = Puppet::Parser::AST::Definition.new(classname(val[1]), ast_context(true).merge(:arguments => val[2], :line => val[0][:line])) @lexer.indefine = false result end .,., module_eval(<<'.,.,', 'grammar.ra', 634) def _reduce_192(val, _values, result) @lexer.commentpop # Our class gets defined in the parent namespace, not our own. @lexer.namepop result = Puppet::Parser::AST::Hostclass.new(classname(val[1]), ast_context(true).merge(:arguments => val[2], :parent => val[3], :code => val[5], :line => val[0][:line])) result end .,., module_eval(<<'.,.,', 'grammar.ra', 641) def _reduce_193(val, _values, result) @lexer.commentpop # Our class gets defined in the parent namespace, not our own. @lexer.namepop result = Puppet::Parser::AST::Hostclass.new(classname(val[1]), ast_context(true).merge(:arguments => val[2], :parent => val[3], :line => val[0][:line])) result end .,., module_eval(<<'.,.,', 'grammar.ra', 650) def _reduce_194(val, _values, result) @lexer.commentpop result = Puppet::Parser::AST::Node.new(val[1], ast_context(true).merge(:parent => val[2], :code => val[4], :line => val[0][:line])) result end .,., module_eval(<<'.,.,', 'grammar.ra', 655) def _reduce_195(val, _values, result) @lexer.commentpop result = Puppet::Parser::AST::Node.new(val[1], ast_context(true).merge(:parent => val[2], :line => val[0][:line])) result end .,., module_eval(<<'.,.,', 'grammar.ra', 659) def _reduce_196(val, _values, result) result = val[0][:value] result end .,., module_eval(<<'.,.,', 'grammar.ra', 660) def _reduce_197(val, _values, result) result = "class" result end .,., module_eval(<<'.,.,', 'grammar.ra', 665) def _reduce_198(val, _values, result) result = [result] result end .,., module_eval(<<'.,.,', 'grammar.ra', 668) def _reduce_199(val, _values, result) result = val[0] result << val[2] result end .,., module_eval(<<'.,.,', 'grammar.ra', 673) def _reduce_200(val, _values, result) result = ast AST::HostName, :value => val[0] result end .,., module_eval(<<'.,.,', 'grammar.ra', 676) def _reduce_201(val, _values, result) result = val[0][:value] result end .,., module_eval(<<'.,.,', 'grammar.ra', 677) def _reduce_202(val, _values, result) result = val[0][:value] result end .,., module_eval(<<'.,.,', 'grammar.ra', 678) def _reduce_203(val, _values, result) result = val[0][:value] result end .,., # reduce 204 omitted module_eval(<<'.,.,', 'grammar.ra', 682) def _reduce_205(val, _values, result) result = nil result end .,., module_eval(<<'.,.,', 'grammar.ra', 686) def _reduce_206(val, _values, result) result = ast AST::ASTArray, :children => [] result end .,., # reduce 207 omitted module_eval(<<'.,.,', 'grammar.ra', 691) def _reduce_208(val, _values, result) result = nil result end .,., module_eval(<<'.,.,', 'grammar.ra', 694) def _reduce_209(val, _values, result) result = val[1] result = [result] unless result[0].is_a?(Array) result end .,., # reduce 210 omitted module_eval(<<'.,.,', 'grammar.ra', 700) def _reduce_211(val, _values, result) result = val[0] result = [result] unless result[0].is_a?(Array) result << val[2] result end .,., module_eval(<<'.,.,', 'grammar.ra', 706) def _reduce_212(val, _values, result) Puppet.warning addcontext("Deprecation notice: must now include '$' in prototype") result = [val[0][:value], val[2]] result end .,., module_eval(<<'.,.,', 'grammar.ra', 710) def _reduce_213(val, _values, result) Puppet.warning addcontext("Deprecation notice: must now include '$' in prototype") result = [val[0][:value]] result end .,., module_eval(<<'.,.,', 'grammar.ra', 713) def _reduce_214(val, _values, result) result = [val[0][:value], val[2]] result end .,., module_eval(<<'.,.,', 'grammar.ra', 715) def _reduce_215(val, _values, result) result = [val[0][:value]] result end .,., # reduce 216 omitted module_eval(<<'.,.,', 'grammar.ra', 720) def _reduce_217(val, _values, result) result = val[1] result end .,., # reduce 218 omitted module_eval(<<'.,.,', 'grammar.ra', 725) def _reduce_219(val, _values, result) result = val[1] result end .,., # reduce 220 omitted # reduce 221 omitted module_eval(<<'.,.,', 'grammar.ra', 731) def _reduce_222(val, _values, result) result = ast AST::Variable, :value => val[0][:value], :line => val[0][:line] result end .,., module_eval(<<'.,.,', 'grammar.ra', 734) def _reduce_223(val, _values, result) result = val[1] result end .,., module_eval(<<'.,.,', 'grammar.ra', 735) def _reduce_224(val, _values, result) result = val[1] result end .,., module_eval(<<'.,.,', 'grammar.ra', 736) def _reduce_225(val, _values, result) result = ast AST::ASTArray result end .,., # reduce 226 omitted # reduce 227 omitted # reduce 228 omitted module_eval(<<'.,.,', 'grammar.ra', 742) def _reduce_229(val, _values, result) result = nil result end .,., module_eval(<<'.,.,', 'grammar.ra', 745) def _reduce_230(val, _values, result) result = ast AST::Regex, :value => val[0][:value] result end .,., module_eval(<<'.,.,', 'grammar.ra', 749) def _reduce_231(val, _values, result) if val[1].instance_of?(AST::ASTHash) result = val[1] else result = ast AST::ASTHash, { :value => val[1] } end result end .,., module_eval(<<'.,.,', 'grammar.ra', 756) def _reduce_232(val, _values, result) if val[1].instance_of?(AST::ASTHash) result = val[1] else result = ast AST::ASTHash, { :value => val[1] } end result end .,., module_eval(<<'.,.,', 'grammar.ra', 762) def _reduce_233(val, _values, result) result = ast AST::ASTHash result end .,., # reduce 234 omitted module_eval(<<'.,.,', 'grammar.ra', 767) def _reduce_235(val, _values, result) if val[0].instance_of?(AST::ASTHash) result = val[0].merge(val[2]) else result = ast AST::ASTHash, :value => val[0] result.merge(val[2]) end result end .,., module_eval(<<'.,.,', 'grammar.ra', 776) def _reduce_236(val, _values, result) result = ast AST::ASTHash, { :value => { val[0] => val[2] } } result end .,., module_eval(<<'.,.,', 'grammar.ra', 779) def _reduce_237(val, _values, result) result = val[0][:value] result end .,., module_eval(<<'.,.,', 'grammar.ra', 780) def _reduce_238(val, _values, result) result = val[0] result end .,., module_eval(<<'.,.,', 'grammar.ra', 783) def _reduce_239(val, _values, result) result = ast AST::HashOrArrayAccess, :variable => val[0][:value], :key => val[2] result end .,., # reduce 240 omitted module_eval(<<'.,.,', 'grammar.ra', 788) def _reduce_241(val, _values, result) result = ast AST::HashOrArrayAccess, :variable => val[0], :key => val[2] result end .,., def _reduce_none(val, _values, result) val[0] end end # class Parser end # module Parser end # module Puppet diff --git a/lib/puppet/parser/parser_support.rb b/lib/puppet/parser/parser_support.rb index 7888fe1dc..86d70720d 100644 --- a/lib/puppet/parser/parser_support.rb +++ b/lib/puppet/parser/parser_support.rb @@ -1,211 +1,192 @@ # I pulled this into a separate file, because I got # tired of rebuilding the parser.rb file all the time. class Puppet::Parser::Parser require 'puppet/parser/functions' require 'puppet/parser/files' require 'puppet/resource/type_collection' require 'puppet/resource/type_collection_helper' require 'puppet/resource/type' require 'monitor' AST = Puppet::Parser::AST include Puppet::Resource::TypeCollectionHelper attr_reader :version, :environment attr_accessor :files attr_accessor :lexer # Add context to a message; useful for error messages and such. def addcontext(message, obj = nil) obj ||= @lexer message += " on line #{obj.line}" if file = obj.file message += " in file #{file}" end message end # Create an AST array containing a single element def aryfy(arg) ast AST::ASTArray, :children => [arg] end # Create an AST object, and automatically add the file and line information if # available. def ast(klass, hash = {}) klass.new ast_context(klass.use_docs, hash[:line]).merge(hash) end def ast_context(include_docs = false, ast_line = nil) result = { :line => ast_line || lexer.line, :file => lexer.file } result[:doc] = lexer.getcomment(result[:line]) if include_docs result end # The fully qualifed name, with the full namespace. def classname(name) [@lexer.namespace, name].join("::").sub(/^::/, '') end def clear initvars end # Raise a Parse error. def error(message, options = {}) if brace = @lexer.expected message += "; expected '%s'" end except = Puppet::ParseError.new(message) except.line = options[:line] || @lexer.line except.file = options[:file] || @lexer.file raise except end def file @lexer.file end def file=(file) unless FileTest.exist?(file) unless file =~ /\.pp$/ file = file + ".pp" end end raise Puppet::AlreadyImportedError, "Import loop detected" if known_resource_types.watching_file?(file) watch_file(file) @lexer.file = file end [:hostclass, :definition, :node, :nodes?].each do |method| define_method(method) do |*args| known_resource_types.send(method, *args) end end def find_hostclass(namespace, name) known_resource_types.find_hostclass(namespace, name) end def find_definition(namespace, name) known_resource_types.find_definition(namespace, name) end def import(file) known_resource_types.loader.import(file, @lexer.file) end def initialize(env) # The environment is needed to know how to find the resource type collection. @environment = env.is_a?(String) ? Puppet::Node::Environment.new(env) : env initvars end # Initialize or reset all of our variables. def initvars @lexer = Puppet::Parser::Lexer.new end # Split an fq name into a namespace and name def namesplit(fullname) ary = fullname.split("::") n = ary.pop || "" ns = ary.join("::") return ns, n end def on_error(token,value,stack) if token == 0 # denotes end of file value = 'end of file' else value = "'#{value[:value]}'" end error = "Syntax error at #{value}" if brace = @lexer.expected error += "; expected '#{brace}'" end except = Puppet::ParseError.new(error) except.line = @lexer.line except.file = @lexer.file if @lexer.file raise except end # how should I do error handling here? def parse(string = nil) if self.file =~ /\.rb$/ main = parse_ruby_file else self.string = string if string begin @yydebug = false main = yyparse(@lexer,:scan) - rescue Racc::ParseError => except - error = Puppet::ParseError.new(except) - error.line = @lexer.line - error.file = @lexer.file - error.set_backtrace except.backtrace - raise error rescue Puppet::ParseError => except except.line ||= @lexer.line except.file ||= @lexer.file raise except - rescue Puppet::Error => except - # and this is a framework error - except.line ||= @lexer.line - except.file ||= @lexer.file - raise except - rescue Puppet::DevError => except - except.line ||= @lexer.line - except.file ||= @lexer.file - raise except rescue => except - error = Puppet::DevError.new(except.message) - error.line = @lexer.line - error.file = @lexer.file - error.set_backtrace except.backtrace - raise error + raise Puppet::ParseError.new(except.message, @lexer.file, @lexer.line, except) end end # Store the results as the top-level class. return Puppet::Parser::AST::Hostclass.new('', :code => main) ensure @lexer.clear end def parse_ruby_file # Execute the contents of the file inside its own "main" object so # that it can call methods in the resource type API. main_object = Puppet::DSL::ResourceTypeAPI.new main_object.instance_eval(File.read(self.file)) # Then extract any types that were created. Puppet::Parser::AST::ASTArray.new :children => main_object.instance_eval { @__created_ast_objects__ } end def string=(string) @lexer.string = string end def version known_resource_types.version end # Add a new file to be checked when we're checking to see if we should be # reparsed. This is basically only used by the TemplateWrapper to let the # parser know about templates that should be parsed. def watch_file(filename) known_resource_types.watch_file(filename) end end diff --git a/lib/puppet/property.rb b/lib/puppet/property.rb index f0d62f044..af4744428 100644 --- a/lib/puppet/property.rb +++ b/lib/puppet/property.rb @@ -1,372 +1,372 @@ # The virtual base class for properties, which are the self-contained building # blocks for actually doing work on the system. require 'puppet' require 'puppet/parameter' class Puppet::Property < Puppet::Parameter require 'puppet/property/ensure' # Because 'should' uses an array, we have a special method for handling # it. We also want to keep copies of the original values, so that # they can be retrieved and compared later when merging. attr_reader :shouldorig attr_writer :noop class << self attr_accessor :unmanaged attr_reader :name # Return array matching info, defaulting to just matching # the first value. def array_matching @array_matching ||= :first end # Set whether properties should match all values or just the first one. def array_matching=(value) value = value.intern if value.is_a?(String) raise ArgumentError, "Supported values for Property#array_matching are 'first' and 'all'" unless [:first, :all].include?(value) @array_matching = value end end # Look up a value's name, so we can find options and such. def self.value_name(name) if value = value_collection.match?(name) value.name end end # Retrieve an option set when a value was defined. def self.value_option(name, option) if value = value_collection.value(name) value.send(option) end end # Define a new valid value for a property. You must provide the value itself, # usually as a symbol, or a regex to match the value. # # The first argument to the method is either the value itself or a regex. # The second argument is an option hash; valid options are: # * :method: The name of the method to define. Defaults to 'set_'. # * :required_features: A list of features this value requires. # * :event: The event that should be returned when this value is set. # * :call: When to call any associated block. The default value # is `instead`, which means to call the value instead of calling the # provider. You can also specify `before` or `after`, which will # call both the block and the provider, according to the order you specify # (the `first` refers to when the block is called, not the provider). def self.newvalue(name, options = {}, &block) value = value_collection.newvalue(name, options, &block) define_method(value.method, &value.block) if value.method and value.block value end # Call the provider method. def call_provider(value) provider.send(self.class.name.to_s + "=", value) rescue NoMethodError self.fail "The #{provider.class.name} provider can not handle attribute #{self.class.name}" end # Call the dynamically-created method associated with our value, if # there is one. def call_valuemethod(name, value) if method = self.class.value_option(name, :method) and self.respond_to?(method) begin event = self.send(method) rescue Puppet::Error raise rescue => detail - error = Puppet::Error.new("Could not set '#{value}' on #{self.class.name}: #{detail}", @resource.line, @resource.file) + error = Puppet::ResourceError.new("Could not set '#{value}' on #{self.class.name}: #{detail}", @resource.line, @resource.file, detail) error.set_backtrace detail.backtrace Puppet.log_exception(detail, error.message) raise error end elsif block = self.class.value_option(name, :block) # FIXME It'd be better here to define a method, so that # the blocks could return values. self.instance_eval(&block) else devfail "Could not find method for value '#{name}'" end end # How should a property change be printed as a string? def change_to_s(current_value, newvalue) begin if current_value == :absent return "defined '#{name}' as #{self.class.format_value_for_display should_to_s(newvalue)}" elsif newvalue == :absent or newvalue == [:absent] return "undefined '#{name}' from #{self.class.format_value_for_display is_to_s(current_value)}" else return "#{name} changed #{self.class.format_value_for_display is_to_s(current_value)} to #{self.class.format_value_for_display should_to_s(newvalue)}" end rescue Puppet::Error, Puppet::DevError raise rescue => detail message = "Could not convert change '#{name}' to string: #{detail}" Puppet.log_exception(detail, message) raise Puppet::DevError, message end end # Figure out which event to return. def event_name value = self.should event_name = self.class.value_option(value, :event) and return event_name name == :ensure or return (name.to_s + "_changed").to_sym return (resource.type.to_s + case value when :present; "_created" when :absent; "_removed" else "_changed" end).to_sym end # Return a modified form of the resource event. def event resource.event :name => event_name, :desired_value => should, :property => self, :source_description => path end attr_reader :shadow # initialize our property def initialize(hash = {}) super if ! self.metaparam? and klass = Puppet::Type.metaparamclass(self.class.name) setup_shadow(klass) end end # Determine whether the property is in-sync or not. If @should is # not defined or is set to a non-true value, then we do not have # a valid value for it and thus consider the property to be in-sync # since we cannot fix it. Otherwise, we expect our should value # to be an array, and if @is matches any of those values, then # we consider it to be in-sync. # # Don't override this method. def safe_insync?(is) # If there is no @should value, consider the property to be in sync. return true unless @should # Otherwise delegate to the (possibly derived) insync? method. insync?(is) end def self.method_added(sym) raise "Puppet::Property#safe_insync? shouldn't be overridden; please override insync? instead" if sym == :safe_insync? end # This method may be overridden by derived classes if necessary # to provide extra logic to determine whether the property is in # sync. In most cases, however, only `property_matches?` needs to be # overridden to give the correct outcome - without reproducing all the array # matching logic, etc, found here. def insync?(is) self.devfail "#{self.class.name}'s should is not array" unless @should.is_a?(Array) # an empty array is analogous to no should values return true if @should.empty? # Look for a matching value, either for all the @should values, or any of # them, depending on the configuration of this property. if match_all? then # Emulate Array#== using our own comparison function. # A non-array was not equal to an array, which @should always is. return false unless is.is_a? Array # If they were different lengths, they are not equal. return false unless is.length == @should.length # Finally, are all the elements equal? In order to preserve the # behaviour of previous 2.7.x releases, we need to impose some fun rules # on "equality" here. # # Specifically, we need to implement *this* comparison: the two arrays # are identical if the is values are == the should values, or if the is # values are == the should values, stringified. # # This does mean that property equality is not commutative, and will not # work unless the `is` value is carefully arranged to match the should. return (is == @should or is == @should.map(&:to_s)) # When we stop being idiots about this, and actually have meaningful # semantics, this version is the thing we actually want to do. # # return is.zip(@should).all? {|a, b| property_matches?(a, b) } else return @should.any? {|want| property_matches?(is, want) } end end # Compare the current and desired value of a property in a property-specific # way. Invoked by `insync?`; this should be overridden if your property # has a different comparison type but does not actually differentiate the # overall insync? logic. def property_matches?(current, desired) # This preserves the older Puppet behaviour of doing raw and string # equality comparisons for all equality. I am not clear this is globally # desirable, but at least it is not a breaking change. --daniel 2011-11-11 current == desired or current == desired.to_s end # because the @should and @is vars might be in weird formats, # we need to set up a mechanism for pretty printing of the values # default to just the values, but this way individual properties can # override these methods def is_to_s(currentvalue) currentvalue end # Send a log message. def log(msg) Puppet::Util::Log.create( :level => resource[:loglevel], :message => msg, :source => self ) end # Should we match all values, or just the first? def match_all? self.class.array_matching == :all end # Execute our shadow's munge code, too, if we have one. def munge(value) self.shadow.munge(value) if self.shadow super end # each property class must define the name method, and property instances # do not change that name # this implicitly means that a given object can only have one property # instance of a given property class def name self.class.name end # for testing whether we should actually do anything def noop # This is only here to make testing easier. if @resource.respond_to?(:noop?) @resource.noop? else if defined?(@noop) @noop else Puppet[:noop] end end end # By default, call the method associated with the property name on our # provider. In other words, if the property name is 'gid', we'll call # 'provider.gid' to retrieve the current value. def retrieve provider.send(self.class.name) end # Set our value, using the provider, an associated block, or both. def set(value) # Set a name for looking up associated options like the event. name = self.class.value_name(value) call = self.class.value_option(name, :call) || :none if call == :instead call_valuemethod(name, value) elsif call == :none # They haven't provided a block, and our parent does not have # a provider, so we have no idea how to handle this. self.fail "#{self.class.name} cannot handle values of type #{value.inspect}" unless @resource.provider call_provider(value) else # LAK:NOTE 20081031 This is a change in behaviour -- you could # previously specify :call => [;before|:after], which would call # the setter *in addition to* the block. I'm convinced this # was never used, and it makes things unecessarily complicated. # If you want to specify a block and still call the setter, then # do so in the block. devfail "Cannot use obsolete :call value '#{call}' for property '#{self.class.name}'" end end # If there's a shadowing metaparam, instantiate it now. # This allows us to create a property or parameter with the # same name as a metaparameter, and the metaparam will only be # stored as a shadow. def setup_shadow(klass) @shadow = klass.new(:resource => self.resource) end # Only return the first value def should return nil unless defined?(@should) self.devfail "should for #{self.class.name} on #{resource.name} is not an array" unless @should.is_a?(Array) if match_all? return @should.collect { |val| self.unmunge(val) } else return self.unmunge(@should[0]) end end # Set the should value. def should=(values) values = [values] unless values.is_a?(Array) @shouldorig = values values.each { |val| validate(val) } @should = values.collect { |val| self.munge(val) } end def should_to_s(newvalue) [newvalue].flatten.join(" ") end def sync devfail "Got a nil value for should" unless should set(should) end # Verify that the passed value is valid. # If the developer uses a 'validate' hook, this method will get overridden. def unsafe_validate(value) super validate_features_per_value(value) end # Make sure that we've got all of the required features for a given value. def validate_features_per_value(value) if features = self.class.value_option(self.class.value_name(value), :required_features) features = Array(features) needed_features = features.collect { |f| f.to_s }.join(", ") raise ArgumentError, "Provider must have features '#{needed_features}' to set '#{self.class.name}' to '#{value}'" unless provider.satisfies?(features) end end # Just return any should value we might have. def value self.should end # Match the Parameter interface, but we really just use 'should' internally. # Note that the should= method does all of the validation and such. def value=(value) self.should = value end end diff --git a/lib/puppet/util/errors.rb b/lib/puppet/util/errors.rb index 5a7a7630b..fd9b98864 100644 --- a/lib/puppet/util/errors.rb +++ b/lib/puppet/util/errors.rb @@ -1,63 +1,62 @@ # Some helper methods for throwing errors. module Puppet::Util::Errors # Throw a dev error. def devfail(msg) self.fail(Puppet::DevError, msg) end # Add line and file info if available and appropriate. def adderrorcontext(error, other = nil) - error.line ||= self.line if self.respond_to?(:line) and self.line - error.file ||= self.file if self.respond_to?(:file) and self.file + error.line ||= self.line if error.respond_to?(:line=) and self.respond_to?(:line) and self.line + error.file ||= self.file if error.respond_to?(:file=) and self.respond_to?(:file) and self.file - error.set_backtrace other.backtrace if other and other.respond_to?(:backtrace) + error.set_backtrace(other.backtrace) if other and other.respond_to?(:backtrace) error end def error_context if file and line " at #{file}:#{line}" elsif line " at line #{line}" elsif file " in #{file}" else "" end end # Wrap a call in such a way that we always throw the right exception and keep # as much context as possible. def exceptwrap(options = {}) options[:type] ||= Puppet::DevError begin return yield rescue Puppet::Error => detail raise adderrorcontext(detail) rescue => detail message = options[:message] || "#{self.class} failed with error #{detail.class}: #{detail}" error = options[:type].new(message) # We can't use self.fail here because it always expects strings, # not exceptions. raise adderrorcontext(error, detail) end retval end # Throw an error, defaulting to a Puppet::Error. def fail(*args) if args[0].is_a?(Class) type = args.shift else type = Puppet::Error end error = adderrorcontext(type.new(args.join(" "))) raise error end end - diff --git a/lib/puppet/util/logging.rb b/lib/puppet/util/logging.rb index 803b402ee..8990e241e 100644 --- a/lib/puppet/util/logging.rb +++ b/lib/puppet/util/logging.rb @@ -1,135 +1,143 @@ # A module to make logging a bit easier. require 'puppet/util/log' require 'puppet/error' module Puppet::Util::Logging def send_log(level, message) Puppet::Util::Log.create({:level => level, :source => log_source, :message => message}.merge(log_metadata)) end # Create a method for each log level. Puppet::Util::Log.eachlevel do |level| define_method(level) do |args| args = args.join(" ") if args.is_a?(Array) send_log(level, args) end end # Log an exception via Puppet.err. Will also log the backtrace if Puppet[:trace] is set. # Parameters: # [exception] an Exception to log # [message] an optional String overriding the message to be logged; by default, we log Exception.message. # If you pass a String here, your string will be logged instead. You may also pass nil if you don't # wish to log a message at all; in this case it is likely that you are only calling this method in order # to take advantage of the backtrace logging. - # [options] supported options: - # :force_console => if true, will ensure that the error is written to the console, even if the console is not - # on the configured list of logging destinations def log_exception(exception, message = :default, options = {}) + err(format_exception(exception, message, Puppet[:trace] || options[:trace])) + end + + def format_exception(exception, message = :default, trace = true) + arr = [] case message - when :default - err(exception.message) - when nil - # don't log anything if they passed a nil; they are just calling for the optional backtrace logging - else - err(message) + when :default + arr << exception.message + when nil + # don't log anything if they passed a nil; they are just calling for the optional backtrace logging + else + arr << message end - err(Puppet::Util.pretty_backtrace(exception.backtrace)) if Puppet[:trace] && exception.backtrace + if trace and exception.backtrace + arr << Puppet::Util.pretty_backtrace(exception.backtrace) + end + if exception.respond_to?(:original) and exception.original + arr << "Wrapped exception:" + arr << format_exception(exception.original, :default, trace) + end + arr.flatten.join("\n") end - def log_and_raise(exception, message) log_exception(exception, message) - raise Puppet::Error.new(message + "\n" + exception) + raise exception, message + "\n" + exception, exception.backtrace end class DeprecationWarning < Exception; end # Log a warning indicating that the code path is deprecated. Note that this method keeps track of the # offending lines of code that triggered the deprecation warning, and will only log a warning once per # offending line of code. It will also stop logging deprecation warnings altogether after 100 unique # deprecation warnings have been logged. # Parameters: # [message] The message to log (logs via ) def deprecation_warning(message) $deprecation_warnings ||= {} if $deprecation_warnings.length < 100 then offender = get_deprecation_offender() if (! $deprecation_warnings.has_key?(offender)) then $deprecation_warnings[offender] = message warning("#{message}\n (at #{offender})") end end end def get_deprecation_offender() # we have to put this in its own method to simplify testing; we need to be able to mock the offender results in # order to test this class, and our framework does not appear to enjoy it if you try to mock Kernel.caller # # let's find the offending line; we need to jump back up the stack a few steps to find the method that called # the deprecated method caller()[2] end def clear_deprecation_warnings $deprecation_warnings.clear if $deprecation_warnings end # TODO: determine whether there might be a potential use for adding a puppet configuration option that would # enable this deprecation logging. # utility method that can be called, e.g., from spec_helper config.after, when tracking down calls to deprecated # code. # Parameters: # [deprecations_file] relative or absolute path of a file to log the deprecations to # [pattern] (default nil) if specified, will only log deprecations whose message matches the provided pattern def log_deprecations_to_file(deprecations_file, pattern = nil) # this method may get called lots and lots of times (e.g., from spec_helper config.after) without the global # list of deprecation warnings being cleared out. We don't want to keep logging the same offenders over and over, # so, we need to keep track of what we've logged. # # It'd be nice if we could just clear out the list of deprecation warnings, but then the very next spec might # find the same offender, and we'd end up logging it again. $logged_deprecation_warnings ||= {} File.open(deprecations_file, "a") do |f| if ($deprecation_warnings) then $deprecation_warnings.each do |offender, message| if (! $logged_deprecation_warnings.has_key?(offender)) then $logged_deprecation_warnings[offender] = true if ((pattern.nil?) || (message =~ pattern)) then f.puts(message) f.puts(offender) f.puts() end end end end end end private def is_resource? defined?(Puppet::Type) && is_a?(Puppet::Type) end def is_resource_parameter? defined?(Puppet::Parameter) && is_a?(Puppet::Parameter) end def log_metadata [:file, :line, :tags].inject({}) do |result, attr| result[attr] = send(attr) if respond_to?(attr) result end end def log_source # We need to guard the existence of the constants, since this module is used by the base Puppet module. (is_resource? or is_resource_parameter?) and respond_to?(:path) and return path.to_s to_s end end diff --git a/lib/puppet/util/settings.rb b/lib/puppet/util/settings.rb index 6deab0c2a..fc558f73f 100644 --- a/lib/puppet/util/settings.rb +++ b/lib/puppet/util/settings.rb @@ -1,1283 +1,1283 @@ require 'puppet' require 'sync' require 'getoptlong' require 'puppet/util/loadedfile' require 'puppet/util/command_line/puppet_option_parser' class Puppet::SettingsError < Puppet::Error end # The class for handling configuration files. class Puppet::Util::Settings include Enumerable require 'puppet/util/settings/string_setting' require 'puppet/util/settings/file_setting' require 'puppet/util/settings/directory_setting' require 'puppet/util/settings/path_setting' require 'puppet/util/settings/boolean_setting' # local reference for convenience PuppetOptionParser = Puppet::Util::CommandLine::PuppetOptionParser attr_accessor :files attr_reader :timer READ_ONLY_SETTINGS = [:run_mode] # These are the settings that every app is required to specify; there are reasonable defaults defined in application.rb. REQUIRED_APP_SETTINGS = [:run_mode, :logdir, :confdir, :vardir] # This method is intended for puppet internal use only; it is a convenience method that # returns reasonable application default settings values for a given run_mode. def self.app_defaults_for_run_mode(run_mode) { :name => run_mode.to_s, :run_mode => run_mode.name, :confdir => run_mode.conf_dir, :vardir => run_mode.var_dir, :rundir => run_mode.run_dir, :logdir => run_mode.log_dir, } end def self.default_global_config_dir() Puppet.features.microsoft_windows? ? File.join(Dir::COMMON_APPDATA, "PuppetLabs", "puppet", "etc") : "/etc/puppet" end def self.default_user_config_dir() "~/.puppet" end def self.default_global_var_dir() Puppet.features.microsoft_windows? ? File.join(Dir::COMMON_APPDATA, "PuppetLabs", "puppet", "var") : "/var/lib/puppet" end def self.default_user_var_dir() "~/.puppet/var" end def self.default_config_file_name() "puppet.conf" end # Retrieve a config value def [](param) value(param) end # Set a config value. This doesn't set the defaults, it sets the value itself. def []=(param, value) set_value(param, value, :memory) end # Generate the list of valid arguments, in a format that GetoptLong can # understand, and add them to the passed option list. def addargs(options) # Add all of the config parameters as valid options. self.each { |name, setting| setting.getopt_args.each { |args| options << args } } options end # Generate the list of valid arguments, in a format that OptionParser can # understand, and add them to the passed option list. def optparse_addargs(options) # Add all of the config parameters as valid options. self.each { |name, setting| options << setting.optparse_args } options end # Is our parameter a boolean parameter? def boolean?(param) param = param.to_sym !!(@config.include?(param) and @config[param].kind_of? BooleanSetting) end # Remove all set values, potentially skipping cli values. def clear @sync.synchronize do unsafe_clear end end # Remove all set values, potentially skipping cli values. def unsafe_clear(clear_cli = true, clear_application_defaults = false) @values.each do |name, values| next if ((name == :application_defaults) and !clear_application_defaults) next if ((name == :cli) and !clear_cli) @values.delete(name) end # Only clear the 'used' values if we were explicitly asked to clear out # :cli values; otherwise, it may be just a config file reparse, # and we want to retain this cli values. @used = [] if clear_cli @app_defaults_initialized = false if clear_application_defaults @cache.clear end private :unsafe_clear # This is mostly just used for testing. def clearused @cache.clear @used = [] end def global_defaults_initialized?() @global_defaults_initialized end def initialize_global_settings(args = []) raise Puppet::DevError, "Attempting to initialize global default settings more than once!" if global_defaults_initialized? # The first two phases of the lifecycle of a puppet application are: # 1) To parse the command line options and handle any of them that are registered, defined "global" puppet # settings (mostly from defaults.rb).) # 2) To parse the puppet config file(s). # # These 2 steps are being handled explicitly here. If there ever arises a situation where they need to be # triggered from outside of this class, without triggering the rest of the lifecycle--we might want to move them # out into a separate method that we call from here. However, this seems to be sufficient for now. # --cprice 2012-03-16 # Here's step 1. parse_global_options(args) # Here's step 2. NOTE: this is a change in behavior where we are now parsing the config file on every run; # before, there were several apps that specifically registered themselves as not requiring anything from # the config file. The fact that we're always parsing it now might be a small performance hit, but it was # necessary in order to make sure that we can resolve the libdir before we look for the available applications. parse_config_files @global_defaults_initialized = true end # This method is called during application bootstrapping. It is responsible for parsing all of the # command line options and initializing the settings accordingly. # # It will ignore options that are not defined in the global puppet settings list, because they may # be valid options for the specific application that we are about to launch... however, at this point # in the bootstrapping lifecycle, we don't yet know what that application is. def parse_global_options(args) # Create an option parser option_parser = PuppetOptionParser.new option_parser.ignore_invalid_options = true # Add all global options to it. self.optparse_addargs([]).each do |option| option_parser.on(*option) do |arg| opt, val = Puppet::Util::Settings.clean_opt(option[0], arg) handlearg(opt, val) end end option_parser.parse(args) end private :parse_global_options ## Private utility method; this is the callback that the OptionParser will use when it finds ## an option that was defined in Puppet.settings. All that this method does is a little bit ## of clanup to get the option into the exact format that Puppet.settings expects it to be in, ## and then passes it along to Puppet.settings. ## ## @param [String] opt the command-line option that was matched ## @param [String, TrueClass, FalseClass] the value for the setting (as determined by the OptionParser) #def handlearg(opt, val) # opt, val = self.class.clean_opt(opt, val) # Puppet.settings.handlearg(opt, val) #end #private :handlearg # A utility method (public, is used by application.rb and perhaps elsewhere) that munges a command-line # option string into the format that Puppet.settings expects. (This mostly has to deal with handling the # "no-" prefix on flag/boolean options). # # @param [String] opt the command line option that we are munging # @param [String, TrueClass, FalseClass] the value for the setting (as determined by the OptionParser) def self.clean_opt(opt, val) # rewrite --[no-]option to --no-option if that's what was given if opt =~ /\[no-\]/ and !val opt = opt.gsub(/\[no-\]/,'no-') end # otherwise remove the [no-] prefix to not confuse everybody opt = opt.gsub(/\[no-\]/, '') [opt, val] end def app_defaults_initialized?() @app_defaults_initialized end def initialize_app_defaults(app_defaults) raise Puppet::DevError, "Attempting to initialize application default settings more than once!" if app_defaults_initialized? REQUIRED_APP_SETTINGS.each do |key| raise Puppet::SettingsError, "missing required app default setting '#{key}'" unless app_defaults.has_key?(key) end app_defaults.each do |key, value| set_value(key, value, :application_defaults) end call_hooks_deferred_to_application_initialization @app_defaults_initialized = true end def call_hooks_deferred_to_application_initialization(options = {}) @hooks_to_call_on_application_initialization.each do |setting| begin setting.handle(self.value(setting.name)) rescue Puppet::SettingsError => err raise err unless options[:ignore_interpolation_dependency_errors] #swallow. We're not concerned if we can't call hooks because dependencies don't exist yet #we'll get another chance after application defaults are initialized end end end private :call_hooks_deferred_to_application_initialization # Do variable interpolation on the value. def convert(value, environment = nil) return nil if value.nil? return value unless value.is_a? String newval = value.gsub(/\$(\w+)|\$\{(\w+)\}/) do |value| varname = $2 || $1 if varname == "environment" and environment environment elsif pval = self.value(varname, environment) pval else raise Puppet::SettingsError, "Could not find value for #{value}" end end newval end # Return a value's description. def description(name) if obj = @config[name.to_sym] obj.desc else nil end end def each @config.each { |name, object| yield name, object } end # Iterate over each section name. def eachsection yielded = [] @config.each do |name, object| section = object.section unless yielded.include? section yield section yielded << section end end end # Return an object by name. def setting(param) param = param.to_sym @config[param] end # Handle a command-line argument. def handlearg(opt, value = nil) @cache.clear if value.is_a?(FalseClass) value = "false" elsif value.is_a?(TrueClass) value = "true" end value &&= munge_value(value) str = opt.sub(/^--/,'') bool = true newstr = str.sub(/^no-/, '') if newstr != str str = newstr bool = false end str = str.intern if @config[str].is_a?(Puppet::Util::Settings::BooleanSetting) if value == "" or value.nil? value = bool end end set_value(str, value, :cli) end def include?(name) name = name.intern if name.is_a? String @config.include?(name) end # check to see if a short name is already defined def shortinclude?(short) short = short.intern if name.is_a? String @shortnames.include?(short) end # Create a new collection of config settings. def initialize @config = {} @shortnames = {} @created = [] @searchpath = nil # Mutex-like thing to protect @values @sync = Sync.new # Keep track of set values. @values = Hash.new { |hash, key| hash[key] = {} } # And keep a per-environment cache @cache = Hash.new { |hash, key| hash[key] = {} } # The list of sections we've used. @used = [] @hooks_to_call_on_application_initialization = [] end # NOTE: ACS ahh the util classes. . .sigh # as part of a fix for 1183, I pulled the logic for the following 5 methods out of the executables and puppet.rb # They probably deserve their own class, but I don't want to do that until I can refactor environments # its a little better than where they were # Prints the contents of a config file with the available config settings, or it # prints a single value of a config setting. def print_config_options env = value(:environment) val = value(:configprint) if val == "all" hash = {} each do |name, obj| val = value(name,env) val = val.inspect if val == "" hash[name] = val end hash.sort { |a,b| a[0].to_s <=> b[0].to_s }.each do |name, val| puts "#{name} = #{val}" end else val.split(/\s*,\s*/).sort.each do |v| if include?(v) #if there is only one value, just print it for back compatibility if v == val puts value(val,env) break end puts "#{v} = #{value(v,env)}" else puts "invalid parameter: #{v}" return false end end end true end def generate_config puts to_config true end def generate_manifest puts to_manifest true end def print_configs return print_config_options if value(:configprint) != "" return generate_config if value(:genconfig) generate_manifest if value(:genmanifest) end def print_configs? (value(:configprint) != "" || value(:genconfig) || value(:genmanifest)) && true end # Return a given object's file metadata. def metadata(param) if obj = @config[param.to_sym] and obj.is_a?(FileSetting) return [:owner, :group, :mode].inject({}) do |meta, p| if v = obj.send(p) meta[p] = v end meta end else nil end end # Make a directory with the appropriate user, group, and mode def mkdir(default) obj = get_config_file_default(default) Puppet::Util::SUIDManager.asuser(obj.owner, obj.group) do mode = obj.mode || 0750 Dir.mkdir(obj.value, mode) end end # Figure out the section name for the run_mode. def run_mode @run_mode || :user end # PRIVATE! This only exists because we need a hook to validate the run mode when it's being set, and # it should never, ever, ever, ever be called from outside of this file. def run_mode=(mode) raise Puppet::DevError, "Invalid run mode '#{mode}'" unless [:master, :agent, :user].include?(mode) @run_mode = mode end private :run_mode= # Return all of the parameters associated with a given section. def params(section = nil) if section section = section.intern if section.is_a? String @config.find_all { |name, obj| obj.section == section }.collect { |name, obj| name } else @config.keys end end # Parse the configuration file. Just provides # thread safety. def parse_config_files # we are now supporting multiple config files; the "main" config file will be the one located in # /etc/puppet (or overridden $confdir)... but we will also look for a config file in the user's home # directory. This was introduced in an effort to provide maximum backwards compatibility while # de-coupling the process of locating the config file from the "run mode" of the application. files = [main_config_file] files << user_config_file unless Puppet.features.root? @sync.synchronize do unsafe_parse(files) end # talking with cprice, Settings.parse will not be the final location for this. He's working on ticket # that, as a side effect, will create a more appropriate place for this. At that time, this will be # moved to the new location. --jeffweiss 24 apr 2012 call_hooks_deferred_to_application_initialization :ignore_interpolation_dependency_errors => true end private :parse_config_files def main_config_file # the algorithm here is basically this: # * use the explicit config file location if one has been specified; this can be affected by modifications # to either the "confdir" or "config" settings (most likely via CLI arguments). # * if no explicit config location has been specified, we fall back to the default. # # The easiest way to determine whether an explicit one has been specified is to simply attempt to evaluate # the value of ":config". This will obviously be successful if they've passed an explicit value for :config, # but it will also result in successful interpolation if they've only passed an explicit value for :confdir. # # If they've specified neither, then the interpolation will fail and we'll get an exception. # begin return self[:config] if self[:config] rescue Puppet::SettingsError => err # This means we failed to interpolate, which means that they didn't explicitly specify either :config or # :confdir... so we'll fall out to the default value. end # return the default value. return File.join(self.class.default_global_config_dir, config_file_name) end private :main_config_file def user_config_file return File.join(self.class.default_user_config_dir, config_file_name) end private :user_config_file # This method is here to get around some life-cycle issues. We need to be able to determine the config file name # before the settings / defaults are fully loaded. However, we also need to respect any overrides of this value # that the user may have specified on the command line. # # The easiest way to do this is to attempt to read the setting, and if we catch an error (meaning that it hasn't been # set yet), we'll fall back to the default value. def config_file_name begin return self[:config_file_name] if self[:config_file_name] rescue Puppet::SettingsError => err # This just means that the setting wasn't explicitly set on the command line, so we will ignore it and # fall through to the default name. end return self.class.default_config_file_name end private :config_file_name # Unsafely parse the file -- this isn't thread-safe and causes plenty of problems if used directly. def unsafe_parse(files) raise Puppet::DevError unless files.length > 0 # build up a single data structure that contains the values from all of the parsed files. data = {} files.each do |file| next unless FileTest.exist?(file) begin file_data = parse_file(file) # This is a little kludgy; basically we are merging a hash of hashes. We can't use "merge" at the # outermost level or we risking losing data from the hash we're merging into. file_data.keys.each do |key| if data.has_key?(key) data[key].merge!(file_data[key]) else data[key] = file_data[key] end end rescue => detail Puppet.log_exception(detail, "Could not parse #{file}: #{detail}") return end end # If we get here and don't have any data, we just return and don't muck with the current state of the world. return if data.empty? # If we get here then we have some data, so we need to clear out any previous settings that may have come from # config files. unsafe_clear(false, false) # And now we can repopulate with the values from our last parsing of the config files. metas = {} data.each do |area, values| metas[area] = values.delete(:_meta) values.each do |key,value| set_value(key, value, area, :dont_trigger_handles => true, :ignore_bad_settings => true ) end end # Determine our environment, if we have one. if @config[:environment] env = self.value(:environment).to_sym else env = "none" end # Call any hooks we should be calling. settings_with_hooks.each do |setting| each_source(env) do |source| if value = @values[source][setting.name] # We still have to use value to retrieve the value, since # we want the fully interpolated value, not $vardir/lib or whatever. # This results in extra work, but so few of the settings # will have associated hooks that it ends up being less work this # way overall. setting.handle(self.value(setting.name, env)) break end end end # We have to do it in the reverse of the search path, # because multiple sections could set the same value # and I'm too lazy to only set the metadata once. searchpath.reverse.each do |source| source = run_mode if source == :run_mode source = @name if (@name && source == :name) if meta = metas[source] set_metadata(meta) end end end private :unsafe_parse # Create a new setting. The value is passed in because it's used to determine # what kind of setting we're creating, but the value itself might be either # a default or a value, so we can't actually assign it. # # See #define_settings for documentation on the legal values for the ":type" option. def newsetting(hash) klass = nil hash[:section] = hash[:section].to_sym if hash[:section] if type = hash[:type] unless klass = { :string => StringSetting, :file => FileSetting, :directory => DirectorySetting, :path => PathSetting, :boolean => BooleanSetting, } [type] raise ArgumentError, "Invalid setting type '#{type}'" end hash.delete(:type) else # The only implicit typing we still do for settings is to fall back to "String" type if they didn't explicitly # specify a type. Personally I'd like to get rid of this too, and make the "type" option mandatory... but # there was a little resistance to taking things quite that far for now. --cprice 2012-03-19 klass = StringSetting end hash[:settings] = self setting = klass.new(hash) setting end # This has to be private, because it doesn't add the settings to @config private :newsetting # Iterate across all of the objects in a given section. def persection(section) section = section.to_sym self.each { |name, obj| if obj.section == section yield obj end } end # Reparse our config file, if necessary. def reparse_config_files if files if filename = any_files_changed? Puppet.notice "Config file #{filename} changed; triggering re-parse of all config files." parse_config_files reuse end end end def files return @files if @files @files = [] [main_config_file, user_config_file].each do |path| if FileTest.exist?(path) @files << Puppet::Util::LoadedFile.new(path) end end @files end private :files # Checks to see if any of the config files have been modified # @return the filename of the first file that is found to have changed, or nil if no files have changed def any_files_changed? files.each do |file| return file.file if file.changed? end nil end private :any_files_changed? def reuse return unless defined?(@used) @sync.synchronize do # yay, thread-safe new = @used @used = [] self.use(*new) end end # The order in which to search for values. def searchpath(environment = nil) if environment [:cli, :memory, environment, :run_mode, :main, :application_defaults] else [:cli, :memory, :run_mode, :main, :application_defaults] end end # Get a list of objects per section def sectionlist sectionlist = [] self.each { |name, obj| section = obj.section || "puppet" sections[section] ||= [] sectionlist << section unless sectionlist.include?(section) sections[section] << obj } return sectionlist, sections end def service_user_available? return @service_user_available if defined?(@service_user_available) return @service_user_available = false unless user_name = self[:user] user = Puppet::Type.type(:user).new :name => self[:user], :audit => :ensure @service_user_available = user.exists? end def legacy_to_mode(type, param) require 'puppet/util/command_line/legacy_command_line' if Puppet::Util::CommandLine::LegacyCommandLine::LEGACY_APPS.has_key?(type) new_type = Puppet::Util::CommandLine::LegacyCommandLine::LEGACY_APPS[type].run_mode Puppet.deprecation_warning "You have configuration parameter $#{param} specified in [#{type}], which is a deprecated section. I'm assuming you meant [#{new_type}]" return new_type end type end # Allow later inspection to determine if the setting was set on the # command line, or through some other code path. Used for the # `dns_alt_names` option during cert generate. --daniel 2011-10-18 def set_by_cli?(param) param = param.to_sym !@values[:cli][param].nil? end def set_value(param, value, type, options = {}) param = param.to_sym unless setting = @config[param] if options[:ignore_bad_settings] return else raise ArgumentError, "Attempt to assign a value to unknown configuration parameter #{param.inspect}" end end setting.handle(value) if setting.has_hook? and not options[:dont_trigger_handles] if read_only_settings.include? param and type != :application_defaults raise ArgumentError, "You're attempting to set configuration parameter $#{param}, which is read-only." end type = legacy_to_mode(type, param) @sync.synchronize do # yay, thread-safe @values[type][param] = value @cache.clear clearused # Clear the list of environments, because they cache, at least, the module path. # We *could* preferentially just clear them if the modulepath is changed, # but we don't really know if, say, the vardir is changed and the modulepath # is defined relative to it. We need the defined?(stuff) because of loading # order issues. Puppet::Node::Environment.clear if defined?(Puppet::Node) and defined?(Puppet::Node::Environment) end # This is a hack. The run_mode should probably not be a "normal" setting, because the places # it is used tend to create lifecycle issues and cause other weird problems. In some places # we need for it to have a default value, in other places it may be preferable to be able to # determine that it has not yet been set. There used to be a global variable that some # code paths would access; as a first step towards cleaning it up, I've gotten rid of the global # variable and am instead using an instance variable in this class, but that means that if # someone modifies the value of the setting at a later point during execution, then the # instance variable needs to be updated as well... so that's what we're doing here. # # This code should be removed if we get a chance to remove run_mode from the defined settings. # --cprice 2012-03-19 self.run_mode = value if param == :run_mode value end # Deprecated; use #define_settings instead def setdefaults(section, defs) Puppet.deprecation_warning("'setdefaults' is deprecated and will be removed; please call 'define_settings' instead") define_settings(section, defs) end # Define a group of settings. # # @param [Symbol] section a symbol to use for grouping multiple settings together into a conceptual unit. This value # (and the conceptual separation) is not used very often; the main place where it will have a potential impact # is when code calls Settings#use method. See docs on that method for further details, but basically that method # just attempts to do any preparation that may be necessary before code attempts to leverage the value of a particular # setting. This has the most impact for file/directory settings, where #use will attempt to "ensure" those # files / directories. # @param [Hash[Hash]] defs the settings to be defined. This argument is a hash of hashes; each key should be a symbol, # which is basically the name of the setting that you are defining. The value should be another hash that specifies # the parameters for the particular setting. Legal values include: # [:default] => required; this is a string value that will be used as a default value for a setting if no other # value is specified (via cli, config file, etc.) This string may include "variables", demarcated with $ or ${}, # which will be interpolated with values of other settings. # [:desc] => required; a description of the setting, used in documentation / help generation # [:type] => not required, but highly encouraged! This specifies the data type that the setting represents. If # you do not specify it, it will default to "string". Legal values include: # :string - A generic string setting # :boolean - A boolean setting; values are expected to be "true" or "false" # :file - A (single) file path; puppet may attempt to create this file depending on how the settings are used. This type # also supports additional options such as "mode", "owner", "group" # :directory - A (single) directory path; puppet may attempt to create this file depending on how the settings are used. This type # also supports additional options such as "mode", "owner", "group" # :path - This is intended to be used for settings whose value can contain multiple directory paths, respresented # as strings separated by the system path separator (e.g. system path, module path, etc.). # [:mode] => an (optional) octal value to be used as the permissions/mode for :file and :directory settings # [:owner] => optional owner username/uid for :file and :directory settings # [:group] => optional group name/gid for :file and :directory settings # def define_settings(section, defs) section = section.to_sym call = [] defs.each { |name, hash| raise ArgumentError, "setting definition for '#{name}' is not a hash!" unless hash.is_a? Hash name = name.to_sym hash[:name] = name hash[:section] = section raise ArgumentError, "Parameter #{name} is already defined" if @config.include?(name) tryconfig = newsetting(hash) if short = tryconfig.short if other = @shortnames[short] raise ArgumentError, "Parameter #{other.name} is already using short name '#{short}'" end @shortnames[short] = tryconfig end @config[name] = tryconfig # Collect the settings that need to have their hooks called immediately. # We have to collect them so that we can be sure we're fully initialized before # the hook is called. call << tryconfig if tryconfig.call_hook_on_define? @hooks_to_call_on_application_initialization << tryconfig if tryconfig.call_hook_on_initialize? } call.each { |setting| setting.handle(self.value(setting.name)) } end # Convert the settings we manage into a catalog full of resources that model those settings. def to_catalog(*sections) sections = nil if sections.empty? catalog = Puppet::Resource::Catalog.new("Settings") @config.keys.find_all { |key| @config[key].is_a?(FileSetting) }.each do |key| file = @config[key] next unless (sections.nil? or sections.include?(file.section)) next unless resource = file.to_resource next if catalog.resource(resource.ref) Puppet.debug("Using settings: adding file resource '#{key}': '#{resource.inspect}'") catalog.add_resource(resource) end add_user_resources(catalog, sections) catalog end # Convert our list of config settings into a configuration file. def to_config str = %{The configuration file for #{Puppet.run_mode.name}. Note that this file is likely to have unused configuration parameters in it; any parameter that's valid anywhere in Puppet can be in any config file, even if it's not used. Every section can specify three special parameters: owner, group, and mode. These parameters affect the required permissions of any files specified after their specification. Puppet will sometimes use these parameters to check its own configured state, so they can be used to make Puppet a bit more self-managing. Generated on #{Time.now}. }.gsub(/^/, "# ") # Add a section heading that matches our name. if @config.include?(:run_mode) str += "[#{self[:run_mode]}]\n" end eachsection do |section| persection(section) do |obj| str += obj.to_config + "\n" unless read_only_settings.include? obj.name or obj.name == :genconfig end end return str end # Convert to a parseable manifest def to_manifest catalog = to_catalog catalog.resource_refs.collect do |ref| catalog.resource(ref).to_manifest end.join("\n\n") end # Create the necessary objects to use a section. This is idempotent; # you can 'use' a section as many times as you want. def use(*sections) sections = sections.collect { |s| s.to_sym } @sync.synchronize do # yay, thread-safe sections = sections.reject { |s| @used.include?(s) } return if sections.empty? begin catalog = to_catalog(*sections).to_ral rescue => detail Puppet.log_and_raise(detail, "Could not create resources for managing Puppet's files and directories in sections #{sections.inspect}: #{detail}") end catalog.host_config = false catalog.apply do |transaction| if transaction.any_failed? report = transaction.report failures = report.logs.find_all { |log| log.level == :err } raise "Got #{failures.length} failure(s) while initializing: #{failures.collect { |l| l.to_s }.join("; ")}" end end sections.each { |s| @used << s } @used.uniq! end end def valid?(param) param = param.to_sym @config.has_key?(param) end def uninterpolated_value(param, environment = nil) param = param.to_sym environment &&= environment.to_sym # See if we can find it within our searchable list of values val = find_value(environment, param) # If we didn't get a value, use the default val = @config[param].default if val.nil? val end def find_value(environment, param) each_source(environment) do |source| # Look for the value. We have to test the hash for whether # it exists, because the value might be false. @sync.synchronize do return @values[source][param] if @values[source].include?(param) end end return nil end private :find_value # Find the correct value using our search path. Optionally accept an environment # in which to search before the other configuration sections. def value(param, environment = nil, bypass_interpolation = false) param = param.to_sym environment &&= environment.to_sym setting = @config[param] # Short circuit to nil for undefined parameters. return nil unless @config.include?(param) # Yay, recursion. #self.reparse unless [:config, :filetimeout].include?(param) # Check the cache first. It needs to be a per-environment # cache so that we don't spread values from one env # to another. if cached = @cache[environment||"none"][param] return cached end val = uninterpolated_value(param, environment) return val if bypass_interpolation if param == :code # if we interpolate code, all hell breaks loose. return val end # Convert it if necessary begin val = convert(val, environment) rescue Puppet::SettingsError => err raise Puppet::SettingsError.new("Error converting value for param '#{param}': #{err}") end val = setting.munge(val) if setting.respond_to?(:munge) # And cache it @cache[environment||"none"][param] = val val end # Open a file with the appropriate user, group, and mode def write(default, *args, &bloc) obj = get_config_file_default(default) writesub(default, value(obj.name), *args, &bloc) end # Open a non-default file under a default dir with the appropriate user, # group, and mode def writesub(default, file, *args, &bloc) obj = get_config_file_default(default) chown = nil if Puppet.features.root? chown = [obj.owner, obj.group] else chown = [nil, nil] end Puppet::Util::SUIDManager.asuser(*chown) do mode = obj.mode ? obj.mode.to_i : 0640 args << "w" if args.empty? args << mode # Update the umask to make non-executable files Puppet::Util.withumask(File.umask ^ 0111) do File.open(file, *args) do |file| yield file end end end end def readwritelock(default, *args, &bloc) file = value(get_config_file_default(default).name) tmpfile = file + ".tmp" sync = Sync.new raise Puppet::DevError, "Cannot create #{file}; directory #{File.dirname(file)} does not exist" unless FileTest.directory?(File.dirname(tmpfile)) sync.synchronize(Sync::EX) do File.open(file, ::File::CREAT|::File::RDWR, 0600) do |rf| rf.lock_exclusive do if File.exist?(tmpfile) raise Puppet::Error, ".tmp file already exists for #{file}; Aborting locked write. Check the .tmp file and delete if appropriate" end # If there's a failure, remove our tmpfile begin writesub(default, tmpfile, *args, &bloc) rescue File.unlink(tmpfile) if FileTest.exist?(tmpfile) raise end begin File.rename(tmpfile, file) rescue => detail Puppet.err "Could not rename #{file} to #{tmpfile}: #{detail}" File.unlink(tmpfile) if FileTest.exist?(tmpfile) end end end end end private # This is just here to simplify testing. This method can be stubbed easily. Constants can't. def read_only_settings() READ_ONLY_SETTINGS end def get_config_file_default(default) obj = nil unless obj = @config[default] raise ArgumentError, "Unknown default #{default}" end raise ArgumentError, "Default #{default} is not a file" unless obj.is_a? FileSetting obj end def add_user_resources(catalog, sections) return unless Puppet.features.root? return if Puppet.features.microsoft_windows? return unless self[:mkusers] @config.each do |name, setting| next unless setting.respond_to?(:owner) next unless sections.nil? or sections.include?(setting.section) if user = setting.owner and user != "root" and catalog.resource(:user, user).nil? resource = Puppet::Resource.new(:user, user, :parameters => {:ensure => :present}) resource[:gid] = self[:group] if self[:group] catalog.add_resource resource end if group = setting.group and ! %w{root wheel}.include?(group) and catalog.resource(:group, group).nil? catalog.add_resource Puppet::Resource.new(:group, group, :parameters => {:ensure => :present}) end end end # Yield each search source in turn. def each_source(environment) searchpath(environment).each do |source| # Modify the source as necessary. source = self.run_mode if source == :run_mode yield source end end # Return all settings that have associated hooks; this is so # we can call them after parsing the configuration file. def settings_with_hooks @config.values.find_all { |setting| setting.has_hook? } end # Extract extra setting information for files. def extract_fileinfo(string) result = {} value = string.sub(/\{\s*([^}]+)\s*\}/) do params = $1 params.split(/\s*,\s*/).each do |str| if str =~ /^\s*(\w+)\s*=\s*([\w\d]+)\s*$/ param, value = $1.intern, $2 result[param] = value raise ArgumentError, "Invalid file option '#{param}'" unless [:owner, :mode, :group].include?(param) if param == :mode and value !~ /^\d+$/ raise ArgumentError, "File modes must be numbers" end else raise ArgumentError, "Could not parse '#{string}'" end end '' end result[:value] = value.sub(/\s*$/, '') result end # Convert arguments into booleans, integers, or whatever. def munge_value(value) # Handle different data types correctly return case value when /^false$/i; false when /^true$/i; true when /^\d+$/i; Integer(value) when true; true when false; false else value.gsub(/^["']|["']$/,'').sub(/\s+$/, '') end end # This method just turns a file in to a hash of hashes. def parse_file(file) text = read_file(file) result = Hash.new { |names, name| names[name] = {} } count = 0 # Default to 'main' for the section. section = :main result[section][:_meta] = {} text.split(/\n/).each do |line| count += 1 case line when /^\s*\[(\w+)\]\s*$/ section = $1.intern # Section names #disallow application_defaults in config file if section == :application_defaults - raise Puppet::Error.new("Illegal section 'application_defaults' in config file", file, line) + raise Puppet::Error, "Illegal section 'application_defaults' in config file #{file} at line #{line}" end # Add a meta section result[section][:_meta] ||= {} when /^\s*#/; next # Skip comments when /^\s*$/; next # Skip blanks when /^\s*(\w+)\s*=\s*(.*?)\s*$/ # settings var = $1.intern # We don't want to munge modes, because they're specified in octal, so we'll # just leave them as a String, since Puppet handles that case correctly. if var == :mode value = $2 else value = munge_value($2) end # Check to see if this is a file argument and it has extra options begin if value.is_a?(String) and options = extract_fileinfo(value) value = options[:value] options.delete(:value) result[section][:_meta][var] = options end result[section][var] = value rescue Puppet::Error => detail detail.file = file detail.line = line raise end else error = Puppet::Error.new("Could not match line #{line}") error.file = file error.line = line raise error end end result end # Read the file in. def read_file(file) begin return File.read(file) rescue Errno::ENOENT raise ArgumentError, "No such file #{file}" rescue Errno::EACCES raise ArgumentError, "Permission denied to file #{file}" end end # Set file metadata. def set_metadata(meta) meta.each do |var, values| values.each do |param, value| @config[var].send(param.to_s + "=", value) end end end # Private method for internal test use only; allows to do a comprehensive clear of all settings between tests. # # @return nil def clear_everything_for_tests() @sync.synchronize do unsafe_clear(true, true) @global_defaults_initialized = false @app_defaults_initialized = false end end private :clear_everything_for_tests end diff --git a/spec/unit/util/logging_spec.rb b/spec/unit/util/logging_spec.rb index 4eebad8fd..70e9d273d 100755 --- a/spec/unit/util/logging_spec.rb +++ b/spec/unit/util/logging_spec.rb @@ -1,125 +1,151 @@ #!/usr/bin/env rspec require 'spec_helper' require 'puppet/util/logging' class LoggingTester include Puppet::Util::Logging end describe Puppet::Util::Logging do before do @logger = LoggingTester.new end Puppet::Util::Log.eachlevel do |level| it "should have a method for sending '#{level}' logs" do @logger.should respond_to(level) end end it "should have a method for sending a log with a specified log level" do @logger.expects(:to_s).returns "I'm a string!" Puppet::Util::Log.expects(:create).with { |args| args[:source] == "I'm a string!" and args[:level] == "loglevel" and args[:message] == "mymessage" } @logger.send_log "loglevel", "mymessage" end describe "when sending a log" do it "should use the Log's 'create' entrance method" do Puppet::Util::Log.expects(:create) @logger.notice "foo" end it "should send itself converted to a string as the log source" do @logger.expects(:to_s).returns "I'm a string!" Puppet::Util::Log.expects(:create).with { |args| args[:source] == "I'm a string!" } @logger.notice "foo" end it "should queue logs sent without a specified destination" do Puppet::Util::Log.close_all Puppet::Util::Log.expects(:queuemessage) @logger.notice "foo" end it "should use the path of any provided resource type" do resource = Puppet::Type.type(:host).new :name => "foo" resource.expects(:path).returns "/path/to/host".to_sym Puppet::Util::Log.expects(:create).with { |args| args[:source] == "/path/to/host" } resource.notice "foo" end it "should use the path of any provided resource parameter" do resource = Puppet::Type.type(:host).new :name => "foo" param = resource.parameter(:name) param.expects(:path).returns "/path/to/param".to_sym Puppet::Util::Log.expects(:create).with { |args| args[:source] == "/path/to/param" } param.notice "foo" end it "should send the provided argument as the log message" do Puppet::Util::Log.expects(:create).with { |args| args[:message] == "foo" } @logger.notice "foo" end it "should join any provided arguments into a single string for the message" do Puppet::Util::Log.expects(:create).with { |args| args[:message] == "foo bar baz" } @logger.notice ["foo", "bar", "baz"] end [:file, :line, :tags].each do |attr| it "should include #{attr} if available" do @logger.singleton_class.send(:attr_accessor, attr) @logger.send(attr.to_s + "=", "myval") Puppet::Util::Log.expects(:create).with { |args| args[attr] == "myval" } @logger.notice "foo" end end end describe "when sending a deprecation warning" do it "should log the message with warn" do @logger.expects(:warning).with do |msg| msg =~ /^foo\n/ end @logger.deprecation_warning 'foo' end it "should only log each offending line once" do @logger.expects(:warning).with do |msg| msg =~ /^foo\n/ end .once 5.times { @logger.deprecation_warning 'foo' } end it "should only log the first 100 messages" do (1..100).each { |i| @logger.expects(:warning).with do |msg| msg =~ /^#{i}\n/ end .once # since the deprecation warning will only log each offending line once, we have to do some tomfoolery # here in order to make it think each of these calls is coming from a unique call stack; we're basically # mocking the method that it would normally use to find the call stack. @logger.expects(:get_deprecation_offender).returns("deprecation log count test ##{i}") @logger.deprecation_warning i } @logger.expects(:warning).with(101).never @logger.deprecation_warning 101 end end + + describe "when formatting exceptions" do + it "should be able to format a chain of exceptions" do + exc3 = Puppet::Error.new("original") + exc3.set_backtrace(["1.rb:4:in `a'","2.rb:2:in `b'","3.rb:1"]) + exc2 = Puppet::Error.new("second", exc3) + exc2.set_backtrace(["4.rb:8:in `c'","5.rb:1:in `d'","6.rb:3"]) + exc1 = Puppet::Error.new("third", exc2) + exc1.set_backtrace(["7.rb:31:in `e'","8.rb:22:in `f'","9.rb:9"]) + # whoa ugly + @logger.format_exception(exc1).should =~ /third +.*7\.rb:31 +.*8\.rb:22 +.*9\.rb:9 +Wrapped exception: +second +.*4\.rb:8 +.*5\.rb:1 +.*6\.rb:3 +Wrapped exception: +original +.*1\.rb:4 +.*2\.rb:2 +.*3\.rb:1/ + end + end end