您的位置:首页 > 编程语言

nova-api代码分析(2)

2014-08-09 16:29 281 查看
上一篇完成了APIRouter类中,初始化ExtensionManager实例 ,ext_mgr = self.ExtensionManager()的功能分析,主要就是对于contrib目录下用户自定义的路由文件类加载进来。

接下来继续分析class APIRouter(base_wsgi.Router)类:

mapper = ProjectMapper() 初始化了routes模块里的Mapper类,包含了resource和connect两个路由加载方法。
ProjectMapper继承自APIMapper类。

class ProjectMapper(APIMapper):
def resource(self, member_name, collection_name, **kwargs):
if 'parent_resource' not in kwargs:
kwargs['path_prefix'] = '{project_id}/'
else:
parent_resource = kwargs['parent_resource']
p_collection = parent_resource['collection_name']
p_member = parent_resource['member_name']
kwargs['path_prefix'] = '{project_id}/%s/:%s_id' % (p_collection,
p_member)
routes.Mapper.resource(self, member_name,
collection_name,
**kwargs)


class APIMapper(routes.Mapper):
def routematch(self, url=None, environ=None):
if url == "":
result = self._match("", environ)
return result[0], result[1]
return routes.Mapper.routematch(self, url, environ)

def connect(self, *args, **kargs):
# NOTE(vish): Default the format part of a route to only accept json
#             and xml so it doesn't eat all characters after a '.'
#             in the url.
kargs.setdefault('requirements', {})
if not kargs['requirements'].get('format'):
kargs['requirements']['format'] = 'json|xml'
return routes.Mapper.connect(self, *args, **kargs)


APIRouter(base_wsgi.Router)继承自nova.api.openstack.compute下的
APIMapper(routes.Mapper)类

方法self._setup_routes(mapper, ext_mgr, init_only),加载openstack核心功能路由

class APIRouter(nova.api.openstack.APIRouter):
"""Routes requests on the OpenStack API to the appropriate controller
and method.
"""
ExtensionManager = extensions.ExtensionManager
#加载openstack的核心路由方法
def _setup_routes(self, mapper, ext_mgr, init_only):
if init_only is None or 'versions' in init_only:
#通过create_resource回调函数,返回wsgi.Resource(Controller(ext_mgr)),放入resources资源字典中
 self.resources['versions'] = versions.create_resource()
#mapper.connect方法,建立resources相应资源与REST请求的链接,具体connect的方法详见其他说明
mapper.connect("versions", "/",
controller=self.resources['versions'],
action='show',
conditions={"method": ['GET']})

mapper.redirect("", "/")

if init_only is None or 'consoles' in init_only:
            #通过create_resource回调函数,返回wsgi.Resource(Controller(ext_mgr)),放入resources资源字典中
self.resources['consoles'] = consoles.create_resource()
#mapper.resource方法,建立resources相应资源与REST请求的链接,具体resource的方法详见其他说明
mapper.resource("console", "consoles",
controller=self.resources['consoles'],
parent_resource=dict(member_name='server',
collection_name='servers'))

if init_only is None or 'consoles' in init_only or \
'servers' in init_only or 'ips' in init_only:
self.resources['servers'] = servers.create_resource(ext_mgr)
mapper.resource("server", "servers",
controller=self.resources['servers'],
collection={'detail': 'GET'},
member={'action': 'POST'})

if init_only is None or 'ips' in init_only:
self.resources['ips'] = ips.create_resource()
mapper.resource("ip", "ips", controller=self.resources['ips'],
parent_resource=dict(member_name='server',
collection_name='servers'))

if init_only is None or 'images' in init_only:
self.resources['images'] = images.create_resource()
mapper.resource("image", "images",
controller=self.resources['images'],
collection={'detail': 'GET'})

if init_only is None or 'limits' in init_only:
self.resources['limits'] = limits.create_resource()
mapper.resource("limit", "limits",
controller=self.resources['limits'])

if init_only is None or 'flavors' in init_only:
self.resources['flavors'] = flavors.create_resource()
mapper.resource("flavor", "flavors",
controller=self.resources['flavors'],
collection={'detail': 'GET'},
member={'action': 'POST'})

if init_only is None or 'image_metadata' in init_only:
self.resources['image_metadata'] = image_metadata.create_resource()
image_metadata_controller = self.resources['image_metadata']

mapper.resource("image_meta", "metadata",
controller=image_metadata_controller,
parent_resource=dict(member_name='image',
collection_name='images'))

mapper.connect("metadata",
"/{project_id}/images/{image_id}/metadata",
controller=image_metadata_controller,
action='update_all',
conditions={"method": ['PUT']})

if init_only is None or 'server_metadata' in init_only:
self.resources['server_metadata'] = \
server_metadata.create_resource()
server_metadata_controller = self.resources['server_metadata']

