openstack Neutron源码分析(三)------linuxbridge-agent
来源:互联网 发布:简易电路图绘制软件 编辑:程序博客网 时间:2024/06/04 21:40
直接从源码开始分析
neutron/plugins/ml2/drivers/linuxbridge/agent/linuxbridge_neutron_agent.py:
common_config.init(sys.argv[1:]) common_config.setup_logging() try: interface_mappings = n_utils.parse_mappings( cfg.CONF.LINUX_BRIDGE.physical_interface_mappings) except ValueError as e: LOG.error(_LE("Parsing physical_interface_mappings failed: %s. " "Agent terminated!"), e) sys.exit(1) LOG.info(_LI("Interface mappings: %s"), interface_mappings) try: bridge_mappings = n_utils.parse_mappings( cfg.CONF.LINUX_BRIDGE.bridge_mappings) except ValueError as e: LOG.error(_LE("Parsing bridge_mappings failed: %s. " "Agent terminated!"), e) sys.exit(1) LOG.info(_LI("Bridge mappings: %s"), bridge_mappings)
首先是从配置文件"/etc/neutron/plugins/ml2/linuxbridge_agent.ini"中解析"#physical_interface_mappings ="和"#bridge_mappings ="两个配置项。
然后用这2个配置初始化”LinuxBridgeManager"对象:
manager = LinuxBridgeManager(bridge_mappings, interface_mappings)
neutron/plugins/ml2/drivers/linuxbridge/agent/linuxbridge_neutron_agent.py:
class LinuxBridgeManager(amb.CommonAgentManagerBase): def __init__(self, bridge_mappings, interface_mappings): super(LinuxBridgeManager, self).__init__() self.bridge_mappings = bridge_mappings self.interface_mappings = interface_mappings self.validate_interface_mappings() self.validate_bridge_mappings() self.ip = ip_lib.IPWrapper() # VXLAN related parameters: self.local_ip = cfg.CONF.VXLAN.local_ip self.vxlan_mode = lconst.VXLAN_NONE if cfg.CONF.VXLAN.enable_vxlan: device = self.get_local_ip_device() self.validate_vxlan_group_with_local_ip() self.local_int = device.name self.check_vxlan_support()
可以看到LinuxBridgeManager继承自CommonAgentManagerBase,这个类是一个抽象类,定义了子类需要实现的方法:
neutron/plugins/ml2/drivers/linuxbridge/agent/_agent_manager_base.py:
@six.add_metaclass(abc.ABCMeta)class CommonAgentManagerBase(object): @abc.abstractmethod def ensure_port_admin_state(self, device, admin_state_up): @abc.abstractmethod def get_agent_configurations(self): @abc.abstractmethod def get_agent_id(self): @abc.abstractmethod def get_all_devices(self): @abc.abstractmethod def get_devices_modified_timestamps(self, devices): @abc.abstractmethod def get_extension_driver_type(self): @abc.abstractmethod def get_rpc_callbacks(self, context, agent, sg_agent): @abc.abstractmethod def get_rpc_consumers(self): @abc.abstractmethod def plug_interface(self, network_id, network_segment, device, device_owner): @abc.abstractmethod def setup_arp_spoofing_protection(self, device, device_details): @abc.abstractmethod def delete_arp_spoofing_protection(self, devices): @abc.abstractmethod def delete_unreferenced_arp_protection(self, current_devices):
self.bridge_mappings = bridge_mappings self.interface_mappings = interface_mappings self.validate_interface_mappings() self.validate_bridge_mappings() self.ip = ip_lib.IPWrapper() # VXLAN related parameters: self.local_ip = cfg.CONF.VXLAN.local_ip self.vxlan_mode = lconst.VXLAN_NONE if cfg.CONF.VXLAN.enable_vxlan: device = self.get_local_ip_device() self.validate_vxlan_group_with_local_ip() self.local_int = device.name self.check_vxlan_support()
接下来会检验配置文件中2个映射的合法性,然后根据是否配置了vxlan进行相应的检查。
初始化完了LinuxBridgeManager对象后,会进行linuxbridge的能力注册:
linuxbridge_capabilities.register()
这个代码的功能其实就是注册回调函数,用于指定类型的agent的相关事件发生时调用注册的函数。其相关机制的实现代码位于neutron/callbacks目录下。
这里注册的就是'Linux bridge agent'初始完成后"after_init"调用"neutron/services/trunk/drivers/linuxbridge/agent/driver.py"中的"init_handler"函数。
具体的注册代码如下:
neutron/plugins/ml2/drivers/linuxbridge/agent/linuxbridge_capabilities.py:
def register(): """Register Linux Bridge capabilities.""" # Add capabilities to be loaded during agent initialization capabilities.register(driver.init_handler, constants.AGENT_TYPE_LINUXBRIDGE)
这样在agent初始化完成后就会调用:
neutron/services/trunk/drivers/linuxbridge/agent/driver.py
def init_handler(resource, event, trigger, agent=None): """Handler for agent init event.""" if agent: LinuxBridgeTrunkDriver()
注册完事件回调后,获取以下2个配置:polling_interval = cfg.CONF.AGENT.polling_interval quitting_rpc_timeout = cfg.CONF.AGENT.quitting_rpc_timeout
然后声明了关键的核心对象CommonAgentLoop:
neutron/plugins/ml2/drivers/agent/_common_agent.py:@profiler.trace_cls("rpc")class CommonAgentLoop(service.Service): def __init__(self, manager, polling_interval, quitting_rpc_timeout, agent_type, agent_binary): """Constructor. :param manager: the manager object containing the impl specifics :param polling_interval: interval (secs) to poll DB. :param quitting_rpc_timeout: timeout in seconds for rpc calls after stop is called. :param agent_type: Specifies the type of the agent :param agent_binary: The agent binary string """ super(CommonAgentLoop, self).__init__() self.mgr = manager self._validate_manager_class() self.polling_interval = polling_interval self.quitting_rpc_timeout = quitting_rpc_timeout self.agent_type = agent_type self.agent_binary = agent_binary
可以看到其实现了service.Service接口,按照前面2节neutron源码的分析,可知它可以被ProcessLauncher启动。可以看到下面确实也是这样:
agent = ca.CommonAgentLoop(manager, polling_interval, quitting_rpc_timeout, constants.AGENT_TYPE_LINUXBRIDGE, LB_AGENT_BINARY) setup_profiler.setup("neutron-linuxbridge-agent", cfg.CONF.host) LOG.info(_LI("Agent initialized successfully, now running... ")) launcher = service.launch(cfg.CONF, agent) launcher.wait()
这样具体功能的实现我们只要看CommonAgentLoop的start方法即可:def start(self): self.prevent_arp_spoofing = cfg.CONF.AGENT.prevent_arp_spoofing # stores all configured ports on agent self.network_ports = collections.defaultdict(list) # flag to do a sync after revival self.fullsync = False self.context = context.get_admin_context_without_session() self.setup_rpc() self.init_extension_manager(self.connection) configurations = {'extensions': self.ext_manager.names()} configurations.update(self.mgr.get_agent_configurations()) #TODO(mangelajo): optimize resource_versions (see ovs agent) self.agent_state = { 'binary': self.agent_binary, 'host': cfg.CONF.host, 'topic': constants.L2_AGENT_TOPIC, 'configurations': configurations, 'agent_type': self.agent_type, 'resource_versions': resources.LOCAL_RESOURCE_VERSIONS, 'start_flag': True} report_interval = cfg.CONF.AGENT.report_interval if report_interval: heartbeat = loopingcall.FixedIntervalLoopingCall( self._report_state) heartbeat.start(interval=report_interval) capabilities.notify_init_event(self.agent_type, self) # The initialization is complete; we can start receiving messages self.connection.consume_in_threads() self.daemon_loop()
由了前面neutron-server代码的分析,分析linuxbridge的代码也不难。
首先是安装rpc服务:
def start(self): self.prevent_arp_spoofing = cfg.CONF.AGENT.prevent_arp_spoofing # stores all configured ports on agent self.network_ports = collections.defaultdict(list) # flag to do a sync after revival self.fullsync = False self.context = context.get_admin_context_without_session() self.setup_rpc()
def setup_rpc(self): self.plugin_rpc = agent_rpc.PluginApi(topics.PLUGIN) self.sg_plugin_rpc = sg_rpc.SecurityGroupServerRpcApi(topics.PLUGIN) self.sg_agent = sg_rpc.SecurityGroupAgentRpc( self.context, self.sg_plugin_rpc, defer_refresh_firewall=True) self.agent_id = self.mgr.get_agent_id() LOG.info(_LI("RPC agent_id: %s"), self.agent_id) self.topic = topics.AGENT self.state_rpc = agent_rpc.PluginReportStateAPI(topics.REPORTS) # RPC network init # Handle updates from service self.rpc_callbacks = self.mgr.get_rpc_callbacks(self.context, self, self.sg_agent) self.endpoints = [self.rpc_callbacks] self._validate_rpc_endpoints() # Define the listening consumers for the agent consumers = self.mgr.get_rpc_consumers() self.connection = agent_rpc.create_consumers(self.endpoints, self.topic, consumers, start_listening=False)
首先是声明'plugin_rpc',这个rpc是一个生产者,用于发起rpc调用,使用topic为'q-plugin'的消息。neutron/agent/rpc.py:
class PluginApi(object): '''Agent side of the rpc API. API version history: 1.0 - Initial version. 1.3 - get_device_details rpc signature upgrade to obtain 'host' and return value to include fixed_ips and device_owner for the device port 1.4 - tunnel_sync rpc signature upgrade to obtain 'host' 1.5 - Support update_device_list and get_devices_details_list_and_failed_devices ''' def __init__(self, topic): target = oslo_messaging.Target(topic=topic, version='1.0') self.client = n_rpc.get_client(target)
接下来声明'sg_plugin_rpc',同上也是一个rpc生产者,用于发起rpc调用。主要是安全组相关的调用。self.sg_plugin_rpc = sg_rpc.SecurityGroupServerRpcApi(topics.PLUGIN)
然后用这个sg_plugin_rpc构造了‘SecurityGroupAgentRpc'对象,这样这个对象就可以用sg_plugin_rpc来发起rpc调用了。
neutron/agent/securitygroups_rpc.py:
class SecurityGroupAgentRpc(object): """Enables SecurityGroup agent support in agent implementations.""" def __init__(self, context, plugin_rpc, local_vlan_map=None, defer_refresh_firewall=False, integration_bridge=None): self.context = context self.plugin_rpc = plugin_rpc self.init_firewall(defer_refresh_firewall, integration_bridge)
可以看到将sg_plugin_rpc赋给了self.plugin_rpc,后面会用它对neutron-server发起rpc调用。然后在init_firewall函数中根据配置的firewall_driver初始化加载并初始化对应的对象:
def init_firewall(self, defer_refresh_firewall=False, integration_bridge=None): firewall_driver = cfg.CONF.SECURITYGROUP.firewall_driver or 'noop' LOG.debug("Init firewall settings (driver=%s)", firewall_driver) if not _is_valid_driver_combination(): LOG.warning(_LW("Driver configuration doesn't match " "with enable_security_group")) firewall_class = firewall.load_firewall_driver_class(firewall_driver) try: self.firewall = firewall_class( integration_bridge=integration_bridge) except TypeError: self.firewall = firewall_class()
接着往下看setup_rpc函数:
self.agent_id = self.mgr.get_agent_id() LOG.info(_LI("RPC agent_id: %s"), self.agent_id)调用mgr的get_agent_id()函数获取agent_id,这个mgr就是前面main函数里构造的LinuxBridgeManager.get_agent_id()根据配置的bridge_mapping,获取对应网卡的mac地址得到,这样保证了不同机器上linuxbridge-agent id的唯一性.
def get_agent_id(self): if self.bridge_mappings: mac = utils.get_interface_mac( list(self.bridge_mappings.values())[0]) else: devices = ip_lib.IPWrapper().get_devices(True) if devices: mac = utils.get_interface_mac(devices[0].name) else: LOG.error(_LE("Unable to obtain MAC address for unique ID. " "Agent terminated!")) sys.exit(1) return 'lb%s' % mac.replace(":", "")然后声明了一个state_rpc对象,linuxbridge-agent用它来向neutron-server定时(默认30s)发送状态报告,这样neutron-server就知道哪些linuxbridge-agent处于活跃状态。
self.topic = topics.AGENTself.state_rpc = agent_rpc.PluginReportStateAPI(topics.REPORTS)
然后是获取rpc_cablbacks对象,并将其作为rpc server的endpoints,关于endpoints前面章节有详细讲解:http://blog.csdn.net/happyanger6/article/details/54777429:
self.rpc_callbacks = self.mgr.get_rpc_callbacks(self.context, self, self.sg_agent)self.endpoints = [self.rpc_callbacks]
最后获取所有要消费的topic,并创建消费者开始监听处理rpc调用:
consumers = self.mgr.get_rpc_consumers()self.connection = agent_rpc.create_consumers(self.endpoints, self.topic, consumers, start_listening=False)其中get_rpc_consumers()用于获取所有的消费topic:
def get_rpc_consumers(self): consumers = [[topics.PORT, topics.UPDATE], [topics.NETWORK, topics.DELETE], [topics.NETWORK, topics.UPDATE], [topics.SECURITY_GROUP, topics.UPDATE]] if cfg.CONF.VXLAN.l2_population: consumers.append([topics.L2POPULATION, topics.UPDATE]) return consumers
接下来是启动定时任务来向neutron-server定时汇报agent的状态,可以看到汇报的状态中都包含了哪些消息:
#TODO(mangelajo): optimize resource_versions (see ovs agent) self.agent_state = { 'binary': self.agent_binary, 'host': cfg.CONF.host, 'topic': constants.L2_AGENT_TOPIC, 'configurations': configurations, 'agent_type': self.agent_type, 'resource_versions': resources.LOCAL_RESOURCE_VERSIONS, 'start_flag': True} report_interval = cfg.CONF.AGENT.report_interval if report_interval: heartbeat = loopingcall.FixedIntervalLoopingCall( self._report_state) heartbeat.start(interval=report_interval)
capabilities.notify_init_event(self.agent_type, self)上面这句就是前面提到的neutron提供的callbacks机制,这里会调用前面注册的事件回调函数"init_handler"
然后开始接收rpc消息:# The initialization is complete; we can start receiving messages self.connection.consume_in_threads()
- openstack Neutron源码分析(三)------linuxbridge-agent
- OpenStack Neutron源码分析:ovs-neutron-agent启动源码解析
- nova boot代码流程分析(四):nova与neutron的l2 agent(neutron-linuxbridge-agent)交互
- OpenStack neutron-openvswitch-agent 启动分析
- 【neutron源码分析】neutron-dhcp-agent源码分析
- Neutron总结-linuxbridge-agent换为openvswitch-agent
- OpenStack之Neutron源码分析 Neutron-server初始化
- Openstack学习笔记之——Neutron-server服务加载与启动源码分析(三)
- Openstack源码分析 Neutron源码分析(二)-------------rpc篇
- OpenStack之Neutron源码分析 Neutron-server初始化----从文件夹的命名也基本可以得出该目录代码的作用,几个重要的文件夹如下: agent: 主要是l3 agent及l
- Openstack neutron 常见故障分析
- OpenStack Neutron中的dhcp agent实现
- openstack-neutron-OVS agent(持续更新)
- neutron: linuxbridge 架构解析
- openstack neutron源码分析(四)--------port的创建流程
- neutron-DHCP-Agent代码分析
- openstack dhcp agent 分析
- OpenStack源码系列---neutron-server
- 2016书单总结--Lucene实战(第二版)--基础篇
- python解析格式文件
- Nginx系列之常用内置变量
- uva 11584 - Partitioning by Palindromes(简单dp)
- 微信小程序使用三元运算符代替wx:if
- openstack Neutron源码分析(三)------linuxbridge-agent
- java volatile变量
- 【HDU1693】Eat the Trees(插头DP)
- 面试题19:二叉树的镜像
- USACO 2.1 海明码 Hamming Codes
- [LeetCode]67. Add Binary
- 这儿有自学前端开发的吗-
- java线程实战:多线程下载(上)
- 面试题20:顺时针打印矩阵