ssh.exec_command("shutdown -h 17:00 &") - python

I have a Python Paramiko script that sends commands to remote hosts on out intranet. There are times when I would like to send the shutdown command to several hosts at once. The issue is that the shutdown command simply sits and waits unless you background it. I have tried using the ampersand (bare as above, or escaped: \&). Here is a small test program. My os is RHEL Linux 5.9 (Python 2.4.3). Note that the sudoers disables requiretty for some users.
#!/usr/bin/python
import paramiko
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect("<hostname>",username="<my username>", password="<mypassword>")
stdin, stdout, stderr = ssh.exec_command("sudo /sbin/shutdown -h 17:00 \&")
stdin.write('\n')stdin.flush()
data = stdout.read().splitlines()
for line in data:
print line

I have solved the issue using the shutdown command as it is intended. First do not escape the ampersand (\&). Since the shutdown command does not return anything to stdout, I just eliminate those lines dealing with the output. The reason for wanting to use shutdown with a time is for user notification.
#!/usr/bin/python
import paramiko
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect("<hostname>",username="<my username>", password="<mypassword>")
stdin, stdout, stderr = ssh.exec_command("sudo /sbin/shutdown -h 17:00 &")]
ssh.close()

Related

open a putty window and run ssh commands - Python

I am new to python. I need to login to a server daily (Desktop -> 1.32 -> 0.20 -> 3.26). For this I need to open putty and using ssh connection i am logging in. To do all this I want to write a script using python.
By using google I thought subprocess.Popen will do that. But Its not working fine.
1st trail:
import subprocess
pid = subprocess.Popen("putty.exe user#xxx.xx.x.32 -pw password").pid
Its working fine (Opening window logging into .32). But cant able to give input. I came to know that to give input for the same process we need to use pipes.
2nd trail:
from subprocess import Popen, PIPE, STDOUT
p = Popen("putty.exe user#xxx.xx.x.32 -pw password", stdout=PIPE, stdin=PIPE, stderr=STDOUT)
grep_stdout = p.communicate(input=b'ssh xx.xx.x.20\n')[0]
print(grep_stdout.decode())
by using this i cant login for the first server also. After logging in to all servers I need the terminal as alive. how to do this???
Edit
I need to do this in a new putty window. After logging in dont close the window. I have some manual work to do.
use powershell to call putty in order to open a new window
from subprocess import Popen
Popen("powershell putty.exe user#host -pw mypassword")
Use paramiko library python
Establish a SSH connection using -
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(hostname,username, password)
Check the status if connection is alive using -
status = ssh.get_transport().is_active()
#returns True if connection is alive/active
ssh.exec_command() is basically a single session. Use exec_command(command1;command2) to execute multiple commands in one session
Also, you can use this to execute multiple commands in single session
channel = ssh.invoke_shell()
stdin = channel.makefile('wb')
stdout = channel.makefile('rb')
stdin.write('''
Command 1
Command 2
''')
print stdout.read()
There is a SSHv2 protocol implementation for python: http://www.paramiko.org/. You can easily install it with pip:
pip install paramiko
Then you can create ssh client, connect to your host and execute commands:
import paramiko
ssh_client = paramiko.SSHClient()
ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh_client.connect('hostname', username='login', password='pwd')
stdin, stdout, stderr = ssh_client.exec_command('command')
I created a bat file on windows, which references putty and putty session-specific info. This bat file can run by itself on windows. To call from python, I used the subprocess.run() -- python 3.5+.
Example of bat file named putty.bat:
start c:\app\PuTTy\putty.exe -load 192.168.1.230-node1-logs -l <logon user> -pw <logon user password for putty session>
Breaking down the bat file:
It begins with window's command "start".
c:\app\PuTTy\putty.exe --> is the putty directory on Windows containing putty.exe.
-load --> tells putty to load a putty profile. The profile is the name you see on the putty client, under "Saved Sessions".
192.168.1.230-node1-logs --> my putty session specific profile.
-l for logon --> followed by the putty logon user.
-pw is the logon password --> followed by the putty logon password.
That concludes the contents of "putty.bat".
From within python, is used the subprocess.run() command.
Example:
import subprocess
...
...
try:
process = subprocess.run(["putty.bat"], check=True, stdout=subprocess.PIPE, universal_newlines=True)
print(process.stdout)
except Exception as e:
print("subprocess call error in open putty command")
print(str(e))
I hope you find this helpful

