Class MCollective::Client
In: lib/mcollective/client.rb
Parent: Object

Helpers for writing clients that can talk to agents, do discovery and so forth

Methods

Attributes

options  [RW] 
stats  [RW] 

Public Class methods

[Source]

    # File lib/mcollective/client.rb, line 8
 8:         def initialize(configfile)
 9:             @config = Config.instance
10:             @config.loadconfig(configfile) unless @config.configured
11:             @connection = PluginManager["connector_plugin"]
12: 
13:             @security = PluginManager["security_plugin"]
14:             @security.initiated_by = :client
15: 
16:             @options = nil
17: 
18:             @subscriptions = {}
19: 
20:             @connection.connect
21:         end

Public Instance methods

Disconnects cleanly from the middleware

[Source]

    # File lib/mcollective/client.rb, line 24
24:         def disconnect
25:             Log.debug("Disconnecting from the middleware")
26:             @connection.disconnect
27:         end

Performs a discovery of nodes matching the filter passed returns an array of nodes

[Source]

     # File lib/mcollective/client.rb, line 83
 83:         def discover(filter, timeout)
 84:             begin
 85:                 reqid = sendreq("ping", "discovery", filter)
 86:                 Log.debug("Waiting #{timeout} seconds for discovery replies to request #{reqid}")
 87: 
 88:                 hosts = []
 89:                 Timeout.timeout(timeout) do
 90:                     loop do
 91:                         msg = receive(reqid)
 92:                         Log.debug("Got discovery reply from #{msg[:senderid]}")
 93:                         hosts << msg[:senderid]
 94:                     end
 95:                 end
 96:             rescue Timeout::Error => e
 97:                 hosts.sort
 98:             rescue Exception => e
 99:                 raise
100:             end
101:         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

[Source]

     # File lib/mcollective/client.rb, line 155
155:         def discovered_req(body, agent, options=false)
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:             print("Determining the amount of hosts matching filter for #{options[:disctimeout]} seconds .... ")
163: 
164:             begin
165:                 discovered_hosts = discover(options[:filter], options[:disctimeout])
166:                 discovered = discovered_hosts.size
167:                 hosts_responded = []
168:                 hosts_not_responded = discovered_hosts
169: 
170:                 stat[:discoverytime] = Time.now.to_f - stat[:starttime]
171: 
172:                 puts("#{discovered}\n\n")
173:             rescue Interrupt
174:                 puts("Discovery interrupted.")
175:                 exit!
176:             end
177: 
178:             raise("No matching clients found") if discovered == 0
179: 
180:             reqid = sendreq(body, agent, options[:filter])
181: 
182:             begin
183:                 Timeout.timeout(options[:timeout]) do
184:                     (1..discovered).each do |c|
185:                         resp = receive(reqid)
186: 
187:                         hosts_responded << resp[:senderid]
188:                         hosts_not_responded.delete(resp[:senderid]) if hosts_not_responded.include?(resp[:senderid])
189: 
190:                         yield(resp)
191:                     end
192:                 end
193:             rescue Interrupt => e
194:             rescue Timeout::Error => e
195:             end
196: 
197:             stat[:totaltime] = Time.now.to_f - stat[:starttime]
198:             stat[:blocktime] = stat[:totaltime] - stat[:discoverytime]
199:             stat[:responses] = hosts_responded.size
200:             stat[:responsesfrom] = hosts_responded
201:             stat[:noresponsefrom] = hosts_not_responded
202:             stat[:discovered] = discovered
203: 
204:             @stats = stat
205:             return stat
206:         end

Prints out the stats returns from req and discovered_req in a nice way

[Source]

     # File lib/mcollective/client.rb, line 209
