mirror of
https://github.com/faucetsdn/ryu.git
synced 2026-05-05 20:36:10 +02:00
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>
This commit is contained in:
parent
b5bc06c654
commit
c380405c19
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user