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 81 81: def discover(filter, timeout) 82: begin 83: reqid = sendreq("ping", "discovery", filter) 84: @log.debug("Waiting #{timeout} seconds for discovery replies to request #{reqid}") 85: 86: hosts = [] 87: Timeout.timeout(timeout) do 88: loop do 89: msg = receive(reqid) 90: @log.debug("Got discovery reply from #{msg[:senderid]}") 91: hosts << msg[:senderid] 92: end 93: end 94: rescue Timeout::Error => e 95: hosts.sort 96: rescue Exception => e 97: raise 98: end 99: 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 153 153: def discovered_req(body, agent, options=false) 154: stat = {:starttime => Time.now.to_f, :discoverytime => 0, :blocktime => 0, :totaltime => 0} 155: 156: options = @options unless options 157: 158: STDOUT.sync = true 159: 160: print("Determining the amount of hosts matching filter for #{options[:disctimeout]} seconds .... ") 161: 162: begin 163: discovered_hosts = discover(options[:filter], options[:disctimeout]) 164: discovered = discovered_hosts.size 165: hosts_responded = [] 166: hosts_not_responded = discovered_hosts 167: 168: stat[:discoverytime] = Time.now.to_f - stat[:starttime] 169: 170: puts("#{discovered}\n\n") 171: rescue Interrupt 172: puts("Discovery interrupted.") 173: exit! 174: end 175: 176: raise("No matching clients found") if discovered == 0 177: 178: reqid = sendreq(body, agent, options[:filter]) 179: 180: begin 181: Timeout.timeout(options[:timeout]) do 182: (1..discovered).each do |c| 183: resp = receive(reqid) 184: 185: hosts_responded << resp[:senderid] 186: hosts_not_responded.delete(resp[:senderid]) if hosts_not_responded.include?(resp[:senderid]) 187: 188: yield(resp) 189: end 190: end 191: rescue Interrupt => e 192: rescue Timeout::Error => e 193: end 194: 195: stat[:totaltime] = Time.now.to_f - stat[:starttime] 196: stat[:blocktime] = stat[:totaltime] - stat[:discoverytime] 197: stat[:responses] = hosts_responded.size 198: stat[:responsesfrom] = hosts_responded 199: stat[:noresponsefrom] = hosts_not_responded 200: stat[:discovered] = discovered 201: 202: @stats = stat 203: return stat 204: end
Prints out the stats returns from req and discovered_req in a nice way
# File lib/mcollective/client.rb, line 207 207: def display_stats(stats, options=false, caption="stomp call summary") 208: options = @options unless options 209: 210: if options[:verbose] 211: puts("\n---- #{caption} ----") 212: 213: if stats[:discovered] 214: puts(" Nodes: #{stats[:discovered]} / #{stats[:responses]}") 215: else 216: puts(" Nodes: #{stats[:responses]}") 217: end 218: 219: printf(" Start Time: %s\n", Time.at(stats[:starttime])) 220: printf(" Discovery Time: %.2fms\n", stats[:discoverytime] * 1000) 221: printf(" Agent Time: %.2fms\n", stats[:blocktime] * 1000) 222: printf(" Total Time: %.2fms\n", stats[:totaltime] * 1000) 223: 224: else 225: if stats[:discovered] 226: printf("\nFinished processing %d / %d hosts in %.2f ms\n\n", stats[:responses], stats[:discovered], stats[:blocktime] * 1000) 227: else 228: printf("\nFinished processing %d hosts in %.2f ms\n\n", stats[:responses], stats[:blocktime] * 1000) 229: end 230: end 231: 232: if stats[:noresponsefrom].size > 0 233: puts("\nNo response from:\n") 234: 235: stats[:noresponsefrom].each do |c| 236: puts if c % 4 == 1 237: printf("%30s", c) 238: end 239: 240: puts 241: end 242: 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 MsgDoesNotMatchRequestID => e 72: @log.debug("Ignoring a message for some other client") 73: retry 74: end 75: 76: msg 77: 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 109 109: def req(body, agent, options=false, waitfor=0) 110: stat = {:starttime => Time.now.to_f, :discoverytime => 0, :blocktime => 0, :totaltime => 0} 111: 112: options = @options unless options 113: 114: STDOUT.sync = true 115: 116: reqid = sendreq(body, agent, options[:filter]) 117: 118: hosts_responded = 0 119: 120: begin 121: Timeout.timeout(options[:timeout]) do 122: loop do 123: resp = receive(reqid) 124: 125: hosts_responded += 1 126: 127: yield(resp) 128: 129: break if (waitfor != 0 && hosts_responded >= waitfor) 130: end 131: end 132: rescue Interrupt => e 133: rescue Timeout::Error => e 134: end 135: 136: stat[:totaltime] = Time.now.to_f - stat[:starttime] 137: stat[:blocktime] = stat[:totaltime] - stat[:discoverytime] 138: stat[:responses] = hosts_responded 139: stat[:noresponsefrom] = [] 140: 141: @stats = stat 142: return stat 143: 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