openstack之WSGI

参考:http://www.cnblogs.com/sammyliu/p/4272611.html

          《OpenStack开源云王者归来》  戢友

WSGI就是web server和应用程序之间的接口定义

1. webob库

webob.dec中的wsgify修饰符将函数(参数request,返回response)封装为wsgi应用,该函数也可以普通调用

@wsgify

def myfunc(req):   #也可以修饰类的方法def myfunc(self, req)

    if True:

        return webob.Response('xxx')   #返回webob.Response对象

    else:

        raise webob.exc.HTTPForbidden   #将异常转换为wsgi的response

wsgify的参数:参数RequestClass表示request的类型,它可以是webob.Request的自定义子类;参数args和kwargs不太常用

函数返回值:webob.Response对象;任何wsgi应用;None被转换为req.response;string被转换为带有该string的req.response;raise webob.exc中的异常

2. paste.deploy

只关心配置文件中带有[type:name]类型的section:[app:{name}]定义wsgi应用,[server:{name}]定义wsgi的server,[composite:{name}]将request分配到多个app上,[filter:{name}]定义wsgi过滤器,[DEFAULT]定义默认变量;应用入口是name为main的应用

单个配置文件可以定义多个应用,定义的格式:使用另外一个配置文件use=config:{config_file}#{app_name};使用egg包中的use=egg:{func_name};使用callable对象use=call:{module_name}:{func_name};使用工厂方法paste.app_factory={module_name}:{func_name}。在use之后的键值会作为func的参数

2.1 factory函数

工厂函数的协议:paste.app_factory,paste.composite_factory,paste.filter_factory,paste.server_factory,所有工厂函数返回含有__call__可执行的方法(函数、方法、类)

1)paste.app_factory参数是字典global_config,关键字参数local_config

   app_factory(global_config, **local_config),返回函数对象

   app(request),返回response

2)paste.composite_factory额外包含参数loader,可以根据name返回一个wsgi应用,例如get_app、get_filter和get_server

def composite_factory(loader, global_config, **local_config):   #将filter和app组合起来

    pipeline=pipeline.split()

    filters=[loader.get_filter(p) for p in pipeline[:-1]]   #返回filter应用

    app=loader.get_app(pipeline[-1])    #返回app应用

    for filter in filters.reverse():

        app=filter(app)

        return app

3)paste.filter_factor返回的方法带有参数app

   filter_factory(global_config, **local_config),返回函数对象

   filter(app),返回函数对象

   filter_func(request),最后调用request.get_response(app)或直接返回response

def filter_factory(global_config, **local_config):

    def filter(app):    #返回带有唯一参数app的callable对象

        return AuthFilter(app, username)    #返回一个被filter的app

    return filter

class AuthFilter(object):

    def __init__(self, app, username):

        self.app=app

        self.username=username

    def __call__(self, environ, start_response):

        if environ.get('REMOTE_USER') == self.username:

            return self.app(environ, start_response)

        start_response('403 Forbidden', [('Content-type', 'text/html')])

        return ['You are forbidden to view this resource']

4)paste.server_factory返回的函数没有返回值

def server_factory(global_conf, host, port):

    port=int(port)

    def serve(app):

        Server(app, host, port).serve_forever()

    return serve

2.2 使用方法

from paste import deploy 

app = deploy.loadapp('config:{config_name}')

底层使用ConfigParser来解析ini文件,deploy有loadapp、loadfilter和loadserver

loadapp会在ini文件中查找main section,它由name_prefix和name组成,name_prefix由object_type.config_prefixes(list)指定,name默认为main,将里面的options组成local_conf字典,根据name_prefix的不同(pipeline、app或filter)产生不同的LoaderContext类型(PIPELINE,APP或FILTER),其成员loader包含了ini文件的信息

pipeline的LoadContext由filter_contexts和app_context组成;app和filter会从local_conf中找object_type.egg_protocols指定的option

