设为首页 收藏本站
查看: 1067|回复: 0

[经验分享] OpenStack点滴02-WSGI

[复制链接]

尚未签到

发表于 2015-4-12 09:25:09 | 显示全部楼层 |阅读模式
  OpenStack对外提供REST API,那么REST API是如何建立起来的呢?
  OpenStack用了一种叫WSGI(Web Service Gateway Interface)的东西,WSGI是Web服务器与Web应用程序或应用框架之间的一种低级别的接口。
  下面是个简单的例子:



#!/usr/bin/env python
from wsgiref.simple_server import make_server  
def hello_world_app(environ, start_response):  
status = '200 OK' # HTTP Status  
headers = [('Content-type', 'text/plain')] # HTTP Headers  
    start_response(status, headers)  
# The returned object is going to be printed  
return ["Hello World"]  
httpd = make_server('', 8088, hello_world_app)  
print "Serving on port 8088..."  
# Serve until process is killed  
httpd.serve_forever()
  执行这段代码,然后浏览器访问http://localhost:8088就会出现Hello World页面。
  大致流程如下:
  
  (1)Client(上例中浏览器)发送请求到Server。
  (2)Server转发请求给Application(上例中hello_world_app)。注:Server和Application之间还有middleware,此处省略。
  (3)Application进行操作后将相应发送给Server。
  (4)Server再将相应转发给Client。
  
  OpenStack使用WSGI的一个工具包paste来配置WSGI appliaction和server的系统,它的好处是将配置和代码分离。python代码写好后如果想要修改页面到app的映射关系,只需要修改配置文件即可。
  用一个简单的例子来示范paste.deploy的工作机制:
  pastedeploy.ini



[composite:test_composite]
use=egg:Paste#urlmap
/:root

[pipeline:root]
pipeline = logrequest showversion

[filter:logrequest]
username = root
password = root123
paste.filter_factory = pastedeploylab:LogFilter.factory

[app:showversion]
version = 1.0.0
paste.app_factory = pastedeploylab:ShowVersion.factory
  app:表示它定义了一个wsgi的application,是一个callable对象。paste. app_factory返回值是一个application对象
  filter:表示这个段定义了一个filter,filter需要完成的工作是将application包装成另一个application(“过滤”),并返回这个包装后的application。
  pipeline:Pipeline 由一些列的filter组成,最后一个是应用,即将前面的fiiter应用到application。
  composite:自己不处理请求,根据映射关系把请求分发到filter、app或者pipeline。/:root就是表示访问url根目录的请求全部分发到root这个pipeline处理
  
  pastedeploy.py



import os
import webob
from webob import Request
from webob import Response
from paste.deploy import loadapp
from wsgiref.simple_server import make_server

#Filter
class LogFilter():
def __init__(self,app):
self.app = app
pass
def __call__(self,environ,start_response):
print "filter:LogFilter is called."
return self.app(environ,start_response)
@classmethod
def factory(cls, global_conf, **kwargs):
print "in LogFilter.factory", global_conf, kwargs
return LogFilter

class ShowVersion():
def __init__(self):
pass
def __call__(self,environ,start_response):
start_response("200 OK",[("Content-type", "text/plain")])
return ["Paste Deploy LAB: Version = 1.0.0",]
@classmethod
def factory(cls,global_conf,**kwargs):
print "in ShowVersion.factory", global_conf, kwargs
return ShowVersion()

if __name__ == '__main__':
configfile="pastedeploy.ini"
appname="test_composite"
wsgi_app = loadapp("config:%s" % os.path.abspath(configfile), appname)
server = make_server('localhost',8080,wsgi_app)
server.serve_forever()
pass
  执行命令python pastedeploy.py,然后在浏览器中输入http://localhost:8080/就可以在网页输出Paste Deploy LAB: Version = 1.0.0
  下面讲解一下工作流程,运行pastedeploy.py文件,首先会调用loadapp函数加载运用,在配置文件pastedeploy.ini找到appname为test_composite,test_composite是一个composite,然后找到pipeline root,根据pipeline找到filter logrequest和app showversion,logrequest和showversion各自用factory生成callable对象。加载完应用后调用make_server启动服务。
  在浏览器输入http://localhost:8080/就会根据urlmap将请求分发到pipeline root,调用LogFilter的__call__方法,其中app就是ShowVersion,然后调用ShowVersion的__call__方法返回消息。
  
   以下写一个简单的OpenStack WSGI实例,参考了臭蛋的博客,臭蛋写的和OpenStack源码很一致。
  其中用到的一些python库:
  1. paste.deploy 配置WSGI appliaction和server
  2. webob 用来对http请求和响应进行封装
  3. routes 实现URL映射
  4. eventlet.wsgi 或者 wsgiref.simple_server,提供wsgi server功能,后者更简单。
  
  首先建立一个test包,然后在test包里面建立如下文件:
  test-paste.ini



