热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

OpenStack云平台流量监控插件tapasaservice(Taas)代码解析(二):

在上一篇文章中,以create_tap_service为例,讲解了OpenStack中云端流量捕获插件Tap-as-a-service的Plugin的代

在上一篇文章中,以create_tap_service为例,讲解了OpenStack中云端流量捕获插件Tap-as-a-service的Plugin的代码流程

(https://blog.csdn.net/m0_37313888/article/details/82693789)

先回顾一下Taas的结构,这篇文章我将继续讲解Taas Agent与Taas Driver的工作流程

neutron对应的OpenStack版本:Queuens,关于如何在生产环境中的openstack上安装tap-as-a-service插件请看我的另一篇文章https://blog.csdn.net/m0_37313888/article/details/83450245)

1.Taas插件加载与初始化

众所周知,OpenStack中Plugin只负责数据库以及消息队列的维护,工作具体是在agent节点上执行的。taas作为在这里是作为OpenvSwitch的一个extension集成到neutron-openvswitch-agent中,并且在neutron-openvswitch-agent启动时进行加载与初始化。

首先看一看taas 在agent上的初始化过程

在neutron-openvswitch-agent加载时,顺便把taas插件加载的过程:

1)插件加载与实例化

在neutron-openvswitch-agent初始化代码中(代码位置:neutron/plugins/ml2/driver/openvswitch/agent/ovs_neutron_agent.py)

,第2290行附近,有这么一段代码,这段代码将实例从配置文件中读出并且添加到ext_mgr作为ext_mgr的一个成员对象。

2288 ext_manager.register_opts(cfg.CONF)
2289
2290 ext_mgr = ext_manager.L2AgentExtensionsManager(cfg.CONF)

对应到neutron/agent/l2/l2_agent_extensions_manager.py中的下面这段代码

21 L2_AGENT_EXT_MANAGER_NAMESPACE = 'neutron.agent.l2.extensions'
22
23
24 def register_opts(conf):
25 agent_ext_mgr_config.register_agent_ext_manager_opts(conf)

neutron/conf/agent/agent_extensions_manager.py

17 AGENT_EXT_MANAGER_OPTS = [
18 cfg.ListOpt('extensions',
19 default=[],
20 help=_('Extensions list to use')),
21 ]
22
23
24 def register_agent_ext_manager_opts(cfg=cfg.CONF):
25 cfg.register_opts(AGENT_EXT_MANAGER_OPTS, 'agent')

代码是直接读了配置文件中agent部分的extensions选项。具体的配置过程请阅读https://blog.csdn.net/m0_37313888/article/details/83450245

加载插件的具体位置如下,加载下面这个插件就是创建了下面这个类的一个实例。

[neutron.agent.l2.extensions]
taas = neutron_taas.services.taas.agents.extensions.taas:TaasAgentExtension

2)插件的初始化

插件的加载只是在内存中生成了一个实例。但是这个插件实例还没有具体发挥它的作用,这个插件实例在OVSNeutronAgent的运行过程中才真正发挥它的作用。在neutron/plugins/ml2/drivers/openvswitch/agent/ovs_neutron_agent.py中插件管理器作为了OVSNeutronAgent的成员参与到了OVSNeutronAgent的工作中.(下图中的ext_mgr就是包含了taas的插件管理器)

neutron/plugins/ml2/drivers/openvswitch/agent/ovs_neutron_agent.py

2297 try:
2298 agent = OVSNeutronAgent(bridge_classes, ext_mgr, cfg.CONF)
2299 capabilities.notify_init_event(n_const.AGENT_TYPE_OVS, agent)
2300 except (RuntimeError, ValueError) as e:
2301 LOG.error("%s Agent terminated!", e)
2302 sys.exit(1)
2303 agent.daemon_loop()

插件管理器被传递到OVSNeutronAgent的成员变量中(下图)

140 self.ext_manager = ext_manager

之后,还是在这个代码中,进行下面的操作:1.绑定agent_api操作2.初始化所有的插件

219 agent_api = ovs_ext_api.OVSAgentExtensionAPI(self.int_br, self.tun_br)
220 self.ext_manager.initialize(
221 self.connection, constants.EXTENSION_DRIVER_TYPE, agent_api)

查看具体的 代码实现如下:

