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: 
12:             @connection = PluginManager["connector_plugin"]
13:             @security = PluginManager["security_plugin"]
14: 
15:             @security.initiated_by = :client
16:             @options = nil
17:             @subscriptions = {}
18: 
19:             @connection.connect
20:         end

Public Instance methods

Returns the configured main collective if no specific collective is specified as options

[Source]

    # File lib/mcollective/client.rb, line 24
24:         def collective
25:             if @options[:collective].nil?
26:                 @config.main_collective
27:             else
28:                 @options[:collective]
29:             end
30:         end

Disconnects cleanly from the middleware

[Source]

    # File lib/mcollective/client.rb, line 33
33:         def disconnect
34:             Log.debug("Disconnecting from the middleware")
35:             @connection.disconnect
36:         end

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

[Source]

     # File lib/mcollective/client.rb, line 94
 94:         def discover(filter, timeout)
 95:             begin
 96:                 hosts = []
 97:                 Timeout.timeout(timeout) do
 98:                     reqid = sendreq("ping", "discovery", filter)
 99:                     Log.debug("Waiting #{timeout} seconds for discovery replies to request #{reqid}")
100: 
101:                     loop do
102:                         msg = receive(reqid)
103:                         Log.debug("Got discovery reply from #{msg[:senderid]}")
104:                         hosts << msg[:senderid]
105:                     end
106:                 end
107:             rescue Timeout::Error => e
108:                 hosts.sort
109:             rescue Exception => e
110:                 raise
111:             end
112:         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 166
166:         def discovered_req(body, agent, options=false)
167:             stat = {:starttime => Time.now.to_f, :discoverytime => 0, :blocktime => 0, :totaltime => 0}
168: 
169:             options = @options unless options
170: 
171:             STDOUT.sync = true
172: 
173:             print("Determining the amount of hosts matching filter for #{options[:disctimeout]} seconds .... ")
174: 
175:             begin
176:                 discovered_hosts = discover(options[:filter], options[:disctimeout])
177:                 discovered = discovered_hosts.size
178:                 hosts_responded = []
179:                 hosts_not_responded = discovered_hosts
180: 
181:                 stat[:discoverytime] = Time.now.to_f - stat[:starttime]
182: 
183:                 puts("#{discovered}\n\n")
184:             rescue Interrupt
185:                 puts("Discovery interrupted.")
186:                 exit!
187:             end
188: 
189:             raise("No matching clients found") if discovered == 0
190: 
191:             begin
192:                 Timeout.timeout(options[:timeout]) do
193:                     reqid = sendreq(body, agent, options[:filter])
194: 
195:                     (1..discovered).each do |c|
196:                         resp = receive(reqid)
197: 
198:                         hosts_responded << resp[:senderid]
199:                         hosts_not_responded.delete(resp[:senderid]) if hosts_not_responded.include?(resp[:senderid])
200: 
201:                         yield(resp)
202:                     end
203:                 end
204:             rescue Interrupt => e
205:             rescue Timeout::Error => e
206:             end
207: 
208:             stat[:totaltime] = Time.now.to_f - stat[:starttime]
209:             stat[:blocktime] = stat[:totaltime] - stat[:discoverytime]
210:             stat[:responses] = hosts_responded.size
211:             stat[:responsesfrom] = hosts_responded
212:             stat[:noresponsefrom] = hosts_not_responded
213:             stat[:discovered] = discovered
214: 
215:             @stats = stat
216:             return stat
217:         end

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

[Source]

     # File lib/mcollective/client.rb, line 220
