概要
- ssh接続からのコマンド送受信
- scpでのファイル取得
補足.
(A) PC側 paramiko + scp 導入
(B) パッケージ依存関係の調査
(C) インストール済みのパッケージのバージョンの調査
(D) パッケージ依存関係の可視化
やりたいこと
- teratermなどで手作業でやっていたことをpythonで自動化したい。
- paramiko を使って Python で SFTP/SCPを行う - Librabucが近いがSCPのコードがなかった
-
ssh接続からのコマンド送受信
PC(SSH Client) ---sshでコマンド実行(mkdir, tar, ...)--> ターゲット(SSH Host) -
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というディレクトリを作成したい場合
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.
コメント + α をつけると以下のような感じです。
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を取得したい場合
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でコピーして実行する場合
# -*- coding: utf-8 -*-
for n in range(10):
with open("test" + str(n), "w+") as f:
f.write("test" + str(n))
# -*- 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が未完了であり、期待したフォルダが表示されない。
といったことがあった
最終的な形
これをマージして以下のようにした。
# -*- 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 パッケージ依存関係
- 図の赤丸がインストールされていなかったパッケージ
- 調査方法は「補足(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に書いてある。
ので、調査しようと思えばできそう。
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
・・・略・・・
- setuptoolsとpycparserは、pip freezeで出てこないので"version"から取得
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を使えばもう少し楽にできそう。
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