遍历生成的context树,通过LoaderContext的create方法依次初始化app、filter函数对象,其中app函数对象在初始化阶段会对URL做映射,并将映射关系放入routes.Mapper中,filter函数对象会在初始化阶段输入app对象,最后返回filter函数对象

3. routes库

http://www.cnblogs.com/sammyliu/p/4272611.html

routes.Mapper对象用于URL解析,调用connect添加URL到方法实例controller的映射;routes.Mapper有个成员maxkeys字典,将参数类型相同(除action,controller以外,URL中包含的参数)的映射关系放在一起,每个映射关系由一个route.Route表示

routes.middleware.RoutesMiddleware是工厂函数app_factory返回的app,它通过wsgi_app函数获取URL的解析结果,如果结果为空,说明相应的URL没有在mapper对象中注册,如果不为空,则返回URL相应的controller对象,由于controller对象也是可调用的,最终会调用controller对象中的__call__方法处理HTTP请求

request.environ['wsgiorg.routing_args'][1]是个dict,包含URL的参数(action,conditions和用户填入的{instance_id})和URL映射的controller对象

request.params是个dict,包含客户端提交的数据

4. 管理Resource的类

4.1 APIRouter

负责分发HTTP Request到其管理的Resource

* 属性resources是个数组,用来保存所有的Resource的Controller类的instance,Resource是Controller的封装,具体实现在Controller类中

* 属性mapper用来保存所有resource,extension resource,resource extension的routes供RoutesMiddleware使用,它其实是一张路由表,每个表项是URL和controller以及action的映射关系

* 属性routes是routes.middleware.RoutesMiddleware的实例,它是WSGI app,它使用mapper和_dispatch_进行初始化,功能是根据URL得到controller和它的action

4.2 Controller

Controller代表对某个资源的操作集合,每个Resource都有一个相应的Controller类,里面定义了CRUD方法,例如create, show, index, update, delete

4.3 ExtensionManager

这么多资源需要使用一个集中的方式对它们进行管理,统一管理用的就是ExtensionManager,配置文件里xxx_extension配置项告诉ExtensionManager去哪找扩展的类文件

4.4 ProjectMapper

APIRouters使用一个mapper属性来保存为所有resource建立的mapping,继承关系是ProjectMapper->APIMapper->routes.Mapper

service.py

import wsgi
class WSGIService(object):
    def __init__(self):
        self.loader = wsgi.Loader()
        self.app = self.loader.load_app()
        self.server = wsgi.Server(self.app,
                                  '0.0.0.0',
                                  8080)
    def start(self):
        self.server.start()
    def wait(self):
        self.server.wait()
    def stop(self):
        self.server.stop()
if __name__ == "__main__":
    server = WSGIService()
    server.start()
    server.wait()

wsgi.py

import eventlet
import eventlet.wsgi
import greenlet
import sys
import os
from paste import deploy
class Loader(object):
    def load_app(self):
        ini_path = os.path.normpath(
             os.path.join(os.path.abspath(sys.argv[0]),
                          os.pardir,
                          'api-paste.ini')
             )
        if not os.path.isfile(ini_path):
            print("Cannot find api-paste.ini.\n")
            exit(1)
        return deploy.loadapp('config:' + ini_path)
class Server(object):
    def __init__(self, app, host='0.0.0.0', port=0):
        self._pool = eventlet.GreenPool(10)
        self.app = app
        self._socket = eventlet.listen((host, port), backlog=10)
        (self.host, self.port) = self._socket.getsockname()
        print("Listening on %(host)s:%(port)s" % self.__dict__)
    def start(self):
        self._server = eventlet.spawn(eventlet.wsgi.server,
                                      self._socket,
                                      self.app,
                                      protocol=eventlet.wsgi.HttpProtocol,
                                      custom_pool=self._pool)
    def stop(self):
        if self._server is not None:
            self._pool.resize(0)
            self._server.kill()
    def wait(self):
         try:
            self._server.wait()
         except greenlet.GreenletExit:
            print("WSGI server has stopped.")

