Experiment 7: SDN northbound application practice based on REST API
1. The purpose of the experiment
- Ability to write programs to call OpenDaylight REST API to implement specific network functions;
- Ability to write programs to call Ryu REST API to implement specific network functions.
2. Experimental environment
- Download the virtual machine software Oracle VisualBox or VMware;
- Install Ubuntu 20.04 Desktop amd64 in a virtual machine, and fully install Mininet, OpenDaylight (Carbon version), Postman and Ryu;
3. Experimental requirements
(1) Basic requirements
- Write a Python program to call the northbound interface of OpenDaylight to achieve the following functions
(1) Use the Mininet platform to build the network topology shown in the figure below, and connect to OpenDaylight;
(2) Issue an instruction to delete the flow table data on s1.
Create del.py file
Click to view codeimport requests from requests.auth import HTTPBasicAuth if __name__ == '__main__': url = 'http://127.0.0.1:8181/restconf/operational/opendaylight-inventory:nodes/node/openflow:1/' headers = {'Content-Type': 'application/json'} response = requests.delete(url=url, headers=headers, auth=HTTPBasicAuth('admin', 'admin')) print(response.content)
(3) Distribute the hard timeout flow table to realize the network interruption of hosts h1 and h3 in the topology for 20s.
timeout.pytimeout.json#!/usr/bin/python import requests from requests.auth import HTTPBasicAuth if __name__ == "__main__": url = 'http://127.0.0.1:8181/restconf/config/opendaylight-inventory:nodes/node/openflow:1/flow-node-inventory:table/0/flow/1' with open("./timeout.json") as file: str = file.read() headers = {'Content-Type': 'application/json'} res = requests.put(url, str, headers=headers, auth=HTTPBasicAuth('admin', 'admin')) print (res.content)
{ "flow": [ { "id": "1", "match": { "in-port": "1", "ethernet-match": { "ethernet-type": { "type": "0x0800" } }, "ipv4-destination": "10.0.0.3/32" }, "instructions": { "instruction": [ { "order": "0", "apply-actions": { "action": [ { "order": "0", "drop-action": {} } ] } } ] }, "flow-name": "flow", "priority": "65535", "hard-timeout": "20", "cookie": "2", "table_id": "0" } ] }
(4) Get the number of active flow tables on s1.
Create the file flows.py
Click to view code#!/usr/bin/python import requests from requests.auth import HTTPBasicAuth if __name__ == "__main__": url = 'http://127.0.0.1:8181/restconf/operational/opendaylight-inventory:nodes/node/openflow:1/flow-node-inventory:table/0/opendaylight-flow-table-statistics:flow-table-statistics' headers = {'Content-Type': 'application/json'} res = requests.get(url,headers=headers, auth=HTTPBasicAuth('admin', 'admin')) print (res.content)
- Write a Python program to call Ryu's northbound interface to achieve the following functions
(1) Implement the same hard timeout flow table delivery on the OpenDaylight experimental topology above.
ryu_timeout.json (click to view the codeimport requests from requests.auth import HTTPBasicAuth if __name__ == '__main__': url = 'http://127.0.0.1:8080/stats/flowentry/add' headers = {'Content-Type': 'application/json'} json = open('ryu_timeout.json').read() response = requests.post(url, data=json, headers=headers) print(response.content)
{ "dpid": 1, "cookie": 1, "cookie_mask": 1, "table_id": 0, "hard_timeout": 20, "priority": 65535, "flags": 1, "match":{ "in_port":1 }, "actions":[] }
(2) Refer to the documentation of Ryu REST API, based on the network topology of the VLAN experiment, program the same VLAN configuration.
Tip: Ryu needs to be connected after topology generation, and Ryu should be able to provide REST API services
VLAN_ID | Hosts |
---|---|
0 | h1 h3 |
1 | h2 h4 |
ryu_vlan.py (click to view the codefrom mininet.topo import Topo class MyTopo(Topo): def __init__(self): #initilaize topology Topo.__init__(self) #add hosts h1=self.addHost('h1') h2=self.addHost('h2') h3=self.addHost('h3') h4=self.addHost('h4') #add Switches s1=self.addSwitch('s1') s2=self.addSwitch('s2') #add Links self.addLink(h1,s1,1,1) self.addLink(h2,s1,1,2) self.addLink(h3,s2,1,1) self.addLink(h4,s2,1,2) self.addLink(s1,s2,3,3) topos = {'mytopo' : (lambda:MyTopo())}
ryu_vlan.json (click to view the codeimport json import requests if __name__ == '__main__': url = 'http://127.0.0.1:8080/stats/flowentry/add' headers = {'Content-Type': 'application/json'} f = open('ryu_vlan.json').read() flows = json.loads(f)['flows'] [requests.post(url, data=json.dumps(flows[i]), headers=headers) for i in range(0, 8)]
{ "flows": [ { "dpid": 1, "priority": 1, "match": { "in_port": 1 }, "actions": [{ "type": "PUSH_VLAN", "ethertype": 33024 }, { "type": "SET_FIELD", "field": "vlan_vid", "value": 4096 }, { "type": "OUTPUT", "port": 3 } ] }, { "dpid": 1, "priority": 1, "match": { "in_port": 2 }, "actions": [{ "type": "PUSH_VLAN", "ethertype": 33024 }, { "type": "SET_FIELD", "field": "vlan_vid", "value": 4097 }, { "type": "OUTPUT", "port": 3 } ] }, { "dpid": 1, "priority": 1, "match": { "vlan_vid": 0 }, "actions": [{ "type": "POP_VLAN", "ethertype": 33024 }, { "type": "OUTPUT", "port": 1 } ] }, { "dpid": 1, "priority": 1, "match": { "vlan_vid": 1 }, "actions": [{ "type": "POP_VLAN", "ethertype": 33024 }, { "type": "OUTPUT", "port": 2 } ] }, { "dpid": 2, "priority": 1, "match": { "in_port": 1 }, "actions": [{ "type": "PUSH_VLAN", "ethertype": 33024 }, { "type": "SET_FIELD", "field": "vlan_vid", "value": 4096 }, { "type": "OUTPUT", "port": 3 } ] }, { "dpid": 2, "priority": 1, "match": { "in_port": 2 }, "actions": [{ "type": "PUSH_VLAN", "ethertype": 33024 }, { "type": "SET_FIELD", "field": "vlan_vid", "value": 4097 }, { "type": "OUTPUT", "port": 3 } ] }, { "dpid": 2, "priority": 1, "match": { "vlan_vid": 0 }, "actions": [{ "type": "POP_VLAN", "ethertype": 33024 }, { "type": "OUTPUT", "port": 1 } ] }, { "dpid": 2, "priority": 1, "match": { "vlan_vid": 1 }, "actions": [{ "type": "POP_VLAN", "ethertype": 33024 }, { "type": "OUTPUT", "port": 2 } ] } ] }
(2) Advanced requirements
Choose either OpenDaylight or Ryu, and programmatically view the names of all nodes (including switches and hosts) in the pre-order VLAN experimental topology, and display all flow entries of each switch.
Click to view codeimport requests import time import re class GetNodes: def __init__(self, ip): self.ip = ip def get_switch_id(self): url = 'http://' + self.ip + '/stats/switches' re_switch_id = requests.get(url=url).json() switch_id_hex = [] for i in re_switch_id: switch_id_hex.append(hex(i)) return switch_id_hex def getflow(self): url = 'http://' + self.ip + '/stats/flow/%d' switch_list = self.get_switch_id() ret_flow = [] for switch in switch_list: new_url = format(url % int(switch, 16)) re_switch_flow = requests.get(url=new_url).json() ret_flow.append(re_switch_flow) return ret_flow def show(self): flow_list = self.getflow() for flow in flow_list: for dpid in flow.keys(): dp_id = dpid switchnum= '{1}'.format(hex(int(dp_id)), int(dp_id)) print('s'+switchnum,end = " ") switchnum = int(switchnum) for list_table in flow.values(): for table in list_table: string1 = str(table) if re.search("'dl_vlan': '(.*?)'", string1) is not None: num = re.search("'dl_vlan': '(.*?)'", string1).group(1); if num == '0' and switchnum == 1: print('h1',end = " ") if num == '1' and switchnum == 1: print('h2',end = " ") if num == '0' and switchnum == 2: print('h3',end = " ") if num == '1' and switchnum == 2: print('h4',end = " ") print("") flow_list = self.getflow() for flow in flow_list: for dpid in flow.keys(): dp_id = dpid print('switch_name:s{1}'.format(hex(int(dp_id)), int(dp_id))) for list_table in flow.values(): for table in list_table: print(table) s1 = GetNodes("127.0.0.1:8080") s1.show()
(3) Personal summary
- Most of this experiment needs to be implemented by code, which is quite difficult for me. With the help of constant review of materials and classmates, this experiment is completed.
- Through this experiment, I further learned the use of ODL REST API and Ryu REST API, and further learned the use of ODL and Ryu northbound interface.