偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

Ryu拓撲發(fā)現(xiàn)原理分析

網(wǎng)絡(luò)
Ryu拓撲發(fā)現(xiàn)的核心模塊是ryu/topology目錄下的switches.py,拓撲發(fā)現(xiàn)的應(yīng)用是同目錄下的dumper.py。在dumper.py中,會利用_CONTEXTS來實例化switches.py中的Switches類,然后將拓撲發(fā)現(xiàn)的相關(guān)信息通過日志方式(LOG.debug)顯示。

Ryu拓撲發(fā)現(xiàn)的核心模塊是ryu/topology目錄下的switches.py,拓撲發(fā)現(xiàn)的應(yīng)用是同目錄下的dumper.py。在dumper.py中,會利用_CONTEXTS來實例化switches.py中的Switches類,然后將拓撲發(fā)現(xiàn)的相關(guān)信息通過日志方式(LOG.debug)顯示。啟動命令如下所示:

ryu-manager –verbose –observe-links ryu.topology.dumper 

或者

ryu-manager –verbose –observe-links ./ryu/topology/dumper.py

其中–verbose參數(shù)用于顯示LOG.debug信息,–observe-links用于指明拓撲發(fā)現(xiàn)。

[[150264]]

接下來對拓撲發(fā)現(xiàn)的核心模塊switches.py進行分析。

1. Port類

存儲端口相關(guān)信息,數(shù)據(jù)成員有:

self.dpid = dpid
self._ofproto = ofproto
self._config = ofpport.config
self._state = ofpport.state
self.port_no = ofpport.port_no
self.hw_addr = ofpport.hw_addr
self.name = ofpport.name

其中要特別注意的是dpid和port_no,即交換機ID和端口號,這兩個信息在下發(fā)流表項時很重要。

2. Switch類

存儲交換機相關(guān)信息,數(shù)據(jù)成員有:

self.dp = dp
self.ports = []

其中dp是Datapath類的實例,該類定義在在ryu/controller/controller.py,主要屬性有:

self.socket = socket
self.address = address
self.is_active = True
self.id = None # datapath_id is unknown yet
self.ports = None

ports是一個由Port類實例組成的列表,存儲該交換機的端口。

3. Link類

保存的是源端口和目的端口(都是Port類實例),數(shù)據(jù)成員有:

self.src = src
self.dst = dst

4. PortState類

該類繼承自dict,保存了從port_no(int型)到port(OFPPort類實例)的映射。該類主要用作self.port_state字典的值(鍵是dpid),用于存儲dpid對應(yīng)的交換機的所有端口情況。

OFPPort類定義在ryu/ofproto目錄下對應(yīng)的ofproto_v1_X_parser.py中(X代表版本號),繼承自一個namedtuple,保存有port_no等信息。

5. PortData類

保存每個端口與對應(yīng)的LLDP報文數(shù)據(jù),數(shù)據(jù)成員有:

self.is_down = is_down
self.lldp_data = lldp_data(這是LLDP報文的數(shù)據(jù))
self.timestamp = None
self.sent = 0

每調(diào)用一次lldp_sent函數(shù),便會把self.timestamp置為當前的時間(time.time()),并將self.sent加1;每調(diào)用一次lldp_received函數(shù),便會把self.sent置為0。

6.PortDataState類

繼承自dict類,保存從Port類到PortData類的映射。該類維護了一個類似雙向循環(huán)鏈表的數(shù)據(jù)結(jié)構(gòu),并重寫了__iter__(),使得遍歷該類的實例(self.ports)時,會按照該雙向循環(huán)鏈表從哨兵節(jié)點(self._root)后一個節(jié)點開始遍歷。

包含一個add_port函數(shù),傳入port和lldp_data,port作鍵,構(gòu)建的PortData類實例作為值。

包含一個lldp_sent(self,port)函數(shù),根據(jù)傳入的port(Port類實例)獲得對應(yīng)的PortData類實例port_data,然后調(diào)用port_data.lldp_sent()(該函數(shù)會設(shè)置時間戳),再調(diào)用self._move_last_key(port),把該port移到類似雙向循環(huán)鏈表的數(shù)據(jù)結(jié)構(gòu)中哨兵節(jié)點的前面(相當于下次遍歷的末尾);***返回port_data。

