openstack Neutron源码分析(三)------linuxbridge-agent

直接从源码开始分析

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.AGENT
self.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()




  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

self-motivation

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值