Wykonuj polecenia przez ssh w Pythonie


136

Piszę skrypt, który zautomatyzuje niektóre polecenia wiersza poleceń w Pythonie. W tej chwili wykonuję telefony tak:

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

Jednak muszę uruchomić niektóre polecenia na zdalnym komputerze. Ręcznie logowałem się za pomocą ssh, a następnie uruchamiałem polecenia. Jak mógłbym to zautomatyzować w Pythonie? Muszę zalogować się za pomocą (znanego) hasła do zdalnego komputera, więc nie mogę po prostu użyć cmd = ssh user@remotehost, zastanawiam się, czy jest moduł, którego powinienem używać?

Odpowiedzi:


201

Skieruję cię do paramiko

zobacz to pytanie

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

3
Założenie jest takie, że paramiko jest tak samo bezpieczne jak (otwarte) ssh. Czy to jest?
user239558

1
co jeśli klucze ssh zostaną wymienione?
Ardit

19
Jeśli używasz kluczy SSH, najpierw przygotuj plik klucza, używając k = paramiko.RSAKey.from_private_key_file(keyfilename)ALBO : LUB k = paramiko.DSSKey.from_private_key_file(keyfilename)WTEDY ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())i na koniec ssh..connect(hostname=host, username=user, pkey=k).
Scott Prive

7
Jako wieloletni użytkownik Paramiko (ale nie ekspert), mogę zasugerować używanie Paramiko, ale powinieneś rozważyć swoje przypadki użycia i to, ile chcesz się nauczyć. Paramiko jest bardzo niskopoziomowe i możesz łatwo wpaść w pułapkę, w której tworzysz „funkcję pomocniczą uruchamiającą polecenia” bez pełnego zrozumienia kodu, którego używasz. Oznacza to, że możesz najpierw zaprojektować powiedz, def run_cmd(host, cmd):które robi to, co chcesz, ale Twoje potrzeby ewoluują. W końcu zmieniasz pomocnika dla nowego przypadku użycia, co zmienia zachowanie starszego istniejącego użycia. Zaplanuj odpowiednio.
Scott Prive

3
W przypadku nieznanego błędu hosta wykonaj: ssh.set_missing_host_key_policy (paramiko.AutoAddPolicy ()) przed
Nemo

49

Lub możesz po prostu użyć commands.getstatusoutput :

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

Używałem go intensywnie i działa świetnie.

W Pythonie 2.6+ użyj subprocess.check_output.


4
+1 za przyjemną, prostą, wbudowaną metodę. W mojej obecnej konfiguracji nie chcę dodawać bibliotek Pythona, więc twoja sugestia jest cenna, również bardzo prosta.
Philip Kearns

3
po prostu upewnij się, że twój zdalny host jest skonfigurowany do ssh bez hasła, jeśli nie, musisz zrobić inne rzeczy do zarządzania uwierzytelnianiem
powerrox.

subprocess.check_output - świetne rozwiązanie!
Tim S.

2
@powerrox jakie są te „inne rzeczy”?
ealeon

2
@TimS. może być konieczne uwzględnienie obsługi uwierzytelniania w dowolny sposób odpowiedni dla danej konfiguracji. Kiedyś spodziewałem się wprowadzenia hasła po monicie. to jest ten wątek z innymi rozwiązaniami: unix.stackexchange.com/questions/147329/ ...
powerrox

29

Widziałeś Fabric ? Pozwala na wykonywanie wszelkiego rodzaju zdalnych rzeczy przez SSH przy użyciu Pythona.


18

Uważam, że paramiko jest trochę za niskopoziomowe, a Fabric nie jest specjalnie przystosowany do wykorzystania jako biblioteka, więc stworzyłem własną bibliotekę o nazwie spur, która używa paramiko do zaimplementowania nieco ładniejszego interfejsu:

import spur

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

Jeśli chcesz biegać wewnątrz powłoki:

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

2
Postanowiłem spróbować spur. Generujesz dodatkowe polecenia powłoki i otrzymujesz: który 'mkdir'> / dev / null 2> & 1; echo $ ?; exec 'mkdir' '-p' '/ data / rpmupdate / 20130207142923'. Chciałbym mieć również dostęp do zwykłego exec_command. Możliwość uruchamiania zadań w tle brakuje również: nohup ./bin/rpmbuildpackages < /dev/null >& /dev/null &. Np. Generuję skrypt zsh (rpmbuildpackages) przy użyciu szablonu i po prostu zostawiam go uruchomionego na maszynie. Może przydałaby się również możliwość monitorowania takich zadań w tle (zapisywanie PIDów w jakimś ~ / .spur).
davidlt

