POX学习笔记之POX拓扑发现原理分析




1. __init__函数
定义_this_cycle和_next_cycle列表,分别存储等待本轮发送和下轮发送的发送对象(包含着LLDP包)。两个列表里的元素是名为LLDPSenderItem的三元组:
SendItem = namedtuple("LLDPSenderItem", ('dpid','port_num','packet'))
当core.openflow组件()注册后监听PortStatus、ConnectionUp、ConnectionDown事件。
2._set_timer函数
取消原来的定时器,重新设置一个新定时器,重复调用_timer_handler函数。

3._timer_handler函数

根据发送数量的要求,从_this_cycle列表中取出相应数目的发送对象,调用core.openflow.sendToDPID函数发送出去,并把该发送对象添加到_next_cycle列表中等待下一轮继续发送。

4.create_discovery_packet函数
构造LLDP包。参数中的dpid指定哪台交换机,port_num指定从交换机的哪个端口发出去,port_addr将作为源以太网地址(交换机相应端口的MAC,此交换机是LLDP源)
5.add_port函数
覆盖原有的该port的信息,调用create_discovery_packet(self,dpid,port_num,port_addr)函数构造一个LLDP包,添加到_next_cycle列表中等待发送。如果设置了定时器,再调用_set_timer。
6.del_port函数
删除_this_cycle和_next_cycle列表中与该端口相关的发送对象,如果设置了定时器,再调用_set_timer。
7.del_switch函数
删除_this_cycle和_next_cycle列表中与该交换机相关的发送对象,如果设置了定时器,再调用_set_timer。
8.ConnectionUp处理函数
获得连接的交换机的所有端口信息,调用add_port函数。调用_set_timer函数。
9.PortStatus处理函数
根据是添加还是删除调用add_port函数或者del_port函数。
10.ConnectionDown处理函数
调用del_switch函数


1.__init函数
定义adjacency字典,该字典是链路到时间戳的映射,存储了所有链路的信息。注意:s1-->s2和s2-->s1对应的链路是等效的(Link类的uni函数决定的)
创建LLDPSender类对象,并在当core.openflow组件()注册后监听ConnectionUp、ConnectionDown、PacketIn事件()。
设置定时器,重复调用_expire_links函数。
2._expire_links函数
找出adjacency中超过保活时间的链路,传递给_delete_links函数。
3._delete_links函数
为每个要删除的链路触发LinkEvent事件,从adjacency中删除超过保活时间的链路。
4.is_edge_port函数
遍历adjacency字典,判定给定端口是不是没有连接其他交换机,是则返回True
5.install_flow函数
调用of.ofp_flow_mod函数生成一个匹配LLDP包的流表项发送到交换机,安装该流表项,使以后交换机收到的LLDP包直接从控制器端口传到控制器。
6.ConnectionUp处理函数
调用install_flow函数。(给新连接发送流表项并安装)
7.ConnectionDown处理函数
找出adjacency中与该连接有关的链路,传递给_delete_links函数。
8.PacketIn处理函数
解析收到的LLDP包,构造Link对象。如果不在adjacency字典中,就把该链路添加进去,并触发LinkEvent事件;如果已经在adjacency字典中,则更新该链路。


