b7dcd40837
ofctl_v1_5 can't send role request messages, because of lack of arguments. This patch fixes it by adding the argument for short_id. ofctl_v1_5 sets None to short_id. It means ofctl_v1_5 uses the default value of short_id. Signed-off-by: Satoshi Fujimoto <satoshi.fujimoto7@gmail.com> Signed-off-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
1116 lines
38 KiB
Python
1116 lines
38 KiB
Python
# Copyright (C) 2016 Nippon Telegraph and Telephone 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.
|
|
|
|
import base64
|
|
import logging
|
|
|
|
from ryu.ofproto import ether
|
|
from ryu.ofproto import ofproto_v1_5
|
|
from ryu.ofproto import ofproto_v1_5_parser
|
|
from ryu.lib import ofctl_utils
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
DEFAULT_TIMEOUT = 1.0
|
|
|
|
UTIL = ofctl_utils.OFCtlUtil(ofproto_v1_5)
|
|
str_to_int = ofctl_utils.str_to_int
|
|
|
|
|
|
def to_action(dp, dic):
|
|
ofp = dp.ofproto
|
|
parser = dp.ofproto_parser
|
|
action_type = dic.get('type')
|
|
return ofctl_utils.to_action(dic, ofp, parser, action_type, UTIL)
|
|
|
|
|
|
def _get_actions(dp, dics):
|
|
actions = []
|
|
for d in dics:
|
|
action = to_action(dp, d)
|
|
if action is not None:
|
|
actions.append(action)
|
|
else:
|
|
LOG.error('Unknown action type: %s', d)
|
|
return actions
|
|
|
|
|
|
def to_instructions(dp, insts):
|
|
instructions = []
|
|
ofp = dp.ofproto
|
|
parser = dp.ofproto_parser
|
|
|
|
for i in insts:
|
|
inst_type = i.get('type')
|
|
if inst_type in ['APPLY_ACTIONS', 'WRITE_ACTIONS']:
|
|
dics = i.get('actions', [])
|
|
actions = _get_actions(dp, dics)
|
|
if actions:
|
|
if inst_type == 'APPLY_ACTIONS':
|
|
instructions.append(
|
|
parser.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS,
|
|
actions))
|
|
else:
|
|
instructions.append(
|
|
parser.OFPInstructionActions(ofp.OFPIT_WRITE_ACTIONS,
|
|
actions))
|
|
elif inst_type == 'CLEAR_ACTIONS':
|
|
instructions.append(
|
|
parser.OFPInstructionActions(ofp.OFPIT_CLEAR_ACTIONS, []))
|
|
elif inst_type == 'GOTO_TABLE':
|
|
table_id = str_to_int(i.get('table_id'))
|
|
instructions.append(parser.OFPInstructionGotoTable(table_id))
|
|
elif inst_type == 'WRITE_METADATA':
|
|
metadata = str_to_int(i.get('metadata'))
|
|
metadata_mask = (str_to_int(i['metadata_mask'])
|
|
if 'metadata_mask' in i
|
|
else parser.UINT64_MAX)
|
|
instructions.append(
|
|
parser.OFPInstructionWriteMetadata(
|
|
metadata, metadata_mask))
|
|
else:
|
|
LOG.error('Unknown instruction type: %s', inst_type)
|
|
|
|
return instructions
|
|
|
|
|
|
def action_to_str(act):
|
|
s = act.to_jsondict()[act.__class__.__name__]
|
|
t = UTIL.ofp_action_type_to_user(s['type'])
|
|
s['type'] = t if t != s['type'] else 'UNKNOWN'
|
|
|
|
if t == 'SET_FIELD':
|
|
field = s.pop('field')
|
|
s['field'] = field['OXMTlv']['field']
|
|
s['mask'] = field['OXMTlv']['mask']
|
|
s['value'] = field['OXMTlv']['value']
|
|
elif t == 'COPY_FIELD':
|
|
oxm_ids = s.pop('oxm_ids')
|
|
s['src_oxm_id'] = oxm_ids[0]['OFPOxmId']['type']
|
|
s['dst_oxm_id'] = oxm_ids[1]['OFPOxmId']['type']
|
|
|
|
return s
|
|
|
|
|
|
def instructions_to_str(instructions):
|
|
|
|
s = []
|
|
|
|
for i in instructions:
|
|
v = i.to_jsondict()[i.__class__.__name__]
|
|
t = UTIL.ofp_instruction_type_to_user(v['type'])
|
|
inst_type = t if t != v['type'] else 'UNKNOWN'
|
|
# apply/write/clear-action instruction
|
|
if isinstance(i, ofproto_v1_5_parser.OFPInstructionActions):
|
|
acts = []
|
|
for a in i.actions:
|
|
acts.append(action_to_str(a))
|
|
v['type'] = inst_type
|
|
v['actions'] = acts
|
|
s.append(v)
|
|
# others
|
|
else:
|
|
v['type'] = inst_type
|
|
s.append(v)
|
|
|
|
return s
|
|
|
|
|
|
def to_match(dp, attrs):
|
|
convert = {'in_port': UTIL.ofp_port_from_user,
|
|
'in_phy_port': str_to_int,
|
|
'metadata': ofctl_utils.to_match_masked_int,
|
|
'eth_dst': ofctl_utils.to_match_eth,
|
|
'eth_src': ofctl_utils.to_match_eth,
|
|
'eth_type': str_to_int,
|
|
'vlan_vid': to_match_vid,
|
|
'vlan_pcp': str_to_int,
|
|
'ip_dscp': str_to_int,
|
|
'ip_ecn': str_to_int,
|
|
'ip_proto': str_to_int,
|
|
'ipv4_src': ofctl_utils.to_match_ip,
|
|
'ipv4_dst': ofctl_utils.to_match_ip,
|
|
'tcp_src': str_to_int,
|
|
'tcp_dst': str_to_int,
|
|
'udp_src': str_to_int,
|
|
'udp_dst': str_to_int,
|
|
'sctp_src': str_to_int,
|
|
'sctp_dst': str_to_int,
|
|
'icmpv4_type': str_to_int,
|
|
'icmpv4_code': str_to_int,
|
|
'arp_op': str_to_int,
|
|
'arp_spa': ofctl_utils.to_match_ip,
|
|
'arp_tpa': ofctl_utils.to_match_ip,
|
|
'arp_sha': ofctl_utils.to_match_eth,
|
|
'arp_tha': ofctl_utils.to_match_eth,
|
|
'ipv6_src': ofctl_utils.to_match_ip,
|
|
'ipv6_dst': ofctl_utils.to_match_ip,
|
|
'ipv6_flabel': str_to_int,
|
|
'icmpv6_type': str_to_int,
|
|
'icmpv6_code': str_to_int,
|
|
'ipv6_nd_target': ofctl_utils.to_match_ip,
|
|
'ipv6_nd_sll': ofctl_utils.to_match_eth,
|
|
'ipv6_nd_tll': ofctl_utils.to_match_eth,
|
|
'mpls_label': str_to_int,
|
|
'mpls_tc': str_to_int,
|
|
'mpls_bos': str_to_int,
|
|
'pbb_isid': ofctl_utils.to_match_masked_int,
|
|
'tunnel_id': ofctl_utils.to_match_masked_int,
|
|
'ipv6_exthdr': ofctl_utils.to_match_masked_int,
|
|
'pbb_uca': str_to_int,
|
|
'tcp_flags': str_to_int,
|
|
'actset_output': str_to_int,
|
|
'packet_type': ofctl_utils.to_match_packet_type}
|
|
|
|
keys = {'dl_dst': 'eth_dst',
|
|
'dl_src': 'eth_src',
|
|
'dl_type': 'eth_type',
|
|
'dl_vlan': 'vlan_vid',
|
|
'nw_src': 'ipv4_src',
|
|
'nw_dst': 'ipv4_dst',
|
|
'nw_proto': 'ip_proto'}
|
|
|
|
if attrs.get('eth_type') == ether.ETH_TYPE_ARP:
|
|
if 'ipv4_src' in attrs and 'arp_spa' not in attrs:
|
|
attrs['arp_spa'] = attrs['ipv4_src']
|
|
del attrs['ipv4_src']
|
|
if 'ipv4_dst' in attrs and 'arp_tpa' not in attrs:
|
|
attrs['arp_tpa'] = attrs['ipv4_dst']
|
|
del attrs['ipv4_dst']
|
|
|
|
kwargs = {}
|
|
for key, value in attrs.items():
|
|
if key in keys:
|
|
# For old field name
|
|
key = keys[key]
|
|
if key in convert:
|
|
value = convert[key](value)
|
|
kwargs[key] = value
|
|
else:
|
|
LOG.error('Unknown match field: %s', key)
|
|
|
|
return dp.ofproto_parser.OFPMatch(**kwargs)
|
|
|
|
|
|
def to_match_vid(value):
|
|
return ofctl_utils.to_match_vid(value, ofproto_v1_5.OFPVID_PRESENT)
|
|
|
|
|
|
def match_to_str(ofmatch):
|
|
match = {}
|
|
|
|
ofmatch = ofmatch.to_jsondict()['OFPMatch']
|
|
ofmatch = ofmatch['oxm_fields']
|
|
|
|
for match_field in ofmatch:
|
|
key = match_field['OXMTlv']['field']
|
|
mask = match_field['OXMTlv']['mask']
|
|
value = match_field['OXMTlv']['value']
|
|
if key == 'vlan_vid':
|
|
value = match_vid_to_str(value, mask)
|
|
elif key == 'in_port':
|
|
value = UTIL.ofp_port_to_user(value)
|
|
elif key == 'packet_type':
|
|
value = [value >> 16, value & 0xffff]
|
|
else:
|
|
if mask is not None:
|
|
value = str(value) + '/' + str(mask)
|
|
match.setdefault(key, value)
|
|
|
|
return match
|
|
|
|
|
|
def match_vid_to_str(value, mask):
|
|
return ofctl_utils.match_vid_to_str(
|
|
value, mask, ofproto_v1_5.OFPVID_PRESENT)
|
|
|
|
|
|
def wrap_dpid_dict(dp, value, to_user=True):
|
|
if to_user:
|
|
return {str(dp.id): value}
|
|
|
|
return {dp.id: value}
|
|
|
|
|
|
def stats_to_str(ofstats):
|
|
|
|
stats = {}
|
|
ofstats = ofstats.to_jsondict()['OFPStats']
|
|
ofstats = ofstats['oxs_fields']
|
|
|
|
for s in ofstats:
|
|
key = s['OXSTlv']['field']
|
|
if key == 'duration':
|
|
value = {
|
|
'duration_sec': s['OXSTlv']['value'][0],
|
|
'duration_nsec': s['OXSTlv']['value'][1],
|
|
}
|
|
elif key == 'idle_time':
|
|
value = {
|
|
'idle_time_sec': s['OXSTlv']['value'][0],
|
|
'idle_time_nsec': s['OXSTlv']['value'][1],
|
|
}
|
|
else:
|
|
value = s['OXSTlv']['value']
|
|
stats.setdefault(key, value)
|
|
|
|
return stats
|
|
|
|
|
|
def get_desc_stats(dp, waiters, to_user=True):
|
|
stats = dp.ofproto_parser.OFPDescStatsRequest(dp, 0)
|
|
msgs = []
|
|
ofctl_utils.send_stats_request(dp, stats, waiters, msgs, LOG)
|
|
s = {}
|
|
|
|
for msg in msgs:
|
|
stats = msg.body
|
|
s = stats.to_jsondict()[stats.__class__.__name__]
|
|
|
|
return wrap_dpid_dict(dp, s, to_user)
|
|
|
|
|
|
def get_queue_stats(dp, waiters, port_no=None, queue_id=None, to_user=True):
|
|
if port_no is None:
|
|
port_no = dp.ofproto.OFPP_ANY
|
|
else:
|
|
port_no = UTIL.ofp_port_from_user(port_no)
|
|
if queue_id is None:
|
|
queue_id = dp.ofproto.OFPQ_ALL
|
|
else:
|
|
queue_id = UTIL.ofp_queue_from_user(queue_id)
|
|
|
|
stats = dp.ofproto_parser.OFPQueueStatsRequest(
|
|
dp, 0, port_no, queue_id)
|
|
msgs = []
|
|
ofctl_utils.send_stats_request(dp, stats, waiters, msgs, LOG)
|
|
|
|
desc = []
|
|
for msg in msgs:
|
|
stats = msg.body
|
|
for stat in stats:
|
|
s = stat.to_jsondict()[stat.__class__.__name__]
|
|
properties = []
|
|
for prop in stat.properties:
|
|
p = prop.to_jsondict()[prop.__class__.__name__]
|
|
if to_user:
|
|
t = UTIL.ofp_queue_stats_prop_type_to_user(prop.type)
|
|
p['type'] = t if t != p['type'] else 'UNKNOWN'
|
|
properties.append(p)
|
|
s['properties'] = properties
|
|
desc.append(s)
|
|
|
|
return wrap_dpid_dict(dp, desc, to_user)
|
|
|
|
|
|
def get_queue_desc(dp, waiters, port_no=None, queue_id=None, to_user=True):
|
|
if port_no is None:
|
|
port_no = dp.ofproto.OFPP_ANY
|
|
else:
|
|
port_no = UTIL.ofp_port_from_user(port_no)
|
|
if queue_id is None:
|
|
queue_id = dp.ofproto.OFPQ_ALL
|
|
else:
|
|
queue_id = UTIL.ofp_queue_from_user(queue_id)
|
|
|
|
stats = dp.ofproto_parser.OFPQueueDescStatsRequest(
|
|
dp, 0, port_no, queue_id)
|
|
msgs = []
|
|
ofctl_utils.send_stats_request(dp, stats, waiters, msgs, LOG)
|
|
|
|
configs = []
|
|
for msg in msgs:
|
|
for queue in msg.body:
|
|
q = queue.to_jsondict()[queue.__class__.__name__]
|
|
prop_list = []
|
|
for prop in queue.properties:
|
|
p = prop.to_jsondict()[prop.__class__.__name__]
|
|
if to_user:
|
|
t = UTIL.ofp_queue_desc_prop_type_to_user(prop.type)
|
|
p['type'] = t if t != prop.type else 'UNKNOWN'
|
|
prop_list.append(p)
|
|
q['properties'] = prop_list
|
|
configs.append(q)
|
|
|
|
return wrap_dpid_dict(dp, configs, to_user)
|
|
|
|
|
|
def get_flow_desc_stats(dp, waiters, flow=None, to_user=True):
|
|
flow = flow if flow else {}
|
|
table_id = UTIL.ofp_table_from_user(
|
|
flow.get('table_id', dp.ofproto.OFPTT_ALL))
|
|
flags = str_to_int(flow.get('flags', 0))
|
|
out_port = UTIL.ofp_port_from_user(
|
|
flow.get('out_port', dp.ofproto.OFPP_ANY))
|
|
out_group = UTIL.ofp_group_from_user(
|
|
flow.get('out_group', dp.ofproto.OFPG_ANY))
|
|
cookie = str_to_int(flow.get('cookie', 0))
|
|
cookie_mask = str_to_int(flow.get('cookie_mask', 0))
|
|
match = to_match(dp, flow.get('match', {}))
|
|
# Note: OpenFlow does not allow to filter flow entries by priority,
|
|
# but for efficiency, ofctl provides the way to do it.
|
|
priority = str_to_int(flow.get('priority', -1))
|
|
|
|
stats = dp.ofproto_parser.OFPFlowDescStatsRequest(
|
|
dp, flags, table_id, out_port, out_group, cookie, cookie_mask,
|
|
match)
|
|
|
|
msgs = []
|
|
ofctl_utils.send_stats_request(dp, stats, waiters, msgs, LOG)
|
|
|
|
flows = []
|
|
for msg in msgs:
|
|
for stats in msg.body:
|
|
if 0 <= priority != stats.priority:
|
|
continue
|
|
|
|
s = stats.to_jsondict()[stats.__class__.__name__]
|
|
s['instructions'] = instructions_to_str(stats.instructions)
|
|
s['stats'] = stats_to_str(stats.stats)
|
|
s['match'] = match_to_str(stats.match)
|
|
flows.append(s)
|
|
|
|
return wrap_dpid_dict(dp, flows, to_user)
|
|
|
|
|
|
def get_flow_stats(dp, waiters, flow=None, to_user=True):
|
|
flow = flow if flow else {}
|
|
table_id = UTIL.ofp_table_from_user(
|
|
flow.get('table_id', dp.ofproto.OFPTT_ALL))
|
|
flags = str_to_int(flow.get('flags', 0))
|
|
out_port = UTIL.ofp_port_from_user(
|
|
flow.get('out_port', dp.ofproto.OFPP_ANY))
|
|
out_group = UTIL.ofp_group_from_user(
|
|
flow.get('out_group', dp.ofproto.OFPG_ANY))
|
|
cookie = str_to_int(flow.get('cookie', 0))
|
|
cookie_mask = str_to_int(flow.get('cookie_mask', 0))
|
|
match = to_match(dp, flow.get('match', {}))
|
|
# Note: OpenFlow does not allow to filter flow entries by priority,
|
|
# but for efficiency, ofctl provides the way to do it.
|
|
priority = str_to_int(flow.get('priority', -1))
|
|
|
|
stats = dp.ofproto_parser.OFPFlowStatsRequest(
|
|
dp, flags, table_id, out_port, out_group, cookie, cookie_mask,
|
|
match)
|
|
|
|
msgs = []
|
|
ofctl_utils.send_stats_request(dp, stats, waiters, msgs, LOG)
|
|
|
|
flows = []
|
|
for msg in msgs:
|
|
for stats in msg.body:
|
|
if 0 <= priority != stats.priority:
|
|
continue
|
|
|
|
s = stats.to_jsondict()[stats.__class__.__name__]
|
|
s['stats'] = stats_to_str(stats.stats)
|
|
s['match'] = match_to_str(stats.match)
|
|
flows.append(s)
|
|
|
|
return wrap_dpid_dict(dp, flows, to_user)
|
|
|
|
|
|
def get_aggregate_flow_stats(dp, waiters, flow=None, to_user=True):
|
|
flow = flow if flow else {}
|
|
table_id = UTIL.ofp_table_from_user(
|
|
flow.get('table_id', dp.ofproto.OFPTT_ALL))
|
|
flags = str_to_int(flow.get('flags', 0))
|
|
out_port = UTIL.ofp_port_from_user(
|
|
flow.get('out_port', dp.ofproto.OFPP_ANY))
|
|
out_group = UTIL.ofp_group_from_user(
|
|
flow.get('out_group', dp.ofproto.OFPG_ANY))
|
|
cookie = str_to_int(flow.get('cookie', 0))
|
|
cookie_mask = str_to_int(flow.get('cookie_mask', 0))
|
|
match = to_match(dp, flow.get('match', {}))
|
|
|
|
stats = dp.ofproto_parser.OFPAggregateStatsRequest(
|
|
dp, flags, table_id, out_port, out_group, cookie, cookie_mask,
|
|
match)
|
|
|
|
msgs = []
|
|
ofctl_utils.send_stats_request(dp, stats, waiters, msgs, LOG)
|
|
|
|
flows = []
|
|
for msg in msgs:
|
|
stats = msg.body
|
|
s = stats.to_jsondict()[stats.__class__.__name__]
|
|
s['stats'] = stats_to_str(stats.stats)
|
|
flows.append(s)
|
|
|
|
return wrap_dpid_dict(dp, flows, to_user)
|
|
|
|
|
|
def get_table_stats(dp, waiters, to_user=True):
|
|
stats = dp.ofproto_parser.OFPTableStatsRequest(dp, 0)
|
|
msgs = []
|
|
ofctl_utils.send_stats_request(dp, stats, waiters, msgs, LOG)
|
|
|
|
tables = []
|
|
for msg in msgs:
|
|
stats = msg.body
|
|
for stat in stats:
|
|
s = stat.to_jsondict()[stat.__class__.__name__]
|
|
|
|
if to_user:
|
|
s['table_id'] = UTIL.ofp_table_to_user(stat.table_id)
|
|
|
|
tables.append(s)
|
|
|
|
return wrap_dpid_dict(dp, tables, to_user)
|
|
|
|
|
|
def get_table_features(dp, waiters, to_user=True):
|
|
stats = dp.ofproto_parser.OFPTableFeaturesStatsRequest(dp, 0, [])
|
|
msgs = []
|
|
ofproto = dp.ofproto
|
|
ofctl_utils.send_stats_request(dp, stats, waiters, msgs, LOG)
|
|
|
|
p_type_instructions = [ofproto.OFPTFPT_INSTRUCTIONS,
|
|
ofproto.OFPTFPT_INSTRUCTIONS_MISS]
|
|
|
|
p_type_next_tables = [ofproto.OFPTFPT_NEXT_TABLES,
|
|
ofproto.OFPTFPT_NEXT_TABLES_MISS,
|
|
ofproto.OFPTFPT_TABLE_SYNC_FROM]
|
|
|
|
p_type_actions = [ofproto.OFPTFPT_WRITE_ACTIONS,
|
|
ofproto.OFPTFPT_WRITE_ACTIONS_MISS,
|
|
ofproto.OFPTFPT_APPLY_ACTIONS,
|
|
ofproto.OFPTFPT_APPLY_ACTIONS_MISS]
|
|
|
|
p_type_packet = ofproto.OFPTFPT_PACKET_TYPES
|
|
|
|
p_type_oxms = [ofproto.OFPTFPT_MATCH,
|
|
ofproto.OFPTFPT_WILDCARDS,
|
|
ofproto.OFPTFPT_WRITE_SETFIELD,
|
|
ofproto.OFPTFPT_WRITE_SETFIELD_MISS,
|
|
ofproto.OFPTFPT_APPLY_SETFIELD,
|
|
ofproto.OFPTFPT_APPLY_SETFIELD_MISS,
|
|
ofproto.OFPTFPT_WRITE_COPYFIELD,
|
|
ofproto.OFPTFPT_WRITE_COPYFIELD_MISS,
|
|
ofproto.OFPTFPT_APPLY_COPYFIELD,
|
|
ofproto.OFPTFPT_APPLY_COPYFIELD_MISS]
|
|
|
|
p_type_experimenter = [ofproto.OFPTFPT_EXPERIMENTER,
|
|
ofproto.OFPTFPT_EXPERIMENTER_MISS]
|
|
|
|
tables = []
|
|
for msg in msgs:
|
|
stats = msg.body
|
|
for stat in stats:
|
|
s = stat.to_jsondict()[stat.__class__.__name__]
|
|
properties = []
|
|
for prop in stat.properties:
|
|
p = {}
|
|
t = UTIL.ofp_table_feature_prop_type_to_user(prop.type)
|
|
p['type'] = t if t != prop.type else 'UNKNOWN'
|
|
if prop.type in p_type_instructions:
|
|
instruction_ids = []
|
|
for i in prop.instruction_ids:
|
|
inst = {'len': i.len,
|
|
'type': i.type}
|
|
instruction_ids.append(inst)
|
|
p['instruction_ids'] = instruction_ids
|
|
elif prop.type in p_type_next_tables:
|
|
table_ids = []
|
|
for i in prop.table_ids:
|
|
table_ids.append(i)
|
|
p['table_ids'] = table_ids
|
|
elif prop.type in p_type_actions:
|
|
action_ids = []
|
|
for i in prop.action_ids:
|
|
act = i.to_jsondict()[i.__class__.__name__]
|
|
action_ids.append(act)
|
|
p['action_ids'] = action_ids
|
|
elif prop.type in p_type_oxms:
|
|
oxm_ids = []
|
|
for i in prop.oxm_ids:
|
|
oxm = i.to_jsondict()[i.__class__.__name__]
|
|
oxm_ids.append(oxm)
|
|
p['oxm_ids'] = oxm_ids
|
|
elif prop.type == p_type_packet:
|
|
oxm_values = []
|
|
for val in prop.oxm_values:
|
|
i = {val[0]: val[1]}
|
|
oxm_values.append(i)
|
|
p['oxm_values'] = oxm_values
|
|
elif prop.type in p_type_experimenter:
|
|
pass
|
|
properties.append(p)
|
|
s['name'] = stat.name.decode('utf-8')
|
|
s['properties'] = properties
|
|
|
|
if to_user:
|
|
s['table_id'] = UTIL.ofp_table_to_user(stat.table_id)
|
|
|
|
tables.append(s)
|
|
|
|
return wrap_dpid_dict(dp, tables, to_user)
|
|
|
|
|
|
def get_port_stats(dp, waiters, port_no=None, to_user=True):
|
|
if port_no is None:
|
|
port_no = dp.ofproto.OFPP_ANY
|
|
else:
|
|
port_no = UTIL.ofp_port_from_user(port_no)
|
|
|
|
stats = dp.ofproto_parser.OFPPortStatsRequest(dp, 0, port_no)
|
|
msgs = []
|
|
ofctl_utils.send_stats_request(dp, stats, waiters, msgs, LOG)
|
|
|
|
ports = []
|
|
for msg in msgs:
|
|
for stats in msg.body:
|
|
s = stats.to_jsondict()[stats.__class__.__name__]
|
|
properties = []
|
|
for prop in stats.properties:
|
|
p = prop.to_jsondict()[prop.__class__.__name__]
|
|
t = UTIL.ofp_port_stats_prop_type_to_user(prop.type)
|
|
p['type'] = t if t != prop.type else 'UNKNOWN'
|
|
properties.append(p)
|
|
s['properties'] = properties
|
|
|
|
if to_user:
|
|
s['port_no'] = UTIL.ofp_port_to_user(stats.port_no)
|
|
|
|
ports.append(s)
|
|
|
|
return wrap_dpid_dict(dp, ports, to_user)
|
|
|
|
|
|
def get_meter_stats(dp, waiters, meter_id=None, to_user=True):
|
|
if meter_id is None:
|
|
meter_id = dp.ofproto.OFPM_ALL
|
|
else:
|
|
meter_id = UTIL.ofp_meter_from_user(meter_id)
|
|
|
|
stats = dp.ofproto_parser.OFPMeterStatsRequest(
|
|
dp, 0, meter_id)
|
|
msgs = []
|
|
ofctl_utils.send_stats_request(dp, stats, waiters, msgs, LOG)
|
|
|
|
meters = []
|
|
for msg in msgs:
|
|
for stats in msg.body:
|
|
s = stats.to_jsondict()[stats.__class__.__name__]
|
|
bands = []
|
|
for band in stats.band_stats:
|
|
b = band.to_jsondict()[band.__class__.__name__]
|
|
bands.append(b)
|
|
s['band_stats'] = bands
|
|
|
|
if to_user:
|
|
s['meter_id'] = UTIL.ofp_meter_to_user(stats.meter_id)
|
|
|
|
meters.append(s)
|
|
|
|
return wrap_dpid_dict(dp, meters, to_user)
|
|
|
|
|
|
def get_meter_features(dp, waiters, to_user=True):
|
|
ofp = dp.ofproto
|
|
type_convert = {ofp.OFPMBT_DROP: 'DROP',
|
|
ofp.OFPMBT_DSCP_REMARK: 'DSCP_REMARK'}
|
|
|
|
capa_convert = {ofp.OFPMF_KBPS: 'KBPS',
|
|
ofp.OFPMF_PKTPS: 'PKTPS',
|
|
ofp.OFPMF_BURST: 'BURST',
|
|
ofp.OFPMF_STATS: 'STATS'}
|
|
|
|
stats = dp.ofproto_parser.OFPMeterFeaturesStatsRequest(dp, 0)
|
|
msgs = []
|
|
ofctl_utils.send_stats_request(dp, stats, waiters, msgs, LOG)
|
|
|
|
features = []
|
|
for msg in msgs:
|
|
for feature in msg.body:
|
|
band_types = []
|
|
for k, v in type_convert.items():
|
|
if (1 << k) & feature.band_types:
|
|
|
|
if to_user:
|
|
band_types.append(v)
|
|
|
|
else:
|
|
band_types.append(k)
|
|
|
|
capabilities = []
|
|
for k, v in sorted(capa_convert.items()):
|
|
if k & feature.capabilities:
|
|
|
|
if to_user:
|
|
capabilities.append(v)
|
|
|
|
else:
|
|
capabilities.append(k)
|
|
|
|
f = {'max_meter': feature.max_meter,
|
|
'band_types': band_types,
|
|
'capabilities': capabilities,
|
|
'max_bands': feature.max_bands,
|
|
'max_color': feature.max_color}
|
|
features.append(f)
|
|
|
|
return wrap_dpid_dict(dp, features, to_user)
|
|
|
|
|
|
def get_meter_desc(dp, waiters, meter_id=None, to_user=True):
|
|
flags = {dp.ofproto.OFPMF_KBPS: 'KBPS',
|
|
dp.ofproto.OFPMF_PKTPS: 'PKTPS',
|
|
dp.ofproto.OFPMF_BURST: 'BURST',
|
|
dp.ofproto.OFPMF_STATS: 'STATS'}
|
|
|
|
if meter_id is None:
|
|
meter_id = dp.ofproto.OFPM_ALL
|
|
else:
|
|
meter_id = UTIL.ofp_meter_from_user(meter_id)
|
|
|
|
stats = dp.ofproto_parser.OFPMeterDescStatsRequest(
|
|
dp, 0, meter_id)
|
|
msgs = []
|
|
ofctl_utils.send_stats_request(dp, stats, waiters, msgs, LOG)
|
|
|
|
configs = []
|
|
for msg in msgs:
|
|
for config in msg.body:
|
|
c = config.to_jsondict()[config.__class__.__name__]
|
|
bands = []
|
|
for band in config.bands:
|
|
b = band.to_jsondict()[band.__class__.__name__]
|
|
|
|
if to_user:
|
|
t = UTIL.ofp_meter_band_type_to_user(band.type)
|
|
b['type'] = t if t != band.type else 'UNKNOWN'
|
|
|
|
bands.append(b)
|
|
c_flags = []
|
|
for k, v in sorted(flags.items()):
|
|
if k & config.flags:
|
|
if to_user:
|
|
c_flags.append(v)
|
|
|
|
else:
|
|
c_flags.append(k)
|
|
|
|
c['flags'] = c_flags
|
|
c['bands'] = bands
|
|
|
|
if to_user:
|
|
c['meter_id'] = UTIL.ofp_meter_to_user(config.meter_id)
|
|
|
|
configs.append(c)
|
|
|
|
return wrap_dpid_dict(dp, configs, to_user)
|
|
|
|
|
|
def get_group_stats(dp, waiters, group_id=None, to_user=True):
|
|
if group_id is None:
|
|
group_id = dp.ofproto.OFPG_ALL
|
|
else:
|
|
group_id = UTIL.ofp_group_from_user(group_id)
|
|
|
|
stats = dp.ofproto_parser.OFPGroupStatsRequest(
|
|
dp, 0, group_id)
|
|
msgs = []
|
|
ofctl_utils.send_stats_request(dp, stats, waiters, msgs, LOG)
|
|
|
|
groups = []
|
|
for msg in msgs:
|
|
for stats in msg.body:
|
|
g = stats.to_jsondict()[stats.__class__.__name__]
|
|
bucket_stats = []
|
|
for bucket_stat in stats.bucket_stats:
|
|
c = bucket_stat.to_jsondict()[bucket_stat.__class__.__name__]
|
|
bucket_stats.append(c)
|
|
g['bucket_stats'] = bucket_stats
|
|
|
|
if to_user:
|
|
g['group_id'] = UTIL.ofp_group_to_user(stats.group_id)
|
|
|
|
groups.append(g)
|
|
|
|
return wrap_dpid_dict(dp, groups, to_user)
|
|
|
|
|
|
def get_group_features(dp, waiters, to_user=True):
|
|
|
|
ofp = dp.ofproto
|
|
type_convert = {ofp.OFPGT_ALL: 'ALL',
|
|
ofp.OFPGT_SELECT: 'SELECT',
|
|
ofp.OFPGT_INDIRECT: 'INDIRECT',
|
|
ofp.OFPGT_FF: 'FF'}
|
|
cap_convert = {ofp.OFPGFC_SELECT_WEIGHT: 'SELECT_WEIGHT',
|
|
ofp.OFPGFC_SELECT_LIVENESS: 'SELECT_LIVENESS',
|
|
ofp.OFPGFC_CHAINING: 'CHAINING',
|
|
ofp.OFPGFC_CHAINING_CHECKS: 'CHAINING_CHECKS'}
|
|
act_convert = {ofp.OFPAT_OUTPUT: 'OUTPUT',
|
|
ofp.OFPAT_COPY_TTL_OUT: 'COPY_TTL_OUT',
|
|
ofp.OFPAT_COPY_TTL_IN: 'COPY_TTL_IN',
|
|
ofp.OFPAT_SET_MPLS_TTL: 'SET_MPLS_TTL',
|
|
ofp.OFPAT_DEC_MPLS_TTL: 'DEC_MPLS_TTL',
|
|
ofp.OFPAT_PUSH_VLAN: 'PUSH_VLAN',
|
|
ofp.OFPAT_POP_VLAN: 'POP_VLAN',
|
|
ofp.OFPAT_PUSH_MPLS: 'PUSH_MPLS',
|
|
ofp.OFPAT_POP_MPLS: 'POP_MPLS',
|
|
ofp.OFPAT_SET_QUEUE: 'SET_QUEUE',
|
|
ofp.OFPAT_GROUP: 'GROUP',
|
|
ofp.OFPAT_SET_NW_TTL: 'SET_NW_TTL',
|
|
ofp.OFPAT_DEC_NW_TTL: 'DEC_NW_TTL',
|
|
ofp.OFPAT_SET_FIELD: 'SET_FIELD',
|
|
ofp.OFPAT_PUSH_PBB: 'PUSH_PBB',
|
|
ofp.OFPAT_POP_PBB: 'POP_PBB',
|
|
ofp.OFPAT_COPY_FIELD: 'COPY_FIELD',
|
|
ofp.OFPAT_METER: 'METER',
|
|
ofp.OFPAT_EXPERIMENTER: 'EXPERIMENTER'}
|
|
|
|
stats = dp.ofproto_parser.OFPGroupFeaturesStatsRequest(dp, 0)
|
|
msgs = []
|
|
ofctl_utils.send_stats_request(dp, stats, waiters, msgs, LOG)
|
|
|
|
features = []
|
|
for msg in msgs:
|
|
feature = msg.body
|
|
types = []
|
|
for k, v in type_convert.items():
|
|
if (1 << k) & feature.types:
|
|
if to_user:
|
|
types.append(v)
|
|
|
|
else:
|
|
types.append(k)
|
|
|
|
capabilities = []
|
|
for k, v in cap_convert.items():
|
|
if k & feature.capabilities:
|
|
if to_user:
|
|
capabilities.append(v)
|
|
|
|
else:
|
|
capabilities.append(k)
|
|
|
|
if to_user:
|
|
max_groups = []
|
|
for k, v in type_convert.items():
|
|
max_groups.append({v: feature.max_groups[k]})
|
|
|
|
else:
|
|
max_groups = feature.max_groups
|
|
|
|
actions = []
|
|
for k1, v1 in type_convert.items():
|
|
acts = []
|
|
for k2, v2 in act_convert.items():
|
|
if (1 << k2) & feature.actions[k1]:
|
|
if to_user:
|
|
acts.append(v2)
|
|
|
|
else:
|
|
acts.append(k2)
|
|
|
|
if to_user:
|
|
actions.append({v1: acts})
|
|
|
|
else:
|
|
actions.append({k1: acts})
|
|
|
|
f = {'types': types,
|
|
'capabilities': capabilities,
|
|
'max_groups': max_groups,
|
|
'actions': actions}
|
|
features.append(f)
|
|
|
|
return wrap_dpid_dict(dp, features, to_user)
|
|
|
|
|
|
def get_group_desc(dp, waiters, group_id=None, to_user=True):
|
|
if group_id is None:
|
|
group_id = dp.ofproto.OFPG_ALL
|
|
else:
|
|
group_id = UTIL.ofp_group_from_user(group_id)
|
|
|
|
stats = dp.ofproto_parser.OFPGroupDescStatsRequest(dp, 0, group_id)
|
|
msgs = []
|
|
ofctl_utils.send_stats_request(dp, stats, waiters, msgs, LOG)
|
|
|
|
descs = []
|
|
for msg in msgs:
|
|
for stats in msg.body:
|
|
d = stats.to_jsondict()[stats.__class__.__name__]
|
|
buckets = []
|
|
for bucket in stats.buckets:
|
|
b = bucket.to_jsondict()[bucket.__class__.__name__]
|
|
actions = []
|
|
for action in bucket.actions:
|
|
if to_user:
|
|
actions.append(action_to_str(action))
|
|
|
|
else:
|
|
actions.append(action)
|
|
properties = []
|
|
for prop in bucket.properties:
|
|
p = prop.to_jsondict()[prop.__class__.__name__]
|
|
t = UTIL.ofp_group_bucket_prop_type_to_user(prop.type)
|
|
p['type'] = t if t != prop.type else 'UNKNOWN'
|
|
properties.append(p)
|
|
b['actions'] = actions
|
|
b['properties'] = properties
|
|
buckets.append(b)
|
|
|
|
d['buckets'] = buckets
|
|
if to_user:
|
|
d['group_id'] = UTIL.ofp_group_to_user(stats.group_id)
|
|
t = UTIL.ofp_group_type_to_user(stats.type)
|
|
d['type'] = t if t != stats.type else 'UNKNOWN'
|
|
|
|
descs.append(d)
|
|
|
|
return wrap_dpid_dict(dp, descs, to_user)
|
|
|
|
|
|
def get_port_desc(dp, waiters, port_no=None, to_user=True):
|
|
if port_no is None:
|
|
port_no = dp.ofproto.OFPP_ANY
|
|
else:
|
|
port_no = UTIL.ofp_port_from_user(port_no)
|
|
|
|
stats = dp.ofproto_parser.OFPPortDescStatsRequest(dp, 0, port_no)
|
|
msgs = []
|
|
ofctl_utils.send_stats_request(dp, stats, waiters, msgs, LOG)
|
|
|
|
descs = []
|
|
|
|
for msg in msgs:
|
|
stats = msg.body
|
|
for stat in stats:
|
|
d = stat.to_jsondict()[stat.__class__.__name__]
|
|
properties = []
|
|
for prop in stat.properties:
|
|
p = prop.to_jsondict()[prop.__class__.__name__]
|
|
|
|
if to_user:
|
|
t = UTIL.ofp_port_desc_prop_type_to_user(prop.type)
|
|
p['type'] = t if t != prop.type else 'UNKNOWN'
|
|
|
|
properties.append(p)
|
|
d['name'] = stat.name.decode('utf-8')
|
|
d['properties'] = properties
|
|
|
|
if to_user:
|
|
d['port_no'] = UTIL.ofp_port_to_user(stat.port_no)
|
|
|
|
descs.append(d)
|
|
|
|
return wrap_dpid_dict(dp, descs, to_user)
|
|
|
|
|
|
def get_role(dp, waiters, to_user=True):
|
|
return ofctl_utils.get_role(dp, waiters, to_user)
|
|
|
|
|
|
def mod_flow_entry(dp, flow, cmd):
|
|
cookie = str_to_int(flow.get('cookie', 0))
|
|
cookie_mask = str_to_int(flow.get('cookie_mask', 0))
|
|
table_id = UTIL.ofp_table_from_user(flow.get('table_id', 0))
|
|
idle_timeout = str_to_int(flow.get('idle_timeout', 0))
|
|
hard_timeout = str_to_int(flow.get('hard_timeout', 0))
|
|
priority = str_to_int(flow.get('priority', 0))
|
|
buffer_id = UTIL.ofp_buffer_from_user(
|
|
flow.get('buffer_id', dp.ofproto.OFP_NO_BUFFER))
|
|
out_port = UTIL.ofp_port_from_user(
|
|
flow.get('out_port', dp.ofproto.OFPP_ANY))
|
|
out_group = UTIL.ofp_group_from_user(
|
|
flow.get('out_group', dp.ofproto.OFPG_ANY))
|
|
importance = str_to_int(flow.get('importance', 0))
|
|
flags = str_to_int(flow.get('flags', 0))
|
|
match = to_match(dp, flow.get('match', {}))
|
|
inst = to_instructions(dp, flow.get('instructions', []))
|
|
|
|
flow_mod = dp.ofproto_parser.OFPFlowMod(
|
|
dp, cookie, cookie_mask, table_id, cmd, idle_timeout,
|
|
hard_timeout, priority, buffer_id, out_port, out_group,
|
|
importance, flags, match, inst)
|
|
|
|
ofctl_utils.send_msg(dp, flow_mod, LOG)
|
|
|
|
|
|
def mod_meter_entry(dp, meter, cmd):
|
|
flags = 0
|
|
if 'flags' in meter:
|
|
meter_flags = meter['flags']
|
|
if not isinstance(meter_flags, list):
|
|
meter_flags = [meter_flags]
|
|
for flag in meter_flags:
|
|
t = UTIL.ofp_meter_flags_from_user(flag)
|
|
f = t if t != flag else None
|
|
if f is None:
|
|
LOG.error('Unknown meter flag: %s', flag)
|
|
continue
|
|
flags |= f
|
|
|
|
meter_id = UTIL.ofp_meter_from_user(meter.get('meter_id', 0))
|
|
|
|
bands = []
|
|
for band in meter.get('bands', []):
|
|
band_type = band.get('type')
|
|
rate = str_to_int(band.get('rate', 0))
|
|
burst_size = str_to_int(band.get('burst_size', 0))
|
|
if band_type == 'DROP':
|
|
bands.append(
|
|
dp.ofproto_parser.OFPMeterBandDrop(rate, burst_size))
|
|
elif band_type == 'DSCP_REMARK':
|
|
prec_level = str_to_int(band.get('prec_level', 0))
|
|
bands.append(
|
|
dp.ofproto_parser.OFPMeterBandDscpRemark(
|
|
rate, burst_size, prec_level))
|
|
elif band_type == 'EXPERIMENTER':
|
|
experimenter = str_to_int(band.get('experimenter', 0))
|
|
bands.append(
|
|
dp.ofproto_parser.OFPMeterBandExperimenter(
|
|
rate, burst_size, experimenter))
|
|
else:
|
|
LOG.error('Unknown band type: %s', band_type)
|
|
|
|
meter_mod = dp.ofproto_parser.OFPMeterMod(
|
|
dp, cmd, flags, meter_id, bands)
|
|
|
|
ofctl_utils.send_msg(dp, meter_mod, LOG)
|
|
|
|
|
|
def mod_group_entry(dp, group, cmd):
|
|
ofp = dp.ofproto
|
|
parser = dp.ofproto_parser
|
|
|
|
group_type = str(group.get('type', 'ALL'))
|
|
t = UTIL.ofp_group_type_from_user(group_type)
|
|
group_type = t if t != group_type else None
|
|
if group_type is None:
|
|
LOG.error('Unknown group type: %s', group.get('type'))
|
|
|
|
group_id = UTIL.ofp_group_from_user(group.get('group_id', 0))
|
|
command_bucket_id = str_to_int(group.get('command_bucket_id', 0))
|
|
|
|
# Note:
|
|
# The list of group property types that are currently defined
|
|
# are only OFPGPT_EXPERIMENTER(Experimenter defined).
|
|
properties = []
|
|
|
|
buckets = []
|
|
for bucket in group.get('buckets', []):
|
|
|
|
# get bucket_id in buckets
|
|
bucket_id = str_to_int(bucket.get('bucket_id', 0))
|
|
|
|
# get actions in buckets
|
|
bucket_actions = []
|
|
for dic in bucket.get('actions', []):
|
|
action = to_action(dp, dic)
|
|
if action is not None:
|
|
bucket_actions.append(action)
|
|
|
|
# get properties in buckets
|
|
bucket_properties = []
|
|
for p in bucket.get('properties', []):
|
|
group_bp_type = str(p.get('type', 'WEIGHT'))
|
|
t = UTIL.ofp_group_bucket_prop_type_from_user(group_bp_type)
|
|
group_bp_type = t if t != group_bp_type else ofp.OFPGBPT_WEIGHT
|
|
|
|
if group_bp_type == ofp.OFPGBPT_WEIGHT:
|
|
weight = str_to_int(p.get('weight', 0))
|
|
bucket_properties.append(
|
|
parser.OFPGroupBucketPropWeight(
|
|
type_=group_bp_type, weight=weight))
|
|
elif group_bp_type == ofp.OFPGBPT_WATCH_PORT:
|
|
watch_port = str_to_int(p.get('watch', dp.ofproto.OFPP_ANY))
|
|
bucket_properties.append(
|
|
parser.OFPGroupBucketPropWatch(
|
|
type_=group_bp_type, watch=watch_port))
|
|
elif group_bp_type == ofp.OFPGBPT_WATCH_GROUP:
|
|
watch_group = str_to_int(p.get('watch', dp.ofproto.OFPG_ANY))
|
|
bucket_properties.append(
|
|
parser.OFPGroupBucketPropWatch(
|
|
type_=group_bp_type, watch=watch_group))
|
|
elif group_bp_type == ofp.OFPGBPT_EXPERIMENTER:
|
|
experimenter = p.get('experimenter', 0)
|
|
exp_type = p.get('exp_type', 0)
|
|
data_type = p.get('data_type', 'ascii')
|
|
if data_type not in ['ascii', 'base64']:
|
|
LOG.error('Unknown data type: %s', data_type)
|
|
data = p.get('data', '')
|
|
if data_type == 'base64':
|
|
data = base64.b64decode(data)
|
|
bucket_properties.append(
|
|
parser.OFPGroupBucketPropExperimenter(
|
|
type_=group_bp_type, experimenter=experimenter,
|
|
exp_type=exp_type, data=data))
|
|
else:
|
|
LOG.error('Unknown group bucket prop type: %s', p['type'])
|
|
|
|
# create bucket
|
|
bucket = parser.OFPBucket(bucket_id=bucket_id,
|
|
actions=bucket_actions,
|
|
properties=bucket_properties)
|
|
buckets.append(bucket)
|
|
|
|
group_mod = parser.OFPGroupMod(dp, cmd, group_type, group_id,
|
|
command_bucket_id, buckets,
|
|
properties)
|
|
|
|
ofctl_utils.send_msg(dp, group_mod, LOG)
|
|
|
|
|
|
def mod_port_behavior(dp, port_config):
|
|
ofp = dp.ofproto
|
|
parser = dp.ofproto_parser
|
|
port_no = UTIL.ofp_port_from_user(port_config.get('port_no', 0))
|
|
hw_addr = str(port_config.get('hw_addr'))
|
|
config = str_to_int(port_config.get('config', 0))
|
|
mask = str_to_int(port_config.get('mask', 0))
|
|
properties = port_config.get('properties')
|
|
|
|
prop = []
|
|
for p in properties:
|
|
type_ = UTIL.ofp_port_mod_prop_type_from_user(p['type'])
|
|
length = None
|
|
if type_ == ofp.OFPPDPT_ETHERNET:
|
|
advertise = UTIL.ofp_port_features_from_user(p['advertise'])
|
|
prop.append(
|
|
parser.OFPPortModPropEthernet(type_, length, advertise))
|
|
elif type_ == ofp.OFPPDPT_OPTICAL:
|
|
prop.append(
|
|
parser.OFPPortModPropOptical(
|
|
type_, length, p['configure'], p['freq_lmda'],
|
|
p['fl_offset'], p['grid_span'], p['tx_pwr']))
|
|
elif type_ == ofp.OFPPDPT_EXPERIMENTER:
|
|
prop.append(
|
|
parser.OFPPortModPropExperimenter(
|
|
type_, length, p['experimenter'], p['exp_type'],
|
|
p['data']))
|
|
else:
|
|
LOG.error('Unknown port desc prop type: %s', type_)
|
|
|
|
port_mod = dp.ofproto_parser.OFPPortMod(
|
|
dp, port_no, hw_addr, config, mask, prop)
|
|
|
|
ofctl_utils.send_msg(dp, port_mod, LOG)
|
|
|
|
|
|
def set_role(dp, role):
|
|
r = UTIL.ofp_role_from_user(role.get('role', dp.ofproto.OFPCR_ROLE_EQUAL))
|
|
role_request = dp.ofproto_parser.OFPRoleRequest(dp, r, None, 0)
|
|
ofctl_utils.send_msg(dp, role_request, LOG)
|
|
|
|
|
|
# NOTE(jkoelker) Alias common funcitons
|
|
send_experimenter = ofctl_utils.send_experimenter
|