class AgentExtensionsManager(stevedore.named.NamedExtensionManager):"""Manage agent extensions."""def __init__(self, conf, namespace):super(AgentExtensionsManager, self).__init__(namespace, conf.agent.extensions,invoke_on_load=True, name_order=True)LOG.info("Loaded agent extensions: %s", self.names())def initialize(self, connection, driver_type, agent_api=None):# Initialize each agent extension in the list.for extension in self:LOG.info("Initializing agent extension '%s'", extension.name)# If the agent has provided an agent_api object, this object will# be passed to all interested extensions. This object must be# consumed by each such extension before the extension's# initialize() method is called, as the initialization step# relies on the agent_api already being available.extension.obj.consume_api(agent_api)extension.obj.initialize(connection, driver_type)

taas-agent-extension在这段代码中被加载,我们来看看taas-agent-extension做了啥

class TaasAgentExtension(l2_extension.L2AgentExtension):def initialize(self, connection, driver_type):"""Initialize agent extension."""self.taas_agent = taas_ovs_agent.TaasOvsAgentRpcCallback(cfg.CONF, driver_type)self.taas_agent.consume_api(self.agent_api)self.taas_agent.initialize()def consume_api(self, agent_api):"""Receive neutron agent API objectAllows an extension to gain access to resources internal to theneutron agent and otherwise unavailable to the extension."""self.agent_api = agent_apidef handle_port(self, context, port):passdef delete_port(self, context, port):pass

三件事:

1. 加载本地rpc回调函数类: TaasOvsAgentRpcCallback

2. 加载agent_api: ovs_ext_api.OVSAgentExtensionAPI(self.int_br, self.tun_br) 

3. 初始化本地rpc回调函数类 

本地的rpc回调函数类如下:

class TaasOvsAgentRpcCallback(api.TaasAgentRpcCallbackMixin):def __init__(self, conf, driver_type):LOG.debug("TaaS OVS Agent initialize called")self.cOnf= confself.driver_type = driver_typesuper(TaasOvsAgentRpcCallback, self).__init__()def initialize(self):self.taas_driver = manager.NeutronManager.load_class_for_provider('neutron_taas.taas.agent_drivers', self.driver_type)()self.taas_driver.consume_api(self.agent_api)self.taas_driver.initialize()self._taas_rpc_setup()TaasOvsAgentService(self).start()def consume_api(self, agent_api):self.agent_api = agent_apidef _invoke_driver_for_plugin_api(self, context, args, func_name):........try:self.taas_driver.__getattribute__(func_name)(args)except Exception:LOG.debug("Failed to invoke the driver")returndef create_tap_service(self, context, tap_service, host):........def create_tap_flow(self, context, tap_flow_msg, host):........def delete_tap_service(self, context, tap_service, host):........def delete_tap_flow(self, context, tap_flow_msg, host):........def _taas_rpc_setup(self):# setup RPC to msg taas pluginself.taas_plugin_rpc = TaasOvsPluginApi(topics.TAAS_PLUGIN, self.conf.host)endpoints = [self]cOnn= n_rpc.Connection()conn.create_consumer(topics.TAAS_AGENT, endpoints, fanout=False)conn.consume_in_threads()def periodic_tasks(self):........

我们来重点看一看上面函数中的initialize()部分的代码,这个是agent上的taas-extension的rpc代码的初始化的核心代码

1. 

self.taas_driver = manager.NeutronManager.load_class_for_provider('neutron_taas.taas.agent_drivers', self.driver_type)()

看一看NeutronManager.load_class_for_provider

@staticmethoddef load_class_for_provider(namespace, plugin_provider):"""Loads plugin using alias or class name:param namespace: namespace where alias is defined:param plugin_provider: plugin alias or class name:returns: plugin that is loaded:raises ImportError: if fails to load plugin"""try:return runtime.load_class_by_alias_or_classname(namespace,plugin_provider)except ImportError:with excutils.save_and_reraise_exception():LOG.error("Plugin '%s' not found.", plugin_provider)

runtime.load_class_by_alias_or_classname(namespace,  plugin_provider)如下:

def load_class_by_alias_or_classname(namespace, name):if not name:LOG.error("Alias or class name is not set")raise ImportError(_("Class not found."))try:mgr = driver.DriverManager(namespace, name, warn_on_missing_entrypoint=False)class_to_load = mgr.driverexcept RuntimeError:e1_info = sys.exc_info()# Fallback to class nametry:class_to_load = importutils.import_class(name)except (ImportError, ValueError):LOG.error("Error loading class by alias",exc_info=e1_info)LOG.error("Error loading class by class name",exc_info=True)raise ImportError(_("Class not found."))return class_to_load

通过stevedore.driver.DriverManager类动态加载模块。这是一个与动态加载类有关的模块。关于这个模块的使用,可以看看这篇文章:http://www.360doc.com/content/14/0429/19/9482_373285413.shtml

