python3基于paramiko模块登录CentOS服务器执行shell命令,实现服务器文件的上传和下载

paramiko是一个用于做远程控制的模块,使用该模块可以登录服务器执行shell命令或实现文件上传下载。

安装paramiko模块

pip3 install paramiko

远程登录CentOS服务器执行shell命令,监控应用程序进程状态,并往钉钉群推送告警消息
创建ssh对象

ssh = paramiko.SSHClient()

如果之前没有连接过的ip,会出现Are you sure you want to continue connecting (yes/no)? yes
设置自动选择yes

key = paramiko.AutoAddPolicy()
ssh.set_missing_host_key_policy(key)

连接服务器

ssh.connect(
hostname='10.133.0.41',port=22, username='a-libin', password='123456!b' ,timeout=5)

执行shell命令,返回一个元组

stdin, stdout, stderr = ssh.exec_command(ssh_command)

打印shell命令执行结果

# 方式一
for i in stdout.readlines():
    print(i)
# 方式二
#获取输出结果,decode('utf-8')解码是为了存在中文能够正常显示
result=stdout.read().decode('utf-8')

完整示例:连接CentOS7服务器,查看应用程序进程是否运行正常,当捕获不到应用程序进程,则向钉钉群推送告警信息

#paramiko是一个用于做远程控制的模块,使用该模块可以对远程服务器进行命令或文件操作
#基于linux的ssh服务
import paramiko
import requests
import json
import datetime

now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
print(now)

#钉钉告警接口地址
api_url = 'https://oapi.dingtalk.com/robot/send?access_token=67a72312e2c10de3cddae622c4282c60162bf0c252fd155a919507d4e9d3d15c'
headers={'Content-Type':'application/json;charset=utf-8'}

#钉钉告警
def msg(text):
    json_text={
        "msgtype":"markdown",
        "markdown":{
            "title": "状态通知",
            "text":text
        }
    }
    print(requests.post(api_url,json.dumps(json_text),headers=headers).content)


def exec_ssh_command(ssh_command):
    #创建一个ssh对象
    ssh = paramiko.SSHClient()
    #如果之前没有连接过的ip,会出现Are you sure you want to continue connecting (yes/no)? yes
    #自动选择yes
    key = paramiko.AutoAddPolicy()
    ssh.set_missing_host_key_policy(key)
    #连接服务器
    ssh.connect(
        hostname='10.133.0.41',port=22, username='a-libin', password='123456!b' ,timeout=5
    )
    #执行shell命令,返回的是一个元组
    #ls /opt/bi/kettle/etljobs_svn/lens_olap/
    stdin, stdout, stderr = ssh.exec_command(ssh_command)
	
	#返回shell命令执行结果
    # for i in stdout.readlines():
    #     print(i)
    #
    ##获取输出结果,decode('utf-8')解码是为了存在中文能够正常显示
    result=stdout.read().decode('utf-8')
    ssh.close()
    return result

if __name__ == '__main__':
	#ps -ef 命令就是列出当前所有的进程信息
    ssh_command = 'ps -ef|grep "airflow-webserver" |grep -v grep|wc -l'
    result = exec_ssh_command(ssh_command)
    # 5
    print(result)
	
	#当应用程序进程数小于1时推送告警
    if int(result)<1:
        text=f"""
    时间:{now}
    服务器地址:10.133.0.41
    告警信息:airflow程序运行异常,请注意查看
        """
        msg(text)

1)paramiko
paramiko属于第三方库,需要使用如下命令行安装。
paramiko包含了两个核心组件:SSHClient 和 SFTPClient

SSHClient:类似于Linux的ssh命令,是对SSH会话的封装,该类封装了传输(Transport),通道(Channel)及SFTPClient建立的方法(open_sftp),通常用于执行远程命令。
Transport:是一种加密的会话,使用时会同步创建一个加密的Tunnels(通道),这个Tunnels叫做Channel。
channel:类Socket,一种安全的SSH传输通道。
Session:Client与Server保持连接的对象。
SFTPClient:类似于Linux的sftp命令,是对SFTP客户端的封装,用以实现远程文件操作,如文件上传、下载、修改文件权限等操作。
2)SSHClient()

#实例化SSHClient
ssh = paramiko.SSHClient()
# 如果之前没有连接过的ip,会出现Are you sure you want to continue connecting (yes/no)? yes
# 自动选择yes
key = paramiko.AutoAddPolicy()
#set_missing_host_key_policy()设置远程服务器没有在know_hosts文件中记录时的应对策略。目前支持三种策略:
#AutoAddPolicy 自动添加主机名及主机密钥到本地HostKeys对象
#WarningPolicy 用于记录一个未知的主机密钥的python警告。功能上和AutoAddPolicy类似,但是会提示是新连接。
#RejectPolicy 自动拒绝未知的主机名和密钥。
ssh.set_missing_host_key_policy(key)
# 连接服务器,以用户名和密码进行认证
ssh.connect(
        hostname='10.133.0.41', port=22, username='xxx', password='xxx', timeout=50
#exec_command在远程服务器执行Linux命令的方法,打开一个Channel并执行命令,但无法执行sudo su 切换用户操作。
#stdin:标准输入
#stdout为正确输出
#stderr为错误输出
#同时只有一个变量有值
stdin, stdout, stderr = ssh.exec_command('cat /opt/bi/kettle/b.txt')  
#打印执行结果
#stdout.read()来进行输出,通过stdout.read().decode('utf-8')进行编码转换输出
#注意:返回的值是byte类型,而不是str类型,需要进行转换
print(stdout.read().decode('utf-8'))
#关闭SSHClient
ssh.close()

3)invoke_shell()
invoke_shell()打开一个新的子shell进程,用来与Linux进行交互,sudo su切换用户不受影响。
注意:建议使用invoke_shell打开shell,往里面发送命令时,通过代码调试,查看输出结果,从而调整recv()方法终止接收的条件。需要根据自己服务器调整代码。

