this patch makes ofctl_rest possible to:

  - support the GROUP action in the FlowMod message
  - support the GroupMod message
  - support the GroupStats message
  - support the GroupFeatures message
  - support the GroupDesc message

see following examples.


FlowMod with the group action:

  e.g.)

    curl -X POST -d '{"dpid": 1,
                      "match": {},
                      "actions": [{"type": "GROUP,
                                   "group_id": 1}]}' 
http://localhost:8080/stats/flowentry/add

GroupMod:

  usage)

    URI:    /stats/groupentry/{cmd}
    METHOD: POST

      NOTE: the value of 'cmd' is one of follows: 'add', 'modify', or 'delete'.

    the message body is as follows:

      type      Group types. 'ALL', 'SELECT', 'INDIRECT', or 'FF'.
      group_id  Group Identifier. (default: 0)
      buckets   a list of buckets.

    where each bucket has the following members:

      weight       Relative weight of bucket. (default: 0)
      watch_port   Port whose state affects whether this bucket is live. 
(default: OFPP_ANY)
      watch_group  Group whose state affects whether this bucket is live. 
(default: OFPG_ANY)
      actions      a list of actions. the format is the same as that of FlowMod.

  e.g.)

    curl -X POST -d '{"dpid": 1,
                      "type": "FF",
                      "group_id": 1,
                      "buckets": [{"watch_port": 2,
                                   "actions": [{"type": "OUTPUT",
                                                "port": 3}]}]}' 
http://localhost:8080/stats/groupentry/add

GroupStats:

  usage)

    URI:    /stats/group/{dpid}
    METHOD: GET

  e.g.)

    curl http://localhost:8080/stats/group/1
    {
      "1": [
        {
          "bucket_stats": [
            {
              "packet_count": 0,
              "byte_count": 0
            }
          ],
          "byte_count": 0,
          "ref_count": 0,
          "duration_nsec": 231000000,
          "packet_count": 0,
          "duration_sec": 11238,
          "group_id": 1
        }
      ]
    }

GroupFeatures:

  usage)

    URI:    /stats/groupfeatures/{dpid}
    METHOD: GET

  e.g.)

    curl http://localhost:8080/stats/groupfeatures/1
    {
      "1": [
        {
          "actions": [
            {"ALL": ["OUTPUT", "COPY_TTL_OUT", "COPY_TTL_IN", "SET_MPLS_TTL", 
"DEC_MPLS_TTL", "PUSH_VLAN", "POP_VLAN", "PUSH_MPLS", "POP_MPLS", "SET_QUEUE", 
"GROUP", "SET_NW_TTL", "DEC_NW_TTL", "SET_FIELD"]},
            {"SELECT": []},
            {"INDIRECT": []},
            {"FF": []}
          ],
          "max_groups": [
            {"ALL": 4294967040},
            {"SELECT": 4294967040},
            {"INDIRECT": 4294967040},
            {"FF": 4294967040}
          ],
          "capabilities": ["SELECT_WEIGHT", "SELECT_LIVENESS", "CHAINING"],
          "types": []
        }
      ]
    }

GroupDesc:

  usage)

    URI:    /stats/groupdesc/{dpid}
    METHOD: GET

  e.g.)

    curl http://localhost:8080/stats/groupdesc/1
    {
      "1": [
        {
          "buckets": [
            {
              "actions": ["OUTPUT:2"],
              "watch_group": 4294967295,
              "weight": 0,
              "watch_port": 3
            }
          ],
          "group_id": 1,
          "type": "FF"
        }
      ]
    }

Signed-off-by: Yuichi Ito <[email protected]>
---
 ryu/app/ofctl_rest.py |  134 +++++++++++++++++
 ryu/lib/ofctl_v1_2.py |  321 ++++++++++++++++++++++++++++++++++++---
 ryu/lib/ofctl_v1_3.py |  397 +++++++++++++++++++++++++++++++++++--------------
 3 files changed, 721 insertions(+), 131 deletions(-)

diff --git a/ryu/app/ofctl_rest.py b/ryu/app/ofctl_rest.py
index e108b03..9fbce30 100644
--- a/ryu/app/ofctl_rest.py
+++ b/ryu/app/ofctl_rest.py
@@ -59,6 +59,16 @@ LOG = logging.getLogger('ryu.app.ofctl_rest')
 # get meters stats of the switch
 # GET /stats/meter/<dpid>
 #
+# get group features stats of the switch
+# GET /stats/groupfeatures/<dpid>
+#
+# get groups desc stats of the switch
+# GET /stats/groupdesc/<dpid>
+#
+# get groups stats of the switch
+# GET /stats/group/<dpid>
+#
+#
 ## Update the switch stats
 #
 # add a flow entry
@@ -82,6 +92,15 @@ LOG = logging.getLogger('ryu.app.ofctl_rest')
 # delete a meter entry
 # POST /stats/meterentry/delete
 #
+# add a group entry
+# POST /stats/groupentry/add
+#
+# modify a group entry
+# POST /stats/groupentry/modify
+#
+# delete a group entry
+# POST /stats/groupentry/delete
+#
 #
 # send a experimeter message
 # POST /stats/experimenter/<dpid>
@@ -194,6 +213,54 @@ class StatsController(ControllerBase):
         body = json.dumps(meters)
         return (Response(content_type='application/json', body=body))

