一文看懂neutron-server中Pecan的用法

前文有对neutron-server源码中的WSGI进行分析,总结出neutron-server的最简初始化过程。本文在此基础上对neutron-server中的Pecan继续进行深入介绍。

Pecan是一个基本对象分发风格路由的轻量级Python Web框架,所以相比PPRW框架(Paste,PastDeploy,Routes,WebOb),使用Pecan实现路由分发的neutron-server逻辑更加简洁易懂。一个V2Controller类,就完全表达了neutron-server中用于路由分发的功能,可见使用Pecan的编程效率。

V2Controller类:

class V2Controller(object)
    @utils.expose(generic=True)
    def index(self):
        if not pecan.request.path_url.endswith('/'):
            pecan.abort(404)
        layout = []
        for name, collection in attributes.CORE_RESOURCES.items():
            href = urlparse.urljoin(pecan.request.path_url, collection)
            resource = {'name': name,'collection': collection,'links': [{'rel': 'self','href': href}]}
            layout.append(resource)
        return {'resources': layout}
​
    @utils.when(index, method='HEAD')
    @utils.when(index, method='POST')
    @utils.when(index, method='PATCH')
    @utils.when(index, method='PUT')
    @utils.when(index, method='DELETE')
    def not_supported(self):
        pecan.abort(405)
​
    @utils.expose()
    def _lookup(self, collection, *remainder):
        if (remainder and manager.NeutronManager.get_resources_for_path_prefix(collection)):
            collection = remainder[0]
            remainder = remainder[1:]
        controller = manager.NeutronManager.get_controller_for_resource(collection)
        if not controller:
            LOG.warning("No controller found for: %s - returning response code 404", collection)
            pecan.abort(404)
        request.context['resource'] = controller.resource
        request.context['collection'] = controller.collection
        request.context['uri_identifiers'] = {}
        return controller, remainder

以上删除了注释和部分代码。路由分发的核心是_lookup。短短十几行代码,已经映射完了核心资源的增删改查路由。不得不说Pecan的简洁,也不得不赞neutron-server重构之后的简洁。下面详细说明Pecan的路由分发用法,然后回头继续介绍Neutron是怎么利用这种特性完成Web端的重构。

Pecan路由

  • 对象属性方式

from pecan import expose
class BooksController(object):
    @expose()
    def index(self):
        return "Welcome to book section."
    @expose()
    def bestsellers(self):
        return "We have 5 books in the top 10."
        
class CatalogController(object):
    @expose()
    def index(self):
        return "Welcome to the catalog."
    books = BooksController()
    
class RootController(object):
    @expose()
    def index(self):
        return "Welcome to store.example.com!"
    @expose()
    def hours(self):
        return "Open 24/7 on the web."
    catalog = CatalogController()

当浏览器请求/catalog/books/bestsellers地址时,Pecan会分隔请求路径为catalog,books和 bestsellers。然后在根Controller对象中查询catalog属性,接着在catalog对象中继续查找books属性,再接着查询books对象中bestsellers属性,最后调用bestsellers并返回结果。

在这种路由方式中,对象属性名称是URL路径中的一部分。

  • _lookup方式

_lookup方法是一个用于处理URL的特殊方法。它返回一个conctroller对象和剩下的URL路径。这是一种递归调用方式,直到解析到URL最后一级地址。例如:

from pecan import expose, abort
from somelib import get_student_by_name
​
class StudentController(object):
    def __init__(self, student):
        self.student = student
    @expose()
    def name(self):
        return self.student.name
        
class RootController(object):
    @expose()
    def _lookup(self, primary_key, *remainder):
        student = get_student_by_primary_key(primary_key)
        if student:
            return StudentController(student), remainder
        else:
            abort(404)

一个HTTP GET请求/8/name将返回primary_key==8的student名字。

_lookup调用栈如下所示:

 

controller中lookup可以形成一个先进后出的调用栈,该栈从根controller开始,直到最后一个controller对象。通过阅读neutron-server源码可知,neutron-server中核心资源的lookup栈只有一级,即root controller 。

那么neutron-server核心资源路由又是怎么建立的呢?实际实现非常简单,仅仅使用了一个dict就实现了controller对象的路由。核心代码如下:

controller = manager.NeutronManager.get_controller_for_resource(collection)

manager.NeutronManager缓存了核心资源的controller对象,缓存的dict属性名称是self.resource_controller_mappings。该dict初始化代码如下:

    for resource, collection in RESOURCES.items():
        resource_registry.register_resource_by_name(resource)
        new_controller = res_ctrl.CollectionsController(collection, resource,
                                                        plugin=plugin)
        manager.NeutronManager.set_controller_for_resource(
            collection, new_controller)

核心资源的controller根据res_ctrl.CollectionsController类创建。先看看neutron-server定义了哪些核心资源,代码如下:

RESOURCES = {'network': 'networks',
             'subnet': 'subnets',
             'subnetpool': 'subnetpools',
             'port': 'ports'}

核心资源目前有4种,分别是:network,subnet,subnetpool和port。

所以res_ctrl.CollectionsController对象被创建了4个。该controller继承自NeutronPecanController,其中plugin_lister方法用于获取plugin对象中的list方法。

@property
    def plugin_lister(self):
        return getattr(self.plugin, self._plugin_handlers[self.LIST])

self._plugin_handlers方法是NeutronPecanController类的构造方法中被初始化的,代码如下:

self._plugin_handlers = {
            self.LIST: 'get%s_%s' % (parent_resource, self.collection),
            self.SHOW: 'get%s_%s' % (parent_resource, self.resource)
        }
for action in [self.CREATE, self.UPDATE, self.DELETE]:
        self._plugin_handlers[action] = '%s%s_%s' % (action, parent_resource, self.resource)

以network资源为例,self._plugin_handlers初始化后的属性如下所示,其它资源依次类推:

{
'list':'get_networks',
'show':'get_network',
'create':'create_network',
'update':'update_network',
'delete':'delete_network'
}

所以,如果是network资源,getattr(self.plugin, self._plugin_handlers[self.LIST])返回的值是'get_networks'字符串。

在self.resource_controller_mappings对象初始化完成之后,如果客户端请求资源,Pecan框架可以直接从缓存中查询出对应资源的controller对象,然后调用controller对象中对应的方法。注意,controller仅仅用于路由,真正提供服务的是plugin对象中的方法。

找到plugin.py文件,在这个文件的Ml2Plugin类或其父类中必然能够搜到以get,show,create,update和delete开头,使用下划线连接资源名称的方法,比如:create_network,create_subnetl和create_port等等。这些方法有些通过RPC的方式调用对应的Agent,以实现逻辑资源到物理资源的映射。到此neutron-server的整个调用流程已十分明朗,剩下的工作是继续分析每个Agent的具体实现,坚持下去必然对整个Neutron了如指掌。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值