1.介绍:
ssh
是一个协议,OpenSSH
是其中一个开源实现,paramiko
是Python
的一个库,实现了SSHv2
协议(底层使用cryptography
)。
有了Paramiko
以后,我们就可以在Python
代码中直接使用SSH
协议对远程服务器执行操作,而不是通过ssh
命令对远程服务器进行操作。
由于paramiko
属于第三方库,所以需要使用如下命令先行安装:pip install paramiko
2.paramiko介绍:
paramiko
包含两个核心组件:SSHClient
和SFTPClient
。SSHClient
的作用类似于Linux
的ssh
命令,是对SSH
会话的封装,该类封装了传输(Transport
),通道(Channel
)及SFTPClient
建立的方法(open_sftp
),通常用于执行远程命令。SFTPClient
的作用类似与Linux
的sftp
命令,是对SFTP
客户端的封装,用以实现远程文件操作,如文件上传、下载、修改文件权限等操作。
3.实践:连接多个remote并实现上传下载和运行command
# -*- coding: utf-8 -*-
import paramiko
from paramiko import SSHClient as sc
import logging
import os
# 把接收的stdout转成generator,不然超过3k,程序会被hang住
class ReceiveGenerator(object):
def __init__(self, stdout):
self.output = stdout
self.out_channel = stdout.channel
def __iter__(self):
return self
def next(self):
"""
python2 compatibly
"""
return self.out_channel.recv(1024)
def __next__(self):
"""
python3 compatibly
"""
return self.out_channel.recv(1024)
def exit_code_ready(self):
return self.out_channel.exit_status_ready()
def exit_code(self):
return self.out_channel.recv_exit_status()
class SSHClient(object):
def __init__(self, transport, sftp, ssh):
self.transport = transport
self.sftp = sftp
self.ssh = ssh
self.hostname = None
@staticmethod
def connect(hostname, port, username, key_path):
try:
# 私钥的文件路径
key = paramiko.RSAKey.from_private_key_file(key_path)
# 创建transport通道
transport = paramiko.Transport(hostname, port)
transport.connect(username=username, pkey=key)
# 创建sftp通道
sftp = paramiko.SFTPClient.from_transport(transport)
ssh = sc()
ssh._transport = transport
# logger.info('connection is succeed, hostname: %s', hostname)
return SSHClient(transport, sftp, ssh)
except:
raise Exception('connection is failed')
# 执行linux上的命令
def ssh_command(self, command):
stdin, stdout, stderr = self.ssh.exec_command(command)
return stdin, stdout, stderr
# 上传
def sftp_upload(self, local_path, remote_path):
self.sftp.put(localpath=local_path, remotepath=remote_path)
# 下载
def sftp_download(self, local_path, remote_path):
self.sftp.get(localpath=local_path, remotepath=remote_path)
def close(self):
self.transport.close()
def structure_output(self, command):
stdin, stdout, stderr = self.ssh_command(command)
# structure a generator class
return ReceiveGenerator(stdout), stderr
# 如果params是string的话直接上传至指定文件, 如果是路径直接上传并执行commamd
def script(self, params, remote_path, command=None):
if os.path.isfile(params):
self.sftp_upload(params, remote_path)
stdout_iterator, stderr = self.structure_output(command)
out = ['']
for i in stdout_iterator:
# 返回长度为0通道关闭
if not len(i):
break
out[0] += i
return {
'stdout': out[0],
'stderr': stderr.read().decode('utf-8'),
'exitcode': stdout_iterator.exit_code()
}
ftp_file = self.sftp.file(remote_path, 'w', -1)
ftp_file.write(params)
ftp_file.flush()
stdout_iterator, stderr = self.structure_output(command)
return {
'stdout': stdout_iterator,
'stderr': stderr.read().decode('utf-8'),
'exitcode': stdout_iterator.exit_code()
}
if __name__ == '__main__':
logger = logging.getLogger(__name__)
logger.setLevel(level=logging.INFO)
handler = logging.StreamHandler()
logger.addHandler(handler)
def main(hostname_list, port, username, pkey_path, input_params, remote_path, command):
"""
:param hostname_list: list, of hostname
:param port: int, port
:param username: string
:param pkey_path: string, path
:param input_params: string, it can be path in local or string
:param remote_path: string, path of remote
:param command: string
:return: a generator class
"""
for hostname in hostname_list:
ssh_client = SSHClient.connect(hostname, port, username, pkey_path)
generator_outputs = ssh_client.script(params=input_params, remote_path=remote_path, command=command)
yield generator_outputs
ssh_client.close()
test_hostname_list = ['52.82.7.121']
test_port = 22
test_username = 'ubuntu'
test_pkey = '/home/python/Desktop/fastone-demo.pem'
test_input_params = '/home/python/Desktop/cb-aws-vmtypes.json'
test_remote_path = 'test.txt'
test_command = 'cat test.txt'
test_outputs_list = main(test_hostname_list,
test_port,
test_username,
test_pkey,
test_input_params,
test_remote_path,
test_command
)
for outputs in test_outputs_list:
for output in outputs:
print('output : ', output)
这里不熟悉的朋友可以先把每个函数拆开实现,再抽象成类。
以上!