MoHand¶
概览¶
MoHand 为通用自动化处理工具,主要用于运维自动化。
灵感来自于 Python 下很经典的运维工具 Fabric ,故您在使用中会看到很多 Fabric 的影子。
最初开发 MoHand 是为了在公司自动化登录堡垒机,由于所在公司的业务较复杂, 故需要登录全国各地的堡垒机,然后跳到对应的私有云内,进行开发&调试工作。
考虑到高扩展性,同时也是因为深知自己能力有限不可多知多能,故在设计整体架构时, 参考了 Gulp 以及 PostCSS 的组件思想,通过 Python 的 stevedore 实现了高扩展性的插件系统,并自己实现了一个用于自动化登录堡垒机的插件 mohand-plugin-expect 。
如果您使用后觉得有意思,欢迎实现更多的插件分享给大家,我会及时在该文档中更新 plugin 列表,
供后来人参考&使用。若其足够优秀,我将考虑将其加入到 MoHand 的 install_requires
中!
安装方法¶
您可以通过 pip
进行安装,本包仅在 Python 3.X
下测试通过:
pip3 install mohand
提示
从 v1.0.1
版本开始,增加了对 Python 2.X
的支持,但由于我主要在 Py3
环境下使用,所以强烈建议您在 Py3 下使用。如果您在 Py2 环境下遇到任何异常,
可以及时提 Issues 给我,我会努力在搬砖的间隙进行修复。。。
使用说明¶
安装成功后,您将获得 mohand
终端命令,该命令在调用前会加载当前路径下的 handfile.py
文件,若没有找到,则会递归向父级路径查找,直到根路径结束。若未找到该文件,则会返回信息如下:
$ mohand
[ERROR] 未找到 handfile 文件!
当其找到该文件后,会立即加载该文件,并停止继续递归。此处我们在当前路径创建该文件,然后再次执行:
$ touch handfile.py
$ mohand
Usage: mohand [OPTIONS] COMMAND [ARGS]...
通用自动化处理工具
详情参考 `GitHub <https://github.com/littlemo/mohand>`_
Options:
--author 作者信息
--version 版本信息
--help Show this message and exit.
此时 mohand
已经可以正常运行了,但是我们还没有可用于执行的子命令,接下来我们在
handfile.py
文件中实现一个子命令:
from mohand.hands import hand
@hand.expect(cmd='ls')
def hello(o):
"""Hello World!"""
print('Hello World!')
注解
此处的 expect
为通过 mohand-plugin-expect 实现的一个 hand
,
其主要用于 SSH
, FTP
等一系列远程连接命令,此处用其执行 ls
仅作为演示,
其正确使用姿势可参考该扩展包中的具体文档说明
好,此时我们已经拥有了一个可执行的子命令,如何证明呢?再执行一次 mohand
看看吧:
$ mohand
Usage: mohand [OPTIONS] COMMAND [ARGS]...
通用自动化处理工具
详情参考 `GitHub <https://github.com/littlemo/mohand>`_
Options:
--author 作者信息
--version 版本信息
--help Show this message and exit.
Commands:
hello Hello World!
注意最后两行,现在已经多了一条子命令,其命令名即为我们之前实现的函数名,说明即该函数的 __doc__
。
这里的 CLI 是基于 click 包实现的,故您基本感受不到其存在,相关逻辑已经被封装到 hand.expect
这个装饰器中了。
接下来就是见证奇迹的时刻了,我们来执行以下这个子命令看看:
$ mohand hello
ls
Hello World!
handfile.py
看我们实现的命令被执行了,打印出了通过装饰器传入的 cmd
, Hello World!
,
以及当前路径下的文件。
还记得我之前说过的循环递归查找 handfile.py
文件么?这个性质将很方便,比如将我们刚实现的
handfile.py
移到 ~
下,这样我们在 ~
目录下就都可以加载到这个文件中的子命令了。
插件实现¶
实现您自己的 hand 插件并不难,您可以参考GitHub上 mohand-plugin-expect 的实现。 您需要做的事情如下:
- 实现一个
mohand.hands.HandBase
的子类,用于注册您的 hand 装饰器、提供版本信息 - 实现一个 hand ,如 mohand-plugin-expect 中的
hand.expect
- 在
setup.py
中添加一个mohand.plugin.hand
的 entry_points
异常模块¶
自定义异常类
hands模块¶
-
class
mohand.hands.
HandDict
(*args, **kwargs)[源代码]¶ 基类:
mohand.utils._AttributeDict
Hand扩展插件集合字典(单例)
-
mohand.hands.
hand
¶ 所有已注册hand插件的装饰器集合实例,可通过
.
语法获取
加载模块¶
-
mohand.load_file.
find_handfile
(names=None)[源代码]¶ 尝试定位
handfile
文件,明确指定或逐级搜索父路径参数: names (str) – 可选,待查找的文件名,主要用于调试,默认使用终端传入的配置 返回: handfile
文件所在的绝对路径,默认为 None返回类型: str
-
mohand.load_file.
get_commands_from_module
(imported)[源代码]¶ 从传入的
imported
中获取所有click.core.Command
参数: imported (module) – 导入的Python包 返回: 包描述文档,仅含终端命令函数的对象字典 返回类型: (str, dict(str, object))
-
mohand.load_file.
is_command_object
(obj)[源代码]¶ 验证传入的 obj 是否为一个 CLI 命令对象
参数: obj (object) – 待判断对象 返回: 是否为 click.core.Command
命令对象返回类型: bool
-
mohand.load_file.
extract_commands
(imported_vars)[源代码]¶ 从传入的变量列表中提取命令(
click.core.Command
)对象参数: imported_vars (dict_items) – 字典的键值条目列表 返回: 判定为终端命令的对象字典 返回类型: dict(str, object)
-
mohand.load_file.
load_handfile
(path, importer=None)[源代码]¶ 导入传入的
handfile
文件路径,并返回(docstring, callables)也就是 handfile 包的
__doc__
属性 (字符串) 和一个{'name': callable}
的字典,包含所有通过 mohand 的 command 测试的 callables参数: - path (str) – 待导入的 handfile 文件路径
- importer (function) – 可选,包导入函数,默认为
__import__
返回: 包描述文档,仅含终端命令函数的对象字典
返回类型:
声明模块¶
用以提供全局参数的声明
-
mohand.state.
env
= {'handfile': 'handfile.py', 'plugin_namespace': 'mohand.plugin.hand', 'version': OrderedDict([('mohand', '1.0.1')])}¶ 全局环境字典,包含所有的配置 部分配置,如
version
,会在加载完扩展插件后进行再次填充
工具模块¶
用以提供全局通用工具方法&类
-
class
mohand.utils.
_AttributeDict
[源代码]¶ 基类:
dict
允许通过查找/赋值属性来操作键值的字典子类
举个栗子:
>>> m = _AttributeDict({'foo': 'bar'}) >>> m.foo 'bar' >>> m.foo = 'not bar' >>> m['foo'] 'not bar'
_AttributeDict
对象还提供了一个.first()
方法,起功能类似.get()
,但接受多个键名作为列表多参,并返回第一个命中的键名的值 再举个栗子:>>> m = _AttributeDict({'foo': 'bar', 'biz': 'baz'}) >>> m.first('wrong', 'incorrect', 'foo', 'biz') 'bar'
-
__weakref__
¶ list of weak references to the object (if defined)
-