Neutron代码结构

Neutron的代码交互稍显复杂,对于理解底层网络原理的同学可能比较容易理解;代码也在不断的被完善和添加新的feature中,所以neutron的代码结构在不断变化中,应该用不变的网络原理去理解上层可能千变万化的neutron代码;这个也是为何《 云计算 网络珠玑》中不花费笔墨讲解neutron代码的原因。
 
1 代码总体 架构
从文件夹的命名也基本可以得出该目录代码的作用,几个重要的文件夹如下:
agent主要是l3 agent及l3 agent ha的相关代码;
common主要是各底层驱动与linux系统命令的交互层;
db是neutron各功能与 数据库 交互数据的代码;
extensions主要包括的一些扩展功能,包括dvr的代码等;
plugins是core plugin的代码,包括ovs,ml2和各个厂商ibm、ryu提供等的plugin;
scheduler是创建dhcp服务和router到各l3 agent的调度分配相关代码;
server是neutron server相关的代码;
services则是包含了lbaas、vpnaas、fwaas、l3-router、metering等服务的plugin和agent代码;
2.Neutron server 启动过程
1)server/__init__.py负责启动neutron-server进程
def 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 thedefault"
                   " search paths(~/.neutron/, ~/, /etc/neutron/, /etc/) and"
                   " the '--config-file'option!"))
   try:
       pool = eventlet.GreenPool()
 
       neutron_api = service.serve_wsgi(service.NeutronApiService)
       api_thread = pool.spawn(neutron_api.wait)
 
       try:
           neutron_rpc = service.serve_rpc()
       except NotImplementedError:
           LOG.info(_("RPC was already started in parent process byplugin."))
       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)
 
if __name__ == "__main__":
main()
 
其中大体流程是读取配置文件,启动相应个数的处理restful api和与各个Agent通信的Rpcworker工作的相关进程;service.serve_wsgi和service.serve_rpc()调用的是service.py里的代码启动API和与各agent通信的rpc相关任务就可以了。
3.l3_agent的启动过程
启动代码在agent/l3_agent.py里:
defmain(manager='neutron.agent.l3_agent.L3NATAgentWithStateReport'):
   _register_opts(cfg.CONF)
   common_config.init(sys.argv[1:])
   config.setup_logging()
   server = neutron_service.Service.create(
       binary='neutron-l3-agent',
       topic=topics.L3_AGENT,
       report_interval=cfg.CONF.AGENT.report_interval,
       manager=manager)
   service.launch(server).wait()
其中main函数的参数L3NATAgentWithStateReport中继承自L3NATAgent,而L3NATAgent继承自firewall_l3_agent.FWaaSL3AgentRpcCallback、l3_ha_agent.AgentMixin和manager.Manager;所以Neutron的FWaas没有单独的进程,是在l3-agent进程里生效的;main函数的另一个参数report_interval表明l3-agent进程向数据库中更新时间的间隔,控制节点的neutron-server则间隔的读取这个更新时间,如果时间相隔太大,则认为l3-agent进程挂掉;其他的neutron服务(或openstack的整个服务判断机制)基本都是如此,这个间隔是可以通过配置文件来配置的。
另外,从services/vpn/Agent.py里的class VPNAgent(l3_agent.L3NATAgentWithStateReport)里可以看出VPN进程也是从l3-agent进程里继承的,所以即使网络节点l3-agent挂了,在控制节点看来,只要vpn进程存在,l3-agent仍然是活着的,因为vpn agent的继承关系导致其会网数据库中更新l3-agent的时间。
网络节点创建路由(namespace)的下发流程是:
agent/l3_agent.py的eventlet.spawn_n(self._process_routers_loop)---_process_router_update----_process_router_if_compatible---process_router---routes_updated(_process_virtual_routes/_update_routing_table)/process_router_floating_ip_nat_rules--_create_agent_gateway_port---_create_namespace;其中_router_added/_router_removed来对防火墙等进行更新通知。
 
