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的原因了。

 



參考資料


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM