| Class | MCollective::Client |
| In: |
lib/mcollective/client.rb
|
| Parent: | Object |
Helpers for writing clients that can talk to agents, do discovery and so forth
| options | [RW] | |
| stats | [RW] |
# File lib/mcollective/client.rb, line 8
8: def initialize(configfile)
9: @config = Config.instance
10: @config.loadconfig(configfile) unless @config.configured
11: @log = Log.instance
12: @connection = PluginManager["connector_plugin"]
13:
14: @security = PluginManager["security_plugin"]
15: @security.initiated_by = :client
16:
17: @options = nil
18:
19: @subscriptions = {}
20:
21: @connection.connect
22: end
Disconnects cleanly from the middleware
# File lib/mcollective/client.rb, line 25
25: def disconnect
26: @log.debug("Disconnecting from the middleware")
27: @connection.disconnect
28: end
Performs a discovery of nodes matching the filter passed returns an array of nodes
# File lib/mcollective/client.rb, line 84
84: def discover(filter, timeout)
85: begin
86: reqid = sendreq("ping", "discovery", filter)
87: @log.debug("Waiting #{timeout} seconds for discovery replies to request #{reqid}")
88:
89: hosts = []
90: Timeout.timeout(timeout) do
91: loop do
92: msg = receive(reqid)
93: @log.debug("Got discovery reply from #{msg[:senderid]}")
94: hosts << msg[:senderid]
95: end
96: end
97: rescue Timeout::Error => e
98: hosts.sort
99: rescue Exception => e
100: raise
101: end
102: end
Performs a discovery and then send a request, performs the passed block for each response
times = discovered_req("status", "mcollectived", options, client) {|resp|
pp resp
}
It returns a hash of times and timeouts for discovery and total run is taken from the options hash which in turn is generally built using MCollective::Optionparser
# File lib/mcollective/client.rb, line 156
156: def discovered_req(body, agent, options=false)
157: stat = {:starttime => Time.now.to_f, :discoverytime => 0, :blocktime => 0, :totaltime => 0}
158:
159: options = @options unless options
160:
161: STDOUT.sync = true
162:
163: print("Determining the amount of hosts matching filter for #{options[:disctimeout]} seconds .... ")
164:
165: begin
166: discovered_hosts = discover(options[:filter], options[:disctimeout])
167: discovered = discovered_hosts.size
168: hosts_responded = []
169: hosts_not_responded = discovered_hosts
170:
171: stat[:discoverytime] = Time.now.to_f - stat[:starttime]
172:
173: puts("#{discovered}\n\n")
174: rescue Interrupt
175: puts("Discovery interrupted.")
176: exit!
177: end
178:
179: raise("No matching clients found") if discovered == 0
180:
181: reqid = sendreq(body, agent, options[:filter])
182:
183: begin
184: Timeout.timeout(options[:timeout]) do
185: (1..discovered).each do |c|
186: resp = receive(reqid)
187:
188: hosts_responded << resp[:senderid]
189: hosts_not_responded.delete(resp[:senderid]) if hosts_not_responded.include?(resp[:senderid])
190:
191: yield(resp)
192: end
193: end
194: rescue Interrupt => e
195: rescue Timeout::Error => e
196: end
197:
198: stat[:totaltime] = Time.now.to_f - stat[:starttime]
199: stat[:blocktime] = stat[:totaltime] - stat[:discoverytime]
200: stat[:responses] = hosts_responded.size
201: stat[:responsesfrom] = hosts_responded
202: stat[:noresponsefrom] = hosts_not_responded
203: stat[:discovered] = discovered
204:
205: @stats = stat
206: return stat
207: end
Prints out the stats returns from req and discovered_req in a nice way
# File lib/mcollective/client.rb, line 210
210: def display_stats(stats, options=false, caption="stomp call summary")
211: options = @options unless options
212:
213: if options[:verbose]
214: puts("\n---- #{caption} ----")
215:
216: if stats[:discovered]
217: puts(" Nodes: #{stats[:discovered]} / #{stats[:responses]}")
218: else
219: puts(" Nodes: #{stats[:responses]}")
220: end
221:
222: printf(" Start Time: %s\n", Time.at(stats[:starttime]))
223: printf(" Discovery Time: %.2fms\n", stats[:discoverytime] * 1000)
224: printf(" Agent Time: %.2fms\n", stats[:blocktime] * 1000)
225: printf(" Total Time: %.2fms\n", stats[:totaltime] * 1000)
226:
227: else
228: if stats[:discovered]
229: printf("\nFinished processing %d / %d hosts in %.2f ms\n\n", stats[:responses], stats[:discovered], stats[:blocktime] * 1000)
230: else
231: printf("\nFinished processing %d hosts in %.2f ms\n\n", stats[:responses], stats[:blocktime] * 1000)
232: end
233: end
234:
235: if stats[:noresponsefrom].size > 0
236: puts("\nNo response from:\n")
237:
238: stats[:noresponsefrom].each do |c|
239: puts if c % 4 == 1
240: printf("%30s", c)
241: end
242:
243: puts
244: end
245: end
Blocking call that waits for ever for a message to arrive.
If you give it a requestid this means you‘ve previously send a request with that ID and now you just want replies that matches that id, in that case the current connection will just ignore all messages not directed at it and keep waiting for more till it finds a matching message.
# File lib/mcollective/client.rb, line 60
60: def receive(requestid = nil)
61: msg = nil
62:
63: begin
64: msg = @connection.receive
65:
66: msg = @security.decodemsg(msg)
67:
68: msg[:senderid] = Digest::MD5.hexdigest(msg[:senderid]) if ENV.include?("MCOLLECTIVE_ANON")
69:
70: raise(MsgDoesNotMatchRequestID, "Message reqid #{requestid} does not match our reqid #{msg[:requestid]}") if msg[:requestid] != requestid
71: rescue SecurityValidationFailed => e
72: @log.warn("Ignoring a message that did not pass security validations")
73: retry
74: rescue MsgDoesNotMatchRequestID => e
75: @log.debug("Ignoring a message for some other client")
76: retry
77: end
78:
79: msg
80: end
Send a request, performs the passed block for each response
times = req("status", "mcollectived", options, client) {|resp|
pp resp
}
It returns a hash of times and timeouts for discovery and total run is taken from the options hash which in turn is generally built using MCollective::Optionparser
# File lib/mcollective/client.rb, line 112
112: def req(body, agent, options=false, waitfor=0)
113: stat = {:starttime => Time.now.to_f, :discoverytime => 0, :blocktime => 0, :totaltime => 0}
114:
115: options = @options unless options
116:
117: STDOUT.sync = true
118:
119: reqid = sendreq(body, agent, options[:filter])
120:
121: hosts_responded = 0
122:
123: begin
124: Timeout.timeout(options[:timeout]) do
125: loop do
126: resp = receive(reqid)
127:
128: hosts_responded += 1
129:
130: yield(resp)
131:
132: break if (waitfor != 0 && hosts_responded >= waitfor)
133: end
134: end
135: rescue Interrupt => e
136: rescue Timeout::Error => e
137: end
138:
139: stat[:totaltime] = Time.now.to_f - stat[:starttime]
140: stat[:blocktime] = stat[:totaltime] - stat[:discoverytime]
141: stat[:responses] = hosts_responded
142: stat[:noresponsefrom] = []
143:
144: @stats = stat
145: return stat
146: end
Sends a request and returns the generated request id, doesn‘t wait for responses and doesn‘t execute any passed in code blocks for responses
# File lib/mcollective/client.rb, line 32
32: def sendreq(msg, agent, filter = {})
33: target = Util.make_target(agent, :command)
34:
35: reqid = Digest::MD5.hexdigest("#{@config.identity}-#{Time.now.to_f.to_s}-#{target}")
36:
37: req = @security.encoderequest(@config.identity, target, msg, reqid, filter)
38:
39: @log.debug("Sending request #{reqid} to #{target}")
40:
41: unless @subscriptions.include?(agent)
42: topic = Util.make_target(agent, :reply)
43: @log.debug("Subscribing to #{topic}")
44:
45: @connection.subscribe(topic)
46: @subscriptions[agent] = 1
47: end
48:
49: @connection.send(target, req)
50:
51: reqid
52: end