防火墙下发:
控制节点\services\firewall\Fwaas_plugin.pyFirewallPlugin(firewall_db.Firewall_db_mixin交互数据库):
rpc 下发
                                                                                                        
网络节点rpc 接收
从firewall_agent-api.py继承生成firewall_l3_agent.py/L3NATAgent create_firewall/update_firewall/delete_firewall_invoke_driver_for_plugin_api--- fwaas_driver--
services/firewall/drivers/linux/iptables_fwaas.pyceate_firewall/update_firewall/delete_firewall
agent/linux/iptables_firewall.py(iptables)-- neutron/agent/linux/utils.pyexecute
其他几个service流程基本都是如此。
4.VPNaas的进程启动
启动服务代码在services/vpn/agent.py里:
def main():
   l3_agent.main(
       manager='neutron.services.vpn.agent.VPNAgent')
可以看出vpnagent其实是启动了一个l3 agent的进程,里面也包含了firewall的相关服务代码,所以vpn需要的规则可以直接调用相关代码下发。
5.LBaas的进程启动
启动服务代码在services/loadbalancer/agent.py里:
def main():
   cfg.CONF.register_opts(OPTS)
   cfg.CONF.register_opts(manager.OPTS)
    #import interface options just in case the driver uses namespaces
   cfg.CONF.register_opts(interface.OPTS)
   config.register_interface_driver_opts_helper(cfg.CONF)
   config.register_agent_state_opts_helper(cfg.CONF)
   config.register_root_helper(cfg.CONF)
 
   common_config.init(sys.argv[1:])
   config.setup_logging()
 
   mgr = manager.LbaasAgentManager(cfg.CONF)
   svc = LbaasAgentService(
       host=cfg.CONF.host,
       topic=topics.LOADBALANCER_AGENT,
       manager=mgr
    )
   service.launch(svc).wait()
相关的函数全在LbaasAgentManager里;Lbaas的几个流程创建直到创建VIP才调用创建底层lbaas对应namespace的底层驱动代码;基本也是plugin.py发送RPC,然后lbaas对应网络节点的agent调用底层驱动代码来创建namespace及配置对应vport的信息,包括VIP。
6.ovs-neutron-agent的启动
neutron\plugins\openvswitch\agent\Ovs_neutron_agent.py下的main函数:
def main():
    #命令注册
   cfg.CONF.register_opts(ip_lib.OPTS)
    #解析配置文件
common_config.init(sys.argv[1:])
#设置日志
   common_config.setup_logging()
   q_utils.log_opt_values(LOG)
try:
    #将解析得到的参数添加到一个字典 数据结构
       agent_config = create_agent_config_map(cfg.CONF)
   except ValueError as e:
       LOG.error(_('%s Agent terminated!'), e)
       sys.exit(1)
 
   is_xen_compute_host = 'rootwrap-xen-dom0' in agent_config['root_helper']
   if is_xen_compute_host:
       # Force ip_lib to always use the root helper to ensure that ip
       # commands target xen dom0 rather than domU.
       cfg.CONF.set_default('ip_lib_force_root', True)
 
   agent = OVSNeutronAgent(**agent_config)
   signal.signal(signal.SIGTERM, agent._handle_sigterm)
 
    #Start everything.
   LOG.info(_("Agent initialized successfully, now running... "))
   agent.daemon_loop()
 
if __name__ == "__main__":
main()
 
Ovs-neutron-agent的所有配置下发最终通过OVSNeutronAgent下发到底层。
7.neutron与db的交互
每个plugin的情况不太一样,详细说明如下:
a) openvswtich 作为core plugin用pluings/openvswitch/ovs_models_v2.py里的
    fromneutron.db import model_base
from neutron.db import models_v2
b) ml2作为core plugin用neutron\plugins\ml2/managers.py里调用neutron\plugins\ml2/Db.py:
   neutron\plugins\ml2
