9

I have the following script which SSH's into a network server and executes some commands, for some reason the SSH connection opens but by the time the commands are executed it closes (I think), as a result the commands are failing with below error? Can anyone provide info how to make the SSH connection persistent?

#!/usr/bin/python
import os
import sys
import json
import fileinput
import pwd
from subprocess import Popen, PIPE, STDOUT
import re
import paramiko
import MySQLdb

resource = r'qca-cdit-01'
ssh = paramiko.SSHClient()
ssh.load_system_host_keys()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(resource, username='username', password='passwordname')
#chan = ssh.get_transport().open_session()
chan = ssh.invoke_shell()
chan.get_pty()

commandstringlist = \
    ['/local/mnt/workspace/LA_host_builds/AU_LINUX_ANDROID_LA.BF64.1.2.1_RB2.05.01.01.081.031_msm8992',
     'cd frameworks/base',
     'git fetch ssh://cdit@review-android.company.com:29418/platform/frameworks/base refs/changes/21/1260821/2 && git cherry-pick FETCH_HEAD']
for cmd_val in commandstringlist:
    #chan.exec_command(cmd_val)
    chan.send(cmd_val)
    print(chan.recv(1024))

error:

Traceback (most recent call last):
  File "ssh_test.py", line 21, in <module>
    chan.get_pty()
  File "/usr/local/lib/python2.7/dist-packages/paramiko/channel.py", line 60, in _check
    return func(self, *args, **kwds)
  File "/usr/local/lib/python2.7/dist-packages/paramiko/channel.py", line 177, in get_pty
    self._wait_for_event()
  File "/usr/local/lib/python2.7/dist-packages/paramiko/channel.py", line 1086, in _wait_for_event
    raise e
paramiko.ssh_exception.SSHException: Channel closed
Jay Kominek
  • 8,674
  • 1
  • 34
  • 51
carte blanche
  • 10,796
  • 14
  • 46
  • 65
  • Why not try use to Ansible to execute command remotely? – DXM May 22 '15 at 18:38
  • Ansible is a deployment tool. It uses ssh to connect to remote machine and perform operation, such as executing custom command and scripts, installing software packages, etc. Unlike chef and puppet, it is agentless, meaning you don't have to install anything on the remove machine, as long as your have ssh access. http://www.ansible.com/home – DXM May 23 '15 at 00:31
  • @user2125827 : It seems it is working when I simply remove`chan.get_pty()`from you code. – user2284570 May 25 '15 at 17:35

4 Answers4

2

Every command you execute using exec_command has a channel of its own and therefore a context of its own. That context includes the working directory. You change the working directory in one context and then try to use it in another. Instead, use the same channel for all the commands. You can either open a channel and use it, or just issue all the commands at once.

commandstringlist = ['cd /local/mnt/workspace/test2 && cd data/log && git fetch ssh://username@review-android.company.com:29418/platform/data/log refs/changes/21/1260821/2 && git cherry-pick FETCH_HEAD']

Here are a few other questions that should explain this in more detail.

https://unix.stackexchange.com/questions/80821/why-does-cd-command-not-work-via-ssh https://superuser.com/questions/46851/keeping-working-directory-across-ssh https://stackoverflow.com/a/6770272/492773

Community
  • 1
  • 1
