class MCollective::Application
Public Class Methods
retrieves a specific option
# File lib/mcollective/application.rb 22 def [](option) 23 intialize_application_options unless @application_options 24 @application_options[option] 25 end
set an option in the options hash
# File lib/mcollective/application.rb 16 def []=(option, value) 17 intialize_application_options unless @application_options 18 @application_options[option] = value 19 end
Intialize a blank set of options if its the first time used else returns active options
# File lib/mcollective/application.rb 10 def application_options 11 intialize_application_options unless @application_options 12 @application_options 13 end
Sets the application description, there can be only one description per application so multiple calls will just change the description
# File lib/mcollective/application.rb 30 def description(descr) 31 self[:description] = descr 32 end
# File lib/mcollective/application.rb 40 def exclude_argument_sections(*sections) 41 sections = [sections].flatten 42 43 sections.each do |s| 44 raise "Unknown CLI argument section #{s}" unless ["rpc", "common", "filter"].include?(s) 45 end 46 47 intialize_application_options unless @application_options 48 self[:exclude_arg_sections] = sections 49 end
Creates an empty set of options
# File lib/mcollective/application.rb 81 def intialize_application_options 82 @application_options = {:description => nil, 83 :usage => [], 84 :cli_arguments => [], 85 :exclude_arg_sections => []} 86 end
Wrapper to create command line options
- name: varaible name that will be used to access the option value - description: textual info shown in --help - arguments: a list of possible arguments that can be used to activate this option - type: a data type that ObjectParser understand of :bool or :array - required: true or false if this option has to be supplied - validate: a proc that will be called with the value used to validate the supplied value option :foo, :description => "The foo option" :arguments => ["--foo ARG"]
after this the value supplied will be in configuration
# File lib/mcollective/application.rb 67 def option(name, arguments) 68 opt = {:name => name, 69 :description => nil, 70 :arguments => [], 71 :type => String, 72 :required => false, 73 :validate => Proc.new { true }} 74 75 arguments.each_pair{|k,v| opt[k] = v} 76 77 self[:cli_arguments] << opt 78 end
Supplies usage information, calling multiple times will create multiple usage lines in –help output
# File lib/mcollective/application.rb 36 def usage(usage) 37 self[:usage] << usage 38 end
Public Instance Methods
Returns an array of all the arguments built using calls to optin
# File lib/mcollective/application.rb 249 def application_cli_arguments 250 application_options[:cli_arguments] 251 end
Retrieve the current application description
# File lib/mcollective/application.rb 236 def application_description 237 application_options[:description] 238 end
Handles failure, if we're far enough in the initialization phase it will log backtraces if its in verbose mode only
# File lib/mcollective/application.rb 255 def application_failure(e, err_dest=STDERR) 256 # peole can use exit() anywhere and not get nasty backtraces as a result 257 if e.is_a?(SystemExit) 258 disconnect 259 raise(e) 260 end 261 262 if options[:verbose] 263 err_dest.puts "\nThe %s application failed to run: %s\n" % [ Util.colorize(:bold, $0), Util.colorize(:red, e.to_s)] 264 else 265 err_dest.puts "\nThe %s application failed to run, use -v for full error backtrace details: %s\n" % [ Util.colorize(:bold, $0), Util.colorize(:red, e.to_s)] 266 end 267 268 if options.nil? || options[:verbose] 269 e.backtrace.first << Util.colorize(:red, " <----") 270 err_dest.puts "\n%s %s" % [ Util.colorize(:red, e.to_s), Util.colorize(:bold, "(#{e.class.to_s})")] 271 e.backtrace.each{|l| err_dest.puts "\tfrom #{l}"} 272 end 273 274 disconnect 275 276 exit 1 277 end
Retrieves the full hash of application options
# File lib/mcollective/application.rb 231 def application_options 232 self.class.application_options 233 end
Builds an ObjectParser config, parse the CLI options and validates based on the option config
# File lib/mcollective/application.rb 137 def application_parse_options(help=false) 138 @options ||= {:verbose => false} 139 140 @options = clioptions(help) do |parser, options| 141 parser.define_head application_description if application_description 142 parser.banner = "" 143 144 if application_usage 145 parser.separator "" 146 147 application_usage.each do |u| 148 parser.separator "Usage: #{u}" 149 end 150 151 parser.separator "" 152 end 153 154 parser.separator "Application Options" unless application_cli_arguments.empty? 155 156 parser.define_tail "" 157 parser.define_tail "The Marionette Collective #{MCollective.version}" 158 159 160 application_cli_arguments.each do |carg| 161 opts_array = [] 162 163 opts_array << :on 164 165 # if a default is set from the application set it up front 166 if carg.include?(:default) 167 configuration[carg[:name]] = carg[:default] 168 end 169 170 # :arguments are multiple possible ones 171 if carg[:arguments].is_a?(Array) 172 carg[:arguments].each {|a| opts_array << a} 173 else 174 opts_array << carg[:arguments] 175 end 176 177 # type was given and its not one of our special types, just pass it onto optparse 178 opts_array << carg[:type] if carg[:type] && ![:boolean, :bool, :array].include?(carg[:type]) 179 180 opts_array << carg[:description] 181 182 # Handle our special types else just rely on the optparser to handle the types 183 if [:bool, :boolean].include?(carg[:type]) 184 parser.send(*opts_array) do |v| 185 validate_option(carg[:validate], carg[:name], v) 186 187 configuration[carg[:name]] = v 188 end 189 190 elsif carg[:type] == :array 191 parser.send(*opts_array) do |v| 192 validate_option(carg[:validate], carg[:name], v) 193 194 configuration[carg[:name]] = [] unless configuration.include?(carg[:name]) 195 configuration[carg[:name]] << v 196 end 197 198 else 199 parser.send(*opts_array) do |v| 200 validate_option(carg[:validate], carg[:name], v) 201 202 configuration[carg[:name]] = v 203 end 204 end 205 end 206 end 207 end
Return the current usage text false if nothing is set
# File lib/mcollective/application.rb 241 def application_usage 242 usage = application_options[:usage] 243 244 usage.empty? ? false : usage 245 end
Creates a standard options hash, pass in a block to add extra headings etc see Optionparser
# File lib/mcollective/application.rb 113 def clioptions(help) 114 oparser = Optionparser.new({:verbose => false, :progress_bar => true}, "filter", application_options[:exclude_arg_sections]) 115 116 options = oparser.parse do |parser, options| 117 if block_given? 118 yield(parser, options) 119 end 120 121 RPC::Helpers.add_simplerpc_options(parser, options) unless application_options[:exclude_arg_sections].include?("rpc") 122 end 123 124 return oparser.parser.help if help 125 126 validate_cli_options 127 128 post_option_parser(configuration) if respond_to?(:post_option_parser) 129 130 return options 131 rescue Exception => e 132 application_failure(e) 133 end
The application configuration built from CLI arguments
# File lib/mcollective/application.rb 90 def configuration 91 @application_configuration ||= {} 92 @application_configuration 93 end
# File lib/mcollective/application.rb 301 def disconnect 302 MCollective::PluginManager["connector_plugin"].disconnect 303 rescue 304 end
A helper that creates a consistent exit code for applications by looking at an instance of MCollective::RPC::Stats
Exit with 0 if nodes were discovered and all passed Exit with 0 if no discovery were done and > 0 responses were received, all ok Exit with 1 if no nodes were discovered Exit with 2 if nodes were discovered but some RPC
requests failed Exit with 3 if nodes were discovered, but no responses received Exit with 4 if no discovery were done and no responses were received
# File lib/mcollective/application.rb 351 def halt(stats) 352 exit(halt_code(stats)) 353 end
# File lib/mcollective/application.rb 313 def halt_code(stats) 314 request_stats = {:discoverytime => 0, 315 :discovered => 0, 316 :okcount => 0, 317 :failcount => 0}.merge(stats.to_hash) 318 319 if (request_stats[:discoverytime] == 0 && request_stats[:responses] == 0) 320 return 4 321 end 322 323 if (request_stats[:discovered] > 0) 324 if (request_stats[:responses] == 0) 325 return 3 326 elsif (request_stats[:failcount] > 0) 327 return 2 328 end 329 end 330 331 if (request_stats[:discovered] == 0) 332 if (request_stats[:responses] && request_stats[:responses] > 0) 333 return 0 334 else 335 return 1 336 end 337 end 338 339 return 0 340 end
# File lib/mcollective/application.rb 279 def help 280 application_parse_options(true) 281 end
Fake abstract class that logs if the user tries to use an application without supplying a main override method.
# File lib/mcollective/application.rb 308 def main 309 STDERR.puts "Applications need to supply a 'main' method" 310 exit 1 311 end
The active options hash used for MC::Client and other configuration
# File lib/mcollective/application.rb 96 def options 97 @options 98 end
Wrapper around MC::RPC#rpcclient that forcably supplies our options hash if someone forgets to pass in options in an application the filters and other cli options wouldnt take effect which could have a disasterous outcome
MCollective::RPC#rpcclient
# File lib/mcollective/application.rb 358 def rpcclient(agent, flags = {}) 359 flags[:options] = options unless flags.include?(:options) 360 flags[:exit_on_failure] = false 361 362 super 363 end
The main logic loop, builds up the options, validate configuration and calls the main as supplied by the user. Disconnects when done and pass any exception onto the application_failure
helper
# File lib/mcollective/application.rb 286 def run 287 application_parse_options 288 289 validate_configuration(configuration) if respond_to?(:validate_configuration) 290 291 Util.setup_windows_sleeper if Util.windows? 292 293 main 294 295 disconnect 296 297 rescue Exception => e 298 application_failure(e) 299 end
# File lib/mcollective/application.rb 209 def validate_cli_options 210 # Check all required parameters were set 211 validation_passed = true 212 application_cli_arguments.each do |carg| 213 # Check for required arguments 214 if carg[:required] 215 unless configuration[ carg[:name] ] 216 validation_passed = false 217 STDERR.puts "The #{carg[:name]} option is mandatory" 218 end 219 end 220 end 221 222 unless validation_passed 223 STDERR.puts "\nPlease run with --help for detailed help" 224 exit 1 225 end 226 227 228 end
Calls the supplied block in an option for validation, an error raised will log to STDERR and exit the application
# File lib/mcollective/application.rb 102 def validate_option(blk, name, value) 103 validation_result = blk.call(value) 104 105 unless validation_result == true 106 STDERR.puts "Validation of #{name} failed: #{validation_result}" 107 exit 1 108 end 109 end