c) fwaas作为service plugin的db配置在于neutron\services\firewall/Fwaas_plugin.py的
from neutron.db.firewall import firewall_db
class FirewallPlugin(firewall_db.Firewall_db_mixin):
d) L3RouterPlugin作为serviceplugin的db配置在neutron\services\l3_router/ L3_router_plugin.py文件里:
from neutron.db import common_db_mixin
from neutron.db import extraroute_db
from neutron.db import l3_dvrscheduler_db
from neutron.db import l3_gwmode_db
from neutron.db import l3_hamode_db
from neutron.db import l3_hascheduler_db
classL3RouterPlugin(common_db_mixin.CommonDbMixin,
                    extraroute_db.ExtraRoute_db_mixin,
                    l3_hamode_db.L3_HA_NAT_db_mixin,
                    l3_gwmode_db.L3_NAT_db_mixin,
                    l3_dvrscheduler_db.L3_DVRsch_db_mixin,
                    l3_hascheduler_db.L3_HA_scheduler_db_mixin)
e)lbaas作为serviceplugin的db配置在\neutron\services\loadbalancer/Plugin.py的
from neutron.db.loadbalancer importloadbalancer_db as ldb
from neutron.db import servicetype_db asst_db
class LoadBalancerPlugin(ldb.LoadBalancerPluginDb,
                        agent_scheduler.LbaasAgentSchedulerDbMixin):
f)vpnaas作为serviceplugin的db配置在\neutron\services\vpn/Plugin.py的
from neutron.db.vpn import vpn_db
class VPNPlugin(vpn_db.VPNPluginDb):
g)metering作为serviceplugin的db配置在neutron\services\metering/Metering_plugin.py里,
from neutron.db.metering import metering_db
from neutron.db.metering importmetering_rpc
classMeteringPlugin(metering_db.MeteringDbMixin)



neutron-server是neutron的核心组件之中的一个。负责直接接收外部请求,然后调用后端对应plugin进行处理。
其核心启动过程代码主要在neutron.server包中。
__init__.py 文件里 包含一个 main() 函数,是 WSGI server開始的模块,而且通过调用 serve_wsgi 来创建一个 NeutronApiService 的实例。然后通过 eventlet greenpool 来执行 WSGI 的应用程序,响应来自client的请求。
主要过程为:
   eventlet.monkey_patch()
绿化各个模块为支持协程(通过打补丁的方式让本地导入的库都支持协程)。
config.parse(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!"))
通过解析命令行传入的參数。获取配置文件所在。
pool = eventlet.GreenPool()
创建基于协程的线程池。
neutron_api = service.serve_wsgi(service.NeutronApiService)
api_thread = pool.spawn(neutron_api.wait)
serve_wsgi方法创建 NeutronApiService 实例(作为一个 WsgiService ),并调用 其的start() 来启动 socket server端。
#neutron.service
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
neutron.service.NeutronApiService类继承自neutron.service.WsgiService 。其 create 方法返回一个 appname 默觉得“ neutron ”的 WsgiService 对象; start 方法则调用 _run_wsgi 方法。
def start(self):
        self.wsgi_app = _run_wsgi(self.app_name)
_run_wsgi 方法主要是从 api-paste.ini 文件里读取应用(最后是利用 neutron.api.v2.router:APIRouter.factory 来构造应用)。然后为应用创建一个 wsgi 的服务端,并启动应用。主要代码为。
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
至此,neutron server 启动完毕。 之后。须要创建rpc 服务端
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)
            rpc_thread.link(lambda gt: api_thread.kill())
            api_thread.link(lambda gt: rpc_thread.kill())
这些代码创建 plugin rpc 服务端,并将 api rpc 的生存绑定到一起,一个死掉,则另外一个也死掉。
pool.waitall()
最后是后台不断等待。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值