+    def get_group_features(self, req, dpid, **_kwargs):
+        dp = self.dpset.get(int(dpid))
+        if dp is None:
+            return Response(status=404)
+
+        if dp.ofproto.OFP_VERSION == ofproto_v1_2.OFP_VERSION:
+            groups = ofctl_v1_2.get_group_features(dp, self.waiters)
+        elif dp.ofproto.OFP_VERSION == ofproto_v1_3.OFP_VERSION:
+            groups = ofctl_v1_3.get_group_features(dp, self.waiters)
+        else:
+            LOG.debug('Unsupported OF protocol')
+            return Response(status=501)
+
+        body = json.dumps(groups)
+        return Response(content_type='application/json', body=body)
+
+    def get_group_desc(self, req, dpid, **_kwargs):
+        dp = self.dpset.get(int(dpid))
+        if dp is None:
+            return Response(status=404)
+
+        if dp.ofproto.OFP_VERSION == ofproto_v1_2.OFP_VERSION:
+            groups = ofctl_v1_2.get_group_desc(dp, self.waiters)
+        elif dp.ofproto.OFP_VERSION == ofproto_v1_3.OFP_VERSION:
+            groups = ofctl_v1_3.get_group_desc(dp, self.waiters)
+        else:
+            LOG.debug('Unsupported OF protocol')
+            return Response(status=501)
+
+        body = json.dumps(groups)
+        return Response(content_type='application/json', body=body)
+
+    def get_group_stats(self, req, dpid, **_kwargs):
+        dp = self.dpset.get(int(dpid))
+        if dp is None:
+            return Response(status=404)
+
+        if dp.ofproto.OFP_VERSION == ofproto_v1_2.OFP_VERSION:
+            groups = ofctl_v1_2.get_group_stats(dp, self.waiters)
+        elif dp.ofproto.OFP_VERSION == ofproto_v1_3.OFP_VERSION:
+            groups = ofctl_v1_3.get_group_stats(dp, self.waiters)
+        else:
+            LOG.debug('Unsupported OF protocol')
+            return Response(status=501)
+
+        body = json.dumps(groups)
+        return Response(content_type='application/json', body=body)
+
     def mod_flow_entry(self, req, cmd, **_kwargs):
         try:
             flow = eval(req.body)
@@ -278,6 +345,41 @@ class StatsController(ControllerBase):

         return Response(status=200)

+    def mod_group_entry(self, req, cmd, **_kwargs):
+        try:
+            group = eval(req.body)
+        except SyntaxError:
+            LOG.debug('invalid syntax %s', req.body)
+            return Response(status=400)
+
+        dpid = group.get('dpid')
+        dp = self.dpset.get(int(dpid))
+        if dp is None:
+            return Response(status=404)
+
+        if dp.ofproto.OFP_VERSION == ofproto_v1_0.OFP_VERSION:
+            LOG.debug('Unsupported OF protocol')
+            return Response(status=501)
+
+        if cmd == 'add':
+            cmd = dp.ofproto.OFPGC_ADD
+        elif cmd == 'modify':
+            cmd = dp.ofproto.OFPGC_MODIFY
+        elif cmd == 'delete':
+            cmd = dp.ofproto.OFPGC_DELETE
+        else:
+            return Response(status=404)
+
+        if dp.ofproto.OFP_VERSION == ofproto_v1_2.OFP_VERSION:
+            ofctl_v1_2.mod_group_entry(dp, group, cmd)
+        elif dp.ofproto.OFP_VERSION == ofproto_v1_3.OFP_VERSION:
+            ofctl_v1_3.mod_group_entry(dp, group, cmd)
+        else:
+            LOG.debug('Unsupported OF protocol')
+            return Response(status=501)
+
+        return Response(status=200)
+
     def send_experimenter(self, req, dpid, **_kwargs):
         dp = self.dpset.get(int(dpid))
         if dp is None:
@@ -356,6 +458,21 @@ class RestStatsApi(app_manager.RyuApp):
                        controller=StatsController, action='get_meter_stats',
                        conditions=dict(method=['GET']))

+        uri = path + '/groupfeatures/{dpid}'
+        mapper.connect('stats', uri,
+                       controller=StatsController, action='get_group_features',
+                       conditions=dict(method=['GET']))
+
+        uri = path + '/groupdesc/{dpid}'
+        mapper.connect('stats', uri,
+                       controller=StatsController, action='get_group_desc',
+                       conditions=dict(method=['GET']))
+
+        uri = path + '/group/{dpid}'
+        mapper.connect('stats', uri,
+                       controller=StatsController, action='get_group_stats',
+                       conditions=dict(method=['GET']))
+
         uri = path + '/flowentry/{cmd}'
         mapper.connect('stats', uri,
                        controller=StatsController, action='mod_flow_entry',
@@ -371,6 +488,11 @@ class RestStatsApi(app_manager.RyuApp):
                        controller=StatsController, action='mod_meter_entry',
                        conditions=dict(method=['POST']))

