From 8b2097b35258dcd5755ea0e08efb1ee2da688587 Mon Sep 17 00:00:00 2001 From: Yuichi Ito Date: Wed, 8 Jan 2014 14:29:21 +0900 Subject: [PATCH] ofctl_rest: support group-related messages 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 Signed-off-by: FUJITA Tomonori --- 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 e108b036..9fbce30d 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/ # +# get group features stats of the switch +# GET /stats/groupfeatures/ +# +# get groups desc stats of the switch +# GET /stats/groupdesc/ +# +# get groups stats of the switch +# GET /stats/group/ +# +# ## 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/ @@ -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 660832ab..ef76b7b9 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)) - if action_type == ofproto_v1_2.OFPAT_OUTPUT: - buf = 'OUTPUT:' + str(a.port) - else: - buf = 'UNKNOWN' + elif isinstance(instruction, + ofproto_v1_2_parser.OFPInstructionGotoTable): + buf = 'GOTO_TABLE:' + str(instruction.table_id) actions.append(buf) + 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 f9836246..99b55d6e 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)