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.
- checksums.yaml +7 -0
- data/bin/mco +64 -0
- data/lib/mcollective.rb +63 -0
- data/lib/mcollective/agent.rb +5 -0
- data/lib/mcollective/agents.rb +149 -0
- data/lib/mcollective/aggregate.rb +85 -0
- data/lib/mcollective/aggregate/average.ddl +33 -0
- data/lib/mcollective/aggregate/average.rb +29 -0
- data/lib/mcollective/aggregate/base.rb +40 -0
- data/lib/mcollective/aggregate/result.rb +9 -0
- data/lib/mcollective/aggregate/result/base.rb +25 -0
- data/lib/mcollective/aggregate/result/collection_result.rb +19 -0
- data/lib/mcollective/aggregate/result/numeric_result.rb +13 -0
- data/lib/mcollective/aggregate/sum.ddl +33 -0
- data/lib/mcollective/aggregate/sum.rb +18 -0
- data/lib/mcollective/aggregate/summary.ddl +33 -0
- data/lib/mcollective/aggregate/summary.rb +53 -0
- data/lib/mcollective/application.rb +365 -0
- data/lib/mcollective/application/completion.rb +104 -0
- data/lib/mcollective/application/describe_filter.rb +87 -0
- data/lib/mcollective/application/facts.rb +62 -0
- data/lib/mcollective/application/find.rb +23 -0
- data/lib/mcollective/application/help.rb +28 -0
- data/lib/mcollective/application/inventory.rb +344 -0
- data/lib/mcollective/application/ping.rb +82 -0
- data/lib/mcollective/application/plugin.rb +369 -0
- data/lib/mcollective/application/rpc.rb +111 -0
- data/lib/mcollective/applications.rb +134 -0
- data/lib/mcollective/cache.rb +145 -0
- data/lib/mcollective/client.rb +353 -0
- data/lib/mcollective/config.rb +245 -0
- data/lib/mcollective/connector.rb +18 -0
- data/lib/mcollective/connector/base.rb +26 -0
- data/lib/mcollective/data.rb +91 -0
- data/lib/mcollective/data/agent_data.ddl +22 -0
- data/lib/mcollective/data/agent_data.rb +17 -0
- data/lib/mcollective/data/base.rb +67 -0
- data/lib/mcollective/data/collective_data.ddl +20 -0
- data/lib/mcollective/data/collective_data.rb +9 -0
- data/lib/mcollective/data/fact_data.ddl +28 -0
- data/lib/mcollective/data/fact_data.rb +55 -0
- data/lib/mcollective/data/fstat_data.ddl +89 -0
- data/lib/mcollective/data/fstat_data.rb +56 -0
- data/lib/mcollective/data/result.rb +45 -0
- data/lib/mcollective/ddl.rb +113 -0
- data/lib/mcollective/ddl/agentddl.rb +253 -0
- data/lib/mcollective/ddl/base.rb +217 -0
- data/lib/mcollective/ddl/dataddl.rb +56 -0
- data/lib/mcollective/ddl/discoveryddl.rb +52 -0
- data/lib/mcollective/ddl/validatorddl.rb +6 -0
- data/lib/mcollective/discovery.rb +143 -0
- data/lib/mcollective/discovery/flatfile.ddl +11 -0
- data/lib/mcollective/discovery/flatfile.rb +48 -0
- data/lib/mcollective/discovery/mc.ddl +11 -0
- data/lib/mcollective/discovery/mc.rb +30 -0
- data/lib/mcollective/discovery/stdin.ddl +11 -0
- data/lib/mcollective/discovery/stdin.rb +68 -0
- data/lib/mcollective/exceptions.rb +28 -0
- data/lib/mcollective/facts.rb +39 -0
- data/lib/mcollective/facts/base.rb +100 -0
- data/lib/mcollective/facts/yaml_facts.rb +65 -0
- data/lib/mcollective/generators.rb +7 -0
- data/lib/mcollective/generators/agent_generator.rb +51 -0
- data/lib/mcollective/generators/base.rb +46 -0
- data/lib/mcollective/generators/data_generator.rb +51 -0
- data/lib/mcollective/generators/templates/action_snippet.erb +13 -0
- data/lib/mcollective/generators/templates/data_input_snippet.erb +7 -0
- data/lib/mcollective/generators/templates/ddl.erb +8 -0
- data/lib/mcollective/generators/templates/plugin.erb +7 -0
- data/lib/mcollective/log.rb +118 -0
- data/lib/mcollective/logger.rb +5 -0
- data/lib/mcollective/logger/base.rb +77 -0
- data/lib/mcollective/logger/console_logger.rb +61 -0
- data/lib/mcollective/logger/file_logger.rb +53 -0
- data/lib/mcollective/logger/syslog_logger.rb +53 -0
- data/lib/mcollective/matcher.rb +224 -0
- data/lib/mcollective/matcher/parser.rb +128 -0
- data/lib/mcollective/matcher/scanner.rb +241 -0
- data/lib/mcollective/message.rb +248 -0
- data/lib/mcollective/monkey_patches.rb +152 -0
- data/lib/mcollective/optionparser.rb +197 -0
- data/lib/mcollective/pluginmanager.rb +180 -0
- data/lib/mcollective/pluginpackager.rb +98 -0
- data/lib/mcollective/pluginpackager/agent_definition.rb +94 -0
- data/lib/mcollective/pluginpackager/debpackage_packager.rb +237 -0
- data/lib/mcollective/pluginpackager/modulepackage_packager.rb +127 -0
- data/lib/mcollective/pluginpackager/ospackage_packager.rb +59 -0
- data/lib/mcollective/pluginpackager/rpmpackage_packager.rb +180 -0
- data/lib/mcollective/pluginpackager/standard_definition.rb +69 -0
- data/lib/mcollective/pluginpackager/templates/debian/Makefile.erb +7 -0
- data/lib/mcollective/pluginpackager/templates/debian/changelog.erb +5 -0
- data/lib/mcollective/pluginpackager/templates/debian/compat.erb +1 -0
- data/lib/mcollective/pluginpackager/templates/debian/control.erb +15 -0
- data/lib/mcollective/pluginpackager/templates/debian/copyright.erb +8 -0
- data/lib/mcollective/pluginpackager/templates/debian/rules.erb +6 -0
- data/lib/mcollective/pluginpackager/templates/module/Modulefile.erb +5 -0
- data/lib/mcollective/pluginpackager/templates/module/README.md.erb +37 -0
- data/lib/mcollective/pluginpackager/templates/module/_manifest.pp.erb +9 -0
- data/lib/mcollective/pluginpackager/templates/redhat/rpm_spec.erb +63 -0
- data/lib/mcollective/registration/base.rb +91 -0
- data/lib/mcollective/rpc.rb +182 -0
- data/lib/mcollective/rpc/actionrunner.rb +158 -0
- data/lib/mcollective/rpc/agent.rb +374 -0
- data/lib/mcollective/rpc/audit.rb +38 -0
- data/lib/mcollective/rpc/client.rb +1066 -0
- data/lib/mcollective/rpc/helpers.rb +321 -0
- data/lib/mcollective/rpc/progress.rb +63 -0
- data/lib/mcollective/rpc/reply.rb +87 -0
- data/lib/mcollective/rpc/request.rb +86 -0
- data/lib/mcollective/rpc/result.rb +90 -0
- data/lib/mcollective/rpc/stats.rb +294 -0
- data/lib/mcollective/runnerstats.rb +90 -0
- data/lib/mcollective/security.rb +26 -0
- data/lib/mcollective/security/base.rb +244 -0
- data/lib/mcollective/shell.rb +126 -0
- data/lib/mcollective/ssl.rb +285 -0
- data/lib/mcollective/util.rb +579 -0
- data/lib/mcollective/validator.rb +85 -0
- data/lib/mcollective/validator/array_validator.ddl +7 -0
- data/lib/mcollective/validator/array_validator.rb +9 -0
- data/lib/mcollective/validator/ipv4address_validator.ddl +7 -0
- data/lib/mcollective/validator/ipv4address_validator.rb +16 -0
- data/lib/mcollective/validator/ipv6address_validator.ddl +7 -0
- data/lib/mcollective/validator/ipv6address_validator.rb +16 -0
- data/lib/mcollective/validator/length_validator.ddl +7 -0
- data/lib/mcollective/validator/length_validator.rb +11 -0
- data/lib/mcollective/validator/regex_validator.ddl +7 -0
- data/lib/mcollective/validator/regex_validator.rb +9 -0
- data/lib/mcollective/validator/shellsafe_validator.ddl +7 -0
- data/lib/mcollective/validator/shellsafe_validator.rb +13 -0
- data/lib/mcollective/validator/typecheck_validator.ddl +7 -0
- data/lib/mcollective/validator/typecheck_validator.rb +28 -0
- 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
|