-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathryu_client.py
More file actions
220 lines (176 loc) · 6.93 KB
/
ryu_client.py
File metadata and controls
220 lines (176 loc) · 6.93 KB
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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Abstraction for Ryu RESTful APIs
import httplib
import json
from flow_entry import FlowEntry, OutputAction
# Flow match and actions specified in body
ADD_FLOW_URL = '/stats/flowentry/add'
# Clear the flow table of a switch, specified by its dpid
DEL_ALL_FLOWS_URL = '/stats/flowentry/clear/%s'
# Flow match specified in body
DEL_FLOW_URL = '/stats/flowentry/delete'
# Query all switches connected to the controller
GET_SWITCHES = '/topology/switches'
# Query links in the topology (switch to switch)
GET_LINKS = '/topology/links'
# Query links (switch to switch) given a dpid
GET_SWITCH_LINKS = '/topology/switch/%s/links'
# Query ingress port of a given MAC address
GET_MAC_INGRESS = '/topology/mac/%s'
RYU_API_HOST = '127.0.0.1'
RYU_API_PORT = 8090
STATUS_SUCCESS = "Success!"
def setRyuEndpoint(ip, port):
assert type(ip) in (str, unicode)
assert type(port) is int
global RYU_API_HOST, RYU_API_PORT
RYU_API_HOST = ip
RYU_API_PORT = port
# Returns 0 if everything is okay, else a non-zero status
def _controllerAction(action, method, body=None):
conn = httplib.HTTPConnection(RYU_API_HOST, RYU_API_PORT)
url = action
#LOG.debug("SENDING DOWN TO CONTROLLER: url = %s ; body = %s" % (url, body))
conn.request(method, url, body)
res = conn.getresponse()
if res.status in (httplib.OK,
httplib.CREATED,
httplib.ACCEPTED,
httplib.NO_CONTENT):
res_body = res.read()
if res_body:
return res_body
else:
return STATUS_SUCCESS
#return res.status
raise httplib.HTTPException(
res, 'code %d reason %s' % (res.status, res.reason),
res.getheaders(), res.read())
# Returns a dictionary version of the flow match in a form Ryu can use
def _getMatchDict(flow):
assert isinstance(flow, FlowEntry)
match = {}
if flow.in_port:
match['in_port'] = flow.in_port
if flow.dl_src:
match['dl_src'] = flow.dl_src
if flow.dl_dst:
match['dl_dst'] = flow.dl_dst
if flow.dl_type:
match['dl_type'] = flow.dl_type
if flow.dl_vlan:
match['dl_vlan'] = flow.dl_vlan
if flow.dl_vlan_pcp:
match['dl_vlan_pcp'] = flow.dl_vlan_pcp
if flow.nw_src:
match['nw_src'] = flow.nw_src
if flow.nw_dst:
match['nw_dst'] = flow.nw_dst
if flow.nw_proto:
match['nw_proto'] = flow.nw_proto
if flow.nw_tos:
match['nw_tos'] = flow.nw_tos
if flow.tp_src:
match['tp_src'] = flow.tp_src
if flow.tp_dst:
match['tp_dst'] = flow.tp_dst
return match
# Insert a flow into a switch
def insertFlow(dpid, flowEntry):
if type(dpid) not in (str, unicode):
raise TypeError("Expecting dpid parameter to be a hexadecimal of type string")
assert isinstance(flowEntry, FlowEntry)
assert flowEntry.isAllWild() is False
assert flowEntry.validateMatch(),\
"If L4 fields are set, ensure protocol fields in L2-L3 are appropriately set"
actions = []
for act in flowEntry.getActions():
if isinstance(act, OutputAction):
actions.append({"type": "OUTPUT", "port": act.param})
else:
LOG.debug("%s ERROR: Unimplemented actions", self.__class__.__name__)
# Convert dpid to integer for ofctl_rest APIs
match = _getMatchDict(flowEntry)
body = {"dpid": int(dpid, 16), "cookie": 0, "actions": actions, "match": match}
if flowEntry.priority is not None:
body['priority'] = flowEntry.priority
#LOG.debug("ADDING FLOW dpid = %s, in_port = %s, src = %s, dst = %s, actions = %s",
# (dpid), flow.in_port, flow.dl_src, flow.dl_dst, actions)
status = _controllerAction(ADD_FLOW_URL, 'POST', json.dumps(body))
return status
# Delete flow(s) from a switch
def deleteFlow(dpid, flowEntry):
if type(dpid) not in (str, unicode):
raise TypeError("Expecting dpid parameter to be a hexadecimal of type string")
assert isinstance(flowEntry, FlowEntry)
assert flowEntry.validateMatch(),\
"If L4 fields are set, ensure protocol fields in L2-L3 are appropriately set"
# Convert dpid to integer for ofctl_rest APIs
dpid = int(dpid, 16)
if flowEntry.isAllWild() and not flowEntry.out_port:
# Delete all flows
#LOG.debug("Deleting all flows in switch %s", dpid)
status = _controllerAction(DEL_ALL_FLOWS_URL % dpid, 'DELETE')
else:
# Delete specific flows
match = _getMatchDict(flowEntry)
body = {"dpid": dpid, "match": match}
if flowEntry.out_port:
body["out_port"] = flowEntry.out_port
status = _controllerAction(DEL_FLOW_URL, 'POST', json.dumps(body))
return status
# Delete all flows from a switch
def deleteAllFlows(dpid):
return deleteFlow(dpid, FlowEntry())
# Query all switches connected to the controller
def listSwitches():
ret = json.loads(_controllerAction(GET_SWITCHES, 'GET'))
# Un-unicode the strings to avoid confusion
for idx, dpid in enumerate(ret):
ret[idx] = str(dpid)
return {"dpids": ret}
# Query links in the topology (switch to switch)
# Return format
# - e.g. { "links": [ { "endpoint1": {"dpid": dpid1, "port": port_no},
# "endpoint2": {"dpid": dpid2, "port": port_no}
# },
# ...
# ]
# }
def listLinks():
ret_links = {"links": []}
links = json.loads(_controllerAction(GET_LINKS, 'GET'))
links = links["items"]
for link in links:
endpoint1 = {"dpid": str(link["dp1"]), "port": link["port1"]}
endpoint2 = {"dpid": str(link["dp2"]), "port": link["port2"]}
ret_links["links"].append( {"endpoint1": endpoint1, "endpoint2": endpoint2} )
return ret_links
# Query links (switch to switch) given a dpid
# Same return format as listLinks()
def listSwitchLinks(dpid):
if type(dpid) not in (str, unicode):
raise TypeError("Expecting dpid parameter to be a hexadecimal of type string")
ret_links = {"links": []}
links = json.loads(_controllerAction(GET_SWITCH_LINKS % dpid, 'GET'))
links = links["items"]
for link in links:
endpoint1 = {"dpid": str(link["dp1"]), "port": link["port1"]}
endpoint2 = {"dpid": str(link["dp2"]), "port": link["port2"]}
ret_links["links"].append( {"endpoint1": endpoint1, "endpoint2": endpoint2} )
return ret_links
# Query ingress port of a given MAC address
# Returns None if nothing found
def getMacIngressPort(mac):
if type(mac) not in (str, unicode):
raise TypeError("MAC address should be a colon-delimited hex string")
ret_port = json.loads(_controllerAction(GET_MAC_INGRESS % mac, 'GET'))
# Un-unicode the keys and values to avoid confusion
if ret_port:
port = {}
for key, val in ret_port.items():
port[str(key)] = str(val)
else:
port = None
return port