mohand-plugin-expect

概览

MoHand 插件,用以提供可自动控制连接应用的任务支持,可用于自动化登录堡垒机完成跳转选择、 账户密码输入等操作。

基于 pexpect 包做的封装实现,并提供了一个增强的 action() 接口, 支持基于正则的 sendline 发送。

其使用原理同 shell 下的 expect ,建议您提前了解下,以便更好的理解接下来的样例。

注解

值得一提的是,通过 shell 脚本使用 expect 登录后,改变窗口尺寸会出现 stty size 并没有根据窗口变换而变换的问题。而这种问题在纯手工登录时不会发生。

在开发本插件时,也遇到了同样的问题,但强迫症实在不能忍,实在太膈应了,于是通过额外监听 SIGWINCH 系统信号,来针对变更自动设置窗口的 stty 大小

安装方法

您可以通过 pip 进行安装,本包仅在 Python 3.X 下测试通过:

pip3 install mohand-plugin-expect

提示

v1.0.1 版本开始,增加了对 Python 2.X 的支持,但由于我主要在 Py3 环境下使用,所以强烈建议您在 Py3 下使用。如果您在 Py2 环境下遇到任何异常, 可以及时提 Issues 给我,我会努力在搬砖的间隙进行修复。。。

注解

建议使用 virtualenv 来安装,避免与其他包产生依赖冲突。

如果您感兴趣的话,可以了解下 virtualenvwrapper ,用其来管理虚拟环境可谓丝般顺滑!

使用说明

接下来我们将模拟一个堡垒机登录场景,并通过本包提供的 expect() 装饰器来实现自动化登录, 场景如下:

ssh -o PreferredAuthentications=password moore@bastion -p 22
moore@bastion's password:
********************************************************************************
*                      Shterm Interactive Terminal v3.2.4                      *
* Copyright (c) 2006-2018 Zhejiang Qizhi Tech. Co., Ltd.  All rights reserved. *
********************************************************************************


System List
  No: System
   0: All Systems
   1: idc-arch
   2: idc-dev
   3: idc-web
Please select system:2

Connecting to any@dev-moore.idc1(10.10.2.213) ...
login: moore
moore@10.10.2.213's password:
Last login: Tue May 15 10:06:23 2018 from bastion
moore@dev-moore.idc1:~$

为实现该登录流程,我们可以在当前路径下的 handfile.py 中编写如下:

import re
from mohand.hands import hand

ACCOUNT_USERNAME = 'moore'
ACCOUNT_PASSWORD = 'xxooxxooyo~~'

@hand.expect(
    cmd='ssh -o PreferredAuthentications=password moore@bastion -p 22')
def test(o):
    """自动登录测试"""
    o.action(
        expect=re.compile(r'[Pp]assword:'),
        sendline=ACCOUNT_PASSWORD)
    o.action(
        expect='Please select system:',
        sendline=re.compile(r'(\d+):\s+idc-dev'),
        before='System List')
    o.action(
        expect='login:',
        sendline=ACCOUNT_USERNAME,
        before='Connecting to')
    o.action(
        expect='password:',
        sendline=ACCOUNT_PASSWORD)

以上即为一个最小可用登录命令,注意此处分别在第一个 action 的 expect 参数与第二个 action 的 sendline 中使用了正则表达式,主要为了演示可以这样使用,而非必须,但为了更好地鲁棒性, 建议多使用正则来实现。

注解

如果您不善于书写正则表达式的话,可以尝试我修改&部署的一个 正则验证工具

将其保存到当前路径下,然后执行 mohand 命令:

$ mohand
Usage: mohand [OPTIONS] COMMAND [ARGS]...

  通用自动化处理工具

  详情参考 `GitHub <https://github.com/littlemo/mohand>`_

Options:
  --author   作者信息
  --version  版本信息
  --help     Show this message and exit.

Commands:
  test  自动登录测试

我们可以看到刚刚编写的 test 函数已经被注册成为了一个子命令, 通过执行该子命令我们就可以实现自动化登录到目标主机了:

$ mohand test

