# Copyright (c) 2011 - 2017, Intel Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""``restfloodlight.py``
`Functionality related to RestFloodlight OVS controller`
"""
import http.client
import json
[docs]class RestFloodlightController(object):
"""RestFloodlight OVS controller.
"""
[docs] def __init__(self, ip, port):
"""Initialize RestFloodlightController class.
Args:
ip(str): Controller IP address
port(int): Controller port
"""
self.controller_ip = ip
self.controller_port = port
self.reply = True
[docs] def probe(self, timeout=10):
"""Method for probing Floodlight Controller.
Args:
timeout(int): timeout
"""
path = '/wm/core/memory/json'
ret = self.rest_call({}, 'GET', path, timeout)
return json.loads(ret[2])
[docs] def get_dpid(self, timeout=10):
"""Method for getting Switch dpid from Floodlight Controller.
Args:
timeout(int): timeout
Returns:
str: Switch dpid
"""
path = '/wm/core/controller/switches/json'
ret = self.rest_call({}, 'GET', path, timeout)
return json.loads(ret[2])[0]['dpid']
[docs] def get_multiple_dpids(self, timeout=10):
"""Method for getting Switches dpids in complex setup via Floodlight Controller.
Args:
timeout(int): timeout
Returns:
list: Switches dpids
"""
path = '/wm/core/controller/switches/json'
ret = self.rest_call({}, 'GET', path, timeout)
return json.loads(ret[2])
[docs] def get_stats(self, switch_id, stat_type, timeout=30):
"""Method for getting OVS statistics from Switch via Floodlight Controller.
Args:
switch_id(str): Switch ID
stat_type(str): Statistics type
timeout(int): reply waiting timeout
Returns:
list: Switch statistics
"""
s_type = ''
if stat_type == "aggstats":
s_type = 'aggregate'
elif stat_type == "portstats":
s_type = 'port'
elif stat_type == "flowstats":
s_type = 'flow'
elif stat_type == "tablestats":
s_type = 'table'
elif stat_type == "queuestats":
s_type = 'queue'
elif stat_type == "descstats":
s_type = 'desc'
else:
s_type = stat_type
path = '/wm/core/switch/%s/%s/json' % (switch_id, s_type, )
ret = self.rest_call({}, 'GET', path, timeout)
return json.loads(ret[2])
[docs] def get_features(self, switch_id, command, reply=True, timeout=30):
"""Method for getting OVS Switch features via Floodlight Controller.
Args:
switch_id(str): Switch ID
command(str): command: "features_request"
reply(bool): wait for reply
timeout(int): reply waiting timeout
Returns:
list: Switch features statistics
"""
features = self.get_stats(switch_id, "features")
return features
[docs] def flow_add(self, switch_id, command_string, name, wildcards=None, priority=32768, reply=False, timeout=30):
"""Method for adding flows via Floodlight Controller.
Args:
switch_id(str): Switch ID
command_string(str): command string, e.g. flow qualifiers and actions, delimited with space
name(str): Flow name
wildcards(str): Flow wildcards
priority(int): Flow priority
reply(bool): specifies wait for reply or not
timeout(int): reply waiting timeout
Returns:
bool: True if flow added
"""
path = '/wm/staticflowentrypusher/json'
cmd = dict()
if not reply:
reply = self.reply
cmd["switch"] = switch_id
cmd["name"] = name
if wildcards:
cmd["wildcards"] = wildcards
for flow_field in command_string.split(" ")[0].split(","):
if "in_port" in flow_field.split("=")[0]:
cmd["ingress-port"] = flow_field.split("=")[1]
elif "dl_src" in flow_field.split("=")[0]:
cmd["src-mac"] = flow_field.split("=")[1]
elif "dl_dst" in flow_field.split("=")[0]:
cmd["dst-mac"] = flow_field.split("=")[1]
elif "dl_vlan" in flow_field.split("=")[0] and "dl_vlan_pcp" not in flow_field.split("=")[0]:
cmd["vlan-id"] = flow_field.split("=")[1]
elif "dl_vlan_pcp" in flow_field.split("=")[0]:
cmd["vlan-priority"] = flow_field.split("=")[1]
elif "dl_type" in flow_field.split("=")[0]:
cmd["ether-type"] = flow_field.split("=")[1]
elif "nw_tos" in flow_field.split("=")[0]:
cmd["tos-bits"] = flow_field.split("=")[1]
elif "nw_proto" in flow_field.split("=")[0]:
cmd["protocol"] = flow_field.split("=")[1]
elif "nw_src" in flow_field.split("=")[0]:
cmd["src-ip"] = flow_field.split("=")[1]
elif "nw_dst" in flow_field.split("=")[0]:
cmd["dst-ip"] = flow_field.split("=")[1]
elif "tp_src" in flow_field.split("=")[0]:
cmd["src-port"] = flow_field.split("=")[1]
elif "tp_dst" in flow_field.split("=")[0]:
cmd["dst-port"] = flow_field.split("=")[1]
elif "ip" in flow_field.split("=")[0] and "ipv6" not in flow_field.split("=")[0]:
cmd["ether-type"] = 2048
elif "icmp" in flow_field.split("=")[0] and "icmp6" not in flow_field.split("=")[0]:
cmd["ether-type"] = 2048
cmd["protocol"] = 1
elif "tcp" in flow_field.split("=")[0] and "tcp6" not in flow_field.split("=")[0]:
cmd["ether-type"] = 2048
cmd["protocol"] = 6
elif "udp" in flow_field.split("=")[0] and "udp6" not in flow_field.split("=")[0]:
cmd["ether-type"] = 2048
cmd["protocol"] = 17
elif "arp" in flow_field.split("=")[0]:
cmd["ether-type"] = 0x0806
elif "ipv6" in flow_field.split("=")[0]:
cmd["ether-type"] = 0x86dd
elif "tcp6" in flow_field.split("=")[0]:
cmd["ether-type"] = 34525
cmd["protocol"] = 6
elif "udp6" in flow_field.split("=")[0]:
cmd["ether-type"] = 34525
cmd["protocol"] = 17
elif "icmp6" in flow_field.split("=")[0]:
cmd["ether-type"] = 34525
cmd["protocol"] = 58
cmd["active"] = "true"
actions_list = []
for action_item in command_string.split(" ")[1].split(','):
if 'strip_vlan' in action_item:
actions_list.append('strip-vlan')
elif 'mod_vlan_vid' in action_item:
actions_list.append('setvlan-id=%s' % (action_item.split(':')[1], ))
elif 'mod_vlan_pcp' in action_item:
actions_list.append('set-vlan-priority=%s' % (action_item.split(':')[1], ))
elif 'mod_dl_src' in action_item:
actions_list.append('set-src-mac=%s' % (action_item[action_item.find(':') + 1:], ))
elif 'mod_dl_dst' in action_item:
actions_list.append('set-dst-mac=%s' % (action_item[action_item.find(':') + 1:], ))
elif 'mod_nw_src' in action_item:
actions_list.append('set-src-ip=%s' % (action_item.split(':')[1], ))
elif 'mod_nw_dst' in action_item:
actions_list.append('set-dst-ip=%s' % (action_item.split(':')[1], ))
elif 'mod_nw_tos' in action_item:
actions_list.append('set-tos-bits=%s' % (action_item.split(':')[1], ))
elif 'mod_tp_src' in action_item:
actions_list.append('set-src-port=%s' % (action_item.split(':')[1], ))
elif 'mod_tp_dst' in action_item:
actions_list.append('set-dst-port=%s' % (action_item.split(':')[1], ))
elif 'enqueue' in action_item:
actions_list.append('enqueue=%s:%s' % (action_item.split(':')[1], action_item.split(':')[2], ))
elif 'DROP' in action_item:
actions_list = []
elif 'IN_PORT' in action_item:
actions_list.append('output=ingress-port')
elif 'ALL' in action_item:
actions_list.append('output=all')
elif 'FLOOD' in action_item:
actions_list.append('output=flood')
elif 'CONTROLLER' in action_item:
actions_list.append('output=controller')
elif 'LOCAL' in action_item:
actions_list.append('output=local')
elif 'NORMAL' in action_item:
actions_list.append('output=normal')
else:
actions_list.append(action_item.replace(':', '=').lower())
cmd["actions"] = ','.join(actions_list)
cmd["priority"] = priority
if reply:
ret = self.rest_call(cmd, 'POST', path, timeout)
return ret[0] == 200
else:
self.rest_call(cmd, 'POST', path, timeout)
[docs] def flow_delete(self, command, name, timeout=30):
"""Method for deleting flows via Floodlight Controller.
Args:
command(str): command, e.g "flow_delete"
name(str): Flow name
timeout(int): reply waiting timeout
Returns:
bool: True if flow deleted
"""
path = '/wm/staticflowentrypusher/json'
if command == "flow_delete":
ret = self.rest_call({'name': name}, 'DELETE', path, timeout)
return ret[0] == 200
[docs] def clear(self, switch_id, timeout=10):
"""Method for clearing all flows from switch.
Args:
switch_id(str): Switch ID
timeout(int): reply waiting timeout
Returns:
bool: True if flows cleared
"""
path = '/wm/staticflowentrypusher/clear/%s/json' % (switch_id, )
ret = self.rest_call({}, "GET", path, timeout)
return ret[0] == 200
[docs] def rest_call(self, data, action, path, timeout=10):
"""Method for executing call via Floodlight REST API.
Args:
data(dict): Rest data
action(str): Action name
path(str): REST path
timeout(int): timeout
Returns:
tuple: response.status, response.reason, response.read()
"""
headers = {
'Content-type': 'application/json',
'Accept': 'application/json',
}
body = json.dumps(data)
conn = http.client.HTTPConnection(self.controller_ip, self.controller_port, timeout)
conn.request(action, path, body, headers)
response = conn.getresponse()
ret = (response.status, response.reason, response.read())
conn.close()
return ret