python使用paramiko实现ssh定时执行命令

#!/usr/bin/env python3
# -*- coding:utf-8 -*-
# @Author: Rosaany
import functools

from paramiko.ssh_exception import NoValidConnectionsError, AuthenticationException
from argparse import RawTextHelpFormatter
import paramiko  # 第三方库, pip安装, pip install paramiko -i https://pypi.douban.com/sample
import time
import sys
import argparse
import socket


def g_time(func):
    # 计时装饰器
    def inner(*arg, **kwarg):
        s_time = time.time()
        res = func(*arg, **kwarg)
        e_time = time.time()
        t_time = e_time - s_time
        days = t_time // (24 * 60 * 60)
        t_time %= (24 * 60 * 60)
        hours = t_time // (60 * 60)
        t_time %= (60 * 60)
        minutes = t_time // 60
        t_time %= 60
        seconds = t_time % 60
        print('>>>程序运行时间:{days:.0f}天{hours:.0f}时{minutes:.0f}分{seconds:.0f}秒'.format(days=days, hours=hours,
                                                                                     minutes=minutes, seconds=seconds))
        return res

    return inner


def retry(exceptions: list, times=3, wait=1):
    """重连机制"""
    if not isinstance(exceptions, (tuple, list)):
        new_exceptions = [exceptions]
    else:
        new_exceptions = exceptions

    def inner_retry(func, count, *args, **kwargs):

        if count > times:
            return
        try:
            return func(*args, **kwargs)
        except tuple(new_exceptions) as e:
            if count < times:
                time.sleep(wait)
                return inner_retry(func, count + 1, *args, **kwargs)
            else:
                print("重连失败!")  # raise e

    def decorator(func):
        @functools.wraps(func)
        def wrapped(*args, **kwargs):
            return inner_retry(func, 0, *args, **kwargs)

        return wrapped

    return decorator


def ssh_connected(ssh, cmds, waits, verbose):
    try:
        # 激活交互式shell
        # https://stackoverflow.com/questions/6203653/how-do-you-execute-multiple-commands-in-a-single-session-in-paramiko-python
        channel = ssh.invoke_shell()
        time.sleep(1)

        stop = True
        while stop:
            try:
                for cmd in cmds:
                    channel.send(cmd.encode())
                    # 模拟回车'Enter'这个动作
                    channel.send(b'\n')
                    # 每个命令间隔时间
                    time.sleep(1)
                    r = channel.recv(40960).decode()
                    if verbose:
                        print(r)
                # 执行完一轮命令,等待时间
                time.sleep(waits)
            except KeyboardInterrupt:  # 捕获ctrl + c时终止程序
                stop = False

            if not waits:
                stop = False

        channel.close()
        ssh.close()
    except AttributeError as e:
        raise e


@g_time
@retry([AttributeError, OSError], times=3, wait=3)
def ssh_client(host: str, user: str, pwd: str, cmds: str, waits: int, verbose: int):
    """ssh客户端"""

    # 私钥文件的存放路径
    # private = paramiko.RSAKey.from_private_key_file(r'C:\Users\xxxx\Documents\xxx')
    # 创建一个实例化
    ssh = paramiko.SSHClient()
    # 加载系统SSH密钥
    ssh.load_system_host_keys()
    # 自动添加策略,保存服务器的主机名和密钥信息,如果不添加,那么不在本地knows_hosts文件中记录的主机将无法连接,默认拒接
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    # 连接设备
    try:
        ssh.connect(hostname=host, username=user, timeout=5, compress=True, password=pwd  # pkey=private,  #可以采用密钥连接
                    )

        print("正在连接主机ip: {}.....".format(host))
    except NoValidConnectionsError:
        print('连接出现了问题')
    except AuthenticationException:
        print('用户名或密码错误')
    except socket.timeout:
        print('请检查你的远程主机通讯连接是否正常')
    except Exception as err:
        print('其他错误问题: {}'.format(err))
    finally:
        ssh_connected(ssh, cmds, waits, verbose)


