mcollective-client 2.0.0 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of mcollective-client might be problematic. Click here for more details.

Files changed (140) hide show
  1. data/lib/mcollective.rb +32 -23
  2. data/lib/mcollective/agent.rb +5 -0
  3. data/lib/mcollective/agents.rb +5 -16
  4. data/lib/mcollective/aggregate.rb +61 -0
  5. data/lib/mcollective/aggregate/base.rb +40 -0
  6. data/lib/mcollective/aggregate/result.rb +9 -0
  7. data/lib/mcollective/aggregate/result/base.rb +25 -0
  8. data/lib/mcollective/aggregate/result/collection_result.rb +19 -0
  9. data/lib/mcollective/aggregate/result/numeric_result.rb +13 -0
  10. data/lib/mcollective/application.rb +7 -4
  11. data/lib/mcollective/applications.rb +3 -14
  12. data/lib/mcollective/cache.rb +145 -0
  13. data/lib/mcollective/client.rb +10 -87
  14. data/lib/mcollective/config.rb +22 -8
  15. data/lib/mcollective/data.rb +87 -0
  16. data/lib/mcollective/data/base.rb +67 -0
  17. data/lib/mcollective/data/result.rb +40 -0
  18. data/lib/mcollective/ddl.rb +113 -0
  19. data/lib/mcollective/ddl/agentddl.rb +185 -0
  20. data/lib/mcollective/ddl/base.rb +220 -0
  21. data/lib/mcollective/ddl/dataddl.rb +56 -0
  22. data/lib/mcollective/ddl/discoveryddl.rb +52 -0
  23. data/lib/mcollective/ddl/validatorddl.rb +6 -0
  24. data/lib/mcollective/discovery.rb +143 -0
  25. data/lib/mcollective/generators.rb +7 -0
  26. data/lib/mcollective/generators/agent_generator.rb +51 -0
  27. data/lib/mcollective/generators/base.rb +46 -0
  28. data/lib/mcollective/generators/data_generator.rb +51 -0
  29. data/lib/mcollective/generators/templates/action_snippet.erb +13 -0
  30. data/lib/mcollective/generators/templates/data_input_snippet.erb +7 -0
  31. data/lib/mcollective/generators/templates/ddl.erb +8 -0
  32. data/lib/mcollective/generators/templates/plugin.erb +7 -0
  33. data/lib/mcollective/logger/console_logger.rb +15 -15
  34. data/lib/mcollective/matcher.rb +167 -0
  35. data/lib/mcollective/matcher/parser.rb +60 -25
  36. data/lib/mcollective/matcher/scanner.rb +156 -78
  37. data/lib/mcollective/message.rb +47 -6
  38. data/lib/mcollective/monkey_patches.rb +17 -0
  39. data/lib/mcollective/optionparser.rb +18 -1
  40. data/lib/mcollective/pluginmanager.rb +3 -3
  41. data/lib/mcollective/pluginpackager.rb +10 -3
  42. data/lib/mcollective/pluginpackager/agent_definition.rb +28 -20
  43. data/lib/mcollective/pluginpackager/standard_definition.rb +11 -9
  44. data/lib/mcollective/registration/base.rb +3 -1
  45. data/lib/mcollective/rpc.rb +18 -24
  46. data/lib/mcollective/rpc/agent.rb +37 -113
  47. data/lib/mcollective/rpc/client.rb +186 -64
  48. data/lib/mcollective/rpc/helpers.rb +42 -80
  49. data/lib/mcollective/rpc/progress.rb +3 -3
  50. data/lib/mcollective/rpc/reply.rb +37 -13
  51. data/lib/mcollective/rpc/request.rb +17 -6
  52. data/lib/mcollective/rpc/result.rb +9 -5
  53. data/lib/mcollective/rpc/stats.rb +71 -24
  54. data/lib/mcollective/security/base.rb +41 -34
  55. data/lib/mcollective/shell.rb +1 -1
  56. data/lib/mcollective/ssl.rb +34 -0
  57. data/lib/mcollective/util.rb +194 -23
  58. data/lib/mcollective/validator.rb +80 -0
  59. data/spec/fixtures/util/1.in +10 -0
  60. data/spec/fixtures/util/1.out +10 -0
  61. data/spec/fixtures/util/2.in +1 -0
  62. data/spec/fixtures/util/2.out +1 -0
  63. data/spec/fixtures/util/3.in +1 -0
  64. data/spec/fixtures/util/3.out +2 -0
  65. data/spec/fixtures/util/4.in +5 -0
  66. data/spec/fixtures/util/4.out +9 -0
  67. data/spec/spec.opts +1 -1
  68. data/spec/spec_helper.rb +2 -0
  69. data/spec/unit/agents_spec.rb +34 -19
  70. data/spec/unit/aggregate/base_spec.rb +57 -0
  71. data/spec/unit/aggregate/result/base_spec.rb +28 -0
  72. data/spec/unit/aggregate/result/collection_result_spec.rb +18 -0
  73. data/spec/unit/aggregate/result/numeric_result_spec.rb +22 -0
  74. data/spec/unit/aggregate_spec.rb +110 -0
  75. data/spec/unit/application_spec.rb +8 -3
  76. data/spec/unit/applications_spec.rb +2 -2
  77. data/spec/unit/cache_spec.rb +115 -0
  78. data/spec/unit/client_spec.rb +78 -0
  79. data/spec/unit/config_spec.rb +32 -34
  80. data/spec/unit/data/base_spec.rb +90 -0
  81. data/spec/unit/data/result_spec.rb +64 -0
  82. data/spec/unit/data_spec.rb +158 -0
  83. data/spec/unit/ddl/agentddl_spec.rb +217 -0
  84. data/spec/unit/{rpc/ddl_spec.rb → ddl/base_spec.rb} +238 -224
  85. data/spec/unit/ddl/dataddl_spec.rb +65 -0
  86. data/spec/unit/ddl/discoveryddl_spec.rb +58 -0
  87. data/spec/unit/ddl_spec.rb +84 -0
  88. data/spec/unit/discovery_spec.rb +196 -0
  89. data/spec/unit/facts/base_spec.rb +1 -1
  90. data/spec/unit/generators/agent_generator_spec.rb +72 -0
  91. data/spec/unit/generators/base_spec.rb +83 -0
  92. data/spec/unit/generators/data_generator_spec.rb +37 -0
  93. data/spec/unit/generators/snippets/agent_ddl +19 -0
  94. data/spec/unit/generators/snippets/data_ddl +20 -0
  95. data/spec/unit/logger/console_logger_spec.rb +76 -0
  96. data/spec/unit/logger/syslog_logger_spec.rb +2 -2
  97. data/spec/unit/matcher/parser_spec.rb +27 -10
  98. data/spec/unit/matcher/scanner_spec.rb +108 -5
  99. data/spec/unit/matcher_spec.rb +260 -0
  100. data/spec/unit/message_spec.rb +35 -13
  101. data/spec/unit/optionparser_spec.rb +2 -2
  102. data/spec/unit/pluginpackager/agent_definition_spec.rb +59 -42
  103. data/spec/unit/pluginpackager/standard_definition_spec.rb +10 -8
  104. data/spec/unit/pluginpackager_spec.rb +131 -0
  105. data/spec/unit/plugins/mcollective/aggregate/average_spec.rb +45 -0
  106. data/spec/unit/plugins/mcollective/aggregate/sum_spec.rb +31 -0
  107. data/spec/unit/plugins/mcollective/aggregate/summary_spec.rb +45 -0
  108. data/spec/unit/plugins/mcollective/connector/activemq_spec.rb +1 -1
  109. data/spec/unit/plugins/mcollective/connector/rabbitmq_spec.rb +478 -0
  110. data/spec/unit/plugins/mcollective/connector/stomp_spec.rb +2 -0
  111. data/spec/unit/plugins/mcollective/data/agent_data_spec.rb +43 -0
  112. data/spec/unit/plugins/mcollective/data/fstat_data_spec.rb +135 -0
  113. data/spec/unit/plugins/mcollective/discovery/flatfile_spec.rb +48 -0
  114. data/spec/unit/plugins/mcollective/discovery/mc_spec.rb +40 -0
  115. data/spec/unit/plugins/mcollective/packagers/debpackage_packager_spec.rb +41 -15
  116. data/spec/unit/plugins/mcollective/packagers/ospackage_spec.rb +1 -1
  117. data/spec/unit/plugins/mcollective/packagers/rpmpackage_packager_spec.rb +22 -38
  118. data/spec/unit/plugins/mcollective/validator/array_validator_spec.rb +19 -0
  119. data/spec/unit/plugins/mcollective/validator/ipv4address_validator_spec.rb +19 -0
  120. data/spec/unit/plugins/mcollective/validator/ipv6address_validator_spec.rb +19 -0
  121. data/spec/unit/plugins/mcollective/validator/length_validator_spec.rb +19 -0
  122. data/spec/unit/plugins/mcollective/validator/regex_validator_spec.rb +19 -0
  123. data/spec/unit/plugins/mcollective/validator/shellsafe_validator_spec.rb +21 -0
  124. data/spec/unit/plugins/mcollective/validator/typecheck_validator_spec.rb +23 -0
  125. data/spec/unit/registration/base_spec.rb +1 -1
  126. data/spec/unit/rpc/actionrunner_spec.rb +2 -2
  127. data/spec/unit/rpc/agent_spec.rb +41 -65
  128. data/spec/unit/rpc/client_spec.rb +430 -134
  129. data/spec/unit/rpc/reply_spec.rb +31 -1
  130. data/spec/unit/rpc/request_spec.rb +33 -12
  131. data/spec/unit/rpc/result_spec.rb +7 -0
  132. data/spec/unit/rpc/stats_spec.rb +14 -14
  133. data/spec/unit/rpc_spec.rb +16 -0
  134. data/spec/unit/security/base_spec.rb +8 -8
  135. data/spec/unit/ssl_spec.rb +20 -2
  136. data/spec/unit/string_spec.rb +15 -0
  137. data/spec/unit/util_spec.rb +141 -21
  138. data/spec/unit/validator_spec.rb +67 -0
  139. metadata +145 -7
  140. data/lib/mcollective/rpc/ddl.rb +0 -258