然后self.taas_driver通过给出的namespace:

neutron_taas.taas.agent_drivers

与 driver_type:

ovs  被加载。具体被加载的类写在了setup.cfg的entry_points里面

[entry_points]
neutron.agent.l2.extensiOns=taas = neutron_taas.services.taas.agents.extensions.taas:TaasAgentExtension
neutron_taas.taas.agent_drivers =ovs = neutron_taas.services.taas.drivers.linux.ovs_taas:OvsTaasDriver
neutron.service_plugins =taas = neutron_taas.services.taas.taas_plugin:TaasPlugin
neutron.db.alembic_migratiOns=tap-as-a-service = neutron_taas.db.migration:alembic_migration
tempest.test_plugins =tap-as-a-service = neutron_taas.tests.tempest_plugin.plugin:NeutronTaaSPlugin
neutronclient.extension =tap_service = neutron_taas.taas_client.tapservicetap_flow = neutron_taas.taas_client.tapflow

即,self.taas_driver = 

neutron_taas.services.taas.drivers.linux.ovs_taas:OvsTaasDriver

2.

self.taas_driver.consume_api(self.agent_api)

在self.taas_driver, 即neutron_taas.services.taas.drivers.linux.ovs_taas:OvsTaasDriver中,consume_api()方法如下:

def consume_api(self, agent_api):self.agent_api = agent_api

简单的增加了一个self.agent_api成员

3.

self.taas_driver.initialize()

还是看一下self.taas_driver,

def initialize(self):self.int_br = self.agent_api.request_int_br()self.tun_br = self.agent_api.request_tun_br()self.tap_br = OVSBridge_tap_extension('br-tap', self.root_helper)# Prepare OVS bridges for TaaSself.setup_ovs_bridges()# Setup key-value manager for ingress BCMC flowsself.bcmc_kvm = taas_ovs_utils.key_value_mgr(4096)

这个时候agent_api就派上用场了!这个对象,顾名思义,就是一个agent所映射产生的一个api对象,提供许多访问本地资源的接口,在ovs中,是本地所建立的网桥与相关网络设备。我们看到,先是获取了int-br网桥与tun_br 网桥,来看一看获取网桥的基本操作,这里agent_api是ovs_ext_api.OVSAgentExtensionAPI(self.int_br, self.tun_br) 

如下

class OVSAgentExtensionAPI(object):'''Implements the Agent API for Open vSwitch agent.Extensions can gain access to this API by overriding the consume_apimethod which has been added to the AgentExtension class.'''def __init__(self, int_br, tun_br):super(OVSAgentExtensionAPI, self).__init__()self.br_int = int_brself.br_tun = tun_brdef request_int_br(self):"""Allows extensions to request an integration bridge to use forextension specific flows."""return OVSCOOKIEBridge(self.br_int)def request_tun_br(self):"""Allows extensions to request a tunnel bridge to use forextension specific flows.If tunneling is not enabled, this method will return None."""if not self.br_tun:return Nonereturn OVSCOOKIEBridge(self.br_tun)

这里OVSCOOKIEBridge让我们想到了web开发中的COOKIE,但是这个其实就是把本地的网桥信息浅拷贝一份然后返回给程序进一步使用

class OVSCOOKIEBridge(object):def __new__(cls, bridge):COOKIE_bridge = bridge.clone()COOKIE_bridge.set_agent_uuid_stamp(bridge.request_COOKIE())return COOKIE_bridgedef __init__(self, bridge):pass

def clone(self):'''Used by OVSCOOKIEBridge, can be overridden by subclasses if abehavior different from copy.copy is needed.'''return copy.copy(self)

接着,然后建立了一个tap网桥。这一步是在agent上实现流量拷贝、迁移、以及重定向的重头戏。

self.setup_ovs_bridges()

具体代码在neutron_taas/services/taas/drivers/linux/ovs_taas.py

这里为了直观的展示这个流程,把流程分为4个部分

1.创建一个tap网桥

刚开始我看这一段的时候也很是迷惑,因为ovs有专门的流量镜像功能啊,为何不直接管理ovs做一个流量镜像呢?原来,tap-as--a-service的主要设计理念是为不同节点上的虚拟机提供统一的流量捕获服务,这样的话方案中流量必须能够方便的进行跨主机迁移。如果采用vxlan隧道的话就很容易建立新的流量隧道实现流量的跨节点迁移。换句话说,流量既能够定向到相同宿主机上的另一台虚拟机,也能重定向到另一台宿主机上的另一台虚拟机!

