Выполнять команды через SSH с Python

136

Я пишу скрипт для автоматизации некоторых команд командной строки в Python. На данный момент я делаю звонки таким образом:

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

Однако мне нужно выполнить некоторые команды на удаленной машине. Вручную, я бы войти в систему с помощью ssh и затем запустить команды. Как бы я автоматизировал это в Python? Мне нужно войти в систему с (известным) паролем на удаленную машину, поэтому я не могу просто использовать cmd = ssh user@remotehost, мне интересно, есть ли модуль, который я должен использовать?

fredley
источник

Ответы:

201

Я отошлю вас к Paramiko

увидеть этот вопрос

ssh = paramiko.SSHClient()
ssh.connect(server, username=username, password=password)
ssh_stdin, ssh_stdout, ssh_stderr = ssh.exec_command(cmd_to_execute)
shahjapan
источник
3
Здесь предполагается, что paramiko так же безопасен, как и (открытый) ssh. Это?
user239558
1
Что делать, если обмен SSH-ключей?
Ардит
19
Если вы используете ключи SSH, сначала подготовить файл_ключа с использованием: k = paramiko.RSAKey.from_private_key_file(keyfilename)ИЛИ k = paramiko.DSSKey.from_private_key_file(keyfilename)ТО ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())и , наконец ssh..connect(hostname=host, username=user, pkey=k).
Скотт Прайв
7
Как давний пользователь Paramiko (но не эксперт), я могу предложить использовать Paramiko, но вы должны рассмотреть ваши варианты использования и то, сколько вы готовы учиться. Paramiko очень низкоуровневый, и вы можете легко попасть в ловушку, где создадите «вспомогательную функцию запуска команды», не полностью понимая код, который вы используете. Это означает, что вы можете придумать def run_cmd(host, cmd):что-то, что сначала делает то, что вы хотите, но ваши потребности развиваются. В итоге вы меняете помощника для нового варианта использования, который меняет поведение старого существующего использования. Планируйте соответственно.
Скотт Прайв
3
Для неизвестной ошибки хоста выполните: ssh.set_missing_host_key_policy (paramiko.AutoAddPolicy ()) до
Nemo
49

Или вы можете просто использовать command.getstatusoutput :

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

Я много использовал его, и он отлично работает.

В Python 2.6+ используйте subprocess.check_output.

powerrox
источник
4
+1 за красивый простой встроенный метод. В моей текущей настройке я не хочу добавлять библиотеки Python, поэтому ваше предложение очень ценно и очень просто.
Филипп Кернс
3
просто убедитесь, что ваш удаленный хост настроен для SSH без пароля, если нет, вам нужно сделать другие вещи для управления аутентификацией
powerrox
subprocess.check_output - отличное решение!
Тим С.
2
@powerrox что это за "другие вещи"?
ealeon
2
@TimS. возможно, вам придется включить обработку аутентификации любыми способами, которые подходят для вашей установки. Раньше я ожидал ввести пароль в приглашении. тогда есть эта ветка с другими решениями: unix.stackexchange.com/questions/147329/…
powerrox
29

Вы смотрели на Fabric ? Это позволяет вам делать все виды удаленных вещей через SSH, используя python.

supersighs
источник
18

Я обнаружил, что paramiko слишком низкоуровневый, а Fabric не особенно хорошо подходит для использования в качестве библиотеки, поэтому я собрал свою собственную библиотеку под названием spur, которая использует paramiko для реализации немного более приятного интерфейса:

import spur

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

Если вам нужно запустить оболочку:

shell.run(["sh", "-c", "echo -n hello"])
Майкл Уильямсон
источник
2
Я решил попробовать spur. Вы генерируете дополнительные команды оболочки и в итоге получаете: which 'mkdir'> / dev / null 2> & 1; echo $ ?; exec 'mkdir' '-p' '/ data / rpmupdate / 20130207142923'. Я хотел бы также иметь доступ к равнине exec_command. Кроме того, отсутствует возможность выполнения фоновых задач: nohup ./bin/rpmbuildpackages < /dev/null >& /dev/null &. Например, я генерирую скрипт zsh (rpmbuildpackages), используя шаблон, а затем просто оставляю его работать на машине. Возможно, было бы неплохо иметь возможность отслеживать такие фоновые задания (сохранение PID в некоторых ~ / .spur).
Давидлт
1
spurпо-видимому, работает только в системах Unix, потому что это зависит от termios. Кто-нибудь знает хорошую библиотеку для Windows?
Габриэль
Не совсем так: если вы используете предварительно скомпилированный установщик , вы сможете установить paramiko и spur. Я просто сделал это сам ...
ravemir
@Gabriel: в одном из последних выпусков должна быть улучшена поддержка в Windows. Если это все еще не работает, пожалуйста, не стесняйтесь открыть вопрос.
Майкл Уильямсон,
@davidlt: при создании SshShell, теперь есть возможность установить тип оболочки. Если для передачи используется минимальная оболочка shell_type=spur.ssh.ShellTypes.minimal, то отправляется только необработанная команда. Внедрение фоновых задач напрямую выходит за рамки Spur, но вы должны иметь возможность запустить команду, которую вы описали, например, вызвав оболочку shell.run(["sh", "-c", "nohup ./bin/rpmbuildpackages < /dev/null >& /dev/null &"]).
Майкл Уильямсон
18

Будь проще. Библиотеки не требуются.

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
Подпроцесс - ваш швейцарский армейский нож в области автоматизации. Затем вы можете комбинировать Python с бесконечными возможностями, такими как сценарии оболочки, sed, awk, grep. Да, вы все можете делать в Python, но разве было бы замечательно просто запустить grep где-нибудь в Python - ну, вы можете.
Ронн Мак
12
если ваши команды ssh запрашивают пароль, как бы вы предоставили его в файле Python?
BeingSuman
1
@BeingSuman Для этого вы можете использовать аутентификацию по ключу SSH. Хотя, я думаю, Вы уже поняли это.
mmrbulbul
9

Все уже заявили (рекомендовали) использовать paramiko, и я просто делюсь кодом Python (можно сказать, API), который позволит вам выполнять несколько команд за один раз.

выполнять команды на разных узлах использования: Commands().run_cmd(host_ip, list_of_commands)

Вы увидите одно TODO, которое я сохранил, чтобы остановить выполнение, если какая-либо из команд не выполняется, я не знаю, как это сделать. поделитесь пожалуйста своими знаниями

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

Спасибо!

IAmSurajBobade
источник
4

Я использовал paramiko a bunch (красиво) и pxssh (тоже красиво). Я бы порекомендовал либо. Они работают немного по-другому, но имеют относительно большое совпадение в использовании.

Эрик Сноу
источник
4
Ссылка на pxssh - это хорошее путешествие в прошлое.
Обен Сонне
3

Парамико, наконец, сработало для меня после добавления дополнительной строки, которая действительно важна (строка 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)

Убедитесь, что пакет paramiko установлен. Первоначальный источник решения: Source

Александр Терешков
источник
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

Работает отлично ...

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

Принятый ответ не работал для меня, вот что я использовал вместо этого:

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

В качестве альтернативы вы можете использовать sshpass :

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

Ссылки:
1. https://github.com/onyxfish/relay/issues/11
2. https://stackoverflow.com/a/61016663/797495

CONvid19
источник
0

Взгляните на spurplusсозданную нами оболочку, spurкоторая предоставляет аннотации типов и некоторые мелкие уловки (переподключение SFTP, md5 и т . Д.): Https://pypi.org/project/spurplus/

marko.ristin
источник
1
обертка вокруг обертки .. красиво :)
Alex R
Что ж, если вы хотите, чтобы ваши сценарии развертывания были тривиальными, я сомневаюсь, что базовые инструменты достаточно просты / абстрактны. Пока мы не наблюдали дырявых абстракций.
marko.ristin
0

Попросить пользователя ввести команду в соответствии с устройством, в которое они входят.
Код ниже подтвержден 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

Ниже приведен пример, если вы хотите, чтобы пользователь вводил имя хоста, имя пользователя, пароль и номер порта.

  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
Вместо того, чтобы опубликовать свой ответ двухдневной давности с лучшим форматированием, почему бы не отредактировать старый ответ и не улучшить его форматирование?
snakecharmerb