choria-mcorpc-support 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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