+        uri = path + '/groupentry/{cmd}'
+        mapper.connect('stats', uri,
+                       controller=StatsController, action='mod_group_entry',
+                       conditions=dict(method=['POST']))
+
         uri = path + '/experimenter/{dpid}'
         mapper.connect('stats', uri,
                        controller=StatsController, action='send_experimenter',
@@ -428,3 +550,15 @@ class RestStatsApi(app_manager.RyuApp):
     @set_ev_cls(ofp_event.EventOFPMeterConfigStatsReply, MAIN_DISPATCHER)
     def meter_config_stats_reply_handler(self, ev):
         self.stats_reply_handler(ev)
+
+    @set_ev_cls(ofp_event.EventOFPGroupStatsReply, MAIN_DISPATCHER)
+    def group_stats_reply_handler(self, ev):
+        self.stats_reply_handler(ev)
+
+    @set_ev_cls(ofp_event.EventOFPGroupFeaturesStatsReply, MAIN_DISPATCHER)
+    def group_features_stats_reply_handler(self, ev):
+        self.stats_reply_handler(ev)
+
+    @set_ev_cls(ofp_event.EventOFPGroupDescStatsReply, MAIN_DISPATCHER)
+    def group_desc_stats_reply_handler(self, ev):
+        self.stats_reply_handler(ev)
diff --git a/ryu/lib/ofctl_v1_2.py b/ryu/lib/ofctl_v1_2.py
index 660832a..ef76b7b 100644
--- a/ryu/lib/ofctl_v1_2.py
+++ b/ryu/lib/ofctl_v1_2.py
@@ -31,41 +31,176 @@ LOG = logging.getLogger('ryu.lib.ofctl_v1_2')
 DEFAULT_TIMEOUT = 1.0


+def str_to_int(src):
+    if isinstance(src, str):
+        if src.startswith("0x") or src.startswith("0X"):
+            dst = int(src, 16)
+        else:
+            dst = int(src)
+    else:
+        dst = src
+    return dst
+
+
+def to_action(dp, dic):
+    ofp = dp.ofproto
+    parser = dp.ofproto_parser
+
+    result = None
+    action_type = dic.get('type')
+    if action_type == 'OUTPUT':
+        out_port = int(dic.get('port', ofp.OFPP_ANY))
+        max_len = int(dic.get('max_len', ofp.OFPCML_MAX))
+        result = parser.OFPActionOutput(out_port, max_len)
+    elif action_type == 'COPY_TTL_OUT':
+        result = parser.OFPActionCopyTtlOut()
+    elif action_type == 'COPY_TTL_IN':
+        result = parser.OFPActionCopyTtlIn()
+    elif action_type == 'SET_MPLS_TTL':
+        mpls_ttl = int(dic.get('mpls_ttl'))
+        result = parser.OFPActionSetMplsTtl(mpls_ttl)
+    elif action_type == 'DEC_MPLS_TTL':
+        result = parser.OFPActionDecMplsTtl()
+    elif action_type == 'PUSH_VLAN':
+        ethertype = int(dic.get('ethertype'))
+        result = parser.OFPActionPushVlan(ethertype)
+    elif action_type == 'POP_VLAN':
+        result = parser.OFPActionPopVlan()
+    elif action_type == 'PUSH_MPLS':
+        ethertype = int(dic.get('ethertype'))
+        result = parser.OFPActionPushMpls(ethertype)
+    elif action_type == 'POP_MPLS':
+        ethertype = int(dic.get('ethertype'))
+        result = parser.OFPActionPopMpls(ethertype)
+    elif action_type == 'SET_QUEUE':
+        queue_id = int(dic.get('queue_id'))
+        result = parser.OFPActionSetQueue(queue_id)
+    elif action_type == 'GROUP':
+        group_id = int(dic.get('group_id'))
+        result = parser.OFPActionGroup(group_id)
+    elif action_type == 'SET_NW_TTL':
+        nw_ttl = int(dic.get('nw_ttl'))
+        result = parser.OFPActionSetNwTtl(nw_ttl)
+    elif action_type == 'DEC_NW_TTL':
+        result = parser.OFPActionDecNwTtl()
+    elif action_type == 'SET_FIELD':
+        field = dic.get('field')
+        value = dic.get('value')
+        if field == 'eth_dst':
+            field = ofp.OXM_OF_ETH_DST
+            value = mac.haddr_to_bin(str(value))
+        elif field == 'eth_src':
+            field = ofp.OXM_OF_ETH_SRC
+            value = mac.haddr_to_bin(str(value))
+        elif field == 'vlan_vid':
+            field = ofp.OXM_OF_VLAN_VID
+            value = int(value)
+        elif field == 'mpls_label':
+            field = ofp.OXM_OF_MPLS_LABEL
+            value = int(value)
+        else:
+            LOG.debug('Unknown field: %s' % field)
+            return None
+        f = parser.OFPMatchField.make(field, value)
+        result = parser.OFPActionSetField(f)
+    else:
+        LOG.debug('Unknown action type: %s' % action_type)
+
+    return result
+
+
 def to_actions(dp, acts):
     inst = []
+    actions = []
+    ofp = dp.ofproto
+    parser = dp.ofproto_parser

     for a in acts:
-        action_type = a.get('type')
-        if action_type == 'OUTPUT':
-            out_port = int(a.get('port', ofproto_v1_2.OFPP_ANY))
-            max_len = int(a.get('max_len', ofproto_v1_2.OFPCML_MAX))
-            actions = [dp.ofproto_parser.OFPActionOutput(
-                       out_port, max_len=max_len)]
-            inst_type = dp.ofproto.OFPIT_APPLY_ACTIONS
-            inst = [dp.ofproto_parser.OFPInstructionActions(
-                    inst_type, actions)]
+        action = to_action(dp, a)
+        if action is not None:
+            actions.append(action)
         else:
-            LOG.debug('Unknown action type')
+            action_type = a.get('type')
+            if action_type == 'GOTO_TABLE':
+                table_id = int(a.get('table_id'))
+                inst.append(parser.OFPInstructionGotoTable(table_id))
+            elif action_type == 'WRITE_METADATA':
+                metadata = str_to_int(a.get('metadata'))
+                metadata_mask = (str_to_int(a['metadata_mask'])
+                                 if 'metadata_mask' in a
+                                 else parser.UINT64_MAX)
+                inst.append(
+                    parser.OFPInstructionWriteMetadata(
+                        metadata, metadata_mask))
+            else:
+                LOG.debug('Unknown action type: %s' % action_type)

+    inst.append(parser.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS,
+                                             actions))
     return inst


+def action_to_str(act):
+    action_type = act.cls_action_type
+
+    if action_type == ofproto_v1_2.OFPAT_OUTPUT:
+        buf = 'OUTPUT:' + str(act.port)
+    elif action_type == ofproto_v1_2.OFPAT_COPY_TTL_OUT:
+        buf = 'COPY_TTL_OUT'
+    elif action_type == ofproto_v1_2.OFPAT_COPY_TTL_IN:
+        buf = 'COPY_TTL_IN'
+    elif action_type == ofproto_v1_2.OFPAT_SET_MPLS_TTL:
+        buf = 'SET_MPLS_TTL:' + str(act.mpls_ttl)
+    elif action_type == ofproto_v1_2.OFPAT_DEC_MPLS_TTL:
+        buf = 'DEC_MPLS_TTL'
+    elif action_type == ofproto_v1_2.OFPAT_PUSH_VLAN:
+        buf = 'PUSH_VLAN:' + str(act.ethertype)
+    elif action_type == ofproto_v1_2.OFPAT_POP_VLAN:
+        buf = 'POP_VLAN'
+    elif action_type == ofproto_v1_2.OFPAT_PUSH_MPLS:
+        buf = 'PUSH_MPLS:' + str(act.ethertype)
+    elif action_type == ofproto_v1_2.OFPAT_POP_MPLS:
+        buf = 'POP_MPLS'
+    elif action_type == ofproto_v1_2.OFPAT_OFPAT_SET_QUEUE:
+        buf = 'SET_QUEUE:' + str(act.queue_id)
+    elif action_type == ofproto_v1_2.OFPAT_GROUP:
+        buf = 'GROUP:' + str(act.group_id)
+    elif action_type == ofproto_v1_2.OFPAT_SET_NW_TTL:
+        buf = 'SET_NW_TTL:' + str(act.nw_ttl)
+    elif action_type == ofproto_v1_2.OFPAT_DEC_NW_TTL:
+        buf = 'DEC_NW_TTL'
+    elif action_type == ofproto_v1_2.OFPAT_SET_FIELD:
+        buf = 'SET_FIELD: {%s:%s}' % (act.field, act.value)
+    else:
+        buf = 'UNKNOWN'
+    return buf
+
+
 def actions_to_str(instructions):
     actions = []

     for instruction in instructions:
-        if not isinstance(instruction,
-                          ofproto_v1_2_parser.OFPInstructionActions):
-            continue
-        for a in instruction.actions:
-            action_type = a.cls_action_type
+        if isinstance(instruction,
+                      ofproto_v1_2_parser.OFPInstructionActions):
+            for a in instruction.actions:
+                actions.append(action_to_str(a))
+
+        elif isinstance(instruction,
+                        ofproto_v1_2_parser.OFPInstructionGotoTable):
+            buf = 'GOTO_TABLE:' + str(instruction.table_id)
+            actions.append(buf)

-            if action_type == ofproto_v1_2.OFPAT_OUTPUT:
-                buf = 'OUTPUT:' + str(a.port)
-            else:
-                buf = 'UNKNOWN'
+        elif isinstance(instruction,
+                        ofproto_v1_2_parser.OFPInstructionWriteMetadata):
+            buf = ('WRITE_METADATA:0x%x/0x%x' % (instruction.metadata,
+                                                 instruction.metadata_mask)
+                   if instruction.metadata_mask
+                   else 'WRITE_METADATA:0x%x' % instruction.metadata)
             actions.append(buf)

+        else:
+            continue
+
     return actions


@@ -348,6 +483,122 @@ def get_port_stats(dp, waiters):
     return ports


+def get_group_stats(dp, waiters):
+    stats = dp.ofproto_parser.OFPGroupStatsRequest(
+        dp, dp.ofproto.OFPG_ALL, 0)
+    msgs = []
+    send_stats_request(dp, stats, waiters, msgs)
+
+    groups = []
+    for msg in msgs:
+        for stats in msg.body:
+            bucket_counters = []
+            for bucket_counter in stats.bucket_counters:
+                c = {'packet_count': bucket_counter.packet_count,
+                     'byte_count': bucket_counter.byte_count}
+                bucket_counters.append(c)
+            g = {'group_id': stats.group_id,
+                 'ref_count': stats.ref_count,
+                 'packet_count': stats.packet_count,
+                 'byte_count': stats.byte_count,
+                 'bucket_stats': bucket_counters}
+            groups.append(g)
+    groups = {str(dp.id): groups}
+    return groups
+
+
+def get_group_features(dp, waiters):
+
+    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_CHCEKS'}
+    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'}
+
+    stats = dp.ofproto_parser.OFPGroupFeaturesStatsRequest(dp, 0)
+    msgs = []
+    send_stats_request(dp, stats, waiters, msgs)
+
+    features = []
+    for msg in msgs:
+        feature = msg.body
+        types = []
+        for k, v in type_convert.items():
+            if (1 << k) & feature.types:
+                types.append(v)
+        capabilities = []
+        for k, v in cap_convert.items():
+            if k & feature.capabilities:
+                capabilities.append(v)
+        max_groups = []
+        for k, v in type_convert.items():
+            max_groups.append({v: feature.max_groups[k]})
+        actions = []
+        for k1, v1 in type_convert.items():
+            acts = []
+            for k2, v2 in act_convert.items():
+                if (1 << k2) & feature.actions[k1]:
+                    acts.append(v2)
+            actions.append({v1: acts})
+        f = {'types': types,
+             'capabilities': capabilities,
+             'max_groups': max_groups,
+             'actions': actions}
+        features.append(f)
+    features = {str(dp.id): features}
+    return features
+
+
+def get_group_desc(dp, waiters):
+
+    type_convert = {dp.ofproto.OFPGT_ALL: 'ALL',
+                    dp.ofproto.OFPGT_SELECT: 'SELECT',
+                    dp.ofproto.OFPGT_INDIRECT: 'INDIRECT',
+                    dp.ofproto.OFPGT_FF: 'FF'}
+
+    stats = dp.ofproto_parser.OFPGroupDescStatsRequest(dp, 0)
+    msgs = []
+    send_stats_request(dp, stats, waiters, msgs)
+
+    descs = []
+    for msg in msgs:
+        for stats in msg.body:
+            buckets = []
+            for bucket in stats.buckets:
+                actions = []
+                for action in bucket.actions:
+                    actions.append(action_to_str(action))
+                b = {'weight': bucket.weight,
+                     'watch_port': bucket.watch_port,
+                     'watch_group': bucket.watch_group,
+                     'actions': actions}
+                buckets.append(b)
+            d = {'type': type_convert.get(stats.type),
+                 'group_id': stats.group_id,
+                 'buckets': buckets}
+            descs.append(d)
+    descs = {str(dp.id): descs}
+    return descs
+
+
 def mod_flow_entry(dp, flow, cmd):
     cookie = int(flow.get('cookie', 0))
     cookie_mask = int(flow.get('cookie_mask', 0))
@@ -370,6 +621,38 @@ def mod_flow_entry(dp, flow, cmd):
     dp.send_msg(flow_mod)


+def mod_group_entry(dp, group, cmd):
+
+    type_convert = {'ALL': dp.ofproto.OFPGT_ALL,
+                    'SELECT': dp.ofproto.OFPGT_SELECT,
+                    'INDIRECT': dp.ofproto.OFPGT_INDIRECT,
+                    'FF': dp.ofproto.OFPGT_FF}
+
+    type_ = type_convert.get(group.get('type'))
+    if not type_:
+        LOG.debug('Unknown type: %s', group.get('type'))
+
+    group_id = int(group.get('group_id', 0))
+
+    buckets = []
+    for bucket in group.get('buckets', []):
+        weight = int(bucket.get('weight', 0))
+        watch_port = int(bucket.get('watch_port', dp.ofproto.OFPP_ANY))
+        watch_group = int(bucket.get('watch_group', dp.ofproto.OFPG_ANY))
+        actions = []
+        for dic in bucket.get('actions', []):
+            action = to_action(dp, dic)
+            if action is not None:
+                actions.append(action)
+        buckets.append(dp.ofproto_parser.OFPBucket(
+            weight, watch_port, watch_group, actions))
+
+    group_mod = dp.ofproto_parser.OFPGroupMod(
+        dp, cmd, type_, group_id, buckets)
+
+    dp.send_msg(group_mod)
+
+
 def send_experimenter(dp, exp):
     experimenter = exp.get('experimenter', 0)
     exp_type = exp.get('exp_type', 0)
diff --git a/ryu/lib/ofctl_v1_3.py b/ryu/lib/ofctl_v1_3.py
index f983624..99b55d6 100644
--- a/ryu/lib/ofctl_v1_3.py
+++ b/ryu/lib/ofctl_v1_3.py
@@ -42,6 +42,78 @@ def str_to_int(src):
     return dst


+def to_action(dp, dic):
+    ofp = dp.ofproto
+    parser = dp.ofproto_parser
+
+    result = None
+    action_type = dic.get('type')
+    if action_type == 'OUTPUT':
+        out_port = int(dic.get('port', ofp.OFPP_ANY))
+        max_len = int(dic.get('max_len', ofp.OFPCML_MAX))
+        result = parser.OFPActionOutput(out_port, max_len)
+    elif action_type == 'COPY_TTL_OUT':
+        result = parser.OFPActionCopyTtlOut()
+    elif action_type == 'COPY_TTL_IN':
+        result = parser.OFPActionCopyTtlIn()
+    elif action_type == 'SET_MPLS_TTL':
+        mpls_ttl = int(dic.get('mpls_ttl'))
+        result = parser.OFPActionSetMplsTtl(mpls_ttl)
+    elif action_type == 'DEC_MPLS_TTL':
+        result = parser.OFPActionDecMplsTtl()
+    elif action_type == 'PUSH_VLAN':
+        ethertype = int(dic.get('ethertype'))
+        result = parser.OFPActionPushVlan(ethertype)
+    elif action_type == 'POP_VLAN':
+        result = parser.OFPActionPopVlan()
+    elif action_type == 'PUSH_MPLS':
+        ethertype = int(dic.get('ethertype'))
+        result = parser.OFPActionPushMpls(ethertype)
+    elif action_type == 'POP_MPLS':
+        ethertype = int(dic.get('ethertype'))
+        result = parser.OFPActionPopMpls(ethertype)
+    elif action_type == 'SET_QUEUE':
+        queue_id = int(dic.get('queue_id'))
+        result = parser.OFPActionSetQueue(queue_id)
+    elif action_type == 'GROUP':
+        group_id = int(dic.get('group_id'))
+        result = parser.OFPActionGroup(group_id)
+    elif action_type == 'SET_NW_TTL':
+        nw_ttl = int(dic.get('nw_ttl'))
+        result = parser.OFPActionSetNwTtl(nw_ttl)
+    elif action_type == 'DEC_NW_TTL':
+        result = parser.OFPActionDecNwTtl()
+    elif action_type == 'SET_FIELD':
+        field = dic.get('field')
+        value = dic.get('value')
+        if field == 'eth_dst':
+            field = ofp.OXM_OF_ETH_DST
+            value = mac.haddr_to_bin(str(value))
+        elif field == 'eth_src':
+            field = ofp.OXM_OF_ETH_SRC
+            value = mac.haddr_to_bin(str(value))
+        elif field == 'vlan_vid':
+            field = ofp.OXM_OF_VLAN_VID
+            value = int(value)
+        elif field == 'mpls_label':
+            field = ofp.OXM_OF_MPLS_LABEL
+            value = int(value)
+        else:
+            LOG.debug('Unknown field: %s' % field)
+            return None
+        f = parser.OFPMatchField.make(field, value)
+        result = parser.OFPActionSetField(f)
+    elif action_type == 'PUSH_PBB':
+        ethertype = int(dic.get('ethertype'))
+        result = parser.OFPActionPushPbb(ethertype)
+    elif action_type == 'POP_PBB':
+        result = parser.OFPActionPopPbb()
+    else:
+        LOG.debug('Unknown action type: %s' % action_type)
+
+    return result
+
+
 def to_actions(dp, acts):
     inst = []
     actions = []
