• Neutron分析(2)——neutron-server启动过程分析


    neutron-server启动过程分析

    1. /etc/init.d/neutron-server

    DAEMON=/usr/bin/neutron-server
    DAEMON_ARGS="--log-file=$LOGFILE"
    DAEMON_DIR=/var/run
    ...
    case $1 in
        start)
            test "$ENABLED" = "true" || exit 0
            log_daemon_msg "Starting neutron server" "neutron-server"
            start-stop-daemon -Sbmv --pidfile $PIDFILE --chdir $DAEMON_DIR --exec $DAEMON -- $DAEMON_ARGS
            log_end_msg $?
            ;;
            ...
    esac
    

    2. /usr/bin/neutron-server

    import sys
    from neutron.server import main
    
    if __name__ == "__main__":
        sys.exit(main())
    

    3. neutron.server.main

    ef main():
        # the configuration will be read into the cfg.CONF global data structure
        config.init(sys.argv[1:])
        if not cfg.CONF.config_file:
            sys.exit(_("ERROR: Unable to find configuration file via the default"
                       " search paths (~/.neutron/, ~/, /etc/neutron/, /etc/) and"
                       " the '--config-file' option!"))
        try:
            pool = eventlet.GreenPool()
    
            # 以协程方式启动Restful API
            neutron_api = service.serve_wsgi(service.NeutronApiService)
            api_thread = pool.spawn(neutron_api.wait)
    
            # 启动RPC API
            try:
                neutron_rpc = service.serve_rpc()
            except NotImplementedError:
                LOG.info(_("RPC was already started in parent process by plugin."))
            else:
                rpc_thread = pool.spawn(neutron_rpc.wait)
    
                # api and rpc should die together.  When one dies, kill the other.
                rpc_thread.link(lambda gt: api_thread.kill())
                api_thread.link(lambda gt: rpc_thread.kill())
    
            pool.waitall()
        except KeyboardInterrupt:
            pass
        except RuntimeError as e:
            sys.exit(_("ERROR: %s") % e)
    

    4. 先看neutron.service.serve_rpc()

    neutron.service.serve_rpc()最重要的工作就是启动各个插件的RpcWorker

    plugin = manager.NeutronManager.get_plugin()
    
    try:
            rpc = RpcWorker(plugin)
    
            if cfg.CONF.rpc_workers < 1:
                rpc.start()
                return rpc
            else:
                launcher = common_service.ProcessLauncher(wait_interval=1.0)
                launcher.launch_service(rpc, workers=cfg.CONF.rpc_workers)
                return launcher
    

    而RpcWorker最重要的工作是调用plugin的start_rpc_listeners来监听消息队列:

    def start(self):
            # We may have just forked from parent process.  A quick disposal of the
            # existing sql connections avoids producing errors later when they are
            # discovered to be broken.
            session.get_engine().pool.dispose()
            self._servers = self._plugin.start_rpc_listeners()
    

    5. 再来看Rest API部分

    service.serve_wsgi(service.NeutronApiService)

    def serve_wsgi(cls):
    
        try:
            service = cls.create()
            service.start()
        except Exception:
            with excutils.save_and_reraise_exception():
                LOG.exception(_('Unrecoverable error: please check log '
                                'for details.'))
    
        return service
    

    service.start()即为self.wsgi_app = _run_wsgi(self.app_name),而该函数最重要的工作是从api-paste.ini中加载app并启动

    def _run_wsgi(app_name):
        app = config.load_paste_app(app_name)
        if not app:
            LOG.error(_('No known API applications configured.'))
            return
        server = wsgi.Server("Neutron")
        server.start(app, cfg.CONF.bind_port, cfg.CONF.bind_host,
                     workers=cfg.CONF.api_workers)
        # Dump all option values here after all options are parsed
        cfg.CONF.log_opt_values(LOG, std_logging.DEBUG)
        LOG.info(_("Neutron service started, listening on %(host)s:%(port)s"),
                 {'host': cfg.CONF.bind_host,
                  'port': cfg.CONF.bind_port})
        return server
    

    6. api-paste.ini

    [composite:neutron]
    use = egg:Paste#urlmap
    /: neutronversions
    /v2.0: neutronapi_v2_0
    
    [composite:neutronapi_v2_0]
    use = call:neutron.auth:pipeline_factory
    noauth = request_id catch_errors extensions neutronapiapp_v2_0
    keystone = request_id catch_errors authtoken keystonecontext extensions neutronapiapp_v2_0
    
    [filter:request_id]
    paste.filter_factory = neutron.openstack.common.middleware.request_id:RequestIdMiddleware.factory
    
    [filter:catch_errors]
    paste.filter_factory = neutron.openstack.common.middleware.catch_errors:CatchErrorsMiddleware.factory
    
    [filter:keystonecontext]
    paste.filter_factory = neutron.auth:NeutronKeystoneContext.factory
    
    [filter:authtoken]
    paste.filter_factory = keystoneclient.middleware.auth_token:filter_factory
    
    [filter:extensions]
    paste.filter_factory = neutron.api.extensions:plugin_aware_extension_middleware_factory
    
    [app:neutronversions]
    paste.app_factory = neutron.api.versions:Versions.factory
    
    [app:neutronapiapp_v2_0]
    paste.app_factory = neutron.api.v2.router:APIRouter.factory
    

    实例化neutron/api/v2/router.py中的APIRouter

    1. class APIRouter(wsgi.Router):
    2. # 一个工厂类方法
    3. @classmethod
    4. def factory(cls, global_config, **local_config):
    5. return cls(**local_config)
    6.  
    7. # 真正调用的实例化方法
    8. def __init__(self, **local_config):
    9. ...
    10. #获取NeutornManage的core_plugin,这个定义在/etc/neutron/neutron.conf,比如我的是
    11. #core_plugin = neutron.plugins.openvswitch.ovs_neutron_plugin.OVSNeutronPluginV2
    12. plugin = manager.NeutronManager.get_plugin()
    13.  
    14. #扫描特定路径下的extensions
    15. ext_mgr = extensions.PluginAwareExtensionManager.get_instance()
    16. ...
    17.  
    18. #定义的局部方法
    19. def _map_resource(collection, resource, params, parent=None):
    20. ...
    21. controller = base.create_resource(
    22. collection, resource, plugin, params, allow_bulk=allow_bulk,
    23. parent=parent, allow_pagination=allow_pagination,
    24. allow_sorting=allow_sorting)
    25. ...
    26. # 将这些resource加进router中
    27. return mapper.collection(collection, resource, **mapper_kwargs)
    28.  
    29.  
    30. # 遍历 {'network': 'networks', 'subnet': 'subnets','port': 'ports'}
    31. # 添加controller
    32. for resource in RESOURCES:
    33. _map_resource(RESOURCES[resource], resource,
    34. attributes.RESOURCE_ATTRIBUTE_MAP.get(
    35. RESOURCES[resource], dict()))
    36.  
    37. for resource in SUB_RESOURCES:
    38. ...
    39. #其实操作和上面一个差不多,

    由这个可以看出,添加的controller类型主要分为三类:(其实只要你在neutron目录下grep一下,看哪里调用了create_resource方法即可)

    1. OVSNeutronPluginV2
    2. extensions/*.py
    3. plugins/*.py

    针对前两途径加载resource的类,下面慢慢进行描述。至于第三种,则是在各个不同的plugin内部额外实现的,不是必须的。

    顺便简单的提一下,在neutron/api/extensions.py下的get_instance方法,这里其实也是和nova一样,是遍历目录下的py文件,来增加extension的

    1. ...
    2. @classmethod
    3. def get_instance(cls):
    4. if cls._instance is None:
    5. cls._instance = cls(get_extensions_path(),
    6. ... NeutronManager.get_service_plugins())

    Resource:OVSNeutronPluginV2的实现

    看了代码的你肯定知道,OVSNeutronPluginV2这个类,作为core_plugin继承了好多的的类

    1. class OVSNeutronPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
    2. external_net_db.External_net_db_mixin,
    3. extraroute_db.ExtraRoute_db_mixin,
    4. l3_gwmode_db.L3_NAT_db_mixin,
    5. sg_db_rpc.SecurityGroupServerRpcMixin,
    6. l3_agentschedulers_db.L3AgentSchedulerDbMixin,
    7. agentschedulers_db.DhcpAgentSchedulerDbMixin,
    8. portbindings_db.PortBindingMixin,
    9. extradhcpopt_db.ExtraDhcpOptMixin,
    10. addr_pair_db.AllowedAddressPairsMixin):

    OVSNeutronPluginV2基本上没有什么自己的method,全靠它的"爹们"了。

    随便抓两个来看下,比如NeutronDbPluginV2,他的method有get_port,create_network之类的,还有L3_NAT_db_mixincreate_router等。反正与db的操作,OVSNeutronPluginV2是不会管的,都在它的父类那边处理。

    再看看OVSNeutronPluginV2继承的这些父类们:

    1. #NeutronDbPluginV2继承自NeutronPluginBaseV2
    2. class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2,
    3. CommonDbMixin):
    4.  
    5.  
    6. class NeutronPluginBaseV2(...) :
    7. @abstractmethod
    8. def create_subnet(self, context, subnet):
    9. @abstractmethod
    10. def update_subnet(self, context, id, subnet):
    11. @abstractmethod
    12. def get_subnet(self, context, id, fields=None):
    13. @abstractmethod
    14. def get_subnets(self, context, filters=None, fields=None,
    15.  

    其类图如下:(仅展示部分) Neutron的OVS-Plugin类图, 仅展示部分

    基本上可以说有一个接口类(如图中的NeutronPluginBaseV2),定义了抽象方法,然后一个具体的db类来实现(如NeutronDbPluginV2,这里是采用SQLAlchemy来完成db模型的)

    plugin_aware_extension_middleware_factory

    在请求进入APIRouter之前,会先经过RequestIdMiddleware(请求header中添加 openstack.request_id)、CatchErrorsMiddleware(错误处理)、keystone权限验证以及 plugin_aware_extension_middleware_factory等几个filter的处理,前三个filter比较直 观,plugin_aware_extension_middleware_factory初始化了Extension目录下的Resource:

    class ExtensionMiddleware(wsgi.Middleware):
        """Extensions middleware for WSGI."""
    
        def __init__(self, application,
                     ext_mgr=None):
            self.ext_mgr = (ext_mgr
                            or ExtensionManager(get_extensions_path()))
            mapper = routes.Mapper()
    
            # extended resources ext_mgr.get_resources()其实在内部会调用每个extensions目录下的extension类的get_resources方法
    for resource in self.ext_mgr.get_resources(): path_prefix = resource.path_prefix if resource.parent: path_prefix = (resource.path_prefix + "/%s/{%s_id}" % (resource.parent["collection_name"], resource.parent["member_name"])) LOG.debug(_('Extended resource: %s'), resource.collection) for action, method in resource.collection_actions.iteritems(): conditions = dict(method=[method]) path = "/%s/%s" % (resource.collection, action) with mapper.submapper(controller=resource.controller, action=action, path_prefix=path_prefix, conditions=conditions) as submap: submap.connect(path) submap.connect("%s.:(format)" % path) mapper.resource(resource.collection, resource.collection, controller=resource.controller, member=resource.member_actions, parent_resource=resource.parent, path_prefix=path_prefix) # extended actions action_controllers = self._action_ext_controllers(application, self.ext_mgr, mapper) for action in self.ext_mgr.get_actions(): LOG.debug(_('Extended action: %s'), action.action_name) controller = action_controllers[action.collection] controller.add_action(action.action_name, action.handler) # extended requests req_controllers = self._request_ext_controllers(application, self.ext_mgr, mapper) for request_ext in self.ext_mgr.get_request_extensions(): LOG.debug(_('Extended request: %s'), request_ext.key) controller = req_controllers[request_ext.key] controller.add_handler(request_ext.handler) self._router = routes.middleware.RoutesMiddleware(self._dispatch, mapper) super(ExtensionMiddleware, self).__init__(application)

    比如在extensions下的securitygroup.py中的get_resources方法,看这个代码就知道其中可以处理security_groupsecurity_group_rule两类请求了:

    @classmethod
        def get_resources(cls):
            """Returns Ext Resources."""
            my_plurals = [(key, key[:-1]) for key in RESOURCE_ATTRIBUTE_MAP.keys()]
            attr.PLURALS.update(dict(my_plurals))
            exts = []
            plugin = manager.NeutronManager.get_plugin()
            for resource_name in ['security_group', 'security_group_rule']:
                collection_name = resource_name.replace('_', '-') + "s"
                params = RESOURCE_ATTRIBUTE_MAP.get(resource_name + "s", dict())
                quota.QUOTAS.register_resource_by_name(resource_name)
                controller = base.create_resource(collection_name,
                                                  resource_name,
                                                  plugin, params, allow_bulk=True,
                                                  allow_pagination=True,
                                                  allow_sorting=True)
    
                ex = extensions.ResourceExtension(collection_name,
                                                  controller,
                                                  attr_map=params)
                exts.append(ex)
    
            return exts

    如此,Neutron-Server就已经基本上启动了,无外乎就是加载配置,router各种resource,然后就等待请求了。其中router哪些resource完全是由配置文件来决定的。 当然,在启动的过程中也会初始化db,这也就是为何在安装neutron的时候无需像novaglance等要执行db sync的原因了。



    参考资料

  • 相关阅读:
    HDU2024 C语言合法标识符
    HDU2044 一只小蜜蜂…(简单递推)
    HDU2023 求平均成绩
    CodeForces 1185C1 Exam in BerSU (easy version) (贪心,排序)
    题目梳理(一)(2019.07.06~2019.07.20)
    Codeforces Gym 100989F(STL: map)
    UVA 10815 Andy's First Dictionary(STL: set)
    HDU2020 绝对值排序
    周二——4.2
    周一——4.1
  • 原文地址:https://www.cnblogs.com/feisky/p/3844636.html
Copyright © 2020-2023  润新知