7. LinkState類

繼承自dict,保存從Link類到時間戳的映射。數(shù)據(jù)成員self._map字典用于存儲Link兩端互相映射的關(guān)系。

8. LLDPPacket類

靜態(tài)方法lldp_packet(dpid,port_no,dl_addr,ttl)用于構(gòu)造LLDP報文,靜態(tài)方法lldp_parse(data)用于解析LLDP包,并返回源DPID和源端口號。

9. Switches類

該類是Ryu拓撲發(fā)現(xiàn)的核心所在。Switches類是app_manager.RyuApp類的子類,當運行switches應(yīng)用時會被實例化,其__init__函數(shù)主要包括:

self.name = ‘switches’
self.dps = {} # datapath_id => Datapath class
self.port_state = {} # datapath_id => ports
self.ports = PortDataState() # Port class -> PortData class
self.links = LinkState() # Link class -> timestamp
self.is_active = True

self.dps字典用于保存dpid到Datapath類實例的映射,會在_register函數(shù)中添加新成員,_unregister函數(shù)中刪除成員。遍歷該字典可以得到連接的所有交換機。

self.port_state字典中鍵為dpid,值為PortState類型。遍歷該字典可以得到所有交換機對應(yīng)的端口情況。當交換機連接時,會檢查交換機的id是否在self.port_state中,不在則創(chuàng)建PortState類實例,把交換機的所有端口號和端口存儲到該實例中;交換機斷開時,會從self.port_state中刪除。

self.ports是PortDataState類的實例,保存每個端口(Port類型)對應(yīng)的LLDP報文數(shù)據(jù)(保存在PortData類實例中),遍歷self.ports用于發(fā)送LLDP報文。

self.links是LinkState類的實例,保存所有連接(Link類型)到時間戳的映射。遍歷self.links的鍵即可得到所有交換機之間的連接情況。

如果ryu-manager啟動時加了–observe-links參數(shù),則下面的self.link_discovery將為真,從而執(zhí)行if下面的語句:

self.link_discovery = self.CONF.observe_links
if self.link_discovery:
self.install_flow = self.CONF.install_lldp_flow
self.explicit_drop = self.CONF.explicit_drop
self.lldp_event = hub.Event()
self.link_event = hub.Event()
self.threads.append(hub.spawn(self.lldp_loop))
self.threads.append(hub.spawn(self.link_loop))

綜上所述,該初始化函數(shù)__init__()主要是創(chuàng)建用于存儲相關(guān)信息的數(shù)據(jù)結(jié)構(gòu),創(chuàng)建兩個事件,然后調(diào)用hub.spawn創(chuàng)建兩個新線程執(zhí)行self.lldp_loop和self.link_loop兩個函數(shù)。

#p#

9.1 lldp_loop函數(shù)

lldp_loop函數(shù)里是一個while循環(huán),只要self.is_active為真,就一直循環(huán)執(zhí)行。(close函數(shù)會把self.is_active置為False,該函數(shù)在離開模塊時自動被調(diào)用)。

(1)執(zhí)行self.lldp_event.clear(),將Event類實例lldp_event的_cond屬性設(shè)為False,用于線程間同步。

提到線程同步,常用的函數(shù)有:

Event.wait()

Event對象的wait的方法只有內(nèi)部信號為真的時候才會很快的執(zhí)行并完成返回。當Event對象的內(nèi)部信號標識為假時,則wait方法一直等待其為真時才返回。同時可以對wait設(shè)置timeout,當達到timeout設(shè)置的時間的時候就可以完成返回或執(zhí)行。

Event.set()

將標識位設(shè)為Ture

Event.clear()

將標識伴設(shè)為False。

Event.isSet()

判斷標識位是否為Ture。

(2)創(chuàng)建ports_now和ports兩個列表,分別存儲尚未發(fā)送過LLDP報文的端口和已發(fā)送過LLDP報文并且超時的端口。