@@ -49,88 +121,73 @@ def to_actions(dp, acts):
     parser = dp.ofproto_parser

     for a in acts:
-        action_type = a.get('type')
-        if action_type == 'OUTPUT':
-            out_port = int(a.get('port', ofproto_v1_3.OFPP_ANY))
-            max_len = int(a.get('max_len', ofproto_v1_3.OFPCML_MAX))
-            actions.append((parser.OFPActionOutput(out_port,
-                                                   max_len)))
-        elif action_type == 'COPY_TTL_OUT':
-            actions.append(parser.OFPActionCopyTtlOut())
-        elif action_type == 'COPY_TTL_IN':
-            actions.append(parser.OFPActionCopyTtlIn())
-        elif action_type == 'SET_MPLS_TTL':
-            mpls_ttl = int(a.get('mpls_ttl'))
-            actions.append((parser.OFPActionSetMplsTtl(mpls_ttl)))
-        elif action_type == 'DEC_MPLS_TTL':
-            actions.append((parser.OFPActionDecMplsTtl()))
-        elif action_type == 'PUSH_VLAN':
-            ethertype = int(a.get('ethertype'))
-            actions.append((parser.OFPActionPushVlan(ethertype)))
-        elif action_type == 'POP_VLAN':
-            actions.append(parser.OFPActionPopVlan())
-        elif action_type == 'PUSH_MPLS':
-            ethertype = int(a.get('ethertype'))
-            actions.append(parser.OFPActionPushMpls(ethertype))
-        elif action_type == 'POP_MPLS':
-            ethertype = int(a.get('ethertype'))
-            actions.append(parser.OFPActionPopMpls(ethertype))
-        elif action_type == 'SET_QUEUE':
-            queue_id = int(a.get('queue_id'))
-            actions.append(parser.OFPActionSetQueue(queue_id))
-        elif action_type == 'GROUP':
-            pass
-        elif action_type == 'SET_NW_TTL':
-            nw_ttl = int(a.get('nw_ttl'))
-            actions.append(parser.OFPActionSetNwTtl(nw_ttl))
-        elif action_type == 'DEC_NW_TTL':
-            actions.append(parser.OFPActionDecNwTtl())
-        elif action_type == 'SET_FIELD':
-            field = a.get('field')
-            value = a.get('value')
-            if field == 'eth_dst':
-                field = ofp.OXM_OF_ETH_DST
-                value = mac.haddr_to_bin(str(value))
-            elif field == 'eth_src':
-                field = ofp.OXM_OF_ETH_SRC
-                value = mac.haddr_to_bin(str(value))
-            elif field == 'vlan_vid':
-                field = ofp.OXM_OF_VLAN_VID
-                value = int(value)
-            elif field == 'mpls_label':
-                field = ofp.OXM_OF_MPLS_LABEL
-                value = int(value)
-            else:
-                LOG.debug('Unknown field: %s' % field)
-                continue
-            f = parser.OFPMatchField.make(field, value)
-            actions.append(parser.OFPActionSetField(f))
-        elif action_type == 'PUSH_PBB':
-            ethertype = int(a.get('ethertype'))
-            actions.append(parser.OFPActionPushPbb(ethertype))
-        elif action_type == 'POP_PBB':
-            actions.append(parser.OFPActionPopPbb())
-        elif action_type == 'GOTO_TABLE':
-            table_id = int(a.get('table_id'))
-            inst.append(parser.OFPInstructionGotoTable(table_id))
-        elif action_type == 'WRITE_METADATA':
-            metadata = str_to_int(a.get('metadata'))
-            metadata_mask = (str_to_int(a['metadata_mask'])
-                             if 'metadata_mask' in a
-                             else ofproto_v1_3_parser.UINT64_MAX)
-            inst.append(
-                parser.OFPInstructionWriteMetadata(metadata, metadata_mask))
-        elif action_type == 'METER':
-            meter_id = int(a.get('meter_id'))
-            inst.append(parser.OFPInstructionMeter(meter_id))
+        action = to_action(dp, a)
+        if action is not None:
+            actions.append(action)
         else:
-            LOG.debug('Unknown action type: %s' % action_type)
+            action_type = a.get('type')
+            if action_type == 'GOTO_TABLE':
+                table_id = int(a.get('table_id'))
+                inst.append(parser.OFPInstructionGotoTable(table_id))
+            elif action_type == 'WRITE_METADATA':
+                metadata = str_to_int(a.get('metadata'))
+                metadata_mask = (str_to_int(a['metadata_mask'])
+                                 if 'metadata_mask' in a
+                                 else parser.UINT64_MAX)
+                inst.append(
+                    parser.OFPInstructionWriteMetadata(
+                        metadata, metadata_mask))
+            elif action_type == 'METER':
+                meter_id = int(a.get('meter_id'))
+                inst.append(parser.OFPInstructionMeter(meter_id))
+            else:
+                LOG.debug('Unknown action type: %s' % action_type)

     inst.append(parser.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS,
                                              actions))
     return inst


+def action_to_str(act):
+    action_type = act.cls_action_type
+
+    if action_type == ofproto_v1_3.OFPAT_OUTPUT:
+        buf = 'OUTPUT:' + str(act.port)
+    elif action_type == ofproto_v1_3.OFPAT_COPY_TTL_OUT:
+        buf = 'COPY_TTL_OUT'
+    elif action_type == ofproto_v1_3.OFPAT_COPY_TTL_IN:
+        buf = 'COPY_TTL_IN'
+    elif action_type == ofproto_v1_3.OFPAT_SET_MPLS_TTL:
+        buf = 'SET_MPLS_TTL:' + str(act.mpls_ttl)
+    elif action_type == ofproto_v1_3.OFPAT_DEC_MPLS_TTL:
+        buf = 'DEC_MPLS_TTL'
+    elif action_type == ofproto_v1_3.OFPAT_PUSH_VLAN:
+        buf = 'PUSH_VLAN:' + str(act.ethertype)
+    elif action_type == ofproto_v1_3.OFPAT_POP_VLAN:
+        buf = 'POP_VLAN'
+    elif action_type == ofproto_v1_3.OFPAT_PUSH_MPLS:
+        buf = 'PUSH_MPLS:' + str(act.ethertype)
+    elif action_type == ofproto_v1_3.OFPAT_POP_MPLS:
+        buf = 'POP_MPLS'
+    elif action_type == ofproto_v1_3.OFPAT_OFPAT_SET_QUEUE:
+        buf = 'SET_QUEUE:' + str(act.queue_id)
+    elif action_type == ofproto_v1_3.OFPAT_GROUP:
+        pass
+    elif action_type == ofproto_v1_3.OFPAT_SET_NW_TTL:
+        buf = 'SET_NW_TTL:' + str(act.nw_ttl)
+    elif action_type == ofproto_v1_3.OFPAT_DEC_NW_TTL:
+        buf = 'DEC_NW_TTL'
+    elif action_type == ofproto_v1_3.OFPAT_SET_FIELD:
+        buf = 'SET_FIELD: {%s:%s}' % (act.field, act.value)
+    elif action_type == ofproto_v1_3.OFPAT_PUSH_PBB:
+        buf = 'PUSH_PBB:' + str(act.ethertype)
+    elif action_type == ofproto_v1_3.OFPAT_POP_PBB:
+        buf = 'POP_PBB'
+    else:
+        buf = 'UNKNOWN'
+    return buf
+
+
 def actions_to_str(instructions):
     actions = []

@@ -138,43 +195,7 @@ def actions_to_str(instructions):
         if isinstance(instruction,
                       ofproto_v1_3_parser.OFPInstructionActions):
             for a in instruction.actions:
-                action_type = a.cls_action_type
-
-                if action_type == ofproto_v1_3.OFPAT_OUTPUT:
-                    buf = 'OUTPUT:' + str(a.port)
-                elif action_type == ofproto_v1_3.OFPAT_COPY_TTL_OUT:
-                    buf = 'COPY_TTL_OUT'
-                elif action_type == ofproto_v1_3.OFPAT_COPY_TTL_IN:
-                    buf = 'COPY_TTL_IN'
-                elif action_type == ofproto_v1_3.OFPAT_SET_MPLS_TTL:
-                    buf = 'SET_MPLS_TTL:' + str(a.mpls_ttl)
-                elif action_type == ofproto_v1_3.OFPAT_DEC_MPLS_TTL:
-                    buf = 'DEC_MPLS_TTL'
-                elif action_type == ofproto_v1_3.OFPAT_PUSH_VLAN:
-                    buf = 'PUSH_VLAN:' + str(a.ethertype)
-                elif action_type == ofproto_v1_3.OFPAT_POP_VLAN:
-                    buf = 'POP_VLAN'
-                elif action_type == ofproto_v1_3.OFPAT_PUSH_MPLS:
-                    buf = 'PUSH_MPLS:' + str(a.ethertype)
-                elif action_type == ofproto_v1_3.OFPAT_POP_MPLS:
-                    buf = 'POP_MPLS'
-                elif action_type == ofproto_v1_3.OFPAT_OFPAT_SET_QUEUE:
-                    buf = 'SET_QUEUE:' + str(a.queue_id)
-                elif action_type == ofproto_v1_3.OFPAT_GROUP:
-                    pass
-                elif action_type == ofproto_v1_3.OFPAT_SET_NW_TTL:
-                    buf = 'SET_NW_TTL:' + str(a.nw_ttl)
-                elif action_type == ofproto_v1_3.OFPAT_DEC_NW_TTL:
-                    buf = 'DEC_NW_TTL'
-                elif action_type == ofproto_v1_3.OFPAT_SET_FIELD:
-                    buf = 'SET_FIELD: {%s:%s}' % (a.field, a.value)
-                elif action_type == ofproto_v1_3.OFPAT_PUSH_PBB:
-                    buf = 'PUSH_PBB:' + str(a.ethertype)
-                elif action_type == ofproto_v1_3.OFPAT_POP_PBB:
-                    buf = 'POP_PBB'
-                else:
-                    buf = 'UNKNOWN'
-                actions.append(buf)
+                actions.append(action_to_str(a))

         elif isinstance(instruction,
                         ofproto_v1_3_parser.OFPInstructionGotoTable):
@@ -581,6 +602,126 @@ def get_meter_config(dp, waiters):
     return configs


