Führen Sie Befehle über ssh mit Python aus

136

Ich schreibe ein Skript, um einige Befehlszeilenbefehle in Python zu automatisieren. Im Moment telefoniere ich so:

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

Ich muss jedoch einige Befehle auf einem Remote-Computer ausführen. Manuell würde ich mich mit ssh anmelden und dann die Befehle ausführen. Wie würde ich dies in Python automatisieren? Ich muss mich mit einem (bekannten) Kennwort auf dem Remote-Computer anmelden, damit ich nicht einfach verwenden cmd = ssh user@remotehostkann. Ich frage mich, ob es ein Modul gibt, das ich verwenden soll.

Fredley
quelle

Antworten:

201

Ich werde Sie an paramiko verweisen

siehe diese Frage

ssh = paramiko.SSHClient()
ssh.connect(server, username=username, password=password)
ssh_stdin, ssh_stdout, ssh_stderr = ssh.exec_command(cmd_to_execute)
shahjapan
quelle
3
Die Annahme hier ist, dass paramiko so sicher ist wie (offenes) ssh. Ist es?
user239558
1
Was ist, wenn die SSH-Schlüssel ausgetauscht werden?
Ardit
19
Wenn Sie SSH-Schlüssel verwenden, bereiten Sie zuerst die Schlüsseldatei mit 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())und schließlich vor ssh..connect(hostname=host, username=user, pkey=k).
Scott Prive
7
Als langjähriger Benutzer von Paramiko (aber kein Experte) kann ich die Verwendung von Paramiko vorschlagen, aber Sie sollten Ihre Anwendungsfälle berücksichtigen und wie viel Sie lernen möchten. Paramiko ist sehr niedrig und Sie können leicht in eine Falle tappen, in der Sie eine "Befehlsausführungshilfe" erstellen, ohne den von Ihnen verwendeten Code vollständig zu verstehen. Das bedeutet, dass Sie vielleicht ein Design entwerfen, das def run_cmd(host, cmd):das tut, was Sie zuerst wollen, aber Ihre Bedürfnisse entwickeln sich. Am Ende ändern Sie den Helfer für den neuen Anwendungsfall, wodurch sich das Verhalten der älteren vorhandenen Verwendung ändert. Planen Sie entsprechend.
Scott Prive
3
Führen Sie für unbekannte Hostfehler Folgendes aus: ssh.set_missing_host_key_policy (paramiko.AutoAddPolicy ()) vor
Nemo
48

Oder Sie können einfach command.getstatusoutput verwenden :

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

Ich habe es ausgiebig benutzt und es funktioniert großartig.

Verwenden Sie in Python 2.6+ subprocess.check_output.

powerrox
quelle
4
+1 für eine schöne einfache eingebaute Methode. In meinem aktuellen Setup möchte ich keine Python-Bibliotheken hinzufügen, daher ist Ihr Vorschlag wertvoll und auch sehr einfach.
Philip Kearns
3
Stellen
subprocess.check_output - tolle Lösung!
Tim S.
2
@powerrox was sind das für "andere Dinge"?
ealeon
2
@ TimS. Möglicherweise müssen Sie die Behandlung für die Authentifizierung mit den für Ihr Setup geeigneten Mitteln einbeziehen. Ich habe erwartet, dass ich an der Eingabeaufforderung ein Passwort eingebe. dann gibt es diesen Thread mit anderen Lösungen: unix.stackexchange.com/questions/147329/…
powerrox
29

Haben Sie sich Fabric angesehen ? Mit Python können Sie alle möglichen Remote-Aufgaben über SSH ausführen.

Supersighs
quelle
18

Ich fand, dass paramiko etwas zu niedrig ist und Fabric nicht besonders gut für die Verwendung als Bibliothek geeignet ist. Deshalb habe ich meine eigene Bibliothek namens spur zusammengestellt , die paramiko verwendet, um eine etwas schönere Oberfläche zu implementieren:

import spur

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

Wenn Sie in einer Shell laufen müssen:

shell.run(["sh", "-c", "echo -n hello"])
Michael Williamson
quelle
2
Ich beschloss es zu versuchen spur. Sie generieren zusätzliche Shell-Befehle und erhalten am Ende Folgendes: 'mkdir'> / dev / null 2> & 1; echo $?; exec 'mkdir' '-p' '/ data / rpmupdate / 20130207142923'. Ich hätte auch gerne Zugang zu einer Ebene exec_command. Außerdem fehlt die Fähigkeit, Hintergrundaufgaben auszuführen : nohup ./bin/rpmbuildpackages < /dev/null >& /dev/null &. Zum Beispiel generiere ich ein zsh-Skript (rpmbuildpackages) unter Verwendung einer Vorlage und lasse es dann einfach auf dem Computer laufen. Vielleicht wäre die Möglichkeit, solche Hintergrundjobs zu überwachen, auch hilfreich (Speichern von PIDs in einigen ~ / .spur).
Davidlt
1
spurfunktioniert anscheinend nur auf Unix-Systemen, da es eine Abhängigkeit von hat termios. Kennt jemand eine gute Bibliothek für Windows?
Gabriel
Nicht ganz richtig: Wenn Sie ein vorkompiliertes Installationsprogramm verwenden , können Sie paramiko und spur installieren. Ich habe es einfach selbst gemacht ...
Ravemir
@ Gabriel: Eine der jüngsten Versionen sollte die Unterstützung unter Windows verbessert haben. Wenn es immer noch nicht funktioniert, können Sie gerne eine Ausgabe öffnen.
Michael Williamson
@davidlt: Beim Erstellen eines SshShellgibt es jetzt die Möglichkeit, den Shell-Typ festzulegen . Wenn beim Übergeben eine minimale Shell verwendet wird shell_type=spur.ssh.ShellTypes.minimal, wird nur der Raw-Befehl gesendet. Das direkte Implementieren von Hintergrundaufgaben fühlt sich für Spur etwas außerhalb des Bereichs an, aber Sie sollten in der Lage sein, den von Ihnen beschriebenen Befehl auszuführen, indem Sie eine Shell aufrufen (z shell.run(["sh", "-c", "nohup ./bin/rpmbuildpackages < /dev/null >& /dev/null &"]. B. ).
Michael Williamson
18

Halte es einfach. Keine Bibliotheken erforderlich.

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
quelle
1
Subprozess ist Ihr Schweizer Taschenmesser in der Automatisierung. Sie können Python dann mit endlosen Möglichkeiten wie Shell-Skripten, sed, awk, grep kombinieren. Ja, Sie können alles in Python tun, aber wäre es nicht großartig, irgendwo in Python einen Grep auszuführen - das können Sie.
Ronn Macc
11
Wenn Ihre ssh-Befehle nach einem Passwort fragen, wie würden Sie das in der Python-Datei bereitstellen?
BeingSuman
1
@BeingSuman Dafür können Sie die SSH-Schlüsselauthentifizierung verwenden. Obwohl ich denke, dass Sie das bereits herausgefunden haben.
mmrbulbul
9

Alle haben bereits angegeben (empfohlen), paramiko zu verwenden, und ich teile nur einen Python-Code (API kann man sagen), mit dem Sie mehrere Befehle auf einmal ausführen können.

Um Befehle auf verschiedenen Knoten auszuführen, verwenden Sie: Commands().run_cmd(host_ip, list_of_commands)

Sie werden ein TODO sehen, das ich beibehalten habe, um die Ausführung zu stoppen, wenn einer der Befehle nicht ausgeführt werden kann. Ich weiß nicht, wie es geht. Bitte teilen Sie Ihr Wissen

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

Danke dir!

IAmSurajBobade
quelle
4

Ich habe Paramiko ein paar (nett) und Pxssh (auch schön) verwendet. Ich würde es auch empfehlen. Sie arbeiten etwas anders, haben aber eine relativ große Überlappung in der Nutzung.

Eric Snow
quelle
4
Der Link zu pxssh ist eine schöne Zeitreise.
Oben Sonne
3

paramiko hat endlich für mich gearbeitet, nachdem ich eine zusätzliche Zeile hinzugefügt habe, die wirklich wichtig ist (Zeile 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)

Stellen Sie sicher, dass das paramiko-Paket installiert ist. Ursprüngliche Quelle der Lösung: Quelle

Alexander Tereshkov
quelle
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
quelle
1

Funktioniert perfekt...

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
quelle
1

Die akzeptierte Antwort hat bei mir nicht funktioniert. Ich habe stattdessen Folgendes verwendet:

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

Alternativ können Sie sshpass verwenden :

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

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

CONvid19
quelle
0

Schauen Sie sich spurpluseinen Wrapper an , den wir entwickelt haben spurund der Typanmerkungen und einige kleinere Gimmicks (erneutes Verbinden von SFTP, md5 usw. ) Enthält : https://pypi.org/project/spurplus/

marko.ristin
quelle
1
ein Wrapper um einen Wrapper um einen Wrapper .. schön :)
Alex R
Wenn Sie Ihre Bereitstellungsskripte trivial halten möchten, bezweifle ich, dass die zugrunde liegenden Tools einfach / abstrakt genug sind. Bisher haben wir keine undichten Abstraktionen beobachtet.
marko.ristin
0

Bitten Sie den Benutzer, den Befehl gemäß dem Gerät einzugeben, bei dem er sich anmeldet.
Der folgende Code wird von PEP8online.com überprüft.

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
quelle
0

Im folgenden Beispiel, falls Sie Benutzereingaben für Hostname, Benutzername, Kennwort und Port-Nr.

  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
quelle
5
Anstatt Ihre Antwort von vor zwei Tagen mit einer besseren Formatierung erneut zu veröffentlichen, können Sie die ältere Antwort bearbeiten und ihre Formatierung verbessern.
Snakecharmerb