37
50

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Paramiko + scp 導入 ~ SSH接続 ~ SCPでファイル転送

Last updated at Posted at 2017-06-18

概要

  1. ssh接続からのコマンド送受信
  2. scpでのファイル取得

補足.
(A) PC側 paramiko + scp 導入
(B) パッケージ依存関係の調査
(C) インストール済みのパッケージのバージョンの調査
(D) パッケージ依存関係の可視化

やりたいこと

  1. ssh接続からのコマンド送受信
    PC(SSH Client) ---sshでコマンド実行(mkdir, tar, ...)--> ターゲット(SSH Host)

  2. scpでのファイル取得
    PC(SSH Client) <---scpでファイルを取得(xxx.tar.gz, ...)---------- ターゲット(SSH Host)

PC(SSH Client)
Windows 7 (64bit)
Python 3.5.2 - Anaconda 4.2.0 (64-bit)
ターゲット(SSH Host)
IPアドレスだけがわかっている(host名不明))
sshでrootログインできる
scpできる

(・・・という処理と調査に参照したサイトを備忘録としてまとめた)

コード

1. ssh接続からのコマンド送受信

192.168.1.100に対してrootでログインして、testというディレクトリを作成したい場合

ssh_mkdir1.py
import paramiko
with paramiko.SSHClient() as ssh:
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    ssh.connect(hostname='192.168.1.100', port=22, username='root', password='')
    stdin, stdout, stderr = ssh.exec_command('mkdir test')

短くかけていい感じ
なお、ssh.connect()でpassword か private key (もしくは両方)が必要とのこと。

SSH - Python with paramiko issue - Stack Overflow

You should provide either a password or a private key (or both), otherwise the SSH client does not know how to authenticate with the login data.

コメント + α をつけると以下のような感じです。

ssh_mkdir.py
import paramiko

# ssh clientオブジェクト(ssh)を作る・・・
with paramiko.SSHClient() as ssh:

    # どうやってhostname & keyを登録するのかわからないので、AutoAddPolicy()としておく
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    
    # ssh接続する
    ssh.connect(hostname='192.168.1.100', port=22, username='root', password='')
    
    # mkdir を実行する
    stdin, stdout, stderr = ssh.exec_command('mkdir test')
    
    # 実行結果のstdoutとstderrを読み出す
    for o in stdout:
        print(o)
    for e in stderr:
        print(e)

2. scpでのファイル取得

xxx.tar.gzを取得したい場合

scp.py
import paramiko
import scp

with paramiko.SSHClient() as ssh:
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    ssh.connect(hostname='192.168.1.100', port=22, username='root', password='')

    # scp clientオブジェクト生成
    with scp.SCPClient(ssh.get_transport()) as scp:
       # scpに対してgetするだけでファイルが取得できる
       scp.get('xxx.tar.gz','xxx.tar.gz')

ターゲットにtest.py(10個のファイルを作成するスクリプト)をscpでコピーして実行する場合

test.py
# -*- coding: utf-8 -*-
for n in range(10):
  with open("test" + str(n), "w+") as f:
     f.write("test" + str(n))
scp2.py
# -*- coding: utf-8 -*-
import paramiko
import scp

with paramiko.SSHClient() as ssh:
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    ssh.connect(hostname='192.168.1.100', port=22, username='root', password='')

    stdin, stdout, stderr = ssh.exec_command('mkdir test_dir')
    for o in stdout:
        print(o.strip())

    with scp.SCPClient(ssh.get_transport()) as scp:
       scp.put('test.py','test_dir/test.py')

    stdin, stdout, stderr = ssh.exec_command('cd test_dir;python test.py')
    for o in stdout:
        print(o.strip())

    stdin, stdout, stderr = ssh.exec_command('cd test_dir;ls')
    for o in stdout:
        print(o.strip())

for o in stdoutなどで処理待ちを入れないと、lsやpython test.pyの部分で同期が取れないようだ。

  • フォルダ作成前にscp.put()が実行される
  • sh.exec_command('cd test_dir;ls')のタイミングで、python test.pyが未完了であり、期待したフォルダが表示されない。

といったことがあった

最終的な形

これをマージして以下のようにした。

get_logs.py
# -*- coding: utf-8 -*-
import paramiko
import scp
import pathlib
from datetime import datetime
import sys

