summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--ryu/app/ofctl_rest.py221
1 files changed, 221 insertions, 0 deletions
diff --git a/ryu/app/ofctl_rest.py b/ryu/app/ofctl_rest.py
new file mode 100644
index 00000000..2a0818e0
--- /dev/null
+++ b/ryu/app/ofctl_rest.py
@@ -0,0 +1,221 @@
+# Copyright (C) 2012 Nippon Telegraph and Telephone Corporation.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import logging
+
+import json
+from webob import Response
+
+from ryu.base import app_manager
+from ryu.controller import ofp_event
+from ryu.controller import dpset
+from ryu.controller.handler import MAIN_DISPATCHER
+from ryu.controller.handler import set_ev_cls
+from ryu.ofproto import ofproto_v1_0
+from ryu.lib import ofctl_v1_0
+from ryu.app.wsgi import ControllerBase, WSGIApplication
+
+
+LOG = logging.getLogger('ryu.app.ofctl_rest')
+
+# REST API
+#
+## Retrieve the switch stats
+#
+# get the list of all switches
+# GET /stats/switches
+#
+# get the desc stats of the switch
+# GET /stats/desc/<dpid>
+#
+# get flows stats of the switch
+# GET /stats/flow/<dpid>
+#
+# get ports stats of the switch
+# GET /stats/port/<dpid>
+#
+## Update the switch stats
+#
+# add a flow entry
+# POST /stats/flowentry
+#
+# delete flows of the switch
+# DELETE /stats/flowentry/clear/<dpid>
+#
+
+
+class StatsController(ControllerBase):
+ def __init__(self, req, link, data, **config):
+ super(StatsController, self).__init__(req, link, data, **config)
+ self.dpset = data['dpset']
+ self.waiters = data['waiters']
+
+ def get_dpids(self, req, **_kwargs):
+ dps = self.dpset.dps.keys()
+ body = json.dumps(dps)
+ return (Response(content_type='application/json', body=body))
+
+ def get_desc_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_0.OFP_VERSION:
+ desc = ofctl_v1_0.get_desc_stats(dp, self.waiters)
+ else:
+ LOG.debug('Unsupported OF protocol')
+ return Response(status=501)
+
+ body = json.dumps(desc)
+ return (Response(content_type='application/json', body=body))
+
+ def get_flow_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_0.OFP_VERSION:
+ flows = ofctl_v1_0.get_flow_stats(dp, self.waiters)
+ 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:
+ return Response(status=404)
+
+ if dp.ofproto.OFP_VERSION == ofproto_v1_0.OFP_VERSION:
+ ports = ofctl_v1_0.get_port_stats(dp, self.waiters)
+ else:
+ LOG.debug('Unsupported OF protocol')
+ return Response(status=501)
+
+ body = json.dumps(ports)
+ return (Response(content_type='application/json', body=body))
+
+ def push_flow_entry(self, req, **_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 dp.ofproto.OFP_VERSION == ofproto_v1_0.OFP_VERSION:
+ ofctl_v1_0.push_flow_entry(dp, flow)
+ else:
+ LOG.debug('Unsupported OF protocol')
+ return Response(status=501)
+
+ return Response(status=200)
+
+ def delete_flow_entry(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_0.OFP_VERSION:
+ ofctl_v1_0.delete_flow_entry(dp)
+ 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]
+ _CONTEXTS = {
+ 'dpset': dpset.DPSet,
+ 'wsgi': WSGIApplication
+ }
+
+ def __init__(self, *args, **kwargs):
+ super(RestStatsApi, self).__init__(*args, **kwargs)
+ self.dpset = kwargs['dpset']
+ wsgi = kwargs['wsgi']
+ self.waiters = {}
+ self.data = {}
+ self.data['dpset'] = self.dpset
+ self.data['waiters'] = self.waiters
+ mapper = wsgi.mapper
+
+ wsgi.registory['StatsController'] = self.data
+ path = '/stats'
+ uri = path + '/switches'
+ mapper.connect('stats', uri,
+ controller=StatsController, action='get_dpids',
+ conditions=dict(method=['GET']))
+
+ uri = path + '/desc/{dpid}'
+ mapper.connect('stats', uri,
+ controller=StatsController, action='get_desc_stats',
+ conditions=dict(method=['GET']))
+
+ uri = path + '/flow/{dpid}'
+ mapper.connect('stats', uri,
+ controller=StatsController, action='get_flow_stats',
+ conditions=dict(method=['GET']))
+
+ uri = path + '/port/{dpid}'
+ mapper.connect('stats', uri,
+ controller=StatsController, action='get_port_stats',
+ conditions=dict(method=['GET']))
+
+ uri = path + '/flowentry'
+ mapper.connect('stats', uri,
+ controller=StatsController, action='push_flow_entry',
+ conditions=dict(method=['POST']))
+ uri = uri + '/clear/{dpid}'
+ mapper.connect('stats', uri,
+ controller=StatsController, action='delete_flow_entry',
+ conditions=dict(method=['DELETE']))
+
+ def stats_reply_handler(self, ev):
+ msg = ev.msg
+ dp = msg.datapath
+
+ if dp.id not in self.waiters:
+ return
+ if msg.xid not in self.waiters[dp.id]:
+ return
+ lock, msgs = self.waiters[dp.id][msg.xid]
+ msgs.append(msg)
+ print 'stats_reply_handler:', msgs
+
+ if msg.flags & dp.ofproto.OFPSF_REPLY_MORE:
+ return
+ del self.waiters[dp.id][msg.xid]
+ lock.set()
+
+ @set_ev_cls(ofp_event.EventOFPDescStatsReply, MAIN_DISPATCHER)
+ def desc_stats_reply_handler(self, ev):
+ self.stats_reply_handler(ev)
+
+ @set_ev_cls(ofp_event.EventOFPFlowStatsReply, MAIN_DISPATCHER)
+ def flow_stats_reply_handler(self, ev):
+ self.stats_reply_handler(ev)
+
+ @set_ev_cls(ofp_event.EventOFPPortStatsReply, MAIN_DISPATCHER)
+ def port_stats_reply_handler(self, ev):
+ self.stats_reply_handler(ev)