209:         def display_stats(stats, options=false, caption="stomp call summary")
210:             options = @options unless options
211: 
212:             if options[:verbose]
213:                 puts("\n---- #{caption} ----")
214: 
215:                 if stats[:discovered]
216:                     puts("           Nodes: #{stats[:discovered]} / #{stats[:responses]}")
217:                 else
218:                     puts("           Nodes: #{stats[:responses]}")
219:                 end
220: 
221:                 printf("      Start Time: %s\n", Time.at(stats[:starttime]))
222:                 printf("  Discovery Time: %.2fms\n", stats[:discoverytime] * 1000)
223:                 printf("      Agent Time: %.2fms\n", stats[:blocktime] * 1000)
224:                 printf("      Total Time: %.2fms\n", stats[:totaltime] * 1000)
225: 
226:             else
227:                 if stats[:discovered]
228:                     printf("\nFinished processing %d / %d hosts in %.2f ms\n\n", stats[:responses], stats[:discovered], stats[:blocktime] * 1000)
229:                 else
230:                     printf("\nFinished processing %d hosts in %.2f ms\n\n", stats[:responses], stats[:blocktime] * 1000)
231:                 end
232:             end
233: 
234:             if stats[:noresponsefrom].size > 0
235:                 puts("\nNo response from:\n")
236: 
237:                 stats[:noresponsefrom].each do |c|
238:                     puts if c % 4 == 1
239:                     printf("%30s", c)
240:                 end
241: 
242:                 puts
243:             end
244:         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.

[Source]

    # File lib/mcollective/client.rb, line 59
59:         def receive(requestid = nil)
60:             msg = nil
61: 
62:             begin
63:                 msg = @connection.receive
64: 
65:                 msg = @security.decodemsg(msg)
66: 
67:                 msg[:senderid] = Digest::MD5.hexdigest(msg[:senderid]) if ENV.include?("MCOLLECTIVE_ANON")
68: 
69:                 raise(MsgDoesNotMatchRequestID, "Message reqid #{requestid} does not match our reqid #{msg[:requestid]}") if msg[:requestid] != requestid
70:             rescue SecurityValidationFailed => e
71:                 Log.warn("Ignoring a message that did not pass security validations")
72:                 retry
73:             rescue MsgDoesNotMatchRequestID => e
74:                 Log.debug("Ignoring a message for some other client")
75:                 retry
76:             end
77: 
78:             msg
79:         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

[Source]

     # File lib/mcollective/client.rb, line 111
111:         def req(body, agent, options=false, waitfor=0)
112:             stat = {:starttime => Time.now.to_f, :discoverytime => 0, :blocktime => 0, :totaltime => 0}
113: 
114:             options = @options unless options
115: 
116:             STDOUT.sync = true
117: 
118:             reqid = sendreq(body, agent, options[:filter])
119: 
120:             hosts_responded = 0
121: 
122:             begin
123:                 Timeout.timeout(options[:timeout]) do
124:                     loop do
125:                         resp = receive(reqid)
126: 
127:                         hosts_responded += 1
128: 
129:                         yield(resp)
130: 
131:                         break if (waitfor != 0 && hosts_responded >= waitfor)
132:                     end
133:                 end
134:             rescue Interrupt => e
135:             rescue Timeout::Error => e
136:             end
137: 
138:             stat[:totaltime] = Time.now.to_f - stat[:starttime]
139:             stat[:blocktime] = stat[:totaltime] - stat[:discoverytime]
140:             stat[:responses] = hosts_responded
141:             stat[:noresponsefrom] = []
142: 
143:             @stats = stat
144:             return stat
145:         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

[Source]

    # File lib/mcollective/client.rb, line 31
31:         def sendreq(msg, agent, filter = {})
32:             target = Util.make_target(agent, :command)
33: 
34:             reqid = Digest::MD5.hexdigest("#{@config.identity}-#{Time.now.to_f.to_s}-#{target}")
35: 
36:             req = @security.encoderequest(@config.identity, target, msg, reqid, filter)
37: 
38:             Log.debug("Sending request #{reqid} to #{target}")
39: 
40:             unless @subscriptions.include?(agent)
41:                 topic = Util.make_target(agent, :reply)
42:                 Log.debug("Subscribing to #{topic}")
43: 
44:                 @connection.subscribe(topic)
45:                 @subscriptions[agent] = 1
46:             end
47: 
48:             @connection.send(target, req)
49: 
50:             reqid
51:         end

[Validate]