Class Index [+]

Quicksearch

MCollective::Matcher

A parser and scanner that creates a stack machine for a simple fact and class matching language used on the CLI to facilitate a rich discovery language

Language EBNF

compound = [“(“] expression [“)”] {[“(“] expression [“)”]} expression = [!|not]statement [“and”|“or”] [!|not] statement char = A-Z | a-z | < | > | => | =< | _ | - |* | / { A-Z | a-z | < | > | => | =< | _ | - | * | / | } int = 0|1|2|3|4|5|6|7|8|9{|0|1|2|3|4|5|6|7|8|9|0}

Public Class Methods

create_compound_callstack(call_string) click to toggle source

Creates a callstack to be evaluated from a compound evaluation string

     # File lib/mcollective/matcher.rb, line 181
181:     def self.create_compound_callstack(call_string)
182:       callstack = Matcher::Parser.new(call_string).execution_stack
183:       callstack.each_with_index do |statement, i|
184:         if statement.keys.first == "fstatement"
185:           callstack[i]["fstatement"] = create_function_hash(statement.values.first)
186:         end
187:       end
188:       callstack
189:     end
create_function_hash(function_call) click to toggle source

Helper creates a hash from a function call string

    # File lib/mcollective/matcher.rb, line 17
17:     def self.create_function_hash(function_call)
18:       func_hash = {}
19:       f = ""
20:       func_parts = function_call.split(/(!=|>=|<=|<|>|=)/)
21:       func_hash["r_compare"] = func_parts.pop
22:       func_hash["operator"] = func_parts.pop
23:       func = func_parts.join
24: 
25:       # Deal with dots in function parameters and functions without dot values
26:       if func.match(/^.+\(.*\)$/)
27:         f = func
28:       else
29:         func_parts = func.split(".")
30:         func_hash["value"] = func_parts.pop
31:         f = func_parts.join(".")
32:       end
33: 
34:       # Deal with regular expression matches
35:       if func_hash["r_compare"] =~ /^\/.*\/$/
36:         func_hash["operator"] = "=~" if func_hash["operator"] == "="
37:         func_hash["operator"] = "!=~" if func_hash["operator"] == "!="
38:         func_hash["r_compare"] = Regexp.new(func_hash["r_compare"].gsub(/^\/|\/$/, ""))
39:       # Convert = operators to == so they can be propperly evaluated
40:       elsif func_hash["operator"] == "="
41:         func_hash["operator"] = "=="
42:       end
43: 
44:       # Grab function name and parameters from left compare string
45:       func_hash["name"], func_hash["params"] = f.split("(")
46:       if func_hash["params"] == ")"
47:         func_hash["params"] = nil
48:       else
49: 
50:         # Walk the function parameters from the front and from the
51:         # back removing the first and last instances of single of
52:         # double qoutes. We do this to handle the case where params
53:         # contain escaped qoutes.
54:         func_hash["params"] = func_hash["params"].gsub(")", "")
55:         func_quotes = func_hash["params"].split(/('|")/)
56: 
57:         func_quotes.each_with_index do |item, i|
58:           if item.match(/'|"/)
59:             func_quotes.delete_at(i)
60:             break
61:           end
62:         end
63: 
64:         func_quotes.reverse.each_with_index do |item,i|
65:           if item.match(/'|"/)
66:             func_quotes.delete_at(func_quotes.size - i - 1)
67:             break
68:           end
69:         end
70: 
71:         func_hash["params"] = func_quotes.join
72:       end
73: 
74:       func_hash
75:     end
eval_compound_fstatement(function_hash) click to toggle source

Returns the result of an evaluated compound statement that includes a function

     # File lib/mcollective/matcher.rb, line 135
135:     def self.eval_compound_fstatement(function_hash)
136:       l_compare = execute_function(function_hash)
137: 
138:       # Break out early and return false if the function returns nil
139:       return false unless l_compare
140: 
141:       # Prevent unwanted discovery by limiting comparison operators
142:       # on Strings and Booleans
143:       if((l_compare.is_a?(String) || l_compare.is_a?(TrueClass) || l_compare.is_a?(FalseClass)) && function_hash["operator"].match(/<|>/))
144:         Log.debug "Cannot do > and < comparison on Booleans and Strings '#{l_compare} #{function_hash["operator"]} #{function_hash["r_compare"]}'"
145:         return false
146:       end
147: 
148:       # Prevent backticks in function parameters
149:       if function_hash["params"] =~ /`/
150:         Log.debug("Cannot use backticks in function parameters")
151:         return false
152:       end
153: 
154:       # Escape strings for evaluation
155:       function_hash["r_compare"] = "\"#{function_hash["r_compare"]}\"" if(l_compare.is_a?(String)  && !(function_hash["operator"] =~ /=~|!=~/))
156: 
157:       # Do a regex comparison if right compare string is a regex
158:       if function_hash["operator"] =~ /(=~|!=~)/
159:         # Fail if left compare value isn't a string
160:         unless l_compare.is_a?(String)
161:           Log.debug("Cannot do a regex check on a non string value.")
162:           return false
163:         else
164:           compare_result = l_compare.match(function_hash["r_compare"])
165:           # Flip return value for != operator
166:           if function_hash["operator"] == "!=~"
167:             !((compare_result.nil?) ? false : true)
168:           else
169:             (compare_result.nil?) ? false : true
170:           end
171:         end
172:         # Otherwise evaluate the logical comparison
173:       else
174:         l_compare = "\"#{l_compare}\"" if l_compare.is_a?(String)
175:         result = eval("#{l_compare} #{function_hash["operator"]} #{function_hash["r_compare"]}")
176:         (result.nil?) ? false : result
177:       end
178:     end
eval_compound_statement(expression) click to toggle source

Evaluates a compound statement

     # File lib/mcollective/matcher.rb, line 115
115:     def self.eval_compound_statement(expression)
116:       if expression.values.first =~ /^\//
117:         return Util.has_cf_class?(expression.values.first)
118:       elsif expression.values.first =~ />=|<=|=|<|>/
119:         optype = expression.values.first.match(/>=|<=|=|<|>/)
120:         name, value = expression.values.first.split(optype[0])
121:         unless value.split("")[0] == "/"
122:           optype[0] == "=" ? optype = "==" : optype = optype[0]
123:         else
124:           optype = "=~"
125:         end
126: 
127:         return Util.has_fact?(name,value, optype).to_s
128:       else
129:         return Util.has_cf_class?(expression.values.first)
130:       end
131:     end
execute_function(function_hash) click to toggle source

Returns the result of an executed function

     # File lib/mcollective/matcher.rb, line 78
 78:     def self.execute_function(function_hash)
 79:       # In the case where a data plugin isn't present there are two ways we can handle
 80:       # the raised exception. The function result can either be false or the entire
 81:       # expression can fail.
 82:       #
 83:       # In the case where we return the result as false it opens us op to unexpected
 84:       # negation behavior.
 85:       #
 86:       #   !foo('bar').name = bar
 87:       #
 88:       # In this case the user would expect discovery to match on all machines where
 89:       # the name value of the foo function does not equal bar. If a non existent function
 90:       # returns false then it is posible to match machines where the name value of the
 91:       # foo function is bar.
 92:       #
 93:       # Instead we raise a DDLValidationError to prevent this unexpected behavior from
 94:       # happening.
 95: 
 96:       result = Data.send(function_hash["name"], function_hash["params"])
 97: 
 98:       if function_hash["value"]
 99:         begin
100:           eval_result = result.send(function_hash["value"])
101:         rescue
102:           # If data field has not been set we set the comparison result to nil
103:           eval_result = nil
104:         end
105:         return eval_result
106:       else
107:         return result
108:       end
109:     rescue NoMethodError
110:       Log.debug("cannot execute discovery function '#{function_hash["name"]}'. data plugin not found")
111:       raise DDLValidationError
112:     end

Disabled; run with --debug to generate this.

[Validate]

Generated with the Darkfish Rdoc Generator 1.1.6.