3be93f56456b59502132399f2947e33a8355923a
[packages/precise/mcollective.git] / lib / mcollective / applications.rb
1 module MCollective
2   class Applications
3     def self.[](appname)
4       load_application(appname)
5       PluginManager["#{appname}_application"]
6     end
7
8     def self.run(appname)
9       load_config
10
11       begin
12         load_application(appname)
13       rescue Exception => e
14         e.backtrace.first << Util.colorize(:red, "  <----")
15         STDERR.puts "Application '#{appname}' failed to load:"
16         STDERR.puts
17         STDERR.puts Util.colorize(:red, "   #{e} (#{e.class})")
18         STDERR.puts
19         STDERR.puts "       %s" % [e.backtrace.join("\n       ")]
20         exit 1
21       end
22
23       PluginManager["#{appname}_application"].run
24     end
25
26     def self.load_application(appname)
27       return if PluginManager.include?("#{appname}_application")
28
29       load_config
30
31       PluginManager.loadclass "MCollective::Application::#{appname.capitalize}"
32       PluginManager << {:type => "#{appname}_application", :class => "MCollective::Application::#{appname.capitalize}"}
33     end
34
35     # Returns an array of applications found in the lib dirs
36     def self.list
37       load_config
38
39       PluginManager.find("application")
40     rescue SystemExit
41       exit 1
42     rescue Exception => e
43       STDERR.puts "Failed to generate application list: #{e.class}: #{e}"
44       exit 1
45     end
46
47     # Filters a string of opts out using Shellwords
48     # keeping only things related to --config and -c
49     def self.filter_extra_options(opts)
50       res = ""
51       words = Shellwords.shellwords(opts)
52       words.each_with_index do |word,idx|
53         if word == "-c"
54           return "--config=#{words[idx + 1]}"
55         elsif word == "--config"
56           return "--config=#{words[idx + 1]}"
57         elsif word =~ /\-c=/
58           return word
59         elsif word =~ /\-\-config=/
60           return word
61         end
62       end
63
64       return ""
65     end
66
67     # We need to know the config file in order to know the libdir
68     # so that we can find applications.
69     #
70     # The problem is the CLI might be stuffed with options only the
71     # app in the libdir might understand so we have a chicken and
72     # egg situation.
73     #
74     # We're parsing and filtering MCOLLECTIVE_EXTRA_OPTS removing
75     # all but config related options and parsing the options looking
76     # just for the config file.
77     #
78     # We're handling failures gracefully and finally restoring the
79     # ARG and MCOLLECTIVE_EXTRA_OPTS to the state they were before
80     # we started parsing.
81     #
82     # This is mostly a hack, when we're redoing how config works
83     # this stuff should be made less sucky
84     def self.load_config
85       return if Config.instance.configured
86
87       original_argv = ARGV.clone
88       original_extra_opts = ENV["MCOLLECTIVE_EXTRA_OPTS"].clone rescue nil
89       configfile = nil
90
91       parser = OptionParser.new
92       parser.on("--config CONFIG", "-c", "Config file") do |f|
93         configfile = f
94       end
95
96       parser.program_name = $0
97
98       parser.on("--help")
99
100       # avoid option parsers own internal version handling that sux
101       parser.on("-v", "--verbose")
102
103       if original_extra_opts
104         begin
105           # optparse will parse the whole ENV in one go and refuse
106           # to play along with the retry trick I do below so in
107           # order to handle unknown options properly I parse out
108           # only -c and --config deleting everything else and
109           # then restore the environment variable later when I
110           # am done with it
111           ENV["MCOLLECTIVE_EXTRA_OPTS"] = filter_extra_options(ENV["MCOLLECTIVE_EXTRA_OPTS"].clone)
112           parser.environment("MCOLLECTIVE_EXTRA_OPTS")
113         rescue Exception => e
114           Log.error("Failed to parse MCOLLECTIVE_EXTRA_OPTS: #{e}")
115         end
116
117         ENV["MCOLLECTIVE_EXTRA_OPTS"] = original_extra_opts.clone
118       end
119
120       begin
121         parser.parse!
122       rescue OptionParser::InvalidOption => e
123         retry
124       end
125
126       ARGV.clear
127       original_argv.each {|a| ARGV << a}
128
129       configfile = Util.config_file_for_user unless configfile
130
131       Config.instance.loadconfig(configfile)
132     end
133   end
134 end