mcollective-client 1.3.3

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.

Files changed (103) hide show
  1. data/bin/mc-call-agent +54 -0
  2. data/bin/mco +27 -0
  3. data/lib/mcollective.rb +70 -0
  4. data/lib/mcollective/agents.rb +160 -0
  5. data/lib/mcollective/application.rb +354 -0
  6. data/lib/mcollective/applications.rb +145 -0
  7. data/lib/mcollective/client.rb +292 -0
  8. data/lib/mcollective/config.rb +202 -0
  9. data/lib/mcollective/connector.rb +18 -0
  10. data/lib/mcollective/connector/base.rb +24 -0
  11. data/lib/mcollective/facts.rb +39 -0
  12. data/lib/mcollective/facts/base.rb +86 -0
  13. data/lib/mcollective/log.rb +103 -0
  14. data/lib/mcollective/logger.rb +5 -0
  15. data/lib/mcollective/logger/base.rb +73 -0
  16. data/lib/mcollective/logger/console_logger.rb +61 -0
  17. data/lib/mcollective/logger/file_logger.rb +46 -0
  18. data/lib/mcollective/logger/syslog_logger.rb +53 -0
  19. data/lib/mcollective/matcher.rb +16 -0
  20. data/lib/mcollective/matcher/parser.rb +93 -0
  21. data/lib/mcollective/matcher/scanner.rb +123 -0
  22. data/lib/mcollective/message.rb +201 -0
  23. data/lib/mcollective/monkey_patches.rb +104 -0
  24. data/lib/mcollective/optionparser.rb +164 -0
  25. data/lib/mcollective/pluginmanager.rb +180 -0
  26. data/lib/mcollective/pluginpackager.rb +26 -0
  27. data/lib/mcollective/pluginpackager/agent_definition.rb +79 -0
  28. data/lib/mcollective/pluginpackager/standard_definition.rb +59 -0
  29. data/lib/mcollective/registration.rb +16 -0
  30. data/lib/mcollective/registration/base.rb +75 -0
  31. data/lib/mcollective/rpc.rb +188 -0
  32. data/lib/mcollective/rpc/actionrunner.rb +142 -0
  33. data/lib/mcollective/rpc/agent.rb +441 -0
  34. data/lib/mcollective/rpc/audit.rb +38 -0
  35. data/lib/mcollective/rpc/client.rb +793 -0
  36. data/lib/mcollective/rpc/ddl.rb +258 -0
  37. data/lib/mcollective/rpc/helpers.rb +339 -0
  38. data/lib/mcollective/rpc/progress.rb +63 -0
  39. data/lib/mcollective/rpc/reply.rb +61 -0
  40. data/lib/mcollective/rpc/request.rb +51 -0
  41. data/lib/mcollective/rpc/result.rb +41 -0
  42. data/lib/mcollective/rpc/stats.rb +185 -0
  43. data/lib/mcollective/runnerstats.rb +90 -0
  44. data/lib/mcollective/security.rb +26 -0
  45. data/lib/mcollective/security/base.rb +237 -0
  46. data/lib/mcollective/shell.rb +87 -0
  47. data/lib/mcollective/ssl.rb +246 -0
  48. data/lib/mcollective/unix_daemon.rb +37 -0
  49. data/lib/mcollective/util.rb +274 -0
  50. data/lib/mcollective/vendor.rb +41 -0
  51. data/lib/mcollective/vendor/require_vendored.rb +2 -0
  52. data/lib/mcollective/windows_daemon.rb +25 -0
  53. data/spec/Rakefile +16 -0
  54. data/spec/fixtures/application/test.rb +7 -0
  55. data/spec/fixtures/test-cert.pem +15 -0
  56. data/spec/fixtures/test-private.pem +15 -0
  57. data/spec/fixtures/test-public.pem +6 -0
  58. data/spec/monkey_patches/instance_variable_defined.rb +7 -0
  59. data/spec/spec.opts +1 -0
  60. data/spec/spec_helper.rb +25 -0
  61. data/spec/unit/agents_spec.rb +280 -0
  62. data/spec/unit/application_spec.rb +636 -0
  63. data/spec/unit/applications_spec.rb +155 -0
  64. data/spec/unit/array.rb +30 -0
  65. data/spec/unit/config_spec.rb +148 -0
  66. data/spec/unit/facts/base_spec.rb +118 -0
  67. data/spec/unit/facts_spec.rb +39 -0
  68. data/spec/unit/log_spec.rb +71 -0
  69. data/spec/unit/logger/base_spec.rb +110 -0
  70. data/spec/unit/logger/syslog_logger_spec.rb +86 -0
  71. data/spec/unit/matcher/parser_spec.rb +106 -0
  72. data/spec/unit/matcher/scanner_spec.rb +71 -0
  73. data/spec/unit/message_spec.rb +401 -0
  74. data/spec/unit/optionparser_spec.rb +113 -0
  75. data/spec/unit/pluginmanager_spec.rb +173 -0
  76. data/spec/unit/pluginpackager/agent_definition_spec.rb +130 -0
  77. data/spec/unit/pluginpackager/standard_definition_spec.rb +75 -0
  78. data/spec/unit/plugins/mcollective/connector/activemq_spec.rb +533 -0
  79. data/spec/unit/plugins/mcollective/connector/stomp/eventlogger_spec.rb +34 -0
  80. data/spec/unit/plugins/mcollective/connector/stomp_spec.rb +417 -0
  81. data/spec/unit/plugins/mcollective/packagers/ospackage_spec.rb +229 -0
  82. data/spec/unit/plugins/mcollective/security/psk_spec.rb +156 -0
  83. data/spec/unit/registration/base_spec.rb +77 -0
  84. data/spec/unit/rpc/actionrunner_spec.rb +213 -0
  85. data/spec/unit/rpc/agent_spec.rb +155 -0
  86. data/spec/unit/rpc/client_spec.rb +523 -0
  87. data/spec/unit/rpc/ddl_spec.rb +388 -0
  88. data/spec/unit/rpc/helpers_spec.rb +55 -0
  89. data/spec/unit/rpc/reply_spec.rb +143 -0
  90. data/spec/unit/rpc/request_spec.rb +115 -0
  91. data/spec/unit/rpc/result_spec.rb +66 -0
  92. data/spec/unit/rpc/stats_spec.rb +288 -0
  93. data/spec/unit/runnerstats_spec.rb +40 -0
  94. data/spec/unit/security/base_spec.rb +279 -0
  95. data/spec/unit/shell_spec.rb +144 -0
  96. data/spec/unit/ssl_spec.rb +244 -0
  97. data/spec/unit/symbol.rb +11 -0
  98. data/spec/unit/unix_daemon.rb +41 -0
  99. data/spec/unit/util_spec.rb +342 -0
  100. data/spec/unit/vendor_spec.rb +34 -0
  101. data/spec/unit/windows_daemon.rb +43 -0
  102. data/spec/windows_spec.opts +1 -0
  103. metadata +242 -0