ssh = paramiko.SSHClient()
# 如果之前没有连接过的ip,会出现Are you sure you want to continue connecting (yes/no)? yes
# 自动选择yes
key = paramiko.AutoAddPolicy()
ssh.set_missing_host_key_policy(key)
# 连接服务器
ssh.connect(hostname='10.133.0.41', port=22, username='xxx', password='xxx', timeout=50)

# 建立一个新的伪终端频道,用来执行子shell
channel = ssh.invoke_shell()

# 向shell发送命令, 注意每条指令后要跟一个换行符\n 当做回车确认
channel.send("sudo su bigdata \n")

#buff用来接收回显,参数是接收一次回显的输出流大小bufsize,bufsize可以自己调整
#如果recv()语句数量多于当前shell显示信息,那么程序就会一直运行,等待下一条shell语句,所以最好有个终止条件。
buff = ''
while not buff.endswith('password for a-libin: '):
	resp = channel.recv(9999)
    buff += resp.decode('utf-8')
print(buff)
#此处buff输出如下
#sudo su bigdata 
#[a-libin@aacetl1 ~]$ sudo su bigdata 
#[sudo] password for a-libin: 
#所以设置终结循环标志为password for a-libin: 
#因此终止循环条件需要根据实际调整

#发送root用户密码
channel.send('123456!b')
channel.send('\n')
buff = ''
#命令执行结束后Linux窗口会显示新的一行[bigdata@aacetl1 a-libin]$等待下条指令
#因此$可以用作识别全部输出结束的标志。
while not buff.endswith('$ '):  
	resp = channel.recv(9999)
    buff += resp.decode('utf-8')
print(buff)
#此处buff输出如下
#[bigdata@aacetl1 a-libin]$ 
#所以设置终结循环标志为$

# 查看是否切换成功
channel.send("whoami")
channel.send("\n")
buff = ''
while not buff.endswith('$ '):
    resp = channel.recv(9999)
    buff += resp.decode('utf-8')
print(buff)

#此处buff输出如下:
#whoami
#bigdata
#[bigdata@aacetl1 a-libin]$
#所以设置终止循环条件为&
Python远程登录Linux操作系统,执行命令、创建目录、上传及下载文件(之二)
远程连接Linux主机,执行命令、创建目录、上传及下载文件

#!/usr/bin/python
#coding:utf-8

import paramiko
from fabric.api import env,put,get
import threading

class Host:
def __init__(self,ip,user,password):
self.user=user
self.ip=ip
self.password=password

 

#执行远程指令

def run(self,cmd):
try:
ssh=paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(self.ip,username=self.user,password=self.password)
for m in cmd:
stdin,stdout,stderr=ssh.exec_command(m)
print stdout.read()
print "Check Status: %s\tOK\n"%(self.ip)
ssh.close()
except:
print "%s\tError\n"%(self.ip)

 

#创建目录

def mkdir(self):
try:
ssh=paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(self.ip,username=self.user,password=self.password)
sftp = ssh.open_sftp()
sftp.mkdir("remote_check")
print "Create folder 'remote_check' in remote hosts successfully!\n"
ssh.close()
except:
print "Create folder failure!\n"

 

#上传本地文件

def upload(self,uSRC,uDST):
env.user=self.user
env.password=self.password
env.host_string=self.ip
put("%s" %(uSRC),"%s" %(uDST))
print "Upload local file : \"%s\" to Host : %s \"%s\"\n" %(uSRC,self.ip.split(':')[0],uDST)

 

#从远程主机下载文件到本地主机

def download(self,dSRC,dDST):
env.user=self.user
env.password=self.password
env.host_string=self.ip
get("%s" %(dSRC),"%s" %(dDST))
print "Download remote file from : \"%s\" to : %s \"%s\"\n" %(dDST,self.ip.split(':')[0],dSRC)

 

if __name__ == '__main__':
t=Host("192.168.142.101","root","passwd")
print "Begin......\n"
t.mkdir()
t.upload("/home/test/1.txt","/home/remote_check")
t.download("/home/2.txt","/home/test/2.txt")
cmd = ["whoami","ls","rm -rf /home/1.txt"]
t.run(cmd)

less is beautiful