220:         def display_stats(stats, options=false, caption="stomp call summary")
221:             options = @options unless options
222: 
223:             if options[:verbose]
224:                 puts("\n---- #{caption} ----")
225: 
226:                 if stats[:discovered]
227:                     puts("           Nodes: #{stats[:discovered]} / #{stats[:responses]}")
228:                 else
229:                     puts("           Nodes: #{stats[:responses]}")
230:                 end
231: 
232:                 printf("      Start Time: %s\n", Time.at(stats[:starttime]))
233:                 printf("  Discovery Time: %.2fms\n", stats[:discoverytime] * 1000)
234:                 printf("      Agent Time: %.2fms\n", stats[:blocktime] * 1000)
235:                 printf("      Total Time: %.2fms\n", stats[:totaltime] * 1000)
236: 
237:             else
238:                 if stats[:discovered]
239:                     printf("\nFinished processing %d / %d hosts in %.2f ms\n\n", stats[:responses], stats[:discovered], stats[:blocktime] * 1000)
240:                 else
241:                     printf("\nFinished processing %d hosts in %.2f ms\n\n", stats[:responses], stats[:blocktime] * 1000)
242:                 end
243:             end
244: 
245:             if stats[:noresponsefrom].size > 0
246:                 puts("\nNo response from:\n")
247: 
248:                 stats[:noresponsefrom].each do |c|
249:                     puts if c % 4 == 1
250:                     printf("%30s", c)
251:                 end
252: 
253:                 puts
254:             end
255:         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 70
70:         def receive(requestid = nil)
71:             msg = nil
72: 
73:             begin
74:                 msg = @connection.receive
75: 
76:                 msg = @security.decodemsg(msg)
77: 
78:                 msg[:senderid] = Digest::MD5.hexdigest(msg[:senderid]) if ENV.include?("MCOLLECTIVE_ANON")
79: 
80:                 raise(MsgDoesNotMatchRequestID, "Message reqid #{requestid} does not match our reqid #{msg[:requestid]}") if msg[:requestid] != requestid
81:             rescue SecurityValidationFailed => e
82:                 Log.warn("Ignoring a message that did not pass security validations")
83:                 retry
84:             rescue MsgDoesNotMatchRequestID => e
85:                 Log.debug("Ignoring a message for some other client")
86:                 retry
87:             end
88: 
89:             msg
90:         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 122
122:         def req(body, agent, options=false, waitfor=0)
123:             stat = {:starttime => Time.now.to_f, :discoverytime => 0, :blocktime => 0, :totaltime => 0}
124: 
125:             options = @options unless options
126: 
127:             STDOUT.sync = true
128: 
129:             hosts_responded = 0
130: 
131:             begin
132:                 Timeout.timeout(options[:timeout]) do
133:                     reqid = sendreq(body, agent, options[:filter])
134: 
135:                     loop do
136:                         resp = receive(reqid)
137: 
138:                         hosts_responded += 1
139: 
140:                         yield(resp)
141: 
142:                         break if (waitfor != 0 && hosts_responded >= waitfor)
143:                     end
144:                 end
145:             rescue Interrupt => e
146:             rescue Timeout::Error => e
147:             end
148: 
149:             stat[:totaltime] = Time.now.to_f - stat[:starttime]
150:             stat[:blocktime] = stat[:totaltime] - stat[:discoverytime]
151:             stat[:responses] = hosts_responded
152:             stat[:noresponsefrom] = []
153: 
154:             @stats = stat
155:             return stat
156:         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 40
40:         def sendreq(msg, agent, filter = {})
41:             target = Util.make_target(agent, :command, collective)
42: 
43:             reqid = Digest::MD5.hexdigest("#{@config.identity}-#{Time.now.to_f.to_s}-#{target}")
44: 
45:             req = @security.encoderequest(@config.identity, target, msg, reqid, filter)
46: 
47:             Log.debug("Sending request #{reqid} to #{target}")
48: 
49:             unless @subscriptions.include?(agent)
50:                 topic = Util.make_target(agent, :reply, collective)
51:                 Log.debug("Subscribing to #{topic}")
52: 
53:                 Util.subscribe(topic)
54:                 @subscriptions[agent] = 1
55:             end
56: 
57:             Timeout.timeout(2) do
58:                 @connection.send(target, req)
59:             end
60: 
61:             reqid
62:         end

[Validate]