Esegui i comandi su ssh con Python


136

Sto scrivendo uno script per automatizzare alcuni comandi della riga di comando in Python. Al momento sto facendo chiamate così:

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

Tuttavia, devo eseguire alcuni comandi su una macchina remota. Manualmente, vorrei accedere usando ssh e quindi eseguire i comandi. Come lo automatizzerei in Python? Devo accedere con una password (nota) al computer remoto, quindi non posso semplicemente usare cmd = ssh user@remotehost, mi chiedo se c'è un modulo che dovrei usare?

Risposte:


201

Ti riferirò a paramiko

vedi questa domanda

ssh = paramiko.SSHClient()
ssh.connect(server, username=username, password=password)
ssh_stdin, ssh_stdout, ssh_stderr = ssh.exec_command(cmd_to_execute)

3
Il presupposto qui è che paramiko è sicuro come (open) ssh. È?
user239558,

1
cosa succede se i tasti ssh vengono scambiati?
Ardit,

19
Se stai usando le chiavi SSH, prima prepara il file di chiavi usando EITHER: k = paramiko.RSAKey.from_private_key_file(keyfilename)OR k = paramiko.DSSKey.from_private_key_file(keyfilename)THEN ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())e infine ssh..connect(hostname=host, username=user, pkey=k).
Scott Prive,

7
Come utente di lunga data di Paramiko (ma non un esperto), posso suggerire di utilizzare Paramiko ma dovresti considerare i tuoi casi d'uso e quanto sei disposto a imparare. Paramiko è di livello molto basso e puoi facilmente cadere in una trappola in cui crei un "comando che esegue la funzione di supporto" senza comprendere appieno il codice che stai utilizzando. Ciò significa che potresti progettare un dire def run_cmd(host, cmd):che fa quello che vuoi all'inizio, ma le tue esigenze si evolvono. Si finisce per cambiare l'helper per il nuovo caso d'uso, che modifica il comportamento dell'utilizzo esistente precedente. Pianificare di conseguenza.
Scott Prive,

3
Per errore host sconosciuto, eseguire: ssh.set_missing_host_key_policy (paramiko.AutoAddPolicy ()) prima
Nemo

49

Oppure puoi semplicemente usare command.getstatusoutput :

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

L'ho usato ampiamente e funziona benissimo.

In Python 2.6+, utilizzare subprocess.check_output.


4
+1 per un bel metodo integrato semplice. Nella mia configurazione attuale non voglio aggiungere librerie Python, quindi il tuo suggerimento è prezioso, anche molto semplice.
Philip Kearns,

3
assicurati solo che il tuo host remoto sia configurato per ssh senza password, in caso contrario, devi fare altre cose per gestire l'autenticazione
powerrox,

subprocess.check_output - ottima soluzione!
Tim S.

2
@powerrox quali sono quelle "altre cose?"
ealeon

2
@TimS. potrebbe essere necessario includere la gestione per l'autenticazione con qualsiasi mezzo appropriato per la propria configurazione. Mi aspettavo di inserire la password al prompt. poi c'è questo thread con altre soluzioni: unix.stackexchange.com/questions/147329/…
powerrox

29

Hai dato un'occhiata a Fabric ? Ti permette di fare ogni sorta di cose remote su SSH usando Python.


18

Ho trovato paramiko un po 'troppo basso livello e Fabric non particolarmente adatto per essere usato come libreria, quindi ho messo insieme la mia libreria chiamata spur che usa paramiko per implementare un'interfaccia leggermente più bella:

import spur

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

Se devi correre all'interno di una shell:

shell.run(["sh", "-c", "echo -n hello"])

2
Ho deciso di provare spur. Generi ulteriori comandi di shell e finisci con: quale 'mkdir'> / dev / null 2> & 1; echo $ ?; exec 'mkdir' '-p' '/ data / rpmupdate / 20130207142923'. Vorrei avere anche accesso a una pianura exec_command. Manca anche la capacità di eseguire le operazioni in background: nohup ./bin/rpmbuildpackages < /dev/null >& /dev/null &. Ad esempio, generi uno script zsh (rpmbuildpackages) usando template e poi semplicemente per lasciarlo in esecuzione sulla macchina. Forse anche la capacità di monitorare tali lavori in background sarebbe buona (salvare i PID in alcuni ~ / .spur).
davidlt,