def tool():
    """
    命令行参数
    """
    ssh_client_help = "一个简单的SSH脚本定时执行命令脚本,如想终止脚本,请按下快捷键ctrl+c \n" \
                      "简单的示例:$python sshclient_exec_command.py -cmds 'top, ls, ifconfig'"

    if len(sys.argv) == 1:
        sys.argv.append('--help')

    parser = argparse.ArgumentParser(description=ssh_client_help, formatter_class=RawTextHelpFormatter)

    parser.add_argument('-u', type=str, default='root', help='ssh访问主机的用户名,默认用户名是root')
    parser.add_argument('-p', type=str, default='root', help='ssh访问主机的密码,默认密码是root')
    parser.add_argument('-host', type=str, default='192.168.1.248', help='ssh访问主机ip,默认访问主机是192.168.1.248')
    parser.add_argument('-cmds', type=str, default="ls -ll, ifconfig",
                        help='执行输入的命令,多个以英文逗号分隔,默认输入命令是"ls -ll,ifconfig"')
    parser.add_argument('-t', type=str, default=0, help='输入一个定时等待时间,单位是秒,默认定时时间为0秒')
    parser.add_argument('-v', type=int, default=1, help='显示详细信息,默认输出详细信息')

    arguments = parser.parse_args()
    return arguments


def main():
    """主程序"""
    arguments = tool()
    host = arguments.host
    user = arguments.u
    pwd = arguments.p
    cmds = arguments.cmds
    verbose = arguments.v
    waits = arguments.t

    if not isinstance(verbose, int):
        verbose = int(verbose)

    if not isinstance(waits, int):
        waits = int(waits)

    if isinstance(cmds, str):
        cmds = cmds.split(',')

    print("执行命令:{cmd}".format(cmd=cmds))

    ssh_client(host=host, user=user, pwd=pwd, cmds=cmds, verbose=verbose, waits=waits)


if __name__ == '__main__':
    # linux实时查看ssh连接通道state变化,-n 1表示每一秒查询一遍,注意TCP四次挥手是否正常释放
    # watch -n 1 -d 'netstat -anlt | grep 192.168.1.1'
    main()

使用示例

$python .\sshclient_exec_commands.py
usage: sshclient_exec_commands.py [-h] [-u U] [-p P] [-host HOST] [-cmds CMDS] [-t T] [-v V]

一个简单的SSH脚本定时执行命令脚本,如想终止脚本,请按下快捷键ctrl+c
简单的示例:$python sshclient_exec_command.py -cmds 'top, ls, ifconfig'

optional arguments:
  -h, --help  show this help message and exit
  -u U        ssh访问主机的用户名,默认用户名是root
  -p P        ssh访问主机的密码,默认密码是123456
  -host HOST  ssh访问主机ip,默认访问主机是192.168.1.1
  -cmds CMDS  执行输入的命令,多个以英文逗号分隔,默认输入命令是"ls -ll,ifconfig"
  -t T        输入一个定时等待时间,单位是秒,默认定时时间为0秒
  -v V        显示详细信息,默认输出详细信息
  • 定时执行

加上-t参数,默认不执行定时,单位是秒
解释:ssh连接主机192.168.1.1,用户名root密码123456,在当前用户目录下每秒输入一次“ls”命令。

python .\sshclient_exec_commands.py -host 192.168.1.1 -u root -p 123456 -cmds "ls" -t 1

  • 执行多个命令

比如:-cmds "ls -l, ifconfig",多个命令,中间加入一个英文逗号
python .\sshclient_exec_commands.py -host 192.168.1.1 -u root -p 123456 -cmds "ls -l, ifconfig" -t 1

posted @ 2022-04-02 18:27  Rosaany  阅读(252)  评论(0编辑  收藏  举报