(3)遍歷self.ports(PortDataState類的實例),獲得key(Port類實例)和data(PortData類實例),如果data.timestamp為None(該端口還沒發(fā)送過LLDP報文),則將key(端口)加入ports_now列表;否則,計算下次應(yīng)該發(fā)送LLDP報文的時間expire,如果已經(jīng)超時,則放到ports列表,否則就是還沒到發(fā)送時間,停止遍歷(發(fā)送LLDP報文時是按序發(fā)的,找到***個未超時的端口,后面的端口肯定更沒有超時,因為后面端口上次發(fā)送LLDP是在前一端口之后,前一個都沒超時后面的自然也沒超時)。

(4)遍歷ports_now列表,對每個端口調(diào)用self.send_lldp_packet(port),發(fā)送LLDP報文。

send_lldp_packet函數(shù)執(zhí)行過程如下:

a. 調(diào)用PortDataState類的lldp_sent函數(shù),該函數(shù)會設(shè)置時間戳,移動相應(yīng)端口在雙向循環(huán)鏈表中的位置,***返回PortData類實例port_data;

b. 如果該端口已經(jīng)down掉,直接返回,否則執(zhí)行下一步;

c. 根據(jù)port.dpid得到對應(yīng)的Datapath類實例dp,如果不存在,則直接返回,否則執(zhí)行下一步;

d. 發(fā)送LLDP報文。具體地:(1)生成actions:從port.port_no端口發(fā)出消息;(2)生成PacketOut消息:datapath指定為上一步得到的dp,actions為前面的,data為步驟a中返回的port_data的lldp_data;

(5)遍歷ports列表,對每個端口調(diào)用self.send_lldp_packet(port),發(fā)送LLDP報文。

9.2 link_loop函數(shù)

link_loop函數(shù)也是一個while循環(huán),只要self.is_active為真,就一直循環(huán)執(zhí)行;

(1)執(zhí)行self.link_event.clear(),將Event類實例link_event的_cond屬性設(shè)為False,用于線程間同步;

(2)創(chuàng)建deleted列表;

(3)遍歷self.links(LinkState類實例),獲得link(Link類實例)和timestamp時間戳。如果已經(jīng)超時,且該link對應(yīng)的源端口是否在self.ports中,并且發(fā)送LLDP次數(shù)已超過self.LINK_LLDP_DROP,則添加到deleted列表中;

(4)遍歷deleted列表,執(zhí)行:

a. 對其中的每條需要刪除的link調(diào)用link_down函數(shù)(該函數(shù)會刪除self.links中l(wèi)ink對應(yīng)的項目,并刪除self.links._map中l(wèi)ink對應(yīng)的項目),并觸發(fā)EventLinkDelete事件。

b. 得到link對應(yīng)的反向link,如果反向link不在deleted列表中,則將self.links中反向link的時間戳置為超時的事件,并將對端端口從self.ports的雙向循環(huán)鏈表中移動到哨兵節(jié)點的后面(下次檢查的開頭),以便盡早檢查反向link是否也斷開了。

9.3 state_change_handler

該函數(shù)用于處理EventOFPStateChange事件,當交換機連接或者斷開時會觸發(fā)該事件。

如果狀態(tài)是MAIN_DISPATCHER:

(1)從ev.datapath獲得Datapath類實例dp,如果該dp的dpid已經(jīng)在self.dps里有,則報出重復(fù)鏈接的警告。

(2)調(diào)用_register(),將dp.id和dp添加到self.dps中;如果該dp.id不在self.port_state中,則創(chuàng)建該dp.id對應(yīng)的PortState實例,并遍歷dp.ports.values,將所有port(OFPPort類型)添加到該PortState實例中。

(3)調(diào)用_get_switch(),如果dp.id在self.dps中,則創(chuàng)建一個Switch類實例,并把self.port_state中對應(yīng)的端口都添加到該實例中,最終返回該實例。

(4)如果交換機沒有重復(fù)連接,觸發(fā)EventSwitchEnter事件。

(5)如果沒設(shè)置self.link_discovery,返回;否則執(zhí)行下一步。

(6)如果設(shè)置了self.install_flow,則根據(jù)OpenFlow版本生成相應(yīng)流表項,使得收到的LLDP報文(根據(jù)目的MAC地址匹配)上報給控制器。

