expect usage after ssh connection - expect

I am able to use expect to connect automatically with SSH. However, I have a lot of shell commands and shell conditions to be executed or tested after the connection. I am wondering how I can deal with this situation. It looks like putting the shell commands in the expect script and use send is not a good option. Are there any other options?

In Perl you can use Net::SSH2, Net::OpenSSH or even the old Net::SSH::Perl
use Net::OpenSSH;
my $ssh = Net::OpenSSH->new('host.example.com', user => 'jsmith', password => 'jsmith2011');
$ssh->error and die "Unable to connect to remote host: " . $ssh->error;
$ssh->system("ls /etc");
my $output = $ssh->capture("rgrep hello /usr/share/doc");
$ssh->scp_get("/var/log/messages", "/tmp/remote_log");
# ...

Related

Writing Automated scripts to configure device

My requirement is like this:
I need to log in to a remote device (say Router/switch) and execute following commands.
telnet xx.xx.xx.xx
//give password here
sys
interface g x/x/x
shut
desc free-port
exit
There are Hundreds of devices for which I cannot waste time doing above damn thing 100 times. I need to write a automated script which does it. so My questions are as follows:
I use Windows system, so What is the best scripting language to be used : Ruby / shell script / perl ? (I was formerly ROR Developer, so i know Ruby, Linux terminal. Now I am working in networking domain. )
What I thought was : Put all Devices into an array and using for loop, call devices one by one and execute above said commands.
I don't have knowledge of scripting, so please guide me further. I don't know where to start from.
Step 1: decide the file structure of your program.
For example, this is the simplest structure
if_admin/
|--config.yml
|--run.rb
Step 2: write a config file or a bunch of config files that contain the different parts of the commands you need to run on the targets.
For example, you can use a yaml file like this:
xx.xx.xx.xx:
password: s3cret
router-shelf: x
slot: x
port: x
yy.yy.yy.yy:
...
Step 3: implement what you want to do
require 'yaml'
require 'net/telnet'
config = YAML.load_file('./config.yml')
config.each do |host, conf|
telnet = Net::Telnet.new('Host' => host)
telnet.login(conf['password'])
telnet.puts <<-CMD
sys
interface g #{conf['router-shelf']}/#{conf['slot']}/#{conf['port']}
shut
desc free-port
CMD
telnet.close
end
If you can use expect script , you are in luck.
#!/usr/bin/expect
set timeout 60
set cmds [list "ssh host1 ..." "ssh host2 ..." "ssh host3 ..."]
foreach cmd $cmds {
spawn -noecho bash -c $cmd
expect {
-re "password" {
exp_send "$env(PASS_WORD)\"
exp_continue
}
eof { wait } ; # at this time the last spawn'ed process has exited
}
}
Here is the rough idea of above script :-
set cmds [list.... will be used as list to store set of commands.
foreach will iterate though those commands
spawn will spawn process for each of the command. you can write multiple command with single telnet in bash, just break down commands using \ (backslash) so it is easily readable and extendable.
expect block will pass password whenever it encounter certain regex.
eof will wait till all commands in spawn process are finish.
set timeout -1 will keep loop running. i think default time for expect script is 10secs.
You can create one more foreach loop for host-list.
I think this will be enough to get you started for your automation process.
As to the question of "What is the best scripting language to be used", I would say go with one that does what you need and one that you're comfortable with using.
If you want to go with Perl, one module that you could use is Net::Telnet. Of course, you'll need Perl itself. I'd recommend using Strawberry Perl, which should already have Net::Telnet installed.
Another possible route is to use putty, which is a SSH and telnet client. You could combine that with TTY Plus, which provides an interface that uses tabs for different putty sessions. And it lets you issue commands to multiple putty sessions. This is one possibility that wouldn't involve a lot of code writing.

ssh perl script not running

I am trying to write a script that will ssh to a remote machine in perl.
I'm not sure what's wrong but when I run the script, it prompts me for the root password and ends up with blank output after I give the password.
Here's my script:
#!/usr/bin/perl
use strict;
use warnings;
my #id=`ssh expert\#x.x.x.x`;
print"#id";
That is what you have asked your program to do. This line
`ssh expert\#x.x.x.x`
Starts a new subprocess running ssh with the given parameters
Exits that subprocess and returns any text output from ssh
You presumably need to interact with the remote system once you have connected, so you either need the perl process to connect to the remote system, or you need to be able to listen and talk to the subprocess that hash connected before it exits
The first is by far the simplest solution. If you use the Net::OpenSSH module and read its documentation then you will see that you can open a connection by creating an object. You can then send commands and retrieve the output using that object's capture method
I would advice against using system ssh command inside perl code for below reasons:
It makes parsing output difficult
Error handling becomes difficult
Less programming flexibility
Rather, use a CPAN library, e.g. Net::SSH::Perl for firing SSH commands from Perl code.
Its simple to open a shell using this module as described below:
$ssh->shell
Opens up an interactive shell on the remote machine and connects it to your STDIN. This is most effective when used with a pseudo tty; otherwise you won't get a command line prompt, and it won't look much like a shell. For this reason--unless you've specifically declined one--a pty will be requested from the remote machine, even if you haven't set the use_pty argument to new (described above).
This is really only useful in an interactive program.
In addition, you'll probably want to set your terminal to raw input before calling this method. This lets Net::SSH::Perl process each character and send it off to the remote machine, as you type it.
To do so, use Term::ReadKey in your program:
use Term::ReadKey;
ReadMode('raw');
$ssh->shell;
ReadMode('restore');
Below is a quick example that demonstrates how easy it would be to use the same module to fire and parse command output:
use Net::SSH::Perl;
my $ssh = Net::SSH::Perl->new($host);
$ssh->login($user, $pass);
my($stdout, $stderr, $exit) = $ssh->cmd($cmd);
Link to Net::SSH::Perl cpan documentation: http://search.cpan.org/~schwigon/Net-SSH-Perl-1.42/lib/Net/SSH/Perl.pm
Another module which I prefer to use is Net::OpenSSH: http://search.cpan.org/~salva/Net-OpenSSH-0.70/lib/Net/OpenSSH.pm
use Net::OpenSSH;
my $ssh = Net::OpenSSH->new($host);
$ssh->error and
die "Couldn't establish SSH connection: ". $ssh->error;
$ssh->system("ls /tmp") or
die "remote command failed: " . $ssh->error;
my #ls = $ssh->capture("ls");
$ssh->error and
die "remote ls command failed: " . $ssh->error;
my ($out, $err) = $ssh->capture2("find /root");
$ssh->error and
die "remote find command failed: " . $ssh->error;
my ($rin, $pid) = $ssh->pipe_in("cat >/tmp/foo") or
die "pipe_in method failed: " . $ssh->error;
print $rin "hello\n";
close $rin;

How to enter a password into another process prompt from Ruby

I am writing an application that needs to run command on a remote Raspberry PI using a revssh script. revssh is a custom script that implements to some level the Revssh protocol concepts. it uses ssh reverse tunneling to send commands from the server to the clients.
I am using Ruby 2.1, I tried to do this using IO.popen but it does not work, so I tried the following:
# revssh (short for reverse ssh ) enables the execution of remote commands
# from the server on connected clients, like the 'psu_pi_analytics' here. but it requires
# to enter a root password each time you want to run a command using 'revssh -c'
IO.popen('revssh -c psu_pi_analytics uname -a', 'w+') do|io|
io.puts 'password' # enter the password when prompted
puts io.gets
end
this code work if the command to execute run on the local machine, but not in my case.
So any thoughts, or suggestions.
What important here is how to deal with the new connection created by the revssh script using ssh, which is managed in the terminal if the script is run directly from the terminal.
Edit:
By not work I mean it still prompts for the password, even if I puts the password to the io.
You can use an Expect-like library (e.g. RExpect, Expect4r) for interacting with other processes.
Another question related to this: Is there an Expect equivalent gem for Ruby?

how to script commands that will be executed on a device connected via ssh?

So, I've established a connection via ssh to a remote machine; and now what I would like to do is to execute few commands, grab some files and copy them back to my host machine.
I am aware that I can run
ssh user#host "command1; command2;....command_n"
and then close the connection, but how can I do the same without use the aforememtioned syntax? I have a lot of complex commands that has a bunch of quote and characters that would be a mess to escape.
Thanks!
My immediate thought is why not create a script and push it over to the remote machine to have it run locally in a text file? If you can't for whatever reason, I fiddled around with this and I think you could probably do well with a HEREDOC:
ssh -t jane#stackoverflow.com bash << 'EOF'
command 1 ...
command 2 ...
command 3 ...
EOF
and it seems to do the right thing. Play with your heredoc to keep your quotes safe, but it will get tricky. The only other thing I can offer (and I totally don't recomend this) is you could use a toy like perl to read and write to the ssh process like so:
open S, "| ssh -i ~/.ssh/host_dsa -t jane#stackoverflow.com bash";
print S "date\n"; # and so on
but this is a really crummy way to go about things. Note that you can do this in other languages.
Instead of the shell use some scripting language (Perl, Python, Ruby, etc.) and some module that takes care of the ugly work. For example:
#!/usr/bin/perl
use Net::OpenSSH;
my $ssh = Net::OpenSSH->new($host, user => $user);
$ssh->system('echo', 'Net::Open$$H', 'Quot%$', 'Th|s', '>For', 'You!');
$ssh->system({stdout_file => '/tmp/ls.out'}, 'ls');
$ssh->scp_put($local_path, $remote_path);
my $out = $ssh->capture("find /etc");
From here: Can I ssh somewhere, run some commands, and then leave myself a prompt?
The use of an expect script seems pretty straightforward... Copied from the above link for convenience, not mine, but I found it very useful.
#!/usr/bin/expect -f
spawn ssh $argv
send "export V=hello\n"
send "export W=world\n"
send "echo \$V \$W\n"
interact
I'm guessing a line like
send "scp -Cpvr someLocalFileOrDirectory you#10.10.10.10/home/you
would get you your files back...
and then:
send "exit"
would terminate the session - or you could end with interact and type in the exit yourself..

copy a txt file from Unix machine to Windows machine

I have the below shell script to copy a txt file from a Unix machine to a Windows machine in the same network:
#!/bin/sh
HOST='localhost'
USER='redacted'
PASSWD='redacted'
FILE='/los_prod/scripts/log.txt'
ftp $HOST <<END_SCRIPT
user $USER
$PASSWD
put $FILE
quit
END_SCRIPT
exit 0
It's giving the error as:
:connection failed.
I am giving all the correct info in the script. What could be the reason?
You might want to have a look here:
http://www.stratigery.com/scripting.ftp.html
It gives a simple way to do what you want.
ftp doesn't accept input from stdin, so you can't just feed the login information in like that. You'd need to use a program like expect to automate the normal ftp command-line client. A better alternative may be to use another language instead of a Bourne shell script. Something like Perl (as mentioned by #sarathi), Ruby, or Python would allow you to write a fairly small script and use the FTP libraries available to them to more easily script it.
Here's a version of your script for expect:
set host "localhost"
set user "redacted"
set pass "redacted"
set file "/los_prod/scripts/log.txt"
spawn ftp $host
expect "Name"
send "$user\n"
expect "Password:"
send "$pass\n"
expect "ftp>"
send "put $file\n"
expect "ftp>"
send "quit\n"
You'll probably have to install expect on your system; it's not as widely available as other interpreters. Any modern Linux distro will have a package for it.

Resources