choria-mcorpc-support 2.21.1 → 2.23.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 (145) hide show
  1. checksums.yaml +4 -4
  2. data/lib/mcollective.rb +1 -2
  3. data/lib/mcollective/agent/bolt_tasks.ddl +253 -0
  4. data/lib/mcollective/agent/bolt_tasks.json +365 -0
  5. data/lib/mcollective/agent/bolt_tasks.rb +178 -0
  6. data/lib/mcollective/agent/choria_util.ddl +152 -0
  7. data/lib/mcollective/agent/choria_util.json +244 -0
  8. data/lib/mcollective/agent/rpcutil.ddl +8 -4
  9. data/lib/mcollective/agent/rpcutil.json +333 -0
  10. data/lib/mcollective/agent/scout.ddl +169 -0
  11. data/lib/mcollective/agent/scout.json +224 -0
  12. data/lib/mcollective/agents.rb +7 -6
  13. data/lib/mcollective/aggregate.rb +4 -4
  14. data/lib/mcollective/aggregate/average.rb +2 -2
  15. data/lib/mcollective/aggregate/base.rb +2 -2
  16. data/lib/mcollective/aggregate/result.rb +3 -3
  17. data/lib/mcollective/aggregate/result/collection_result.rb +2 -2
  18. data/lib/mcollective/aggregate/result/numeric_result.rb +2 -2
  19. data/lib/mcollective/aggregate/sum.rb +2 -2
  20. data/lib/mcollective/aggregate/summary.rb +3 -4
  21. data/lib/mcollective/application.rb +57 -21
  22. data/lib/mcollective/application/choria.rb +189 -0
  23. data/lib/mcollective/application/completion.rb +6 -6
  24. data/lib/mcollective/application/facts.rb +11 -11
  25. data/lib/mcollective/application/federation.rb +237 -0
  26. data/lib/mcollective/application/find.rb +4 -4
  27. data/lib/mcollective/application/help.rb +3 -3
  28. data/lib/mcollective/application/inventory.rb +3 -341
  29. data/lib/mcollective/application/ping.rb +5 -51
  30. data/lib/mcollective/application/playbook.rb +207 -0
  31. data/lib/mcollective/application/plugin.rb +106 -106
  32. data/lib/mcollective/application/rpc.rb +3 -108
  33. data/lib/mcollective/application/tasks.rb +425 -0
  34. data/lib/mcollective/applications.rb +11 -10
  35. data/lib/mcollective/audit/choria.rb +33 -0
  36. data/lib/mcollective/cache.rb +2 -4
  37. data/lib/mcollective/client.rb +11 -10
  38. data/lib/mcollective/config.rb +21 -34
  39. data/lib/mcollective/connector/base.rb +2 -1
  40. data/lib/mcollective/connector/nats.ddl +9 -0
  41. data/lib/mcollective/connector/nats.rb +450 -0
  42. data/lib/mcollective/data.rb +8 -3
  43. data/lib/mcollective/data/agent_data.rb +1 -1
  44. data/lib/mcollective/data/base.rb +6 -5
  45. data/lib/mcollective/data/bolt_task_data.ddl +90 -0
  46. data/lib/mcollective/data/bolt_task_data.rb +32 -0
  47. data/lib/mcollective/data/collective_data.rb +1 -1
  48. data/lib/mcollective/data/fact_data.rb +6 -6
  49. data/lib/mcollective/data/fstat_data.rb +2 -4
  50. data/lib/mcollective/data/result.rb +7 -2
  51. data/lib/mcollective/ddl/agentddl.rb +5 -17
  52. data/lib/mcollective/ddl/base.rb +10 -13
  53. data/lib/mcollective/discovery.rb +24 -39
  54. data/lib/mcollective/discovery/choria.ddl +11 -0
  55. data/lib/mcollective/discovery/choria.rb +223 -0
  56. data/lib/mcollective/discovery/flatfile.rb +7 -8
  57. data/lib/mcollective/discovery/mc.rb +2 -2
  58. data/lib/mcollective/discovery/stdin.rb +17 -18
  59. data/lib/mcollective/exceptions.rb +13 -0
  60. data/lib/mcollective/facts/base.rb +9 -9
  61. data/lib/mcollective/facts/yaml_facts.rb +12 -12
  62. data/lib/mcollective/generators.rb +3 -3
  63. data/lib/mcollective/generators/agent_generator.rb +3 -4
  64. data/lib/mcollective/generators/base.rb +14 -15
  65. data/lib/mcollective/generators/data_generator.rb +5 -6
  66. data/lib/mcollective/log.rb +2 -2
  67. data/lib/mcollective/logger/base.rb +3 -2
  68. data/lib/mcollective/logger/console_logger.rb +10 -10
  69. data/lib/mcollective/logger/file_logger.rb +7 -7
  70. data/lib/mcollective/logger/syslog_logger.rb +11 -15
  71. data/lib/mcollective/message.rb +8 -39
  72. data/lib/mcollective/monkey_patches.rb +2 -4
  73. data/lib/mcollective/optionparser.rb +2 -1
  74. data/lib/mcollective/pluginmanager.rb +3 -5
  75. data/lib/mcollective/pluginpackager.rb +1 -3
  76. data/lib/mcollective/pluginpackager/agent_definition.rb +3 -8
  77. data/lib/mcollective/pluginpackager/forge_packager.rb +7 -9
  78. data/lib/mcollective/pluginpackager/standard_definition.rb +1 -2
  79. data/lib/mcollective/registration/base.rb +18 -16
  80. data/lib/mcollective/rpc.rb +2 -4
  81. data/lib/mcollective/rpc/actionrunner.rb +16 -18
  82. data/lib/mcollective/rpc/agent.rb +26 -43
  83. data/lib/mcollective/rpc/audit.rb +1 -0
  84. data/lib/mcollective/rpc/client.rb +67 -85
  85. data/lib/mcollective/rpc/helpers.rb +55 -62
  86. data/lib/mcollective/rpc/progress.rb +2 -2
  87. data/lib/mcollective/rpc/reply.rb +17 -19
  88. data/lib/mcollective/rpc/request.rb +7 -5
  89. data/lib/mcollective/rpc/result.rb +6 -8
  90. data/lib/mcollective/rpc/stats.rb +49 -58
  91. data/lib/mcollective/security/base.rb +13 -56
  92. data/lib/mcollective/security/choria.rb +765 -0
  93. data/lib/mcollective/shell.rb +9 -4
  94. data/lib/mcollective/signer/base.rb +28 -0
  95. data/lib/mcollective/signer/choria.rb +185 -0
  96. data/lib/mcollective/ssl.rb +8 -6
  97. data/lib/mcollective/util.rb +58 -55
  98. data/lib/mcollective/util/bolt_support.rb +176 -0
  99. data/lib/mcollective/util/bolt_support/plan_runner.rb +167 -0
  100. data/lib/mcollective/util/bolt_support/task_result.rb +94 -0
  101. data/lib/mcollective/util/bolt_support/task_results.rb +128 -0
  102. data/lib/mcollective/util/choria.rb +946 -0
  103. data/lib/mcollective/util/indifferent_hash.rb +12 -0
  104. data/lib/mcollective/util/natswrapper.rb +242 -0
  105. data/lib/mcollective/util/playbook.rb +435 -0
  106. data/lib/mcollective/util/playbook/data_stores.rb +201 -0
  107. data/lib/mcollective/util/playbook/data_stores/base.rb +99 -0
  108. data/lib/mcollective/util/playbook/data_stores/consul_data_store.rb +88 -0
  109. data/lib/mcollective/util/playbook/data_stores/environment_data_store.rb +33 -0
  110. data/lib/mcollective/util/playbook/data_stores/etcd_data_store.rb +42 -0
  111. data/lib/mcollective/util/playbook/data_stores/file_data_store.rb +106 -0
  112. data/lib/mcollective/util/playbook/data_stores/shell_data_store.rb +103 -0
  113. data/lib/mcollective/util/playbook/inputs.rb +265 -0
  114. data/lib/mcollective/util/playbook/nodes.rb +207 -0
  115. data/lib/mcollective/util/playbook/nodes/mcollective_nodes.rb +86 -0
  116. data/lib/mcollective/util/playbook/nodes/pql_nodes.rb +40 -0
  117. data/lib/mcollective/util/playbook/nodes/shell_nodes.rb +55 -0
  118. data/lib/mcollective/util/playbook/nodes/terraform_nodes.rb +65 -0
  119. data/lib/mcollective/util/playbook/nodes/yaml_nodes.rb +47 -0
  120. data/lib/mcollective/util/playbook/playbook_logger.rb +47 -0
  121. data/lib/mcollective/util/playbook/puppet_logger.rb +51 -0
  122. data/lib/mcollective/util/playbook/report.rb +152 -0
  123. data/lib/mcollective/util/playbook/task_result.rb +55 -0
  124. data/lib/mcollective/util/playbook/tasks.rb +196 -0
  125. data/lib/mcollective/util/playbook/tasks/base.rb +45 -0
  126. data/lib/mcollective/util/playbook/tasks/graphite_event_task.rb +64 -0
  127. data/lib/mcollective/util/playbook/tasks/mcollective_task.rb +356 -0
  128. data/lib/mcollective/util/playbook/tasks/shell_task.rb +93 -0
  129. data/lib/mcollective/util/playbook/tasks/slack_task.rb +105 -0
  130. data/lib/mcollective/util/playbook/tasks/webhook_task.rb +136 -0
  131. data/lib/mcollective/util/playbook/template_util.rb +98 -0
  132. data/lib/mcollective/util/playbook/uses.rb +169 -0
  133. data/lib/mcollective/util/tasks_support.rb +751 -0
  134. data/lib/mcollective/util/tasks_support/cli.rb +260 -0
  135. data/lib/mcollective/util/tasks_support/default_formatter.rb +138 -0
  136. data/lib/mcollective/util/tasks_support/json_formatter.rb +108 -0
  137. data/lib/mcollective/validator.rb +6 -1
  138. data/lib/mcollective/validator/bolt_task_name_validator.ddl +7 -0
  139. data/lib/mcollective/validator/bolt_task_name_validator.rb +11 -0
  140. data/lib/mcollective/validator/length_validator.rb +1 -3
  141. metadata +65 -6
  142. data/lib/mcollective/application/describe_filter.rb +0 -87
  143. data/lib/mcollective/matcher.rb +0 -220
  144. data/lib/mcollective/matcher/parser.rb +0 -128
  145. data/lib/mcollective/matcher/scanner.rb +0 -241
