Add openflow-port-mapping CLI command
[osm/openvim.git] / ODL.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3
4 ##
5 # Copyright 2015 Telefónica Investigación y Desarrollo, S.A.U.
6 # This file is part of openvim
7 # All Rights Reserved.
8 #
9 # Licensed under the Apache License, Version 2.0 (the "License"); you may
10 # not use this file except in compliance with the License. You may obtain
11 # a copy of the License at
12 #
13 # http://www.apache.org/licenses/LICENSE-2.0
14 #
15 # Unless required by applicable law or agreed to in writing, software
16 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
17 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
18 # License for the specific language governing permissions and limitations
19 # under the License.
20 #
21 # For those usages not covered by the Apache License, Version 2.0 please
22 # contact with: nfvlabs@tid.es
23 ##
24
25 """
26 Implement the plugging for OpendayLight openflow controller
27 It creates the class OF_conn to create dataplane connections
28 with static rules based on packet destination MAC address
29 """
30
31 __author__="Pablo Montes, Alfonso Tierno"
32 __date__ ="$28-oct-2014 12:07:15$"
33
34
35 import json
36 import requests
37 import base64
38 import logging
39 import openflow_conn
40
41
42 class OF_conn(openflow_conn.OpenflowConn):
43 """OpenDayLight connector. No MAC learning is used"""
44
45 def __init__(self, params):
46 """ Constructor.
47 Params: dictionary with the following keys:
48 of_dpid: DPID to use for this controller
49 of_ip: controller IP address
50 of_port: controller TCP port
51 of_user: user credentials, can be missing or None
52 of_password: password credentials
53 of_debug: debug level for logging. Default to ERROR
54 other keys are ignored
55 Raise an exception if same parameter is missing or wrong
56 """
57
58 # check params
59 if "of_ip" not in params or params["of_ip"]==None or "of_port" not in params or params["of_port"]==None:
60 raise ValueError("IP address and port must be provided")
61
62 openflow_conn.OpenflowConn.__init__(self, params)
63 # internal variables
64 self.name = "OpenDayLight"
65 self.headers = {'content-type': 'application/json', 'Accept': 'application/json'}
66 self.auth=None
67 self.pp2ofi={} # From Physical Port to OpenFlow Index
68 self.ofi2pp={} # From OpenFlow Index to Physical Port
69
70 self.dpid = str(params["of_dpid"])
71 self.id = 'openflow:'+str(int(self.dpid.replace(':', ''), 16))
72 self.url = "http://%s:%s" %( str(params["of_ip"]), str(params["of_port"] ) )
73 if "of_user" in params and params["of_user"]!=None:
74 if not params.get("of_password"):
75 of_password=""
76 else:
77 of_password=str(params["of_password"])
78 self.auth = base64.b64encode(str(params["of_user"])+":"+of_password)
79 self.headers['Authorization'] = 'Basic '+self.auth
80
81 self.logger = logging.getLogger('vim.OF.ODL')
82 self.logger.setLevel( getattr(logging, params.get("of_debug", "ERROR")) )
83
84 def get_of_switches(self):
85 """
86 Obtain a a list of switches or DPID detected by this controller
87 :return: list length, and a list where each element a tuple pair (DPID, IP address)
88 Raise an OpenflowconnConnectionException exception if fails with text_error
89 """
90 try:
91 of_response = requests.get(self.url+"/restconf/operational/opendaylight-inventory:nodes",
92 headers=self.headers)
93 error_text = "Openflow response %d: %s" % (of_response.status_code, of_response.text)
94 if of_response.status_code != 200:
95 self.logger.warning("get_of_switches " + error_text)
96 raise openflow_conn.OpenflowconnUnexpectedResponse("Error get_of_switches " + error_text)
97
98 self.logger.debug("get_of_switches " + error_text)
99 info = of_response.json()
100
101 if type(info) != dict:
102 self.logger.error("get_of_switches. Unexpected response, not a dict: %s", str(info))
103 raise openflow_conn.OpenflowconnUnexpectedResponse("Unexpected response, not a dict. Wrong version?")
104
105 nodes = info.get('nodes')
106 if type(nodes) is not dict:
107 self.logger.error("get_of_switches. Unexpected response at 'nodes', not found or not a dict: %s", str(type(info)))
108 raise openflow_conn.OpenflowconnUnexpectedResponse("Unexpected response at 'nodes', not found or "
109 "not a dict. Wrong version?")
110
111 node_list = nodes.get('node')
112 if type(node_list) is not list:
113 self.logger.error("get_of_switches. Unexpected response, at 'nodes':'node', "
114 "not found or not a list: %s", str(type(node_list)))
115 raise openflow_conn.OpenflowconnUnexpectedResponse("Unexpected response, at 'nodes':'node', not found "
116 "or not a list. Wrong version?")
117
118 switch_list=[]
119 for node in node_list:
120 node_id = node.get('id')
121 if node_id is None:
122 self.logger.error("get_of_switches. Unexpected response at 'nodes':'node'[]:'id', not found: %s", str(node))
123 raise openflow_conn.OpenflowconnUnexpectedResponse("Unexpected response at 'nodes':'node'[]:'id', "
124 "not found . Wrong version?")
125
126 if node_id == 'controller-config':
127 continue
128
129 node_ip_address = node.get('flow-node-inventory:ip-address')
130 if node_ip_address is None:
131 self.logger.error("get_of_switches. Unexpected response at 'nodes':'node'[]:'flow-node-inventory:"
132 "ip-address', not found: %s", str(node))
133 raise openflow_conn.OpenflowconnUnexpectedResponse("Unexpected response at 'nodes':'node'[]:"
134 "'flow-node-inventory:ip-address', "
135 "not found. Wrong version?")
136
137 node_id_hex=hex(int(node_id.split(':')[1])).split('x')[1].zfill(16)
138 switch_list.append( (':'.join(a+b for a,b in zip(node_id_hex[::2], node_id_hex[1::2])), node_ip_address))
139
140 return len(switch_list), switch_list
141 except requests.exceptions.RequestException as e:
142 error_text = type(e).__name__ + ": " + str(e)
143 self.logger.error("get_of_switches " + error_text)
144 raise openflow_conn.OpenflowconnConnectionException(error_text)
145 except ValueError as e:
146 # ValueError in the case that JSON can not be decoded
147 error_text = type(e).__name__ + ": " + str(e)
148 self.logger.error("get_of_switches " + error_text)
149 raise openflow_conn.OpenflowconnUnexpectedResponse(error_text)
150
151 def obtain_port_correspondence(self):
152 """
153 Obtain the correspondence between physical and openflow port names
154 :return: dictionary: with physical name as key, openflow name as value,
155 Raise a OpenflowconnConnectionException expection in case of failure
156 """
157 try:
158 of_response = requests.get(self.url+"/restconf/operational/opendaylight-inventory:nodes",
159 headers=self.headers)
160 error_text = "Openflow response %d: %s" % (of_response.status_code, of_response.text)
161 if of_response.status_code != 200:
162 self.logger.warning("obtain_port_correspondence " + error_text)
163 raise openflow_conn.OpenflowconnUnexpectedResponse(error_text)
164 self.logger.debug("obtain_port_correspondence " + error_text)
165 info = of_response.json()
166
167 if type(info) != dict:
168 self.logger.error("obtain_port_correspondence. Unexpected response not a dict: %s", str(info))
169 raise openflow_conn.OpenflowconnUnexpectedResponse("Unexpected openflow response, not a dict. "
170 "Wrong version?")
171
172 nodes = info.get('nodes')
173 if type(nodes) is not dict:
174 self.logger.error("obtain_port_correspondence. Unexpected response at 'nodes', "
175 "not found or not a dict: %s", str(type(nodes)))
176 raise openflow_conn.OpenflowconnUnexpectedResponse("Unexpected response at 'nodes',not found or not a dict. Wrong version?")
177
178 node_list = nodes.get('node')
179 if type(node_list) is not list:
180 self.logger.error("obtain_port_correspondence. Unexpected response, at 'nodes':'node', "
181 "not found or not a list: %s", str(type(node_list)))
182 raise openflow_conn.OpenflowconnUnexpectedResponse("Unexpected response, at 'nodes':'node', "
183 "not found or not a list. Wrong version?")
184
185 for node in node_list:
186 node_id = node.get('id')
187 if node_id is None:
188 self.logger.error("obtain_port_correspondence. Unexpected response at 'nodes':'node'[]:'id', "
189 "not found: %s", str(node))
190 raise openflow_conn.OpenflowconnUnexpectedResponse("Unexpected response at 'nodes':'node'[]:'id', "
191 "not found . Wrong version?")
192
193 if node_id == 'controller-config':
194 continue
195
196 # Figure out if this is the appropriate switch. The 'id' is 'openflow:' plus the decimal value
197 # of the dpid
198 # In case this is not the desired switch, continue
199 if self.id != node_id:
200 continue
201
202 node_connector_list = node.get('node-connector')
203 if type(node_connector_list) is not list:
204 self.logger.error("obtain_port_correspondence. Unexpected response at "
205 "'nodes':'node'[]:'node-connector', not found or not a list: %s", str(node))
206 raise openflow_conn.OpenflowconnUnexpectedResponse("Unexpected response at 'nodes':'node'[]:"
207 "'node-connector', not found or not a list. "
208 "Wrong version?")
209
210 for node_connector in node_connector_list:
211 self.pp2ofi[ str(node_connector['flow-node-inventory:name']) ] = str(node_connector['id'] )
212 self.ofi2pp[ node_connector['id'] ] = str(node_connector['flow-node-inventory:name'])
213
214 node_ip_address = node.get('flow-node-inventory:ip-address')
215 if node_ip_address is None:
216 self.logger.error("obtain_port_correspondence. Unexpected response at 'nodes':'node'[]:"
217 "'flow-node-inventory:ip-address', not found: %s", str(node))
218 raise openflow_conn.OpenflowconnUnexpectedResponse("Unexpected response at 'nodes':'node'[]:"
219 "'flow-node-inventory:ip-address', not found. Wrong version?")
220 self.ip_address = node_ip_address
221
222 # If we found the appropriate dpid no need to continue in the for loop
223 break
224
225 # print self.name, ": obtain_port_correspondence ports:", self.pp2ofi
226 return self.pp2ofi
227 except requests.exceptions.RequestException as e:
228 error_text = type(e).__name__ + ": " + str(e)
229 self.logger.error("obtain_port_correspondence " + error_text)
230 raise openflow_conn.OpenflowconnConnectionException(error_text)
231 except ValueError as e:
232 # ValueError in the case that JSON can not be decoded
233 error_text = type(e).__name__ + ": " + str(e)
234 self.logger.error("obtain_port_correspondence " + error_text)
235 raise openflow_conn.OpenflowconnUnexpectedResponse(error_text)
236
237 def get_of_rules(self, translate_of_ports=True):
238 """
239 Obtain the rules inserted at openflow controller
240 :param translate_of_ports:
241 :return: dict if ok: with the rule name as key and value is another dictionary with the following content:
242 priority: rule priority
243 name: rule name (present also as the master dict key)
244 ingress_port: match input port of the rule
245 dst_mac: match destination mac address of the rule, can be missing or None if not apply
246 vlan_id: match vlan tag of the rule, can be missing or None if not apply
247 actions: list of actions, composed by a pair tuples:
248 (vlan, None/int): for stripping/setting a vlan tag
249 (out, port): send to this port
250 switch: DPID, all
251 Raise a OpenflowconnConnectionException expection in case of failure
252
253 """
254
255 try:
256 # get rules
257 if len(self.ofi2pp) == 0:
258 self.obtain_port_correspondence()
259
260 of_response = requests.get(self.url+"/restconf/config/opendaylight-inventory:nodes/node/" + self.id +
261 "/table/0", headers=self.headers)
262 error_text = "Openflow response %d: %s" % (of_response.status_code, of_response.text)
263
264 # The configured page does not exist if there are no rules installed. In that case we return an empty dict
265 if of_response.status_code == 404:
266 return {}
267
268 elif of_response.status_code != 200:
269 self.logger.warning("get_of_rules " + error_text)
270 raise openflow_conn.OpenflowconnUnexpectedResponse(error_text)
271
272 self.logger.debug("get_of_rules " + error_text)
273
274 info = of_response.json()
275
276 if type(info) != dict:
277 self.logger.error("get_of_rules. Unexpected response not a dict: %s", str(info))
278 raise openflow_conn.OpenflowconnUnexpectedResponse("Unexpected openflow response, not a dict. "
279 "Wrong version?")
280
281 table = info.get('flow-node-inventory:table')
282 if type(table) is not list:
283 self.logger.error("get_of_rules. Unexpected response at 'flow-node-inventory:table', "
284 "not a list: %s", str(type(table)))
285 raise openflow_conn.OpenflowconnUnexpectedResponse("Unexpected response at 'flow-node-inventory:table',"
286 " not a list. Wrong version?")
287
288 flow_list = table[0].get('flow')
289 if flow_list is None:
290 return {}
291
292 if type(flow_list) is not list:
293 self.logger.error("get_of_rules. Unexpected response at 'flow-node-inventory:table'[0]:'flow', not a list: %s", str(type(flow_list)))
294 raise openflow_conn.OpenflowconnUnexpectedResponse("Unexpected response at 'flow-node-inventory:"
295 "table'[0]:'flow', not a list. Wrong version?")
296
297 # TODO translate ports according to translate_of_ports parameter
298
299 rules = dict()
300 for flow in flow_list:
301 if not ('id' in flow and 'match' in flow and 'instructions' in flow and
302 'instruction' in flow['instructions'] and
303 'apply-actions' in flow['instructions']['instruction'][0] and
304 'action' in flow['instructions']['instruction'][0]['apply-actions']):
305 raise openflow_conn.OpenflowconnUnexpectedResponse("unexpected openflow response, one or more "
306 "elements are missing. Wrong version?")
307
308 flow['instructions']['instruction'][0]['apply-actions']['action']
309
310 rule = dict()
311 rule['switch'] = self.dpid
312 rule['priority'] = flow.get('priority')
313 # rule['name'] = flow['id']
314 # rule['cookie'] = flow['cookie']
315 if 'in-port' in flow['match']:
316 in_port = flow['match']['in-port']
317 if not in_port in self.ofi2pp:
318 raise openflow_conn.OpenflowconnUnexpectedResponse("Error: Ingress port " + in_port +
319 " is not in switch port list")
320
321 if translate_of_ports:
322 in_port = self.ofi2pp[in_port]
323
324 rule['ingress_port'] = in_port
325
326 if 'vlan-match' in flow['match'] and 'vlan-id' in flow['match']['vlan-match'] and \
327 'vlan-id' in flow['match']['vlan-match']['vlan-id'] and \
328 'vlan-id-present' in flow['match']['vlan-match']['vlan-id'] and \
329 flow['match']['vlan-match']['vlan-id']['vlan-id-present'] == True:
330 rule['vlan_id'] = flow['match']['vlan-match']['vlan-id']['vlan-id']
331
332 if 'ethernet-match' in flow['match'] and 'ethernet-destination' in flow['match']['ethernet-match'] and \
333 'address' in flow['match']['ethernet-match']['ethernet-destination']:
334 rule['dst_mac'] = flow['match']['ethernet-match']['ethernet-destination']['address']
335
336 instructions=flow['instructions']['instruction'][0]['apply-actions']['action']
337
338 max_index=0
339 for instruction in instructions:
340 if instruction['order'] > max_index:
341 max_index = instruction['order']
342
343 actions=[None]*(max_index+1)
344 for instruction in instructions:
345 if 'output-action' in instruction:
346 if not 'output-node-connector' in instruction['output-action']:
347 raise openflow_conn.OpenflowconnUnexpectedResponse("unexpected openflow response, one or "
348 "more elementa are missing. "
349 "Wrong version?")
350
351 out_port = instruction['output-action']['output-node-connector']
352 if not out_port in self.ofi2pp:
353 raise openflow_conn.OpenflowconnUnexpectedResponse("Error: Output port " + out_port +
354 " is not in switch port list")
355
356 if translate_of_ports:
357 out_port = self.ofi2pp[out_port]
358
359 actions[instruction['order']] = ('out',out_port)
360
361 elif 'strip-vlan-action' in instruction:
362 actions[instruction['order']] = ('vlan', None)
363
364 elif 'set-field' in instruction:
365 if not ('vlan-match' in instruction['set-field'] and 'vlan-id' in instruction['set-field']['vlan-match'] and 'vlan-id' in instruction['set-field']['vlan-match']['vlan-id']):
366 raise openflow_conn.OpenflowconnUnexpectedResponse("unexpected openflow response, one or "
367 "more elements are missing. "
368 "Wrong version?")
369
370 actions[instruction['order']] = ('vlan', instruction['set-field']['vlan-match']['vlan-id']['vlan-id'])
371
372 actions = [x for x in actions if x != None]
373
374 rule['actions'] = list(actions)
375 rules[flow['id']] = dict(rule)
376
377 #flow['id']
378 #flow['priority']
379 #flow['cookie']
380 #flow['match']['in-port']
381 #flow['match']['vlan-match']['vlan-id']['vlan-id']
382 # match -> in-port
383 # -> vlan-match -> vlan-id -> vlan-id
384 #flow['match']['vlan-match']['vlan-id']['vlan-id-present']
385 #TODO we asume that is not using rules with vlan-id-present:false
386 #instructions -> instruction -> apply-actions -> action
387 #instructions=flow['instructions']['instruction'][0]['apply-actions']['action']
388 #Es una lista. Posibles elementos:
389 #max_index=0
390 #for instruction in instructions:
391 # if instruction['order'] > max_index:
392 # max_index = instruction['order']
393 #actions=[None]*(max_index+1)
394 #for instruction in instructions:
395 # if 'output-action' in instruction:
396 # actions[instruction['order']] = ('out',instruction['output-action']['output-node-connector'])
397 # elif 'strip-vlan-action' in instruction:
398 # actions[instruction['order']] = ('vlan', None)
399 # elif 'set-field' in instruction:
400 # actions[instruction['order']] = ('vlan', instruction['set-field']['vlan-match']['vlan-id']['vlan-id'])
401 #
402 #actions = [x for x in actions if x != None]
403 # -> output-action -> output-node-connector
404 # -> pop-vlan-action
405 return rules
406 except requests.exceptions.RequestException as e:
407 error_text = type(e).__name__ + ": " + str(e)
408 self.logger.error("get_of_rules " + error_text)
409 raise openflow_conn.OpenflowconnConnectionException(error_text)
410 except ValueError as e:
411 # ValueError in the case that JSON can not be decoded
412 error_text = type(e).__name__ + ": " + str(e)
413 self.logger.error("get_of_rules " + error_text)
414 raise openflow_conn.OpenflowconnUnexpectedResponse(error_text)
415
416 def del_flow(self, flow_name):
417 """
418 Delete an existing rule
419 :param flow_name: flow_name, this is the rule name
420 :return: Raise a OpenflowconnConnectionException expection in case of failure
421 """
422
423 try:
424 of_response = requests.delete(self.url+"/restconf/config/opendaylight-inventory:nodes/node/" + self.id +
425 "/table/0/flow/"+flow_name, headers=self.headers)
426 error_text = "Openflow response %d: %s" % (of_response.status_code, of_response.text)
427 if of_response.status_code != 200:
428 self.logger.warning("del_flow " + error_text)
429 raise openflow_conn.OpenflowconnUnexpectedResponse(error_text)
430 self.logger.debug("del_flow OK " + error_text)
431 return None
432 except requests.exceptions.RequestException as e:
433 # raise an exception in case of contection error
434 error_text = type(e).__name__ + ": " + str(e)
435 self.logger.error("del_flow " + error_text)
436 raise openflow_conn.OpenflowconnConnectionException(error_text)
437
438 def new_flow(self, data):
439 """
440 Insert a new static rule
441 :param data: dictionary with the following content:
442 priority: rule priority
443 name: rule name
444 ingress_port: match input port of the rule
445 dst_mac: match destination mac address of the rule, missing or None if not apply
446 vlan_id: match vlan tag of the rule, missing or None if not apply
447 actions: list of actions, composed by a pair tuples with these posibilities:
448 ('vlan', None/int): for stripping/setting a vlan tag
449 ('out', port): send to this port
450 :return: Raise a OpenflowconnConnectionException expection in case of failure
451 """
452
453 try:
454
455 if len(self.pp2ofi) == 0:
456 self.obtain_port_correspondence()
457
458 # We have to build the data for the opendaylight call from the generic data
459 sdata = dict()
460 sdata['flow-node-inventory:flow'] = list()
461 sdata['flow-node-inventory:flow'].append(dict())
462 flow = sdata['flow-node-inventory:flow'][0]
463 flow['id'] = data['name']
464 flow['flow-name'] = data['name']
465 flow['idle-timeout'] = 0
466 flow['hard-timeout'] = 0
467 flow['table_id'] = 0
468 flow['priority'] = data.get('priority')
469 flow['match'] = dict()
470 if not data['ingress_port'] in self.pp2ofi:
471 error_text = 'Error. Port '+data['ingress_port']+' is not present in the switch'
472 self.logger.warning("new_flow " + error_text)
473 raise openflow_conn.OpenflowconnUnexpectedResponse(error_text)
474 flow['match']['in-port'] = self.pp2ofi[data['ingress_port']]
475 if 'dst_mac' in data:
476 flow['match']['ethernet-match'] = dict()
477 flow['match']['ethernet-match']['ethernet-destination'] = dict()
478 flow['match']['ethernet-match']['ethernet-destination']['address'] = data['dst_mac']
479 if data.get('vlan_id'):
480 flow['match']['vlan-match'] = dict()
481 flow['match']['vlan-match']['vlan-id'] = dict()
482 flow['match']['vlan-match']['vlan-id']['vlan-id-present'] = True
483 flow['match']['vlan-match']['vlan-id']['vlan-id'] = int(data['vlan_id'])
484 flow['instructions'] = dict()
485 flow['instructions']['instruction'] = list()
486 flow['instructions']['instruction'].append(dict())
487 flow['instructions']['instruction'][0]['order'] = 1
488 flow['instructions']['instruction'][0]['apply-actions'] = dict()
489 flow['instructions']['instruction'][0]['apply-actions']['action'] = list()
490 actions = flow['instructions']['instruction'][0]['apply-actions']['action']
491
492 order = 0
493 for action in data['actions']:
494 new_action = { 'order': order }
495 if action[0] == "vlan":
496 if action[1] == None:
497 # strip vlan
498 new_action['strip-vlan-action'] = dict()
499 else:
500 new_action['set-field'] = dict()
501 new_action['set-field']['vlan-match'] = dict()
502 new_action['set-field']['vlan-match']['vlan-id'] = dict()
503 new_action['set-field']['vlan-match']['vlan-id']['vlan-id-present'] = True
504 new_action['set-field']['vlan-match']['vlan-id']['vlan-id'] = int(action[1])
505 elif action[0] == 'out':
506 new_action['output-action'] = dict()
507 if not action[1] in self.pp2ofi:
508 error_msj = 'Port '+action[1]+' is not present in the switch'
509 raise openflow_conn.OpenflowconnUnexpectedResponse(error_msj)
510
511 new_action['output-action']['output-node-connector'] = self.pp2ofi[ action[1] ]
512 else:
513 error_msj = "Unknown item '%s' in action list" % action[0]
514 self.logger.error("new_flow " + error_msj)
515 raise openflow_conn.OpenflowconnUnexpectedResponse(error_msj)
516
517 actions.append(new_action)
518 order += 1
519
520 # print json.dumps(sdata)
521 of_response = requests.put(self.url+"/restconf/config/opendaylight-inventory:nodes/node/" + self.id +
522 "/table/0/flow/" + data['name'],
523 headers=self.headers, data=json.dumps(sdata) )
524 error_text = "Openflow response %d: %s" % (of_response.status_code, of_response.text)
525 if of_response.status_code != 200:
526 self.logger.warning("new_flow " + error_text)
527 raise openflow_conn.OpenflowconnUnexpectedResponse(error_text)
528 self.logger.debug("new_flow OK " + error_text)
529 return None
530
531 except requests.exceptions.RequestException as e:
532 # raise an exception in case of contection error
533 error_text = type(e).__name__ + ": " + str(e)
534 self.logger.error("new_flow " + error_text)
535 raise openflow_conn.OpenflowconnConnectionException(error_text)
536
537 def clear_all_flows(self):
538 """
539 Delete all existing rules
540 :return: Raise a OpenflowconnConnectionException expection in case of failure
541 """
542 try:
543 of_response = requests.delete(self.url+"/restconf/config/opendaylight-inventory:nodes/node/" + self.id +
544 "/table/0", headers=self.headers)
545 error_text = "Openflow response %d: %s" % (of_response.status_code, of_response.text)
546 if of_response.status_code != 200 and of_response.status_code != 404: #HTTP_Not_Found
547 self.logger.warning("clear_all_flows " + error_text)
548 raise openflow_conn.OpenflowconnUnexpectedResponse(error_text)
549 self.logger.debug("clear_all_flows OK " + error_text)
550 except requests.exceptions.RequestException as e:
551 error_text = type(e).__name__ + ": " + str(e)
552 self.logger.error("clear_all_flows " + error_text)
553 raise openflow_conn.OpenflowconnConnectionException(error_text)