mcollective-client 1.3.3
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of mcollective-client might be problematic. Click here for more details.
- data/bin/mc-call-agent +54 -0
- data/bin/mco +27 -0
- data/lib/mcollective.rb +70 -0
- data/lib/mcollective/agents.rb +160 -0
- data/lib/mcollective/application.rb +354 -0
- data/lib/mcollective/applications.rb +145 -0
- data/lib/mcollective/client.rb +292 -0
- data/lib/mcollective/config.rb +202 -0
- data/lib/mcollective/connector.rb +18 -0
- data/lib/mcollective/connector/base.rb +24 -0
- data/lib/mcollective/facts.rb +39 -0
- data/lib/mcollective/facts/base.rb +86 -0
- data/lib/mcollective/log.rb +103 -0
- data/lib/mcollective/logger.rb +5 -0
- data/lib/mcollective/logger/base.rb +73 -0
- data/lib/mcollective/logger/console_logger.rb +61 -0
- data/lib/mcollective/logger/file_logger.rb +46 -0
- data/lib/mcollective/logger/syslog_logger.rb +53 -0
- data/lib/mcollective/matcher.rb +16 -0
- data/lib/mcollective/matcher/parser.rb +93 -0
- data/lib/mcollective/matcher/scanner.rb +123 -0
- data/lib/mcollective/message.rb +201 -0
- data/lib/mcollective/monkey_patches.rb +104 -0
- data/lib/mcollective/optionparser.rb +164 -0
- data/lib/mcollective/pluginmanager.rb +180 -0
- data/lib/mcollective/pluginpackager.rb +26 -0
- data/lib/mcollective/pluginpackager/agent_definition.rb +79 -0
- data/lib/mcollective/pluginpackager/standard_definition.rb +59 -0
- data/lib/mcollective/registration.rb +16 -0
- data/lib/mcollective/registration/base.rb +75 -0
- data/lib/mcollective/rpc.rb +188 -0
- data/lib/mcollective/rpc/actionrunner.rb +142 -0
- data/lib/mcollective/rpc/agent.rb +441 -0
- data/lib/mcollective/rpc/audit.rb +38 -0
- data/lib/mcollective/rpc/client.rb +793 -0
- data/lib/mcollective/rpc/ddl.rb +258 -0
- data/lib/mcollective/rpc/helpers.rb +339 -0
- data/lib/mcollective/rpc/progress.rb +63 -0
- data/lib/mcollective/rpc/reply.rb +61 -0
- data/lib/mcollective/rpc/request.rb +51 -0
- data/lib/mcollective/rpc/result.rb +41 -0
- data/lib/mcollective/rpc/stats.rb +185 -0
- data/lib/mcollective/runnerstats.rb +90 -0
- data/lib/mcollective/security.rb +26 -0
- data/lib/mcollective/security/base.rb +237 -0
- data/lib/mcollective/shell.rb +87 -0
- data/lib/mcollective/ssl.rb +246 -0
- data/lib/mcollective/unix_daemon.rb +37 -0
- data/lib/mcollective/util.rb +274 -0
- data/lib/mcollective/vendor.rb +41 -0
- data/lib/mcollective/vendor/require_vendored.rb +2 -0
- data/lib/mcollective/windows_daemon.rb +25 -0
- data/spec/Rakefile +16 -0
- data/spec/fixtures/application/test.rb +7 -0
- data/spec/fixtures/test-cert.pem +15 -0
- data/spec/fixtures/test-private.pem +15 -0
- data/spec/fixtures/test-public.pem +6 -0
- data/spec/monkey_patches/instance_variable_defined.rb +7 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +25 -0
- data/spec/unit/agents_spec.rb +280 -0
- data/spec/unit/application_spec.rb +636 -0
- data/spec/unit/applications_spec.rb +155 -0
- data/spec/unit/array.rb +30 -0
- data/spec/unit/config_spec.rb +148 -0
- data/spec/unit/facts/base_spec.rb +118 -0
- data/spec/unit/facts_spec.rb +39 -0
- data/spec/unit/log_spec.rb +71 -0
- data/spec/unit/logger/base_spec.rb +110 -0
- data/spec/unit/logger/syslog_logger_spec.rb +86 -0
- data/spec/unit/matcher/parser_spec.rb +106 -0
- data/spec/unit/matcher/scanner_spec.rb +71 -0
- data/spec/unit/message_spec.rb +401 -0
- data/spec/unit/optionparser_spec.rb +113 -0
- data/spec/unit/pluginmanager_spec.rb +173 -0
- data/spec/unit/pluginpackager/agent_definition_spec.rb +130 -0
- data/spec/unit/pluginpackager/standard_definition_spec.rb +75 -0
- data/spec/unit/plugins/mcollective/connector/activemq_spec.rb +533 -0
- data/spec/unit/plugins/mcollective/connector/stomp/eventlogger_spec.rb +34 -0
- data/spec/unit/plugins/mcollective/connector/stomp_spec.rb +417 -0
- data/spec/unit/plugins/mcollective/packagers/ospackage_spec.rb +229 -0
- data/spec/unit/plugins/mcollective/security/psk_spec.rb +156 -0
- data/spec/unit/registration/base_spec.rb +77 -0
- data/spec/unit/rpc/actionrunner_spec.rb +213 -0
- data/spec/unit/rpc/agent_spec.rb +155 -0
- data/spec/unit/rpc/client_spec.rb +523 -0
- data/spec/unit/rpc/ddl_spec.rb +388 -0
- data/spec/unit/rpc/helpers_spec.rb +55 -0
- data/spec/unit/rpc/reply_spec.rb +143 -0
- data/spec/unit/rpc/request_spec.rb +115 -0
- data/spec/unit/rpc/result_spec.rb +66 -0
- data/spec/unit/rpc/stats_spec.rb +288 -0
- data/spec/unit/runnerstats_spec.rb +40 -0
- data/spec/unit/security/base_spec.rb +279 -0
- data/spec/unit/shell_spec.rb +144 -0
- data/spec/unit/ssl_spec.rb +244 -0
- data/spec/unit/symbol.rb +11 -0
- data/spec/unit/unix_daemon.rb +41 -0
- data/spec/unit/util_spec.rb +342 -0
- data/spec/unit/vendor_spec.rb +34 -0
- data/spec/unit/windows_daemon.rb +43 -0
- data/spec/windows_spec.opts +1 -0
- metadata +242 -0
@@ -0,0 +1,145 @@
|
|
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 << RPC::Helpers.colorize(:red, " <----")
|
15
|
+
STDERR.puts "Application '#{appname}' failed to load:"
|
16
|
+
STDERR.puts
|
17
|
+
STDERR.puts RPC::Helpers.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
|
+
applist = []
|
40
|
+
|
41
|
+
Config.instance.libdir.each do |libdir|
|
42
|
+
applicationdir = "#{libdir}/mcollective/application"
|
43
|
+
next unless File.directory?(applicationdir)
|
44
|
+
|
45
|
+
Dir.entries(applicationdir).grep(/\.rb$/).each do |application|
|
46
|
+
applist << File.basename(application, ".rb")
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
applist
|
51
|
+
rescue SystemExit
|
52
|
+
exit 1
|
53
|
+
rescue Exception => e
|
54
|
+
STDERR.puts "Failed to generate application list: #{e.class}: #{e}"
|
55
|
+
exit 1
|
56
|
+
end
|
57
|
+
|
58
|
+
# Filters a string of opts out using Shellwords
|
59
|
+
# keeping only things related to --config and -c
|
60
|
+
def self.filter_extra_options(opts)
|
61
|
+
res = ""
|
62
|
+
words = Shellwords.shellwords(opts)
|
63
|
+
words.each_with_index do |word,idx|
|
64
|
+
if word == "-c"
|
65
|
+
return "--config=#{words[idx + 1]}"
|
66
|
+
elsif word == "--config"
|
67
|
+
return "--config=#{words[idx + 1]}"
|
68
|
+
elsif word =~ /\-c=/
|
69
|
+
return word
|
70
|
+
elsif word =~ /\-\-config=/
|
71
|
+
return word
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
return ""
|
76
|
+
end
|
77
|
+
|
78
|
+
# We need to know the config file in order to know the libdir
|
79
|
+
# so that we can find applications.
|
80
|
+
#
|
81
|
+
# The problem is the CLI might be stuffed with options only the
|
82
|
+
# app in the libdir might understand so we have a chicken and
|
83
|
+
# egg situation.
|
84
|
+
#
|
85
|
+
# We're parsing and filtering MCOLLECTIVE_EXTRA_OPTS removing
|
86
|
+
# all but config related options and parsing the options looking
|
87
|
+
# just for the config file.
|
88
|
+
#
|
89
|
+
# We're handling failures gracefully and finally restoring the
|
90
|
+
# ARG and MCOLLECTIVE_EXTRA_OPTS to the state they were before
|
91
|
+
# we started parsing.
|
92
|
+
#
|
93
|
+
# This is mostly a hack, when we're redoing how config works
|
94
|
+
# this stuff should be made less sucky
|
95
|
+
def self.load_config
|
96
|
+
return if Config.instance.configured
|
97
|
+
|
98
|
+
original_argv = ARGV.clone
|
99
|
+
original_extra_opts = ENV["MCOLLECTIVE_EXTRA_OPTS"].clone rescue nil
|
100
|
+
configfile = nil
|
101
|
+
|
102
|
+
parser = OptionParser.new
|
103
|
+
parser.on("--config CONFIG", "-c", "Config file") do |f|
|
104
|
+
configfile = f
|
105
|
+
end
|
106
|
+
|
107
|
+
parser.program_name = $0
|
108
|
+
|
109
|
+
parser.on("--help")
|
110
|
+
|
111
|
+
# avoid option parsers own internal version handling that sux
|
112
|
+
parser.on("-v", "--verbose")
|
113
|
+
|
114
|
+
if original_extra_opts
|
115
|
+
begin
|
116
|
+
# optparse will parse the whole ENV in one go and refuse
|
117
|
+
# to play along with the retry trick I do below so in
|
118
|
+
# order to handle unknown options properly I parse out
|
119
|
+
# only -c and --config deleting everything else and
|
120
|
+
# then restore the environment variable later when I
|
121
|
+
# am done with it
|
122
|
+
ENV["MCOLLECTIVE_EXTRA_OPTS"] = filter_extra_options(ENV["MCOLLECTIVE_EXTRA_OPTS"].clone)
|
123
|
+
parser.environment("MCOLLECTIVE_EXTRA_OPTS")
|
124
|
+
rescue Exception => e
|
125
|
+
Log.error("Failed to parse MCOLLECTIVE_EXTRA_OPTS: #{e}")
|
126
|
+
end
|
127
|
+
|
128
|
+
ENV["MCOLLECTIVE_EXTRA_OPTS"] = original_extra_opts.clone
|
129
|
+
end
|
130
|
+
|
131
|
+
begin
|
132
|
+
parser.parse!
|
133
|
+
rescue OptionParser::InvalidOption => e
|
134
|
+
retry
|
135
|
+
end
|
136
|
+
|
137
|
+
ARGV.clear
|
138
|
+
original_argv.each {|a| ARGV << a}
|
139
|
+
|
140
|
+
configfile = Util.config_file_for_user unless configfile
|
141
|
+
|
142
|
+
Config.instance.loadconfig(configfile)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
@@ -0,0 +1,292 @@
|
|
1
|
+
module MCollective
|
2
|
+
# Helpers for writing clients that can talk to agents, do discovery and so forth
|
3
|
+
class Client
|
4
|
+
attr_accessor :options, :stats
|
5
|
+
|
6
|
+
def initialize(configfile)
|
7
|
+
@config = Config.instance
|
8
|
+
@config.loadconfig(configfile) unless @config.configured
|
9
|
+
|
10
|
+
@connection = PluginManager["connector_plugin"]
|
11
|
+
@security = PluginManager["security_plugin"]
|
12
|
+
|
13
|
+
@security.initiated_by = :client
|
14
|
+
@options = nil
|
15
|
+
@subscriptions = {}
|
16
|
+
|
17
|
+
@connection.connect
|
18
|
+
end
|
19
|
+
|
20
|
+
# Returns the configured main collective if no
|
21
|
+
# specific collective is specified as options
|
22
|
+
def collective
|
23
|
+
if @options[:collective].nil?
|
24
|
+
@config.main_collective
|
25
|
+
else
|
26
|
+
@options[:collective]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# Disconnects cleanly from the middleware
|
31
|
+
def disconnect
|
32
|
+
Log.debug("Disconnecting from the middleware")
|
33
|
+
@connection.disconnect
|
34
|
+
end
|
35
|
+
|
36
|
+
# Sends a request and returns the generated request id, doesn't wait for
|
37
|
+
# responses and doesn't execute any passed in code blocks for responses
|
38
|
+
def sendreq(msg, agent, filter = {})
|
39
|
+
if msg.is_a?(Message)
|
40
|
+
request = msg
|
41
|
+
agent = request.agent
|
42
|
+
else
|
43
|
+
ttl = @options[:ttl] || @config.ttl
|
44
|
+
request = Message.new(msg, nil, {:agent => agent, :type => :request, :collective => collective, :filter => filter, :ttl => ttl})
|
45
|
+
request.reply_to = @options[:reply_to] if @options[:reply_to]
|
46
|
+
end
|
47
|
+
|
48
|
+
request.encode!
|
49
|
+
|
50
|
+
Log.debug("Sending request #{request.requestid} to the #{request.agent} agent with ttl #{request.ttl} in collective #{request.collective}")
|
51
|
+
|
52
|
+
subscribe(agent, :reply)
|
53
|
+
|
54
|
+
request.publish
|
55
|
+
|
56
|
+
request.requestid
|
57
|
+
end
|
58
|
+
|
59
|
+
def subscribe(agent, type)
|
60
|
+
unless @subscriptions.include?(agent)
|
61
|
+
subscription = Util.make_subscriptions(agent, type, collective)
|
62
|
+
Log.debug("Subscribing to #{type} target for agent #{agent}")
|
63
|
+
|
64
|
+
Util.subscribe(subscription)
|
65
|
+
@subscriptions[agent] = 1
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def unsubscribe(agent, type)
|
70
|
+
if @subscriptions.include?(agent)
|
71
|
+
subscription = Util.make_subscriptions(agent, type, collective)
|
72
|
+
Log.debug("Unsubscribing #{type} target for #{agent}")
|
73
|
+
|
74
|
+
Util.unsubscribe(subscription)
|
75
|
+
@subscriptions.delete(agent)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
# Blocking call that waits for ever for a message to arrive.
|
79
|
+
#
|
80
|
+
# If you give it a requestid this means you've previously send a request
|
81
|
+
# with that ID and now you just want replies that matches that id, in that
|
82
|
+
# case the current connection will just ignore all messages not directed at it
|
83
|
+
# and keep waiting for more till it finds a matching message.
|
84
|
+
def receive(requestid = nil)
|
85
|
+
reply = nil
|
86
|
+
|
87
|
+
begin
|
88
|
+
reply = @connection.receive
|
89
|
+
reply.type = :reply
|
90
|
+
reply.expected_msgid = requestid
|
91
|
+
|
92
|
+
reply.decode!
|
93
|
+
|
94
|
+
reply.payload[:senderid] = Digest::MD5.hexdigest(reply.payload[:senderid]) if ENV.include?("MCOLLECTIVE_ANON")
|
95
|
+
|
96
|
+
raise(MsgDoesNotMatchRequestID, "Message reqid #{requestid} does not match our reqid #{reply.requestid}") unless reply.requestid == requestid
|
97
|
+
rescue SecurityValidationFailed => e
|
98
|
+
Log.warn("Ignoring a message that did not pass security validations")
|
99
|
+
retry
|
100
|
+
rescue MsgDoesNotMatchRequestID => e
|
101
|
+
Log.debug("Ignoring a message for some other client")
|
102
|
+
retry
|
103
|
+
end
|
104
|
+
|
105
|
+
reply
|
106
|
+
end
|
107
|
+
|
108
|
+
# Performs a discovery of nodes matching the filter passed
|
109
|
+
# returns an array of nodes
|
110
|
+
#
|
111
|
+
# An integer limit can be supplied this will have the effect
|
112
|
+
# of the discovery being cancelled soon as it reached the
|
113
|
+
# requested limit of hosts
|
114
|
+
def discover(filter, timeout, limit=0)
|
115
|
+
raise "Limit has to be an integer" unless limit.is_a?(Fixnum)
|
116
|
+
|
117
|
+
begin
|
118
|
+
hosts = []
|
119
|
+
Timeout.timeout(timeout) do
|
120
|
+
reqid = sendreq("ping", "discovery", filter)
|
121
|
+
Log.debug("Waiting #{timeout} seconds for discovery replies to request #{reqid}")
|
122
|
+
|
123
|
+
loop do
|
124
|
+
reply = receive(reqid)
|
125
|
+
Log.debug("Got discovery reply from #{reply.payload[:senderid]}")
|
126
|
+
hosts << reply.payload[:senderid]
|
127
|
+
|
128
|
+
return hosts if limit > 0 && hosts.size == limit
|
129
|
+
end
|
130
|
+
end
|
131
|
+
rescue Timeout::Error => e
|
132
|
+
rescue Exception => e
|
133
|
+
raise
|
134
|
+
ensure
|
135
|
+
unsubscribe("discovery", :reply)
|
136
|
+
end
|
137
|
+
|
138
|
+
hosts.sort
|
139
|
+
end
|
140
|
+
|
141
|
+
# Send a request, performs the passed block for each response
|
142
|
+
#
|
143
|
+
# times = req("status", "mcollectived", options, client) {|resp|
|
144
|
+
# pp resp
|
145
|
+
# }
|
146
|
+
#
|
147
|
+
# It returns a hash of times and timeouts for discovery and total run is taken from the options
|
148
|
+
# hash which in turn is generally built using MCollective::Optionparser
|
149
|
+
def req(body, agent=nil, options=false, waitfor=0)
|
150
|
+
if body.is_a?(Message)
|
151
|
+
agent = body.agent
|
152
|
+
options = body.options
|
153
|
+
waitfor = body.discovered_hosts.size || 0
|
154
|
+
end
|
155
|
+
|
156
|
+
stat = {:starttime => Time.now.to_f, :discoverytime => 0, :blocktime => 0, :totaltime => 0}
|
157
|
+
|
158
|
+
options = @options unless options
|
159
|
+
|
160
|
+
STDOUT.sync = true
|
161
|
+
|
162
|
+
hosts_responded = 0
|
163
|
+
|
164
|
+
begin
|
165
|
+
Timeout.timeout(options[:timeout]) do
|
166
|
+
reqid = sendreq(body, agent, options[:filter])
|
167
|
+
|
168
|
+
loop do
|
169
|
+
resp = receive(reqid)
|
170
|
+
|
171
|
+
hosts_responded += 1
|
172
|
+
|
173
|
+
yield(resp.payload)
|
174
|
+
|
175
|
+
break if (waitfor != 0 && hosts_responded >= waitfor)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
rescue Interrupt => e
|
179
|
+
rescue Timeout::Error => e
|
180
|
+
ensure
|
181
|
+
unsubscribe(agent, :reply)
|
182
|
+
end
|
183
|
+
|
184
|
+
stat[:totaltime] = Time.now.to_f - stat[:starttime]
|
185
|
+
stat[:blocktime] = stat[:totaltime] - stat[:discoverytime]
|
186
|
+
stat[:responses] = hosts_responded
|
187
|
+
stat[:noresponsefrom] = []
|
188
|
+
|
189
|
+
@stats = stat
|
190
|
+
return stat
|
191
|
+
end
|
192
|
+
|
193
|
+
# Performs a discovery and then send a request, performs the passed block for each response
|
194
|
+
#
|
195
|
+
# times = discovered_req("status", "mcollectived", options, client) {|resp|
|
196
|
+
# pp resp
|
197
|
+
# }
|
198
|
+
#
|
199
|
+
# It returns a hash of times and timeouts for discovery and total run is taken from the options
|
200
|
+
# hash which in turn is generally built using MCollective::Optionparser
|
201
|
+
def discovered_req(body, agent, options=false)
|
202
|
+
stat = {:starttime => Time.now.to_f, :discoverytime => 0, :blocktime => 0, :totaltime => 0}
|
203
|
+
|
204
|
+
options = @options unless options
|
205
|
+
|
206
|
+
STDOUT.sync = true
|
207
|
+
|
208
|
+
print("Determining the amount of hosts matching filter for #{options[:disctimeout]} seconds .... ")
|
209
|
+
|
210
|
+
begin
|
211
|
+
discovered_hosts = discover(options[:filter], options[:disctimeout])
|
212
|
+
discovered = discovered_hosts.size
|
213
|
+
hosts_responded = []
|
214
|
+
hosts_not_responded = discovered_hosts
|
215
|
+
|
216
|
+
stat[:discoverytime] = Time.now.to_f - stat[:starttime]
|
217
|
+
|
218
|
+
puts("#{discovered}\n\n")
|
219
|
+
rescue Interrupt
|
220
|
+
puts("Discovery interrupted.")
|
221
|
+
exit!
|
222
|
+
end
|
223
|
+
|
224
|
+
raise("No matching clients found") if discovered == 0
|
225
|
+
|
226
|
+
begin
|
227
|
+
Timeout.timeout(options[:timeout]) do
|
228
|
+
reqid = sendreq(body, agent, options[:filter])
|
229
|
+
|
230
|
+
(1..discovered).each do |c|
|
231
|
+
resp = receive(reqid)
|
232
|
+
|
233
|
+
hosts_responded << resp.payload[:senderid]
|
234
|
+
hosts_not_responded.delete(resp.payload[:senderid]) if hosts_not_responded.include?(resp.payload[:senderid])
|
235
|
+
|
236
|
+
yield(resp.payload)
|
237
|
+
end
|
238
|
+
end
|
239
|
+
rescue Interrupt => e
|
240
|
+
rescue Timeout::Error => e
|
241
|
+
end
|
242
|
+
|
243
|
+
stat[:totaltime] = Time.now.to_f - stat[:starttime]
|
244
|
+
stat[:blocktime] = stat[:totaltime] - stat[:discoverytime]
|
245
|
+
stat[:responses] = hosts_responded.size
|
246
|
+
stat[:responsesfrom] = hosts_responded
|
247
|
+
stat[:noresponsefrom] = hosts_not_responded
|
248
|
+
stat[:discovered] = discovered
|
249
|
+
|
250
|
+
@stats = stat
|
251
|
+
return stat
|
252
|
+
end
|
253
|
+
|
254
|
+
# Prints out the stats returns from req and discovered_req in a nice way
|
255
|
+
def display_stats(stats, options=false, caption="stomp call summary")
|
256
|
+
options = @options unless options
|
257
|
+
|
258
|
+
if options[:verbose]
|
259
|
+
puts("\n---- #{caption} ----")
|
260
|
+
|
261
|
+
if stats[:discovered]
|
262
|
+
puts(" Nodes: #{stats[:discovered]} / #{stats[:responses]}")
|
263
|
+
else
|
264
|
+
puts(" Nodes: #{stats[:responses]}")
|
265
|
+
end
|
266
|
+
|
267
|
+
printf(" Start Time: %s\n", Time.at(stats[:starttime]))
|
268
|
+
printf(" Discovery Time: %.2fms\n", stats[:discoverytime] * 1000)
|
269
|
+
printf(" Agent Time: %.2fms\n", stats[:blocktime] * 1000)
|
270
|
+
printf(" Total Time: %.2fms\n", stats[:totaltime] * 1000)
|
271
|
+
|
272
|
+
else
|
273
|
+
if stats[:discovered]
|
274
|
+
printf("\nFinished processing %d / %d hosts in %.2f ms\n\n", stats[:responses], stats[:discovered], stats[:blocktime] * 1000)
|
275
|
+
else
|
276
|
+
printf("\nFinished processing %d hosts in %.2f ms\n\n", stats[:responses], stats[:blocktime] * 1000)
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
if stats[:noresponsefrom].size > 0
|
281
|
+
puts("\nNo response from:\n")
|
282
|
+
|
283
|
+
stats[:noresponsefrom].each do |c|
|
284
|
+
puts if c % 4 == 1
|
285
|
+
printf("%30s", c)
|
286
|
+
end
|
287
|
+
|
288
|
+
puts
|
289
|
+
end
|
290
|
+
end
|
291
|
+
end
|
292
|
+
end
|
@@ -0,0 +1,202 @@
|
|
1
|
+
module MCollective
|
2
|
+
# A pretty sucky config class, ripe for refactoring/improving
|
3
|
+
class Config
|
4
|
+
include Singleton
|
5
|
+
|
6
|
+
attr_reader :topicprefix, :daemonize, :pluginconf, :libdir, :configured,
|
7
|
+
:logfile, :keeplogs, :max_log_size, :loglevel, :logfacility, :identity,
|
8
|
+
:daemonize, :connector, :securityprovider, :factsource, :registration,
|
9
|
+
:registerinterval, :topicsep, :classesfile, :rpcauditprovider, :rpcaudit,
|
10
|
+
:configdir, :rpcauthprovider, :rpcauthorization, :color, :configfile,
|
11
|
+
:rpchelptemplate, :rpclimitmethod, :logger_type, :fact_cache_time,
|
12
|
+
:collectives, :main_collective, :ssl_cipher, :registration_collective,
|
13
|
+
:direct_addressing, :direct_addressing_threshold, :queueprefix, :ttl
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
@configured = false
|
17
|
+
end
|
18
|
+
|
19
|
+
def loadconfig(configfile)
|
20
|
+
set_config_defaults(configfile)
|
21
|
+
|
22
|
+
if File.exists?(configfile)
|
23
|
+
File.open(configfile, "r").each do |line|
|
24
|
+
|
25
|
+
# strip blank spaces, tabs etc off the end of all lines
|
26
|
+
line.gsub!(/\s*$/, "")
|
27
|
+
|
28
|
+
unless line =~ /^#|^$/
|
29
|
+
if (line =~ /(.+?)\s*=\s*(.+)/)
|
30
|
+
key = $1
|
31
|
+
val = $2
|
32
|
+
|
33
|
+
case key
|
34
|
+
when "topicsep"
|
35
|
+
@topicsep = val
|
36
|
+
when "registration"
|
37
|
+
@registration = val.capitalize
|
38
|
+
when "registration_collective"
|
39
|
+
@registration_collective = val
|
40
|
+
when "registerinterval"
|
41
|
+
@registerinterval = val.to_i
|
42
|
+
when "collectives"
|
43
|
+
@collectives = val.split(",").map {|c| c.strip}
|
44
|
+
when "main_collective"
|
45
|
+
@main_collective = val
|
46
|
+
when "topicprefix"
|
47
|
+
@topicprefix = val
|
48
|
+
when "queueprefix"
|
49
|
+
@queueprefix = val
|
50
|
+
when "logfile"
|
51
|
+
@logfile = val
|
52
|
+
when "keeplogs"
|
53
|
+
@keeplogs = val.to_i
|
54
|
+
when "max_log_size"
|
55
|
+
@max_log_size = val.to_i
|
56
|
+
when "loglevel"
|
57
|
+
@loglevel = val
|
58
|
+
when "logfacility"
|
59
|
+
@logfacility = val
|
60
|
+
when "libdir"
|
61
|
+
paths = val.split(File::PATH_SEPARATOR)
|
62
|
+
paths.each do |path|
|
63
|
+
@libdir << path
|
64
|
+
unless $LOAD_PATH.include?(path)
|
65
|
+
$LOAD_PATH << path
|
66
|
+
end
|
67
|
+
end
|
68
|
+
when "identity"
|
69
|
+
@identity = val
|
70
|
+
when "direct_addressing"
|
71
|
+
val =~ /^1|y/i ? @direct_addressing = true : @direct_addressing = false
|
72
|
+
when "direct_addressing_threshold"
|
73
|
+
@direct_addressing_threshold = val.to_i
|
74
|
+
when "color"
|
75
|
+
val =~ /^1|y/i ? @color = true : @color = false
|
76
|
+
when "daemonize"
|
77
|
+
val =~ /^1|y/i ? @daemonize = true : @daemonize = false
|
78
|
+
when "securityprovider"
|
79
|
+
@securityprovider = val.capitalize
|
80
|
+
when "factsource"
|
81
|
+
@factsource = val.capitalize
|
82
|
+
when "connector"
|
83
|
+
@connector = val.capitalize
|
84
|
+
when "classesfile"
|
85
|
+
@classesfile = val
|
86
|
+
when /^plugin.(.+)$/
|
87
|
+
@pluginconf[$1] = val
|
88
|
+
when "rpcaudit"
|
89
|
+
val =~ /^1|y/i ? @rpcaudit = true : @rpcaudit = false
|
90
|
+
when "rpcauditprovider"
|
91
|
+
@rpcauditprovider = val.capitalize
|
92
|
+
when "rpcauthorization"
|
93
|
+
val =~ /^1|y/i ? @rpcauthorization = true : @rpcauthorization = false
|
94
|
+
when "rpcauthprovider"
|
95
|
+
@rpcauthprovider = val.capitalize
|
96
|
+
when "rpchelptemplate"
|
97
|
+
@rpchelptemplate = val
|
98
|
+
when "rpclimitmethod"
|
99
|
+
@rpclimitmethod = val.to_sym
|
100
|
+
when "logger_type"
|
101
|
+
@logger_type = val
|
102
|
+
when "fact_cache_time"
|
103
|
+
@fact_cache_time = val.to_i
|
104
|
+
when "ssl_cipher"
|
105
|
+
@ssl_cipher = val
|
106
|
+
when "ttl"
|
107
|
+
@ttl = val.to_i
|
108
|
+
else
|
109
|
+
raise("Unknown config parameter #{key}")
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
read_plugin_config_dir("#{@configdir}/plugin.d")
|
116
|
+
|
117
|
+
raise 'Identities can only match /\w\.\-/' unless @identity.match(/^[\w\.\-]+$/)
|
118
|
+
|
119
|
+
@configured = true
|
120
|
+
|
121
|
+
@libdir.each {|dir| Log.warn("Cannot find libdir: #{dir}") unless File.directory?(dir)}
|
122
|
+
|
123
|
+
if @logger_type == "syslog"
|
124
|
+
raise "The sylog logger is not usable on the Windows platform" if Util.windows?
|
125
|
+
end
|
126
|
+
|
127
|
+
PluginManager.loadclass("Mcollective::Facts::#{@factsource}_facts")
|
128
|
+
PluginManager.loadclass("Mcollective::Connector::#{@connector}")
|
129
|
+
PluginManager.loadclass("Mcollective::Security::#{@securityprovider}")
|
130
|
+
PluginManager.loadclass("Mcollective::Registration::#{@registration}")
|
131
|
+
PluginManager.loadclass("Mcollective::Audit::#{@rpcauditprovider}") if @rpcaudit
|
132
|
+
PluginManager << {:type => "global_stats", :class => RunnerStats.new}
|
133
|
+
else
|
134
|
+
raise("Cannot find config file '#{configfile}'")
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def set_config_defaults(configfile)
|
139
|
+
@stomp = Hash.new
|
140
|
+
@subscribe = Array.new
|
141
|
+
@pluginconf = Hash.new
|
142
|
+
@connector = "Stomp"
|
143
|
+
@securityprovider = "Psk"
|
144
|
+
@factsource = "Yaml"
|
145
|
+
@identity = Socket.gethostname
|
146
|
+
@registration = "Agentlist"
|
147
|
+
@registerinterval = 0
|
148
|
+
@registration_collective = nil
|
149
|
+
@topicsep = "."
|
150
|
+
@topicprefix = "/topic/"
|
151
|
+
@queueprefix = "/queue/"
|
152
|
+
@classesfile = "/var/lib/puppet/state/classes.txt"
|
153
|
+
@rpcaudit = false
|
154
|
+
@rpcauditprovider = ""
|
155
|
+
@rpcauthorization = false
|
156
|
+
@rpcauthprovider = ""
|
157
|
+
@configdir = File.dirname(configfile)
|
158
|
+
@color = !Util.windows?
|
159
|
+
@configfile = configfile
|
160
|
+
@logger_type = "file"
|
161
|
+
@keeplogs = 5
|
162
|
+
@max_log_size = 2097152
|
163
|
+
@rpclimitmethod = :first
|
164
|
+
@libdir = Array.new
|
165
|
+
@fact_cache_time = 300
|
166
|
+
@loglevel = "info"
|
167
|
+
@logfacility = "user"
|
168
|
+
@collectives = ["mcollective"]
|
169
|
+
@main_collective = @collectives.first
|
170
|
+
@ssl_cipher = "aes-256-cbc"
|
171
|
+
@direct_addressing = false
|
172
|
+
@direct_addressing_threshold = 10
|
173
|
+
@ttl = 60
|
174
|
+
|
175
|
+
# look in the config dir for the template so users can provide their own and windows
|
176
|
+
# with odd paths will just work more often, but fall back to old behavior if it does
|
177
|
+
# not exist
|
178
|
+
@rpchelptemplate = File.join(File.dirname(configfile), "rpc-help.erb")
|
179
|
+
@rpchelptemplate = "/etc/mcollective/rpc-help.erb" unless File.exists?(@rpchelptemplate)
|
180
|
+
end
|
181
|
+
|
182
|
+
def read_plugin_config_dir(dir)
|
183
|
+
return unless File.directory?(dir)
|
184
|
+
|
185
|
+
Dir.new(dir).each do |pluginconfigfile|
|
186
|
+
next unless pluginconfigfile =~ /^([\w]+).cfg$/
|
187
|
+
|
188
|
+
plugin = $1
|
189
|
+
File.open("#{dir}/#{pluginconfigfile}", "r").each do |line|
|
190
|
+
# strip blank lines
|
191
|
+
line.gsub!(/\s*$/, "")
|
192
|
+
next if line =~ /^#|^$/
|
193
|
+
if (line =~ /(.+?)\s*=\s*(.+)/)
|
194
|
+
key = $1
|
195
|
+
val = $2
|
196
|
+
@pluginconf["#{plugin}.#{key}"] = val
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|