1
spurnajwyraźniej działa tylko na systemach uniksowych, ponieważ jest zależny od termios. Czy ktoś zna dobrą bibliotekę dla systemu Windows?
Gabriel,

Nie do końca prawda: jeśli używasz prekompilowanego instalatora , będziesz mógł zainstalować paramiko i spur. Po prostu zrobiłem to sam ...
ravemir

@Gabriel: jedna z ostatnich wersji powinna mieć ulepszoną obsługę w systemie Windows. Jeśli nadal nie działa, otwórz problem.
Michael Williamson,

@davidlt: podczas konstruowania SshShellpowłoki istnieje teraz możliwość ustawienia typu powłoki. Jeśli przez przekazanie używana jest powłoka minimalna, shell_type=spur.ssh.ShellTypes.minimalwysyłane jest tylko polecenie surowe. Wdrażanie zadań w tle bezpośrednio wydaje się być nieco poza zakresem Spur, ale powinieneś być w stanie uruchomić opisane polecenie, wywołując powłokę, np shell.run(["sh", "-c", "nohup ./bin/rpmbuildpackages < /dev/null >& /dev/null &"].).
Michael Williamson,

18

Nie komplikuj. Żadne biblioteki nie są wymagane.

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
Subprocess to Twój szwajcarski scyzoryk w automatyzacji. Następnie możesz połączyć Pythona z nieskończonymi możliwościami, takimi jak skrypty powłoki, sed, awk, grep. Tak, możesz wszystko zrobić w Pythonie, ale czy nie byłoby wspaniale uruchomić grep gdzieś w Pythonie - cóż, możesz.
Ronn Macc

12
jeśli twoje polecenie ssh prosi o hasło, jak możesz je podać w pliku Pythona?
BeingSuman

1
@BeingSuman Możesz do tego użyć uwierzytelniania za pomocą klucza SSH. Chociaż myślę, że już to rozgryzłeś.
mmrbulbul

9

Wszyscy już powiedzieli (zalecane) użycie paramiko i po prostu udostępniam kod Pythona (można powiedzieć API), który pozwoli ci wykonać wiele poleceń za jednym razem.

aby wykonać polecenia na innym węźle użyj: Commands().run_cmd(host_ip, list_of_commands)

Zobaczysz jedno TODO, które zatrzymałem, aby zatrzymać wykonywanie, jeśli którekolwiek z poleceń nie zostanie wykonane, nie wiem, jak to zrobić. proszę podziel się swoją wiedzą

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

Dziękuję Ci!


4

Użyłem paramiko a bunch (nice) i pxssh (także fajnie). Poleciłbym też. Działają trochę inaczej, ale ich użycie jest stosunkowo duże.


4
Odnośnik do pxssh to miła podróż w czasie.
Oben Sonne

3

paramiko w końcu zadziałało dla mnie po dodaniu dodatkowej linii, która jest naprawdę ważna (linia 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)

Upewnij się, że pakiet paramiko jest zainstalowany. Oryginalne źródło rozwiązania: Źródło


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

Działa świetnie...

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

Zaakceptowana odpowiedź nie zadziałała dla mnie, oto, czego użyłem:

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

Alternatywnie możesz użyć sshpass :

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

Źródła:
1. https://github.com/onyxfish/relay/issues/11
2. https://stackoverflow.com/a/61016663/797495


0

Spójrz na spurplusopakowanie, które opracowaliśmy, spurktóre zawiera adnotacje typu i kilka drobnych sztuczek (ponowne łączenie SFTP, md5 itp .): Https://pypi.org/project/spurplus/


1
owijka owijająca owijka ... fajnie :)
Alex R

Cóż, jeśli chcesz, aby twoje skrypty wdrażania były trywialne, wątpię, czy narzędzia bazowe są wystarczająco proste / abstrakcyjne. Jak dotąd nie zaobserwowaliśmy nieszczelnych abstrakcji.
marko.ristin

0

Poproszenie Użytkownika o wpisanie komendy na urządzeniu, na którym się loguje.
Poniższy kod jest weryfikowany przez 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

Poniżej przykład, jeśli chcesz, aby użytkownik wprowadził nazwę hosta, nazwę użytkownika, hasło i numer portu.

  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
Zamiast publikować ponownie swoją odpowiedź sprzed dwóch dni z lepszym formatowaniem, możesz edytować starszą odpowiedź i poprawić jej formatowanie.
snakecharmerb
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.