Realizar comandos sobre ssh con Python

136

Estoy escribiendo un script para automatizar algunos comandos de línea de comandos en Python. Por el momento estoy haciendo llamadas así:

cmd = "some unix command"
retcode = subprocess.call(cmd,shell=True)

Sin embargo, necesito ejecutar algunos comandos en una máquina remota. Manualmente, iniciaría sesión con ssh y luego ejecutaría los comandos. ¿Cómo automatizaría esto en Python? Necesito iniciar sesión con una contraseña (conocida) en la máquina remota, así que no puedo simplemente usar cmd = ssh user@remotehost, me pregunto si hay un módulo que debería usar.

Fredley
fuente

Respuestas:

201

Te referiré a paramiko

ver esta pregunta

ssh = paramiko.SSHClient()
ssh.connect(server, username=username, password=password)
ssh_stdin, ssh_stdout, ssh_stderr = ssh.exec_command(cmd_to_execute)
shahjapan
fuente
3
La suposición aquí es que paramiko es tan seguro como (abierto) ssh. ¿Lo es?
user239558
1
¿Qué pasa si se intercambian las claves ssh?
Ardit
19
Si está utilizando claves SSH, primero prepare el archivo de claves utilizando k = paramiko.RSAKey.from_private_key_file(keyfilename)O : Y k = paramiko.DSSKey.from_private_key_file(keyfilename)ENTONCES ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())y finalmente ssh..connect(hostname=host, username=user, pkey=k).
Scott Prive
77
Como usuario de Paramiko desde hace mucho tiempo (pero no como experto), puedo sugerirle que use Paramiko, pero debe considerar sus casos de uso y cuánto está dispuesto a aprender. Paramiko es de muy bajo nivel, y puede caer fácilmente en una trampa donde crea una "función de ayuda de ejecución de comandos" sin comprender completamente el código que está utilizando. Eso significa que puede diseñar decir un def run_cmd(host, cmd):que hace lo que quiere al principio, pero sus necesidades evolucionan. Termina cambiando el ayudante para el nuevo caso de uso, que cambia el comportamiento del uso existente anterior. Planificar en consecuencia.
Scott Prive
3
Para errores de host desconocidos, haga: ssh.set_missing_host_key_policy (paramiko.AutoAddPolicy ()) antes
Nemo
49

O simplemente puede usar command.getstatusoutput :

   commands.getstatusoutput("ssh machine 1 'your script'")

Lo usé ampliamente y funciona muy bien.

En Python 2.6+, use subprocess.check_output.

powerrox
fuente
44
+1 para un método integrado simple y agradable. En mi configuración actual, no quiero agregar bibliotecas de Python, por lo que su sugerencia es valiosa, también muy directa.
Philip Kearns el
3
solo asegúrese de que su host remoto esté configurado para ssh sin contraseña, de lo contrario, debe hacer otras cosas para administrar la autenticación
powerrox
subprocess.check_output - ¡gran solución!
Tim S.
2
@powerrox, ¿cuáles son esas "otras cosas"?
ealeon
2
@TimS. Es posible que deba incluir el manejo para la autenticación por cualquier medio que sea apropiado para su configuración. Solía ​​esperar ingresar la contraseña en el indicador. luego está este hilo con otras soluciones: unix.stackexchange.com/questions/147329/…
powerrox
29

¿Has echado un vistazo a Fabric ? Le permite hacer todo tipo de cosas remotas a través de SSH usando Python.

supersighs
fuente
18

Descubrí que paramiko era un nivel demasiado bajo y Fabric no era especialmente adecuado para ser utilizado como biblioteca, así que armé mi propia biblioteca llamada spur que usa paramiko para implementar una interfaz un poco más agradable:

import spur

shell = spur.SshShell(hostname="localhost", username="bob", password="password1")
result = shell.run(["echo", "-n", "hello"])
print result.output # prints hello

Si necesita ejecutar dentro de un shell:

shell.run(["sh", "-c", "echo -n hello"])
Michael Williamson
fuente
2
Decidí intentarlo spur. Genera comandos de shell adicionales y termina con: which 'mkdir'> / dev / null 2> & 1; echo $ ?; exec 'mkdir' '-p' '/ data / rpmupdate / 20130207142923'. Me gustaría tener también acceso a una llanura exec_command. También capacidad de ejecutar tareas en segundo plano que falta: nohup ./bin/rpmbuildpackages < /dev/null >& /dev/null &. Por ejemplo, genero un script zsh (rpmbuildpackages) usando la plantilla y luego lo dejo ejecutándose en la máquina. Tal vez la capacidad de monitorear dichos trabajos en segundo plano también sería buena (guardar PID en algunos ~ / .spur).
davidlt
1
spuraparentemente solo funciona en sistemas unix porque depende de ellos termios. ¿Alguien sabe una buena biblioteca para Windows?
Gabriel
No del todo cierto: si utiliza un instalador precompilado , podrá instalar paramiko y spur. Lo hice yo mismo ...
ravemir
@ Gabriel: uno de los lanzamientos recientes debería haber mejorado el soporte en Windows. Si todavía no funciona, no dude en abrir un problema.
Michael Williamson
@davidlt: al construir un SshShell, ahora existe la opción de establecer el tipo de shell. Si se usa un shell mínimo al pasar shell_type=spur.ssh.ShellTypes.minimal, solo se envía el comando sin formato. Implementar tareas en segundo plano directamente se siente un poco fuera de alcance para Spur, pero debe poder ejecutar el comando que ha descrito invocando un shell, por ejemplo shell.run(["sh", "-c", "nohup ./bin/rpmbuildpackages < /dev/null >& /dev/null &"]).
Michael Williamson
18

