choria-mcorpc-support 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. checksums.yaml +7 -0
  2. data/bin/mco +64 -0
  3. data/lib/mcollective.rb +63 -0
  4. data/lib/mcollective/agent.rb +5 -0
  5. data/lib/mcollective/agents.rb +149 -0
  6. data/lib/mcollective/aggregate.rb +85 -0
  7. data/lib/mcollective/aggregate/average.ddl +33 -0
  8. data/lib/mcollective/aggregate/average.rb +29 -0
  9. data/lib/mcollective/aggregate/base.rb +40 -0
  10. data/lib/mcollective/aggregate/result.rb +9 -0
  11. data/lib/mcollective/aggregate/result/base.rb +25 -0
  12. data/lib/mcollective/aggregate/result/collection_result.rb +19 -0
  13. data/lib/mcollective/aggregate/result/numeric_result.rb +13 -0
  14. data/lib/mcollective/aggregate/sum.ddl +33 -0
  15. data/lib/mcollective/aggregate/sum.rb +18 -0
  16. data/lib/mcollective/aggregate/summary.ddl +33 -0
  17. data/lib/mcollective/aggregate/summary.rb +53 -0
  18. data/lib/mcollective/application.rb +365 -0
  19. data/lib/mcollective/application/completion.rb +104 -0
  20. data/lib/mcollective/application/describe_filter.rb +87 -0
  21. data/lib/mcollective/application/facts.rb +62 -0
  22. data/lib/mcollective/application/find.rb +23 -0
  23. data/lib/mcollective/application/help.rb +28 -0
  24. data/lib/mcollective/application/inventory.rb +344 -0
  25. data/lib/mcollective/application/ping.rb +82 -0
  26. data/lib/mcollective/application/plugin.rb +369 -0
  27. data/lib/mcollective/application/rpc.rb +111 -0
  28. data/lib/mcollective/applications.rb +134 -0
  29. data/lib/mcollective/cache.rb +145 -0
  30. data/lib/mcollective/client.rb +353 -0
  31. data/lib/mcollective/config.rb +245 -0
  32. data/lib/mcollective/connector.rb +18 -0
  33. data/lib/mcollective/connector/base.rb +26 -0
  34. data/lib/mcollective/data.rb +91 -0
  35. data/lib/mcollective/data/agent_data.ddl +22 -0
  36. data/lib/mcollective/data/agent_data.rb +17 -0
  37. data/lib/mcollective/data/base.rb +67 -0
  38. data/lib/mcollective/data/collective_data.ddl +20 -0
  39. data/lib/mcollective/data/collective_data.rb +9 -0
  40. data/lib/mcollective/data/fact_data.ddl +28 -0
  41. data/lib/mcollective/data/fact_data.rb +55 -0
  42. data/lib/mcollective/data/fstat_data.ddl +89 -0
  43. data/lib/mcollective/data/fstat_data.rb +56 -0
  44. data/lib/mcollective/data/result.rb +45 -0
  45. data/lib/mcollective/ddl.rb +113 -0
  46. data/lib/mcollective/ddl/agentddl.rb +253 -0
  47. data/lib/mcollective/ddl/base.rb +217 -0
  48. data/lib/mcollective/ddl/dataddl.rb +56 -0
  49. data/lib/mcollective/ddl/discoveryddl.rb +52 -0
  50. data/lib/mcollective/ddl/validatorddl.rb +6 -0
  51. data/lib/mcollective/discovery.rb +143 -0
  52. data/lib/mcollective/discovery/flatfile.ddl +11 -0
  53. data/lib/mcollective/discovery/flatfile.rb +48 -0
  54. data/lib/mcollective/discovery/mc.ddl +11 -0
  55. data/lib/mcollective/discovery/mc.rb +30 -0
  56. data/lib/mcollective/discovery/stdin.ddl +11 -0
  57. data/lib/mcollective/discovery/stdin.rb +68 -0
  58. data/lib/mcollective/exceptions.rb +28 -0
  59. data/lib/mcollective/facts.rb +39 -0
  60. data/lib/mcollective/facts/base.rb +100 -0
  61. data/lib/mcollective/facts/yaml_facts.rb +65 -0
  62. data/lib/mcollective/generators.rb +7 -0
  63. data/lib/mcollective/generators/agent_generator.rb +51 -0
  64. data/lib/mcollective/generators/base.rb +46 -0
  65. data/lib/mcollective/generators/data_generator.rb +51 -0
  66. data/lib/mcollective/generators/templates/action_snippet.erb +13 -0
  67. data/lib/mcollective/generators/templates/data_input_snippet.erb +7 -0
  68. data/lib/mcollective/generators/templates/ddl.erb +8 -0
  69. data/lib/mcollective/generators/templates/plugin.erb +7 -0
  70. data/lib/mcollective/log.rb +118 -0
  71. data/lib/mcollective/logger.rb +5 -0
  72. data/lib/mcollective/logger/base.rb +77 -0
  73. data/lib/mcollective/logger/console_logger.rb +61 -0
  74. data/lib/mcollective/logger/file_logger.rb +53 -0
  75. data/lib/mcollective/logger/syslog_logger.rb +53 -0
  76. data/lib/mcollective/matcher.rb +224 -0
  77. data/lib/mcollective/matcher/parser.rb +128 -0
  78. data/lib/mcollective/matcher/scanner.rb +241 -0
  79. data/lib/mcollective/message.rb +248 -0
  80. data/lib/mcollective/monkey_patches.rb +152 -0
  81. data/lib/mcollective/optionparser.rb +197 -0
  82. data/lib/mcollective/pluginmanager.rb +180 -0
  83. data/lib/mcollective/pluginpackager.rb +98 -0
  84. data/lib/mcollective/pluginpackager/agent_definition.rb +94 -0
  85. data/lib/mcollective/pluginpackager/debpackage_packager.rb +237 -0
  86. data/lib/mcollective/pluginpackager/modulepackage_packager.rb +127 -0
  87. data/lib/mcollective/pluginpackager/ospackage_packager.rb +59 -0
  88. data/lib/mcollective/pluginpackager/rpmpackage_packager.rb +180 -0
  89. data/lib/mcollective/pluginpackager/standard_definition.rb +69 -0
  90. data/lib/mcollective/pluginpackager/templates/debian/Makefile.erb +7 -0
  91. data/lib/mcollective/pluginpackager/templates/debian/changelog.erb +5 -0
  92. data/lib/mcollective/pluginpackager/templates/debian/compat.erb +1 -0
  93. data/lib/mcollective/pluginpackager/templates/debian/control.erb +15 -0
  94. data/lib/mcollective/pluginpackager/templates/debian/copyright.erb +8 -0
  95. data/lib/mcollective/pluginpackager/templates/debian/rules.erb +6 -0
  96. data/lib/mcollective/pluginpackager/templates/module/Modulefile.erb +5 -0
  97. data/lib/mcollective/pluginpackager/templates/module/README.md.erb +37 -0
  98. data/lib/mcollective/pluginpackager/templates/module/_manifest.pp.erb +9 -0
  99. data/lib/mcollective/pluginpackager/templates/redhat/rpm_spec.erb +63 -0
  100. data/lib/mcollective/registration/base.rb +91 -0
  101. data/lib/mcollective/rpc.rb +182 -0
  102. data/lib/mcollective/rpc/actionrunner.rb +158 -0
  103. data/lib/mcollective/rpc/agent.rb +374 -0
  104. data/lib/mcollective/rpc/audit.rb +38 -0
  105. data/lib/mcollective/rpc/client.rb +1066 -0
  106. data/lib/mcollective/rpc/helpers.rb +321 -0
  107. data/lib/mcollective/rpc/progress.rb +63 -0
  108. data/lib/mcollective/rpc/reply.rb +87 -0
  109. data/lib/mcollective/rpc/request.rb +86 -0
  110. data/lib/mcollective/rpc/result.rb +90 -0
  111. data/lib/mcollective/rpc/stats.rb +294 -0
  112. data/lib/mcollective/runnerstats.rb +90 -0
  113. data/lib/mcollective/security.rb +26 -0
  114. data/lib/mcollective/security/base.rb +244 -0
  115. data/lib/mcollective/shell.rb +126 -0
  116. data/lib/mcollective/ssl.rb +285 -0
  117. data/lib/mcollective/util.rb +579 -0
  118. data/lib/mcollective/validator.rb +85 -0
  119. data/lib/mcollective/validator/array_validator.ddl +7 -0
  120. data/lib/mcollective/validator/array_validator.rb +9 -0
  121. data/lib/mcollective/validator/ipv4address_validator.ddl +7 -0
  122. data/lib/mcollective/validator/ipv4address_validator.rb +16 -0
  123. data/lib/mcollective/validator/ipv6address_validator.ddl +7 -0
  124. data/lib/mcollective/validator/ipv6address_validator.rb +16 -0
  125. data/lib/mcollective/validator/length_validator.ddl +7 -0
  126. data/lib/mcollective/validator/length_validator.rb +11 -0
  127. data/lib/mcollective/validator/regex_validator.ddl +7 -0
  128. data/lib/mcollective/validator/regex_validator.rb +9 -0
  129. data/lib/mcollective/validator/shellsafe_validator.ddl +7 -0
  130. data/lib/mcollective/validator/shellsafe_validator.rb +13 -0
  131. data/lib/mcollective/validator/typecheck_validator.ddl +7 -0
  132. data/lib/mcollective/validator/typecheck_validator.rb +28 -0
  133. metadata +215 -0
