module Oxidized require 'net/telnet' require 'oxidized/input/cli' class Telnet < Input include CLI attr_reader :telnet def connect node @node = node @timeout = CFG.timeout @node.model.cfg['telnet'].each { |cb| instance_exec &cb } begin @telnet = Net::Telnet.new 'Host' => @node.ip, 'Waittime' => @timeout, 'Model' => @node.model expect username @telnet.puts @node.auth[:username] expect password @telnet.puts @node.auth[:password] expect @node.prompt rescue Timeout::Error, Errno::ECONNREFUSED, Errno::ECONNRESET, Errno::EPIPE, Errno::EHOSTUNREACH return false end end def cmd cmd, expect=@node.prompt Log.debug "Telnet: #{cmd} @#{@node.name}" args = { 'String' => cmd } args.merge!({ 'Match' => expect, 'Timeout' => @timeout }) if expect begin @telnet.cmd args rescue Timeout::Error, Errno::ECONNRESET, Errno::EPIPE return false end end def send data @telnet.write data end private def expect re @telnet.waitfor 'Match' => re, 'Timeout' => @timeout end def disconnect begin disconnect_cli @telnet.close rescue Errno::ECONNRESET end end def username re=/^(Username|login)/ @username or @username = re end def password re=/^Password/ @password or @password = re end end end class Net::Telnet ## FIXME: we just need 'line = model.expects line' to handle pager ## how to do this, without redefining the whole damn thing def waitfor(options) # :yield: recvdata time_out = @options["Timeout"] waittime = @options["Waittime"] fail_eof = @options["FailEOF"] model = @options["Model"] if options.kind_of?(Hash) prompt = if options.has_key?("Match") options["Match"] elsif options.has_key?("Prompt") options["Prompt"] elsif options.has_key?("String") Regexp.new( Regexp.quote(options["String"]) ) end time_out = options["Timeout"] if options.has_key?("Timeout") waittime = options["Waittime"] if options.has_key?("Waittime") fail_eof = options["FailEOF"] if options.has_key?("FailEOF") else prompt = options end if time_out == false time_out = nil end line = '' buf = '' rest = '' until(prompt === line and not IO::select([@sock], nil, nil, waittime)) unless IO::select([@sock], nil, nil, time_out) raise TimeoutError, "timed out while waiting for more data" end begin c = @sock.readpartial(1024 * 1024) @dumplog.log_dump('<', c) if @options.has_key?("Dump_log") if @options["Telnetmode"] c = rest + c if Integer(c.rindex(/#{IAC}#{SE}/no) || 0) < Integer(c.rindex(/#{IAC}#{SB}/no) || 0) buf = preprocess(c[0 ... c.rindex(/#{IAC}#{SB}/no)]) rest = c[c.rindex(/#{IAC}#{SB}/no) .. -1] elsif pt = c.rindex(/#{IAC}[^#{IAC}#{AO}#{AYT}#{DM}#{IP}#{NOP}]?\z/no) || c.rindex(/\r\z/no) buf = preprocess(c[0 ... pt]) rest = c[pt .. -1] else buf = preprocess(c) rest = '' end else # Not Telnetmode. # # We cannot use preprocess() on this data, because that # method makes some Telnetmode-specific assumptions. buf = rest + c rest = '' unless @options["Binmode"] if pt = buf.rindex(/\r\z/no) buf = buf[0 ... pt] rest = buf[pt .. -1] end buf.gsub!(/#{EOL}/no, "\n") end end @log.print(buf) if @options.has_key?("Output_log") line += buf line = model.expects line line = yield line if block_given? yield buf if block_given? rescue EOFError # End of file reached raise if fail_eof if line == '' line = nil yield nil if block_given? end break end end line end end