Class: MCollective::Application

Inherits:
Object
  • Object
show all
Includes:
RPC
Defined in:
lib/mcollective/application.rb

Direct Known Subclasses

Completion, Describe_filter, Facts, Find, Help, Inventory, Ping, Plugin, Rpc

Defined Under Namespace

Classes: Completion, Describe_filter, Facts, Find, Help, Inventory, Ping, Plugin, Rpc

Class Method Summary collapse

Instance Method Summary collapse

Methods included from RPC

const_missing, discovered, #empty_filter?, #printrpc, #printrpcstats, #rpcoptions, stats

Class Method Details

.[](option) ⇒ Object

retrieves a specific option



22
23
24
25
# File 'lib/mcollective/application.rb', line 22

def [](option)
  intialize_application_options unless @application_options
  @application_options[option]
end

.[]=(option, value) ⇒ Object

set an option in the options hash



16
17
18
19
# File 'lib/mcollective/application.rb', line 16

def []=(option, value)
  intialize_application_options unless @application_options
  @application_options[option] = value
end

.application_optionsObject

Intialize a blank set of options if its the first time used else returns active options



10
11
12
13
# File 'lib/mcollective/application.rb', line 10

def application_options
  intialize_application_options unless @application_options
  @application_options
end

.description(descr) ⇒ Object

Sets the application description, there can be only one description per application so multiple calls will just change the description



30
31
32
# File 'lib/mcollective/application.rb', line 30

def description(descr)
  self[:description] = descr
end

.exclude_argument_sections(*sections) ⇒ Object



40
41
42
43
44
45
46
47
48
49
# File 'lib/mcollective/application.rb', line 40

def exclude_argument_sections(*sections)
  sections = [sections].flatten

  sections.each do |s|
    raise "Unknown CLI argument section #{s}" unless ["rpc", "common", "filter"].include?(s)
  end

  intialize_application_options unless @application_options
  self[:exclude_arg_sections] = sections
end

.intialize_application_optionsObject

Creates an empty set of options



81
82
83
84
85
86
# File 'lib/mcollective/application.rb', line 81

def intialize_application_options
  @application_options = {:description          => nil,
                          :usage                => [],
                          :cli_arguments        => [],
                          :exclude_arg_sections => []}
end

.option(name, arguments) ⇒ Object

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



67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/mcollective/application.rb', line 67

def option(name, arguments)
  opt = {:name => name,
         :description => nil,
         :arguments => [],
         :type => String,
         :required => false,
         :validate => Proc.new { true }}

  arguments.each_pair{|k,v| opt[k] = v}

  self[:cli_arguments] << opt
end

.usage(usage) ⇒ Object

Supplies usage information, calling multiple times will create multiple usage lines in –help output



36
37
38
# File 'lib/mcollective/application.rb', line 36

def usage(usage)
  self[:usage] << usage
end

Instance Method Details

#application_cli_argumentsObject

Returns an array of all the arguments built using calls to optin



249
250
251
# File 'lib/mcollective/application.rb', line 249

def application_cli_arguments
  application_options[:cli_arguments]
end

#application_descriptionObject

Retrieve the current application description



236
237
238
# File 'lib/mcollective/application.rb', line 236

def application_description
  application_options[:description]
end

#application_failure(e, err_dest = STDERR) ⇒ Object

Handles failure, if we’re far enough in the initialization phase it will log backtraces if its in verbose mode only



255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
# File 'lib/mcollective/application.rb', line 255

def application_failure(e, err_dest=STDERR)
  # peole can use exit() anywhere and not get nasty backtraces as a result
  if e.is_a?(SystemExit)
    disconnect
    raise(e)
  end

  if options[:verbose]
    err_dest.puts "\nThe %s application failed to run: %s\n" % [ Util.colorize(:bold, $0), Util.colorize(:red, e.to_s)]
  else
    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)]
  end

  if options.nil? || options[:verbose]
    e.backtrace.first << Util.colorize(:red, "  <----")
    err_dest.puts "\n%s %s" % [ Util.colorize(:red, e.to_s), Util.colorize(:bold, "(#{e.class.to_s})")]
    e.backtrace.each{|l| err_dest.puts "\tfrom #{l}"}
  end

  disconnect

  exit 1
end

#application_optionsObject

Retrieves the full hash of application options



231
232
233
# File 'lib/mcollective/application.rb', line 231

def application_options
  self.class.application_options
end

#application_parse_options(help = false) ⇒ Object

Builds an ObjectParser config, parse the CLI options and validates based on the option config



137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
# File 'lib/mcollective/application.rb', line 137