kichik
  • 33,220
  • 7
  • 94
  • 114
  • am looking for a way where ssh connection is open and I have the flexibility to run mulitple commands independently... – carte blanche May 04 '15 at 18:39
  • 1
    You can run as many commands we you want. The connection doesn't close, only the channel. Adding `cd` to each command you run would be the simplest solution. You can also open a channel or use `invoke_shell()` to get the exact same features you get when you use `ssh` from command line. – kichik May 04 '15 at 18:44
  • am not clear when you said `Adding cd to each command you run would be the simplest solution` ,can you explain a bit more? also,can you give an example for `You can also open a channel or use invoke_shell() to get the exact same features` – carte blanche May 04 '15 at 18:50
  • Look at the new `commandstringlist` I posted. It has `['cd ... && command']` instead of `['cd ...', 'command']`. – kichik May 04 '15 at 18:51
  • @ kichikk - the problem is its just not these commands,i have a lot of commands to execute once the ssh connection is open,its kind of difficult to to keep adding `&&` – carte blanche May 04 '15 at 19:20
  • @user2125827: for opening a channel, you could try the solution that was posted [here](http://stackoverflow.com/a/7613010/2646573) in response to a similar question. – Erlend Graff May 18 '15 at 21:47
  • @ErlendGraff - using the channel ,will I be able to execute back to back commands,like `chan.exec_command('cd dir/path')` `chan.exec_command(`git cherry-pick)`,I want to be able to send many consecutive commands – carte blanche May 18 '15 at 21:52
  • @user2125827: If you do `chan = ssh.invoke_shell()` instead of `chan = ssh.get_transport().open_session()` and `chan.get_pty()`, then you should be able to execute commands back-to-back using `chan.send()` and `chan.recv()` instead of `chan.exec_command()`. Note: you may have to send explicit newlines, such as `chan.send('cd somefolder\n')`, `chan.send('ls\n')`. – Erlend Graff May 18 '15 at 22:08
  • @ErlendGraff - I updated the latest code and latest error I get,can you help me how to overcome this?where should I exactly use chan.send() and chand.recv() – carte blanche May 18 '15 at 22:12
  • @ErlendGraff --> updated again with `chan.invoke_shell()` and running into same error – carte blanche May 18 '15 at 22:17
  • @user2125827: If you use `invoke_shell()`, try to remove the call to `chan.get_pty()`. – Erlend Graff May 18 '15 at 22:18
  • @ErlendGraff --> how do I send chan.send(cmd_val) this like a new line?I dont think the commands are working as the git command is not cherrypicked – carte blanche May 18 '15 at 22:20
  • @user2125827: couldn't you just do `chan.send(cmd_val + '\n')`? – Erlend Graff May 18 '15 at 22:31
  • @ErlendGraff - I did try that but I dont think the commands are getting executed as I dont see the git command being successful.. – carte blanche May 18 '15 at 22:38
  • @ErlendGraff -- your suggestions are the closest i got to my answer,any reason we can debug why these commands are not working?I dont get any error – carte blanche May 18 '15 at 23:18
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/78141/discussion-between-user2125827-and-erlend-graff). – carte blanche May 19 '15 at 01:03
2

The trick is to change to the directory before executing the command. You can easily integrate it into your execute_command:

def execute_command (cmd, pwd=None):
    if pwd:
        cmd = 'cd "%s";%s' % (pwd, cmd)
    print cmd
    si,so,se = ssh.exec_command(cmd) 
    print os.getcwd()
    print "printing so"
    soreadList = so.readlines()
    print soreadList
    print "printing se"
    errList = se.readlines()
    print errList

The command is run by a shell on the remote machine, so any shell tricks like setting environment variables could be added also.

tdelaney
  • 73,364
  • 6
  • 83
  • 116
0

Can anyone provide info how to make the SSH connection persistent?

The following is an example of how to make an SSH connection persistent by attempting to execute a command and then falling back on connecting then executing the command when errors occur.

import paramiko

class sshConnection:
  def __init__( self, host, un, pw ):
    self.host = host
    self.un = un
    self.pw = pw

  def connect( self ):
    self.ssh = paramiko.SSHClient()
    self.ssh.load_system_host_keys()
    self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    self.ssh.connect(self.host, username=self.un, password=self.pw)

  def cmd ( self, cmd, tries=0 ):
    self.si, self.so, self.se = None, None, None
    try:
      self.si, self.so, self.se= self.ssh.exec_command(cmd)
    except Exception, e:
      if tries > 3:
        raise
      self.connect( )
      self.cmd(  cmd, tries+1 )
    return self.si, self.so, self.se



conn = sshConnection( "host","username","password" )

si, out, err = conn.cmd("ls -al")
print "\n".join(map ( lambda x: x.strip(), out.readlines()) )
Clarus
  • 2,259
  • 16
  • 27
  • I want to be able to run consective commands,i tried `si, out, err = conn.cmd("cd /local/mnt/workspace/LA_host_builds/AU_LINUX_ANDROID_LA.BF64.1.2.1_RB2.05.01.01.081.031_msm8992") print "\n".join(map ( lambda x: x.strip(), out.readlines()) ) si, out, err = conn.cmd("cd frameworks/base") print "\n".join(map ( lambda x: x.strip(), out.readlines()) ) si, out, err = conn.cmd("git fetch ssh://cdit@review-android.quicinc.com:29418/platform/frameworks/base refs/changes/21/1260821/2 && git cherry-pick FETCH_HEAD") print "\n".join(map ( lambda x: x.strip(), out.readlines()) )` – carte blanche May 18 '15 at 23:01
  • What sort of error are you getting? This code has been tested and for the most part errors should be delivered as they are received. – Clarus May 18 '15 at 23:48
  • there is no error,its just that your code wont work for back-back to commands – carte blanche May 18 '15 at 23:59
  • back-to-back commands should work fine. You should check both stdout and stderr for errors. Also "cd foo", will lose state, so a better approach would be .cmd("cd foo && ls && echo bar") – Clarus May 19 '15 at 00:09
  • 1
    the whole purpose of this dicussion is to keep the state` "cd foo" `and avoid doing `cmd("cd foo && ls && echo bar") ` – carte blanche May 19 '15 at 00:11
  • If you use paramiko you are stuck with the interface it provides. – Clarus May 19 '15 at 00:43
0

Simply do :

#!/usr/bin/python
import os
import sys
import json
import fileinput
import pwd
from subprocess import Popen, PIPE, STDOUT
import re
import paramiko
import MySQLdb

resource = r'qca-cdit-01'
ssh = paramiko.SSHClient()
ssh.load_system_host_keys()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(resource, username='username', password='passwordname')
#chan = ssh.get_transport().open_session()
chan = ssh.invoke_shell()

commandstringlist = \
    ['\n/local/mnt/workspace/LA_host_builds/AU_LINUX_ANDROID_LA.BF64.1.2.1_RB2.05.01.01.081.031_msm8992\n',
     '\ncd frameworks/base\n',
     '\ngit fetch ssh://cdit@review-android.company.com:29418/platform/frameworks/base refs/changes/21/1260821/2 && git cherry-pick FETCH_HEAD\n']
for cmd_val in commandstringlist:
    #chan.exec_command(cmd_val)
    chan.send(cmd_val)
    print(chan.recv(8192))

which worked for me. (You forgot .send() doesn’t add lines automatically)

user2284570
  • 2,891
  • 3
  • 26
  • 74