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

Reply via email to