diff --git a/ryu/app/ofctl/__init__.py b/ryu/app/ofctl/__init__.py new file mode 100644 index 00000000..bd6808cb --- /dev/null +++ b/ryu/app/ofctl/__init__.py @@ -0,0 +1,15 @@ +# Copyright (C) 2014 Nippon Telegraph and Telephone Corporation. +# Copyright (C) 2014 YAMAMOTO Takashi +# +# 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. diff --git a/ryu/app/ofctl/api.py b/ryu/app/ofctl/api.py new file mode 100644 index 00000000..a3d0a332 --- /dev/null +++ b/ryu/app/ofctl/api.py @@ -0,0 +1,35 @@ +# Copyright (C) 2014 Nippon Telegraph and Telephone Corporation. +# Copyright (C) 2014 YAMAMOTO Takashi +# +# 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. + +# client for ryu.app.ofctl.service + +import event + + +def get_datapath(app, dpid): + """ + Get datapath object by dpid. + Returns None on error. + """ + assert isinstance(dpid, (int, long)) + return app.send_request(event.GetDatapathRequest(dpid=dpid))() + + +def send_msg(app, msg): + """ + Send an openflow message. + """ + return app.send_request(event.SendMsgRequest(msg=msg))() diff --git a/ryu/app/ofctl/event.py b/ryu/app/ofctl/event.py new file mode 100644 index 00000000..99208337 --- /dev/null +++ b/ryu/app/ofctl/event.py @@ -0,0 +1,55 @@ +# Copyright (C) 2014 Nippon Telegraph and Telephone Corporation. +# Copyright (C) 2014 YAMAMOTO Takashi +# +# 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. + +from ryu.controller import event + + +# base classes + +class _RequestBase(event.EventRequestBase): + def __init__(self): + self.dst = 'ofctl_service' + + +class _ReplyBase(event.EventReplyBase): + pass + + +# get datapath + +class GetDatapathRequest(_RequestBase): + def __init__(self, dpid): + assert isinstance(dpid, (int, long)) + super(GetDatapathRequest, self).__init__() + self.dpid = dpid + + +# send msg + +class SendMsgRequest(_RequestBase): + def __init__(self, msg): + super(SendMsgRequest, self).__init__() + self.msg = msg + + +# generic reply + +class Reply(_ReplyBase): + def __init__(self, result=None): + self.result = result + + def __call__(self): + return self.result diff --git a/ryu/app/ofctl/service.py b/ryu/app/ofctl/service.py new file mode 100644 index 00000000..eac8d681 --- /dev/null +++ b/ryu/app/ofctl/service.py @@ -0,0 +1,117 @@ +# Copyright (C) 2014 Nippon Telegraph and Telephone Corporation. +# Copyright (C) 2014 YAMAMOTO Takashi +# +# 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. + +# ofctl service + +from ryu.base import app_manager + +from ryu.controller import ofp_event +from ryu.controller.handler import CONFIG_DISPATCHER, MAIN_DISPATCHER,\ + DEAD_DISPATCHER +from ryu.controller.handler import set_ev_cls + +import event + + +class _SwitchInfo(object): + def __init__(self, datapath): + self.datapath = datapath + self.xids = {} + self.barriers = {} + self.results = {} + + +class OfctlService(app_manager.RyuApp): + def __init__(self, *args, **kwargs): + super(OfctlService, self).__init__(*args, **kwargs) + self.name = 'ofctl_service' + self._switches = {} + + @set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER) + def _switch_features_handler(self, ev): + datapath = ev.msg.datapath + id = datapath.id + assert isinstance(id, (int, long)) + self.logger.info('add dpid %s datapath %s' % (id, datapath)) + self._switches[datapath.id] = _SwitchInfo(datapath=datapath) + + @set_ev_cls(ofp_event.EventOFPStateChange, DEAD_DISPATCHER) + def _handle_dead(self, ev): + datapath = ev.datapath + id = datapath.id + self.logger.info('del dpid %s datapath %s' % (id, datapath)) + datapath2 = self._switches.pop(id) + assert datapath2 == datapath + + @set_ev_cls(event.GetDatapathRequest, MAIN_DISPATCHER) + def _handle_get_datapath(self, req): + id = req.dpid + assert isinstance(id, (int, long)) + try: + datapath = self._switches[id].datapath + except KeyError: + datapath = None + self.logger.info('dpid %s -> datapath %s' % (id, datapath)) + rep = event.Reply(result=datapath) + self.reply_to_request(req, rep) + + @set_ev_cls(event.SendMsgRequest, MAIN_DISPATCHER) + def _handle_send_msg(self, req): + msg = req.msg + datapath = msg.datapath + datapath.set_xid(msg) + xid = msg.xid + datapath.send_msg(msg) + barrier = datapath.ofproto_parser.OFPBarrierRequest(datapath) + datapath.set_xid(barrier) + barrier_xid = barrier.xid + datapath.send_msg(barrier) + si = self._switches[datapath.id] + si.xids[xid] = req + si.barriers[barrier_xid] = xid + + @set_ev_cls(ofp_event.EventOFPBarrierReply, MAIN_DISPATCHER) + def _handle_barrier(self, ev): + msg = ev.msg + datapath = msg.datapath + try: + si = self._switches[datapath.id] + except KeyError: + self.logger.error('unknown dpid %s' % (datapath.id,)) + return + try: + xid = si.barriers[msg.xid] + except KeyError: + self.logger.error('unknown barrier xid %s' % (msg.xid,)) + return + try: + result = si.results.pop(xid) + except KeyError: + result = None + req = si.xids.pop(xid) + rep = event.Reply(result=result) + self.reply_to_request(req, rep) + + @set_ev_cls(ofp_event.EventOFPErrorMsg, MAIN_DISPATCHER) + def _handle_error(self, ev): + msg = ev.msg + datapath = msg.datapath + try: + si = self._switches[datapath.id] + except KeyError: + self.logger.error('unknown dpid %s' % (datapath.id,)) + return + si.results[xid] = ev.msg