def application_parse_options(help=false)
  @options ||= {:verbose => false}

  @options = clioptions(help) do |parser, options|
    parser.define_head application_description if application_description
    parser.banner = ""

    if application_usage
      parser.separator ""

      application_usage.each do |u|
        parser.separator "Usage: #{u}"
      end

      parser.separator ""
    end

    parser.separator "Application Options" unless application_cli_arguments.empty?

    parser.define_tail ""
    parser.define_tail "The Marionette Collective #{MCollective.version}"


    application_cli_arguments.each do |carg|
      opts_array = []

      opts_array << :on

      # if a default is set from the application set it up front
      if carg.include?(:default)
        configuration[carg[:name]] = carg[:default]
      end

      # :arguments are multiple possible ones
      if carg[:arguments].is_a?(Array)
        carg[:arguments].each {|a| opts_array << a}
      else
        opts_array << carg[:arguments]
      end

      # type was given and its not one of our special types, just pass it onto optparse
      opts_array << carg[:type] if carg[:type] && ![:boolean, :bool, :array].include?(carg[:type])

      opts_array << carg[:description]

      # Handle our special types else just rely on the optparser to handle the types
      if [:bool, :boolean].include?(carg[:type])
        parser.send(*opts_array) do |v|
          validate_option(carg[:validate], carg[:name], v)

          configuration[carg[:name]] = v
        end

      elsif carg[:type] == :array
        parser.send(*opts_array) do |v|
          validate_option(carg[:validate], carg[:name], v)

          configuration[carg[:name]] = [] unless configuration.include?(carg[:name])
          configuration[carg[:name]] << v
        end

      else
        parser.send(*opts_array) do |v|
          validate_option(carg[:validate], carg[:name], v)

          configuration[carg[:name]] = v
        end
      end
    end
  end
end

#application_usageObject

Return the current usage text false if nothing is set



241
242
243
244
245
# File 'lib/mcollective/application.rb', line 241

def application_usage
  usage = application_options[:usage]

  usage.empty? ? false : usage
end

#clioptions(help) ⇒ Object

Creates a standard options hash, pass in a block to add extra headings etc see Optionparser



113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# File 'lib/mcollective/application.rb', line 113

def clioptions(help)
  oparser = Optionparser.new({:verbose => false, :progress_bar => true}, "filter", application_options[:exclude_arg_sections])

  options = oparser.parse do |parser, options|
    if block_given?
      yield(parser, options)
    end

    RPC::Helpers.add_simplerpc_options(parser, options) unless application_options[:exclude_arg_sections].include?("rpc")
  end

  return oparser.parser.help if help

  validate_cli_options

  post_option_parser(configuration) if respond_to?(:post_option_parser)

  return options
rescue Exception => e
  application_failure(e)
end

#configurationObject

The application configuration built from CLI arguments



90
91
92
93
# File 'lib/mcollective/application.rb', line 90

def configuration
  @application_configuration ||= {}
  @application_configuration
end

#disconnectObject



301
302
303
304
# File 'lib/mcollective/application.rb', line 301

def disconnect
  MCollective::PluginManager["connector_plugin"].disconnect
rescue
end

#halt(stats) ⇒ Object

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



351
352
353
# File 'lib/mcollective/application.rb', line 351

def halt(stats)
  exit(halt_code(stats))
end

#halt_code(stats) ⇒ Object



313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
# File 'lib/mcollective/application.rb', line 313

def halt_code(stats)
  request_stats = {:discoverytime => 0,
                   :discovered => 0,
                   :okcount => 0,
                   :failcount => 0}.merge(stats.to_hash)

  if (request_stats[:discoverytime] == 0 && request_stats[:responses] == 0)
    return 4
  end

  if (request_stats[:discovered] > 0)
    if (request_stats[:responses] == 0)
      return 3
    elsif (request_stats[:failcount] > 0)
      return 2
    end
  end

  if (request_stats[:discovered] == 0)
    if (request_stats[:responses] && request_stats[:responses] > 0)
      return 0
    else
      return 1
    end
  end

  return 0
end

#helpObject



279
280
281
# File 'lib/mcollective/application.rb', line 279

def help
  application_parse_options(true)
end

#mainObject

Fake abstract class that logs if the user tries to use an application without supplying a main override method.



308
309
310
311
# File 'lib/mcollective/application.rb', line 308

def main
  STDERR.puts "Applications need to supply a 'main' method"
  exit 1
end

#optionsObject

The active options hash used for MC::Client and other configuration



96
97
98
# File 'lib/mcollective/application.rb', line 96

def options
  @options
end

#rpcclient(agent, flags = {}) ⇒ Object

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



358
359
360
361
362
363
# File 'lib/mcollective/application.rb', line 358

def rpcclient(agent, flags = {})
  flags[:options] = options unless flags.include?(:options)
  flags[:exit_on_failure] = false

  super
end

#runObject

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



286
287
288
289
290
291
292
293
294
295
296
297
298
299
# File 'lib/mcollective/application.rb', line 286

def run
  application_parse_options

  validate_configuration(configuration) if respond_to?(:validate_configuration)

  Util.setup_windows_sleeper if Util.windows?

  main

  disconnect

rescue Exception => e
  application_failure(e)
end

#validate_cli_optionsObject



209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
# File 'lib/mcollective/application.rb', line 209

def validate_cli_options
  # Check all required parameters were set
  validation_passed = true
  application_cli_arguments.each do |carg|
    # Check for required arguments
    if carg[:required]
      unless configuration[ carg[:name] ]
        validation_passed = false
        STDERR.puts "The #{carg[:name]} option is mandatory"
      end
    end
  end

  unless validation_passed
    STDERR.puts "\nPlease run with --help for detailed help"
    exit 1
  end


end

#validate_option(blk, name, value) ⇒ Object

Calls the supplied block in an option for validation, an error raised will log to STDERR and exit the application



102
103
104
105
106
107
108
109
# File 'lib/mcollective/application.rb', line 102

def validate_option(blk, name, value)
  validation_result = blk.call(value)

  unless validation_result == true
    STDERR.puts "Validation of #{name} failed: #{validation_result}"
    exit 1
  end
end