How to wait for a process that has been started on a remote computer using ssh in a script?

I have a script that, in a specific case is required to run a very time-consuming script on a remote computer. I currently do this with:
if specific_case:
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect('10.0.0.1', username='username', password='password')
stdin, stdout, stderr = client.exec_command('tmux new-session -d -s session_name \'python /home/username/flamethrower.py'\'')
Now - so far so good.. How could I wait for python /home/username/flamethrower.py in the script that initially started it and possibly have a minimalistic feedback?
The computer running the script that starts flamethrower.py on the remote computer is a windows 8.1, the computer where flamethrower.py is run, is a Debian wheezy.
pseudo coded:
[...] # as above
while not stdin, stdout, stderr = client.exec_command('tmux new-session -d -s session_name \'python /home/username/flamethrower.py'\''):
#while it's not done, print a dot every minute
sys.stdout.print('.')
sys.flush()
sleep(60)
Have your remote script output to some log file, including when finished, then poll the remote connection every so often for the finish output, (and fetch the remaining output).

Executing reboot command over SSH using Paramiko

I use Paramiko for establishing SSH connection with some target device and I want to execute reboot command.
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(zip_hostname, username=username, password=password, timeout=1)
try:
stdin, stdout, stderr = ssh.exec_command("/sbin/reboot -f")
# .........
# some code
# .........
except AuthenticationException, e:
print ''
finally:
ssh.close()
But after executing ssh.exec_command("/sbin/reboot -f") "some code" does not execute because program is stuck in exec_command (the disconnection takes place caused by rebooting). What should I do to solve my problem?
Try this:
ssh.exec_command("/sbin/reboot -f > /dev/null 2>&1 &")
All the output of reboot is redirected to /dev/null to make it produce no output and it is started in the background thanks to the '&' sign in the end. Hopefully the program won't hang on that line this way, because the remote shell gives the prompt back.
Get the transport from the ssh and set the keepalive using:
transport = ssh.get_transport()
transport.set_keepalive(5)
This sets the keepalive to 5 seconds; mind you I would have expected the timeout=1 to have achieved the same thing.
All you need to do is to call channel.exec_command() instead of the high-level interface client.exec_command()
# exec fire and forget
timeout=0.5
transport = ssh.get_transport()
chan = ssh.get_transport().open_session(timeout=timeout)
chan.settimeout(timeout)
try:
chan.exec_command(command)
except socket.timeout:
pass
I was having this issue and managed to avoid it by switching to this command:
/sbin/shutdown -r now
Note this command does not result in any STDOUT or STDERR output
In case you or anyone else gets stuck trying to reboot host with sudo using forwarding agents (ssh keys) or in my case (yubikey)
If you look at this as bash you would reboot a host as non root user like this.
ssh -t -A user#hostname sudo /sbin/reboot
For the -A flag, from ssh man page
Enables forwarding of the authentication agent connection. This can also be specified on a per-host basis in a
configuration file.
Agent forwarding should be enabled with caution. Users with the ability to bypass file permissions on the
remote host (for the agent’s Unix-domain socket) can access the local agent through the forwarded connection.
An attacker cannot obtain key material from the agent, however they can perform operations on the keys that
enable them to authenticate using the identities loaded into the agent.*
For the -t flag, from ssh man page
Force pseudo-tty allocation. This can be used to execute arbitrary screen-based programs on a remote machine,
which can be very useful, e.g. when implementing menu services. Multiple -t options force tty allocation, even
if ssh has no local tty.*
So lets break this down into how you would do this in paramiko
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(hostname=host, username=username)
s = ssh.get_transport().open_session()
paramiko.agent.AgentRequestHandler(s)
ssh.exec_command("sudo /sbin/reboot", get_pty=True)
For authentication forwarding (-A flag in bash ssh command) for paramiko
ssh = paramiko.SSHClient() #'ssh' is client variable
s = ssh.get_transport().open_session() #get 'ssh' transport and open sessions assigned to 's' variable
paramiko.agent.AgentRequestHandler(s) #call in 's' to the forwarding agent for current ssh session
Now for force pseudo-tty allocation (-t flag in bash ssh command) for paramiko
ssh.exec_command("sudo /sbin/reboot", get_pty=True)
Adding 'get_pty=True' to exec_command will allow you execute sudo /sbin/reboot
Hope this helps, everyone's environments are different but this should work as it the exact same thing as if you ran it as bash.

