neutron-server.service 启动命令:
neutron-server --config-file /etc/neutron/neutron.conf --config-file /etc/neutron/api-paste.ini
位于 setup.cfg -> [entry_points] -> console_scripts,很很多个main函数入口,安装后,这些命令位于/usr/bin/,可以直接执行。
程序入口点举例:neutron-server = neutron.cmd.eventlet.server:main_wsgi_eventlet
创建eventlet pool,启动wsgi server,具体做事情的是api-paste.ini中定义的app:neutron,请求达到时,经过一系列的filter后,达到APIRouter处理
详情见openstack-setuptools以及setuptools-pbr(源码解析一)
#neutron.cmd.eventlet.server文件中main_wsgi_eventlet方法
neutron-server = neutron.cmd.eventlet.server:main_wsgi_eventlet
wsgi_eventlet.main()
server.boot_server(_eventlet_wsgi_server)
#此处传入的参数一般为 --config-file /etc/neutron/neutron.conf --config-file /etc/neutron/plugins/ml2/ml2_conf.ini
config.init(sys.argv[1:])
#设置neutron的logging,就是以neutron为进程名的打印
config.setup_logging()
server_func()
#创建线程池
pool = eventlet.GreenPool()
#neutron api的注册
neutron_api = service.serve_wsgi(service.NeutronApiService)
#启动api
api_thread = pool.spawn(neutron_api.wait)
try:
#注册rpc服务
neutron_rpc = service.serve_rpc()
except NotImplementedError:
LOG.info(_LI("RPC was already started in parent process by "
"plugin."))
else:
#启动rpc线程
rpc_thread = pool.spawn(neutron_rpc.wait)
#启动rpc plugin works
plugin_workers = service.start_plugin_workers()
for worker in plugin_workers:
pool.spawn(worker.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()
def serve_wsgi(cls):
try:
service = cls.create()
#启动api server实例
service.start()
except Exception:
with excutils.save_and_reraise_exception():
LOG.exception(_LE('Unrecoverable error: please check log '
'for details.'))
return service
class WsgiService(object):
"""Base class for WSGI based services.
For each api you define, you must also define these flags:
:_listen: The address on which to listen
:_listen_port: The port on which to listen
"""
def __init__(self, app_name):
self.app_name = app_name
self.wsgi_app = None
def start(self):
# 启动时 调用_run_wsgi加载app
self.wsgi_app = _run_wsgi(self.app_name)
def wait(self):
self.wsgi_app.wait()
def _run_wsgi(app_name):
# app_name 默认是neutron
#创建app
app = config.load_paste_app(app_name)
if not app:
LOG.error(_LE('No known API applications configured.'))
return
# 创建server
server = wsgi.Server("Neutron")
#绑定并启动server
server.start(app, cfg.CONF.bind_port, cfg.CONF.bind_host,
workers=_get_api_workers())
LOG.info(_LI("Neutron service started, listening on %(host)s:%(port)s"),
{'host': cfg.CONF.bind_host, 'port': cfg.CONF.bind_port})
return run_wsgi_app(app)
def load_paste_app(app_name):
#neutron.conf中定义或者默认值为api-paste.ini
config_path = cfg.CONF.find_file(cfg.CONF.api_paste_config)
if not config_path:
raise cfg.ConfigFilesNotFoundError(
config_files=[cfg.CONF.api_paste_config])
config_path = os.path.abspath(config_path)
LOG.info(_LI("Config paste file: %s"), config_path)
try:
#创建app
app = deploy.loadapp("config:%s" % config_path, name=app_name)
except (LookupError, ImportError):
msg = (_("Unable to load %(app_name)s from "
"configuration file %(config_path)s.") %
{'app_name': app_name,
'config_path': config_path})
LOG.exception(msg)
raise RuntimeError(msg)
return app
用来发现和配置WSGI应用的一套系统
#url请求分发到对应的app,use表明的是分发的方式,这里是urlmap进行分发的
[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 = oslo_middleware:RequestId.factory
[filter:catch_errors]
paste.filter_factory = oslo_middleware:CatchErrors.factory
[filter:keystonecontext]
paste.filter_factory = neutron.auth:NeutronKeystoneContext.factory
[filter:authtoken]
paste.filter_factory = keystonemiddleware.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
[app:neutronapiapp_v2_0]
#初始化neutron.api.v2.router中APIRouter.factory
paste.app_factory = neutron.api.v2.router:APIRouter.factory
RESOURCES = {'network': 'networks',
'subnet': 'subnets',
'subnetpool': 'subnetpools',
'port': 'ports'}
SUB_RESOURCES = {}
COLLECTION_ACTIONS = ['index', 'create']
MEMBER_ACTIONS = ['show', 'update', 'delete']
REQUIREMENTS = {'id': attributes.UUID_PATTERN, 'format': 'json'}
class APIRouter(wsgi.Router):
@classmethod
def factory(cls, global_config, **local_config):
return cls(**local_config)
def __init__(self, **local_config):
#创建mapper
mapper = routes_mapper.Mapper()
#获取NeutornManage的core_plugin,这个定义在/etc/neutron/neutron.conf,比如我的是core_plugin = ml2
plugin = manager.NeutronManager.get_plugin()
#扫描neutron/extensions下所有的extension
ext_mgr = extensions.PluginAwareExtensionManager.get_instance()
ext_mgr.extend_resources("2.0", attributes.RESOURCE_ATTRIBUTE_MAP)
col_kwargs = dict(collection_actions=COLLECTION_ACTIONS,
member_actions=MEMBER_ACTIONS)
#构建映射规则函数
def _map_resource(collection, resource, params, parent=None):
allow_bulk = cfg.CONF.allow_bulk
allow_pagination = cfg.CONF.allow_pagination
allow_sorting = cfg.CONF.allow_sorting
#创建controller
controller = base.create_resource(
collection, resource, plugin, params, allow_bulk=allow_bulk,
parent=parent, allow_pagination=allow_pagination,
allow_sorting=allow_sorting)
path_prefix = None
if parent:
path_prefix = "/%s/{%s_id}/%s" % (parent['collection_name'],
parent['member_name'],
collection)
mapper_kwargs = dict(controller=controller,
requirements=REQUIREMENTS,
path_prefix=path_prefix,
**col_kwargs)
# mapper.collection这个就是构建映射规则
#collection:一个字符串,就是资源的复数形式,比如networks
#resource:一个字符串,就是资源的单数形式,比如network
return mapper.collection(collection, resource,
**mapper_kwargs)
#构建/的映射规则
mapper.connect('index', '/', controller=Index(RESOURCES))
for resource in RESOURCES:
#针对每一个核心资源(network、subnet等),调用_map_resource,构建映射规则
_map_resource(RESOURCES[resource], resource,
attributes.RESOURCE_ATTRIBUTE_MAP.get(
RESOURCES[resource], dict()))
resource_registry.register_resource_by_name(resource)
#SUB_RESOURCES是空的,没啥用
for resource in SUB_RESOURCES:
_map_resource(SUB_RESOURCES[resource]['collection_name'], resource,
attributes.RESOURCE_ATTRIBUTE_MAP.get(
SUB_RESOURCES[resource]['collection_name'],
dict()),
SUB_RESOURCES[resource]['parent'])
#清除当前配置在策略引擎中的所有规则。其在单元测试中和核心API router初始化的最后调用,确保在所有扩展加载之后加载规则
policy.reset()
#初始化wsgi.Router
super(APIRouter, self).__init__(mapper)
- extension应该放在neutron/extensions文件夹下,或者在配置文件中设置api_extensions_path
- extension的class名应该和文件同名,当然首字母应该大写
- 应该实现neutron.api.extensions.py中ExtensionDescriptor定义的接口
- 在对应的plugin的supported_extension_aliases 中增加我们extension的别名。前面我们写的core plugin就有这个属性
FAULT_MAP = {exceptions.NotFound: webob.exc.HTTPNotFound,
exceptions.Conflict: webob.exc.HTTPConflict,
exceptions.InUse: webob.exc.HTTPConflict,
exceptions.BadRequest: webob.exc.HTTPBadRequest,
exceptions.ServiceUnavailable: webob.exc.HTTPServiceUnavailable,
exceptions.NotAuthorized: webob.exc.HTTPForbidden,
netaddr.AddrFormatError: webob.exc.HTTPBadRequest,
oslo_policy.PolicyNotAuthorized: webob.exc.HTTPForbidden
}
class Controller(object):
LIST = 'list'
SHOW = 'show'
CREATE = 'create'
UPDATE = 'update'
DELETE = 'delete'
def index(self, request, **kwargs):
···
def show(self,req,id):
···
def create(self,req,body):
···
def update(self,req,body,id):
···
def create_resource(collection, resource, plugin, params, allow_bulk=False,
member_actions=None, parent=None, allow_pagination=False,
allow_sorting=False):
#创建controller
controller = Controller(plugin, collection, resource, params, allow_bulk,
member_actions=member_actions, parent=parent,
allow_pagination=allow_pagination,
allow_sorting=allow_sorting)
return wsgi_resource.Resource(controller, FAULT_MAP)
def Resource(controller, faults=None, deserializers=None, serializers=None):
@webob.dec.wsgify(RequestClass=Request)
def resource(request):
···
# 从controller获取执行action的函数。
method = getattr(controller, action)
# 执行action,相关参数经反序列化后通过args参数传入controller。
result = method(request=request, **args)
···
# 构造Response进行返回。
return webob.Response(request=request, status=status,
content_type=content_type,
body=body)
#返回resource
return resource
def serve_rpc():
#获取plugin
plugin = manager.NeutronManager.get_plugin()
#获取service_plugins(包含plugin)
service_plugins = (
manager.NeutronManager.get_service_plugins().values())
#检查配置文件中rpc_workers
if cfg.CONF.rpc_workers < 1:
cfg.CONF.set_override('rpc_workers', 1)
#检查plugin是否实现start_rpc_listeners方法
if not plugin.rpc_workers_supported():
LOG.debug("Active plugin doesn't implement start_rpc_listeners")
if 0 < cfg.CONF.rpc_workers:
LOG.error(_LE("'rpc_workers = %d' ignored because "
"start_rpc_listeners is not implemented."),
cfg.CONF.rpc_workers)
raise NotImplementedError()
try:
#创建service_plugins RpcWorker
rpc = RpcWorker(service_plugins)
LOG.debug('using launcher for rpc, workers=%s', cfg.CONF.rpc_workers)
#关闭连接池
session.dispose()
#创建ProcessLauncher,launcher详情见md文件
launcher = common_service.ProcessLauncher(cfg.CONF, wait_interval=1.0)
#执行launch_service方法
launcher.launch_service(rpc, workers=cfg.CONF.rpc_workers)
return launcher
except Exception:
with excutils.save_and_reraise_exception():
LOG.exception(_LE('Unrecoverable error: please check log for '
'details.'))
def launch_service(self, service, workers=1):
#检查是否是ServiceBase子类
_check_service_base(service)
#创建ServiceWrapper
wrap = ServiceWrapper(service, workers)
#设置不让垃圾回收机制回收已经存在的实例
if hasattr(gc, 'freeze'):
gc.freeze()
LOG.info('Starting %d workers', wrap.workers)
while self.running and len(wrap.children) < wrap.workers:
#启动进程
self._start_child(wrap)
def _start_child(self, wrap):
if len(wrap.forktimes) > wrap.workers:
#休眠
if time.time() - wrap.forktimes[0] < wrap.workers:
LOG.info('Forking too fast, sleeping')
time.sleep(1)
#wrap.forktimes顶部放入0
wrap.forktimes.pop(0)
#wrap.forktimes加入时间
wrap.forktimes.append(time.time())
#创建子进程
pid = os.fork()
#如果是子进程
if pid == 0:
#运行_child_process
self.launcher = self._child_process(wrap.service)
while True:
#子进程句柄
self._child_process_handle_signal()
status, signo = self._child_wait_for_exit_or_signal(
self.launcher)
if not _is_sighup_and_daemon(signo):
self.launcher.wait()
break
self.launcher.restart()
os._exit(status)
LOG.debug('Started child %d', pid)
wrap.children.add(pid)
self.children[pid] = wrap
return pid
def _child_process(self, service):
#子进程句柄
self._child_process_handle_signal()
eventlet.hubs.use_hub()
#关闭流
os.close(self.writepipe)
# Create greenthread to watch for parent to close pipe
eventlet.spawn_n(self._pipe_watcher)
# Reseed random number generator
random.seed()
launcher = Launcher(self.conf, restart_method=self.restart_method)
#调用launch_service
launcher.launch_service(service)
return launcher
def launch_service(self, service, workers=1):
if workers is not None and workers != 1:
raise ValueError(_("Launcher asked to start multiple workers"))
_check_service_base(service)
service.backdoor_port = self.backdoor_port
#添加services进程
self.services.add(service)
def add(self, service):
self.services.append(service)
#添加线程
self.tg.add_thread(self.run_service, service, self.done)
def add_thread(self, callback, *args, **kwargs):
#运行callback函数
gt = self.pool.spawn(callback, *args, **kwargs)
th = Thread(gt, self, link=False)
self.threads.append(th)
gt.link(_on_thread_done, self, th)
return th
def run_service(service, done):
try:
#执行start()方法
service.start()
except Exception:
LOG.exception('Error starting thread.')
raise SystemExit(1)
else:
done.wait()
start_listeners_method = 'start_rpc_listeners'
def start(self):
for plugin in self._plugins:
if hasattr(plugin, self.start_listeners_method):
try:
#执行plugin的start_rpc_listeners方法
servers = getattr(plugin, self.start_listeners_method)()
except NotImplementedError:
continue
self._servers.extend(servers)
def start_rpc_listeners(self):
#定义回调endpoints
self._setup_rpc()
self.topic = topics.PLUGIN
self.conn = n_rpc.create_connection(new=True)
#创建endpoints服务
self.conn.create_consumer(self.topic, self.endpoints, fanout=False)
return self.conn.consume_in_threads()
def _setup_rpc(self):
"""Initialize components to support agent communication."""
self.endpoints = [
rpc.RpcCallbacks(self.notifier, self.type_manager),
securitygroups_rpc.SecurityGroupServerRpcCallback(),
dvr_rpc.DVRServerRpcCallback(),
dhcp_rpc.DhcpRpcCallback(),
agents_db.AgentExtRpcCallback(),
metadata_rpc.MetadataRpcCallback(),
resources_rpc.ResourcesPullRpcCallback()
]
不管什么版本,基本差不多,只是代码做了些许改变,基本流程还是不会变化