基于Tornado的ansible的二次封装

Tornado

简介

FriendFeed使用了一款使用 Python 编写的,相对简单的 非阻塞式 Web 服务器。其应用程序使用的 Web 框架看起来有些像 web.py 或者 Google 的 webapp, 不过为了能有效利用非阻塞式服务器环境,这个 Web 框架还包含了一些相关的有用工具 和优化。

Tornado 就是我们在 FriendFeed 的 Web 服务器及其常用工具的开源版本。Tornado 和现在的主流 Web 服务器框架(包括大多数 Python 的框架)有着明显的区别:它是非阻塞式服务器,而且速度相当快。得利于其 非阻塞的方式和对 epoll 的运用,Tornado 每秒可以处理数以千计的连接,因此 Tornado 是实时 Web 服务的一个 理想框架。我们开发这个 Web 服务器的主要目的就是为了处理 FriendFeed 的实时功能 ——在 FriendFeed 的应用里每一个活动用户都会保持着一个服务器连接。

简单示例

以下是经典的 “Hello, world” 示例:

1
2
3
4
5
6
7
8
9
10
11
import tornado.ioloop
import tornado.web
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.write("Hello, world")
application = tornado.web.Application([
(r"/", MainHandler),
])
if __name__ == "__main__":
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()

下载与安装

自动安装

Tornado 已经列入 PyPI ,因此可以通过 pip 或者 easy_install 来安装。如果你没有安装 libcurl 的话,你需要将其单独安装到系统中。

手动安装

安装包 下载 tornado-2.0.tar.gz

1
2
3
4
tar xvzf tornado-2.0.tar.gz
cd tornado-2.0
python setup.py build
sudo python setup.py install

##开发历程

简单实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
import tornado.ioloop
from tornado.options import define, options
import tornado.web
import ansible.runner
from ansible.inventory import Inventory
import simplejson
import hashlib
define("key", default='d41d8cd98f00b204e9800998ecf8427e')
# 加密/解谜
def getmd5(str):
m = hashlib.md5()
m.update(str)
return m.hexdigest()
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.write("Hello, world")
class CommandHandler(tornado.web.RequestHandler):
'''
通过继承tornado.web.RequestHandler
改写post/get方法,获取json数据
'''
def post(self):
data = simplejson.loads(self.request.body)
badcmd = ['reboot','rm','kill','pkill','shutdown','half','mv','dd','mkfs','wget']
type = data['type']
cmd = data['cmd']
host = data['host']
token = data['token']
cmdinfo = cmd.split(" ",1)
print type,host,options.key
hotkey = type+host+options.key
print hotkey
result = getmd5(hotkey)
print result
if sign != result:
self.write("Sign is Error")
else:
if cmdinfo[0] in badcmd:
self.write("This is Danger Shell")
else:
runner = ansible.runner.Runner(
module_name=type,
module_args=cmd,
pattern=host,
forks=10
)
datastructure = runner.run()
self.write(datastructure)
class GetGroupHandler(tornado.web.RequestHandler):
def get(self):
i = Inventory()
groups = i.list_groups()
self.write('\n'.join(groups))
application = tornado.web.Application([
(r"/", MainHandler),
(r"/command", CommandHandler),
(r"/getgroup", GetGroupHandler),
])
if __name__ == "__main__":
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()

上面实例,通过继承tornado.web.RequestHandler来得到 json数据,然后根据数据中的元素执行相应命令
然后调用 ansible的api接口,执行相应的命令。
这样,我们有一个思路

  • 用户认证,得到许可,拿到,server端返回token值
  • 客户端得到token值,以token作为认证,进行命令传递
  • server端通过后,把执行结果以json回传

用户登陆

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
user = ansible
passwold = ansible
class Login(tornado.web.RequestHandler):
def post:
creds = {'username': self.get_arguments('username')[0],
'password': self.get_arguments('password')[0],
'eauth': self.get_arguments('eauth')[0],
}
token = self.application.auth.mk_token(creds)
if not 'token' in token:
# TODO: nicer error message
self.send_error(401)
return
ret = {'return': [{
'token': token['token'],
'expire': token['expire'],
'start': token['start'],
'user': token['name'],
'eauth': token['eauth'],
'perms': perms,
}]}
self.write(self.serialize(ret))
self.finish()

添加 https 认证

1
2
3
4
5
server = HTTPServer(application,ssl_options={
"certfile": os.path.join(os.path.abspath("."), "server.crt"),
"keyfile": os.path.join(os.path.abspath("."), "server.key"),
})

然后把相关的证书扔到 py 文件的目录下。改成相应的名字。

消息传递

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import Ansible-API
class CommandHandler(tornado.web.RequestHandler):
def post(self):
data = simplejson.loads(self.request.body)
types = data['fun']
cmd = data['cmd']
host = data['host']
token = data['token']
cmdinfo = cmd.split(" ",1)
'''
监测token 省略,也就是所谓的用户认证,只做介绍
'''
ruselt = Ansible-API.run({
'fun': types,
'cmd' : cmd,
'tgt': host,
'arg': cmdinfo
})
ret = {
'return':[
{
ruselt['state'],
}
]
}
self.write(self.serialize(ret))
self.finish()

ansible二次封装

后续待开发,也就是把常用的模块封装起来
最简单的

1
2
3
4
5
6
7
8
9
10
import ansible.runner
class Ansible-API():
def run(data):
runner = ansible.runner.Runner(
module_name=data['fun'],
module_args=data['cmd'],
pattern=data['tgt'],
forks=10
)
return runner

小结

把ansible用 Tornado加以修饰,也是为了自己的自动化运维平台更好的兼容ansible,也为了可以将web服务与ansible分离。
而且封装后,会使其它语言调用无障碍。ansible 会使运用一些危险的命令,我们可以在 server端进行控制