[composite:test_composite]
use=egg:Paste#urlmap
/v1:testapp
[app:testapp]
paste.app_factory = test.router:API.factory
  
  server.py



import os
import logging
import sys
from paste import deploy
from wsgiref.simple_server import make_server
LOG = logging.getLogger(__name__)
module_dir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]),
os.pardir,os.pardir))
sys.path.insert(0,module_dir)
bind_host = "127.0.0.1"
bind_port = 8088
def server(app_name, conf_file):
print "server"
app = load_paste_app(app_name,conf_file)
serve = make_server(bind_host,bind_port,app)
serve.serve_forever()
def load_paste_app(app_name, conf_file):
print "load_paste_app"
LOG.debug("Loading %(app_name) from %(conf_file)",
{'app_name':app_name, 'conf_file':conf_file})
try:
app = deploy.loadapp("config:%s" % os.path.abspath(conf_file), name=app_name)
return app
except (LookupError, ImportError) as e:
LOG.error(str(e))
raise RuntimeError(str(e))
if __name__ == '__main__':
app_name = "test_composite"
conf_file = "test-paste.ini"
server(app_name,conf_file)
  
  wsgi.py



import logging
import routes.middleware
import webob.dec
import webob.exc

class Router(object):
def __init__(self, mapper=None):
print "Router.__init__"
self.map =  mapper
self._router = routes.middleware.RoutesMiddleware(self._dispatch,
self.map)
@classmethod
def factory(cls, global_conf, **local_conf):
print "Router.__factory__"
return cls()
@webob.dec.wsgify
def __call__(self,req):
print "Router.__call__"
return self._router
@staticmethod
@webob.dec.wsgify
def _dispatch(req):
print "Router._dispatch"
# TODO
match = req.environ['wsgiorg.routing_args'][1]
if not match:
return webob.exc.HTTPNotFound()
app = match['controller']
return app
  
  router.py



import routes
from test import wsgi
from test import versions
class API(wsgi.Router):
def __init__(self, mapper=None):
print "API.__init__"
if(mapper == None):
mapper = routes.Mapper()
versions_resource = versions.create_resource()
mapper.connect("/test",controller=versions_resource,
action="index")
super(API,self).__init__(mapper)
  
  versions.py



import httplib
import json
import webob.dec
from test import wsgi
from webob import Response
class Controller(object):
def __init__(self):
print "Controller.__init__"
# TODO
self.version = "0.1"
def index(self,req):
print "Controller.index"
response = Response(request=req,
status=httplib.MULTIPLE_CHOICES,
content_type='application/json')
response.body = json.dumps(dict(versions=self.version))
return response
@webob.dec.wsgify
def __call__(self, request):
print "Controller.__call__"
# TODO
return self.index(request)
def create_resource():
print "create_resource"
return Controller()
   @webob.dec.wsgify 装饰器将一个普通函数转变成WSGI应用程序
  
  执行python server.py , 然后在浏览器输入http://localhost:8088/v1/test 就会出现相关页面。
  由于在函数中加了打印语句,启动时会输出:



server
load_paste_app
Router.__factory__
API.__init__
create_resource
Controller.__init__
Router.__init__
  访问页面会输出:



Router.__call__
Router._dispatch
Controller.__call__
Controller.index
  
  这是一个OpenStack WSGI原型,还需完善,比如在router.py文件中,/test并没有和index方法绑定,只是在Controller.__call__方法中静态的调用了index方法。

  

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-56184-1-1.html 上篇帖子: 一步一步教你安装openstack 下篇帖子: 零基础学习openstack【完整中级篇】及openstack资源汇总
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表