self.tap_br.create()# Connect br-tap to br-int and br-tunself.int_br.add_patch_port('patch-int-tap', 'patch-tap-int')self.tap_br.add_patch_port('patch-tap-int', 'patch-int-tap')self.tun_br.add_patch_port('patch-tun-tap', 'patch-tap-tun')self.tap_br.add_patch_port('patch-tap-tun', 'patch-tun-tap')# Get patch port IDspatch_tap_int_id = self.tap_br.get_port_ofport('patch-tap-int')patch_tap_tun_id = self.tap_br.get_port_ofport('patch-tap-tun')patch_tun_tap_id = self.tun_br.get_port_ofport('patch-tun-tap')

2.删除预先定义的与tap-as-a-service相关的所有规则

self.tap_br.delete_flows(table=0)self.tap_br.delete_flows(table=taas_ovs_consts.TAAS_RECV_LOC)self.tap_br.delete_flows(table=taas_ovs_consts.TAAS_RECV_REM)self.tun_br.delete_flows(table=0,in_port=patch_tun_tap_id)self.tun_br.delete_flows(table=taas_ovs_consts.TAAS_SEND_UCAST)self.tun_br.delete_flows(table=taas_ovs_consts.TAAS_SEND_FLOOD)self.tun_br.delete_flows(table=taas_ovs_consts.TAAS_CLASSIFY)self.tun_br.delete_flows(table=taas_ovs_consts.TAAS_DST_CHECK)self.tun_br.delete_flows(table=taas_ovs_consts.TAAS_SRC_CHECK)self.tun_br.delete_flows(table=taas_ovs_consts.TAAS_DST_RESPOND)self.tun_br.delete_flows(table=taas_ovs_consts.TAAS_SRC_RESPOND)

3.初始化tap网桥的OpenFlow流表

self.tap_br.add_flow(table=0,priority=1,in_port=patch_tap_int_id,actiOns="resubmit(,%s)" %taas_ovs_consts.TAAS_RECV_LOC)self.tap_br.add_flow(table=0,priority=1,in_port=patch_tap_tun_id,actiOns="resubmit(,%s)" %taas_ovs_consts.TAAS_RECV_REM)self.tap_br.add_flow(table=0,priority=0,actiOns="drop")self.tap_br.add_flow(table=taas_ovs_consts.TAAS_RECV_LOC,priority=0,actiOns="output:%s" % str(patch_tap_tun_id))self.tap_br.add_flow(table=taas_ovs_consts.TAAS_RECV_REM,priority=0,actiOns="drop")

这段代码可能看起来比较吃力,我在下面做出一些解释:

里面出现了 taas_ovs_consts.TAAS_RECV_LOC 与 taas_ovs_consts.TAAS_RECV_REM两张表。结合后面的代码仔细一看,TAAS_RECV_LOC负责处理从br-int过来的流量(流出vm的流量),默认为转发到br-tun;TAAS_RECV_REM负责处理从br-tun过来的流量,默认为不让任何来自于br-tun的流量通过。来自其他网桥的流量也默认不通过。

4.初始化br-tun网桥的OpenFlow流表

self.tun_br.add_flow(table=0,priority=1,in_port=patch_tun_tap_id,actiOns="resubmit(,%s)" %taas_ovs_consts.TAAS_SEND_UCAST)self.tun_br.add_flow(table=taas_ovs_consts.TAAS_SEND_UCAST,priority=0,actiOns="resubmit(,%s)" %taas_ovs_consts.TAAS_SEND_FLOOD)flow_action = self._create_tunnel_flood_flow_action()if flow_action != "":self.tun_br.add_flow(table=taas_ovs_consts.TAAS_SEND_FLOOD,priority=0,actiOns=flow_action)self.tun_br.add_flow(table=taas_ovs_consts.TAAS_CLASSIFY,priority=2,reg0=0,actiOns="resubmit(,%s)" %taas_ovs_consts.TAAS_DST_CHECK)self.tun_br.add_flow(table=taas_ovs_consts.TAAS_CLASSIFY,priority=1,reg0=1,actiOns="resubmit(,%s)" %taas_ovs_consts.TAAS_DST_CHECK)self.tun_br.add_flow(table=taas_ovs_consts.TAAS_CLASSIFY,priority=1,reg0=2,actiOns="resubmit(,%s)" %taas_ovs_consts.TAAS_SRC_CHECK)self.tun_br.add_flow(table=taas_ovs_consts.TAAS_DST_CHECK,priority=0,actiOns="drop")self.tun_br.add_flow(table=taas_ovs_consts.TAAS_SRC_CHECK,priority=0,actiOns="drop")self.tun_br.add_flow(table=taas_ovs_consts.TAAS_DST_RESPOND,priority=2,reg0=0,actiOns="output:%s" % str(patch_tun_tap_id))self.tun_br.add_flow(table=taas_ovs_consts.TAAS_DST_RESPOND,priority=1,reg0=1,actiOns=("output:%s,""move:NXM_OF_VLAN_TCI[0..11]->NXM_NX_TUN_ID""[0..11],mod_vlan_vid:2,output:in_port" %str(patch_tun_tap_id)))self.tun_br.add_flow(table=taas_ovs_consts.TAAS_SRC_RESPOND,priority=1,actiOns=("learn(table=%s,hard_timeout=60,""priority=1,NXM_OF_VLAN_TCI[0..11],""load:NXM_OF_VLAN_TCI[0..11]->NXM_NX_TUN_ID""[0..11],load:0->NXM_OF_VLAN_TCI[0..11],""output:NXM_OF_IN_PORT[])" %taas_ovs_consts.TAAS_SEND_UCAST))return