@@ -0,0 +1,258 @@
1
+ module MCollective
2
+ module RPC
3
+ # A class that helps creating data description language files
4
+ # for agents. You can define meta data, actions, input and output
5
+ # describing the behavior of your agent.
6
+ #
7
+ # Later you can access this information to assist with creating
8
+ # of user interfaces or online help
9
+ #
10
+ # A sample DDL can 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
+ class DDL
37
+ attr_reader :meta
38
+
39
+ def initialize(agent, loadddl=true)
40
+ @actions = {}
41
+ @meta = {}
42
+ @config = MCollective::Config.instance
43
+ @agent = agent
44
+
45
+ if loadddl
46
+ if ddlfile = findddlfile(agent)
47
+ instance_eval(File.read(ddlfile))
48
+ else
49
+ raise("Can't find DDL for agent '#{agent}'")
50
+ end
51
+ end
52
+ end
53
+
54
+ def findddlfile(agent)
55
+ @config.libdir.each do |libdir|
56
+ ddlfile = File.join([libdir, "mcollective", "agent", "#{agent}.ddl"])
57
+
58
+ if File.exist?(ddlfile)
59
+ Log.debug("Found #{agent} ddl at #{ddlfile}")
60
+ return ddlfile
61
+ end
62
+ end
63
+ return false
64
+ end
65
+
66
+ # Registers meta data for the introspection hash
67
+ def metadata(meta)
68
+ [:name, :description, :author, :license, :version, :url, :timeout].each do |arg|
69
+ raise "Metadata needs a :#{arg}" unless meta.include?(arg)
70
+ end
71
+
72
+ @meta = meta
73
+ end
74
+
75
+ # Creates the definition for an action, you can nest input definitions inside the
76
+ # action to attach inputs and validation to the actions
77
+ #
78
+ # action "status", :description => "Restarts a Service" do
79
+ # display :always
80
+ #
81
+ # input "service",
82
+ # :prompt => "Service Action",
83
+ # :description => "The action to perform",
84
+ # :type => :list,
85
+ # :optional => true,
86
+ # :list => ["start", "stop", "restart", "status"]
87
+ #
88
+ # output "status"
89
+ # :description => "The status of the service after the action"
90
+ #
91
+ # end
92
+ def action(name, input, &block)
93
+ raise "Action needs a :description" unless input.include?(:description)
94
+
95
+ unless @actions.include?(name)
96
+ @actions[name] = {}
97
+ @actions[name][:action] = name
98
+ @actions[name][:input] = {}
99
+ @actions[name][:output] = {}
100
+ @actions[name][:display] = :failed
101
+ @actions[name][:description] = input[:description]
102
+ end
103
+
104
+ # if a block is passed it might be creating input methods, call it
105
+ # we set @current_action so the input block can know what its talking
106
+ # to, this is probably an epic hack, need to improve.
107
+ @current_action = name
108
+ block.call if block_given?
109
+ @current_action = nil
110
+ end
111
+
112
+ # Registers an input argument for a given action
113
+ #
114
+ # See the documentation for action for how to use this
115
+ def input(argument, properties)
116
+ raise "Cannot figure out what action input #{argument} belongs to" unless @current_action
117
+
118
+ action = @current_action
119
+
120
+ [:prompt, :description, :type, :optional].each do |arg|
121
+ raise "Input needs a :#{arg}" unless properties.include?(arg)
122
+ end
123
+
124
+ @actions[action][:input][argument] = {:prompt => properties[:prompt],
125
+ :description => properties[:description],
126
+ :type => properties[:type],
127
+ :optional => properties[:optional]}
128
+
129
+ case properties[:type]
130
+ when :string
131
+ raise "Input type :string needs a :validation argument" unless properties.include?(:validation)
132
+ raise "Input type :string needs a :maxlength argument" unless properties.include?(:maxlength)
133
+
134
+ @actions[action][:input][argument][:validation] = properties[:validation]
135
+ @actions[action][:input][argument][:maxlength] = properties[:maxlength]
136
+
137
+ when :list
138
+ raise "Input type :list needs a :list argument" unless properties.include?(:list)
139
+
140
+ @actions[action][:input][argument][:list] = properties[:list]
141
+ end
142
+ end
143
+
144
+ # Registers an output argument for a given action
145
+ #
146
+ # See the documentation for action for how to use this
147
+ def output(argument, properties)
148
+ raise "Cannot figure out what action input #{argument} belongs to" unless @current_action
149
+ raise "Output #{argument} needs a description argument" unless properties.include?(:description)
150
+ raise "Output #{argument} needs a display_as argument" unless properties.include?(:display_as)
151
+
152
+ action = @current_action
153
+
154
+ @actions[action][:output][argument] = {:description => properties[:description],
155
+ :display_as => properties[:display_as]}
156
+ end
157
+
158
+ # Sets the display preference to either :ok, :failed, :flatten or :always
159
+ # operates on action level
160
+ def display(pref)
161
+ # defaults to old behavior, complain if its supplied and invalid
162
+ unless [:ok, :failed, :flatten, :always].include?(pref)
163
+ raise "Display preference #{pref} is not valid, should be :ok, :failed, :flatten or :always"
164
+ end
165
+
166
+ action = @current_action
167
+ @actions[action][:display] = pref
168
+ end
169
+
170
+ # Generates help using the template based on the data
171
+ # created with metadata and input
172
+ def help(template)
173
+ template = IO.read(template)
174
+ meta = @meta
175
+ actions = @actions
176
+
177
+ erb = ERB.new(template, 0, '%')
178
+ erb.result(binding)
179
+ end
180
+
181
+ # Returns an array of actions this agent support
182
+ def actions
183
+ @actions.keys
184
+ end
185
+
186
+ # Returns the interface for a specific action
187
+ def action_interface(name)
188
+ @actions[name] || {}
189
+ end
190
+
191
+ # Helper to use the DDL to figure out if the remote call should be
192
+ # allowed based on action name and inputs.
193
+ def validate_request(action, arguments)
194
+ # is the action known?
195
+ unless actions.include?(action)
196
+ raise DDLValidationError, "Attempted to call action #{action} for #{@agent} but it's not declared in the DDL"
197
+ end
198
+
199
+ input = action_interface(action)[:input]
200
+
201
+ input.keys.each do |key|
202
+ unless input[key][:optional]
203
+ unless arguments.keys.include?(key)
204
+ raise DDLValidationError, "Action #{action} needs a #{key} argument"
205
+ end
206
+ end
207
+
208
+ # validate strings, lists and booleans, we'll add more types of validators when
209
+ # all the use cases are clear
210
+ #
211
+ # only does validation for arguments actually given, since some might
212
+ # be optional. We validate the presense of the argument earlier so
213
+ # this is a safe assumption, just to skip them.
214
+ #
215
+ # :string can have maxlength and regex. A maxlength of 0 will bypasss checks
216
+ # :list has a array of valid values
217
+ if arguments.keys.include?(key)
218
+ case input[key][:type]
219
+ when :string
220
+ raise DDLValidationError, "Input #{key} should be a string" unless arguments[key].is_a?(String)
221
+
222
+ if input[key][:maxlength].to_i > 0
223
+ if arguments[key].size > input[key][:maxlength].to_i
224
+ raise DDLValidationError, "Input #{key} is longer than #{input[key][:maxlength]} character(s)"
225
+ end
226
+ end
227
+
228
+ unless arguments[key].match(Regexp.new(input[key][:validation]))
229
+ raise DDLValidationError, "Input #{key} does not match validation regex #{input[key][:validation]}"
230
+ end
231
+
232
+ when :list
233
+ unless input[key][:list].include?(arguments[key])
234
+ raise DDLValidationError, "Input #{key} doesn't match list #{input[key][:list].join(', ')}"
235
+ end
236
+
237
+ when :boolean
238
+ unless [TrueClass, FalseClass].include?(arguments[key].class)
239
+ raise DDLValidationError, "Input #{key} should be a boolean"
240
+ end
241
+
242
+ when :integer
243
+ raise DDLValidationError, "Input #{key} should be a integer" unless arguments[key].is_a?(Fixnum)
244
+
245
+ when :float
246
+ raise DDLValidationError, "Input #{key} should be a floating point number" unless arguments[key].is_a?(Float)
247
+
248
+ when :number
249
+ raise DDLValidationError, "Input #{key} should be a number" unless arguments[key].is_a?(Numeric)
250
+ end
251
+ end
252
+ end
253
+
254
+ true
255
+ end
256
+ end
257
+ end
258
+ end
@@ -0,0 +1,339 @@
1
+ module MCollective
2
+ module RPC
3
+ # Various utilities for the RPC system
4
+ class Helpers
5
+ # Checks in PATH returns true if the command is found
6
+ def self.command_in_path?(command)
7
+ found = ENV["PATH"].split(File::PATH_SEPARATOR).map do |p|
8
+ File.exist?(File.join(p, command))
9
+ end
10
+
11
+ found.include?(true)
12
+ end
13
+
14
+ # Parse JSON output as produced by printrpc and extract
15
+ # the "sender" of each rpc response
16
+ #
17
+ # The simplist valid JSON based data would be:
18
+ #
19
+ # [
20
+ # {"sender" => "example.com"},
21
+ # {"sender" => "another.com"}
22
+ # ]
23
+ def self.extract_hosts_from_json(json)
24
+ hosts = JSON.parse(json)
25
+
26
+ raise "JSON hosts list is not an array" unless hosts.is_a?(Array)
27
+
28
+ hosts.map do |host|
29
+ raise "JSON host list is not an array of Hashes" unless host.is_a?(Hash)
30
+ raise "JSON host list does not have senders in it" unless host.include?("sender")
31
+
32
+ host["sender"]
33
+ end.uniq
34
+ end
35
+
36
+ # Given an array of something, make sure each is a string
37
+ # chomp off any new lines and return just the array of hosts
38
+ def self.extract_hosts_from_array(hosts)
39
+ [hosts].flatten.map do |host|
40
+ raise "#{host} should be a string" unless host.is_a?(String)
41
+ host.chomp
42
+ end
43
+ end
44
+
45
+ # Figures out the columns and liens of the current tty
46
+ #
47
+ # Returns [0, 0] if it can't figure it out or if you're
48
+ # not running on a tty
49
+ def self.terminal_dimensions
50
+ return [0, 0] unless STDOUT.tty?
51
+
52
+ return [80, 40] if Util.windows?
53
+
54
+ if ENV["COLUMNS"] && ENV["LINES"]
55
+ return [ENV["COLUMNS"].to_i, ENV["LINES"].to_i]
56
+
57
+ elsif ENV["TERM"] && command_in_path?("tput")
58
+ return [`tput cols`.to_i, `tput lines`.to_i]
59
+
60
+ elsif command_in_path?('stty')
61
+ return `stty size`.scan(/\d+/).map {|s| s.to_i }
62
+ else
63
+ return [0, 0]
64
+ end
65
+ rescue
66
+ [0, 0]
67
+ end
68
+
69
+ # Return color codes, if the config color= option is false
70
+ # just return a empty string
71
+ def self.color(code)
72
+ colorize = Config.instance.color
73
+
74
+ colors = {:red => "",
75
+ :green => "",
76
+ :yellow => "",
77
+ :cyan => "",
78
+ :bold => "",
79
+ :reset => ""}
80
+
81
+ if colorize
82
+ return colors[code] || ""
83
+ else
84
+ return ""
85
+ end
86
+ end
87
+
88
+ # Helper to return a string in specific color
89
+ def self.colorize(code, msg)
90
+ "#{self.color(code)}#{msg}#{self.color(:reset)}"
91
+ end
92
+
93
+ # Returns a blob of text representing the results in a standard way
94
+ #
95
+ # It tries hard to do sane things so you often
96
+ # should not need to write your own display functions
97
+ #
98
+ # If the agent you are getting results for has a DDL
99
+ # it will use the hints in there to do the right thing specifically
100
+ # it will look at the values of display in the DDL to choose
101
+ # when to show results
102
+ #
103
+ # If you do not have a DDL you can pass these flags:
104
+ #
105
+ # printrpc exim.mailq, :flatten => true
106
+ # printrpc exim.mailq, :verbose => true
107
+ #
108
+ # If you've asked it to flatten the result it will not print sender
109
+ # hostnames, it will just print the result as if it's one huge result,
110
+ # handy for things like showing a combined mailq.
111
+ def self.rpcresults(result, flags = {})
112
+ flags = {:verbose => false, :flatten => false, :format => :console}.merge(flags)
113
+
114
+ result_text = ""
115
+ ddl = nil
116
+
117
+ # if running in verbose mode, just use the old style print
118
+ # no need for all the DDL helpers obfuscating the result
119
+ if flags[:format] == :json
120
+ if STDOUT.tty?
121
+ result_text = JSON.pretty_generate(result)
122
+ else
123
+ result_text = result.to_json
124
+ end
125
+ else
126
+ if flags[:verbose]
127
+ result_text = old_rpcresults(result, flags)
128
+ else
129
+ [result].flatten.each do |r|
130
+ begin
131
+ ddl ||= DDL.new(r.agent).action_interface(r.action.to_s)
132
+
133
+ sender = r[:sender]
134
+ status = r[:statuscode]
135
+ message = r[:statusmsg]
136
+ display = ddl[:display]
137
+ result = r[:data]
138
+
139
+ # appand the results only according to what the DDL says
140
+ case display
141
+ when :ok
142
+ if status == 0
143
+ result_text << text_for_result(sender, status, message, result, ddl)
144
+ end
145
+
146
+ when :failed
147
+ if status > 0
148
+ result_text << text_for_result(sender, status, message, result, ddl)
149
+ end
150
+
151
+ when :always
152
+ result_text << text_for_result(sender, status, message, result, ddl)
153
+
154
+ when :flatten
155
+ result_text << text_for_flattened_result(status, result)
156
+
157
+ end
158
+ rescue Exception => e
159
+ # no DDL so just do the old style print unchanged for
160
+ # backward compat
161
+ result_text = old_rpcresults(result, flags)
162
+ end
163
+ end
164
+ end
165
+ end
166
+
167
+ result_text
168
+ end
169
+
170
+ # Return text representing a result
171
+ def self.text_for_result(sender, status, msg, result, ddl)
172
+ statusses = ["",
173
+ colorize(:red, "Request Aborted"),
174
+ colorize(:yellow, "Unknown Action"),
175
+ colorize(:yellow, "Missing Request Data"),
176
+ colorize(:yellow, "Invalid Request Data"),
177
+ colorize(:red, "Unknown Request Status")]
178
+
179
+ result_text = "%-40s %s\n" % [sender, statusses[status]]
180
+ result_text << " %s\n" % [colorize(:yellow, msg)] unless msg == "OK"
181
+
182
+ # only print good data, ignore data that results from failure
183
+ if [0, 1].include?(status)
184
+ if result.is_a?(Hash)
185
+ # figure out the lengths of the display as strings, we'll use
186
+ # it later to correctly justify the output
187
+ lengths = result.keys.map do |k|
188
+ begin
189
+ ddl[:output][k][:display_as].size
190
+ rescue
191
+ k.to_s.size
192
+ end
193
+ end
194
+
195
+ result.keys.each do |k|
196
+ # get all the output fields nicely lined up with a
197
+ # 3 space front padding
198
+ begin
199
+ display_as = ddl[:output][k][:display_as]
200
+ rescue
201
+ display_as = k.to_s
202
+ end
203
+
204
+ display_length = display_as.size
205
+ padding = lengths.max - display_length + 3
206
+ result_text << " " * padding
207
+
208
+ result_text << "#{display_as}:"
209
+
210
+ if [String, Numeric].include?(result[k].class)
211
+ lines = result[k].to_s.split("\n")
212
+
213
+ if lines.empty?
214
+ result_text << "\n"
215
+ else
216
+ lines.each_with_index do |line, i|
217
+ i == 0 ? padtxt = " " : padtxt = " " * (padding + display_length + 2)
218
+
219
+ result_text << "#{padtxt}#{line}\n"
220
+ end
221
+ end
222
+ else
223
+ padding = " " * (lengths.max + 5)
224
+ result_text << " " << result[k].pretty_inspect.split("\n").join("\n" << padding) << "\n"
225
+ end
226
+ end
227
+ else
228
+ result_text << "\n\t" + result.pretty_inspect.split("\n").join("\n\t")
229
+ end
230
+ end
231
+
232
+ result_text << "\n"
233
+ result_text
234
+ end
235
+
236
+ # Returns text representing a flattened result of only good data
237
+ def self.text_for_flattened_result(status, result)
238
+ result_text = ""
239
+
240
+ if status <= 1
241
+ unless result.is_a?(String)
242
+ result_text << result.pretty_inspect
243
+ else
244
+ result_text << result
245
+ end
246
+ end
247
+ end
248
+
249
+ # Backward compatible display block for results without a DDL
250
+ def self.old_rpcresults(result, flags = {})
251
+ result_text = ""
252
+
253
+ if flags[:flatten]
254
+ result.each do |r|
255
+ if r[:statuscode] <= 1
256
+ data = r[:data]
257
+
258
+ unless data.is_a?(String)
259
+ result_text << data.pretty_inspect
260
+ else
261
+ result_text << data
262
+ end
263
+ else
264
+ result_text << r.pretty_inspect
265
+ end
266
+ end
267
+
268
+ result_text << ""
269
+ else
270
+ [result].flatten.each do |r|
271
+
272
+ if flags[:verbose]
273
+ result_text << "%-40s: %s\n" % [r[:sender], r[:statusmsg]]
274
+
275
+ if r[:statuscode] <= 1
276
+ r[:data].pretty_inspect.split("\n").each {|m| result_text += " #{m}"}
277
+ result_text << "\n\n"
278
+ elsif r[:statuscode] == 2
279
+ # dont print anything, no useful data to display
280
+ # past what was already shown
281
+ elsif r[:statuscode] == 3
282
+ # dont print anything, no useful data to display
283
+ # past what was already shown
284
+ elsif r[:statuscode] == 4
285
+ # dont print anything, no useful data to display
286
+ # past what was already shown
287
+ else
288
+ result_text << " #{r[:statusmsg]}"
289
+ end
290
+ else
291
+ unless r[:statuscode] == 0
292
+ result_text << "%-40s %s\n" % [r[:sender], colorize(:red, r[:statusmsg])]
293
+ end
294
+ end
295
+ end
296
+ end
297
+
298
+ result_text << ""
299
+ end
300
+
301
+ # Add SimpleRPC common options
302
+ def self.add_simplerpc_options(parser, options)
303
+ parser.separator ""
304
+
305
+ # add SimpleRPC specific options to all clients that use our library
306
+ parser.on('--np', '--no-progress', 'Do not show the progress bar') do |v|
307
+ options[:progress_bar] = false
308
+ end
309
+
310
+ parser.on('--one', '-1', 'Send request to only one discovered nodes') do |v|
311
+ options[:mcollective_limit_targets] = 1
312
+ end
313
+
314
+ parser.on('--batch SIZE', Integer, 'Do requests in batches') do |v|
315
+ options[:batch_size] = v
316
+ end
317
+
318
+ parser.on('--batch-sleep SECONDS', Float, 'Sleep time between batches') do |v|
319
+ options[:batch_sleep_time] = v
320
+ end
321
+
322
+ parser.on('--limit-nodes COUNT', '--ln', 'Send request to only a subset of nodes, can be a percentage') do |v|
323
+ raise "Invalid limit specified: #{v} valid limits are /^\d+%*$/" unless v =~ /^\d+%*$/
324
+
325
+ if v =~ /^\d+$/
326
+ options[:mcollective_limit_targets] = v.to_i
327
+ else
328
+ options[:mcollective_limit_targets] = v
329
+ end
330
+ end
331
+
332
+ parser.on('--json', '-j', 'Produce JSON output') do |v|
333
+ options[:progress_bar] = false
334
+ options[:output_format] = :json
335
+ end
336
+ end
337
+ end
338
+ end
339
+ end