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: @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
Disconnects cleanly from the middleware
# 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
# 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
# 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
# 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.
# 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
# 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
# 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