Skip to content
Browse files

14254 - Create pluggable data sources usable in agents and discovery

- Fixed String comparison
- Removed ability to do < and > comparisons with Strings and Booleans
- Changed function hash keys to strings from symbols
- Removed exits from parser
- Fixed Scanner regex that identifies correct function statements
- Updated Scanner and Parser spec tests
  • Loading branch information...
1 parent 5602d25 commit adc517e5a05c809fb701a3fa99e1a1974ad7f403 @ploubser committed with ripienaar May 9, 2012
View
2 lib/mcollective/client.rb
@@ -271,7 +271,7 @@ def timeout_for_compound_filter(compound_filter)
compound_filter.each do |filter|
filter.each do |statement|
if statement["fstatement"]
- pluginname = Data.pluginname(statement["fstatement"][:name])
+ pluginname = Data.pluginname(statement["fstatement"]["name"])
ddl = DDL.new(pluginname, :data)
timeout += ddl.meta[:timeout]
end
View
18 lib/mcollective/matcher/parser.rb
@@ -17,34 +17,28 @@ def initialize(args)
# Exit and highlight any malformed tokens
def exit_with_token_errors
- puts "Error. Malformed token(s) found while parsing -S input"
@token_errors.each do |error_range|
(error_range[0]..error_range[1]).each do |i|
@scanner.arguments[i] = Util.colorize(:red, @scanner.arguments[i])
end
end
- puts @scanner.arguments.join
- exit 1
+ raise "Malformed token(s) found while parsing -S input #{@scanner.arguments.join}"
end
def exit_with_parse_errors
- puts "Error. Parser errors found while parsing -S input"
@parse_errors.each do |error_range|
(error_range[0]..error_range[1]).each do |i|
@scanner.arguments[i] = Util.colorize(:red, @scanner.arguments[i])
end
end
- puts @scanner.arguments.join
- exit 1
+ raise "Parse errors found while parsing -S input #{ @scanner.arguments.join}"
end
def exit_with_paren_errors
- puts "Error. Missing parenthesis found while parsing -S input"
@paren_errors.each do |i|
@scanner.arguments[i] = Util.colorize(:red, @scanner.arguments[i])
end
- puts @scanner.arguments.join
- exit 1
+ raise "Missing parenthesis found while parsing -S input #{@scanner.arguments.join}"
end
# Parse the input string, one token at a time a contruct the call stack
@@ -64,7 +58,7 @@ def parse
@token_errors << c_token_value
when "and"
- unless (n_token =~ /not|fstatement|statement|\(/) || (scanner.token_index == scanner.arguments.size)
+ unless (n_token =~ /not|fstatement|statement|\(/) || (scanner.token_index == scanner.arguments.size) && !(n_token == nil)
@parse_errors << [pre_index, scanner.token_index]
end
@@ -75,7 +69,7 @@ def parse
end
when "or"
- unless (n_token =~ /not|fstatement|statement|\(/) || (scanner.token_index == scanner.arguments.size)
+ unless (n_token =~ /not|fstatement|statement|\(/) || (scanner.token_index == scanner.arguments.size) && !(n_token == nil)
@parse_errors << [pre_index, scanner.token_index]
end
@@ -86,7 +80,7 @@ def parse
end
when "not"
- unless n_token =~ /fstatement|statement|\(|not/
+ unless n_token =~ /fstatement|statement|\(|not/ && !(n_token == nil)
@parse_errors << [pre_index, scanner.token_index]
end
View
17 lib/mcollective/matcher/scanner.rb
@@ -101,10 +101,10 @@ def gen_statement
end
current_token_value << @arguments[j]
if be_greedy
- begin
+ while !(j+1 >= @arguments.size) && @arguments[j] != ')'
j += 1
current_token_value << @arguments[j]
- end while @arguments[j] != ')'
+ end
j += 1
be_greedy = false
else
@@ -113,25 +113,22 @@ def gen_statement
end until (j >= @arguments.size) || (@arguments[j] =~ /\s|\)/)
end
rescue Exception => e
- raise "An exception was raised while trying to tokenize '#{current_token_value}'"
- puts e
+ raise "An exception was raised while trying to tokenize '#{current_token_value} - #{e}'"
end
@token_index += current_token_value.size - 1
- if current_token_value.match(/^(and|or|not|!)$/)
- return "bad_token", [@token_index - current_token_value.size + 1, @token_index]
# bar(
- elsif current_token_value.match(/.+?\($/)
+ if current_token_value.match(/.+?\($/)
return "bad_token", [@token_index - current_token_value.size + 1, @token_index]
# /foo/=bar
- elsif current_token_value.match(/^\/.+?\/=.+/)
+ elsif current_token_value.match(/^\/.+?\/(<|>|=).+/)
return "bad_token", [@token_index - current_token_value.size + 1, @token_index]
- elsif current_token_value.match(/^.+?\/=.+/)
+ elsif current_token_value.match(/^.+?\/(<|>|=).+/)
return "bad_token", [@token_index - current_token_value.size + 1, @token_index]
else
if func
- if current_token_value.match(/^.+?\((\s*\'.+?\')*(?=(\s*,\s*\'.+?\')*)\s*\)(\.[a-zA-Z0-9_]+)?((=|<|>).+)?$/)
+ if current_token_value.match(/^.+?\((\s*'[^']+'\s*(,\s*'[^']+')*)?\)(\.[a-zA-Z0-9_]+)?((=|<|>).+)?$/)
return "fstatement", current_token_value
else
return "bad_token", [@token_index - current_token_value.size + 1, @token_index]
View
8 lib/mcollective/message.rb
@@ -144,9 +144,9 @@ def validate_compount_filter(compound_filter)
compound_filter.each do |filter|
filter.each do |statement|
if statement["fstatement"]
- functionname = statement["fstatement"][:name]
+ functionname = statement["fstatement"]["name"]
pluginname = Data.pluginname(functionname)
- value = statement["fstatement"][:value]
+ value = statement["fstatement"]["value"]
begin
ddl = DDL.new(pluginname, :data)
@@ -156,9 +156,9 @@ def validate_compount_filter(compound_filter)
# parses numbers and booleans entered as strings into proper
# types of data so that DDL validation will pass
- statement["fstatement"][:params] = Data.ddl_transform_input(ddl, statement["fstatement"][:params])
+ statement["fstatement"]["params"] = Data.ddl_transform_input(ddl, statement["fstatement"]["params"])
- Data.ddl_validate(ddl, statement["fstatement"][:params])
+ Data.ddl_validate(ddl, statement["fstatement"]["params"])
unless value && Data.ddl_has_output?(ddl, value)
raise DDLValidationError, "#{functionname}() does not return a #{value} value"
View
4 lib/mcollective/registration/base.rb
@@ -38,7 +38,9 @@ def config
end
def msg_filter
- {"agent" => "registration"}
+ filter = Util.empty_filter
+ filter["agent"] << "registration"
+ filter
end
def target_collective
View
4 lib/mcollective/runner.rb
@@ -17,8 +17,6 @@ def initialize(configfile)
@agents = Agents.new
- Data.load_data_sources
-
unless Util.windows?
Signal.trap("USR1") do
Log.info("Reloading all agents after receiving USR1 signal")
@@ -36,6 +34,8 @@ def initialize(configfile)
# Starts the main loop, before calling this you should initialize the MCollective::Config singleton.
def run
+ Data.load_data_sources
+
Util.subscribe(Util.make_subscriptions("mcollective", :broadcast))
Util.subscribe(Util.make_subscriptions("mcollective", :directed)) if @config.direct_addressing
View
43 lib/mcollective/util.rb
@@ -295,31 +295,54 @@ def self.ruby_version
RUBY_VERSION
end
+ # Helper creates a hash from a function call string
def self.create_function_hash(function_call)
func_hash = {}
- func, func_hash[:operator], func_hash[:compare_value] = function_call.split(/(>=|<=|<|>|=)/)
- f, func_hash[:value] = func.split(".")
- func_hash[:name], func_hash[:params] = f.split("(")
- func_hash[:params] = func_hash[:params].gsub(")", "").gsub("'", "")
+ func, func_hash["operator"], func_hash["r_compare"] = function_call.split(/(>=|<=|<|>|=)/)
- func_hash[:operator] = "==" if func_hash[:operator] == "="
+ # Deal with dots in function parameters
+ func_parts = func.split(".")
+ func_hash["value"] = func_parts.pop
+ f = func_parts.join(".")
+
+ func_hash["name"], func_hash["params"] = f.split("(")
+ func_hash["params"] = func_hash["params"].gsub(")", "").gsub("'", "")
+
+ func_hash["operator"] = "==" if func_hash["operator"] == "="
func_hash
end
+ # Returns the result of an executed function
def self.execute_function(function_hash)
- result = MCollective::Data.send(function_hash[:name], function_hash[:params])
- if function_hash[:value]
- result.send(function_hash[:value])
+ result = MCollective::Data.send(function_hash["name"], function_hash["params"])
+ if function_hash["value"]
+ eval_result = result.send(function_hash["value"])
+ eval_result = "\"#{eval_result}\"" if eval_result.is_a? String
+ return eval_result
else
- result
+ return result
end
end
+ # Returns the result of an evaluated compound statement that
+ # includes a function
def self.eval_compound_fstatement(function_hash)
- return eval("#{execute_function(function_hash)} #{function_hash[:operator]} #{function_hash[:compare_value]}")
+ l_compare = execute_function(function_hash)
+
+ # Prevent unwanted discovery by limiting comparison operators
+ # on Strings and Booleans
+ if((l_compare.is_a?(String) || l_compare.is_a?(TrueClass) || l_compare.is_a?(FalseClass)) && function_hash["operator"].match(/<|>/))
+ Log.debug "Cannot do > and < comparison on Booleans and Strings '#{l_compare} #{function_hash["operator"]} #{function_hash["r_compare"]}'"
+ return false
+ end
+
+ function_hash["r_compare"] = "\"#{function_hash["r_compare"]}\"" if(l_compare.is_a?(String) && l_compare.match(/\".+\"/))
+
+ return eval("#{l_compare} #{function_hash["operator"]} #{function_hash["r_compare"]}")
end
+ # Creates a callstack to be evaluated from a compound evaluation string
def self.create_compound_callstack(call_string)
callstack = MCollective::Matcher::Parser.new(call_string).execution_stack
callstack.each_with_index do |statement, i|
View
20 spec/unit/matcher/parser_spec.rb
@@ -49,18 +49,18 @@ module Matcher
it "should not parse an incorrect and token" do
expect {
parser = Parser.new("and foo=bar")
- }.to raise_error("Error at column 10. \n Expression cannot start with 'and'")
+ }.to raise_error(RuntimeError, /Parse errors found while parsing -S input.*/)
end
it "should parse a correct 'or' token" do
parser = Parser.new("foo=bar or bar=foo")
parser.execution_stack.should == [{"statement" => "foo=bar"}, {"or" => "or"}, {"statement" => "bar=foo"}]
end
- it "should not parse an incorrect and token" do
- expect {
+ it "should not parse an incorrect or token" do
+ expect{
parser = Parser.new("or foo=bar")
- }.to raise_error("Error at column 9. \n Expression cannot start with 'or'")
+ }.to raise_error(RuntimeError, /Parse errors found while parsing -S input.*/)
end
it "should parse a correct 'not' token" do
@@ -71,9 +71,9 @@ module Matcher
end
it "should not parse an incorrect 'not' token" do
- expect {
+ expect{
parser = Parser.new("foo=bar !")
- }.to raise_error("Error at column 8. \nExpected 'and', 'or', ')'. Found 'not'")
+ }.to raise_error(RuntimeError, /Parse errors found while parsing -S input.*/)
end
it "should parse correct parentheses" do
@@ -82,15 +82,15 @@ module Matcher
end
it "should fail on incorrect parentheses" do
- expect {
+ expect{
parser = Parser.new(")foo=bar(")
- }.to raise_error("Error. Missing parentheses '('.")
+ }.to raise_error(RuntimeError, /Malformed token\(s\) found while parsing -S input.*/)
end
it "should fail on missing parentheses" do
- expect {
+ expect{
parser = Parser.new("(foo=bar")
- }.to raise_error("Error. Missing parentheses ')'.")
+ }.to raise_error(RuntimeError, /Missing parenthesis found while parsing -S input.*/)
end
it "should parse correctly formatted compound statements" do
View
68 spec/unit/matcher/scanner_spec.rb
@@ -59,12 +59,70 @@ module Matcher
token.should == ["statement", "/class/"]
end
- it "should fail if expression terminates with 'and'" do
- scanner = Scanner.new("and")
+ it "should identify a function statement token with a dot value" do
+ scanner = Scanner.new("foo('bar').baz")
+ token = scanner.get_token
+ token.should == ["fstatement", "foo('bar').baz"]
+ end
+
+ it "should identify a function statement token without a dot value" do
+ scanner = Scanner.new("foo('bar')")
+ token = scanner.get_token
+ token.should == ["fstatement", "foo('bar')"]
+ end
+
+ it "should identify a function statement with multiple parameters" do
+ scanner = Scanner.new("foo('bar','baz')")
+ token = scanner.get_token
+ token.should == ["fstatement", "foo('bar','baz')"]
+ end
+
+ it "should identify a bad token when a function is missing its end bracket" do
+ scanner = Scanner.new("foo(")
+ token = scanner.get_token
+ token.should == ["bad_token", [0,3]]
+ end
+
+ it "should identify a bad token when there is a regex before a comparison operator" do
+ scanner = Scanner.new("/foo/=bar")
+ token = scanner.get_token
+ token.should == ["bad_token", [0,8]]
+ end
+
+ it "should identify a bad token where there is a forward slash before a comparison operator" do
+ scanner = Scanner.new("/foo=bar")
+ token = scanner.get_token
+ token.should == ["bad_token", [0,7]]
+ end
+
+ it "should identify a bad token where there is only one forward slash after a comparison operator" do
+ scanner = Scanner.new("foo=/bar")
+ token = scanner.get_token
+ token.should == ["bad_token", [0,7]]
+ end
+
+ it "should identify a bad token where function parameters are not in single quotes" do
+ scanner = Scanner.new("foo(bar)")
+ token = scanner.get_token
+ token.should == ["bad_token", [0,7]]
+ end
- expect {
- token = scanner.get_token
- }.to raise_error("Class name cannot be 'and', 'or', 'not'. Found 'and'")
+ it "should identify a bad token where there are non alphanumerical or underscore chars in the dot value" do
+ scanner = Scanner.new("foo('bar').val-ue")
+ token = scanner.get_token
+ token.should == ["bad_token", [0,16]]
+ end
+
+ it "should identify a bad token where there are chained dot values" do
+ scanner = Scanner.new("foo('bar').a.b")
+ token = scanner.get_token
+ token.should == ["bad_token", [0,13]]
+ end
+
+ it "should identify bad tokens where function parameters are not comma seperated" do
+ scanner = Scanner.new("foo('a' 'b')")
+ token = scanner.get_token
+ token.should == ["bad_token", [0,11]]
end
end
end

0 comments on commit adc517e

Please sign in to comment.
Something went wrong with that request. Please try again.