前言
一直有很多朋友在使用ODL做实验时很好奇ODL的拓扑是怎么来的,其实这个拓扑模块的后台数据的组成是由topology-manager来实现的,当然还会结合inventory-manager模块以及LLDP模块。这里只讲topology-manager模块,该模块是在openflowplugin-release-lithium-sr3大工程中的applications文件夹下。
topology-manager模块是作为openflowplugin的应用层程序(APP),负责处理operational数据库下network-topology:network-topology数据节点(datastore数据库)的增删改查,例如odl控制器发现添加一台主机host、新加主机与交换机的link链接等。显示拓扑的前端需要从该数据节点上获取主机或者交换机节点数据才能绘制网络拓扑图,构成拓扑图来源有两方面,一方面是通过LLDP发现的switch设备以及相关link连接,另一外面是通过L2switch的hosttracker模块发现的下挂在switch上的host主机以及相关连接。
首先从YANG模型上分析topology-manager模块,由于拓扑显示的前台数据跟network-topology的YANG树形模型关系紧密,所以直接看network-topology。
一、拓扑的数据模型
network-topology:network-topology数据节点是直接使用了外部ietf-topology定义的yang文件,主要的数据树结构为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
container network-topology { list topology { description " This is the model of an abstract topology. A topology contains nodes and links. Each topology MUST be identified by unique topology-id for reason that a network could contain many topologies."; key "topology-id"; leaf topology-id { type topology-id; description " It is presumed that a datastore will contain many topologies. To distinguish between topologies it is vital to have UNIQUE topology identifiers."; } list node { description "The list of network nodes defined for the topology."; key "node-id"; uses node-attributes; must "boolean(../underlay-topology[*]/node[./supporting-nodes/node-ref])"; // This constraint is meant to ensure that a referenced node is in fact // a node in an underlay topology. list termination-point { description "A termination point can terminate a link. Depending on the type of topology, a termination point could, for example, refer to a port or an interface."; key "tp-id"; uses tp-attributes; } } list link { description " A Network Link connects a by Local (Source) node and a Remote (Destination) Network Nodes via a set of the nodes' termination points. As it is possible to have several links between the same source and destination nodes, and as a link could potentially be re-homed between termination points, to ensure that we would always know to distinguish between links, every link is identified by a dedicated link identifier. Note that a link models a point-to-point link, not a multipoint link. Layering dependencies on links in underlay topologies are not represented as the layering information of nodes and of termination points is sufficient."; key "link-id"; uses link-attributes; must "boolean(../underlay-topology/link[./supporting-link])"; // Constraint: any supporting link must be part of an underlay topology must "boolean(../node[./source/source-node])"; // Constraint: A link must have as source a node of the same topology must "boolean(../node[./destination/dest-node])"; // Constraint: A link must have as source a destination of the same topology must "boolean(../node/termination-point[./source/source-tp])"; // Constraint: The source termination point must be contained in the source node must "boolean(../node/termination-point[./destination/dest-tp])"; // Constraint: The destination termination point must be contained // in the destination node } } |
从中可以看到network-topology的container放的是一个topology列表,而每一个topology都是由node列表与link列表组成,也就是说web页面上的拓扑由交换机、主机节点以及对应的链接组成。
二、Topology-manager与其他模块的依赖
在topology-manager的处理流程需要依赖两条主线,第一是在applications文件夹下的topology-lldp-discovery模块会处理交换机与交换机的link连接(大概流程是lldp-speaker发送lldp报文探测新加入的设备是否为openflow交换机,如果是交换机则由topology-lldp-discovery处理并发出link发现的通告),因此topology-lldp-discovery模块会发出以下两类通告FlowTopologyDiscoveryListener:
1 2 |
onLinkDiscovered //链接添加 onLinkRemoved //链接移除 |
第二是topology-manager需要监听Nodes/Node/NodeConnector/FlowCapableNodeConnector和Nodes/Node/NodeConnector/FlowCapableNode数据的变化,此数据就是在inventory-manager模块添加的交换机信息节点,所以当inventory-manager模块新加或者移除一台交换机节点时,会触发Nodes/Node/NodeConnector/FlowCapableNodeConnector
以及Nodes/Node/NodeConnector/FlowCapableNode数据节点的变化,进而引起topology-manager模块进入
1 2 3 4 5 |
processAddedTerminationPoints processUpdatedTerminationPoints processRemovedTerminationPoints processAddedNode processRemovedNode |
处理流程。
三、Topology-manager流程
topology-manager的入口在TopologyLldpDiscoveryImplModule.java文件的createInstance()函数,进入该函数后new出FlowCapableTopologyProvider对象,然后注册FlowTopologyDiscoveryListener通告的接收器FlowCapableTopologyExporter,同时设置监听Nodes/Node/NodeConnector/FlowCapableNodeConnector
以及Nodes/Node/NodeConnector/FlowCapableNode的数据变化,
先分析FlowTopologyDiscoveryListener通告的接收器FlowCapableTopologyExporter。
3.1 onLinkDiscovered事件
当控制器通过LLDP发现新加的设备与现有设备存在一条链路时(交换机与交换机之间的链路),会触发onLinkDiscovered函数的调用,将该link保存在数据库当中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
public void onLinkDiscovered(final LinkDiscovered notification) { processor.enqueueOperation(new TopologyOperation() { @Override public void applyOperation(final ReadWriteTransaction transaction) { //构造network-topology的link数据 final Link link = toTopologyLink(notification); //数据应该存放的数据树节点位置 final InstanceIdentifier<Link> path = TopologyManagerUtil.linkPath(link, iiToTopology); transaction.merge(LogicalDatastoreType.OPERATIONAL, path, link, true); } @Override public String toString() { return "onLinkDiscovered"; } }); } |
3.2 onLinkRemoved事件
当上述链接断开之后,就会触发onLinkRemoved函数的调用,将之前保存在数据库当中的link信息删除。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
public void onLinkRemoved(final LinkRemoved notification) { processor.enqueueOperation(new TopologyOperation() { @Override public void applyOperation(final ReadWriteTransaction transaction) { Optional<Link> linkOptional = Optional.absent(); try { // read that checks if link exists (if we do not do this we might get an exception on delete) linkOptional = transaction.read(LogicalDatastoreType.OPERATIONAL, TopologyManagerUtil.linkPath(toTopologyLink(notification), iiToTopology)).checkedGet(); } catch (ReadFailedException e) { LOG.warn("Error occured when trying to read Link: {}", e.getMessage()); LOG.debug("Error occured when trying to read Link.. ", e); } //删除network-topology下的link数据 if (linkOptional.isPresent()) { transaction.delete(LogicalDatastoreType.OPERATIONAL, TopologyManagerUtil.linkPath(toTopologyLink(notification), iiToTopology)); } } @Override public String toString() { return "onLinkRemoved"; } }); } |
3.3 FlowCapableNode数据变化事件
再来分析监听Nodes/Node/NodeConnector/FlowCapableNode的数据变化的NodeChangeListenerImpl,当Nodes/Node/NodeConnector/FlowCapableNode的数据发生变化(inventory-manager当中新加/删除一台交换机节点),会触发NodeChangeListenerImpl的onDataChanged函数调用,在该函数当中调用了以下两个函数
1 2 |
processAddedNode(change.getCreatedData()); processRemovedNode(change.getRemovedPaths()); |
两个函数在自己内部判断是否是节点添加/删除事件,最终将交换机节点信息写入network-topology数据树当中。
1 2 3 4 5 6 7 8 9 10 |
protected void createData(final InstanceIdentifier<?> iiToNodeInInventory) { final NodeId nodeIdInTopology = provideTopologyNodeId(iiToNodeInInventory); if (nodeIdInTopology != null) { final InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node> iiToTopologyNode = provideIIToTopologyNode(nodeIdInTopology); //写入network-topology数据树 sendToTransactionChain(prepareTopologyNode(nodeIdInTopology, iiToNodeInInventory), iiToTopologyNode); } else { LOG.debug("Inventory node key is null. Data can't be written to topology"); } } |
3.4 FlowCapableNodeConnector数据变化事件
最后分析Nodes/Node/NodeConnector/FlowCapableNodeConnector的数据监听类TerminationPointChangeListenerImpl,当Nodes/Node/NodeConnector/FlowCapableNodeConnector数据发生变化时,会触发onDataChanged函数调用
1 2 3 |
processAddedTerminationPoints(change.getCreatedData()); processUpdatedTerminationPoints(change.getUpdatedData()); processRemovedTerminationPoints(change.getRemovedPaths()); |
类似的,上述3个函数也是内部判断是否是数据新加/更新/删除事件,假如是新加事件,则调用createData函数将TerminationPoint写入network-topology数据树。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
protected void createData(final InstanceIdentifier<?> iiToNodeInInventory, final DataObject data) { TpId terminationPointIdInTopology = provideTopologyTerminationPointId(iiToNodeInInventory); if (terminationPointIdInTopology != null) { InstanceIdentifier<TerminationPoint> iiToTopologyTerminationPoint = provideIIToTopologyTerminationPoint( terminationPointIdInTopology, iiToNodeInInventory); TerminationPoint point = prepareTopologyTerminationPoint(terminationPointIdInTopology, iiToNodeInInventory); sendToTransactionChain(point, iiToTopologyTerminationPoint); if (data instanceof FlowCapableNodeConnector) { removeLinks((FlowCapableNodeConnector) data, point); } } else { LOG.debug("Inventory node connector key is null. Data can't be written to topology termination point"); } } |
四、总结
ODL的前端显示需要后台数据支撑,当前版本的ODL对于前端需要显示的拓扑信息是由network-topology数据节点提供的,在network-topology数据节点下存在两类数据,一种是Link,一种是node,此时的node包括主机host和switch节点,host节点id是以host:开头,而switch节点的id是以openflow:开头,这个跟inventory数据节点的node是不一样的。
另外当switch设备(node)down掉之后,NodeChangeListenerImpl的processRemovedNode函数会将该node节点移除,同时与之相关的link(比如之前已在该node下发现host主机),并且会引起hosttracker当中的HostTrackerImpl删除该switch下的host主机,这样在拓扑上就不会有孤立的主机节点,但是该hosttracker还存在bug(已提交到odlbug系统,https://bugs.opendaylight.org/show_bug.cgi?id=7254,但迟迟没有人来解决,也没有回复,在此也求助大神们,谁能帮忙看下这个问题?或者怎么联系ODL组织?),使用mininet测试,一旦模拟的host数量过多(200多个)时,down掉switch,会有1-2个host节点残余在拓扑上,通过代码和log查看,并不是调用删除处有问题,好像是调用的odl基础框架有问题,还在进一步跟踪当中。
作者简介:鸿哥,硕士研究生,国内某通信设备公司软件研发工程师,主要从事云计算、SDN技术开发