@@ -0,0 +1,53 @@
1
+ module MCollective
2
+ module Logger
3
+ # Implements a syslog based logger using the standard ruby syslog class
4
+ class Syslog_logger<Base
5
+ require 'syslog'
6
+
7
+ include Syslog::Constants
8
+
9
+ def start
10
+ config = Config.instance
11
+
12
+ facility = syslog_facility(config.logfacility)
13
+ level = config.loglevel.to_sym
14
+
15
+ Syslog.close if Syslog.opened?
16
+ Syslog.open(File.basename($0), 3, facility)
17
+
18
+ set_level(level)
19
+ end
20
+
21
+ def syslog_facility(facility)
22
+ begin
23
+ Syslog.const_get("LOG_#{facility.upcase}")
24
+ rescue NameError => e
25
+ STDERR.puts "Invalid syslog facility #{facility} supplied, reverting to USER"
26
+ Syslog::LOG_USER
27
+ end
28
+ end
29
+
30
+ def set_logging_level(level)
31
+ # noop
32
+ end
33
+
34
+ def valid_levels
35
+ {:info => :info,
36
+ :warn => :warning,
37
+ :debug => :debug,
38
+ :fatal => :crit,
39
+ :error => :err}
40
+ end
41
+
42
+ def log(level, from, msg)
43
+ if @known_levels.index(level) >= @known_levels.index(@active_level)
44
+ Syslog.send(map_level(level), "#{from} #{msg}")
45
+ end
46
+ rescue
47
+ # if this fails we probably cant show the user output at all,
48
+ # STDERR it as last resort
49
+ STDERR.puts("#{level}: #{msg}")
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,224 @@
1
+ module MCollective
2
+ # A parser and scanner that creates a stack machine for a simple
3
+ # fact and class matching language used on the CLI to facilitate
4
+ # a rich discovery language
5
+ #
6
+ # Language EBNF
7
+ #
8
+ # compound = ["("] expression [")"] {["("] expression [")"]}
9
+ # expression = [!|not]statement ["and"|"or"] [!|not] statement
10
+ # char = A-Z | a-z | < | > | => | =< | _ | - |* | / { A-Z | a-z | < | > | => | =< | _ | - | * | / | }
11
+ # int = 0|1|2|3|4|5|6|7|8|9{|0|1|2|3|4|5|6|7|8|9|0}
12
+ module Matcher
13
+ require "mcollective/matcher/parser"
14
+ require "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
+ 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
113
+
114
+ # Evaluates a compound statement
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
132
+
133
+ # Returns the result of an evaluated compound statement that
134
+ # includes a function
135
+ def self.eval_compound_fstatement(function_hash)
136
+ l_compare = execute_function(function_hash)
137
+ r_compare = function_hash["r_compare"]
138
+ operator = function_hash["operator"]
139
+
140
+ # Break out early and return false if the function returns nil
141
+ if l_compare.nil?
142
+ return false
143
+ end
144
+
145
+ # Prevent unwanted discovery by limiting comparison operators
146
+ # on Strings and Booleans
147
+ if((l_compare.is_a?(String) || l_compare.is_a?(TrueClass) ||
148
+ l_compare.is_a?(FalseClass)) && function_hash["operator"].match(/<|>/))
149
+ Log.debug("Cannot do > and < comparison on Booleans and Strings " +
150
+ "'#{l_compare} #{function_hash["operator"]} #{function_hash["r_compare"]}'")
151
+ return false
152
+ end
153
+
154
+ # Prevent backticks in function parameters
155
+ if function_hash["params"] =~ /`/
156
+ Log.debug("Cannot use backticks in function parameters")
157
+ return false
158
+ end
159
+
160
+ # Do a regex comparison if right compare string is a regex
161
+ if operator=~ /(=~|!=~)/
162
+ # Fail if left compare value isn't a string
163
+ unless l_compare.is_a?(String)
164
+ Log.debug("Cannot do a regex check on a non string value.")
165
+ return false
166
+ else
167
+ result = l_compare.match(r_compare)
168
+ # Flip return value for != operator
169
+ if function_hash["operator"] == "!=~"
170
+ return !result
171
+ else
172
+ return !!result
173
+ end
174
+ end
175
+ # Otherwise do a normal comparison while taking the type into account
176
+ else
177
+ if l_compare.is_a? String
178
+ r_compare = r_compare.to_s
179
+ elsif r_compare.is_a? String
180
+ if l_compare.is_a? Numeric
181
+ r_compare = r_compare.strip
182
+ begin
183
+ r_compare = Integer(r_compare)
184
+ rescue ArgumentError
185
+ begin
186
+ r_compare = Float(r_compare)
187
+ rescue ArgumentError
188
+ raise ArgumentError, "invalid numeric value: #{r_compare}"
189
+ end
190
+ end
191
+ elsif l_compare.is_a? TrueClass or l_compare.is_a? FalseClass
192
+ r_compare = r_compare.strip
193
+ if r_compare == true.to_s
194
+ r_compare = true
195
+ elsif r_compare == false.to_s
196
+ r_compare = false
197
+ else
198
+ raise ArgumentError, "invalid boolean value: #{r_compare}"
199
+ end
200
+ end
201
+ end
202
+ operator = operator.strip
203
+ if operator =~ /(?:(!=|<=|>=|<|>)|==?)/
204
+ operator = $1 ? $1.to_sym : :==
205
+ else
206
+ raise ArgumentError, "invalid operator: #{operator}"
207
+ end
208
+ result = l_compare.send(operator, r_compare)
209
+ return result
210
+ end
211
+ end
212
+
213
+ # Creates a callstack to be evaluated from a compound evaluation string
214
+ def self.create_compound_callstack(call_string)
215
+ callstack = Matcher::Parser.new(call_string).execution_stack
216
+ callstack.each_with_index do |statement, i|
217
+ if statement.keys.first == "fstatement"
218
+ callstack[i]["fstatement"] = create_function_hash(statement.values.first)
219
+ end
220
+ end
221
+ callstack
222
+ end
223
+ end
224
+ end
@@ -0,0 +1,128 @@
1
+ module MCollective
2
+ module Matcher
3
+ class Parser
4
+ attr_reader :scanner, :execution_stack
5
+
6
+ def initialize(args)
7
+ @scanner = Scanner.new(args)
8
+ @execution_stack = []
9
+ @parse_errors = []
10
+ @token_errors = []
11
+ @paren_errors = []
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}"
42
+ end
43
+
44
+ # Parse the input string, one token at a time a contruct the call stack
45
+ def parse
46
+ pre_index = @scanner.token_index
47
+ p_token,p_token_value = nil
48
+ c_token,c_token_value = @scanner.get_token
49
+ parenth = 0
50
+
51
+ while (c_token != nil)
52
+ @scanner.token_index += 1
53
+ n_token, n_token_value = @scanner.get_token
54
+
55
+ unless n_token == " "
56
+ case c_token
57
+ when "bad_token"
58
+ @token_errors << c_token_value
59
+
60
+ when "and"
61
+ unless (n_token =~ /not|fstatement|statement|\(/) || (scanner.token_index == scanner.arguments.size) && !(n_token == nil)
62
+ @parse_errors << [pre_index, scanner.token_index]
63
+ end
64
+
65
+ if p_token == nil
66
+ @parse_errors << [pre_index - c_token.size, scanner.token_index]
67
+ elsif (p_token == "and" || p_token == "or")
68
+ @parse_errors << [pre_index - 1 - p_token.size, pre_index - 1]
69
+ end
70
+
71
+ when "or"
72
+ unless (n_token =~ /not|fstatement|statement|\(/) || (scanner.token_index == scanner.arguments.size) && !(n_token == nil)
73
+ @parse_errors << [pre_index, scanner.token_index]
74
+ end
75
+
76
+ if p_token == nil
77
+ @parse_errors << [pre_index - c_token.size, scanner.token_index]
78
+ elsif (p_token == "and" || p_token == "or")
79
+ @parse_errors << [pre_index - 1 - p_token.size, pre_index - 1]
80
+ end
81
+
82
+ when "not"
83
+ unless n_token =~ /fstatement|statement|\(|not/ && !(n_token == nil)
84
+ @parse_errors << [pre_index, scanner.token_index]
85
+ end
86
+
87
+ when "statement","fstatement"
88
+ unless n_token =~ /and|or|\)/
89
+ unless scanner.token_index == scanner.arguments.size
90
+ @parse_errors << [pre_index, scanner.token_index]
91
+ end
92
+ end
93
+
94
+ when ")"
95
+ unless (n_token =~ /|and|or|not|\(/)
96
+ unless(scanner.token_index == scanner.arguments.size)
97
+ @parse_errors << [pre_index, scanner.token_index]
98
+ end
99
+ end
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
105
+
106
+ when "("
107
+ unless n_token =~ /fstatement|statement|not|\(/
108
+ @parse_errors << [pre_index, scanner.token_index]
109
+ end
110
+ @paren_errors.push((n_token.nil?) ? scanner.token_index - 1: scanner.token_index - n_token_value.size)
111
+
112
+ else
113
+ @parse_errors << [pre_index, scanner.token_index]
114
+ end
115
+
116
+ unless n_token == " " ||c_token == "bad_token"
117
+ @execution_stack << {c_token => c_token_value}
118
+ end
119
+
120
+ p_token, p_token_value = c_token, c_token_value
121
+ c_token, c_token_value = n_token, n_token_value
122
+ end
123
+ pre_index = @scanner.token_index
124
+ end
125
+ end
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,241 @@
1
+ module MCollective
2
+ module Matcher
3
+ class Scanner
4
+ attr_accessor :arguments, :token_index
5
+
6
+ def initialize(arguments)
7
+ @token_index = 0
8
+ @arguments = arguments.split("")
9
+ @seperation_counter = 0
10
+ @white_spaces = 0
11
+ end
12
+
13
+ # Scans the input string and identifies single language tokens
14
+ def get_token
15
+ if @token_index >= @arguments.size
16
+ return nil
17
+ end
18
+
19
+ case @arguments[@token_index]
20
+ when "("
21
+ return "(", "("
22
+
23
+ when ")"
24
+ return ")", ")"
25
+
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
29
+ return "not", "not"
30
+ else
31
+ gen_statement
32
+ end
33
+
34
+ when "!"
35
+ return "not", "not"
36
+
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
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"
49
+ else
50
+ gen_statement
51
+ end
52
+
53
+ when " "
54
+ return " ", " "
55
+
56
+ else
57
+ gen_statement
58
+ end
59
+ end
60
+
61
+ private
62
+ # Helper generates a statement token
63
+ def gen_statement
64
+ func = false
65
+ current_token_value = ""
66
+ j = @token_index
67
+ escaped = false
68
+
69
+ begin
70
+ if (@arguments[j] == "/")
71
+ begin
72
+ current_token_value << @arguments[j]
73
+ j += 1
74
+ end until (j >= @arguments.size) || (@arguments[j] =~ /\s/)
75
+ elsif (@arguments[j] =~ /=|<|>/)
76
+ while !(@arguments[j] =~ /=|<|>/)
77
+ current_token_value << @arguments[j]
78
+ j += 1
79
+ end
80
+
81
+ current_token_value << @arguments[j]
82
+ j += 1
83
+
84
+ if @arguments[j] == "/"
85
+ begin
86
+ current_token_value << @arguments[j]
87
+ j += 1
88
+ if @arguments[j] == "/"
89
+ current_token_value << "/"
90
+ break
91
+ end
92
+ end until (j >= @arguments.size) || (@arguments[j] =~ /\//)
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
+ if @arguments[j] == '\\'
106
+ # eat the escape char
107
+ current_token_value << @arguments[j]
108
+ j += 1
109
+ escaped = true
110
+ end
111
+
112
+ current_token_value << @arguments[j]
113
+ j += 1
114
+ end
115
+ current_token_value << @arguments[j] if @arguments[j]
116
+ break
117
+ end
118
+
119
+ if @arguments[j] == "("
120
+ func = true
121
+
122
+ current_token_value << @arguments[j]
123
+ j += 1
124
+
125
+ while j < @arguments.size
126
+ current_token_value << @arguments[j]
127
+ if @arguments[j] == ')'
128
+ j += 1
129
+ break
130
+ end
131
+ j += 1
132
+ end
133
+ elsif @arguments[j] == '"' || @arguments[j] == "'"
134
+ escaped = true
135
+ escaped_with = @arguments[j]
136
+
137
+ j += 1 # step over first " or '
138
+ @white_spaces += 1
139
+ # identified "..." or '...'
140
+ while j < @arguments.size
141
+ if @arguments[j] == '\\'
142
+ # eat the escape char but don't add it to the token, or we
143
+ # end up with \\\"
144
+ j += 1
145
+ @white_spaces += 1
146
+ unless j < @arguments.size
147
+ break
148
+ end
149
+ elsif @arguments[j] == escaped_with
150
+ j += 1
151
+ @white_spaces += 1
152
+ break
153
+ end
154
+ current_token_value << @arguments[j]
155
+ j += 1
156
+ end
157
+ else
158
+ current_token_value << @arguments[j]
159
+ j += 1
160
+ end
161
+
162
+ if(@arguments[j] == ' ')
163
+ break if(is_klass?(j) && !(@arguments[j-1] =~ /=|<|>/))
164
+ end
165
+
166
+ if( (@arguments[j] == ' ') && (@seperation_counter < 2) && !(current_token_value.match(/^.+(=|<|>).+$/)) )
167
+ if((index = lookahead(j)))
168
+ j = index
169
+ end
170
+ end
171
+ end until (j >= @arguments.size) || (@arguments[j] =~ /\s|\)/)
172
+ @seperation_counter = 0
173
+ end
174
+ rescue Exception => e
175
+ raise "An exception was raised while trying to tokenize '#{current_token_value} - #{e}'"
176
+ end
177
+
178
+ @token_index += current_token_value.size + @white_spaces - 1
179
+ @white_spaces = 0
180
+
181
+ # bar(
182
+ if current_token_value.match(/.+?\($/)
183
+ return "bad_token", [@token_index - current_token_value.size + 1, @token_index]
184
+ # /foo/=bar
185
+ elsif current_token_value.match(/^\/.+?\/(<|>|=).+/)
186
+ return "bad_token", [@token_index - current_token_value.size + 1, @token_index]
187
+ elsif current_token_value.match(/^.+?\/(<|>|=).+/)
188
+ return "bad_token", [@token_index - current_token_value.size + 1, @token_index]
189
+ else
190
+ if func
191
+ if current_token_value.match(/^.+?\((\s*(')[^']*(')\s*(,\s*(')[^']*('))*)?\)(\.[a-zA-Z0-9_]+)?((!=|<=|>=|=|>|<).+)?$/) ||
192
+ current_token_value.match(/^.+?\((\s*(")[^"]*(")\s*(,\s*(")[^"]*("))*)?\)(\.[a-zA-Z0-9_]+)?((!=|<=|>=|=|>|<).+)?$/)
193
+ return "fstatement", current_token_value
194
+ else
195
+ return "bad_token", [@token_index - current_token_value.size + 1, @token_index]
196
+ end
197
+ else
198
+ if escaped
199
+ return "statement", current_token_value
200
+ end
201
+ slash_err = false
202
+ current_token_value.split('').each do |c|
203
+ if c == '/'
204
+ slash_err = !slash_err
205
+ end
206
+ end
207
+ return "bad_token", [@token_index - current_token_value.size + 1, @token_index] if slash_err
208
+ return "statement", current_token_value
209
+ end
210
+ end
211
+ end
212
+
213
+ # Deal with special puppet class statement
214
+ def is_klass?(j)
215
+ while(j < @arguments.size && @arguments[j] == ' ')
216
+ j += 1
217
+ end
218
+
219
+ if @arguments[j] =~ /=|<|>/
220
+ return false
221
+ else
222
+ return true
223
+ end
224
+ end
225
+
226
+ # Eat spaces while looking for the next comparison symbol
227
+ def lookahead(index)
228
+ index += 1
229
+ while(index <= @arguments.size)
230
+ @white_spaces += 1
231
+ unless(@arguments[index] =~ /\s/)
232
+ @seperation_counter +=1
233
+ return index
234
+ end
235
+ index += 1
236
+ end
237
+ return nil
238
+ end
239
+ end
240
+ end
241
+ end