Executar comandos sobre ssh com Python

136

Estou escrevendo um script para automatizar alguns comandos de linha de comando em Python. No momento eu estou fazendo chamadas assim:

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

No entanto, preciso executar alguns comandos em uma máquina remota. Manualmente, eu entraria usando ssh e depois executaria os comandos. Como eu automatizaria isso em Python? Preciso fazer login com uma senha (conhecida) na máquina remota, para não poder usar apenas cmd = ssh user@remotehost, estou me perguntando se há um módulo que devo usar?

Fredley
fonte

Respostas:

201

Vou encaminhá-lo para paramiko

veja esta pergunta

ssh = paramiko.SSHClient()
ssh.connect(server, username=username, password=password)
ssh_stdin, ssh_stdout, ssh_stderr = ssh.exec_command(cmd_to_execute)
shahjapan
fonte
3
A suposição aqui é que o paramiko é tão seguro quanto o (aberto) ssh. É isso?
user239558
1
e se as chaves ssh forem trocadas?
Ardit
19
Se você estiver usando chaves SSH, primeiro prepare o arquivo de chave 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 finalmente ssh..connect(hostname=host, username=user, pkey=k).
21817 Scott Prive
7
Como usuário de longa data do Paramiko (mas não um especialista), posso sugerir o uso do Paramiko, mas você deve considerar seus casos de uso e o quanto deseja aprender. O Paramiko é de nível muito baixo, e você pode facilmente cair em uma armadilha onde cria uma "função auxiliar de execução de comando" sem entender completamente o código que está usando. Isso significa que você pode projetar, digamos, um def run_cmd(host, cmd):que faça o que você quer a princípio, mas suas necessidades evoluem. Você acaba alterando o auxiliar para o novo caso de uso, que altera o comportamento do uso existente mais antigo. Planeje adequadamente.
21817 Scott Prive
3
Para erro de host desconhecido, faça: ssh.set_missing_host_key_policy (paramiko.AutoAddPolicy ()) antes
Nemo
49

Ou você pode simplesmente usar commands.getstatusoutput :

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

Eu o usei extensivamente e funciona muito bem.

No Python 2.6 ou superior, use subprocess.check_output.

powerrox
fonte
4
+1 para um método interno simples e agradável. Na minha configuração atual, não quero adicionar bibliotecas Python, para que sua sugestão seja valiosa, muito direta também.
Philip Kearns
3
Apenas certifique-se o seu host remoto está configurado para SSH sem senha, se não, você tem que fazer outras coisas para o gerenciamento de autenticação
powerrox
subprocess.check_output - ótima solução!
Tim S.
2
@powerrox o que são essas "outras coisas"?
ealeon
2
@TimS. pode ser necessário incluir a manipulação da autenticação por qualquer meio apropriado para sua configuração. Eu costumava esperar digitar a senha no prompt. existe esse encadeamento com outras soluções: unix.stackexchange.com/questions/147329/…
powerrox:
29

Você já deu uma olhada no Fabric ? Ele permite que você faça todo tipo de coisas remotas sobre SSH usando python.

supersighs
fonte
18

Achei que o paramiko era um pouco baixo demais e o Fabric não era especialmente adequado para ser usado como biblioteca, então montei minha própria biblioteca chamada spur, que usa o paramiko para implementar uma interface um pouco mais agradável:

import spur

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

Se você precisar executar dentro de um shell:

shell.run(["sh", "-c", "echo -n hello"])
Michael Williamson
fonte
2
Eu decidi tentar spur. Você gera comandos adicionais do shell e termina com: qual 'mkdir'> / dev / null 2> & 1; eco $ ?; exec 'mkdir' '-p' '/ data / rpmupdate / 20130207142923'. Eu também gostaria de ter acesso a uma planície exec_command. Também falta capacidade de executar tarefas em segundo plano: nohup ./bin/rpmbuildpackages < /dev/null >& /dev/null &. Por exemplo, eu gero um script zsh (rpmbuildpackages) usando o modelo e depois apenas para deixá-lo em execução na máquina. Talvez a capacidade de monitorar esses trabalhos em segundo plano também seja boa (salvando PIDs em alguns ~ / .spur).
Davidlt
1
spuraparentemente, só funciona em sistemas unix porque depende termios. Alguém conhece uma boa biblioteca para Windows?
Gabriel
Não é totalmente verdade: se você usar um instalador pré - compilado , poderá instalar o paramiko e o spur. Eu acabei de fazer isso sozinho ...
ravemir 12/03/14
@ Gabriel: um dos lançamentos recentes deve ter suporte aprimorado no Windows. Se ainda não estiver funcionando, sinta-se à vontade para abrir um problema.
Michael Williamson
@ Davididlt: ao construir um SshShell, agora existe a opção de definir o tipo de shell. Se um shell mínimo for usado passando shell_type=spur.ssh.ShellTypes.minimal, apenas o comando bruto será enviado. A implementação direta de tarefas em segundo plano parece um pouco fora do escopo para o Spur, mas você poderá executar o comando que descreveu invocando um shell, por exemplo shell.run(["sh", "-c", "nohup ./bin/rpmbuildpackages < /dev/null >& /dev/null &"]).
Michael Williamson
18

Mantenha simples. Nenhuma biblioteca necessária.

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
fonte
1
Subprocesso é o seu canivete suíço em automação. Você pode combinar o Python com infinitas possibilidades, como scripts de shell, sed, awk, grep. Sim, todos vocês podem fazer em Python, mas não seria ótimo executar um grep em algum lugar em python - bem, você pode.
Ronn Macc
12
se seus comandos ssh pedirem senha, como você forneceria isso no arquivo Python?
BeingSuman
1
@BeingSuman Você pode usar a autenticação de chave SSH para isso. Embora eu ache que você já tenha entendido isso.
mmrbulbul
9

Todos já declaramos (recomendado) o uso de paramiko e estou apenas compartilhando um código python (API pode-se dizer) que permitirá que você execute vários comandos de uma só vez.

para executar comandos em diferentes nós, use: Commands().run_cmd(host_ip, list_of_commands)

Você verá um TODO, que guardei para interromper a execução se algum dos comandos falhar na execução, não sei como fazê-lo. por favor, compartilhe seu conhecimento

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

Obrigado!

IAmSurajBobade
fonte
4

Eu usei um monte de paramiko (legal) e pxssh (também legal). Eu recomendaria também. Eles funcionam de maneira um pouco diferente, mas têm uma sobreposição relativamente grande no uso.

Eric Snow
fonte
4
O link para pxssh é uma boa jornada de volta no tempo.
Oben Sonne
3

O paramiko finalmente funcionou para mim depois de adicionar uma linha adicional, que é realmente importante (linha 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)

Verifique se o pacote paramiko está instalado. Fonte original da solução: Source

Alexander Tereshkov
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)
Harshan Gowda
fonte
1

Funciona perfeitamente...

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

A resposta aceita não funcionou para mim, aqui está o que eu usei:

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

Como alternativa, você pode usar sshpass :

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

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

CONvid19
fonte
0

Dê uma olhada spurplus, um invólucro que desenvolvemos spurque fornece anotações de tipo e alguns truques menores (reconectando SFTP, md5 etc. ): Https://pypi.org/project/spurplus/

marko.ristin
fonte
1
um invólucro em torno de um invólucro em torno de um invólucro .. nice :)
Alex R
Bem, se você deseja manter seus scripts de implantação triviais, duvido que as ferramentas subjacentes sejam simples / abstratas o suficiente. Até agora, não observamos abstrações com vazamentos.
Mario.ristin 28/06/19
0

Solicitando ao Usuário que insira o comando de acordo com o dispositivo em que está efetuando login.
O código abaixo é validado pelo 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
fonte
0

Abaixo, exemplo, caso deseje entradas de usuário para nome do host, nome de usuário, senha e número da 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()
Harshan Gowda
fonte
5
Em vez de reposicionar sua resposta de dois dias atrás com melhor formatação, por que não editar a resposta mais antiga e melhorar sua formatação?
Snakecharmerb 13/05/19