log_dir = str(datetime.now().strftime('log_%Y%m%d_%H%M%S'))
log_tar_gz = log_dir + '.tar.gz'

remote_path = log_tar_gz
local_path  = str(pathlib.Path(__file__).resolve().parent.joinpath(log_tar_gz))

collect_log_cmds = [
   'mkdir ' + log_dir, 
   # To Do : log_dirにログを集める処理を何かしら書く
   'tar -zcvf ' + log_tar_gz + ' ' + log_dir
]

cleanup_cmds = [
	'rm -r ' + log_dir,
	'rm ' + log_tar_gz
]

def check_result(stdin, stdout, stderr):
    result = True
    for o in stdout:
        if o:
            print("    " + o.strip())
    for e in stderr:
        if e:
            print("    " + e.strip())
            result = False
    return result

def exec_command(ssh, cmds):
    result = True
    for cmd in cmds:
        print("  " + cmd)
        stdin, stdout, stderr = ssh.exec_command(cmd)
        if check_result(stdin, stdout, stderr):
            pass
        else:
            print("ERROR -- " + cmd)
            result = False
    return result

with paramiko.SSHClient() as ssh:
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    ssh.connect(hostname='192.168.1.100', port=22, username='root', password='')
    
    print("start -- collect_log")
    if exec_command(ssh,  collect_log_cmds):
        print("done -- collect_log)
    else:
        sys.exit()

    print("start -- scp get " + remote_path)
    with scp.SCPClient(ssh.get_transport()) as scp:
        scp.get(remote_path,local_path)

    if pathlib.Path(local_path).is_file():
        print("done  -- scp get " + local_path)
    else:
        print("ERROR -- scp get " + local_path)
        sys.exit()

    print("start -- cleanup")
    if exec_command(ssh, cleanup_cmds):
        print("done  -- cleanup")
    else:
        print("ERROR -- cleanup")
        sys.exit()

補足

(A) PC側 paramiko + scp 導入

paramiko 2.2.1 パッケージ依存関係

paramiko.png

  • 図の赤丸がインストールされていなかったパッケージ
  • 調査方法は「補足(B)~(D)」を参照

パッケージ取得

  • pypiにブラウザでアクセスして.whlをダウンロードした
required used .whl
bcrypt(>=3.1.3)  bcrypt 3.1.3 bcrypt-3.1.3-cp35-cp35m-win_amd64.whl
pynacl(>=1.0.1) pynacl 1.1.2 PyNaCl-1.1.2-cp35-cp35m-win_amd64.whl
paramiko(2.2.1)  paramiko 2.2.1 paramiko-2.2.1-py2.py3-none-any.whl
scp(0.10.2)  scp 0.10.2 scp-0.10.2-py2.py3-none-any.whl
  • bcryptの.whlはどれなのかわからなかったので、環境に合わないものをいれたときのエラーでOK/NGを判断した(トライアンドエラーで解決した。)

パッケージインストール

  • pip install <ダウンロードした.whl>で実施
> pip install bcrypt-3.1.3-cp35-cp35m-win_amd64.whl
・・・
> pip install PyNaCl-1.1.2-cp35-cp35m-win_amd64.whl
・・・
> pip install paramiko-2.2.1-py2.py3-none-any.whl
・・・・
> pip install scp-0.10.2-py2.py3-none-any.whl
・・・・

(B) パッケージ依存関係の調査

  • pip installを実行してごりごり調べた
  • 「Collecting bcrypt>=3.1.3 (from paramiko==2.2.1)」といったメッセージで出てくる
> pip install paramiko-2.2.1-py2.py3-none-any.whl
Collecting bcrypt>=3.1.3 (from paramiko==2.2.1)
  Retrying (Retry(total=4, connect=None, read=None, redirect=None)) after connection broken by 'NewConnectionError('<pip._vendor.requests.packages.urllib3.connection.VerifiedHTTPSConnection object at 0x02DB4A30>: Failed to establish a new connection: [Errno 11004] getaddrinfo failed',)': /simple/bcrypt/
・・・
  • paramikoのインストールが可能になると、以下のようになる
> pip install paramiko-2.2.1-py2.py3-none-any.whl
Requirement already satisfied: bcrypt>=3.1.3 in ・・・\lib\site-packages (from paramiko==2.2.1)
Requirement already satisfied: cryptography>=1.1 in ・・・\lib\site-packages (from paramiko==2.2.1)
Requirement already satisfied: six in ・・・\lib\site-packages (from pynacl>=1.0.1->paramiko==2.2.1)
Requirement already satisfied: cffi>=1.4.1 in ・・・\lib\site-packages (from pynacl>=1.0.1->paramiko==2.2.1)
Requirement already satisfied: idna>=2.0 in ・・・\ib\site-packages (from cryptography>=1.1->paramiko==2.2.1)
Requirement already satisfied: setuptools>=11.3 in in ・・・\lib\site-packages\setuptools-27.2.0-py3.6.egg (from cryptography>=1.1->paramiko==2.2.1)
Requirement already satisfied: pycparser in ・・・\ib\site-packages (from cffi>=1.4.1->pynacl>=1.0.1->paramiko==2.2.1)
Installing collected packages: pynacl, bcrypt, paramiko

あとでわかったこと・・・ .whlは.zipである。

  • paramiko-2.2.1-py2.py3-none-any.whlを.zipに変換して解凍する。
  • paramiko-2.2.1.dist-infoにMETADATAが入っている。
  • METADATAをテキストエディタで開くと、Requires-Distに書いてある。

ので、調査しようと思えばできそう。

paramiko-2.2.1.dist-info/METADATA
Metadata-Version: 2.0
Name: paramiko
Version: 2.2.1
・・・
Requires-Dist: bcrypt (>=3.1.3)
Requires-Dist: cryptography (>=1.1)
Requires-Dist: pynacl (>=1.0.1)
Requires-Dist: pyasn1 (>=0.1.7)
・・・

(C) インストール済みのパッケージのバージョンの調査

  • pip freezeをすると一覧でだいたい取れる
> pip freeze
alabaster==0.7.9
anaconda-client==1.6.0
anaconda-navigator==1.5
・・・略・・・
ipython
In [1]: import setuptools

In [2]: setuptools.__version__
Out[2]: '27.2.0'

In [3]: import pycparser

In [4]: pycparser.__version__
Out[4]: '2.14'

(D) パッケージ依存関係の可視化

  • 補足(B)と(C)の結果から、dotをテキストエディタでごりごり書いた。
  • pipdeptreeを使えばもう少し楽にできそう。
paramiko_depend.dot
digraph pramiko {
   scp->paramiko
   paramiko->bcrypt;
   paramiko->pynacl;
   paramiko->pyasn1;
   paramiko->cryptography;
   bcrypt->cffi;
   bcrypt->six;
   cryptography->idna;
   cryptography->setuptools;
   cffi->pycparser;
   scp[label="scp\n(0.10.2)", color = "red"];
   paramiko[label="paramiko\n(2.2.1)", color = "red"];
   bcrypt[label="bcrypt\n(>=3.1.3)", color = "red"];
   pynacl[label="pynacl\n(>=1.0.1)", color = "red"];
   pyasn1[label="pyasn1\n(0.1.9)"];
   cryptography[label="cryptography\n(1.5)"];
   cffi[label="cffi\n(1.7.0)"];
   six[label="six\n(1.10.0)"]
   idna[label="idna\n(2.1)"]
   setuptools[label="setuptools\n(27.2.0)"]
   pycparser[label="pycparser\n(2.14)"]
}
  • Graphvizでpng化(Graphviz2.38\bin\dot.exe)
> dot.exe -Tpng paramiko_depend.dot > paramiko_depend.png

参考

paramiko 2.2.1 : Python Package Index
scp 0.10.2 : Python Package Index
Paramiko Release
Paramikoでコマンドの送信、結果の記録を自動で行う - Qiita
SSH - Python with paramiko issue - Stack Overflow
Paramikoを使ってシェルスクリプトをリモートホストに流しこむ - Qiita
paramiko を使って Python で SFTP/SCPを行う - Librabuc
Pythonでscp - 面白コンテンツ探求日記
SSH - Python with paramiko issue - Stack Overflow
【Python】pipとwheel – Qiita
pythonパッケージの依存関係をgraphvizで可視化 – Qiita
pythonモジュールのバージョンは - Drkcore
DOT言語入門 | You Look Too Cool

37
50
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
37
50

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?