Mantenlo simple. No se requieren bibliotecas.

import subprocess

subprocess.Popen("ssh {user}@{host} {cmd}".format(user=user, host=host, cmd='ls -l'), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
Ronn Macc
fuente
1
Subprocess es su navaja suiza en automatización. Luego puede combinar Python con infinitas posibilidades como scripts de shell, sed, awk, grep. Sí, todos pueden hacerlo en Python, pero no sería genial ejecutar un grep en algún lugar de Python, bueno, sí.
Ronn Macc
12
si sus comandos ssh solicitan contraseña, ¿cómo proporcionaría eso en el archivo Python?
BeingSuman
1
@BeingSuman Puede usar la autenticación de clave SSH para eso. Aunque supongo que ya lo has descubierto.
mmrbulbul
9

Todos ya han declarado (recomendado) usar paramiko y solo estoy compartiendo un código de Python (API uno puede decir) que le permitirá ejecutar múltiples comandos de una vez.

para ejecutar comandos en diferentes nodos de uso: Commands().run_cmd(host_ip, list_of_commands)

Verá un TODO, que he guardado para detener la ejecución si alguno de los comandos no se ejecuta, no sé cómo hacerlo. por favor comparte tu conocimiento

#!/usr/bin/python

import os
import sys
import select
import paramiko
import time


class Commands:
    def __init__(self, retry_time=0):
        self.retry_time = retry_time
        pass

    def run_cmd(self, host_ip, cmd_list):
        i = 0
        while True:
        # print("Trying to connect to %s (%i/%i)" % (self.host, i, self.retry_time))
        try:
            ssh = paramiko.SSHClient()
            ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
            ssh.connect(host_ip)
            break
        except paramiko.AuthenticationException:
            print("Authentication failed when connecting to %s" % host_ip)
            sys.exit(1)
        except:
            print("Could not SSH to %s, waiting for it to start" % host_ip)
            i += 1
            time.sleep(2)

        # If we could not connect within time limit
        if i >= self.retry_time:
            print("Could not connect to %s. Giving up" % host_ip)
            sys.exit(1)
        # After connection is successful
        # Send the command
        for command in cmd_list:
            # print command
            print "> " + command
            # execute commands
            stdin, stdout, stderr = ssh.exec_command(command)
            # TODO() : if an error is thrown, stop further rules and revert back changes
            # Wait for the command to terminate
            while not stdout.channel.exit_status_ready():
                # Only print data if there is data to read in the channel
                if stdout.channel.recv_ready():
                    rl, wl, xl = select.select([ stdout.channel ], [ ], [ ], 0.0)
                    if len(rl) > 0:
                        tmp = stdout.channel.recv(1024)
                        output = tmp.decode()
                        print output

        # Close SSH connection
        ssh.close()
        return

def main(args=None):
    if args is None:
        print "arguments expected"
    else:
        # args = {'<ip_address>', <list_of_commands>}
        mytest = Commands()
        mytest.run_cmd(host_ip=args[0], cmd_list=args[1])
    return


if __name__ == "__main__":
    main(sys.argv[1:])

¡Gracias!

IAmSurajBobade
fuente
4

He usado paramiko a bunch (nice) y pxssh (también nice). Yo recomendaría cualquiera. Funcionan un poco diferente pero tienen una superposición relativamente grande en el uso.

Eric Snow
fuente
44
El enlace a pxssh es un buen viaje en el tiempo.
Oben Sonne
3

Paramiko finalmente funcionó para mí después de agregar una línea adicional, que es realmente importante (línea 3):

import paramiko

p = paramiko.SSHClient()
p.set_missing_host_key_policy(paramiko.AutoAddPolicy())   # This script doesn't work for me unless this line is added!
p.connect("server", port=22, username="username", password="password")
stdin, stdout, stderr = p.exec_command("your command")
opt = stdout.readlines()
opt = "".join(opt)
print(opt)

Asegúrese de que el paquete paramiko esté instalado. Fuente original de la solución: Fuente

Alexander Tereshkov
fuente
1
#Reading the Host,username,password,port from excel file
import paramiko 
import xlrd

ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())