api-paste.ini

[pipeline:main]
pipeline = auth instance
[app:instance]
paste.app_factory = routers:app_factory
[filter:auth]
paste.filter_factory = middleware:Auth.factory

middleware.py

from webob import Response
from webob.dec import wsgify
from webob import exc
import webob
class Auth(object):
    def __init__(self, app):
        self.app = app
    @classmethod
    def factory(cls, global_config, **local_config):
        def _factory(app):
            return cls(app)
        return _factory
    @wsgify(RequestClass=webob.Request)
    def __call__(self, req):
        resp = self.process_request(req)
        if resp:
            return resp
        return req.get_response(self.app)
    def process_request(self, req):
        if req.headers.get('X-Auth-Token') != 'open-sesame':
            return exc.HTTPForbidden()

routers.py

import routes
import routes.middleware
import webob
import webob.dec
from webob.dec import wsgify
import controllers
class Router(object):
    def __init__(self):
        self.mapper = routes.Mapper()
        self.add_routes()
        self._router = routes.middleware.RoutesMiddleware(self._dispatch,
                                                          self.mapper)
    def add_routes(self):
        controller = controllers.Controller()
        self.mapper.connect("/instances",
                           controller=controller, action="create",
                           conditions=dict(method=["POST"]))
        self.mapper.connect("/instances",
                           controller=controller, action="index",
                           conditions=dict(method=["GET"]))
        self.mapper.connect("/instances/{instance_id}",
                           controller=controller, action="show",
                           conditions=dict(method=["GET"]))
        self.mapper.connect("/instances/{instance_id}",
                           controller=controller, action="update",
                           conditions=dict(method=["PUT"]))
        self.mapper.connect("/instances/{instance_id}",
                           controller=controller, action="delete",
                           conditions=dict(method=["DELETE"]))
    @wsgify(RequestClass=webob.Request)
    def __call__(self, request):
        return self._router
    @staticmethod
    @wsgify(RequestClass=webob.Request)
    def _dispatch(request):
        match = request.environ['wsgiorg.routing_args'][1]
        if not match:
             return _err()
        app = match['controller']
        return app
def _err():
    return 'The Resource is Not Found.'
def app_factory(global_config, **local_config):
    return Router()

controllers.py

import uuid
import webob
import simplejson
from webob.dec import wsgify
class Controller(object):
    def __init__(self):
        self.instances = {}
        for i in xrange(3):
            inst_id = str(uuid.uuid4())
            self.instances[inst_id] = {'id': inst_id,
                                       'name': 'inst-' + str(i)}
    def create(self, req):
        print(req.params)
        name = req.params['name']
        if name:
            inst_id = str(uuid.uuid4())
            inst = {'id': inst_id,
                    'name': name}
            self.instances[inst_id] = inst
            return {'instance': inst}
    def show(self, req, instance_id):
        inst = self.instances.get(instance_id)
        return {'instance': inst}
    def index(self, req):
        return {'instances': self.instances.values()}
    def delete(self, req, instance_id):
        if self.instances.get(instance_id):
            self.instances.pop(instance_id)
    def update(self, req, instance_id):
        inst = self.instances.get(instance_id)
        name = req.params['name']
        if inst and name:
            inst['name'] = name
            return {'instance': inst}
    @wsgify(RequestClass=webob.Request)
    def __call__(self, req):
        arg_dict = req.environ['wsgiorg.routing_args'][1]
        action = arg_dict.pop('action')
        del arg_dict['controller']
        method = getattr(self, action)
        result = method(req, **arg_dict)
        if result is None:
            return webob.Response(body='',
                                  status='204 Not Found',
                                  headerlist=[('Content-Type',
                                               'application/json')])
        else:
            if not isinstance(result, basestring):
                result = simplejson.dumps(result)
            return result


时间: 2024-07-29 06:44:19

openstack之WSGI的相关文章

Openstack API 类型 & REST 风格