+def get_group_stats(dp, waiters):
+    stats = dp.ofproto_parser.OFPGroupStatsRequest(
+        dp, 0, dp.ofproto.OFPG_ALL)
+    msgs = []
+    send_stats_request(dp, stats, waiters, msgs)
+
+    groups = []
+    for msg in msgs:
+        for stats in msg.body:
+            bucket_stats = []
+            for bucket_stat in stats.bucket_stats:
+                c = {'packet_count': bucket_stat.packet_count,
+                     'byte_count': bucket_stat.byte_count}
+                bucket_stats.append(c)
+            g = {'group_id': stats.group_id,
+                 'ref_count': stats.ref_count,
+                 'packet_count': stats.packet_count,
+                 'byte_count': stats.byte_count,
+                 'duration_sec': stats.duration_sec,
+                 'duration_nsec': stats.duration_nsec,
+                 'bucket_stats': bucket_stats}
+            groups.append(g)
+    groups = {str(dp.id): groups}
+    return groups
+
+
+def get_group_features(dp, waiters):
+
+    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_CHCEKS'}
+    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'}
+
+    stats = dp.ofproto_parser.OFPGroupFeaturesStatsRequest(dp, 0)
+    msgs = []
+    send_stats_request(dp, stats, waiters, msgs)
+
+    features = []
+    for msg in msgs:
+        feature = msg.body
+        types = []
+        for k, v in type_convert.items():
+            if (1 << k) & feature.types:
+                types.append(v)
+        capabilities = []
+        for k, v in cap_convert.items():
+            if k & feature.capabilities:
+                capabilities.append(v)
+        max_groups = []
+        for k, v in type_convert.items():
+            max_groups.append({v: feature.max_groups[k]})
+        actions = []
+        for k1, v1 in type_convert.items():
+            acts = []
+            for k2, v2 in act_convert.items():
+                if (1 << k2) & feature.actions[k1]:
+                    acts.append(v2)
+            actions.append({v1: acts})
+        f = {'types': types,
+             'capabilities': capabilities,
+             'max_groups': max_groups,
+             'actions': actions}
+        features.append(f)
+    features = {str(dp.id): features}
+    return features
+
+
+def get_group_desc(dp, waiters):
+
+    type_convert = {dp.ofproto.OFPGT_ALL: 'ALL',
+                    dp.ofproto.OFPGT_SELECT: 'SELECT',
+                    dp.ofproto.OFPGT_INDIRECT: 'INDIRECT',
+                    dp.ofproto.OFPGT_FF: 'FF'}
+
+    stats = dp.ofproto_parser.OFPGroupDescStatsRequest(dp, 0)
+    msgs = []
+    send_stats_request(dp, stats, waiters, msgs)
+
+    descs = []
+    for msg in msgs:
+        for stats in msg.body:
+            buckets = []
+            for bucket in stats.buckets:
+                actions = []
+                for action in bucket.actions:
+                    actions.append(action_to_str(action))
+                b = {'weight': bucket.weight,
+                     'watch_port': bucket.watch_port,
+                     'watch_group': bucket.watch_group,
+                     'actions': actions}
+                buckets.append(b)
+            d = {'type': type_convert.get(stats.type),
+                 'group_id': stats.group_id,
+                 'buckets': buckets}
+            descs.append(d)
+    descs = {str(dp.id): descs}
+    return descs
+
+
 def mod_flow_entry(dp, flow, cmd):
     cookie = int(flow.get('cookie', 0))
     cookie_mask = int(flow.get('cookie_mask', 0))
@@ -643,6 +784,38 @@ def mod_meter_entry(dp, flow, cmd):
     dp.send_msg(meter_mod)


+def mod_group_entry(dp, group, cmd):
+
+    type_convert = {'ALL': dp.ofproto.OFPGT_ALL,
+                    'SELECT': dp.ofproto.OFPGT_SELECT,
+                    'INDIRECT': dp.ofproto.OFPGT_INDIRECT,
+                    'FF': dp.ofproto.OFPGT_FF}
+
+    type_ = type_convert.get(group.get('type'))
+    if not type_:
+        LOG.debug('Unknown type: %s', group.get('type'))
+
+    group_id = int(group.get('group_id', 0))
+
+    buckets = []
+    for bucket in group.get('buckets', []):
+        weight = int(bucket.get('weight', 0))
+        watch_port = int(bucket.get('watch_port', dp.ofproto.OFPP_ANY))
+        watch_group = int(bucket.get('watch_group', dp.ofproto.OFPG_ANY))
+        actions = []
+        for dic in bucket.get('actions', []):
+            action = to_action(dp, dic)
+            if action is not None:
+                actions.append(action)
+        buckets.append(dp.ofproto_parser.OFPBucket(
+            weight, watch_port, watch_group, actions))
+
+    group_mod = dp.ofproto_parser.OFPGroupMod(
+        dp, cmd, type_, group_id, buckets)
+
+    dp.send_msg(group_mod)
+
+
 def send_experimenter(dp, exp):
     experimenter = exp.get('experimenter', 0)
     exp_type = exp.get('exp_type', 0)
-- 
1.7.10.4


------------------------------------------------------------------------------
Rapidly troubleshoot problems before they affect your business. Most IT 
organizations don't have a clear picture of how application performance 
affects their revenue. With AppDynamics, you get 100% visibility into your 
Java,.NET, & PHP application. Start your 15-day FREE TRIAL of AppDynamics Pro!
http://pubads.g.doubleclick.net/gampad/clk?id=84349831&iu=/4140/ostg.clktrk
_______________________________________________
Ryu-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/ryu-devel

Reply via email to