@@ -0,0 +1,45 @@
1
+ module MCollective
2
+ module Util
3
+ class Playbook
4
+ class Tasks
5
+ class Base
6
+ attr_accessor :description
7
+ attr_writer :fail_ok
8
+
9
+ include TemplateUtil
10
+
11
+ def initialize(playbook)
12
+ @playbook = playbook
13
+ @fail_ok = false
14
+
15
+ startup_hook
16
+ end
17
+
18
+ def startup_hook; end
19
+
20
+ def to_s
21
+ "#<%s description: %s>" % [self.class, t(description)]
22
+ end
23
+
24
+ def run_task(result)
25
+ validate_configuration!
26
+
27
+ result.timed_run(self)
28
+ end
29
+
30
+ def run
31
+ raise(StandardError, "run not implemented", caller)
32
+ end
33
+
34
+ def validate_configuration!
35
+ raise(StandardError, "validate_configuration! not implemented", caller)
36
+ end
37
+
38
+ def from_hash(properties)
39
+ raise(StandardError, "from_hash not implemented", caller)
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,64 @@
1
+ require "uri"
2
+
3
+ module MCollective
4
+ module Util
5
+ class Playbook
6
+ class Tasks
7
+ class Graphite_eventTask < Base
8
+ def run
9
+ webhook_task.run
10
+ end
11
+
12
+ def request
13
+ {
14
+ "what" => @what,
15
+ "tags" => @tags.join(","),
16
+ "when" => Time.now.to_i,
17
+ "data" => @data
18
+ }
19
+ end
20
+
21
+ def webhook_task
22
+ return @_webhook if @_webhook
23
+
24
+ @_webhook = Tasks::WebhookTask.new(@playbook)
25
+
26
+ @_webhook.from_hash(
27
+ "description" => @description,
28
+ "headers" => @headers,
29
+ "uri" => @graphite,
30
+ "method" => "POST",
31
+ "data" => request
32
+ )
33
+
34
+ @_webhook
35
+ end
36
+
37
+ def validate_configuration!
38
+ raise("The 'what' property is required") unless @what
39
+ raise("The 'data' property is required") unless @data
40
+ raise("The 'graphite' property is required") if @graphite == ""
41
+ raise("'tags' should be an array") unless @tags.is_a?(Array)
42
+ raise("'headers' should be a hash") unless @headers.is_a?(Hash)
43
+ raise("The graphite url should be either http or https") unless ["http", "https"].include?(@uri.scheme)
44
+ end
45
+
46
+ def to_execution_result(results)
47
+ r = webhook_task.to_execution_result(results)
48
+ r["type"] = "graphite"
49
+ r
50
+ end
51
+
52
+ def from_hash(properties)
53
+ @what = properties["what"]
54
+ @data = properties["data"]
55
+ @graphite = properties.fetch("graphite", "")
56
+ @headers = properties.fetch("headers", {})
57
+ @tags = properties.fetch("tags", ["choria"])
58
+ @uri = URI.parse(@graphite)
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,356 @@
1
+ module MCollective
2
+ module Util
3
+ class Playbook
4
+ class Tasks
5
+ class McollectiveTask < Base
6
+ def startup_hook
7
+ @properties = {}
8
+ @post = []
9
+ @nodes = []
10
+ end
11
+
12
+ # Determines if jgrep is available and loads it
13
+ #
14
+ # @return [Boolean]
15
+ def jgrep?
16
+ @_jgrep = false
17
+
18
+ require "jgrep"
19
+
20
+ if JGrep.respond_to?("validate_expression")
21
+ @_jgrep = true
22
+ else
23
+ Log.warn("Please install a version of JGrep greater than or equal to 1.5.0")
24
+ end
25
+
26
+ @_jgrep
27
+ rescue LoadError
28
+ @_jgrep = false
29
+ end
30
+
31
+ # Creates and cache an RPC::Client for the configured agent
32
+ #
33
+ # @param from_cache [Boolean] when false a new instance is always returned
34
+ # @return [RPC::Client]
35
+ def client(from_cache=true)
36
+ if from_cache
37
+ @_rpc_client ||= create_and_configure_client
38
+ else
39
+ create_and_configure_client
40
+ end
41
+ end
42
+
43
+ # Creates a new RPC::Client and configures it with the configured settings
44
+ #
45
+ # @todo discovery
46
+ # @return [RPC::Client]
47
+ def create_and_configure_client
48
+ client = RPC::Client.new(@agent, :configfile => Util.config_file_for_user, :options => Util.default_options)
49
+ client.batch_size = @batch_size if @batch_size
50
+ client.batch_sleep_time = @batch_sleep_time if @batch_sleep_time
51
+ client.discover(:nodes => @nodes)
52
+ client.progress = false
53
+ client
54
+ end
55
+
56
+ # Validates the internal configuration of the task
57
+ #
58
+ # @raise [StandardError] on failure of the internal state
59
+ def validate_configuration!
60
+ raise("Nodes have to be an array") unless @nodes.is_a?(Array)
61
+ raise("Nodes need to be supplied, refusing to run against empty node list") if @nodes.empty?
62
+
63
+ if @assertion
64
+ raise("jgrep is required for tasks with an assertion") unless jgrep?
65
+
66
+ parse_result = parse_assertion
67
+
68
+ unless parse_result == true
69
+ parse_result.tr!("\n", " ")
70
+ raise("Could not parse the assertion %s: %s" % [@assertion, parse_result])
71
+ end
72
+ end
73
+ end
74
+
75
+ # Parse an action in the form agent.action
76
+ #
77
+ # @todo check it complies to format
78
+ # @param action [String] in the form agent.action
79
+ # @return [Array<String, String>] the agent and action parts
80
+ def parse_action(action)
81
+ action.split(".")
82
+ end
83
+
84
+ # Initialize the task from a hash
85
+ #
86
+ # @param data [Hash] input data matching tasks/rpc.json schema
87
+ # @return [McollectiveTask]
88
+ def from_hash(data)
89
+ @nodes = data.fetch("nodes", [])
90
+ @agent, @action = parse_action(data["action"])
91
+ @batch_size = data["batch_size"]
92
+ @batch_sleep_time = data["batch_sleep_time"]
93
+ @properties = data.fetch("properties", {})
94
+ @post = data.fetch("post", [])
95
+ @log_replies = !data.fetch("silent", false)
96
+ @assertion = data["assert"]
97
+
98
+ @_rpc_client = nil
99
+
100
+ self
101
+ end
102
+
103
+ # Determines the run result
104
+ #
105
+ # @param stats [RPC::Stats]
106
+ # @param replies [Array<RPC::Result>]
107
+ # @return [Array<Boolean, String, Array<Hash>>] success, message, rpc replies
108
+ def run_result(stats, replies)
109
+ reply_data = replies.map do |reply|
110
+ {
111
+ "agent" => reply.agent,
112
+ "action" => reply.action,
113
+ "sender" => reply.results[:sender],
114
+ "statuscode" => reply.results[:statuscode],
115
+ "statusmsg" => reply.results[:statusmsg],
116
+ "data" => reply.results[:data],
117
+ "requestid" => stats.requestid
118
+ }
119
+ end
120
+
121
+ success = request_success?(stats)
122
+ assert_failed = -1
123
+
124
+ if @assertion && success
125
+ passed, assert_failed = assert_replies(replies)
126
+ if passed
127
+ Log.info("Assertion '%s' passed on all %d nodes" % [@assertion, replies.size])
128
+ else
129
+ Log.warn("Assertion '%s' failed on %d/%d nodes" % [@assertion, assert_failed, replies.size])
130
+ success = false
131
+ end
132
+ end
133
+
134
+ if success
135
+ msg = nil
136
+
137
+ msg = summary_message(stats) if should_summarize?
138
+ msg ||= success_message(stats)
139
+
140
+ [true, msg, reply_data]
141
+ elsif assert_failed > -1
142
+ [false, "Failed request %s for %s#%s assertion failed on %d node(s)" % [stats.requestid, @agent, @action, assert_failed], reply_data]
143
+ else
144
+ failed = stats.failcount + stats.noresponsefrom.size
145
+ [false, "Failed request %s for %s#%s on %d failed node(s)" % [stats.requestid, @agent, @action, failed], reply_data]
146
+ end
147
+ end
148
+
149
+ def to_execution_result(results)
150
+ e_result = {}
151
+
152
+ results[2].each do |data|
153
+ result = {
154
+ "value" => JSON.parse(JSON.dump(data)),
155
+ "type" => "mcollective",
156
+ "fail_ok" => @fail_ok
157
+ }
158
+
159
+ unless data["statuscode"] == 0
160
+ result["error"] = {
161
+ "msg" => data["statusmsg"],
162
+ "kind" => "choria.playbook/taskerror",
163
+ "details" => {
164
+ "agent" => @agent,
165
+ "action" => @action,
166
+ "issue_code" => data["statuscode"]
167
+ }
168
+ }
169
+ end
170
+
171
+ e_result[data["sender"]] = result
172
+ end
173
+
174
+ if client.stats
175
+ client.stats.noresponsefrom.each do |nr|
176
+ e_result[nr] = {
177
+ "value" => {},
178
+ "type" => "mcollective",
179
+ "fail_ok" => @fail_ok,
180
+ "error" => {
181
+ "msg" => "No response from node %s" % nr,
182
+ "kind" => "choria.playbook/taskerror",
183
+ "details" => {
184
+ "agent" => @agent,
185
+ "action" => @action
186
+ }
187
+ }
188
+ }
189
+ end
190
+ end
191
+
192
+ e_result
193
+ end
194
+
195
+ # Logs the result of a request
196
+ #
197
+ # @param stats [RPC::Stats]
198
+ # @param replies [Array<RPC::Result>]
199
+ def log_results(stats, replies)
200
+ if request_success?(stats)
201
+ log_success(stats)
202
+ else
203
+ log_failure(stats, replies)
204
+ end
205
+ end
206
+
207
+ # Logs a successfull request
208
+ #
209
+ # @param stats [RPC::Stats]
210
+ def log_success(stats)
211
+ Log.debug(success_message(stats))
212
+ end
213
+
214
+ # Creates the messages logged on success
215
+ #
216
+ # @return [String]
217
+ def success_message(stats)
218
+ "Successful request %s for %s#%s in %0.2fs against %d node(s)" % [stats.requestid, @agent, @action, stats.totaltime, stats.okcount]
219
+ end
220
+
221
+ # Creates the summary message
222
+ #
223
+ # @param stats [RPC::Stat]
224
+ # @return [String,nil]
225
+ def summary_message(stats)
226
+ summary = {}
227
+
228
+ if stats.aggregate_summary.size + stats.aggregate_failures.size > 0
229
+ stats.aggregate_summary.each do |aggregate|
230
+ summary.merge!(aggregate.result[:value])
231
+ end
232
+ end
233
+
234
+ return nil if summary.empty?
235
+
236
+ "Summary for %s: %s" % [@description, summary.to_json]
237
+ end
238
+
239
+ # Logs a failed request
240
+ #
241
+ # @param stats [RPC::Stats]
242
+ # @param replies [Array<RPC::Result>]
243
+ def log_failure(stats, replies)
244
+ stats ||= client.stats
245
+
246
+ Log.warn("Failed request %s for %s#%s in %0.2fs" % [stats.requestid, @agent, @action, stats.totaltime])
247
+
248
+ Log.warn("No responses from: %s" % stats.noresponsefrom.join(", ")) unless stats.noresponsefrom.empty?
249
+
250
+ if stats.failcount > 0
251
+ replies.each do |reply|
252
+ Log.warn("Failed result from %s: %s" % [reply.results[:sender], reply.results[:statusmsg]]) if reply.results[:statuscode] > 0
253
+ end
254
+ end
255
+ end
256
+
257
+ def log_reply(reply)
258
+ if reply.results[:statuscode] == 0
259
+ return unless @log_replies
260
+
261
+ Log.info("%s %s#%s success: %s" % [reply.results[:sender], @agent, @action, reply.results[:data].inspect])
262
+ else
263
+ Log.warn("%s %s#%s failure: %s" % [reply.results[:sender], @agent, @action, reply.results[:data].inspect])
264
+ end
265
+ end
266
+
267
+ # Validates the supplied assertion using JGrep's parser
268
+ #
269
+ # @return [true,String] true when its valid, else a string with any errors
270
+ def parse_assertion
271
+ return "JGrep not installed" unless jgrep?
272
+
273
+ JGrep.validate_expression(@assertion)
274
+ end
275
+
276
+ # Runs the reply data through the assertion, true when all replies match
277
+ #
278
+ # @return [Boolean, Integer] boolean indicating overall success, the integer shows how many nodes failed
279
+ def assert_replies(replies)
280
+ return [false, replies.size] unless @assertion
281
+ return [false, replies.size] unless jgrep?
282
+
283
+ result = []
284
+ expected = replies.size
285
+ jdata = replies.map {|reply| reply.results[:data]}.to_json
286
+
287
+ begin
288
+ Log.debug("Asserting '%s' over %d replies" % [@assertion, replies.size])
289
+ result = JGrep.jgrep(jdata, @assertion)
290
+ rescue
291
+ Log.error("Assertion '%s' could not be matched: %s" % [@asssertion, expected])
292
+ end
293
+
294
+ [result.size == expected, expected - result.size]
295
+ end
296
+
297
+ # Determines if a request was succesfull
298
+ #
299
+ # @param stats [RPC::Stats]
300
+ # @return [Boolean]
301
+ def request_success?(stats)
302
+ return false if stats.failcount > 0
303
+ return false if stats.okcount == 0
304
+ return false unless stats.noresponsefrom.empty?
305
+
306
+ true
307
+ end
308
+
309
+ # Determines if a summary was requested
310
+ #
311
+ # @return [Boolean]
312
+ def should_summarize?
313
+ @post.include?("summarize")
314
+ end
315
+
316
+ # Converts the string keys to symbols if the DDL is based on symbols
317
+ #
318
+ # This is to be compatible with either JSON or non JSON mcollectives
319
+ #
320
+ # @param properties [Hash] the properties for the task
321
+ # @return [Hash] mapped to what the DDL expects
322
+ def symbolize_basic_input_arguments(properties)
323
+ input = client.ddl.action_interface(@action)[:input] || {}
324
+ client.ddl.symbolize_basic_input_arguments(input, properties)
325
+ end
326
+
327
+ # Logs a single RPC reply
328
+ # Performs a single attempt at calling the agent
329
+ # @todo should return some kind of task status object
330
+ # @return [Array<Boolean, String, Array<RPC::Result>>] success, message, rpc replies
331
+ def run
332
+ Log.info("Starting request for %s#%s against %d nodes" % [@agent, @action, @nodes.size])
333
+
334
+ begin
335
+ replies = []
336
+
337
+ client.send(@action, symbolize_basic_input_arguments(@properties)) do |_, s|
338
+ replies << s
339
+ log_reply(s)
340
+ end
341
+
342
+ log_results(client.stats, replies)
343
+ run_result(client.stats, replies)
344
+ rescue
345
+ msg = "Could not create request for %s#%s: %s: %s" % [@agent, @action, $!.class, $!.to_s]
346
+ Log.debug(msg)
347
+ Log.debug($!.backtrace.join("\t\n"))
348
+
349
+ [false, msg, []]
350
+ end
351
+ end
352
+ end
353
+ end
354
+ end
355
+ end
356
+ end