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: 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
Returns the configured main collective if no specific collective is specified as options
# 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
# 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
# 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
# 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
# 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.
# 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
# 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
# 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