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