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



247
248
249
# File 'lib/mcollective/application.rb', line 247

def application_cli_arguments
  application_options[:cli_arguments]
end

#application_descriptionObject

Retrieve the current application description



234
235
236
# File 'lib/mcollective/application.rb', line 234

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



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

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



229
230
231
# File 'lib/mcollective/application.rb', line 229

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
204
205
# 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.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



239
240
241
242
243
# File 'lib/mcollective/application.rb', line 239

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



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

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



334
335
336
# File 'lib/mcollective/application.rb', line 334

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

#halt_code(stats) ⇒ Object



311
312
313
314
315
316
317
318
319
320
321
322
323
# File 'lib/mcollective/application.rb', line 311

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

  return 4 if request_stats[:discoverytime] == 0 && request_stats[:responses] == 0
  return 3 if request_stats[:discovered] > 0 && request_stats[:responses] == 0
  return 2 if request_stats[:discovered] > 0 && request_stats[:failcount] > 0
  return 1 if request_stats[:discovered] == 0
  return 0 if request_stats[:discoverytime] == 0 && request_stats[:discovered] == request_stats[:okcount]
  return 0 if request_stats[:discovered] == request_stats[:okcount]
end

#helpObject



277
278
279
# File 'lib/mcollective/application.rb', line 277

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.



306
307
308
309
# File 'lib/mcollective/application.rb', line 306

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



341
342
343
344
345
346
# File 'lib/mcollective/application.rb', line 341

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



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

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



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

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