choria-mcorpc-support 2.22.1 → 2.23.0.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (145) hide show
  1. checksums.yaml +4 -4
  2. data/lib/mcollective.rb +1 -1
  3. data/lib/mcollective/agent/bolt_tasks.ddl +235 -0
  4. data/lib/mcollective/agent/bolt_tasks.json +347 -0
  5. data/lib/mcollective/agent/bolt_tasks.rb +176 -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 +7 -3
  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 +249 -0
  23. data/lib/mcollective/application/completion.rb +6 -6
  24. data/lib/mcollective/application/describe_filter.rb +20 -20
  25. data/lib/mcollective/application/facts.rb +11 -11
  26. data/lib/mcollective/application/federation.rb +239 -0
  27. data/lib/mcollective/application/find.rb +4 -4
  28. data/lib/mcollective/application/help.rb +3 -3
  29. data/lib/mcollective/application/inventory.rb +3 -341
  30. data/lib/mcollective/application/ping.rb +3 -77
  31. data/lib/mcollective/application/playbook.rb +207 -0
  32. data/lib/mcollective/application/plugin.rb +106 -106
  33. data/lib/mcollective/application/rpc.rb +3 -108
  34. data/lib/mcollective/application/tasks.rb +416 -0
  35. data/lib/mcollective/applications.rb +11 -10
  36. data/lib/mcollective/audit/choria.rb +33 -0
  37. data/lib/mcollective/cache.rb +2 -4
  38. data/lib/mcollective/client.rb +11 -10
  39. data/lib/mcollective/config.rb +21 -26
  40. data/lib/mcollective/connector/base.rb +2 -1
  41. data/lib/mcollective/connector/nats.ddl +9 -0
  42. data/lib/mcollective/connector/nats.rb +450 -0
  43. data/lib/mcollective/data.rb +8 -3
  44. data/lib/mcollective/data/agent_data.rb +1 -1
  45. data/lib/mcollective/data/base.rb +6 -5
  46. data/lib/mcollective/data/bolt_task_data.ddl +90 -0
  47. data/lib/mcollective/data/bolt_task_data.rb +32 -0
  48. data/lib/mcollective/data/collective_data.rb +1 -1
  49. data/lib/mcollective/data/fact_data.rb +6 -6
  50. data/lib/mcollective/data/fstat_data.rb +2 -4
  51. data/lib/mcollective/data/result.rb +7 -2
  52. data/lib/mcollective/ddl/agentddl.rb +5 -17
  53. data/lib/mcollective/ddl/base.rb +10 -13
  54. data/lib/mcollective/discovery.rb +12 -26
  55. data/lib/mcollective/discovery/choria.ddl +11 -0
  56. data/lib/mcollective/discovery/choria.rb +223 -0
  57. data/lib/mcollective/discovery/flatfile.rb +7 -8
  58. data/lib/mcollective/discovery/mc.rb +2 -2
  59. data/lib/mcollective/discovery/stdin.rb +17 -18
  60. data/lib/mcollective/exceptions.rb +13 -0
  61. data/lib/mcollective/facts/base.rb +9 -9
  62. data/lib/mcollective/facts/yaml_facts.rb +12 -12
  63. data/lib/mcollective/generators.rb +3 -3
  64. data/lib/mcollective/generators/agent_generator.rb +3 -4
  65. data/lib/mcollective/generators/base.rb +14 -15
  66. data/lib/mcollective/generators/data_generator.rb +5 -6
  67. data/lib/mcollective/log.rb +2 -2
  68. data/lib/mcollective/logger/base.rb +3 -2
  69. data/lib/mcollective/logger/console_logger.rb +10 -10
  70. data/lib/mcollective/logger/file_logger.rb +7 -7
  71. data/lib/mcollective/logger/syslog_logger.rb +11 -15
  72. data/lib/mcollective/matcher.rb +14 -14
  73. data/lib/mcollective/matcher/parser.rb +31 -41
  74. data/lib/mcollective/matcher/scanner.rb +69 -74
  75. data/lib/mcollective/message.rb +10 -17
  76. data/lib/mcollective/monkey_patches.rb +2 -4
  77. data/lib/mcollective/optionparser.rb +1 -0
  78. data/lib/mcollective/pluginmanager.rb +3 -5
  79. data/lib/mcollective/pluginpackager.rb +1 -3
  80. data/lib/mcollective/pluginpackager/agent_definition.rb +3 -8
  81. data/lib/mcollective/pluginpackager/forge_packager.rb +7 -9
  82. data/lib/mcollective/pluginpackager/standard_definition.rb +1 -2
  83. data/lib/mcollective/registration/base.rb +18 -16
  84. data/lib/mcollective/rpc.rb +2 -4
  85. data/lib/mcollective/rpc/actionrunner.rb +16 -18
  86. data/lib/mcollective/rpc/agent.rb +26 -43
  87. data/lib/mcollective/rpc/audit.rb +1 -0
  88. data/lib/mcollective/rpc/client.rb +67 -85
  89. data/lib/mcollective/rpc/helpers.rb +55 -62
  90. data/lib/mcollective/rpc/progress.rb +2 -2
  91. data/lib/mcollective/rpc/reply.rb +17 -19
  92. data/lib/mcollective/rpc/request.rb +7 -5
  93. data/lib/mcollective/rpc/result.rb +6 -8
  94. data/lib/mcollective/rpc/stats.rb +49 -58
  95. data/lib/mcollective/security/base.rb +29 -36
  96. data/lib/mcollective/security/choria.rb +765 -0
  97. data/lib/mcollective/shell.rb +9 -4
  98. data/lib/mcollective/signer/base.rb +28 -0
  99. data/lib/mcollective/signer/choria.rb +185 -0
  100. data/lib/mcollective/ssl.rb +8 -6
  101. data/lib/mcollective/util.rb +52 -53
  102. data/lib/mcollective/util/bolt_support.rb +176 -0
  103. data/lib/mcollective/util/bolt_support/plan_runner.rb +167 -0
  104. data/lib/mcollective/util/bolt_support/task_result.rb +94 -0
  105. data/lib/mcollective/util/bolt_support/task_results.rb +128 -0
  106. data/lib/mcollective/util/choria.rb +1103 -0
  107. data/lib/mcollective/util/indifferent_hash.rb +12 -0
  108. data/lib/mcollective/util/natswrapper.rb +242 -0
  109. data/lib/mcollective/util/playbook.rb +435 -0
  110. data/lib/mcollective/util/playbook/data_stores.rb +201 -0
  111. data/lib/mcollective/util/playbook/data_stores/base.rb +99 -0
  112. data/lib/mcollective/util/playbook/data_stores/consul_data_store.rb +88 -0
  113. data/lib/mcollective/util/playbook/data_stores/environment_data_store.rb +33 -0
  114. data/lib/mcollective/util/playbook/data_stores/etcd_data_store.rb +42 -0
  115. data/lib/mcollective/util/playbook/data_stores/file_data_store.rb +106 -0
  116. data/lib/mcollective/util/playbook/data_stores/shell_data_store.rb +103 -0
  117. data/lib/mcollective/util/playbook/inputs.rb +265 -0
  118. data/lib/mcollective/util/playbook/nodes.rb +207 -0
  119. data/lib/mcollective/util/playbook/nodes/mcollective_nodes.rb +86 -0
  120. data/lib/mcollective/util/playbook/nodes/pql_nodes.rb +40 -0
  121. data/lib/mcollective/util/playbook/nodes/shell_nodes.rb +55 -0
  122. data/lib/mcollective/util/playbook/nodes/terraform_nodes.rb +65 -0
  123. data/lib/mcollective/util/playbook/nodes/yaml_nodes.rb +47 -0
  124. data/lib/mcollective/util/playbook/playbook_logger.rb +47 -0
  125. data/lib/mcollective/util/playbook/puppet_logger.rb +51 -0
  126. data/lib/mcollective/util/playbook/report.rb +152 -0
  127. data/lib/mcollective/util/playbook/task_result.rb +55 -0
  128. data/lib/mcollective/util/playbook/tasks.rb +196 -0
  129. data/lib/mcollective/util/playbook/tasks/base.rb +45 -0
  130. data/lib/mcollective/util/playbook/tasks/graphite_event_task.rb +64 -0
  131. data/lib/mcollective/util/playbook/tasks/mcollective_task.rb +356 -0
  132. data/lib/mcollective/util/playbook/tasks/shell_task.rb +93 -0
  133. data/lib/mcollective/util/playbook/tasks/slack_task.rb +105 -0
  134. data/lib/mcollective/util/playbook/tasks/webhook_task.rb +136 -0
  135. data/lib/mcollective/util/playbook/template_util.rb +98 -0
  136. data/lib/mcollective/util/playbook/uses.rb +169 -0
  137. data/lib/mcollective/util/tasks_support.rb +733 -0
  138. data/lib/mcollective/util/tasks_support/cli.rb +260 -0
  139. data/lib/mcollective/util/tasks_support/default_formatter.rb +138 -0
  140. data/lib/mcollective/util/tasks_support/json_formatter.rb +108 -0
  141. data/lib/mcollective/validator.rb +6 -1
  142. data/lib/mcollective/validator/bolt_task_name_validator.ddl +7 -0
  143. data/lib/mcollective/validator/bolt_task_name_validator.rb +11 -0
  144. data/lib/mcollective/validator/length_validator.rb +1 -3
  145. metadata +67 -4
@@ -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