From a4e9abfcbc342a662406ba2c706970fa25986798 Mon Sep 17 00:00:00 2001 From: Yusuke Iwase Date: Mon, 19 Jan 2015 10:56:23 +0900 Subject: [PATCH] ofctl_rest: support OFPAggregateStats message this patch makes ofctl_rest enable use of OFPAggregateStats message. Get aggregate flow stats: usage) URI: /stats/aggregateflow/ method: GET e.g.) $ curl -X GET http://localhost:8080/stats/aggregateflow/1 { "1": [ { "packet_count": 18, "byte_count": 756, "flow_count": 3 } ] } Get aggregate flow stats filtered by fields: usage) URI: /stats/aggregateflow/ method: POST the message body is as follows. table_id Table ID (int) out_port Require matching entries to include this as an output port (int) out_group Require matching entries to include this as an output group (int) cookie Require matching entries to contain this cookie value (int) cookie_mask Mask used to restrict the cookie bits that must match (int) match Fields to match (dict) e.g.) $ curl -X POST -d '{ "table_id": 0, "out_port": 2, "cookie": 1, "cookie_mask": 1, "match":{ "in_port":1 } }' http://localhost:8080/stats/aggregateflow/1 Signed-off-by: IWASE Yusuke Signed-off-by: FUJITA Tomonori --- ryu/app/ofctl_rest.py | 40 ++++++++++++++++++++++++++++++++++++++++ ryu/lib/ofctl_v1_0.py | 24 ++++++++++++++++++++++++ ryu/lib/ofctl_v1_2.py | 26 ++++++++++++++++++++++++++ ryu/lib/ofctl_v1_3.py | 28 ++++++++++++++++++++++++++++ 4 files changed, 118 insertions(+) diff --git a/ryu/app/ofctl_rest.py b/ryu/app/ofctl_rest.py index 8efc0a45..c398d941 100644 --- a/ryu/app/ofctl_rest.py +++ b/ryu/app/ofctl_rest.py @@ -52,6 +52,12 @@ LOG = logging.getLogger('ryu.app.ofctl_rest') # get flows stats of the switch filtered by the fields # POST /stats/flow/ # +# get aggregate flows stats of the switch +# GET /stats/aggregateflow/ +# +# get aggregate flows stats of the switch filtered by the fields +# POST /stats/aggregateflow/ +# # get ports stats of the switch # GET /stats/port/ # @@ -181,6 +187,33 @@ class StatsController(ControllerBase): body = json.dumps(flows) return (Response(content_type='application/json', body=body)) + def get_aggregate_flow_stats(self, req, dpid, **_kwargs): + if req.body == '': + flow = {} + else: + try: + flow = ast.literal_eval(req.body) + except SyntaxError: + LOG.debug('invalid syntax %s', req.body) + return Response(status=400) + + dp = self.dpset.get(int(dpid)) + if dp is None: + return Response(status=404) + + if dp.ofproto.OFP_VERSION == ofproto_v1_0.OFP_VERSION: + flows = ofctl_v1_0.get_aggregate_flow_stats(dp, self.waiters, flow) + elif dp.ofproto.OFP_VERSION == ofproto_v1_2.OFP_VERSION: + flows = ofctl_v1_2.get_aggregate_flow_stats(dp, self.waiters, flow) + elif dp.ofproto.OFP_VERSION == ofproto_v1_3.OFP_VERSION: + flows = ofctl_v1_3.get_aggregate_flow_stats(dp, self.waiters, flow) + else: + LOG.debug('Unsupported OF protocol') + return Response(status=501) + + body = json.dumps(flows) + return Response(content_type='application/json', body=body) + def get_port_stats(self, req, dpid, **_kwargs): dp = self.dpset.get(int(dpid)) if dp is None: @@ -572,6 +605,12 @@ class RestStatsApi(app_manager.RyuApp): controller=StatsController, action='get_flow_stats', conditions=dict(method=['GET', 'POST'])) + uri = path + '/aggregateflow/{dpid}' + mapper.connect('stats', uri, + controller=StatsController, + action='get_aggregate_flow_stats', + conditions=dict(method=['GET', 'POST'])) + uri = path + '/port/{dpid}' mapper.connect('stats', uri, controller=StatsController, action='get_port_stats', @@ -650,6 +689,7 @@ class RestStatsApi(app_manager.RyuApp): @set_ev_cls([ofp_event.EventOFPStatsReply, ofp_event.EventOFPDescStatsReply, ofp_event.EventOFPFlowStatsReply, + ofp_event.EventOFPAggregateStatsReply, ofp_event.EventOFPPortStatsReply, ofp_event.EventOFPQueueStatsReply, ofp_event.EventOFPMeterStatsReply, diff --git a/ryu/lib/ofctl_v1_0.py b/ryu/lib/ofctl_v1_0.py index ee4bffa6..a72d9b79 100644 --- a/ryu/lib/ofctl_v1_0.py +++ b/ryu/lib/ofctl_v1_0.py @@ -322,6 +322,30 @@ def get_flow_stats(dp, waiters, flow={}): return flows +def get_aggregate_flow_stats(dp, waiters, flow={}): + match = to_match(dp, flow.get('match', {})) + table_id = int(flow.get('table_id', 0xff)) + out_port = int(flow.get('out_port', dp.ofproto.OFPP_NONE)) + + stats = dp.ofproto_parser.OFPAggregateStatsRequest( + dp, 0, match, table_id, out_port) + + msgs = [] + send_stats_request(dp, stats, waiters, msgs) + + flows = [] + for msg in msgs: + stats = msg.body + for st in stats: + s = {'packet_count': st.packet_count, + 'byte_count': st.byte_count, + 'flow_count': st.flow_count} + flows.append(s) + flows = {str(dp.id): flows} + + return flows + + def get_port_stats(dp, waiters): stats = dp.ofproto_parser.OFPPortStatsRequest( dp, 0, dp.ofproto.OFPP_NONE) diff --git a/ryu/lib/ofctl_v1_2.py b/ryu/lib/ofctl_v1_2.py index 8c8e7682..07299ee5 100644 --- a/ryu/lib/ofctl_v1_2.py +++ b/ryu/lib/ofctl_v1_2.py @@ -465,6 +465,32 @@ def get_flow_stats(dp, waiters, flow={}): return flows +def get_aggregate_flow_stats(dp, waiters, flow={}): + table_id = int(flow.get('table_id', dp.ofproto.OFPTT_ALL)) + out_port = int(flow.get('out_port', dp.ofproto.OFPP_ANY)) + out_group = int(flow.get('out_group', dp.ofproto.OFPG_ANY)) + cookie = int(flow.get('cookie', 0)) + cookie_mask = int(flow.get('cookie_mask', 0)) + match = to_match(dp, flow.get('match', {})) + + stats = dp.ofproto_parser.OFPAggregateStatsRequest( + dp, table_id, out_port, out_group, cookie, cookie_mask, match) + + msgs = [] + send_stats_request(dp, stats, waiters, msgs) + + flows = [] + for msg in msgs: + stats = msg.body + s = {'packet_count': stats.packet_count, + 'byte_count': stats.byte_count, + 'flow_count': stats.flow_count} + flows.append(s) + flows = {str(dp.id): flows} + + return flows + + def get_port_stats(dp, waiters): stats = dp.ofproto_parser.OFPPortStatsRequest( dp, dp.ofproto.OFPP_ANY, 0) diff --git a/ryu/lib/ofctl_v1_3.py b/ryu/lib/ofctl_v1_3.py index b15f2b52..34383068 100644 --- a/ryu/lib/ofctl_v1_3.py +++ b/ryu/lib/ofctl_v1_3.py @@ -493,6 +493,34 @@ def get_flow_stats(dp, waiters, flow={}): return flows +def get_aggregate_flow_stats(dp, waiters, flow={}): + table_id = int(flow.get('table_id', dp.ofproto.OFPTT_ALL)) + flags = int(flow.get('flags', 0)) + out_port = int(flow.get('out_port', dp.ofproto.OFPP_ANY)) + out_group = int(flow.get('out_group', dp.ofproto.OFPG_ANY)) + cookie = int(flow.get('cookie', 0)) + cookie_mask = int(flow.get('cookie_mask', 0)) + match = to_match(dp, flow.get('match', {})) + + stats = dp.ofproto_parser.OFPAggregateStatsRequest( + dp, flags, table_id, out_port, out_group, cookie, cookie_mask, + match) + + msgs = [] + send_stats_request(dp, stats, waiters, msgs) + + flows = [] + for msg in msgs: + stats = msg.body + s = {'packet_count': stats.packet_count, + 'byte_count': stats.byte_count, + 'flow_count': stats.flow_count} + flows.append(s) + flows = {str(dp.id): flows} + + return flows + + def get_port_stats(dp, waiters): stats = dp.ofproto_parser.OFPPortStatsRequest( dp, 0, dp.ofproto.OFPP_ANY)