1
spurapparentemente funziona solo su sistemi unix perché ha una dipendenza termios. Qualcuno conosce una buona libreria per Windows?
Gabriel,

Non del tutto vero: se usi un programma di installazione precompilato , sarai in grado di installare paramiko e spur. L'ho fatto da solo ...
ravemir,

@Gabriel: una delle versioni recenti dovrebbe avere un supporto migliorato su Windows. Se il problema persiste, non esitare ad aprire un problema.
Michael Williamson,

@davidlt: quando si costruisce un SshShell, ora c'è l'opzione per impostare il tipo di shell. Se si passa una shell minima passando shell_type=spur.ssh.ShellTypes.minimal, viene inviato solo il comando raw. L'implementazione di attività in background sembra un po 'fuori dall'ambito di Spur, ma dovresti essere in grado di eseguire il comando che hai descritto invocando una shell, ad esempio shell.run(["sh", "-c", "nohup ./bin/rpmbuildpackages < /dev/null >& /dev/null &"]).
Michael Williamson,

18

Mantienilo semplice. Nessuna biblioteca richiesta.

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()

1
Il sottoprocesso è il tuo coltellino svizzero nell'automazione. È quindi possibile combinare Python con infinite possibilità come script di shell, sed, awk, grep. Sì, puoi farlo tutti in Python, ma non sarebbe bello eseguire un grep da qualche parte in Python - beh, puoi.
Ronn Macc

12
se i tuoi comandi ssh chiedono la password come lo forniresti nel file Python?
BeingSuman,

1
@BeingSuman È possibile utilizzare l'autenticazione con chiave SSH per questo. Anche se credo che tu l'abbia già capito.
mmrbulbul,

9

Tutti hanno già dichiarato (raccomandato) usando paramiko e sto solo condividendo un codice Python (API si potrebbe dire) che ti permetterà di eseguire più comandi in una volta sola.

per eseguire comandi su diversi nodi usare: Commands().run_cmd(host_ip, list_of_commands)

Vedrai un TODO, che ho tenuto per interrompere l'esecuzione se uno qualsiasi dei comandi non riesce a eseguire, non so come farlo. per favore condividi le tue conoscenze

#!/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:])

Grazie!


4

Ho usato paramiko un sacco (bello) e pxssh (anche bello). Consiglierei neanche. Funzionano in modo leggermente diverso ma hanno una sovrapposizione relativamente grande nell'uso.


4
Il collegamento a pxssh è un bel viaggio indietro nel tempo.
Oben Sonne,

3

paramiko finalmente ha funzionato per me dopo aver aggiunto una riga aggiuntiva, che è davvero importante (riga 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)

Assicurarsi che il pacchetto paramiko sia installato. Fonte originale della soluzione: Fonte


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)

1

Funziona perfettamente ...

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()

1

La risposta accettata non ha funzionato per me, ecco cosa ho usato invece:

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
...

In alternativa, puoi usare sshpass :

import subprocess
cmd = """ sshpass -p "myPas$" ssh user@d.d.d.d -p 22222 'my command; exit' """
print( subprocess.getoutput(cmd) )

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


0

Dai un'occhiata a spurplusun wrapper che abbiamo sviluppato spurche fornisce annotazioni di tipo e alcuni espedienti minori (ricollegamento di SFTP, md5 ecc .): Https://pypi.org/project/spurplus/


1
un involucro attorno ad un involucro attorno ad un involucro .. bello :)
Alex R

Bene, se vuoi mantenere banali gli script di distribuzione, dubito che gli strumenti sottostanti siano abbastanza semplici / astratti. Finora non abbiamo osservato astrazioni che perdono.
marko.ristin,

0

Chiedere all'utente di immettere il comando in base al dispositivo a cui sta effettuando l'accesso.
Il codice seguente è convalidato da 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)

0

Di seguito, incase se desideri input utente per nome host, nome utente, password e n. Porta.

  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()

5
Invece di ripubblicare la risposta di due giorni fa con una formattazione migliore, perché non modificare la risposta precedente e migliorarne la formattazione?
snakecharmerb
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.