Class: MCollective::Application

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

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



20
21
22
23
# File 'lib/mcollective/application.rb', line 20

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

.[]=(option, value) ⇒ Object

set an option in the options hash



14
15
16
17
# File 'lib/mcollective/application.rb', line 14

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



8
9
10
11
# File 'lib/mcollective/application.rb', line 8

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



28
29
30
# File 'lib/mcollective/application.rb', line 28

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

.exclude_argument_sections(*sections) ⇒ Object



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

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



79
80
81
82
83
84
# File 'lib/mcollective/application.rb', line 79

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



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

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



34
35
36
# File 'lib/mcollective/application.rb', line 34

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



245
246
247
# File 'lib/mcollective/application.rb', line 245

def application_cli_arguments
  application_options[:cli_arguments]
end

#application_descriptionObject

Retrieve the current application description



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

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



251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
# File 'lib/mcollective/application.rb', line 251

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

  err_dest.puts "\nThe %s application failed to run, use -v for full error details: %s\n" % [ Util.colorize(:bold, $0), Util.colorize(:red, e.to_s)]

  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



227
228
229
# File 'lib/mcollective/application.rb', line 227

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



135
136
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
# File 'lib/mcollective/application.rb', line 135

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.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]] = true
        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



237
238
239
240
241
# File 'lib/mcollective/application.rb', line 237

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



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

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



88
89
90
91
# File 'lib/mcollective/application.rb', line 88

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

#disconnectObject



293
294
295
296
# File 'lib/mcollective/application.rb', line 293

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 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 not responses received Exit with 4 if no discovery were done and no responses were received



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
341
342
343
344
345
# File 'lib/mcollective/application.rb', line 314

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

  # was discovery done?
  if request_stats[:discoverytime] != 0
    # was any nodes discovered
    if request_stats[:discovered] == 0
      exit 1

    # nodes were discovered, did we get responses
    elsif request_stats[:responses] == 0
      exit 3

    else
      # we got responses and discovery was done, no failures
      if request_stats[:failcount] == 0
        exit 0
      else
        exit 2
      end
    end
  else
    # discovery wasnt done and we got no responses
    if request_stats[:responses] == 0
      exit 4
    else
      exit 0
    end
  end
end

#helpObject



271
272
273
# File 'lib/mcollective/application.rb', line 271

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.



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

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



94
95
96
# File 'lib/mcollective/application.rb', line 94

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



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

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



278
279
280
281
282
283
284
285
286
287
288
289
290
291
# File 'lib/mcollective/application.rb', line 278

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



205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
# File 'lib/mcollective/application.rb', line 205

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



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

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