(7)如果交換機沒有重復(fù)連接,則遍歷(3)中得到的switch.ports的所有端口,如果端口port不是被保留的,則調(diào)用self._port_added(port),該函數(shù)會調(diào)用LLDPPacket.lldp_packet()函數(shù)生成LLDP報文數(shù)據(jù)lldp_data(用于和“port.is_down()”一起構(gòu)造PortData類實例),然后調(diào)用PortDataState類的add_port(port,lldp_data)。

add_port()函數(shù)會檢查port是否在self.ports中,不在則將該port添加到雙向循環(huán)鏈表中哨兵節(jié)點的后面(下次檢查的開頭),并把port和對應(yīng)的PortData類實例(該端口對應(yīng)的LLDP報文數(shù)據(jù))添加到self.ports中。

(8)調(diào)用self.lldp_event.set()

如果狀態(tài)是DEAD_DISPATCHER:

(1)如果dp.id為None,即握手之前交換機就斷開連接了,則直接返回;否則執(zhí)行下一步。

(2)調(diào)用_get_switch()獲得Switch實例;

(3)調(diào)用_unregister(),從self.dps和self.port_state中刪除該dpid對應(yīng)的數(shù)據(jù);

(4)觸發(fā)EventSwitchLeave事件。

(5)如果沒有設(shè)置link_discovery,返回;否則執(zhí)行下一步。

(6)遍歷switch.ports中的每個端口port,如果不是保留端口,則調(diào)用PortDataState類的del_port(),將self.ports中port對應(yīng)的數(shù)據(jù)刪除;調(diào)用Switches類的_link_down()。_link_down函數(shù)執(zhí)行如下操作:

a. 調(diào)用LinkState類的port_deleted函數(shù)。在port_deleted函數(shù)里,首先調(diào)用get_peer()獲得對端端口,然后生成兩個Link對象(src->dst和dst->src),并將這兩個對象從self.links中刪除(反向Link可能不存在);刪除src->dst和dst->src之間的映射(存儲在_map字典中)。***返回傳入的port對應(yīng)的對端port和傳入的port本身。

b. 根據(jù)返回的“傳入的port對應(yīng)的對端port和傳入的port本身”,創(chuàng)建Link對象,觸發(fā)EventLinkDelete事件(如果反向連接也存在,會觸發(fā)兩次EventLinkDelete事件)。

c. 調(diào)用self.ports.move_front(dst),該函數(shù)會從self.ports中得到dst對應(yīng)的PortData類實例port_data,如果port_data不為None,則調(diào)用clear_timestamp函數(shù)將其timestamp屬性置為None,并將dst移動到雙向循環(huán)鏈表中哨兵節(jié)點的后面(下次檢查的開頭)。

(7)調(diào)用self.lldp_event.set()。

#p#

9.4 port_status_handler

該函數(shù)用于處理EventOFPPortStatus事件,該事件是交換機主動發(fā)給控制器的。

如果原因為“添加”:

(1)在self.port_state里dp.id對應(yīng)的PortState實例中添加該端口,并觸發(fā)EventPortAdd事件。

(2)如果沒有設(shè)置self.link_discovery,則返回;否則執(zhí)行下一步。

(3)調(diào)用_get_port函數(shù),該函數(shù)首先根據(jù)傳入的dpid得到Switch實例,然后遍歷實例的ports列表,找到并返回傳入的端口號對應(yīng)的端口(Port類實例)。如果找到了端口并且端口不是保留的,則調(diào)用_port_added(),該函數(shù)會獲得LLDP相關(guān)的數(shù)據(jù)部分(用于構(gòu)造PortData類實例),然后調(diào)用PortDataState類的add_port(),該函數(shù)會將Port和對應(yīng)的PortData映射關(guān)系存儲到self.ports中;調(diào)用self.lldp_event.set()。

如果原因為“刪除”:

(1)在self.port_state里該dpid對應(yīng)的PortState實例中刪除該端口,并觸發(fā)EventPortDelete事件。

(2)如果沒有設(shè)置self.link_discovery,則返回;否則執(zhí)行下一步。

(3)調(diào)用_get_port函數(shù),該函數(shù)首先根據(jù)傳入的dpid得到Switch實例,然后遍歷實例的ports列表,找到并返回傳入的端口號對應(yīng)的端口(Port類實例)。如果找到了端口并且端口不是保留的,則:

a. 調(diào)用del_port(),將該端口及對應(yīng)的PortData從self.ports刪除;

b. 調(diào)用_link_down(),該函數(shù)會調(diào)用LinkState類的port_deleted函數(shù),并返回傳入的port對應(yīng)的對端port和傳入的port本身。在port_deleted函數(shù)里,首先調(diào)用get_peer()獲得對端端口,然后生成兩個Link對象(src->dst和dst->src),并將這兩個對象從self.links中刪除(反向Link可能不存在);刪除src->dst和dst->src之間的映射(存儲在_map字典中)。根據(jù)返回的“傳入的port對應(yīng)的對端port和傳入的port本身”,創(chuàng)建Link對象,觸發(fā)EventLinkDelete事件(如果反向連接也存在,會觸發(fā)兩次EventLinkDelete事件)。調(diào)用self.ports.move_front();

c. 調(diào)用self.lldp_event.set()。如果原因為“修改”:

(1)修改self.port_state里該dpid對應(yīng)的PortState實例值,并觸發(fā)EventPortModify事件。

(2)如果沒有設(shè)置self.link_discovery,則返回;否則執(zhí)行下一步。

(3)調(diào)用_get_port函數(shù),該函數(shù)首先根據(jù)傳入的dpid得到Switch實例,然后遍歷實例的ports列表,找到并返回傳入的端口號對應(yīng)的端口(Port類實例)。如果找到了端口并且端口不是保留的:

a. 調(diào)用PortDataState類的set_down(),該函數(shù)會調(diào)用Port類的is_down(),檢測端口是否已關(guān)閉;獲得Port對應(yīng)的PortData實例,調(diào)用PortData的set_down函數(shù),將對應(yīng)的is_down修改為當前狀態(tài)(布爾值);調(diào)用PortData的clear_timestamp(),將對應(yīng)的timestamp修改為None。如果檢測端口沒有關(guān)閉,調(diào)用_move_front_key()。set_down函數(shù)返回是否已關(guān)閉的檢測結(jié)果。如果檢測結(jié)果是已關(guān)閉,則調(diào)用_link_down()。

b. 調(diào)用self.lldp_event.set()。

9.5 packet_in_handler

該函數(shù)用于處理EventOFPPacketIn事件。

(1)如果沒有設(shè)置self.link_discovery,直接返回;否則執(zhí)行下一步。

(2)嘗試調(diào)用LLDPPacket.lldp_parse(msg.data)來按照LLDP報文格式解碼收到的報文,獲得源交換機dpid和源端口號(該LLDP報文從哪臺交換機的哪個端口發(fā)出的)。如果不是LLDP報文格式,返回;否則執(zhí)行下一步。

(3)獲得目的交換機的dpid和目的端口(上報Packet_In消息的交換機dpid和接收到LLDP報文的端口號)。

(4)調(diào)用_get_port函數(shù),得到源端口對應(yīng)的Port類實例。如果不存在或者該實例的dpid跟目的dpid相同,則直接返回;否則執(zhí)行下一步。

(5)調(diào)用PortDataState類的lldp_received函數(shù),該函數(shù)會再調(diào)用PortData類的lldp_received函數(shù),將對應(yīng)的self.sent值置為0。

(6)調(diào)用_get_port函數(shù),得到目的端口對應(yīng)的Port類實例。如果不存在該實例,則返回;否則執(zhí)行下一步。

(7)調(diào)用LinkState類的get_peer函數(shù),得到源端口原先對應(yīng)的目的端口。如果該目的端口存在,且與現(xiàn)在解析得到的目的端口不同,則說明原先的鏈路已斷開,觸發(fā)EventLinkDelete事件。

(8)根據(jù)源端口和目的端口構(gòu)造Link類實例,如果該實例不存在于self.links,則說明是新鏈路,觸發(fā)EventLinkAdd事件。

(9)調(diào)用LinkState類的update_link函數(shù),該函數(shù)會將上一步構(gòu)造的Link類實例加上時間戳存儲到self.links中,并構(gòu)造逆向鏈路,返回逆向鏈路是否在self.links中的布爾值。如果逆向鏈路還不存在,那很有可能會馬上存在,因此調(diào)用PortDataState類的move_front函數(shù),將目的端口移動到雙向循環(huán)鏈表中哨兵節(jié)點的后面(下次檢查的頭部),盡早檢查;調(diào)用self.lldp_event.set()。

(10)如果設(shè)置了self.explicit_drop,則調(diào)用_drop_packet函數(shù)。

10. 拓撲發(fā)現(xiàn)概述

Switches類的初始化函數(shù)__init__()創(chuàng)建用于存儲相關(guān)信息的數(shù)據(jù)結(jié)構(gòu)(self.dps、self.port_state、self.ports和self.links),創(chuàng)建兩個事件(self.lldp_event 和self.link_event),然后調(diào)用hub.spawn創(chuàng)建兩個新線程執(zhí)行self.lldp_loop和self.link_loop兩個函數(shù)。其他工作就交給事件觸發(fā)和事件處理函數(shù)了。

交換機連接時觸發(fā)EventOFPStateChange事件,在對應(yīng)的處理函數(shù)state_change_handler中會把連接上的交換機存儲到self.dps中,并把交換機的端口情況存儲到self.port_state中,并生成相應(yīng)的LLDP報文數(shù)據(jù),存儲在self.ports中(鍵為Port類型,值為PortData類型,PortData類的數(shù)據(jù)成員lldp_data存儲LLDP報文數(shù)據(jù))。

lldp_loop函數(shù)會不停遍歷self.ports,并在需要的時候由send_lldp_packet函數(shù)執(zhí)行發(fā)送LLDP報文的操作。

當LLDP報文被送回到控制器時,觸發(fā)EventOFPPacketIn事件,對應(yīng)的處理函數(shù)packet_in_handler會解析LLDP報文,得到交換機之間的連接信息(Link類),存儲到self.links中。

link_loop函數(shù)會遍歷self.links,及時檢查鏈路是否還是活的。

后記:

實際使用Ryu獲取拓撲信息時,更好的方式是使用Ryu提供的REST API,具體方法將在下文中介紹。但分析switches.py的過程對了解Ryu的工作機制和應(yīng)用編寫方法還是蠻有用的。

責任編輯:何妍 來源: 劍客
相關(guān)推薦

2015-07-27 14:57:32

OpenFlow協(xié)議Ryu

2015-12-30 13:21:33

SDN控制器拓撲

2015-09-11 09:15:32

RyuSDN

2023-11-29 16:21:30

Kubernetes服務(wù)注冊

2021-08-02 07:57:03

注冊Nacos源碼

2009-07-27 11:37:04

網(wǎng)絡(luò)拓撲摩卡

2017-07-07 14:30:27

Flink架構(gòu)拓撲

2018-12-19 14:00:07

MySQL主從復(fù)制數(shù)據(jù)庫

2010-08-12 16:18:17

網(wǎng)絡(luò)拓撲運維管理摩卡軟件

2009-01-11 22:32:53

電子發(fā)現(xiàn)法規(guī)遵從數(shù)據(jù)管理

2014-12-08 11:17:51

SDNDocker部署Docker

2017-01-23 13:34:44

2021-08-10 07:00:00

Nacos Clien服務(wù)分析

2020-10-13 07:35:22

JUC - Count

2023-04-26 08:39:41

Bitmap元素存儲

2022-04-13 08:23:31

Golang并發(fā)

2021-10-12 17:19:17

Random局限性變量

2013-01-21 10:55:52

大數(shù)據(jù)Ayasdi拓撲數(shù)據(jù)

2009-06-29 14:30:42

網(wǎng)絡(luò)拓撲KoolPoint摩卡

2010-05-25 09:26:13

IPv6鄰居發(fā)現(xiàn)協(xié)議
點贊
收藏

51CTO技術(shù)棧公眾號