1._calc_spanning_tree函数
会返回tree结构,是DPID1到(DPID2,DPID1的与DPID2相连的端口号)元组的映射。计算根据Discovery类的adjacency字典进行。
函数中的adj结构会由{l.dpid1:{l.dpid2:[l],...},...}变为{s1:{s2:l.port1},s2:{s1:l.port2}},s1到s2可能通过多个端口相连。但只要找到第一个,就再也不找了,相当于s1到s2的连接只保留了一条。
最后构造出tree结构。其实tree的内容和adj一样,只是把内字典变成了集合"([ ])"。tree最终结构为{s1:([(s2,port1),(s3,port2),...]),s2:([(s1,port),...]),...}
2.全局变量_prev
DPID到{端口号1:是否关闭泛洪,端口号2:是否关闭泛洪,...},即端口状态的映射。
3.全局变量_dirty_switches
有待修改端口的交换机DPID到定时器的映射。
4._invalidate_ports函数
如果给定的dpid已经在_dirty_switches中,表示我们已经打算进行检查,直接返回;否则,设置定时器,添加到_dirty_switches字典中。定时器会调用_check_ports函数,并把dpid作为参数传递过去。
5._check_ports函数
从_dirty_switches中删除dpid对应的项,向该dpid对应的交换机发送barrier_request和features_request请求。
6._update_tree函数
首先调用_calc_spanning_tree()得到tree结构,然后遍历tree结构。遍历过程:
首先得到tree中每条链路对应的起始交换机sw,得到sw所有的端口。遍历这些端口,如果其端口号在tree中,则允许通过该端口泛洪(flood=True);即使不在tree中,如果该端口是边缘端口,也泛洪。如果在_prev中保存的端口状态与现在需要设置的状态一致,则什么都不需要做,开始遍历下一个端口;否则,更新统计加1,更新_prev中记录的端口状态,并向该端口发送OpenFlow消息告知应把端口设置为何状态,然后调用_invalidate_ports函数。(最终目的是为了发送features_request请求)
_update_tree函数中有一句“for p in con.ports.itervalues():”,con是Connection类的实例。Connection类在of_01.py中定义,self.ports = PortCollection()。而PortCollection类手动实现了一个类似的字典的类,提供端口号到端口的映射。
7.ConnectionUp事件处理函数
将_prev中该连接对应的交换机的旧端口状态清空。
如果默认禁止泛洪,把该连接对应的交换机所有端口在_prev中的位置设置为False,并发送ofp_port_mod消息告知禁止泛洪;调用_invalidate_ports函数(以便将该dpid加到_dirty_switches中)。
如果要延缓调用_update_tree函数,则设置定时器,延缓调用_update_tree函数。
8.LinkEvent事件处理函数
如果该链路两端在_prev中的记录都是False,也就是不泛洪(没有利用,相当于“关闭”),那么不需要任何处理,直接返回;否则,调用_update_tree函数。

discovery.py的launch函数里调用core.registerNew()注册Discovery组件时会创建Discovery类的实例,监听ConnectionUp、ConnectionDown、PacketIn事件,并创建LLDPSender类实例,设置其监听PortStatus、ConnectionUp、ConnectionDown事件。

spanning_tree.py的launch函数里有core.call_when_ready(start_spanning_tree, "openflow_discovery"),其中openflow_discovery是Discovery类在core里注册的名称。当Discovery组件注册后,调用start_spanning_tree函数。
start_spanning_tree函数里设置监听core.openflow()的ConnectionUp事件,core.openflow_discovery的LinkEvent事件。
接下来的工作就交给事件触发和事件处理函数了。
当连接新交换机时,触发ConnectionUp事件,LLDPSender类实例会获得连接的交换机的所有端口信息,调用add_port函数,构造LLDP包;调用_set_timer函数,从而调用_timer_handler函数,发送LLDP包。
当LLDP包送到控制器时,触发PacketIn事件,Discovery类的实例解析收到的LLDP包,构造Link对象。如果不在adjacency字典中,就把该链路添加进去,并触发LinkEvent事件;如果已经在adjacency字典中,则更新该链路。更新链路会触发LinkEvent事件,从而引起discovery.py里_update_tree函数被调用,从而调用_calc_spanning_tree()得到tree结构。
同理,连接断开或者端口改变时都会通过事件引发调用_calc_spanning_tree(),从而重新得到网络拓扑。
tree结构中的网络拓扑结构为
{s1:([(s2,port1),(s3,port2),...]),s2:([(s1,port),...]),...}
也就是字典“交换机:(连接的其他交换机,连接用的端口)构成的集合”

相关内容

    暂无相关文章