目录 目录 Openstack 提供了三种操作方式 Web界面 CIL 指令行 RESTful API REST 风格 RESTFul风格的API设计 基于HTTP协议的RESTful API OpenStack中的RESTful API开发框架 Openstack 提供了三种操作方式 Web界面 也就是通过Dashboard(儀表板)来使用Openstack雲計算平台上的功能.通过Web界面使用 OpenStack Services 这种方式是通过 OpenStack Horizon Proj

[网摘学习]关于OpenStack架构

本文源自:http://www.javachen.com/2011/07/openstack-architecture-overview/,本人只做学习使用,所有权归原作者所有. What is OpenStack? OpenStack提供开放源码软件,建立公共和私有云. OpenStack是一个社区和一个项目,以及开放源码软件,以帮助企业运行的虚拟计算或者存储云. OpenStackd开源项目由社区维护,包括OpenStack计算(代号为Nova),OpenStack对象存储(代号为SWIFT

Web 开发规范 — WSGI

目录 目录 WSGI 简介 为什么需要 WSGI 这个规范 WSGI 如何工作 WSGI的角色 Server 如何调用 Application application 的两个参数 application 对象的返回值 再谈Server如何调用application WSGI 中间件 WSGI的实现和部署 参考资料 WSGI 简介 WSGI(Web Server Gateway Interface) Web 服务器网关接口.从名称上来看WSGI就是一个网关,作用就是在协议之间进行转换.具体而言,W

Openstack组件实现原理 — Glance架构(V1/V2)

目录 目录 Glance 安装列表 Glance Image service Image service 的组件 Glance-Api Glance-Registry Glance-db Image StoreStore Backend Image Glance 架构 Glance Restful API V1 Glance Restful API V2 Glance 安装列表 Openstack组建部署 - Glance Install Glance Image service Image s

OpenStack Swift源码初探--proxy下的server.py

          OpenStack Swift作为开源的云存储工具,被越来越多的公司使用.为了记录和巩固学习swift的开源源码,所以进行一系列的源码开源学习笔记,供初学者快速学习和理解swift的内部功能.           proxy下面的server.py模块是所有对account,container,object等对象进行管理操作的在swift的proxy端的总入口.在swift系统在接收到url请求后,先是经过middleware处理链分别验证处理后,再进入到proxy下面的se

openstack创建实例总是出错

问题描述 在centos7下进行的openstack的安装,然后进行实例的创建,但是在创建总是失败,创建之前用nova-manageservicelist所有的service都是笑脸,但是boot了以后,nova-compute的state就变成了down[root@controller~]#nova-manageservicelistBinaryHostZoneStatusStateUpdated_Atnova-conductorcontrollerinternalenabled:-)2015

openstack nova 源码解析 — Nova API 执行过程从(novaclient到Action)

目录 目录 Nova API Nova API 的执行过程 novaclient 将 Commands 转换为标准的HTTP请求 PasteDeploy 将 HTTP 请求路由到具体的 WSGI Application Routes 将 HTTP 请求路由到具体的操作函数并执行 Nova API Nova API 是访问.使用 Nova 各组件服务的唯一途径,作为 novaclient 和 Nova services 之间的中间层.Nova API 需要保证高度的稳定性,所以这些 API 的名称

Openstack Nova 源码分析 — RPC 远程调用过程

目录 目录 Nova Project Services Project 的程序入口 setuppy Nova中RPC远程过程调用 nova-compute RPC API的实现 novacomputemanager 模块 最后 Nova Project Services nova-api:捕获novaclient发送过来的HTTP请求,并且将它转换为AMQP消息,通过Queue来与别的services通信. nova-conductor:为数据库访问提供了一层安全保障. NOTE:除了nova-

Openstack组建部署 — Glance Install

目录 目录 前文列表 Image service overview Openstack Image service包含的组件 Install and configure Prerequisites 先决条件 To create the database To create the service credentials Install and configure components Install the packages Edit the etcglanceglance-apiconf fi