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.
- data/lib/mcollective.rb +32 -23
- data/lib/mcollective/agent.rb +5 -0
- data/lib/mcollective/agents.rb +5 -16
- data/lib/mcollective/aggregate.rb +61 -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/application.rb +7 -4
- data/lib/mcollective/applications.rb +3 -14
- data/lib/mcollective/cache.rb +145 -0
- data/lib/mcollective/client.rb +10 -87
- data/lib/mcollective/config.rb +22 -8
- data/lib/mcollective/data.rb +87 -0
- data/lib/mcollective/data/base.rb +67 -0
- data/lib/mcollective/data/result.rb +40 -0
- data/lib/mcollective/ddl.rb +113 -0
- data/lib/mcollective/ddl/agentddl.rb +185 -0
- data/lib/mcollective/ddl/base.rb +220 -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/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/logger/console_logger.rb +15 -15
- data/lib/mcollective/matcher.rb +167 -0
- data/lib/mcollective/matcher/parser.rb +60 -25
- data/lib/mcollective/matcher/scanner.rb +156 -78
- data/lib/mcollective/message.rb +47 -6
- data/lib/mcollective/monkey_patches.rb +17 -0
- data/lib/mcollective/optionparser.rb +18 -1
- data/lib/mcollective/pluginmanager.rb +3 -3
- data/lib/mcollective/pluginpackager.rb +10 -3
- data/lib/mcollective/pluginpackager/agent_definition.rb +28 -20
- data/lib/mcollective/pluginpackager/standard_definition.rb +11 -9
- data/lib/mcollective/registration/base.rb +3 -1
- data/lib/mcollective/rpc.rb +18 -24
- data/lib/mcollective/rpc/agent.rb +37 -113
- data/lib/mcollective/rpc/client.rb +186 -64
- data/lib/mcollective/rpc/helpers.rb +42 -80
- data/lib/mcollective/rpc/progress.rb +3 -3
- data/lib/mcollective/rpc/reply.rb +37 -13
- data/lib/mcollective/rpc/request.rb +17 -6
- data/lib/mcollective/rpc/result.rb +9 -5
- data/lib/mcollective/rpc/stats.rb +71 -24
- data/lib/mcollective/security/base.rb +41 -34
- data/lib/mcollective/shell.rb +1 -1
- data/lib/mcollective/ssl.rb +34 -0
- data/lib/mcollective/util.rb +194 -23
- data/lib/mcollective/validator.rb +80 -0
- data/spec/fixtures/util/1.in +10 -0
- data/spec/fixtures/util/1.out +10 -0
- data/spec/fixtures/util/2.in +1 -0
- data/spec/fixtures/util/2.out +1 -0
- data/spec/fixtures/util/3.in +1 -0
- data/spec/fixtures/util/3.out +2 -0
- data/spec/fixtures/util/4.in +5 -0
- data/spec/fixtures/util/4.out +9 -0
- data/spec/spec.opts +1 -1
- data/spec/spec_helper.rb +2 -0
- data/spec/unit/agents_spec.rb +34 -19
- data/spec/unit/aggregate/base_spec.rb +57 -0
- data/spec/unit/aggregate/result/base_spec.rb +28 -0
- data/spec/unit/aggregate/result/collection_result_spec.rb +18 -0
- data/spec/unit/aggregate/result/numeric_result_spec.rb +22 -0
- data/spec/unit/aggregate_spec.rb +110 -0
- data/spec/unit/application_spec.rb +8 -3
- data/spec/unit/applications_spec.rb +2 -2
- data/spec/unit/cache_spec.rb +115 -0
- data/spec/unit/client_spec.rb +78 -0
- data/spec/unit/config_spec.rb +32 -34
- data/spec/unit/data/base_spec.rb +90 -0
- data/spec/unit/data/result_spec.rb +64 -0
- data/spec/unit/data_spec.rb +158 -0
- data/spec/unit/ddl/agentddl_spec.rb +217 -0
- data/spec/unit/{rpc/ddl_spec.rb → ddl/base_spec.rb} +238 -224
- data/spec/unit/ddl/dataddl_spec.rb +65 -0
- data/spec/unit/ddl/discoveryddl_spec.rb +58 -0
- data/spec/unit/ddl_spec.rb +84 -0
- data/spec/unit/discovery_spec.rb +196 -0
- data/spec/unit/facts/base_spec.rb +1 -1
- data/spec/unit/generators/agent_generator_spec.rb +72 -0
- data/spec/unit/generators/base_spec.rb +83 -0
- data/spec/unit/generators/data_generator_spec.rb +37 -0
- data/spec/unit/generators/snippets/agent_ddl +19 -0
- data/spec/unit/generators/snippets/data_ddl +20 -0
- data/spec/unit/logger/console_logger_spec.rb +76 -0
- data/spec/unit/logger/syslog_logger_spec.rb +2 -2
- data/spec/unit/matcher/parser_spec.rb +27 -10
- data/spec/unit/matcher/scanner_spec.rb +108 -5
- data/spec/unit/matcher_spec.rb +260 -0
- data/spec/unit/message_spec.rb +35 -13
- data/spec/unit/optionparser_spec.rb +2 -2
- data/spec/unit/pluginpackager/agent_definition_spec.rb +59 -42
- data/spec/unit/pluginpackager/standard_definition_spec.rb +10 -8
- data/spec/unit/pluginpackager_spec.rb +131 -0
- data/spec/unit/plugins/mcollective/aggregate/average_spec.rb +45 -0
- data/spec/unit/plugins/mcollective/aggregate/sum_spec.rb +31 -0
- data/spec/unit/plugins/mcollective/aggregate/summary_spec.rb +45 -0
- data/spec/unit/plugins/mcollective/connector/activemq_spec.rb +1 -1
- data/spec/unit/plugins/mcollective/connector/rabbitmq_spec.rb +478 -0
- data/spec/unit/plugins/mcollective/connector/stomp_spec.rb +2 -0
- data/spec/unit/plugins/mcollective/data/agent_data_spec.rb +43 -0
- data/spec/unit/plugins/mcollective/data/fstat_data_spec.rb +135 -0
- data/spec/unit/plugins/mcollective/discovery/flatfile_spec.rb +48 -0
- data/spec/unit/plugins/mcollective/discovery/mc_spec.rb +40 -0
- data/spec/unit/plugins/mcollective/packagers/debpackage_packager_spec.rb +41 -15
- data/spec/unit/plugins/mcollective/packagers/ospackage_spec.rb +1 -1
- data/spec/unit/plugins/mcollective/packagers/rpmpackage_packager_spec.rb +22 -38
- data/spec/unit/plugins/mcollective/validator/array_validator_spec.rb +19 -0
- data/spec/unit/plugins/mcollective/validator/ipv4address_validator_spec.rb +19 -0
- data/spec/unit/plugins/mcollective/validator/ipv6address_validator_spec.rb +19 -0
- data/spec/unit/plugins/mcollective/validator/length_validator_spec.rb +19 -0
- data/spec/unit/plugins/mcollective/validator/regex_validator_spec.rb +19 -0
- data/spec/unit/plugins/mcollective/validator/shellsafe_validator_spec.rb +21 -0
- data/spec/unit/plugins/mcollective/validator/typecheck_validator_spec.rb +23 -0
- data/spec/unit/registration/base_spec.rb +1 -1
- data/spec/unit/rpc/actionrunner_spec.rb +2 -2
- data/spec/unit/rpc/agent_spec.rb +41 -65
- data/spec/unit/rpc/client_spec.rb +430 -134
- data/spec/unit/rpc/reply_spec.rb +31 -1
- data/spec/unit/rpc/request_spec.rb +33 -12
- data/spec/unit/rpc/result_spec.rb +7 -0
- data/spec/unit/rpc/stats_spec.rb +14 -14
- data/spec/unit/rpc_spec.rb +16 -0
- data/spec/unit/security/base_spec.rb +8 -8
- data/spec/unit/ssl_spec.rb +20 -2
- data/spec/unit/string_spec.rb +15 -0
- data/spec/unit/util_spec.rb +141 -21
- data/spec/unit/validator_spec.rb +67 -0
- metadata +145 -7
- data/lib/mcollective/rpc/ddl.rb +0 -258
data/lib/mcollective/client.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
module MCollective
|
2
2
|
# Helpers for writing clients that can talk to agents, do discovery and so forth
|
3
3
|
class Client
|
4
|
-
attr_accessor :options, :stats
|
4
|
+
attr_accessor :options, :stats, :discoverer
|
5
5
|
|
6
6
|
def initialize(configfile)
|
7
7
|
@config = Config.instance
|
@@ -14,6 +14,7 @@ module MCollective
|
|
14
14
|
@options = nil
|
15
15
|
@subscriptions = {}
|
16
16
|
|
17
|
+
@discoverer = Discovery.new(self)
|
17
18
|
@connection.connect
|
18
19
|
end
|
19
20
|
|
@@ -49,7 +50,7 @@ module MCollective
|
|
49
50
|
|
50
51
|
Log.debug("Sending request #{request.requestid} to the #{request.agent} agent with ttl #{request.ttl} in collective #{request.collective}")
|
51
52
|
|
52
|
-
subscribe(agent, :reply)
|
53
|
+
subscribe(agent, :reply) unless request.reply_to
|
53
54
|
|
54
55
|
request.publish
|
55
56
|
|
@@ -112,30 +113,7 @@ module MCollective
|
|
112
113
|
# of the discovery being cancelled soon as it reached the
|
113
114
|
# requested limit of hosts
|
114
115
|
def discover(filter, timeout, limit=0)
|
115
|
-
|
116
|
-
|
117
|
-
begin
|
118
|
-
hosts = []
|
119
|
-
Timeout.timeout(timeout) do
|
120
|
-
reqid = sendreq("ping", "discovery", filter)
|
121
|
-
Log.debug("Waiting #{timeout} seconds for discovery replies to request #{reqid}")
|
122
|
-
|
123
|
-
loop do
|
124
|
-
reply = receive(reqid)
|
125
|
-
Log.debug("Got discovery reply from #{reply.payload[:senderid]}")
|
126
|
-
hosts << reply.payload[:senderid]
|
127
|
-
|
128
|
-
return hosts if limit > 0 && hosts.size == limit
|
129
|
-
end
|
130
|
-
end
|
131
|
-
rescue Timeout::Error => e
|
132
|
-
rescue Exception => e
|
133
|
-
raise
|
134
|
-
ensure
|
135
|
-
unsubscribe("discovery", :reply)
|
136
|
-
end
|
137
|
-
|
138
|
-
hosts.sort
|
116
|
+
discovered = @discoverer.discover(filter, timeout, limit)
|
139
117
|
end
|
140
118
|
|
141
119
|
# Send a request, performs the passed block for each response
|
@@ -155,15 +133,16 @@ module MCollective
|
|
155
133
|
|
156
134
|
stat = {:starttime => Time.now.to_f, :discoverytime => 0, :blocktime => 0, :totaltime => 0}
|
157
135
|
|
158
|
-
|
136
|
+
timeout = @discoverer.discovery_timeout(@options[:timeout], @options[:filter])
|
159
137
|
|
160
138
|
STDOUT.sync = true
|
161
139
|
|
162
140
|
hosts_responded = 0
|
141
|
+
reqid = nil
|
163
142
|
|
164
143
|
begin
|
165
|
-
Timeout.timeout(
|
166
|
-
reqid = sendreq(body, agent, options[:filter])
|
144
|
+
Timeout.timeout(timeout) do
|
145
|
+
reqid = sendreq(body, agent, @options[:filter])
|
167
146
|
|
168
147
|
loop do
|
169
148
|
resp = receive(reqid)
|
@@ -185,70 +164,14 @@ module MCollective
|
|
185
164
|
stat[:blocktime] = stat[:totaltime] - stat[:discoverytime]
|
186
165
|
stat[:responses] = hosts_responded
|
187
166
|
stat[:noresponsefrom] = []
|
167
|
+
stat[:requestid] = reqid
|
188
168
|
|
189
169
|
@stats = stat
|
190
170
|
return stat
|
191
171
|
end
|
192
172
|
|
193
|
-
# Performs a discovery and then send a request, performs the passed block for each response
|
194
|
-
#
|
195
|
-
# times = discovered_req("status", "mcollectived", options, client) {|resp|
|
196
|
-
# pp resp
|
197
|
-
# }
|
198
|
-
#
|
199
|
-
# It returns a hash of times and timeouts for discovery and total run is taken from the options
|
200
|
-
# hash which in turn is generally built using MCollective::Optionparser
|
201
173
|
def discovered_req(body, agent, options=false)
|
202
|
-
|
203
|
-
|
204
|
-
options = @options unless options
|
205
|
-
|
206
|
-
STDOUT.sync = true
|
207
|
-
|
208
|
-
print("Determining the amount of hosts matching filter for #{options[:disctimeout]} seconds .... ")
|
209
|
-
|
210
|
-
begin
|
211
|
-
discovered_hosts = discover(options[:filter], options[:disctimeout])
|
212
|
-
discovered = discovered_hosts.size
|
213
|
-
hosts_responded = []
|
214
|
-
hosts_not_responded = discovered_hosts
|
215
|
-
|
216
|
-
stat[:discoverytime] = Time.now.to_f - stat[:starttime]
|
217
|
-
|
218
|
-
puts("#{discovered}\n\n")
|
219
|
-
rescue Interrupt
|
220
|
-
puts("Discovery interrupted.")
|
221
|
-
exit!
|
222
|
-
end
|
223
|
-
|
224
|
-
raise("No matching clients found") if discovered == 0
|
225
|
-
|
226
|
-
begin
|
227
|
-
Timeout.timeout(options[:timeout]) do
|
228
|
-
reqid = sendreq(body, agent, options[:filter])
|
229
|
-
|
230
|
-
(1..discovered).each do |c|
|
231
|
-
resp = receive(reqid)
|
232
|
-
|
233
|
-
hosts_responded << resp.payload[:senderid]
|
234
|
-
hosts_not_responded.delete(resp.payload[:senderid]) if hosts_not_responded.include?(resp.payload[:senderid])
|
235
|
-
|
236
|
-
yield(resp.payload)
|
237
|
-
end
|
238
|
-
end
|
239
|
-
rescue Interrupt => e
|
240
|
-
rescue Timeout::Error => e
|
241
|
-
end
|
242
|
-
|
243
|
-
stat[:totaltime] = Time.now.to_f - stat[:starttime]
|
244
|
-
stat[:blocktime] = stat[:totaltime] - stat[:discoverytime]
|
245
|
-
stat[:responses] = hosts_responded.size
|
246
|
-
stat[:responsesfrom] = hosts_responded
|
247
|
-
stat[:noresponsefrom] = hosts_not_responded
|
248
|
-
stat[:discovered] = discovered
|
249
|
-
|
250
|
-
@stats = stat
|
251
|
-
return stat
|
174
|
+
raise "Client#discovered_req has been removed, please port your agent and client to the SimpleRPC framework"
|
252
175
|
end
|
253
176
|
|
254
177
|
# Prints out the stats returns from req and discovered_req in a nice way
|
data/lib/mcollective/config.rb
CHANGED
@@ -3,14 +3,18 @@ module MCollective
|
|
3
3
|
class Config
|
4
4
|
include Singleton
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
:
|
9
|
-
:
|
10
|
-
:
|
11
|
-
:
|
12
|
-
:
|
13
|
-
:
|
6
|
+
attr_accessor :mode
|
7
|
+
|
8
|
+
attr_reader :topicprefix, :daemonize, :pluginconf, :libdir, :configured
|
9
|
+
attr_reader :logfile, :keeplogs, :max_log_size, :loglevel, :logfacility
|
10
|
+
attr_reader :identity, :daemonize, :connector, :securityprovider, :factsource
|
11
|
+
attr_reader :registration, :registerinterval, :topicsep, :classesfile
|
12
|
+
attr_reader :rpcauditprovider, :rpcaudit, :configdir, :rpcauthprovider
|
13
|
+
attr_reader :rpcauthorization, :color, :configfile, :rpchelptemplate
|
14
|
+
attr_reader :rpclimitmethod, :logger_type, :fact_cache_time, :collectives
|
15
|
+
attr_reader :main_collective, :ssl_cipher, :registration_collective
|
16
|
+
attr_reader :direct_addressing, :direct_addressing_threshold, :ttl, :helptemplatedir
|
17
|
+
attr_reader :queueprefix, :default_discovery_method, :default_discovery_options
|
14
18
|
|
15
19
|
def initialize
|
16
20
|
@configured = false
|
@@ -105,6 +109,12 @@ module MCollective
|
|
105
109
|
@ssl_cipher = val
|
106
110
|
when "ttl"
|
107
111
|
@ttl = val.to_i
|
112
|
+
when "helptemplatedir"
|
113
|
+
@helptemplatedir = val
|
114
|
+
when "default_discovery_options"
|
115
|
+
@default_discovery_options << val
|
116
|
+
when "default_discovery_method"
|
117
|
+
@default_discovery_method = val
|
108
118
|
else
|
109
119
|
raise("Unknown config parameter #{key}")
|
110
120
|
end
|
@@ -170,13 +180,17 @@ module MCollective
|
|
170
180
|
@ssl_cipher = "aes-256-cbc"
|
171
181
|
@direct_addressing = false
|
172
182
|
@direct_addressing_threshold = 10
|
183
|
+
@default_discovery_method = "mc"
|
184
|
+
@default_discovery_options = []
|
173
185
|
@ttl = 60
|
186
|
+
@mode = :client
|
174
187
|
|
175
188
|
# look in the config dir for the template so users can provide their own and windows
|
176
189
|
# with odd paths will just work more often, but fall back to old behavior if it does
|
177
190
|
# not exist
|
178
191
|
@rpchelptemplate = File.join(File.dirname(configfile), "rpc-help.erb")
|
179
192
|
@rpchelptemplate = "/etc/mcollective/rpc-help.erb" unless File.exists?(@rpchelptemplate)
|
193
|
+
@helptemplatedir = File.dirname(@rpchelptemplate)
|
180
194
|
end
|
181
195
|
|
182
196
|
def read_plugin_config_dir(dir)
|
@@ -0,0 +1,87 @@
|
|
1
|
+
module MCollective
|
2
|
+
module Data
|
3
|
+
autoload :Base, "mcollective/data/base"
|
4
|
+
autoload :Result, "mcollective/data/result"
|
5
|
+
|
6
|
+
def self.load_data_sources
|
7
|
+
PluginManager.find_and_load("data")
|
8
|
+
|
9
|
+
PluginManager.grep(/_data$/).each do |plugin|
|
10
|
+
begin
|
11
|
+
unless PluginManager[plugin].class.activate?
|
12
|
+
Log.debug("Disabling data plugin %s due to plugin activation policy" % plugin)
|
13
|
+
PluginManager.delete(plugin)
|
14
|
+
end
|
15
|
+
rescue Exception => e
|
16
|
+
Log.debug("Disabling data plugin %s due to exception #{e.class}: #{e}" % plugin)
|
17
|
+
PluginManager.delete(plugin)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.pluginname(plugin)
|
23
|
+
plugin.to_s =~ /_data$/i ? plugin.to_s.downcase : "%s_data" % plugin.to_s.downcase
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.[](plugin)
|
27
|
+
PluginManager[pluginname(plugin)]
|
28
|
+
end
|
29
|
+
|
30
|
+
# Data.package("httpd").architecture
|
31
|
+
def self.method_missing(method, *args)
|
32
|
+
super unless PluginManager.include?(pluginname(method))
|
33
|
+
|
34
|
+
PluginManager[pluginname(method)].lookup(args.first)
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.ddl(plugin)
|
38
|
+
DDL.new(pluginname(plugin), :data)
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.ddl_validate(ddl, argument)
|
42
|
+
name = ddl.meta[:name]
|
43
|
+
query = ddl.entities[:data]
|
44
|
+
|
45
|
+
raise DDLValidationError, "No dataquery has been defined in the DDL for data plugin #{name}" unless query
|
46
|
+
|
47
|
+
input = query[:input]
|
48
|
+
output = query[:output]
|
49
|
+
|
50
|
+
raise DDLValidationError, "No :query input has been defined in the DDL for data plugin #{name}" unless input[:query]
|
51
|
+
raise DDLValidationError, "No output has been defined in the DDL for data plugin #{name}" if output.keys.empty?
|
52
|
+
|
53
|
+
return true if argument.nil? && input[:query][:optional]
|
54
|
+
|
55
|
+
ddl.validate_input_argument(input, :query, argument)
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.ddl_has_output?(ddl, output)
|
59
|
+
ddl.entities[:data][:output].include?(output.to_sym) rescue false
|
60
|
+
end
|
61
|
+
|
62
|
+
# For an input where the DDL requests a boolean or some number
|
63
|
+
# this will convert the input to the right type where possible
|
64
|
+
# else just returns the origin input unedited
|
65
|
+
#
|
66
|
+
# if anything here goes wrong just return the input value
|
67
|
+
# this is not really the end of the world or anything since
|
68
|
+
# all that will happen is that DDL validation will fail and
|
69
|
+
# the user will get an error, no need to be too defensive here
|
70
|
+
def self.ddl_transform_input(ddl, input)
|
71
|
+
begin
|
72
|
+
type = ddl.entities[:data][:input][:query][:type]
|
73
|
+
|
74
|
+
case type
|
75
|
+
when :boolean
|
76
|
+
return DDL.string_to_boolean(input)
|
77
|
+
|
78
|
+
when :number, :integer, :float
|
79
|
+
return DDL.string_to_number(input)
|
80
|
+
end
|
81
|
+
rescue
|
82
|
+
end
|
83
|
+
|
84
|
+
return input
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module MCollective
|
2
|
+
module Data
|
3
|
+
class Base
|
4
|
+
attr_reader :name, :result, :ddl, :timeout
|
5
|
+
|
6
|
+
# Register plugins that inherits base
|
7
|
+
def self.inherited(klass)
|
8
|
+
type = klass.to_s.split("::").last.downcase
|
9
|
+
|
10
|
+
PluginManager << {:type => type, :class => klass.to_s, :single_instance => false}
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
@name = self.class.to_s.split("::").last.downcase
|
15
|
+
@ddl = DDL.new(@name, :data)
|
16
|
+
@result = Result.new
|
17
|
+
@timeout = @ddl.meta[:timeout] || 1
|
18
|
+
|
19
|
+
startup_hook
|
20
|
+
end
|
21
|
+
|
22
|
+
def lookup(what)
|
23
|
+
ddl_validate(what)
|
24
|
+
|
25
|
+
Log.debug("Doing data query %s for '%s'" % [ @name, what ])
|
26
|
+
|
27
|
+
Timeout::timeout(@timeout) do
|
28
|
+
query_data(what)
|
29
|
+
end
|
30
|
+
|
31
|
+
@result
|
32
|
+
rescue Timeout::Error
|
33
|
+
# Timeout::Error is a inherited from Interrupt which seems a really
|
34
|
+
# strange choice, making it an equivelant of ^C and such. Catch it
|
35
|
+
# and raise something less critical that will not the runner to just
|
36
|
+
# give up the ghost
|
37
|
+
msg = "Data plugin %s timed out on query '%s'" % [@name, what]
|
38
|
+
Log.error(msg)
|
39
|
+
raise MsgTTLExpired, msg
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.query(&block)
|
43
|
+
self.module_eval { define_method("query_data", &block) }
|
44
|
+
end
|
45
|
+
|
46
|
+
def ddl_validate(what)
|
47
|
+
Data.ddl_validate(@ddl, what)
|
48
|
+
end
|
49
|
+
|
50
|
+
# activate_when do
|
51
|
+
# file.exist?("/usr/bin/puppet")
|
52
|
+
# end
|
53
|
+
def self.activate_when(&block)
|
54
|
+
(class << self; self; end).instance_eval do
|
55
|
+
define_method("activate?", &block)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Always be active unless a specific block is given with activate_when
|
60
|
+
def self.activate?
|
61
|
+
return true
|
62
|
+
end
|
63
|
+
|
64
|
+
def startup_hook;end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module MCollective
|
2
|
+
module Data
|
3
|
+
class Result
|
4
|
+
# remove some methods that might clash with commonly
|
5
|
+
# used return data to improve the effectiveness of the
|
6
|
+
# method_missing lookup strategy
|
7
|
+
undef :type if method_defined?(:type)
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@data = {}
|
11
|
+
end
|
12
|
+
|
13
|
+
def include?(key)
|
14
|
+
@data.include?(key.to_sym)
|
15
|
+
end
|
16
|
+
|
17
|
+
def [](key)
|
18
|
+
@data[key.to_sym]
|
19
|
+
end
|
20
|
+
|
21
|
+
def []=(key, val)
|
22
|
+
raise "Can only store String, Integer, Float or Boolean data but got #{val.class} for key #{key}" unless [String, Fixnum, Float, TrueClass, FalseClass].include?(val.class)
|
23
|
+
|
24
|
+
@data[key.to_sym] = val
|
25
|
+
end
|
26
|
+
|
27
|
+
def keys
|
28
|
+
@data.keys
|
29
|
+
end
|
30
|
+
|
31
|
+
def method_missing(method, *args)
|
32
|
+
key = method.to_sym
|
33
|
+
|
34
|
+
raise NameError, "undefined local variable or method `%s'" % key unless include?(key)
|
35
|
+
|
36
|
+
@data[key]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
module MCollective
|
2
|
+
# A set of classes that helps create data description language files
|
3
|
+
# for plugins. You can define meta data, actions, input and output
|
4
|
+
# describing the behavior of your agent or other plugins
|
5
|
+
#
|
6
|
+
# DDL files are used for input validation, constructing outputs,
|
7
|
+
# producing online help, informing the various display routines and
|
8
|
+
# so forth.
|
9
|
+
#
|
10
|
+
# A sample DDL for an agent be seen below, you'd put this in your agent
|
11
|
+
# dir as <agent name>.ddl
|
12
|
+
#
|
13
|
+
# metadata :name => "SimpleRPC Service Agent",
|
14
|
+
# :description => "Agent to manage services using the Puppet service provider",
|
15
|
+
# :author => "R.I.Pienaar",
|
16
|
+
# :license => "GPLv2",
|
17
|
+
# :version => "1.1",
|
18
|
+
# :url => "http://mcollective-plugins.googlecode.com/",
|
19
|
+
# :timeout => 60
|
20
|
+
#
|
21
|
+
# action "status", :description => "Gets the status of a service" do
|
22
|
+
# display :always
|
23
|
+
#
|
24
|
+
# input :service,
|
25
|
+
# :prompt => "Service Name",
|
26
|
+
# :description => "The service to get the status for",
|
27
|
+
# :type => :string,
|
28
|
+
# :validation => '^[a-zA-Z\-_\d]+$',
|
29
|
+
# :optional => true,
|
30
|
+
# :maxlength => 30
|
31
|
+
#
|
32
|
+
# output :status,
|
33
|
+
# :description => "The status of service",
|
34
|
+
# :display_as => "Service Status"
|
35
|
+
# end
|
36
|
+
#
|
37
|
+
# There are now many types of DDL and ultimately all pugins should have
|
38
|
+
# DDL files. The code is organized so that any plugin type will magically
|
39
|
+
# just work - they will be an instane of Base which has #metadata and a few
|
40
|
+
# common cases.
|
41
|
+
#
|
42
|
+
# For plugin types that require more specific behaviors they can just add a
|
43
|
+
# class here that inherits from Base and add their specific behavior.
|
44
|
+
#
|
45
|
+
# Base defines a specific behavior for input, output and metadata which we'd
|
46
|
+
# like to keep standard across plugin types so do not completely override the
|
47
|
+
# behavior of input. The methods are written that they will gladly store extra
|
48
|
+
# content though so you add, do not remove. See the AgentDDL class for an example
|
49
|
+
# where agents want a :required argument to be always set.
|
50
|
+
module DDL
|
51
|
+
autoload :Base, "mcollective/ddl/base"
|
52
|
+
autoload :AgentDDL, "mcollective/ddl/agentddl"
|
53
|
+
autoload :DataDDL, "mcollective/ddl/dataddl"
|
54
|
+
autoload :DiscoveryDDL, "mcollective/ddl/discoveryddl"
|
55
|
+
|
56
|
+
# There used to be only one big nasty DDL class with a bunch of mashed
|
57
|
+
# together behaviors. It's been around for ages and we would rather not
|
58
|
+
# ask all the users to change their DDL.new calls to some other factory
|
59
|
+
# method that would have this exact same behavior.
|
60
|
+
#
|
61
|
+
# So we override the behavior of #new which is a hugely sucky thing to do
|
62
|
+
# but ultimately it's what would be least disrupting to code out there
|
63
|
+
# today. We did though change DDL to a module to make it possibly a
|
64
|
+
# little less suprising, possibly.
|
65
|
+
def self.new(*args, &blk)
|
66
|
+
load_and_cache(*args)
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.load_and_cache(*args)
|
70
|
+
Cache.setup(:ddl, 300)
|
71
|
+
|
72
|
+
plugin = args.first
|
73
|
+
args.size > 1 ? type = args[1].to_s : type = "agent"
|
74
|
+
path = "%s/%s" % [type, plugin]
|
75
|
+
|
76
|
+
begin
|
77
|
+
ddl = Cache.read(:ddl, path)
|
78
|
+
rescue
|
79
|
+
begin
|
80
|
+
klass = DDL.const_get("%sDDL" % type.capitalize)
|
81
|
+
rescue NameError
|
82
|
+
klass = Base
|
83
|
+
end
|
84
|
+
|
85
|
+
ddl = Cache.write(:ddl, path, klass.new(*args))
|
86
|
+
end
|
87
|
+
|
88
|
+
return ddl
|
89
|
+
end
|
90
|
+
|
91
|
+
# As we're taking arguments on the command line we need a
|
92
|
+
# way to input booleans, true on the cli is a string so this
|
93
|
+
# method will take the ddl, find all arguments that are supposed
|
94
|
+
# to be boolean and if they are the strings "true"/"yes" or "false"/"no"
|
95
|
+
# turn them into the matching boolean
|
96
|
+
def self.string_to_boolean(val)
|
97
|
+
return true if ["true", "t", "yes", "y", "1"].include?(val.downcase)
|
98
|
+
return false if ["false", "f", "no", "n", "0"].include?(val.downcase)
|
99
|
+
|
100
|
+
raise "#{val} does not look like a boolean argument"
|
101
|
+
end
|
102
|
+
|
103
|
+
# a generic string to number function, if a number looks like a float
|
104
|
+
# it turns it into a float else an int. This is naive but should be sufficient
|
105
|
+
# for numbers typed on the cli in most cases
|
106
|
+
def self.string_to_number(val)
|
107
|
+
return val.to_f if val =~ /^\d+\.\d+$/
|
108
|
+
return val.to_i if val =~ /^\d+$/
|
109
|
+
|
110
|
+
raise "#{val} does not look like a number"
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|