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