This patch adds OXS class parser for Extensible Flow Entry Statistics in OpenFlow Spec 1.5.
note: This pacth has no implementation for specific OXS Experimenter classes as oxm_fields.py has. (eg. ONF Experimenter, Nicira Experimenter) Signed-off-by: IWASE Yusuke <[email protected]> --- ryu/ofproto/ofproto_v1_5.py | 30 +++- ryu/ofproto/oxs_fields.py | 352 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 374 insertions(+), 8 deletions(-) create mode 100644 ryu/ofproto/oxs_fields.py diff --git a/ryu/ofproto/ofproto_v1_5.py b/ryu/ofproto/ofproto_v1_5.py index f9a3668..e6a9983 100644 --- a/ryu/ofproto/ofproto_v1_5.py +++ b/ryu/ofproto/ofproto_v1_5.py @@ -20,8 +20,7 @@ OpenFlow 1.5 definitions. from ryu.lib import type_desc from ryu.ofproto import oxm_fields -# TODO: oxs_fields -# from ryu.ofproto import oxs_fields +from ryu.ofproto import oxs_fields from struct import calcsize @@ -449,12 +448,27 @@ assert calcsize(OFP_STATS_PACK_STR) == OFP_STATS_SIZE OFPXSC_OPENFLOW_BASIC = 0x8002 # Basic stats class for OpenFlow OFPXSC_EXPERIMENTER = 0xFFFF # Experimenter class -# enum oxs_ofb_stat_fields -OFPXST_OFB_DURATION = 0 # Time flow entry has been alive. -OFPXST_OFB_IDLE_TIME = 1 # Time flow entry has been idle. -OFPXST_OFB_FLOW_COUNT = 3 # Number of aggregated flow entries. -OFPXST_OFB_PACKET_COUNT = 4 # Number of packets in flow entry. -OFPXST_OFB_BYTE_COUNT = 5 # Number of bytes in flow entry. + +def _oxs_tlv_header(class_, field, reserved, length): + return (class_ << 16) | (field << 9) | (reserved << 8) | length + + +def oxs_tlv_header(field, length): + return _oxs_tlv_header(OFPXSC_OPENFLOW_BASIC, field, 0, length) + + +def oxs_tlv_header_extract_length(header): + return header & 0xff + +oxs_types = [ + oxs_fields.OpenFlowBasic('duration', 0, type_desc.Int4, 2), + oxs_fields.OpenFlowBasic('idle_time', 1, type_desc.Int4, 2), + oxs_fields.OpenFlowBasic('flow_count', 3, type_desc.Int4), + oxs_fields.OpenFlowBasic('packet_count', 4, type_desc.Int8), + oxs_fields.OpenFlowBasic('byte_count', 5, type_desc.Int8), +] + +oxs_fields.generate(__name__) # enum ofp_action_type OFPAT_OUTPUT = 0 # Output to switch port. diff --git a/ryu/ofproto/oxs_fields.py b/ryu/ofproto/oxs_fields.py new file mode 100644 index 0000000..ec2049b --- /dev/null +++ b/ryu/ofproto/oxs_fields.py @@ -0,0 +1,352 @@ +# Copyright (C) 2015 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. + +# there are two representations of value which this module deal with. +# +# "user" +# the readable value which are strings. +# +# "internal" +# the on-wire bytes value. + +# There are two types of OXS headers. +# +# 32-bit OXS header +# 31 16 15 9 8 7 0 +# +-------------------------------+-------------+-+---------------+ +# | class | field |r| length | +# +-------------------------------+-------------+-+---------------+ +# +# 64-bit experimenter OXS header +# 31 16 15 9 8 7 0 +# +-------------------------------+-------------+-+---------------+ +# | class (OFPXSC_EXPERIMENTER) | field |r| length | +# +-------------------------------+-------------+-+---------------+ +# | experimenter ID | +# +---------------------------------------------------------------+ +# +# Description of OXS header fields +# +----------------------+-------+--------------------------------------------+ +# | Name | Width | Usage | +# +----------+-----------+-------+--------------------------------------------+ +# | oxs_type | oxs_class | 16 | Stat class: member class or reserved class | +# | +-----------+-------+--------------------------------------------+ +# | | oxs_field | 7 | Stat field within the class | +# +----------+-----------+-------+--------------------------------------------+ +# | reserved | 1 | Reserved for future use | +# +----------------------+-------+--------------------------------------------+ +# | length | 8 | Length of OXS payload | +# +----------------------+-------+--------------------------------------------+ + +import struct +from ryu.ofproto import ofproto_common +from ryu.lib.pack_utils import msg_pack_into +from ryu.lib import type_desc + + +OFPXSC_OPENFLOW_BASIC = 0x8002 +OFPXSC_EXPERIMENTER = 0xFFFF + + +OFPXSC_HEADER_PACK_STR = '!I' +OFPXSC_EXP_HEADER_PACK_STR = '!I' + + +class _OxsClass(object): + # oxs_class = OFPXSC_* must be an attribute of subclass. + + def __init__(self, name, field, desc, count=1): + self.name = name + self.oxs_field = field + self.oxs_type = field | (self.oxs_class << 7) + # 'num' has not corresponding field in the specification. + # This is specific to this implementation and used to retrieve + # _OxsClass subclass from 'num_to_field' dictionary. + self.num = self.oxs_type + # 'desc' is an instance of a class included in type_desc module. + self.desc = desc + # 'count' is the number of OFPXST_OFB_* field value. + # eg. OFPXST_OFB_DURATION has two values in own field. + self.count = count + + +class OpenFlowBasic(_OxsClass): + oxs_class = OFPXSC_OPENFLOW_BASIC + + +class _Experimenter(_OxsClass): + oxs_class = OFPXSC_EXPERIMENTER + # experimenter_id must be an attribute of subclass. + + def __init__(self, name, field, desc, count): + super(_Experimenter, self).__init__(name, field, desc, count) + self.num = (self.experimenter_id, self.oxs_type) + + +def generate(modname): + import sys + import functools + + mod = sys.modules[modname] + + def add_attr(k, v): + setattr(mod, k, v) + + for i in mod.oxs_types: + if i.oxs_class != OFPXSC_OPENFLOW_BASIC: + continue + uk = i.name.upper() + add_attr('OFPXST_OFB_' + uk, i.oxs_field) + add_attr('OXS_OF_' + uk, mod.oxs_tlv_header(i.oxs_field, + i.desc.size * i.count)) + + name_to_field = dict((f.name, f) for f in mod.oxs_types) + num_to_field = dict((f.num, f) for f in mod.oxs_types) + add_attr('oxs_from_user', functools.partial(_from_user, name_to_field)) + add_attr('oxs_from_user_header', + functools.partial(_from_user_header, name_to_field)) + add_attr('oxs_to_user', functools.partial(_to_user, num_to_field)) + add_attr('oxs_to_user_header', + functools.partial(_to_user_header, num_to_field)) + add_attr('_oxs_field_desc', functools.partial(_field_desc, num_to_field)) + add_attr('oxs_parse', functools.partial(_parse, mod)) + add_attr('oxs_parse_header', functools.partial(_parse_header, mod)) + add_attr('oxs_serialize', functools.partial(_serialize, mod)) + add_attr('oxs_serialize_header', functools.partial(_serialize_header, mod)) + add_attr('oxs_to_jsondict', _to_jsondict) + add_attr('oxs_from_jsondict', _from_jsondict) + + +def _get_field_info_by_name(name_to_field, name): + try: + f = name_to_field[name] + desc = f.desc + num = f.num + except KeyError: + desc = type_desc.UnknownType + if name.startswith('field_'): + num = int(name.split('_')[1]) + else: + raise KeyError('unknown stat field ' + name) + return num, desc + + +def _from_user_header(name_to_field, name): + (num, desc) = _get_field_info_by_name(name_to_field, name) + return num + + +def _from_user(name_to_field, name, user_value): + (num, desc) = _get_field_info_by_name(name_to_field, name) + if isinstance(user_value, (tuple, list)): + value = [] + for uv in user_value: + v = desc.from_user(uv) + value.append(v) + return num, tuple(value) + else: + return num, desc.from_user(user_value) + + +def _get_field_info_by_number(num_to_field, n): + try: + f = num_to_field[n] + desc = f.desc + name = f.name + except KeyError: + desc = type_desc.UnknownType + name = 'field_%d' % (n,) + return name, desc + + +def _to_user_header(num_to_field, n): + (name, desc) = _get_field_info_by_number(num_to_field, n) + return name + + +def _to_user(num_to_field, n, v): + (name, desc) = _get_field_info_by_number(num_to_field, n) + if v is not None: + if isinstance(v, (tuple, list)): + value = [] + for bv in v: + if hasattr(desc, 'size') and desc.size != len(bv): + raise Exception( + 'Unexpected OXS payload length %d for %s (expected %d)' + % (len(bv), name, desc.size)) + uv = desc.to_user(bv) + value.append(uv) + value = tuple(value) # XXX + else: + if hasattr(desc, 'size') and desc.size != len(v): + raise Exception( + 'Unexpected OXS payload length %d for %s (expected %d)' + % (len(v), name, desc.size)) + value = desc.to_user(v) + else: + value = None + return name, value + + +def _field_desc(num_to_field, n): + return num_to_field[n] + + +def _parse_header_impl(mod, buf, offset): + (header, ) = struct.unpack_from(OFPXSC_HEADER_PACK_STR, buf, offset) + hdr_len = struct.calcsize(OFPXSC_HEADER_PACK_STR) + assert hdr_len == 4 + oxs_type = header >> 9 # class|field + oxs_class = oxs_type >> 7 + oxs_length = header & 0xff + if oxs_class == OFPXSC_EXPERIMENTER: + # Experimenter OXSs have 64-bit header. (vs 32-bit for other OXSs) + (exp_id, ) = struct.unpack_from(OFPXSC_EXP_HEADER_PACK_STR, buf, + offset + hdr_len) + exp_hdr_len = struct.calcsize(OFPXSC_EXP_HEADER_PACK_STR) + assert exp_hdr_len == 4 + num = (exp_id, oxs_type) + else: + exp_hdr_len = 0 + num = oxs_type + # Note: OXS payload length (oxs_len) includes Experimenter ID (exp_hdr_len) + # for experimenter OXSs. + value_len = oxs_length - exp_hdr_len + assert value_len > 0 + field_len = hdr_len + oxs_length + hdr_len += exp_hdr_len + return num, hdr_len, value_len, field_len + + +def _parse_header(mod, buf, offset): + (num, hdr_len, value_len, + field_len) = _parse_header_impl(mod, buf, offset) + return num, hdr_len + + +def _parse_body(mod, buf, offset, num, value_len): + if isinstance(num, (tuple, list)): + (exp_id, oxs_type) = num + else: + oxs_type = num + oxs_field = oxs_type & 0x3f + count = 1 + if oxs_field == mod.OFPXST_OFB_DURATION or \ + oxs_field == mod.OFPXST_OFB_IDLE_TIME: + # Note: OFPXST_OFB_DURATION and OFPXST_OFB_IDLE_TIME fields have + # two values. The first 32 bit value is the time in sec and the + # second is the time in nano-sec. + count = 2 + value_len //= count + value_pack_str = '!%ds' % value_len + assert struct.calcsize(value_pack_str) == value_len + if count == 1: + (value, ) = struct.unpack_from(value_pack_str, buf, offset) + else: + value = [] + for c in range(0, count): + (v, ) = struct.unpack_from(value_pack_str, buf, offset) + value.append(v) + offset += value_len + value = tuple(value) # XXX + return value + + +def _parse(mod, buf, offset): + (num, hdr_len, value_len, + field_len) = _parse_header_impl(mod, buf, offset) + offset += hdr_len + value = _parse_body(mod, buf, offset, num, value_len) + return num, value, field_len + + +def _make_exp_hdr(mod, n): + exp_hdr = bytearray() + try: + desc = mod._oxs_field_desc(n) + except KeyError: + return n, exp_hdr + if desc.oxs_class == OFPXSC_EXPERIMENTER: + (exp_id, oxs_type) = n + assert desc.experimenter_id == exp_id + msg_pack_into(OFPXSC_EXP_HEADER_PACK_STR, exp_hdr, 0, + desc.experimenter_id) + assert len(exp_hdr) == struct.calcsize(OFPXSC_EXP_HEADER_PACK_STR) + n = desc.oxs_type + assert (n >> 7) == OFPXSC_EXPERIMENTER + return n, exp_hdr + + +def _serialize_header(mod, n, buf, offset): + try: + f = mod._oxs_field_desc(n) + value_len = f.desc.size * f.count + except KeyError: + value_len = 0 + n, exp_hdr = _make_exp_hdr(mod, n) + exp_hdr_len = len(exp_hdr) + pack_str = "!I%ds" % (exp_hdr_len,) + msg_pack_into(pack_str, buf, offset, + (n << 9) | (0 << 8) | (exp_hdr_len + value_len), + bytes(exp_hdr)) + return struct.calcsize(pack_str) + + +def _serialize_body(mod, n, value, buf, offset): + if isinstance(value, (tuple, list)): + value_len = 0 + for v in value: + v_len = len(v) + pack_str = "!%ds" % (v_len,) + msg_pack_into(pack_str, buf, offset, v) + offset += v_len + value_len += v_len + else: + value_len = len(value) + pack_str = "!%ds" % (value_len,) + msg_pack_into(pack_str, buf, offset, value) + return value_len + + +def _serialize(mod, n, value, buf, offset): + try: + f = mod._oxs_field_desc(n) + value_len = f.desc.size * f.count + except KeyError: + if isinstance(value, (tuple, list)): + value_len = len(value) * len(value[0]) + else: + value_len = len(value) + n, exp_hdr = _make_exp_hdr(mod, n) + exp_hdr_len = len(exp_hdr) + pack_str = "!I%ds" % (exp_hdr_len,) + msg_pack_into(pack_str, buf, offset, + (n << 9) | (0 << 8) | (exp_hdr_len + value_len), + bytes(exp_hdr)) + hdr_len = struct.calcsize(pack_str) + offset += hdr_len + value_len = _serialize_body(mod, n, value, buf, offset) + return hdr_len + value_len + + +def _to_jsondict(k, uv): + return {"OXSTlv": {"field": k, "value": uv}} + + +def _from_jsondict(j): + tlv = j['OXSTlv'] + field = tlv['field'] + value = tlv['value'] + return (field, value) -- 1.9.1 ------------------------------------------------------------------------------ One dashboard for servers and applications across Physical-Virtual-Cloud Widest out-of-the-box monitoring support with 50+ applications Performance metrics, stats and reports that give you Actionable Insights Deep dive visibility with transaction tracing using APM Insight. http://ad.doubleclick.net/ddm/clk/290420510;117567292;y _______________________________________________ Ryu-devel mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/ryu-devel
