Ruby Net::Telnet waitfor() doesnt work - ruby

i have check out many different ways to fix this and it might be specific to the enviroment.
i'm creating a script in Ruby to telnet into a remote server, login, enter the shell type (its a prompt after logging in and has no "prompt" in front) and then entering a userid at the next prompt. currently it gets logged in via user and pass, but freezes at the term part.
i have tried to use regex, strings, sleep and then just puts("xterm" but nothing allows it to go past this prompt.
here is a snippet of code that is getting hung up and the output to go along:
$telnet = Net::Telnet::new("Host" => 'hostname',
"Output_log" => 'output.txt',
"Dump_log" => 'Dump.txt',
"Binmode" => false,
"Timeout" => false,
"Prompt" => /.* $ $/) { |c| print c }
$telnet.login("user", "pass") { |c| print c }
$telnet.waitfor("TERM = (xterm) ")
$telnet.puts("xterm") { |c| print c }
$telnet.waitfor(Match => /Enter\s*$/) { |c| print c }
$telnet.puts("userid")
the output is as follows:
HP-UX ds107492 B.11.11 U 9000/800 (tm)
login: user
Password:
Please wait...checking for disk quotas
. i
.[ci
.*s1^i
See /etc/copyright for copyright notices
You have mail.
'You have mail.'
TERM = (xterm)

$s.waitfor("Match" => /TERM\s*/){ |c| print c }
$s.print("xterm\n"){ |c| print c }
seems to be the answer, it allowed me to go past the prompt. still not sure what the difference between this regex and all the others i tried are :/

Related

How to allow users to edit given string via $stdin in ruby

I'm searching to allows users to edit an existing string.
Edit the following string: Edit me
# After user delete and add characters
Edit the following string: Edit you
I thought to prepend some data to the $stdin but seems like it's not possible and anyway IMHO it's a too radical solution.
Someone told me to use GNU Readline's Ruby wrapper so I've taken a quick look and I found Readline#pre_input_hook which acts before Readline start taking the input.
I tried:
require 'readline'
Readline.pre_input_hook = -> { "Edit me" }
result = Readline.readline("Edit the following string: ")
puts result
But seems not work.
begin
system("stty raw -echo")
print (acc = "Edit me: ")
loop.each_with_object(acc) do |_,acc|
sym = $stdin.getc
case sym.ord
when 13 # carriage return
break acc
when 127 # backspace
print "\e[1D \e[1D"
acc.slice!(acc.length - 1) if acc.length > 0
else # regular symbol
print sym
acc << sym
end
end
ensure
system("stty -raw echo")
puts
puts "\e[0mEntered: |#{acc}|"
end
Here you go. More info on terminal control sequences. Also, ANSI terminal codes.
I found prompt.ask from tty-prompt fulfilled my need:
$ gem install tty-prompt
$ irb
irb(main):001:0> require "tty-prompt"
=> true
irb(main):002:0> prompt = TTY::Prompt.new
=> #<TTY::Prompt prefix="" quiet=false enabled_color=nil active_color=:green
error_color=:red help_color=:bright_black input=#<IO:<ST...
irb(main):003:0> prompt.ask("What is your name?", default: ENV["USER"])
What is your name? xxx
=> "xxx"
irb(main):004:0> prompt.ask("What is your name?", value: "Mike")
What is your name? Michael
=> "Michael"

Programmatically Loading Libraries with a Ruby REPL

I've been trying to get load project libraries in a script, then start either IRB or ripl to allow the user to have quick interaction with project libraries. I've succeeded in doing this.
My issue is programmatically turning off command-line echo.
Manually, this is easy with a call to irb_context.echo = false, but this doesn't work programmatically since control is handed over to IRB at IRB.start and irb_context isn't available before the call to IRB.start
If you want IRB to write nothing when the user gives input, you can set the info manually:
IRB.conf[:PROMPT][:NO_REPLY] = {
:PROMPT_I => "%N(%m):%03n:%i> ",
:PROMPT_S => "%N(%m):%03n:%i%l ",
:PROMPT_C => "%N(%m):%03n:%i* ",
:RETURN => "\n" # used to printf
}
IRB.conf[:PROMPT_MODE] = :NO_REPLY
This way, the return value will be ignored by the REPL, that will print just a newline. For more info, see the official docs.

Ruby script pucking on new line in a file

If I only have one host in the file the scripts does as intended. As soon as I add another address in the file I get this error. I understand that it does not like the new line character at the end of the first host in the file, how is this problem alleviated? I'm basically looking for the script to run down the hostfile, and for every address or host-name in the file, run the session.
'initialize': newline at the end of hostname
File.read('hostfile').each_line do |hostname|
session = Net::SSH.start(hostname, #username, :password => #password, :encryption => 'aes256-cbc', :host_key => 'ssh-rsa')
cmd_session = Net::SSH::Telnet.new('Session' => session)
cmd_session.cmd("en\r#{#enable}")
cmd_session.cmd('terminal pager 0')
cmd_session.cmd('show threat-detection statistics') { |c| print c }
cmd_session.close
end
session = Net::SSH.start(hostname.strip, #username.....
Should get you going. For further reference see the docs

Ruby and telnet: waitfor doesnt work

I need to login in a server with Ruby and Telnet and execute a few commands. My actual script is:
tn = Net::Telnet::new("Host" => "#{ip}", "Port" => 23, "Timeout" => 60,
"Output_log"=>"output_log.log",
"Dump_log"=> "dump_log.log",
"Prompt" => /[#]/ )
tn.cmd("#{USER}\n#{PASS}") { |c| print c }
puts tn.cmd("Conf")
tn.waitfor(/config/) { |str| puts str }
puts tn.cmd("Int fa23")
puts tn.cmd("Shut")
puts tn.cmd("No shut")
puts tn.cmd("Exit")
tn.close
I must only execute the second command (Int fa23) after the string "config" is found at the output. The problem is that waitfor is not working. Here is the output_log:
Trying XX.XX.XX.XX...
Connected to XX.XX.XX.XX.
User Name:username
Password:*************
BOT-SWT-VSAT-AL-...#Conf
BOT-SWT-VSAT-AL-...(config)#
The script stops with waitfor time out error. What am I doing wrong?
Please add the waitfor right after Net::Telnet::new
You should wait for the connection to get established (which happens when creating a Telnet instance) and then wait for the server to respond back every time before sending the next command.
localhost = Net::Telnet::new("Host" => "*****",
"Port" => ***,
"Timeout" => 10,
"Prompt" => /[$%#>] \z/n)
localhost.waitfor(/USER/) {
localhost.cmd("****") {
localhost.waitfor(/PASS/) {
|c| print c
# your next commands
...
# localhost.close
}
}
}
The key here is to make sure that you have received all the packets from the server (until EOF is received) before responding back. Then sometimes, even consuming/waiting for a space character might count (depending to how your the telnet server is designed).
Then, make sure to set the right regular expression for your match.

ruby output format

I have the following code, which works fine. The problem is that I get the following output:
Device ID: SEP1C17D3415659
IP address: 10.2.3.101
I would like to get:
SEP1C17D3415659 10.2.3.101
Thanks for your help
require 'net/telnet'
require '/apps/dateformat'
#tdate = Formatdate.new.mydate
hosts = %w"SW3"
hosts.each do |hostname|
tn = Net::Telnet::new("Host" => "#{hostname}",
"Timeout" => 10000,
"Prompt" => /[$%#>] \z/n)
tn.cmd('String' =>'user' , 'Match'=>/Password:/) { |c| puts c }
tn.cmd('String' =>'password', 'Match'=>/#/) { |c| puts c }
tn.cmd('String' =>'terminal length 0', 'Match'=>/#/) { |c| puts c }
File.open("/agents/#{hostname}-#{#tdate}-agents.txt",'w') do |o|
run=tn.cmd('String' =>'sh cd ne de | inc Device ID: | IP address:', 'Match'=>/#/) { |c| puts c }
run.each_line do |re|
mac = re.match /Device ID: ([\S]+)/
#ip = re.match /([\S]+) address/
ip = re.match /IP address: ([\S]+)/
o.puts mac
o.puts ip
end
end
end
puts automatically prints a new line after each output. Simply put both on the same line.
outputString = mac + " " + ip
o.puts outputString
If you have other output functions available such as print, it will function without putting a newline between the two statements.
You're correctly specify the grouping you want for your regular expression, but you're not using it when printing it out.
Your regular expression is:
mac = re.match /Device ID: ([\S]+)/
and this matches the whole line Device ID: SEP1C17D3415659, putting the part you want (SEP1C17D3415659) into a group so you can get at it later. However you then print it out using
o.puts mac
This gives the whole match, not just the group. What you want is this instead:
o.puts mac[1]
which specifies just the group.
Also, as Cody says, puts adds a newline on each call. If you want both matches to print on the same line try something like:
o.print "#{mac[1]} #{ip[1]}\n"
instead.
Edit:
I'm not familiar with net/telnet or the command you're running, but it looks like you're running a certain command, then filtering for certain lines and then printing some info from those lines. If this is the case, then any lines that don't match will give you nil for mac and ip so you'll get "undefind method []" on those lines.
If this is the case you can simply use:
o.print "#{mac[1]} #{ip[1]}\n" unless mac.nil?
You might find it worth while to restructure your code slightly to better express what you're doing, by combining the two regular expressions into one. Without knowing the structure of the actual lines I can't create the real expression you'll need, but it would look something like this:
run.each_line do |line|
match = line.match /^Device ID: ([\S]+), IP address: ([\S]+)$/ #these are the lines we want
next unless match #skip lines we don't want
o.print "#{match[1]} #{match[2]}\n" #print the details we're interested in
end

Resources