diff options
author | Yuichi Ito <ito.yuichi0@gmail.com> | 2013-12-20 11:43:01 +0900 |
---|---|---|
committer | FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp> | 2013-12-20 12:25:15 +0900 |
commit | c380405c1909f35e0f6ac4807fe173afaa60f779 (patch) | |
tree | 7c7fa258889a7224b914370da305abe31dca19a8 | |
parent | b5bc06c6545657021b1b071dbd958e97c78d18c1 (diff) |
ofctl_v1_3: support meter
Support meter in FlowMod instructions and support MeterMod, MeterStats, MeterFeatures.
see following examples.
FlowMod with the meter instruction:
flow = {'match': {},
'actions': [{'type': 'METER',
'meter_id': 1}]
curl -X POST -d '{"dpid": 1, "match": {}, "actions": [{"type": "METER", "meter_id": 1}]}' http://localhost:8080/stats/flowentry/add
MeterMod:
meter = {'meter_id': 1,
'flags': 'KBPS',
'bands': [{'type': 'DROP',
'rate': 1000}]}
curl -X POST -d '{"dpid": 1, "meter_id": 1, "flags": "KBPS", "bands": [{"type": "DROP", "rate": 1000}]}' http://localhost:8080/stats/meterentry/add
NOTE: flags: 'KBPS', 'PKTPS', 'BURST', 'STATS'
type: 'DROP', 'REMARK', 'EXPERIMENTER'
MeterStats:
curl http://localhost:8080/stats/meter/1
MetetFeatures:
curl http://localhost:8080/stats/meterfeatures/1
Signed-off-by: Yuichi Ito <ito.yuichi0@gmail.com>
Signed-off-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
-rw-r--r-- | ryu/app/ofctl_rest.py | 86 | ||||
-rw-r--r-- | ryu/lib/ofctl_v1_3.py | 90 |
2 files changed, 176 insertions, 0 deletions
diff --git a/ryu/app/ofctl_rest.py b/ryu/app/ofctl_rest.py index 9840b0f0..907bed58 100644 --- a/ryu/app/ofctl_rest.py +++ b/ryu/app/ofctl_rest.py @@ -48,6 +48,12 @@ LOG = logging.getLogger('ryu.app.ofctl_rest') # get ports stats of the switch # GET /stats/port/<dpid> # +# get meter features stats of the switch +# GET /stats/meterfeatures/<dpid> +# +# get meters stats of the switch +# GET /stats/meter/<dpid> +# ## Update the switch stats # # add a flow entry @@ -62,6 +68,14 @@ LOG = logging.getLogger('ryu.app.ofctl_rest') # delete all flow entries of the switch # DELETE /stats/flowentry/clear/<dpid> # +# add a meter entry +# POST /stats/meterentry/add +# +# modify a meter entry +# POST /stats/meterentry/modify +# +# delete a meter entry +# POST /stats/meterentry/delete class StatsController(ControllerBase): @@ -123,6 +137,34 @@ class StatsController(ControllerBase): body = json.dumps(ports) return (Response(content_type='application/json', body=body)) + def get_meter_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_3.OFP_VERSION: + meters = ofctl_v1_3.get_meter_features(dp, self.waiters) + else: + LOG.debug('Unsupported OF protocol') + return Response(status=501) + + body = json.dumps(meters) + return (Response(content_type='application/json', body=body)) + + def get_meter_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_3.OFP_VERSION: + meters = ofctl_v1_3.get_meter_stats(dp, self.waiters) + else: + LOG.debug('Unsupported OF protocol') + return Response(status=501) + + body = json.dumps(meters) + return (Response(content_type='application/json', body=body)) + def mod_flow_entry(self, req, cmd, **_kwargs): try: flow = eval(req.body) @@ -169,6 +211,35 @@ class StatsController(ControllerBase): return Response(status=200) + def mod_meter_entry(self, req, cmd, **_kwargs): + try: + flow = eval(req.body) + except SyntaxError: + LOG.debug('invalid syntax %s', req.body) + return Response(status=400) + + dpid = flow.get('dpid') + dp = self.dpset.get(int(dpid)) + if dp is None: + return Response(status=404) + + if cmd == 'add': + cmd = dp.ofproto.OFPMC_ADD + elif cmd == 'modify': + cmd = dp.ofproto.OFPMC_MODIFY + elif cmd == 'delete': + cmd = dp.ofproto.OFPMC_DELETE + else: + return Response(status=404) + + if dp.ofproto.OFP_VERSION == ofproto_v1_3.OFP_VERSION: + ofctl_v1_3.mod_meter_entry(dp, flow, cmd) + else: + LOG.debug('Unsupported OF protocol') + return Response(status=501) + + return Response(status=200) + class RestStatsApi(app_manager.RyuApp): OFP_VERSIONS = [ofproto_v1_0.OFP_VERSION, @@ -210,6 +281,16 @@ class RestStatsApi(app_manager.RyuApp): controller=StatsController, action='get_port_stats', conditions=dict(method=['GET'])) + uri = path + '/meterfeatures/{dpid}' + mapper.connect('stats', uri, + controller=StatsController, action='get_meter_features', + conditions=dict(method=['GET'])) + + uri = path + '/meter/{dpid}' + mapper.connect('stats', uri, + controller=StatsController, action='get_meter_stats', + conditions=dict(method=['GET'])) + uri = path + '/flowentry/{cmd}' mapper.connect('stats', uri, controller=StatsController, action='mod_flow_entry', @@ -220,6 +301,11 @@ class RestStatsApi(app_manager.RyuApp): controller=StatsController, action='delete_flow_entry', conditions=dict(method=['DELETE'])) + uri = path + '/meterentry/{cmd}' + mapper.connect('stats', uri, + controller=StatsController, action='mod_meter_entry', + conditions=dict(method=['POST'])) + def stats_reply_handler(self, ev): msg = ev.msg dp = msg.datapath diff --git a/ryu/lib/ofctl_v1_3.py b/ryu/lib/ofctl_v1_3.py index 996f6059..78225696 100644 --- a/ryu/lib/ofctl_v1_3.py +++ b/ryu/lib/ofctl_v1_3.py @@ -118,6 +118,9 @@ def to_actions(dp, acts): 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)) else: LOG.debug('Unknown action type: %s' % action_type) @@ -184,6 +187,11 @@ def actions_to_str(instructions): else 'WRITE_METADATA:0x%x' % instruction.metadata) actions.append(buf) + elif isinstance(instruction, + ofproto_v1_3_parser.OFPInstructionMeter): + buf = 'METER:' + str(instruction.meter_id) + actions.append(buf) + else: continue @@ -429,6 +437,48 @@ def get_port_stats(dp, waiters): return ports +def get_meter_stats(dp, waiters): + stats = dp.ofproto_parser.OFPMeterStatsRequest( + dp, 0, dp.ofproto.OFPM_ALL) + msgs = [] + send_stats_request(dp, stats, waiters, msgs) + + meters = [] + for msg in msgs: + for stats in msg.body: + bands = [] + for band in stats.band_stats: + b = {'packet_band_count': band.packet_band_count, + 'byte_band_count': band.byte_band_count} + bands.append(b) + s = {'meter_id': stats.meter_id, + 'len': stats.len, + 'flow_count': stats.flow_count, + 'packet_in_count': stats.packet_in_count, + 'byte_in_count': stats.byte_in_count, + 'band_stats': bands} + meters.append(s) + meters = {str(dp.id): meters} + return meters + + +def get_meter_features(dp, waiters): + stats = dp.ofproto_parser.OFPMeterFeaturesStatsRequest(dp, 0) + msgs = [] + send_stats_request(dp, stats, waiters, msgs) + + features = [] + for msg in msgs: + for feature in msg.body: + f = {'max_meter': msg.body.max_meter, + 'band_type': msg.body.band_type, + 'max_bands': msg.body.max_bands, + 'max_color': msg.body.max_color} + features.append(f) + features = {str(dp.id): features} + return features + + def mod_flow_entry(dp, flow, cmd): cookie = int(flow.get('cookie', 0)) cookie_mask = int(flow.get('cookie_mask', 0)) @@ -449,3 +499,43 @@ def mod_flow_entry(dp, flow, cmd): flags, match, inst) dp.send_msg(flow_mod) + + +def mod_meter_entry(dp, flow, cmd): + + flags_convert = {'KBPS': dp.ofproto.OFPMF_KBPS, + 'PKTPS': dp.ofproto.OFPMF_PKTPS, + 'BURST': dp.ofproto.OFPMF_BURST, + 'STATS': dp.ofproto.OFPMF_STATS} + + flags = flags_convert.get(flow.get('flags')) + if not flags: + LOG.debug('Unknown flags: %s', flow.get('flags')) + + meter_id = int(flow.get('meter_id', 0)) + + bands = [] + for band in flow.get('bands', []): + band_type = band.get('type') + rate = int(band.get('rate', 0)) + burst_size = int(band.get('burst_size', 0)) + if band_type == 'DROP': + bands.append( + dp.ofproto_parser.OFPMeterBandDrop(rate, burst_size)) + elif band_type == 'REMARK': + prec_level = int(band.get('prec_level', 0)) + bands.append( + dp.ofproto_parser.OFPMeterBandDscpRemark( + rate, burst_size, prec_level)) + elif band_type == 'EXPERIMENTER': + experimenter = int(band.get('experimenter', 0)) + bands.append( + dp.ofproto_parser.OFPMeterBandExperimenter( + rate, burst_size, experimenter)) + else: + LOG.debug('Unknown band type: %s', band_type) + + meter_mod = dp.ofproto_parser.OFPMeterMod( + dp, cmd, flags, meter_id, bands) + + dp.send_msg(meter_mod) |