@@ -12,5 +12,172 @@ module MCollective
12
12
  module Matcher
13
13
  autoload :Parser, "mcollective/matcher/parser"
14
14
  autoload :Scanner, "mcollective/matcher/scanner"
15
+
16
+ # Helper creates a hash from a function call string
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
76
+
77
+ # Returns the result of an executed function
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
+ eval_result = result.send(function_hash["value"])
100
+ return eval_result
101
+ else
102
+ return result
103
+ end
104
+ rescue NoMethodError
105
+ Log.debug("cannot execute discovery function '#{function_hash["name"]}'. data plugin not found")
106
+ raise DDLValidationError
107
+ end
108
+
109
+ # Evaluates a compound statement
110
+ def self.eval_compound_statement(expression)
111
+ if expression.values.first =~ /^\//
112
+ return Util.has_cf_class?(expression.values.first)
113
+ elsif expression.values.first =~ />=|<=|=|<|>/
114
+ optype = expression.values.first.match(/>=|<=|=|<|>/)
115
+ name, value = expression.values.first.split(optype[0])
116
+ unless value.split("")[0] == "/"
117
+ optype[0] == "=" ? optype = "==" : optype = optype[0]
118
+ else
119
+ optype = "=~"
120
+ end
121
+
122
+ return Util.has_fact?(name,value, optype).to_s
123
+ else
124
+ return Util.has_cf_class?(expression.values.first)
125
+ end
126
+ end
127
+
128
+ # Returns the result of an evaluated compound statement that
129
+ # includes a function
130
+ def self.eval_compound_fstatement(function_hash)
131
+ l_compare = execute_function(function_hash)
132
+
133
+ # Prevent unwanted discovery by limiting comparison operators
134
+ # on Strings and Booleans
135
+ if((l_compare.is_a?(String) || l_compare.is_a?(TrueClass) || l_compare.is_a?(FalseClass)) && function_hash["operator"].match(/<|>/))
136
+ Log.debug "Cannot do > and < comparison on Booleans and Strings '#{l_compare} #{function_hash["operator"]} #{function_hash["r_compare"]}'"
137
+ return false
138
+ end
139
+
140
+ # Prevent backticks in function parameters
141
+ if function_hash["params"] =~ /`/
142
+ Log.debug("Cannot use backticks in function parameters")
143
+ return false
144
+ end
145
+
146
+ # Escape strings for evaluation
147
+ function_hash["r_compare"] = "\"#{function_hash["r_compare"]}\"" if(l_compare.is_a?(String) && !(function_hash["operator"] =~ /=~|!=~/))
148
+
149
+ # Do a regex comparison if right compare string is a regex
150
+ if function_hash["operator"] =~ /(=~|!=~)/
151
+ # Fail if left compare value isn't a string
152
+ unless l_compare.is_a?(String)
153
+ Log.debug("Cannot do a regex check on a non string value.")
154
+ return false
155
+ else
156
+ compare_result = l_compare.match(function_hash["r_compare"])
157
+ # Flip return value for != operator
158
+ if function_hash["operator"] == "!=~"
159
+ !((compare_result.nil?) ? false : true)
160
+ else
161
+ (compare_result.nil?) ? false : true
162
+ end
163
+ end
164
+ # Otherwise evaluate the logical comparison
165
+ else
166
+ l_compare = "\"#{l_compare}\"" if l_compare.is_a?(String)
167
+ result = eval("#{l_compare} #{function_hash["operator"]} #{function_hash["r_compare"]}")
168
+ (result.nil?) ? false : result
169
+ end
170
+ end
171
+
172
+ # Creates a callstack to be evaluated from a compound evaluation string
173
+ def self.create_compound_callstack(call_string)
174
+ callstack = Matcher::Parser.new(call_string).execution_stack
175
+ callstack.each_with_index do |statement, i|
176
+ if statement.keys.first == "fstatement"
177
+ callstack[i]["fstatement"] = create_function_hash(statement.values.first)
178
+ end
179
+ end
180
+ callstack
181
+ end
15
182
  end
16
183
  end
@@ -6,11 +6,44 @@ module MCollective
6
6
  def initialize(args)
7
7
  @scanner = Scanner.new(args)
8
8
  @execution_stack = []
9
+ @parse_errors = []
10
+ @token_errors = []
11
+ @paren_errors = []
9
12
  parse
13
+ exit_with_token_errors if @token_errors.size > 0
14
+ exit_with_parse_errors if @parse_errors.size > 0
15
+ exit_with_paren_errors if @paren_errors.size > 0
16
+ end
17
+
18
+ # Exit and highlight any malformed tokens
19
+ def exit_with_token_errors
20
+ @token_errors.each do |error_range|
21
+ (error_range[0]..error_range[1]).each do |i|
22
+ @scanner.arguments[i] = Util.colorize(:red, @scanner.arguments[i])
23
+ end
24
+ end
25
+ raise "Malformed token(s) found while parsing -S input #{@scanner.arguments.join}"
26
+ end
27
+
28
+ def exit_with_parse_errors
29
+ @parse_errors.each do |error_range|
30
+ (error_range[0]..error_range[1]).each do |i|
31
+ @scanner.arguments[i] = Util.colorize(:red, @scanner.arguments[i])
32
+ end
33
+ end
34
+ raise "Parse errors found while parsing -S input #{ @scanner.arguments.join}"
35
+ end
36
+
37
+ def exit_with_paren_errors
38
+ @paren_errors.each do |i|
39
+ @scanner.arguments[i] = Util.colorize(:red, @scanner.arguments[i])
40
+ end
41
+ raise "Missing parenthesis found while parsing -S input #{@scanner.arguments.join}"
10
42
  end
11
43
 
12
44
  # Parse the input string, one token at a time a contruct the call stack
13
45
  def parse
46
+ pre_index = @scanner.token_index
14
47
  p_token,p_token_value = nil
15
48
  c_token,c_token_value = @scanner.get_token
16
49
  parenth = 0
@@ -21,71 +54,73 @@ module MCollective
21
54
 
22
55
  unless n_token == " "
23
56
  case c_token
57
+ when "bad_token"
58
+ @token_errors << c_token_value
59
+
24
60
  when "and"
25
- unless (n_token =~ /not|statement|\(/) || (scanner.token_index == scanner.arguments.size)
26
- raise "Error at column #{scanner.token_index}. \nExpected 'not', 'statement' or '('. Found '#{n_token_value}'"
61
+ unless (n_token =~ /not|fstatement|statement|\(/) || (scanner.token_index == scanner.arguments.size) && !(n_token == nil)
62
+ @parse_errors << [pre_index, scanner.token_index]
27
63
  end
28
64
 
29
65
  if p_token == nil
30
- raise "Error at column #{scanner.token_index}. \n Expression cannot start with 'and'"
66
+ @parse_errors << [pre_index - c_token.size, scanner.token_index]
31
67
  elsif (p_token == "and" || p_token == "or")
32
- raise "Error at column #{scanner.token_index}. \n #{p_token} cannot be followed by 'and'"
68
+ @parse_errors << [pre_index - 1 - p_token.size, pre_index - 1]
33
69
  end
34
70
 
35
71
  when "or"
36
- unless (n_token =~ /not|statement|\(/) || (scanner.token_index == scanner.arguments.size)
37
- raise "Error at column #{scanner.token_index}. \nExpected 'not', 'statement', '('. Found '#{n_token_value}'"
72
+ unless (n_token =~ /not|fstatement|statement|\(/) || (scanner.token_index == scanner.arguments.size) && !(n_token == nil)
73
+ @parse_errors << [pre_index, scanner.token_index]
38
74
  end
39
75
 
40
76
  if p_token == nil
41
- raise "Error at column #{scanner.token_index}. \n Expression cannot start with 'or'"
77
+ @parse_errors << [pre_index - c_token.size, scanner.token_index]
42
78
  elsif (p_token == "and" || p_token == "or")
43
- raise "Error at column #{scanner.token_index}. \n #{p_token} cannot be followed by 'or'"
79
+ @parse_errors << [pre_index - 1 - p_token.size, pre_index - 1]
44
80
  end
45
81
 
46
82
  when "not"
47
- unless n_token =~ /statement|\(|not/
48
- raise "Error at column #{scanner.token_index}. \nExpected 'statement' or '('. Found '#{n_token_value}'"
83
+ unless n_token =~ /fstatement|statement|\(|not/ && !(n_token == nil)
84
+ @parse_errors << [pre_index, scanner.token_index]
49
85
  end
50
86
 
51
- when "statement"
87
+ when "statement","fstatement"
52
88
  unless n_token =~ /and|or|\)/
53
89
  unless scanner.token_index == scanner.arguments.size
54
- raise "Error at column #{scanner.token_index}. \nExpected 'and', 'or', ')'. Found '#{n_token_value}'"
90
+ @parse_errors << [pre_index, scanner.token_index]
55
91
  end
56
92
  end
57
93
 
58
94
  when ")"
59
95
  unless (n_token =~ /|and|or|not|\(/)
60
96
  unless(scanner.token_index == scanner.arguments.size)
61
- raise "Error at column #{scanner.token_index}. \nExpected 'and', 'or', 'not' or '('. Found '#{n_token_value}'"
97
+ @parse_errors << [pre_index, scanner.token_index]
62
98
  end
63
99
  end
64
- parenth += 1
100
+ unless @paren_errors.empty?
101
+ @paren_errors.pop
102
+ else
103
+ @paren_errors.push((n_token.nil?) ? scanner.token_index - 1: scanner.token_index - n_token_value.size)
104
+ end
65
105
 
66
106
  when "("
67
- unless n_token =~ /statement|not|\(/
68
- raise "Error at column #{scanner.token_index}. \nExpected 'statement', '(', not. Found '#{n_token_value}'"
107
+ unless n_token =~ /fstatement|statement|not|\(/
108
+ @parse_errors << [pre_index, scanner.token_index]
69
109
  end
70
- parenth -= 1
110
+ @paren_errors.push((n_token.nil?) ? scanner.token_index - 1: scanner.token_index - n_token_value.size)
71
111
 
72
112
  else
73
- raise "Unexpected token found at column #{scanner.token_index}. '#{c_token_value}'"
113
+ @parse_errors << [pre_index, scanner.token_index]
74
114
  end
75
115
 
76
- unless n_token == " "
116
+ unless n_token == " " ||c_token == "bad_token"
77
117
  @execution_stack << {c_token => c_token_value}
78
118
  end
79
119
 
80
120
  p_token, p_token_value = c_token, c_token_value
81
121
  c_token, c_token_value = n_token, n_token_value
82
122
  end
83
- end
84
-
85
- if parenth < 0
86
- raise "Error. Missing parentheses ')'."
87
- elsif parenth > 0
88
- raise "Error. Missing parentheses '('."
123
+ pre_index = @scanner.token_index
89
124
  end
90
125
  end
91
126
  end
@@ -5,7 +5,9 @@ module MCollective
5
5
 
6
6
  def initialize(arguments)
7
7
  @token_index = 0
8
- @arguments = arguments
8
+ @arguments = arguments.split("")
9
+ @seperation_counter = 0
10
+ @white_spaces = 0
9
11
  end
10
12
 
11
13
  # Scans the input string and identifies single language tokens
@@ -14,110 +16,186 @@ module MCollective
14
16
  return nil
15
17
  end
16
18
 
17
- begin
18
- case @arguments.split("")[@token_index]
19
- when "("
20
- return "(", "("
21
-
22
- when ")"
23
- return ")", ")"
19
+ case @arguments[@token_index]
20
+ when "("
21
+ return "(", "("
24
22
 
25
- when "n"
26
- if (@arguments.split("")[@token_index + 1] == "o") && (@arguments.split("")[@token_index + 2] == "t") && ((@arguments.split("")[@token_index + 3] == " ") || (@arguments.split("")[@token_index + 3] == "("))
27
- @token_index += 2
28
- return "not", "not"
29
- else
30
- gen_statement
31
- end
23
+ when ")"
24
+ return ")", ")"
32
25
 
33
- when "!"
26
+ when "n"
27
+ if (@arguments[@token_index + 1] == "o") && (@arguments[@token_index + 2] == "t") && ((@arguments[@token_index + 3] == " ") || (@arguments[@token_index + 3] == "("))
28
+ @token_index += 2
34
29
  return "not", "not"
30
+ else
31
+ gen_statement
32
+ end
35
33
 
36
- when "a"
37
- if (@arguments.split("")[@token_index + 1] == "n") && (@arguments.split("")[@token_index + 2] == "d") && ((@arguments.split("")[@token_index + 3] == " ") || (@arguments.split("")[@token_index + 3] == "("))
38
- @token_index += 2
39
- return "and", "and"
40
- else
41
- gen_statement
42
- end
43
-
44
- when "o"
45
- if (@arguments.split("")[@token_index + 1] == "r") && ((@arguments.split("")[@token_index + 2] == " ") || (@arguments.split("")[@token_index + 2] == "("))
46
- @token_index += 1
47
- return "or", "or"
48
- else
49
- gen_statement
50
- end
34
+ when "!"
35
+ return "not", "not"
51
36
 
52
- when " "
53
- return " ", " "
37
+ when "a"
38
+ if (@arguments[@token_index + 1] == "n") && (@arguments[@token_index + 2] == "d") && ((@arguments[@token_index + 3] == " ") || (@arguments[@token_index + 3] == "("))
39
+ @token_index += 2
40
+ return "and", "and"
41
+ else
42
+ gen_statement
43
+ end
54
44
 
45
+ when "o"
46
+ if (@arguments[@token_index + 1] == "r") && ((@arguments[@token_index + 2] == " ") || (@arguments[@token_index + 2] == "("))
47
+ @token_index += 1
48
+ return "or", "or"
55
49
  else
56
50
  gen_statement
57
51
  end
52
+
53
+ when " "
54
+ return " ", " "
55
+
56
+ else
57
+ gen_statement
58
58
  end
59
- rescue NoMethodError => e
60
- pp e
61
- raise "Cannot end statement with 'and', 'or', 'not'"
62
59
  end
63
60
 
64
61
  private
65
62
  # Helper generates a statement token
66
63
  def gen_statement
64
+ func = false
67
65
  current_token_value = ""
68
66
  j = @token_index
69
67
 
70
68
  begin
71
- if (@arguments.split("")[j] == "/")
69
+ if (@arguments[j] == "/")
72
70
  begin
73
- current_token_value << @arguments.split("")[j]
71
+ current_token_value << @arguments[j]
72
+ j += 1
73
+ end until (j >= @arguments.size) || (@arguments[j] =~ /\s/)
74
+ elsif (@arguments[j] =~ /=|<|>/)
75
+ while !(@arguments[j] =~ /=|<|>/)
76
+ current_token_value << @arguments[j]
74
77
  j += 1
75
- if @arguments.split("")[j] == "/"
76
- current_token_value << "/"
78
+ end
79
+
80
+ current_token_value << @arguments[j]
81
+ j += 1
82
+
83
+ if @arguments[j] == "/"
84
+ begin
85
+ current_token_value << @arguments[j]
86
+ j += 1
87
+ if @arguments[j] == "/"
88
+ current_token_value << "/"
89
+ break
90
+ end
91
+ end until (j >= @arguments.size) || (@arguments[j] =~ /\//)
92
+ else
93
+ while (j < @arguments.size) && ((@arguments[j] != " ") && (@arguments[j] != ")"))
94
+ current_token_value << @arguments[j]
95
+ j += 1
96
+ end
97
+ end
98
+ else
99
+ begin
100
+ # Identify and tokenize regular expressions by ignoring everything between /'s
101
+ if @arguments[j] == '/'
102
+ current_token_value << '/'
103
+ j+=1
104
+ while(j < @arguments.size && @arguments[j] != '/')
105
+ current_token_value << @arguments[j]
106
+ j += 1
107
+ end
108
+ current_token_value << @arguments[j] if @arguments[j]
77
109
  break
78
110
  end
79
- end until (j >= @arguments.size) || (@arguments.split("")[j] =~ /\//)
80
- elsif (@arguments.split("")[j] =~ /=|<|>/)
81
- while !(@arguments.split("")[j] =~ /=|<|>/)
82
- current_token_value << @arguments.split("")[j]
83
- j += 1
84
- end
85
-
86
- current_token_value << @arguments.split("")[j]
87
- j += 1
88
-
89
- if @arguments.split("")[j] == "/"
90
- begin
91
- current_token_value << @arguments.split("")[j]
92
- j += 1
93
- if @arguments.split("")[j] == "/"
94
- current_token_value << "/"
95
- break
96
- end
97
- end until (j >= @arguments.size) || (@arguments.split("")[j] =~ /\//)
98
- else
99
- while (j < @arguments.size) && ((@arguments.split("")[j] != " ") && (@arguments.split("")[j] != ")"))
100
- current_token_value << @arguments.split("")[j]
101
- j += 1
102
- end
103
- end
104
- else
105
- begin
106
- current_token_value << @arguments.split("")[j]
107
- j += 1
108
- end until (j >= @arguments.size) || (@arguments.split("")[j] =~ /\s|\)/)
109
- end
110
- rescue Exception => e
111
- raise "Invalid token found - '#{current_token_value}'"
111
+ if @arguments[j+1] == "("
112
+ func = true
113
+ be_greedy = true
114
+ end
115
+ current_token_value << @arguments[j]
116
+ if be_greedy
117
+ while !(j+1 >= @arguments.size) && @arguments[j] != ')'
118
+ j += 1
119
+ current_token_value << @arguments[j]
112
120
  end
113
-
114
- if current_token_value =~ /^(and|or|not|!)$/
115
- raise "Class name cannot be 'and', 'or', 'not'. Found '#{current_token_value}'"
121
+ j += 1
122
+ be_greedy = false
123
+ else
124
+ j += 1
125
+ end
126
+ if(@arguments[j] == ' ')
127
+ break if(is_klass?(j) && !(@arguments[j-1] =~ /=|<|>/))
128
+ end
129
+ if( (@arguments[j] == ' ') && (@seperation_counter < 2) && !(current_token_value.match(/^.+(=|<|>).+$/)) )
130
+ if((index = lookahead(j)))
131
+ j = index
116
132
  end
133
+ end
134
+ end until (j >= @arguments.size) || (@arguments[j] =~ /\s|\)/)
135
+ @seperation_counter = 0
136
+ end
137
+ rescue Exception => e
138
+ raise "An exception was raised while trying to tokenize '#{current_token_value} - #{e}'"
139
+ end
117
140
 
118
- @token_index += current_token_value.size - 1
119
- return "statement", current_token_value
141
+ @token_index += current_token_value.size + @white_spaces - 1
142
+ @white_spaces = 0
143
+
144
+ # bar(
145
+ if current_token_value.match(/.+?\($/)
146
+ return "bad_token", [@token_index - current_token_value.size + 1, @token_index]
147
+ # /foo/=bar
148
+ elsif current_token_value.match(/^\/.+?\/(<|>|=).+/)
149
+ return "bad_token", [@token_index - current_token_value.size + 1, @token_index]
150
+ elsif current_token_value.match(/^.+?\/(<|>|=).+/)
151
+ return "bad_token", [@token_index - current_token_value.size + 1, @token_index]
152
+ else
153
+ if func
154
+ if current_token_value.match(/^.+?\((\s*(')[^']*(')\s*(,\s*(')[^']*('))*)?\)(\.[a-zA-Z0-9_]+)?((!=|<=|>=|=|>|<).+)?$/) ||
155
+ current_token_value.match(/^.+?\((\s*(")[^"]*(")\s*(,\s*(")[^"]*("))*)?\)(\.[a-zA-Z0-9_]+)?((!=|<=|>=|=|>|<).+)?$/)
156
+ return "fstatement", current_token_value
157
+ else
158
+ return "bad_token", [@token_index - current_token_value.size + 1, @token_index]
120
159
  end
160
+ else
161
+ slash_err = false
162
+ current_token_value.split('').each do |c|
163
+ if c == '/'
164
+ slash_err = !slash_err
165
+ end
166
+ end
167
+ return "bad_token", [@token_index - current_token_value.size + 1, @token_index] if slash_err
168
+ return "statement", current_token_value
169
+ end
170
+ end
171
+ end
172
+
173
+ # Deal with special puppet class statement
174
+ def is_klass?(j)
175
+ while(j < @arguments.size && @arguments[j] == ' ')
176
+ j += 1
177
+ end
178
+
179
+ if @arguments[j] =~ /=|<|>/
180
+ return false
181
+ else
182
+ return true
183
+ end
184
+ end
185
+
186
+ # Eat spaces while looking for the next comparison symbol
187
+ def lookahead(index)
188
+ index += 1
189
+ while(index <= @arguments.size)
190
+ @white_spaces += 1
191
+ unless(@arguments[index] =~ /\s/)
192
+ @seperation_counter +=1
193
+ return index
194
+ end
195
+ index += 1
121
196
  end
197
+ return nil
198
+ end
122
199
  end
200
+ end
123
201
  end