diff options
-rw-r--r-- | ryu/app/ofctl_rest.py | 221 |
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) |