mapper.resource("server_meta", "metadata",
controller=server_metadata_controller,
parent_resource=dict(member_name='server',
collection_name='servers'))

mapper.connect("metadata",
"/{project_id}/servers/{server_id}/metadata",
controller=server_metadata_controller,
action='update_all',
conditions={"method": ['PUT']})




继续看APIRouter的下一个方法self._setup_ext_routes(mapper, ext_mgr, init_only),此方法加载了所有contrib目录下一下加载进来的文件路由

重点看一下ExtensionManager类的内容:

class ExtensionManager(object):
"""Load extensions from the configured extension path.

See nova/tests/api/openstack/compute/extensions/foxinsocks.py or an
example extension implementation.

"""
def sorted_extensions(self):
if self.sorted_ext_list is None:
self.sorted_ext_list = sorted(self.extensions.iteritems())

for _alias, ext in self.sorted_ext_list:
yield ext

def is_loaded(self, alias):
return alias in self.extensions

def register(self, ext):
# Do nothing if the extension doesn't check out
if not self._check_extension(ext):
return

alias = e
4000
xt.alias
LOG.audit(_('Loaded extension: %s'), alias)

if alias in self.extensions:
raise exception.NovaException("Found duplicate extension: %s"
% alias)
self.extensions[alias] = ext
self.sorted_ext_list = None
#获取resources方法
def get_resources(self):
"""Returns a list of ResourceExtension objects."""

resources = []
#向resources列表中添加extensions
        resources.append(ResourceExtension('extensions',
ExtensionsController(self)))
#sorted_extensions对ext_mgr进行排序后,通过迭代器返回ext实例
for ext in self.sorted_extensions():
try:
#调用每一个路由文件里的get_resources()回调函数,返回路由信息的resources列表extend到resources
 resources.extend(ext.get_resources())
except AttributeError:
# NOTE(dprince): Extension aren't required to have resource
# extensions
pass
return resources

def get_controller_extensions(self):
"""Returns a list of ControllerExtension objects."""
controller_exts = []
for ext in self.sorted_extensions():
try:
get_ext_method = ext.get_controller_extensions
except AttributeError:
# NOTE(Vek): Extensions aren't required to have
# controller extensions
continue
controller_exts.extend(get_ext_method())
return controller_exts

def _check_extension(self, extension):
"""Checks for required methods in extension objects."""
try:
LOG.debug('Ext name: %s', extension.name)
LOG.debug('Ext alias: %s', extension.alias)
LOG.debug('Ext description: %s',
' '.join(extension.__doc__.strip().split()))
LOG.debug('Ext namespace: %s', extension.namespace)
LOG.debug('Ext updated: %s', extension.updated)
except AttributeError as ex:
LOG.exception(_("Exception loading extension: %s"), unicode(ex))
return False

return True

def load_extension(self, ext_factory):
"""Execute an extension factory.

Loads an extension.  The 'ext_factory' is the name of a
callable that will be imported and called with one
argument--the extension manager.  The factory callable is
expected to call the register() method at least once.
"""

LOG.debug("Loading extension %s", ext_factory)

if isinstance(ext_factory, six.string_types):
# Load the factory
factory = importutils.import_class(ext_factory)
else:
factory = ext_factory

# Call it
LOG.debug("Calling extension factory %s", ext_factory)
factory(self)

def _load_extensions(self):
"""Load extensions specified on the command line."""

extensions = list(self.cls_list)

for ext_factory in extensions:
try:
self.load_extension(ext_factory)
except Exception as exc:
LOG.warn(_LW('Failed to load extension %(ext_factory)s: '
'%(exc)s'),
{'ext_factory': ext_factory, 'exc': exc})


_setup_ext_routes方法加载了contrib目录下所有的路由文件,下面举一个volumes.py文件的例子,看看具体内容:

class Volumes(extensions.ExtensionDescriptor):
"""Volumes support."""

name = "Volumes"
alias = "os-volumes"
namespace = "http://docs.openstack.org/compute/ext/volumes/api/v1.1"
updated = "2011-03-25T00:00:00Z"

def get_resources(self):
resources = []

# NOTE(justinsb): No way to provide singular name ('volume')
# Does this matter?
res = extensions.ResourceExtension('os-volumes',
VolumeController(),
collection_actions={'detail': 'GET'})
resources.append(res)

attachment_controller = VolumeAttachmentController(self.ext_mgr)
res = extensions.ResourceExtension('os-volume_attachments',
attachment_controller,
parent=dict(
member_name='server',
collection_name='servers'))
resources.append(res)

res = extensions.ResourceExtension('os-volumes_boot',
inherits='servers')
resources.append(res)

res = extensions.ResourceExtension('os-snapshots',
SnapshotController(),
collection_actions={'detail': 'GET'})
resources.append(res)

return resources


以上内容基本分析完WSGIService的load_app方法,来加载路由信息的代码内容,下面继续分析,nova-api代码中启动WSGIService的代码
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: