def one_row_observation(self): #NEED_Norm=True#需要正規劃? while True: jitter_ms=[] loss_percent=[] bandwidth_bytes_per_s=[] latency_ms=[] for edge in list(GLOBAL_VALUE.G.edges()): edge_id1 = edge[0] edge_id2 = edge[1] if GLOBAL_VALUE.check_edge_is_link(edge_id1, edge_id2): #放入拓樸 jitter_ms.append(GLOBAL_VALUE.G[edge_id1][edge_id2]["detect"]["jitter_ms"]) loss_percent.append(GLOBAL_VALUE.G[edge_id1][edge_id2]["detect"]["loss_percent"]) bandwidth_bytes_per_s.append(GLOBAL_VALUE.G[edge_id1][edge_id2]["detect"]["bandwidth_bytes_per_s"]) latency_ms.append(GLOBAL_VALUE.G[edge_id1][edge_id2]["detect"]["latency_ms"]) ans=np.array([]) ans_no_norm=np.array([]) try: ans_no_norm=np.concatenate((jitter_ms,loss_percent,bandwidth_bytes_per_s,latency_ms)) jitter_ms=np.interp(jitter_ms,(0,GLOBAL_VALUE.MAX_Jitter_ms),(0,1)) loss_percent=np.interp(loss_percent,(0,GLOBAL_VALUE.MAX_Loss_Percent),(0,1)) bandwidth_bytes_per_s=np.interp(bandwidth_bytes_per_s,(0,GLOBAL_VALUE.MAX_bandwidth_bytes_per_s),(0,1)) latency_ms=np.interp(latency_ms,(0,GLOBAL_VALUE.MAX_DELAY_TO_LOSS_ms),(0,1)) ans=np.concatenate((jitter_ms,loss_percent,bandwidth_bytes_per_s,latency_ms)) except: print("one_row_observation error") if len(ans.tolist())!=0: return ans.tolist(),len(ans.tolist()),ans_no_norm.tolist() time.sleep(0.5)
def get_current_flow_group_entry(self, dst, priority): """ 根據dst ip與priority優先權 拿到目前網路已經設定的flow entry """ all_flow_mod = [] all_group_mod = [] _cookie_id = GLOBAL_VALUE.get_cookie(dst, priority) for datapath_id in GLOBAL_VALUE.G.nodes: #搜尋 if _cookie_id in GLOBAL_VALUE.G.nodes[(datapath_id, None)]["GROUP_TABLE"]: all_group_mod.append( GLOBAL_VALUE.G.nodes[(datapath_id, None)]["GROUP_TABLE"][_cookie_id]) for match in GLOBAL_VALUE.G.nodes[(datapath_id, None)]["FLOW_TABLE"][2][priority]: mod = GLOBAL_VALUE.G.nodes[( datapath.id, None)]["FLOW_TABLE"][2][priority][match] try: ipv4_dst = msg.match.get("ipv4_dst") if ipv4_dst == dst: all_flow_mod.append(mod) except: pass return all_flow_mod, all_group_mod
def node_iteration(): while True: for node_id in GLOBAL_VALUE.G.copy().nodes: if GLOBAL_VALUE.check_node_is_switch(node_id): datapath = GLOBAL_VALUE.G.nodes[node_id]["datapath"] func(datapath) hub.sleep(self.delta)
def clear_link_tmp_data(): for edge in list(GLOBAL_VALUE.G.edges()): edge_id1 = edge[0] edge_id2 = edge[1] if GLOBAL_VALUE.check_edge_is_link(edge_id1, edge_id2): # GLOBAL_VALUE.G[start_switch][end_switch]["port"][end_switch] link_data = GLOBAL_VALUE.G[edge_id1][edge_id2] link_data["tmp"] = nested_dict()
def check_all_edge(self)->int: #取出所有交換機的port #這裡必須等待所有交換機都連線控制器 #FIXME 等待有更好的寫法 while True: port_check=nested_dict(2,str) #確認port是否連接為host for node_id in GLOBAL_VALUE.G.copy().nodes: if GLOBAL_VALUE.check_node_is_switch(node_id): for k in GLOBAL_VALUE.G.nodes[node_id]["port"]: port_check[node_id[0]][k]=None if len(GLOBAL_VALUE.G.nodes[node_id]["port"][k]["host"])!=0: pass port_check[node_id[0]][k]="host" #確認port是否連接為edge count_edge=0 for edge in list(GLOBAL_VALUE.G.edges()): edge_id1 = edge[0] edge_id2 = edge[1] if GLOBAL_VALUE.check_edge_is_link(edge_id1, edge_id2): port_check[edge_id1[0]][edge_id1[1]]="edge" port_check[edge_id2[0]][edge_id2[1]]="edge" print(port_check,"check_all_edge") #確認是否每個port連接對象完成 check_ok=True count_edge=0 for datapath_id in port_check: for port in port_check[datapath_id]: if port_check[datapath_id][port]==None: check_ok=False elif port_check[datapath_id][port]=="edge": count_edge=count_edge+1 print(port_check,"check_all_edge2") if check_ok and count_edge!=0: return count_edge time.sleep(1)
def sent_opeld_to_all_port(): for node_id in GLOBAL_VALUE.G.copy().nodes: if GLOBAL_VALUE.check_node_is_switch(node_id): switch_data = GLOBAL_VALUE.G.nodes[node_id] # switch_data = switch_data.copy() for port_no in switch_data["port"].keys(): # if the port can working if switch_data["port"][port_no]["OFPMP_PORT_DESC"]["state"] == ofproto_v1_5.OFPPS_LIVE: hub.spawn(self.send_opeld_packet, switch_data["datapath"], port_no, extra_byte=self.monitor_each_opeld_extra_byte, num_packets=self.monitor_sent_opedl_packets)
def start_dynamic_tc_module(self): print("start_dynamic_tc_module等待拓樸完成") hub.sleep(20) interface_list=[] for node_id in GLOBAL_VALUE.G.copy().nodes: if GLOBAL_VALUE.check_node_is_port(node_id): datapath_id=node_id[0] port_id=node_id[1] interface_list.append("s"+str(datapath_id)+"-eth"+str(port_id)) self._dynamic_tc_module = Process(target=dynamic_tc.entry, args=(interface_list,)) self._dynamic_tc_module.start()
def send_ADD_FlowMod(mod): """ 這裡是低階發送FlowMod為了統計與紀錄所有flow entry """ datapath = mod.datapath OFPFlowMod = mod.__dict__ table_id = OFPFlowMod["table_id"] match = OFPFlowMod["match"] priority = OFPFlowMod["priority"] ofp = datapath.ofproto # 要交換機當flow entry被刪除都要通知控制器 mod.flags = ofp.OFPFF_SEND_FLOW_REM # if self._check_no_overlap_on_server(datapath.id,table_id,priority,match): mod.xid = GLOBAL_VALUE.get_xid(datapath.id) GLOBAL_VALUE.G.nodes[(datapath.id, None) ]["FLOW_TABLE"][table_id][priority][str(match)] = mod GLOBAL_VALUE.error_search_by_xid[datapath.id][mod.xid]=mod datapath.send_msg(mod) # return True return True
def arp_request_all(dst_ip): "暴力搜尋dst_ip的mac為多少:目的維護arp與了解到底哪個ip在哪個port這樣才能路由" for node_id in GLOBAL_VALUE.G.copy().nodes: if GLOBAL_VALUE.check_node_is_switch(node_id): switch_data = GLOBAL_VALUE.G.nodes[node_id] for port_no in switch_data["port"].keys(): # if the port can working if switch_data["port"][port_no]["OFPMP_PORT_DESC"][ "state"] == ofproto_v1_5.OFPPS_LIVE: # arp_src_ip="0.0.0.0"這種是ARP probe型態的封包 # An ARP Probe with an all-zero 'sender IP address' -rfc5227 [Page 6] hub.spawn(send_arp_packet, datapath=switch_data["datapath"], out_port=port_no, eth_src_mac='00:00:00:00:00:00', eth_dst_mac="ff:ff:ff:ff:ff:ff", arp_opcode=arp.ARP_REQUEST, arp_src_mac='00:00:00:00:00:00', arp_src_ip="0.0.0.0", arp_dst_mac='00:00:00:00:00:00', arp_dst_ip=dst_ip, payload=GLOBAL_VALUE.ARP_SEND_FROM_CONTROLLER)
def send_add_group_mod_v1(datapath, port_list: list, weight_list: list, group_id: int, dry_run=False): #ofp_group_bucket_prop_weight 的weight為uint16_t所以0~65535 #沒必要設定權重0所以要把數值壓縮在1~65535 #注意型態weight_list [np.int64,np.int64...] print("send_add_group_mod_v1") weight_list = list( minmax_scale(weight_list, feature_range=(1, 65535)).astype(int)) """ group_id是給flow entry指引要導到哪個group entry """ ofp = datapath.ofproto ofp_parser = datapath.ofproto_parser buckets = [] #Each bucket of the group must have an unique Bucket ID-openflow1.51-p115 所以亂給個id給它 反正我沒用到 bucket_id = 1 for port, weight in zip(port_list, weight_list): #bucket_id=GLOBAL_VALUE.G.nodes[(datapath.id,None)]["now_max_group_id"] actions = [ofp_parser.OFPActionOutput(port)] #spec openflow1.5.1 p.116 struct ofp_group_bucket_prop_weight #注意ryu ofv1.5.1的group範例寫錯 #下面這個寫法要靠自己挖ryu原始碼才寫出來,所以遇到問題盡量挖控制器ryu,openvswitch原始碼 #ofp_bucket->properties->ofp_group_bucket_prop_weight-spec openflow1.5.1 p.115~p.116 _weight = ofp_parser.OFPGroupBucketPropWeight(weight=int(weight), length=8, type_=ofp.OFPGBPT_WEIGHT) buckets.append( ofp_parser.OFPBucket(bucket_id=bucket_id, actions=actions, properties=[_weight])) bucket_id = bucket_id + 1 #GLOBAL_VALUE.G.nodes[(datapath.id,None)]["now_max_group_id"]=GLOBAL_VALUE.G.nodes[(datapath.id,None)]["now_max_group_id"]+1 #從多個路線根據權重(weight)隨機從buckets選一個OFPBucket(ofp.OFPGT_SELECT),OFPBucket裡面就放要actions要出去哪個port command = None if GLOBAL_VALUE.G.nodes[(datapath.id, None)]["GROUP_TABLE"][group_id] == {}: command = ofp.OFPGC_ADD else: command = ofp.OFPGC_MODIFY #openvswitch擴展協議可以控制group table 的hash的演算法 #https://github.com/openvswitch/ovs/blob/master/Documentation/group-selection-method-property.txt #底下黑魔法寫法 ryu的OFPGroupBucketPropExperimenter結構不適合寫我硬繞過去所以醜醜 #目前範例不啟用 properties = [] """hash_alog="nohash" if hash_alog=="hash": #"hash" ascii == 0x68617368 hash_alog_magic=[0,0x68617368,0,0,0,0,0] _select_method=ofp_parser.OFPGroupBucketPropExperimenter(type_=ofp.OFPGBPT_EXPERIMENTER,exp_type=1,experimenter=0x0000154d,data=hash_alog_magic) properties=[_select_method]""" mod = ofp_parser.OFPGroupMod(datapath, command=command, type_=ofp.OFPGT_SELECT, group_id=group_id, buckets=buckets, properties=properties) if dry_run: return mod #xid為了讓控制器知道哪個動作是錯誤的 mod.xid = GLOBAL_VALUE.get_xid(datapath.id) print("send_add_group_mod_v1acquire") datapath.send_msg(mod) print("send_add_group_mod_v1release") GLOBAL_VALUE.G.nodes[(datapath.id, None)]["GROUP_TABLE"][group_id] = mod #當發生錯誤就可以搜尋哪個動錯出錯 GLOBAL_VALUE.error_search_by_xid[datapath.id][mod.xid] = mod return mod
def setting_multi_route_path(self, route_path_list, weight_list, dst, prev_path=[], prev_weight=[], tos=0, idle_timeout=0, hard_timeout=0, delivery_schemes="unicast"): """ 這個版本比較笨需要全部刪除才能重新設定 .. mermaid:: graph TD Start --> 確保沒有其他線程正在設定dst_ip,tos的路徑 確保沒有其他線程正在設定dst_ip,tos的路徑-->|否|確保沒有其他線程正在設定dst_ip,tos的路徑 確保沒有其他線程正在設定dst_ip,tos的路徑-->|是|計算多條路權重合併 計算多條路權重合併-->刪除dst_ip與tos的所有路徑 刪除dst_ip與tos的所有路徑 --> 尋找所有岔路 尋找所有岔路 --> 設定所有group[設定所有group entry] 設定所有group[設定所有group entry] --> 設定非起點的flow[設定非起點的flow entry] 設定非起點的flow[設定非起點的flow entry] --> 設定起點的flow[設定起點的flow entry] 設定起點的flow[設定起點的flow entry] --> End .. mermaid:: sequenceDiagram Title: 設定mulipath路徑流程 autonumber 交換機->>控制器: ofp_packet_in(flow table發生table miss的封包) 控制器->>控制器: 依照封包的tos去選擇,不同Qos設計出來的拓樸權重,該權重利用強化學習優化 控制器->>交換機: ofp_group_mod(如果有岔路就需要設定group entry) rect rgba(0, 0, 255, .1) 控制器->>+交換機: OFPT_BARRIER_REQUEST(確認先前設定已經完成) 交換機->>-控制器: OFPT_BARRIER_REPLY(當完成設定就會回傳) Note over 交換機,控制器: 確保group entry設定完成 end 控制器->>交換機: ofp_flow_mod(設定非起點的flow entry,如果有岔路就需要設定group id) rect rgba(0, 0, 255, .1) 控制器->>+交換機: OFPT_BARRIER_REQUEST(確認先前設定已經完成) 交換機->>-控制器: OFPT_BARRIER_REPLY(當完成設定就會回傳) Note over 交換機,控制器: 確保非起點的flow entry設定完成 end 控制器->>交換機: ofp_flow_mod(設定起點的flow entry,如果有岔路就需要設定group id) """ #確保不為空 if route_path_list == []: return all_mod = [] #route_path_list 必須保證free loop priority, dscp = prioritylib.tos_base(tos) #print(dst,priority) _cookie = GLOBAL_VALUE.get_cookie(dst, priority) for path, weight in zip(route_path_list, weight_list): a = "" for i in path[2::3]: a = a + "->" + str(i) #print(a) # https://osrg.github.io/ryu-book/zh_tw/html/spanning_tree.html # delivery_schemes https://en.wikipedia.org/wiki/Routing#Delivery_schemes # 單播多路徑unicast # FIXME 未來開發多播multicast dst 可能要很多個? # 多條路徑(route_path_list)結合每個路徑的權重(weight_list) # weight_list[0]代表route_path_list[0]的權重 以此類推 # 探討group如何select bucket # flow_mask_hash_fields https://github.com/openvswitch/ovs/blob/v2.15.0/lib/flow.c#L2462 # pick_default_select_group https://github.com/openvswitch/ovs/blob/v2.15.0/ofproto/ofproto-dpif-xlate.c#L4564 # group_best_live_bucket https://github.com/openvswitch/ovs/blob/v2.15.0/ofproto/ofproto-dpif-xlate.c#L1956 print("載入原先的路徑") for path, weight in zip(prev_path, prev_weight): route_path_list.append(path) weight_list.append(weight) #紀錄每個交換機需要OUTPUT的 print("紀錄每個交換機需要OUTPUT的") _switch = nested_dict(2, dict) for path, weight in zip(route_path_list, weight_list): for i in path[2::3]: set_datapath_id = i[0] set_out_port = i[1] if _switch[set_datapath_id][set_out_port] != {}: #FIXME 多條路線經過同的路徑 權重利用平均 最小數值為1 _switch[set_datapath_id][set_out_port] = max( int((_switch[set_datapath_id][set_out_port] + weight) / 2), 1) else: _switch[set_datapath_id][set_out_port] = weight print("刪除光原先的group entry flow entry重新設定") #刪除光原先的group entry flow entry重新設定 self.clear_multi_route_path(dst, priority) print("設定只有岔路需要設定group entry") #設定只有岔路需要設定group entry set_switch_for_barrier = set() for set_datapath_id in _switch: tmp_datapath = GLOBAL_VALUE.G.nodes[(set_datapath_id, None)]["datapath"] ofp = tmp_datapath.ofproto parser = tmp_datapath.ofproto_parser # 交換機出路大於2大表有岔路 and 此交換機還沒設定過group entry if len(_switch[set_datapath_id].keys() ) >= 2 and set_datapath_id not in set_switch_for_barrier: port_list = list(_switch[set_datapath_id].keys()) group_weight_list = [] for p in list(_switch[set_datapath_id].keys()): group_weight_list.append(_switch[set_datapath_id][p]) group_mod = OFPT_GROUP_MOD.send_add_group_mod_v1(tmp_datapath, port_list, group_weight_list, group_id=_cookie) all_mod.append(group_mod) set_switch_for_barrier.add(tmp_datapath) print("確保剛才group entry 設定完成這樣後面用到group entry的路線才不會錯誤") #確保剛才group entry 設定完成這樣後面用到group entry的路線才不會錯誤 for tmp_datapath in set_switch_for_barrier: self.wait_finish_switch_BARRIER_finish(tmp_datapath) #開始設定除了起點的flow entry print("開始設定除了起點的flow entry") set_switch_for_barrier = set() for path, weight in zip(route_path_list, weight_list): for i in path[5::3]: set_datapath_id = i[0] set_out_port = i[1] tmp_datapath = GLOBAL_VALUE.G.nodes[(set_datapath_id, None)]["datapath"] set_switch_for_barrier.add(tmp_datapath) ofp = tmp_datapath.ofproto parser = tmp_datapath.ofproto_parser match = parser.OFPMatch(eth_type=ether_types.ETH_TYPE_IP, ipv4_dst=dst, ip_dscp=dscp) if len(_switch[set_datapath_id].keys()) >= 2: #有岔路需要group entry action = [parser.OFPActionGroup(group_id=_cookie)] instruction = [ parser.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, action) ] mod = parser.OFPFlowMod(datapath=tmp_datapath, priority=priority, table_id=2, command=ofp.OFPFC_ADD, match=match, cookie=_cookie, instructions=instruction, idle_timeout=idle_timeout, hard_timeout=hard_timeout) all_mod.append(mod) OFPT_FLOW_MOD.send_ADD_FlowMod(mod) else: #沒有岔路不需要group entry action = [parser.OFPActionOutput(port=set_out_port)] instruction = [ parser.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, action) ] mod = parser.OFPFlowMod(datapath=tmp_datapath, priority=priority, table_id=2, command=ofp.OFPFC_ADD, match=match, cookie=_cookie, instructions=instruction, idle_timeout=idle_timeout, hard_timeout=hard_timeout) all_mod.append(mod) OFPT_FLOW_MOD.send_ADD_FlowMod(mod) #確保已經設定 print("確保已經設定") for tmp_datapath in set_switch_for_barrier: self.wait_finish_switch_BARRIER_finish(tmp_datapath) set_switch_for_barrier = set() #全部設定完成 才能開始設定開頭的路線 print("全部設定完成 才能開始設定開頭的路線") for path, weight in zip(route_path_list, weight_list): i = path[2] #開頭 set_datapath_id = i[0] set_out_port = i[1] tmp_datapath = GLOBAL_VALUE.G.nodes[(set_datapath_id, None)]["datapath"] set_switch_for_barrier.add(tmp_datapath) ofp = tmp_datapath.ofproto parser = tmp_datapath.ofproto_parser match = parser.OFPMatch(eth_type=ether_types.ETH_TYPE_IP, ipv4_dst=dst, ip_dscp=dscp) if len(_switch[set_datapath_id].keys()) >= 2: action = [parser.OFPActionGroup(group_id=_cookie)] instruction = [ parser.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, action) ] all_mod.append(mod) mod = parser.OFPFlowMod(datapath=tmp_datapath, priority=priority, table_id=2, command=ofp.OFPFC_ADD, match=match, cookie=_cookie, instructions=instruction, idle_timeout=idle_timeout, hard_timeout=hard_timeout) all_mod.append(mod) OFPT_FLOW_MOD.send_ADD_FlowMod(mod) else: action = [parser.OFPActionOutput(port=set_out_port)] instruction = [ parser.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, action) ] mod = parser.OFPFlowMod(datapath=tmp_datapath, priority=priority, table_id=2, command=ofp.OFPFC_ADD, match=match, cookie=_cookie, instructions=instruction, idle_timeout=idle_timeout, hard_timeout=hard_timeout) all_mod.append(mod) OFPT_FLOW_MOD.send_ADD_FlowMod(mod) print("BARRIER 全部設定完成") for tmp_datapath in set_switch_for_barrier: self.wait_finish_switch_BARRIER_finish(tmp_datapath) print("BARRIER finish 全部設定完成") # FIXME 這個數值有最大值 需要回收 GLOBAL_VALUE.PATH[dst][priority]["cookie"] = _cookie if GLOBAL_VALUE.PATH[dst][priority]["path"] == {}: GLOBAL_VALUE.PATH[dst][priority]["path"] = [] if GLOBAL_VALUE.PATH[dst][priority]["weight"] == {}: GLOBAL_VALUE.PATH[dst][priority]["weight"] = [] for p, w in zip(route_path_list, weight_list): if p not in GLOBAL_VALUE.PATH[dst][priority]["path"]: GLOBAL_VALUE.PATH[dst][priority]["path"].append(p) GLOBAL_VALUE.PATH[dst][priority]["weight"].append(w) if GLOBAL_VALUE.PATH[dst][priority]["graph"] == {}: GLOBAL_VALUE.PATH[dst][priority]["graph"] = nx.DiGraph() for path in route_path_list: prev_node = None for node in path: if prev_node != None: if GLOBAL_VALUE.PATH[dst][priority]["graph"] == {}: GLOBAL_VALUE.PATH[dst][priority]["graph"] = nx.DiGraph() GLOBAL_VALUE.PATH[dst][priority]["graph"].add_edge(prev_node, node, weight=1) prev_node = node #print(all_mod) return all_mod
def _update_tui(self): title = """ ███████╗██████╗ ███╗ ██╗ ███╗ ███╗ ██████╗ ███╗ ██╗██╗████████╗ ██████╗ ██████╗ ██╔════╝██╔══██╗████╗ ██║ ████╗ ████║██╔═══██╗████╗ ██║██║╚══██╔══╝██╔═══██╗██╔══██╗ ███████╗██║ ██║██╔██╗ ██║ ██╔████╔██║██║ ██║██╔██╗ ██║██║ ██║ ██║ ██║██████╔╝ ╚════██║██║ ██║██║╚██╗██║ ██║╚██╔╝██║██║ ██║██║╚██╗██║██║ ██║ ██║ ██║██╔══██╗ ███████║██████╔╝██║ ╚████║ ██║ ╚═╝ ██║╚██████╔╝██║ ╚████║██║ ██║ ╚██████╔╝██║ ██║ ╚══════╝╚═════╝ ╚═╝ ╚═══╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝Author:Lu-Yi-Hsun """ Mapping_idx = 0 MAX_TUI_LINE = 300 for idx, i in enumerate(title.split("\n")): self.TUI_Mapping.append(i) Mapping_idx = idx for i in range(MAX_TUI_LINE): self.TUI_Mapping.append("") Mapping_start = Mapping_idx+1 while True: Mapping_idx = Mapping_start for node_id in GLOBAL_VALUE.G.copy().nodes: if GLOBAL_VALUE.check_node_is_switch(node_id): switch_title = Mapping_idx Mapping_idx += 1 if_EDGE_SWITCH = "" for port in GLOBAL_VALUE.G.copy().nodes[node_id]["port"].keys(): host = GLOBAL_VALUE.G.nodes[node_id]["port"][port]["host"] if host != {}: if_EDGE_SWITCH = "Edge Switch" stats = GLOBAL_VALUE.G.nodes[node_id]["port"][port]["OFPMP_PORT_STATS"] desc = GLOBAL_VALUE.G.nodes[node_id]["port"][port]["OFPMP_PORT_DESC"] line = "★ port"+str(port)+" state "+str(desc["state"])+" duration_sec "+str(stats["duration_sec"])+" rx_bytes "+str(stats["rx_bytes"])+" tx_bytes "+str( stats["tx_bytes"])+" rx_bytes_delta "+str(stats["rx_bytes_delta"])+" tx_bytes_delta "+str(stats["tx_bytes_delta"])+" host "+str(host) self.TUI_Mapping[Mapping_idx] = line Mapping_idx += 1 line = "datapath ID"+str(node_id)+" "+if_EDGE_SWITCH self.TUI_Mapping[switch_title] = line line = "all_port_duration_s " + \ str(GLOBAL_VALUE.G.nodes[node_id]["all_port_duration_s"]) self.TUI_Mapping[Mapping_idx] = line """ 'duration_sec': 26664, 'duration_nsec': 862000000, 'rx_packets': 22, 'tx_packets': 19 5, 'rx_bytes': 1636, 'tx_bytes': 12016, 'rx_dropped': 0, 'tx_dropped': 0, 'rx_errors': 0, 'tx_errors': 0 """ Mapping_idx += 1 self.TUI_Mapping[Mapping_idx] = "" Mapping_idx += 1 self.TUI_Mapping[Mapping_idx] = "Show Flow Entry" Mapping_idx += 1 for table_id in GLOBAL_VALUE.G.copy().nodes[node_id]["FLOW_TABLE"]: for priority in GLOBAL_VALUE.G.copy().nodes[node_id]["FLOW_TABLE"][table_id]: for match in GLOBAL_VALUE.G.copy().nodes[node_id]["FLOW_TABLE"][table_id][priority]: self.TUI_Mapping[Mapping_idx] = "table_id: "+str( table_id)+" priority: "+str(priority)+" match: "+str(match) Mapping_idx += 1 self.TUI_Mapping[Mapping_idx] = "Topology Link" Mapping_idx += 1 # self.TUI_Mapping[Mapping_idx]=str(GLOBAL_VALUE.G.edges()) # print(GLOBAL_VALUE.G.edges()) for edge in list(GLOBAL_VALUE.G.edges()): edge_id1 = edge[0] edge_id2 = edge[1] if GLOBAL_VALUE.check_edge_is_link(edge_id1, edge_id2): # GLOBAL_VALUE.G[start_switch][end_switch]["port"][end_switch] ed = GLOBAL_VALUE.G[edge_id1][edge_id2] edge_start = "datapath ID " + \ str(edge_id1[0]) + " port_no "+str(edge_id1[1]) edge_end = "datapath ID " + \ str(edge_id2[0]) + " port_no "+str(edge_id2[1]) edge_data = edge_start+" ---> "+edge_end self.TUI_Mapping[Mapping_idx] = edge_data Mapping_idx += 1 link_data = "" loss_percent = str(ed["detect"]["loss_percent"]) bandwidth_bytes_per_s = str( ed["detect"]["bandwidth_bytes_per_s"]) latency_ms = str(ed["detect"]["latency_ms"]) jitter_ms = str(ed["detect"]["jitter_ms"]) link_data = "loss_percent:"+loss_percent + \ " "+"bandwidth_bytes_per_s:"+bandwidth_bytes_per_s+" " + \ "latency_ms:"+latency_ms+" "+"jitter_ms"+jitter_ms # print(link_data) self.TUI_Mapping[Mapping_idx] = link_data Mapping_idx += 1 # GLOBAL_VALUE.G[start_switch][end_switch]["detect"][link_index]["jitter_ms"]=jitter#millisecond # GLOBAL_VALUE.G[start_switch][end_switch]["detect"][link_index]["loss_percent"]=loss#%how many percent of the packet loss # GLOBAL_VALUE.G[start_switch][end_switch]["detect"][link_index]["bandwidth_bytes_per_s"]=bw# # GLOBAL_VALUE.G[start_switch][end_switch]["detect"][link_index]["latency_ms"]=delay_one_packet#from a to b ,just one way ,millisecond # GLOBAL_VALUE.G[start_switch][end_switch]["weight"]=formula.OSPF(bw*8) for i in range(30): self.TUI_Mapping[Mapping_idx] = "" Mapping_idx += 1 hub.sleep(1)
def rl_zmq_connecter(self): """ 這裡負責傳送必要資訊給強化學習並且拿回策略 """ self.clear_temp_file() print("請至少等待所有交換機都連線完成") time.sleep(10) #FIXME 這裡要處理等待拓樸探索完成,必須等待拓樸建構完成才知道強化學習輸出的結構,缺點無法應用在拓樸頻繁更動的環境 count_edge=self.check_all_edge() # Send reply back to client _,observation_space,_=self.one_row_observation() init_RL={"action_space":count_edge,"observation_space":observation_space} print(init_RL) init_RL_str=json.dumps(init_RL) action_uuid=0 self.write_for_rl(str(init_RL_str)) while True: # Wait for next request from client message = self.read_for_robot() # Do some 'work' try: message_list=json.loads(message) message_action_uuid=int(message_list["action_uuid"]) #print(message_action_uuid,action_uuid) if message_action_uuid<action_uuid: #為了確保action是新的 continue action_uuid=message_action_uuid message_list=message_list["action"] except: hub.sleep(1) continue print("ACTIONs",message_list) e_index=0 #把ai回傳的權重放入拓樸 for edge in list(GLOBAL_VALUE.G.edges()): edge_id1 = edge[0] edge_id2 = edge[1] if GLOBAL_VALUE.check_edge_is_link(edge_id1, edge_id2): #放入拓樸 #每個 #print(edge_id1,edge_id2) GLOBAL_VALUE.G[edge_id1][edge_id2]["ppinin"]=message_list[e_index] e_index=e_index+1 print("權重\n\n") print(edge_id1,edge_id2,GLOBAL_VALUE.G[edge_id1][edge_id2]["ppinin"]) print("權重\n\n") GLOBAL_VALUE.NEED_ACTIVE_ROUTE=True #這裡設定-10為了當沒有封包流動reward應該0但是一直頻繁設定網路我給負號獎勵 time.sleep(10) # Send reply back to client GLOBAL_VALUE.ALL_REWARD=GLOBAL_VALUE.ALL_REWARD-10 obs,_,obs_no_norm=self.one_row_observation() step_data={"action_uuid":action_uuid,"obs":list(obs),"obs_no_norm":list(obs_no_norm),"reward":GLOBAL_VALUE.ALL_REWARD} #FIXME 回傳 print("step_data",step_data) self.write_for_rl(str(json.dumps(step_data)))
def setting_multi_route_path_base_vlan(self, route_path_list, weight_list, ipv4_dst, ipv4_src): #利用vlan tag把每個simple path切開 #舉例有5條路線 #起始:一開始起點交換機就要從group entry裡面5個bucket選擇一個bucket,每個bucket代表某一條simple path,bucket做三個動作 1."Push VLAN header" 2."Set-Field VLAN_VID value"3."Output port" VLAN_VID為此simple path push vlan tag #中間:路由根據src dst vlan_id 去路由 #最後:終點交換機要pop vlan tag才丟給host """ 非常重要要注意要清楚 vlan運作模式 不管有沒有塞入vlan,ethertype永遠不變,只會向後擠 還沒塞入vlan |dst-mac|src-mac|ethertype|payload 塞入vlan |dst-mac|src-mac|0x8100|tci|ethertype|payload """ priority = 2 idle_timeout = 5 _cookie = GLOBAL_VALUE.get_cookie() vlan_tag = 1 for path in route_path_list: vlan_tci = ofproto_v1_5.OFPVID_PRESENT + vlan_tag #spec openflow1.5.1 p.82,你要多加這個openvswitch才知道你要設定vlan_vid #path:[(1, 4), (1, None), (1, 2), (3, 1), (3, None), (3, 2), (4, 2), (4, None), (4, 3)] for index, i in enumerate(path[5::3]): set_datapath_id = i[0] set_out_port = i[1] tmp_datapath = GLOBAL_VALUE.G.nodes[(set_datapath_id, None)]["datapath"] ofp = tmp_datapath.ofproto parser = tmp_datapath.ofproto_parser if index == (len(path) / 3) - 2: #最後 #print("最後") match = parser.OFPMatch(eth_type=ether_types.ETH_TYPE_IP, vlan_vid=vlan_tci, ipv4_src=ipv4_src, ipv4_dst=ipv4_dst) action_set = [ parser.OFPActionPopVlan(), parser.OFPActionOutput(port=set_out_port) ] instruction = [ parser.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, action_set) ] mod = parser.OFPFlowMod(datapath=tmp_datapath, priority=priority, table_id=2, command=ofp.OFPFC_ADD, match=match, cookie=_cookie, instructions=instruction, idle_timeout=idle_timeout) OFPT_FLOW_MOD.send_ADD_FlowMod(mod) else: #中間 #print("中間") match = parser.OFPMatch(eth_type=ether_types.ETH_TYPE_IP, vlan_vid=vlan_tci, ipv4_dst=ipv4_dst, ipv4_src=ipv4_src) action = [parser.OFPActionOutput(port=set_out_port)] instruction = [ parser.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, action) ] mod = parser.OFPFlowMod(datapath=tmp_datapath, priority=priority, table_id=2, command=ofp.OFPFC_ADD, match=match, cookie=_cookie, instructions=instruction, idle_timeout=idle_timeout) OFPT_FLOW_MOD.send_ADD_FlowMod(mod) vlan_tag = vlan_tag + 1 #起始 port_list = [] vlan_tag = [] vlan_index = 1 for path in route_path_list: i = path[2] set_datapath_id = i[0] set_out_port = i[1] port_list.append(set_out_port) tmp_datapath = GLOBAL_VALUE.G.nodes[(set_datapath_id, None)]["datapath"] vlan_tag.append(vlan_index) vlan_index = vlan_index + 1 #保證剛才路徑設定完成 self.wait_finish_switch_BARRIER_finish(tmp_datapath) ofp = tmp_datapath.ofproto parser = tmp_datapath.ofproto_parser send_add_group_mod(self, tmp_datapath, port_list, weight_list, vlan_tag_list=vlan_tag, group_id=_cookie) #確保先前的所有設定已經完成,才能開始設定起點的flow table #如果不確保會導致封包已經開始傳送但是路徑尚未設定完成的錯誤 #保證group 設定完成 self.wait_finish_switch_BARRIER_finish(tmp_datapath) match = parser.OFPMatch(eth_type=ether_types.ETH_TYPE_IP, ipv4_dst=ipv4_dst, ipv4_src=ipv4_src) action = [parser.OFPActionGroup(group_id=_cookie)] instruction = [ parser.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, action) ] mod = parser.OFPFlowMod(datapath=tmp_datapath, priority=priority, table_id=2, command=ofp.OFPFC_ADD, match=match, cookie=_cookie, instructions=instruction, idle_timeout=idle_timeout) OFPT_FLOW_MOD.send_ADD_FlowMod(mod) #上面都完成就紀錄 GLOBAL_VALUE.PATH[ipv4_src][ipv4_dst]["cookie"][_cookie] = route_path_list
def _update_link(): print("_update_link") def clear_link_tmp_data(): for edge in list(GLOBAL_VALUE.G.edges()): edge_id1 = edge[0] edge_id2 = edge[1] if GLOBAL_VALUE.check_edge_is_link(edge_id1, edge_id2): # GLOBAL_VALUE.G[start_switch][end_switch]["port"][end_switch] link_data = GLOBAL_VALUE.G[edge_id1][edge_id2] link_data["tmp"] = nested_dict() def sent_opeld_to_all_port(): for node_id in GLOBAL_VALUE.G.copy().nodes: if GLOBAL_VALUE.check_node_is_switch(node_id): switch_data = GLOBAL_VALUE.G.nodes[node_id] # switch_data = switch_data.copy() for port_no in switch_data["port"].keys(): # if the port can working if switch_data["port"][port_no]["OFPMP_PORT_DESC"]["state"] == ofproto_v1_5.OFPPS_LIVE: hub.spawn(self.send_opeld_packet, switch_data["datapath"], port_no, extra_byte=self.monitor_each_opeld_extra_byte, num_packets=self.monitor_sent_opedl_packets) while True: hub.sleep(1) #計算reward self.reward_cal() # 統計鏈路 # NOTE start # print(GLOBAL_VALUE.G.nodes[datapath.id]["port"][1]["OFPMP_PORT_DESC"]["properties"],datapath.id) clear_link_tmp_data() # 重算所有統計 # sent opeld to all port of each switch for doing link detect and topology detect sent_opeld_to_all_port() # 發送封包 # wait for packet back hub.sleep(self.monitor_wait_opeld_back) # print(GLOBAL_VALUE.G[2][1]["tmp"]) # print(GLOBAL_VALUE.G[2][1]["port"]) for edge in list(GLOBAL_VALUE.G.edges()): edge_id1 = edge[0] edge_id2 = edge[1] if GLOBAL_VALUE.check_edge_is_link(edge_id1, edge_id2): start_switch = (edge_id1[0], None) start_port_number = edge_id1[1] end_switch = (edge_id2[0], None) end_port_number = edge_id2[1] # print(GLOBAL_VALUE.G[start_switch][end_switch]["tmp"]) packet_start_time = [] packet_end_time = [] seq_packet = GLOBAL_VALUE.G[edge_id1][edge_id2]["tmp"] get_packets_number = len(seq_packet.keys()) # latency for seq in seq_packet.keys(): packet_start_time.append( seq_packet[seq]["start"]) packet_end_time.append(seq_packet[seq]["end"]) latency = [] for s_t, e_t in zip(packet_start_time, packet_end_time): latency.append(e_t-s_t) #all_t = max(packet_end_time)-min(packet_start_time) # jitter if len(packet_end_time) != 0 or len(packet_start_time) != 0: curr_speed = min(int(GLOBAL_VALUE.G.nodes[start_switch]["port"][start_port_number]["OFPMP_PORT_DESC"]["properties"][0]["curr_speed"]), int( GLOBAL_VALUE.G.nodes[end_switch]["port"][end_port_number]["OFPMP_PORT_DESC"]["properties"][0]["curr_speed"])) # curr_speed kbps tx_bytes_delta = int( GLOBAL_VALUE.G.nodes[start_switch]["port"][start_port_number]["OFPMP_PORT_STATS"]["tx_bytes_delta"]) jitter = abs(np.std(latency) * 1000) # millisecond # A. S. Tanenbaum and D. J. Wetherall, Computer Networks, 5th ed. Upper Saddle River, NJ, USA: Prentice Hall Press, 2010. # "The variation (i.e., standard deviation) in the delay or packet arrival times is called jitter." # default one packet from A TO B latency millisecond(ms) delay_one_packet = abs(np.mean(latency)*1000) loss = 1-(get_packets_number / self.monitor_sent_opedl_packets) # bytes_s=(get_packets_number*(self.monitor_each_opeld_extra_byte+16))/all_t # available using bandwith bytes per second # curr_speed is kbps bw = ((1000*curr_speed)/8) - \ (tx_bytes_delta/self.delta) # print(start_switch,end_switch,link_index,"jitter",jitter,"delay_one_packet",delay_one_packet,"loss",loss,"gbytes_s",bw) # millisecond GLOBAL_VALUE.G[edge_id1][edge_id2]["detect"]["jitter_ms"] = jitter # %how many percent of the packet loss GLOBAL_VALUE.G[edge_id1][edge_id2]["detect"]["loss_percent"] = loss # available bandwidth GLOBAL_VALUE.G[edge_id1][edge_id2]["detect"]["bandwidth_bytes_per_s"] = bw # from a to b ,just one way ,millisecond GLOBAL_VALUE.G[edge_id1][edge_id2]["detect"]["latency_ms"] = delay_one_packet # 這裡你可以自創演算法去評估權重 GLOBAL_VALUE.G[edge_id1][edge_id2]["weight"] = delay_one_packet#formula.OSPF(bw*8) #GLOBAL_VALUE.G[edge_id1][edge_id2]["ppinin"] = delay_one_packet #GLOBAL_VALUE.G[edge_id1][edge_id2]["ppinin"]=0 #print(GLOBAL_VALUE.G[edge_id1][edge_id2]["weight"],"weight") GLOBAL_VALUE.G[edge_id1][edge_id2]["hop"] = 1 GLOBAL_VALUE.G[edge_id1][edge_id2]["low_delay"] = delay_one_packet GLOBAL_VALUE.G[edge_id1][edge_id2]["low_jitter"] = jitter GLOBAL_VALUE.G[edge_id1][edge_id2]["EIGRP"] = formula.EIGRP( (bw*8)/1000, curr_speed, tx_bytes_delta, delay_one_packet, loss) if self.set_weight_call_back_function != None: self.set_weight_call_back_function(GLOBAL_VALUE.G[edge_id1][edge_id2], jitter, loss, bw, delay_one_packet)