From c252a589b6b0274a17bfe40db3fd57ebeea3beb8 Mon Sep 17 00:00:00 2001 From: Saku Ytti Date: Wed, 1 May 2013 13:45:02 +0300 Subject: =?UTF-8?q?Add=20Model#expect,=20support=20block=20at=C2=A0post/pr?= =?UTF-8?q?e=20config?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now we can deal with pager and additional PW prompts, such as 'enable' Examples in IOS model how to use. The Telnet implementation is particularly fugly, I just need one line in 'waitfor' to handle pager while waiting for prompt, but couldn't figure out clean way to do it, so needed to rewrit whole Telnet#waitfor just to add that line. --- lib/oxidized/input/cli.rb | 18 ++++++--- lib/oxidized/input/ssh.rb | 12 ++++-- lib/oxidized/input/telnet.rb | 95 +++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 114 insertions(+), 11 deletions(-) (limited to 'lib/oxidized/input') diff --git a/lib/oxidized/input/cli.rb b/lib/oxidized/input/cli.rb index 1d58e85..8daddac 100644 --- a/lib/oxidized/input/cli.rb +++ b/lib/oxidized/input/cli.rb @@ -8,18 +8,26 @@ module Oxidized end def get - @post_login.each { |command| cmd command } + @post_login.each { |command, block| block ? block.call : (cmd command) } d = @node.model.get disconnect d end + + def disconnect_cli + @pre_logout.each { |command, block| block ? block.call : (cmd command) } + end - def post_login _post_login - @post_login << _post_login unless @exec + def post_login _post_login=nil, &block + unless @exec + @post_login << [_post_login, block] + end end - def pre_logout _pre_logout - @pre_logout << _pre_logout unless @exec + def pre_logout _pre_logout=nil, &block + unless @exec + @pre_logout << [_pre_logout, block] + end end end end diff --git a/lib/oxidized/input/ssh.rb b/lib/oxidized/input/ssh.rb index 3471eea..3d14a57 100644 --- a/lib/oxidized/input/ssh.rb +++ b/lib/oxidized/input/ssh.rb @@ -15,7 +15,7 @@ module Oxidized rescue Timeout::Error, Net::SSH::Disconnect, Errno::ECONNREFUSED return false end - @ses = open_shell @ssh unless @exec + open_shell @ssh unless @exec not @ssh.closed? end @@ -28,11 +28,15 @@ module Oxidized end end + def send data + @ses.send_data data + end + private def disconnect begin - @pre_logout.each { |command| cmd command } + disconnect_cli @ssh.loop @ssh.close if not @ssh.closed? rescue Net::SSH::Disconnect @@ -40,9 +44,10 @@ module Oxidized end def open_shell ssh - ses = ssh.open_channel do |ch| + @ses = ssh.open_channel do |ch| ch.on_data do |ch, data| @output << data + @output = @node.model.expects @output end ch.request_pty do |ch, success| raise NoShell, "Can't get PTY" unless success @@ -52,7 +57,6 @@ module Oxidized end end expect @node.prompt - ses end def exec state=nil diff --git a/lib/oxidized/input/telnet.rb b/lib/oxidized/input/telnet.rb index 0ec08f9..c1afde2 100644 --- a/lib/oxidized/input/telnet.rb +++ b/lib/oxidized/input/telnet.rb @@ -10,7 +10,8 @@ module Oxidized @timeout = CFG.timeout @node.model.cfg['telnet'].each { |cb| instance_exec &cb } begin - @telnet = Net::Telnet.new 'Host' => @node.ip, 'Waittime' => @timeout + @telnet = Net::Telnet.new 'Host' => @node.ip, 'Waittime' => @timeout, + 'Model' => @node.model expect username @telnet.puts @node.auth[:username] expect password @@ -32,6 +33,10 @@ module Oxidized end end + def send data + @telnet.write data + end + private def expect re @@ -40,7 +45,7 @@ module Oxidized def disconnect begin - @pre_logout.each { |command| cmd(command, nil) } + disconnect_cli @telnet.close rescue Errno::ECONNRESET end @@ -56,3 +61,89 @@ module Oxidized 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 Net::ReadTimeout, "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 -- cgit v1.2.1