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,260 @@
1
+ module MCollective
2
+ module Util
3
+ class TasksSupport
4
+ class CLI
5
+ attr_reader :output
6
+
7
+ def initialize(support, format, verbose)
8
+ @support = support
9
+
10
+ if format == :json
11
+ require_relative "json_formatter"
12
+ @output = CLI::JSONFormatter.new(self, verbose)
13
+ else
14
+ require_relative "default_formatter"
15
+ @output = CLI::DefaultFormatter.new(self, verbose)
16
+ end
17
+ end
18
+
19
+ # Shows the task list
20
+ #
21
+ # @param environment [String] the environment to query
22
+ # @param detail [Boolean] show task descriptions
23
+ # @param out [IO]
24
+ def show_task_list(environment, detail, out=$stdout)
25
+ out.puts "Known tasks in the %s environment" % environment
26
+ out.puts
27
+
28
+ known_tasks = task_list(environment, detail, out)
29
+
30
+ out.print("\r")
31
+
32
+ padding = known_tasks.keys.map(&:size).max + 2
33
+
34
+ if detail
35
+ known_tasks.each do |name, description|
36
+ out.puts " %-#{padding}s %s" % [name, description]
37
+ end
38
+
39
+ out.puts
40
+ out.puts("Use mco task <TASK> to see task help")
41
+ else
42
+ known_tasks.keys.in_groups_of(3) do |tasks|
43
+ out.puts " %s" % tasks.compact.map {|t| "%-#{padding}s" % t }.join(" ")
44
+ end
45
+
46
+ out.puts
47
+ out.puts("Use mco task <TASK> to see task help")
48
+ out.puts("Pass option --detail to see task descriptions")
49
+ end
50
+ end
51
+
52
+ # Retrieves the task list
53
+ #
54
+ # @param environment [String] the environment to query
55
+ # @param detail [Boolean] show task descriptions
56
+ def task_list(environment, detail, out=$stdout)
57
+ tasks = {}
58
+
59
+ out.print("Retrieving tasks....")
60
+
61
+ known_tasks = @support.tasks(environment)
62
+
63
+ known_tasks.each_with_index do |task, idx|
64
+ description = nil
65
+ if detail
66
+ out.print(twirl("Retrieving tasks....", known_tasks.size, idx))
67
+ meta = task_metadata(task["name"], environment)
68
+ description = meta["metadata"]["description"]
69
+ end
70
+
71
+ tasks[task["name"]] = description || ""
72
+ end
73
+
74
+ tasks
75
+ end
76
+
77
+ # Creates help for a task
78
+ #
79
+ # @param task [String] task name
80
+ # @param environment [String] environment to feetch task from
81
+ # @param out [IO]
82
+ def show_task_help(task, environment, out=$stdout)
83
+ out.puts("Retrieving task metadata for task %s from the Puppet Server" % task)
84
+
85
+ begin
86
+ meta = task_metadata(task, environment)
87
+ rescue
88
+ abort($!.to_s)
89
+ end
90
+
91
+ out.puts
92
+
93
+ out.puts("%s - %s" % [Util.colorize(:bold, task), meta["metadata"]["description"]])
94
+ out.puts
95
+
96
+ if meta["metadata"]["parameters"].nil? || meta["metadata"]["parameters"].empty?
97
+ out.puts("The task takes no parameters or have none defined")
98
+ out.puts
99
+ else
100
+ out.puts("Task Parameters:")
101
+
102
+ meta["metadata"]["parameters"].sort_by {|n, _| n}.each do |name, details|
103
+ out.puts(" %-30s %s (%s)" % [name, details["description"], details["type"]])
104
+ end
105
+ end
106
+
107
+ out.puts
108
+ out.puts "Task Files:"
109
+ meta["files"].each do |file|
110
+ out.puts(" %-30s %s bytes" % [file["filename"], file["size_bytes"]])
111
+ end
112
+
113
+ out.puts
114
+ out.puts("Use 'mco tasks run %s' to run this task" % [task])
115
+ end
116
+
117
+ # (see DefaultFormatter.print_task_summary)
118
+ def print_task_summary(*args)
119
+ @output.print_task_summary(*args)
120
+ end
121
+
122
+ # (see DefaultFormatter.print_result)
123
+ def print_result(*args)
124
+ @output.print_result(*args)
125
+ end
126
+
127
+ # (see DefaultFormatter.print_rpc_stats)
128
+ def print_rpc_stats(*args)
129
+ @output.print_rpc_stats(*args)
130
+ end
131
+
132
+ # (see DefaultFormatter.print_result_metadata)
133
+ def print_result_metadata(*args)
134
+ @output.print_result_metadata(*args)
135
+ end
136
+
137
+ # Parses the given CLI input string and creates results based on it
138
+ #
139
+ # @param configuration [Hash] the mcollective Application configuration
140
+ # @return [Hash]
141
+ def task_input(configuration)
142
+ result = {}
143
+
144
+ input = configuration[:__json_input]
145
+
146
+ if input
147
+ if input.start_with?("@")
148
+ input.sub!("@", "")
149
+ result = JSON.parse(File.read(input)) if input.end_with?("json")
150
+ result = YAML.safe_load(File.read(input)) if input.end_with?("yaml")
151
+ else
152
+ result = JSON.parse(input)
153
+ end
154
+ end
155
+
156
+ configuration.each do |item, value|
157
+ next if item.to_s.start_with?("__")
158
+
159
+ result[item.to_s] = value
160
+ end
161
+
162
+ result
163
+ end
164
+
165
+ # Validates the inputs provided on the CLI would be acceptable to the task
166
+ #
167
+ # @param task [String] task name
168
+ # @param meta [Hash] task metadata
169
+ # @param input [Hash] proposed input to pass to the task
170
+ # @return [Boolean]
171
+ # @raize [Exit] on failure
172
+ def validate_task_input(task, meta, input)
173
+ ok, reason = @support.validate_task_inputs(input, meta)
174
+
175
+ return true if ok
176
+
177
+ abort(reason)
178
+ end
179
+
180
+ # Adds CLI options for all defined input
181
+ #
182
+ # @param meta [Hash] the task metadata
183
+ # @param application [Application]
184
+ def create_task_options(task, environment, application)
185
+ meta = task_metadata(task, environment)
186
+
187
+ return if meta["metadata"]["parameters"].nil? || meta["metadata"]["parameters"].empty?
188
+
189
+ meta["metadata"]["parameters"].sort_by {|n, _| n}.each do |name, details|
190
+ type, array, _ = @support.puppet_type_to_ruby(details["type"])
191
+
192
+ properties = {
193
+ :description => "%s (%s)" % [details["description"], details["type"]],
194
+ :arguments => ["--%s %s" % [name.downcase, name.upcase]],
195
+ :type => type
196
+ }
197
+
198
+ properties[:type] = :array if array
199
+
200
+ if type == Hash
201
+ properties[:arguments] = ["--%s JSON" % name.downcase]
202
+ properties[:type] = String
203
+ end
204
+
205
+ properties[:arguments] = ["--[no-]%s" % name.downcase] if type == :boolean
206
+
207
+ application.class.option(name.intern, properties)
208
+ end
209
+ end
210
+
211
+ # Transform hash inputs given as json strings to hashes
212
+ #
213
+ # @param meta [Hash] task metadata
214
+ # @param input [Hash] proposed input to pass to the task
215
+ def transform_hash_strings(meta, input)
216
+ return unless meta["metadata"]["parameters"]
217
+
218
+ meta["metadata"]["parameters"].each do |name, details|
219
+ type, _, _ = @support.puppet_type_to_ruby(details["type"])
220
+
221
+ next unless type == Hash && input[name].is_a?(String)
222
+
223
+ begin
224
+ input[name] = JSON.parse(input[name])
225
+ rescue
226
+ raise("Could not parse input '%s' as JSON data: %s: %s" % [name, $!.class, $!.message])
227
+ end
228
+ end
229
+ end
230
+
231
+ # Retrieves task metadata from Puppet Server and cache it
232
+ #
233
+ # @param task [String] the task to fetch
234
+ # @param environment [String] Puppet environment
235
+ def task_metadata(task, environment)
236
+ @_metadata ||= {}
237
+
238
+ return @_metadata[task] if @_metadata[task]
239
+
240
+ @_metadata[task] = @support.task_metadata(task, environment)
241
+ end
242
+
243
+ # Draw a compact CLI progress indicator
244
+ #
245
+ # @param msg [String] the message to prefix
246
+ # @param max [Integer] the biggest number to expect
247
+ # @param current [Integer] the current number
248
+ def twirl(msg, max, current)
249
+ charset = ["▖", "▘", "▝", "▗"]
250
+ index = current % charset.size
251
+ char = charset[index]
252
+ char = Util.colorize(:green, "✓") if max == current
253
+
254
+ format = "\r%s %s %#{@max.to_s.size}d / %d"
255
+ format % [msg, char, current, max]
256
+ end
257
+ end
258
+ end
259
+ end
260
+ end
@@ -0,0 +1,138 @@
1
+ module MCollective
2
+ module Util
3
+ class TasksSupport
4
+ class CLI
5
+ class DefaultFormatter
6
+ attr_reader :out
7
+
8
+ def initialize(cli, verbose=false, out=$stdout)
9
+ @cli = cli
10
+ @verbose = verbose
11
+ @out = out
12
+ end
13
+
14
+ # Prints the summary of a task over a number of machines
15
+ #
16
+ # @param taskid [String] the task id
17
+ # @param names [Array<String>] list of unique task names seen in the estate
18
+ # @param callers [Array<String>] list of unique callers seen in the estate
19
+ # @param completed [Integer] nodes that completed the task - success or fails
20
+ # @param running [Integer] nodes still running the task
21
+ # @param task_not_known [Integer] nodes where the task is unknown
22
+ # @param wrapper_failure [Integer] nodes where the wrapper executable failed to launch
23
+ # @param success [Integer] nodes that completed the task succesfully
24
+ # @param fails [Integer] nodes that did not complete the task succesfully
25
+ # @param runtime [Float] total run time across all nodes
26
+ # @param rpcstats [RPC::Stats] the stats from the RPC client that fetched the statusses
27
+ def print_task_summary(taskid, names, callers, completed, running, task_not_known, wrapper_failure, success, fails, runtime, rpcstats)
28
+ if callers.size > 1 || names.size > 1
29
+ out.puts
30
+ out.puts("%s received more than 1 task name or caller name for this task, this should not happen" % Util.colorize(:red, "WARNING"))
31
+ out.puts("happen in normal operations and might indicate forged requests were made or cache corruption.")
32
+ out.puts
33
+ end
34
+
35
+ out.puts("Summary for task %s" % [Util.colorize(:bold, taskid)])
36
+ out.puts
37
+ out.puts(" Task Name: %s" % names.join(","))
38
+ out.puts(" Caller: %s" % callers.join(","))
39
+ out.puts(" Completed: %s" % (completed > 0 ? Util.colorize(:green, completed) : Util.colorize(:yellow, completed)))
40
+ out.puts(" Running: %s" % (running > 0 ? Util.colorize(:yellow, running) : Util.colorize(:green, running)))
41
+ out.puts(" Unknown Task: %s" % Util.colorize(:red, task_not_known)) if task_not_known > 0
42
+ out.puts(" Wrapper Failure: %s" % Util.colorize(:red, wrapper_failure)) if wrapper_failure > 0
43
+ out.puts
44
+ out.puts(" Successful: %s" % (success > 0 ? Util.colorize(:green, success) : Util.colorize(:red, success)))
45
+ out.puts(" Failed: %s" % (fails > 0 ? Util.colorize(:red, fails) : fails))
46
+ out.puts
47
+ out.puts(" Average Run Time: %.2fs" % [runtime / (running + completed)])
48
+
49
+ if rpcstats.noresponsefrom.empty?
50
+ out.puts
51
+ out.puts rpcstats.no_response_report
52
+ end
53
+
54
+ if running > 0
55
+ out.puts
56
+ out.puts("%s nodes are still running, use 'mco tasks status %s' to check on them later" % [Util.colorize(:bold, running), taskid])
57
+ end
58
+ end
59
+
60
+ # Prints the RPC stats for a request
61
+ #
62
+ # @param stats [RPC::Stats] request stats
63
+ def print_rpc_stats(stats)
64
+ out.puts stats.report("Task Stats", false, @verbose)
65
+ end
66
+
67
+ # Prints an individual result
68
+ #
69
+ # @param result [RPC::Result]
70
+ def print_result(result)
71
+ status = result[:data]
72
+ stdout_text = status[:stdout] || ""
73
+
74
+ unless @verbose
75
+ begin
76
+ stdout_text = JSON.parse(status[:stdout])
77
+ stdout_text.delete("_error")
78
+ stdout_text = stdout_text.to_json
79
+ stdout_text = nil if stdout_text == "{}"
80
+ rescue # rubocop:disable Lint/SuppressedException
81
+ end
82
+ end
83
+
84
+ if result[:statuscode] != 0
85
+ out.puts("%-40s %s" % [
86
+ Util.colorize(:red, result[:sender]),
87
+ Util.colorize(:yellow, result[:statusmsg])
88
+ ])
89
+
90
+ out.puts(" %s" % stdout_text) if stdout_text
91
+ out.puts(" %s" % status[:stderr]) unless ["", nil].include?(status[:stderr])
92
+ out.puts
93
+ elsif result[:statuscode] == 0 && @verbose
94
+ out.puts(result[:sender])
95
+ out.puts(" %s" % stdout_text) if stdout_text
96
+ out.puts(" %s" % status[:stderr]) unless ["", nil].include?(status[:stderr])
97
+ out.puts
98
+ end
99
+ end
100
+
101
+ # Prints metadata for an individual result
102
+ #
103
+ # @param status [RPC::Reply] the individual task status to print
104
+ def print_result_metadata(status)
105
+ result = status.results
106
+
107
+ if [0, 1].include?(result[:statuscode])
108
+ if result[:data][:exitcode] == 0
109
+ out.puts(" %-40s %s" % [result[:sender], Util.colorize(:green, result[:data][:exitcode])])
110
+ else
111
+ out.puts(" %-40s %s" % [result[:sender], Util.colorize(:red, result[:data][:exitcode])])
112
+ end
113
+
114
+ out.puts(" %s by %s at %s" % [
115
+ Util.colorize(:bold, result[:data][:task]),
116
+ result[:data][:callerid],
117
+ Time.at(result[:data][:start_time]).utc.strftime("%F %T")
118
+ ])
119
+
120
+ out.puts(" completed: %s runtime: %s stdout: %s stderr: %s" % [
121
+ result[:data][:completed] ? Util.colorize(:bold, "yes") : Util.colorize(:yellow, "no"),
122
+ Util.colorize(:bold, "%.2f" % result[:data][:runtime]),
123
+ result[:data][:stdout].empty? ? Util.colorize(:yellow, "no") : Util.colorize(:bold, "yes"),
124
+ result[:data][:stderr].empty? ? Util.colorize(:bold, "no") : Util.colorize(:red, "yes")
125
+ ])
126
+ elsif result[:statuscode] == 3
127
+ out.puts(" %-40s %s" % [result[:sender], Util.colorize(:yellow, "Unknown Task")])
128
+ else
129
+ out.puts(" %-40s %s" % [result[:sender], Util.colorize(:yellow, result[:statusmsg])])
130
+ end
131
+
132
+ out.puts
133
+ end
134
+ end
135
+ end
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,108 @@
1
+ module MCollective
2
+ module Util
3
+ class TasksSupport
4
+ class CLI
5
+ class JSONFormatter
6
+ attr_reader :out
7
+
8
+ def initialize(cli, verbose=false, out=$stdout)
9
+ @cli = cli
10
+ @verbose = verbose
11
+ @out = out
12
+
13
+ @started = false
14
+ @in_results = false
15
+ @in_stats = false
16
+ @first_item = true
17
+ end
18
+
19
+ def start
20
+ return if @started
21
+
22
+ @started = true
23
+ @in_results = true
24
+
25
+ out.puts '{"items": ['
26
+ end
27
+
28
+ def end_results
29
+ return unless @in_results
30
+
31
+ @in_results = false
32
+ out.puts "],"
33
+ end
34
+
35
+ # (see DefaultFormatter.print_task_summary)
36
+ def print_task_summary(taskid, names, callers, completed, running, task_not_known, wrapper_failure, success, fails, runtime, rpcstats)
37
+ end_results
38
+
39
+ stats = {
40
+ "names" => names,
41
+ "callers" => callers,
42
+ "completed" => completed,
43
+ "running" => running,
44
+ "task_not_known" => task_not_known,
45
+ "wrapper_failure" => wrapper_failure,
46
+ "success" => success,
47
+ "failed" => fails,
48
+ "average_runtime" => runtime / (running + completed),
49
+ "noresponses" => rpcstats.noresponsefrom
50
+ }
51
+
52
+ summary = {
53
+ "nodes" => rpcstats.discovered_nodes.size,
54
+ "taskid" => taskid,
55
+ "completed" => stats["completed"] == rpcstats.discovered_nodes.size,
56
+ "success" => success == rpcstats.discovered_nodes.size
57
+ }
58
+
59
+ out.puts '"stats":'
60
+ out.puts stats.to_json
61
+ out.puts ","
62
+ out.puts '"summary":'
63
+
64
+ out.puts summary.to_json
65
+ out.puts "}"
66
+ end
67
+
68
+ # (see DefaultFormatter.print_rpc_stats)
69
+ def print_rpc_stats(stats)
70
+ end_results
71
+ end
72
+
73
+ # (see DefaultFormatter.print_result)
74
+ def print_result(result)
75
+ start
76
+
77
+ result = result.results
78
+
79
+ item = {
80
+ "host" => result[:sender]
81
+ }
82
+
83
+ result[:data].each do |k, v|
84
+ item[k.to_s] = v
85
+ end
86
+
87
+ begin
88
+ item["stdout"] = JSON.parse(item["stdout"])
89
+ item["stdout"] = item["stdout"].delete("_output") if item["stdout"]["_output"]
90
+ rescue # rubocop:disable Lint/SuppressedException
91
+ end
92
+
93
+ out.puts "," unless @first_item
94
+
95
+ out.puts item.to_json
96
+
97
+ @first_item = false
98
+ end
99
+
100
+ # (see DefaultFormatter.print_result_metadata)
101
+ def print_result_metadata(status)
102
+ print_result(status)
103
+ end
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end