Eu tenho um arquivo de texto em minha máquina local que é gerado por um script Python diário executado no cron.
Eu gostaria de adicionar um pouco de código para que esse arquivo seja enviado com segurança ao meu servidor por SSH.
Eu tenho um arquivo de texto em minha máquina local que é gerado por um script Python diário executado no cron.
Eu gostaria de adicionar um pouco de código para que esse arquivo seja enviado com segurança ao meu servidor por SSH.
Respostas:
Você pode chamar o scp
comando bash (ele copia arquivos por SSH ) com subprocess.run
:
import subprocess
subprocess.run(["scp", FILE, "USER@SERVER:PATH"])
#e.g. subprocess.run(["scp", "foo.bar", "joe@srvr.net:/path/to/foo.bar"])
Se você estiver criando o arquivo que deseja enviar no mesmo programa Python, convém chamar o subprocess.run
comando fora do with
bloco que está usando para abrir o arquivo (ou chamar .close()
o arquivo primeiro se não estiver usando um with
bloco), para que você saiba que é liberado para o disco a partir do Python.
Você precisa gerar (na máquina de origem) e instalar (na máquina de destino) uma chave ssh de antemão para que o scp seja autenticado automaticamente com sua chave ssh pública (em outras palavras, para que seu script não peça uma senha) .
subprocess.check_call(['scp', srcfile, dest])
pode ser usado desde Python 2.5 em vez derc = Popen(..).wait(); if rc != 0: raise ..
Para fazer isso em Python (ou seja, sem envolver scp por meio de subprocesso.Popen ou semelhante) com a biblioteca Paramiko , você faria algo assim:
import os
import paramiko
ssh = paramiko.SSHClient()
ssh.load_host_keys(os.path.expanduser(os.path.join("~", ".ssh", "known_hosts")))
ssh.connect(server, username=username, password=password)
sftp = ssh.open_sftp()
sftp.put(localpath, remotepath)
sftp.close()
ssh.close()
(Você provavelmente gostaria de lidar com hosts desconhecidos, erros, criar quaisquer diretórios necessários e assim por diante).
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
após a instanciação ssh
.
Você provavelmente usaria o módulo de subprocesso . Algo assim:
import subprocess
p = subprocess.Popen(["scp", myfile, destination])
sts = os.waitpid(p.pid, 0)
Onde destination
está provavelmente da forma user@remotehost:remotepath
. Obrigado a @Charles Duffy por apontar a fraqueza em minha resposta original, que usava um único argumento de string para especificar a operação scp shell=True
- que não lidaria com espaços em branco nos caminhos.
A documentação do módulo tem exemplos de verificação de erros que você pode querer executar em conjunto com esta operação.
Certifique-se de configurar as credenciais adequadas para que possa executar um scp autônomo e sem senha entre as máquinas . Já existe uma questão stackoverflow para isso .
subprocess.check_call(['scp', myfile, destination])
poderia ser usado em vez disso desde Python 2.5 (2006)
Existem algumas maneiras diferentes de abordar o problema:
Cada abordagem tem suas peculiaridades. Você precisará configurar as chaves SSH para habilitar logins sem senha se estiver agrupando comandos do sistema como "ssh", "scp" ou "rsync". Você pode inserir uma senha em um script usando Paramiko ou alguma outra biblioteca, mas pode achar a falta de documentação frustrante, especialmente se você não estiver familiarizado com os fundamentos da conexão SSH (por exemplo, trocas de chaves, agentes, etc). Provavelmente nem é preciso dizer que as chaves SSH quase sempre são uma ideia melhor do que as senhas para esse tipo de coisa.
NOTA: é difícil superar o rsync se você planeja transferir arquivos via SSH, especialmente se a alternativa for o scp antigo.
Usei Paramiko com o objetivo de substituir chamadas de sistema, mas me vi atraído de volta para os comandos agrupados devido à sua facilidade de uso e familiaridade imediata. Você pode ser diferente. Dei uma olhada em Conch há algum tempo, mas não me agradou.
Se optar pelo caminho de chamada do sistema, Python oferece uma variedade de opções, como os.system ou os comandos / módulos de subprocesso. Eu iria com o módulo de subprocesso se estiver usando a versão 2.4+.
Alcançou o mesmo problema, mas em vez de "hackear" ou emular linha de comando:
Encontrou esta resposta aqui .
from paramiko import SSHClient
from scp import SCPClient
ssh = SSHClient()
ssh.load_system_host_keys()
ssh.connect('example.com')
with SCPClient(ssh.get_transport()) as scp:
scp.put('test.txt', 'test2.txt')
scp.get('test2.txt')
Você pode fazer algo assim, para lidar com a verificação da chave do host também
import os
os.system("sshpass -p password scp -o StrictHostKeyChecking=no local_file_path username@hostname:remote_path")
fabric
pode ser usado para fazer upload de arquivos vis ssh:
#!/usr/bin/env python
from fabric.api import execute, put
from fabric.network import disconnect_all
if __name__=="__main__":
import sys
# specify hostname to connect to and the remote/local paths
srcdir, remote_dirname, hostname = sys.argv[1:]
try:
s = execute(put, srcdir, remote_dirname, host=hostname)
print(repr(s))
finally:
disconnect_all()
Você pode usar o pacote vassalo, que é projetado exatamente para isso.
Tudo que você precisa é instalar o vassalo e fazer
from vassal.terminal import Terminal
shell = Terminal(["scp username@host:/home/foo.txt foo_local.txt"])
shell.run()
Além disso, você salvará as credenciais de autenticação e não precisará digitá-las novamente.
Usei sshfs para montar o diretório remoto via ssh e shutil para copiar os arquivos:
$ mkdir ~/sshmount
$ sshfs user@remotehost:/path/to/remote/dst ~/sshmount
Então, em python:
import shutil
shutil.copy('a.txt', '~/sshmount')
Esse método tem a vantagem de poder transmitir dados se estiver gerando dados, em vez de armazenar em cache localmente e enviar um único arquivo grande.
Tente fazer isso se não quiser usar certificados SSL:
import subprocess
try:
# Set scp and ssh data.
connUser = 'john'
connHost = 'my.host.com'
connPath = '/home/john/'
connPrivateKey = '/home/user/myKey.pem'
# Use scp to send file from local to host.
scp = subprocess.Popen(['scp', '-i', connPrivateKey, 'myFile.txt', '{}@{}:{}'.format(connUser, connHost, connPath)])
except CalledProcessError:
print('ERROR: Connection to host failed!')
Usando o recurso externo paramiko;
from paramiko import SSHClient
from scp import SCPClient
import os
ssh = SSHClient()
ssh.load_host_keys(os.path.expanduser(os.path.join("~", ".ssh", "known_hosts")))
ssh.connect(server, username='username', password='password')
with SCPClient(ssh.get_transport()) as scp:
scp.put('test.txt', 'test2.txt')
Chamar o scp
comando via subprocesso não permite receber o relatório de progresso dentro do script. pexpect
pode ser usado para extrair essa informação:
import pipes
import re
import pexpect # $ pip install pexpect
def progress(locals):
# extract percents
print(int(re.search(br'(\d+)%$', locals['child'].after).group(1)))
command = "scp %s %s" % tuple(map(pipes.quote, [srcfile, destination]))
pexpect.run(command, events={r'\d+%': progress})
Veja o arquivo de cópia do python na rede local (linux -> linux)
Uma abordagem muito simples é a seguinte:
import os
os.system('sshpass -p "password" scp user@host:/path/to/file ./')
Nenhuma biblioteca python é necessária (apenas sistema operacional) e funciona, no entanto, o uso desse método depende de outro cliente ssh para ser instalado. Isso pode resultar em um comportamento indesejado se executado em outro sistema.
Tipo de hacky, mas o seguinte deve funcionar :)
import os
filePath = "/foo/bar/baz.py"
serverPath = "/blah/boo/boom.py"
os.system("scp "+filePath+" user@myserver.com:"+serverPath)