由于在 mohand.hands.hand 中封装了 click 库,故我们还可以根据实际需求来添加额外的传参, 具体可以参考其官方文档,以下提供一种思路:

FUNC_DICT = {
    'project': 'cd path/to/project',
    'mongo': 'mongo xxoo:2000/db_name',
}

@hand._click.option(
    '--workspace', '-w',
    type=hand._click.Choice(FUNC_DICT.keys()),
    help='工作空间')
@hand.expect(
    cmd='ssh -o PreferredAuthentications=password moore@bastion -p 22')
def test(o, workspace):
    """自动登录测试"""
    ...

    if workspace:
        o.action(
            expect=re.compile(r'.*@.*:.*~.*\$'),
            sendline=FUNC_DICT.get(workspace))

以上,提供了一个可选的关键字参数 --workspace ,可通过在调用时传入此参数, 来达到额外进入工作空间的目的,调用命令如下:

$ mohand test -w project  # 自动登录,并进入项目所在路径
$ mohand test -w mongo    # 自动登录,并进入指定mongo数据库

hand模块

mohand_plugin_expect.hand.expect(*dargs, **dkwargs)[源代码]

将被装饰函数封装为一个 click.core.Command 类,成为 mohand 的子命令

该装饰器可能被作为一个简单无参数的装饰器使用(如: @hand.expect ) 或者包含定制其行为的含参数装饰器使用(如: @hand.expect(timeout=30)

注解

该装饰器最终会通过插件系统被注册到 hands.hand 中。

此处的 expect 装饰器本身是应该不支持无参数装饰的,但考虑到其作为样例实现, 故将其实现为兼容两种传参的装饰器

参数:
  • log_level (int) – 当前子命令的日志输出等级,默认为: logging.INFO
  • cmd (str) – 用于构造上下文环境的终端命令,将传入 pexpect.spawn
  • encoding (str) – 可选,spawn命令执行时的指定编码,默认为 utf-8
  • timeout (int) – 可选,执行 spawn 的超时时间,默认为 30
返回:

被封装后的函数

返回类型:

function

class mohand_plugin_expect.hand.Child(*args, **kwargs)[源代码]

基类:object

pexpect实例创建后的返回子线程,支持一系列接口方法

static sigwinch_passthrough(sig, data)[源代码]

处理窗口尺寸变化信号,并相应更新 tty 窗口大小设置

action(expect, sendline, before='', retry=3, timeout=5, expect_callback=None, **kwargs)[源代码]

执行一个action,当检测到符合指定 beforeexpect 后, 发送 sendline

参数:
  • expect (str) – 期望的输入匹配行,支持多对象的列表,以及正则表达式对象 如:[‘good’, ‘bad’, re.compile(r’\d’), pexpect.EOF, pexpect.TIMEOUT]
  • sendline (str or regex) – 发送一行字串,若为正则表达式,则发送匹配到的结果字串
  • before (str) – 可选,用来辅助判定期望的输入匹配行,默认为 ''
  • retry (int) – (已弃用)可选,默认为 3 ,重试次数
  • timeout (int) – 可选,单位秒,默认为 5 , 单条动作中 expect 的超时时间
  • expect_callback (function(index)) – expect 的回调函数,传入 expect 列表中匹配到的对象的索引, 可在其中执行定制的处理逻辑

发布说明

v1.0.2 (2018-07-01 23:20:15)

Optimize

  1. 优化 action 接口实现逻辑,默认超时时间为5s,去除重试逻辑
  2. 优化 action 接口异常处理逻辑,超时异常发生时正常打印 before 信息,便于调试处理

v1.0.1 (2018-05-19 12:35:48)

Feature

  1. 增加了对 Python 2.X 的支持

v1.0.0 (2018-05-15 14:36:14)

Feature

  1. 实现对于 pexpect 的封装,并实现上下文管理器支持,便于最终用户使用
  2. 实现用于执行单条动作的 action 接口方法,支持正则识别
  3. 实现 expect 装饰器并通过 stevedore 接口注册到 mohand
  4. 实现对于 SIGWINCH 系统信号的监听,并动态修改目标 stty 的窗口尺寸设置