Running process of remote SSH server in the background using Python Paramiko

I am finding hard to run a process on a remote SSH server at background using Paramiko. I used :
stdin, stdout, stderr = ssh.exec_command('executefile.py &')
and found that no process of executefile.py was found running.
Then I tried using other way as including a backward slash:
stdin, stdout, stderr = ssh.exec_command('executefile.py \&')
This method worked. There was an instance running on machine but no surprise, it was not running at background. I could come to know as it is not running at background as when code stuck at second line after this code. It was
all_inf = stdout.readlines()
Now code was not going beyond above line unless the process of the script was killed.
I am learning Paramiko, any help is appreciated.
I've tried all the methods described here and here without success, and finally realized that you need to use channels instead of using the SSHClient directly for calling exec_command (this does not work in background):
client = paramiko.SSHClient()
client.connect(ip_address, username='root', pkey=paramiko_key, timeout=5)
client.exec_command('python script.py > /dev/null 2>&1 &')
You should create and use a channel, this works in background:
client = paramiko.SSHClient()
client.connect(ip_address, username='root', pkey=paramiko_key, timeout=5)
transport = client.get_transport()
channel = transport.open_session()
channel.exec_command('python script.py > /dev/null 2>&1 &')
So nohup, dtach, screen, etc, are actually not necessary.
You can try:
stdin, stdout, stderr = ssh.exec_command('nohup python executefile.py >/dev/null 2>&1 &')
exec_command isn't executing the command in an interactive shell, so "running a process in the background" doesn't really make sense.
If you really want to do this, you could use the command nohup to start your process, and keep it alive when the session exits. Remember that you can't get stdin, stdout, or stderr when you do this, since you are detaching the process from the shell, so redirect them accordingly.
I tried transport class and it was really great. Here's the code I used:
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(hostname = "host_ip", username = "un"], password = "up")
channel = ssh.get_transport().open_session()
pty = channel.get_pty()
shell = ssh.invoke_shell()
shell.send("cd /my/directory/; nohup ./exec_name > /dev/null 2>&1 &\n")
But I still don't know how to kill it using python scripts; I have an open question about it here.
EDIT 1:
I have solved my problem about killing the process somehow; you can check it.
You could try using screen
screen -d -m ping 8.8.8.8
This is would start a screen and ping 8.8.8.8. You can view this screen by using
screen -ls
and attach using
screen -D <<screen_name>>
Note that the screen will terminate after the command has finished executing.

Paramiko and Pseudo-tty Allocation

I'm trying to use Paramiko to connect to a remote host and execute a number of text file substitutions.
i, o, e = client.exec_command("perl -p -i -e 's/" + initial + "/"
+ replaced + "/g'" + conf);
Some of these commands need to be run as sudo, which results in:
sudo: sorry, you must have a tty to
run sudo
I can force pseudo-tty allocation with the -t switch and ssh.
Is it possible to do the same thing using paramiko?
Actually it's quite simple. Just:
stdin, stdout, stderr = client.exec_command(command, get_pty=True)
The following code works for me:
#!/usr/bin/env python
import paramiko
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect('localhost',username='root',password='secret')
chan = ssh.get_transport().open_session()
chan.get_pty()
chan.exec_command('tty')
print(chan.recv(1024))
This was just assembled from looking at a few examples online... not sure if its the "right" way.
I think you want the invoke_shell method of the SSHClient object (I'd love to give a URL but the paramiko docs at lag.net are frame-heavy and just won't show me a specific URL for a given spot in the docs) -- it gives you a Channel, on which you can do exec_command and the like, but does that through a pseudo-terminal (complete with terminal type and numbers of rows and columns!-) which seems to be what you're asking for.
According to the sudo manpage:
The -S (stdin) option causes sudo to read the password from the standard input instead of the terminal device. The
password must be followed by a newline character.
You can write to the stdin because it is a file object with write():
import paramiko
client = paramiko.client.SSHClient()
client.set_missing_host_key_policy(paramiko.client.AutoAddPolicy())
client.connect(hostname='localhost', port=22, username='user', password='password')
stdin, stdout, stderr = client.exec_command('sudo -S aptitude update')
stdin.write('password\n')
stdin.flush()
# print the results
print stdout.read()
client.close()

Categories

Resources