导读
本文启动分析,可以简单了解
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
- class APIRouter(wsgi.Router):
- # 一个工厂类方法
- @classmethod
- def factory(cls, global_config, **local_config):
- return cls(**local_config)
- # 真正调用的实例化方法
- def __init__(self, **local_config):
- ...
- #获取NeutornManage的core_plugin,这个定义在/etc/neutron/neutron.conf,比如我的是
- #core_plugin = neutron.plugins.openvswitch.ovs_neutron_plugin.OVSNeutronPluginV2
- plugin = manager.NeutronManager.get_plugin()
- #扫描特定路径下的extensions
- ext_mgr = extensions.PluginAwareExtensionManager.get_instance()
- ...
-
- #定义的局部方法
- def _map_resource(collection, resource, params, parent=None):
- ...
- controller = base.create_resource(
- collection, resource, plugin, params, allow_bulk=allow_bulk,
- parent=parent, allow_pagination=allow_pagination,
- allow_sorting=allow_sorting)
- ...
- # 将这些resource加进router中
- return mapper.collection(collection, resource, **mapper_kwargs)
-
- # 遍历 {'network': 'networks', 'subnet': 'subnets','port': 'ports'}
- # 添加controller
- for resource in RESOURCES:
- _map_resource(RESOURCES[resource], resource,
- attributes.RESOURCE_ATTRIBUTE_MAP.get(
- RESOURCES[resource], dict()))
- for resource in SUB_RESOURCES:
- ...
复制代码
#其实操作和上面一个差不多,由这个可以看出,添加的controller类型主要分为三类:(其实只要你在neutron目录下grep一下,看哪里调用了create_resource方法即可) - OVSNeutronPluginV2
-
- extensions/*.py
-
- plugins/*.py
复制代码
针对前两途径加载resource的类,下面慢慢进行描述。至于第三种,则是在各个不同的plugin内部额外实现的,不是必须的。 顺便简单的提一下,在neutron/api/extensions.py下的get_instance方法,这里其实也是和nova一样,是遍历目录下的py文件,来增加extension的 - ...
-
- @classmethod
-
- def get_instance(cls):
-
- if cls._instance is None:
-
- cls._instance = cls(get_extensions_path(),
-
- ... NeutronManager.get_service_plugins())
复制代码
Resource:OVSNeutronPluginV2的实现看了代码的你肯定知道,OVSNeutronPluginV2这个类,作为core_plugin继承了好多的的类 - class OVSNeutronPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
-
- external_net_db.External_net_db_mixin,
-
- extraroute_db.ExtraRoute_db_mixin,
-
- l3_gwmode_db.L3_NAT_db_mixin,
-
- sg_db_rpc.SecurityGroupServerRpcMixin,
-
- l3_agentschedulers_db.L3AgentSchedulerDbMixin,
-
- agentschedulers_db.DhcpAgentSchedulerDbMixin,
-
- portbindings_db.PortBindingMixin,
-
- extradhcpopt_db.ExtraDhcpOptMixin,
-
- addr_pair_db.AllowedAddressPairsMixin):
复制代码
OVSNeutronPluginV2基本上没有什么自己的method,全靠它的"爹们"了。 随便抓两个来看下,比如NeutronDbPluginV2,他的method有get_port,create_network之类的,还有L3_NAT_db_mixin的create_router等。反正与db的操作,OVSNeutronPluginV2是不会管的,都在它的父类那边处理。 再看看OVSNeutronPluginV2继承的这些父类们: - #NeutronDbPluginV2继承自NeutronPluginBaseV2
-
- class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2,
-
- CommonDbMixin):
-
- class NeutronPluginBaseV2(...) :
-
- @abstractmethod
-
- def create_subnet(self, context, subnet):
-
- @abstractmethod
-
- def update_subnet(self, context, id, subnet):
-
- @abstractmethod
-
- def get_subnet(self, context, id, fields=None):
-
- @abstractmethod
-
- def get_subnets(self, context, filters=None, fields=None,
复制代码
其类图如下:(仅展示部分)
基本上可以说有一个接口类(如图中的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_group和security_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的时候无需像nova,glance等要执行db sync的原因了。
|