以上就是给计算节点开机后,taas插件的初始化过程。关于在实际使用时流表的配置,以及为什么要这么配置,我后期会专门写一篇文章来讲解。


推荐阅读
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • Android工程师面试准备及设计模式使用场景
    本文介绍了Android工程师面试准备的经验,包括面试流程和重点准备内容。同时,还介绍了建造者模式的使用场景,以及在Android开发中的具体应用。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • 前景:当UI一个查询条件为多项选择,或录入多个条件的时候,比如查询所有名称里面包含以下动态条件,需要模糊查询里面每一项时比如是这样一个数组条件:newstring[]{兴业银行, ... [详细]
  • 深入解析Linux下的I/O多路转接epoll技术
    本文深入解析了Linux下的I/O多路转接epoll技术,介绍了select和poll函数的问题,以及epoll函数的设计和优点。同时讲解了epoll函数的使用方法,包括epoll_create和epoll_ctl两个系统调用。 ... [详细]
  • 本文介绍了一道经典的状态压缩题目——关灯问题2,并提供了解决该问题的算法思路。通过使用二进制表示灯的状态,并枚举所有可能的状态,可以求解出最少按按钮的次数,从而将所有灯关掉。本文还对状压和位运算进行了解释,并指出了该方法的适用性和局限性。 ... [详细]
  • C++ STL复习(13)容器适配器
    STL提供了3种容器适配器,分别为stack栈适配器、queue队列适配器以及priority_queue优先权队列适配器。不同场景下,由于不同的序列式 ... [详细]
  • 本文介绍了Python高级网络编程及TCP/IP协议簇的OSI七层模型。首先简单介绍了七层模型的各层及其封装解封装过程。然后讨论了程序开发中涉及到的网络通信内容,主要包括TCP协议、UDP协议和IPV4协议。最后还介绍了socket编程、聊天socket实现、远程执行命令、上传文件、socketserver及其源码分析等相关内容。 ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • Tomcat/Jetty为何选择扩展线程池而不是使用JDK原生线程池?
    本文探讨了Tomcat和Jetty选择扩展线程池而不是使用JDK原生线程池的原因。通过比较IO密集型任务和CPU密集型任务的特点,解释了为何Tomcat和Jetty需要扩展线程池来提高并发度和任务处理速度。同时,介绍了JDK原生线程池的工作流程。 ... [详细]
  • 深入理解Kafka服务端请求队列中请求的处理
    本文深入分析了Kafka服务端请求队列中请求的处理过程,详细介绍了请求的封装和放入请求队列的过程,以及处理请求的线程池的创建和容量设置。通过场景分析、图示说明和源码分析,帮助读者更好地理解Kafka服务端的工作原理。 ... [详细]
  • MySQL数据库锁机制及其应用(数据库锁的概念)
    本文介绍了MySQL数据库锁机制及其应用。数据库锁是计算机协调多个进程或线程并发访问某一资源的机制,在数据库中,数据是一种供许多用户共享的资源,如何保证数据并发访问的一致性和有效性是数据库必须解决的问题。MySQL的锁机制相对简单,不同的存储引擎支持不同的锁机制,主要包括表级锁、行级锁和页面锁。本文详细介绍了MySQL表级锁的锁模式和特点,以及行级锁和页面锁的特点和应用场景。同时还讨论了锁冲突对数据库并发访问性能的影响。 ... [详细]
author-avatar
h38868863
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有