summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorYuichi Ito <ito.yuichi0@gmail.com>2013-12-20 11:43:01 +0900
committerFUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>2013-12-20 12:25:15 +0900
commitc380405c1909f35e0f6ac4807fe173afaa60f779 (patch)
tree7c7fa258889a7224b914370da305abe31dca19a8
parentb5bc06c6545657021b1b071dbd958e97c78d18c1 (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.py86
-rw-r--r--ryu/lib/ofctl_v1_3.py90
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)