loc = ('/Users/harshgow/Documents/PYTHON_WORK/labcred.xlsx')
wo = xlrd.open_workbook(loc)
sheet = wo.sheet_by_index(0)
Host = sheet.cell_value(0,1)
Port = int(sheet.cell_value(3,1))
User = sheet.cell_value(1,1)
Pass = sheet.cell_value(2,1)

def details(Host,Port,User,Pass):
    ssh.connect(Host, Port, User, Pass)
    print('connected to ip ',Host)
    stdin, stdout, stderr = ssh.exec_command("")
    stdin.write('xcommand SystemUnit Boot Action: Restart\n')
    print('success')

details(Host,Port,User,Pass)
Harshan Gowda
fuente
1

Funciona perfectamente...

import paramiko
import time

ssh = paramiko.SSHClient()
#ssh.load_system_host_keys()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect('10.106.104.24', port=22, username='admin', password='')

time.sleep(5)
print('connected')
stdin, stdout, stderr = ssh.exec_command(" ")

def execute():
       stdin.write('xcommand SystemUnit Boot Action: Restart\n')
       print('success')

execute()
Harshan Gowda
fuente
1

La respuesta aceptada no funcionó para mí, esto es lo que usé en su lugar:

import paramiko
import os

ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
# ssh.load_system_host_keys()
ssh.load_host_keys(os.path.expanduser('~/.ssh/known_hosts'))
ssh.connect("d.d.d.d", username="user", password="pass", port=22222)

ssh_stdin, ssh_stdout, ssh_stderr = ssh.exec_command("ls -alrt")
exit_code = ssh_stdout.channel.recv_exit_status() # handles async exit error 

for line in ssh_stdout:
    print(line.strip())

total 44
-rw-r--r--.  1 root root  129 Dec 28  2013 .tcshrc
-rw-r--r--.  1 root root  100 Dec 28  2013 .cshrc
-rw-r--r--.  1 root root  176 Dec 28  2013 .bashrc
...

Alternativamente, puede usar sshpass :

import subprocess
cmd = """ sshpass -p "myPas$" ssh [email protected] -p 22222 'my command; exit' """
print( subprocess.getoutput(cmd) )

Referencias:
1. https://github.com/onyxfish/relay/issues/11
2. https://stackoverflow.com/a/61016663/797495

CONvid19
fuente
0

Eche un vistazo a spurplusun contenedor que desarrollamos alrededor spurque proporciona anotaciones de tipo y algunos trucos menores (reconectando SFTP, md5, etc. ): https://pypi.org/project/spurplus/

marko.ristin
fuente
1
una envoltura alrededor de una envoltura alrededor de una envoltura .. agradable :)
Alex R
Bueno, si desea mantener sus scripts de implementación triviales, dudo que las herramientas subyacentes sean lo suficientemente simples / abstractas. Hasta ahora, no observamos abstracciones con fugas.
marko.ristin
0

Solicitar al usuario que ingrese el comando según el dispositivo en el que
inician sesión. El código siguiente está validado por PEP8online.com.

import paramiko
import xlrd
import time

ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
loc = ('/Users/harshgow/Documents/PYTHON_WORK/labcred.xlsx')
wo = xlrd.open_workbook(loc)
sheet = wo.sheet_by_index(0)
Host = sheet.cell_value(0, 1)
Port = int(sheet.cell_value(3, 1))
User = sheet.cell_value(1, 1)
Pass = sheet.cell_value(2, 1)

def details(Host, Port, User, Pass):
    time.sleep(2)
    ssh.connect(Host, Port, User, Pass)
    print('connected to ip ', Host)
    stdin, stdout, stderr = ssh.exec_command("")
    x = input('Enter the command:')
    stdin.write(x)
    stdin.write('\n')
    print('success')

details(Host, Port, User, Pass)
Harshan Gowda
fuente
0

Ejemplo a continuación, en caso de que desee entradas de usuario para nombre de host, nombre de usuario, contraseña y número de puerto.

  import paramiko

  ssh = paramiko.SSHClient()

  ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())



  def details():

  Host = input("Enter the Hostname: ")

  Port = input("Enter the Port: ")

  User = input("Enter the Username: ")

  Pass = input("Enter the Password: ")

  ssh.connect(Host, Port, User, Pass, timeout=2)

  print('connected')

  stdin, stdout, stderr = ssh.exec_command("")

  stdin.write('xcommand SystemUnit Boot Action: Restart\n')

  print('success')

  details()
Harshan Gowda
fuente
55
En lugar de volver a publicar su respuesta de hace dos días con un mejor formato, ¿por qué no editar la respuesta anterior y mejorar su formato?
snakecharmerb