Class | MCollective::Application |
In: |
lib/mcollective/application.rb
|
Parent: | Object |
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, line 28 28: def description(descr) 29: self[:description] = descr 30: end
# File lib/mcollective/application.rb, line 38 38: def exclude_argument_sections(*sections) 39: sections = [sections].flatten 40: 41: sections.each do |s| 42: raise "Unknown CLI argument section #{s}" unless ["rpc", "common", "filter"].include?(s) 43: end 44: 45: intialize_application_options unless @application_options 46: self[:exclude_arg_sections] = sections 47: 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[:foo]
# File lib/mcollective/application.rb, line 65 65: def option(name, arguments) 66: opt = {:name => name, 67: :description => nil, 68: :arguments => [], 69: :type => String, 70: :required => false, 71: :validate => Proc.new { true }} 72: 73: arguments.each_pair{|k,v| opt[k] = v} 74: 75: self[:cli_arguments] << opt 76: end
Returns an array of all the arguments built using calls to optin
# File lib/mcollective/application.rb, line 247 247: def application_cli_arguments 248: application_options[:cli_arguments] 249: end
Retrieve the current application description
# File lib/mcollective/application.rb, line 234 234: def application_description 235: application_options[:description] 236: 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, line 253 253: def application_failure(e, err_dest=STDERR) 254: # peole can use exit() anywhere and not get nasty backtraces as a result 255: if e.is_a?(SystemExit) 256: disconnect 257: raise(e) 258: end 259: 260: if e.is_a?(CodedError) 261: err_dest.puts "\nThe %s application failed to run: %s: %s\n" % [ Util.colorize(:bold, $0), Util.colorize(:bold, e.code), Util.colorize(:red, e.to_s)] 262: 263: err_dest.puts 264: if options[:verbose] 265: err_dest.puts "Use the 'mco doc %s' command for details about this error" % e.code 266: else 267: err_dest.puts "Use the 'mco doc %s' command for details about this error, use -v for full error backtrace details" % e.code 268: end 269: else 270: if options[:verbose] 271: err_dest.puts "\nThe %s application failed to run: %s\n" % [ Util.colorize(:bold, $0), Util.colorize(:red, e.to_s)] 272: else 273: 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)] 274: end 275: end 276: 277: if options.nil? || options[:verbose] 278: e.backtrace.first << Util.colorize(:red, " <----") 279: err_dest.puts "\n%s %s" % [ Util.colorize(:red, e.to_s), Util.colorize(:bold, "(#{e.class.to_s})")] 280: e.backtrace.each{|l| err_dest.puts "\tfrom #{l}"} 281: end 282: 283: disconnect 284: 285: exit 1 286: end
Builds an ObjectParser config, parse the CLI options and validates based on the option config
# File lib/mcollective/application.rb, line 135 135: def application_parse_options(help=false) 136: @options ||= {:verbose => false} 137: 138: @options = clioptions(help) do |parser, options| 139: parser.define_head application_description if application_description 140: parser.banner = "" 141: 142: if application_usage 143: parser.separator "" 144: 145: application_usage.each do |u| 146: parser.separator "Usage: #{u}" 147: end 148: 149: parser.separator "" 150: end 151: 152: parser.separator "Application Options" unless application_cli_arguments.empty? 153: 154: parser.define_tail "" 155: parser.define_tail "The Marionette Collective #{MCollective.version}" 156: 157: 158: application_cli_arguments.each do |carg| 159: opts_array = [] 160: 161: opts_array << :on 162: 163: # if a default is set from the application set it up front 164: if carg.include?(:default) 165: configuration[carg[:name]] = carg[:default] 166: end 167: 168: # :arguments are multiple possible ones 169: if carg[:arguments].is_a?(Array) 170: carg[:arguments].each {|a| opts_array << a} 171: else 172: opts_array << carg[:arguments] 173: end 174: 175: # type was given and its not one of our special types, just pass it onto optparse 176: opts_array << carg[:type] if carg[:type] && ![:boolean, :bool, :array].include?(carg[:type]) 177: 178: opts_array << carg[:description] 179: 180: # Handle our special types else just rely on the optparser to handle the types 181: if [:bool, :boolean].include?(carg[:type]) 182: parser.send(*opts_array) do |v| 183: validate_option(carg[:validate], carg[:name], v) 184: 185: configuration[carg[:name]] = v 186: end 187: 188: elsif carg[:type] == :array 189: parser.send(*opts_array) do |v| 190: validate_option(carg[:validate], carg[:name], v) 191: 192: configuration[carg[:name]] = [] unless configuration.include?(carg[:name]) 193: configuration[carg[:name]] << v 194: end 195: 196: else 197: parser.send(*opts_array) do |v| 198: validate_option(carg[:validate], carg[:name], v) 199: 200: configuration[carg[:name]] = v 201: end 202: end 203: end 204: end 205: end
Creates a standard options hash, pass in a block to add extra headings etc see Optionparser
# File lib/mcollective/application.rb, line 111 111: def clioptions(help) 112: oparser = Optionparser.new({:verbose => false, :progress_bar => true}, "filter", application_options[:exclude_arg_sections]) 113: 114: options = oparser.parse do |parser, options| 115: if block_given? 116: yield(parser, options) 117: end 118: 119: RPC::Helpers.add_simplerpc_options(parser, options) unless application_options[:exclude_arg_sections].include?("rpc") 120: end 121: 122: return oparser.parser.help if help 123: 124: validate_cli_options 125: 126: post_option_parser(configuration) if respond_to?(:post_option_parser) 127: 128: return options 129: rescue Exception => e 130: application_failure(e) 131: end
The application configuration built from CLI arguments
# File lib/mcollective/application.rb, line 88 88: def configuration 89: @application_configuration ||= {} 90: @application_configuration 91: end
# File lib/mcollective/application.rb, line 310 310: def disconnect 311: MCollective::PluginManager["connector_plugin"].disconnect 312: rescue 313: 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, line 345 345: def halt(stats) 346: exit(halt_code(stats)) 347: end
# File lib/mcollective/application.rb, line 322 322: def halt_code(stats) 323: request_stats = {:discoverytime => 0, 324: :discovered => 0, 325: :okcount => 0, 326: :failcount => 0}.merge(stats.to_hash) 327: 328: return 4 if request_stats[:discoverytime] == 0 && request_stats[:responses] == 0 329: return 3 if request_stats[:discovered] > 0 && request_stats[:responses] == 0 330: return 2 if request_stats[:discovered] > 0 && request_stats[:failcount] > 0 331: return 1 if request_stats[:discovered] == 0 332: return 0 if request_stats[:discoverytime] == 0 && request_stats[:discovered] == request_stats[:okcount] 333: return 0 if request_stats[:discovered] == request_stats[:okcount] 334: end
# File lib/mcollective/application.rb, line 288 288: def help 289: application_parse_options(true) 290: end
The active options hash used for MC::Client and other configuration
# File lib/mcollective/application.rb, line 94 94: def options 95: @options 96: 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
# File lib/mcollective/application.rb, line 352 352: def rpcclient(agent, flags = {}) 353: flags[:options] = options unless flags.include?(:options) 354: flags[:exit_on_failure] = false 355: 356: super 357: 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, line 295 295: def run 296: application_parse_options 297: 298: validate_configuration(configuration) if respond_to?(:validate_configuration) 299: 300: Util.setup_windows_sleeper if Util.windows? 301: 302: main 303: 304: disconnect 305: 306: rescue Exception => e 307: application_failure(e) 308: end
# File lib/mcollective/application.rb, line 207 207: def validate_cli_options 208: # Check all required parameters were set 209: validation_passed = true 210: application_cli_arguments.each do |carg| 211: # Check for required arguments 212: if carg[:required] 213: unless configuration[ carg[:name] ] 214: validation_passed = false 215: STDERR.puts "The #{carg[:name]} option is mandatory" 216: end 217: end 218: end 219: 220: unless validation_passed 221: STDERR.puts "\nPlease run with --help for detailed help" 222: exit 1 223: end 224: 225: 226: 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, line 100 100: def validate_option(blk, name, value) 101: validation_result = blk.call(value) 102: 103: unless validation_result == true 104: STDERR.puts "Validation of #{name} failed: #{validation_result}" 105: exit 1 106: end 107: end