nova-api代码分析(2)

来源:互联网 发布:动态加载js 完成 编辑:程序博客网 时间:2024/04/30 00:17

上一篇完成了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 = ext.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的代码




0 0
原创粉丝点击