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: 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端进行控制