This is alternative of dpset. * Don't call other app's method directly. * Use Event request/reply
event.py: event class modules datapath.py: datapath discovery app using ofp_event dumper.py: test app using discovery event TODO: support link discovery using LLDP Signed-off-by: YAMADA Hideki <[email protected]> --- ryu/physicaltopology/datapath.py | 142 ++++++++++++++++++++++++++++++++++++++ ryu/physicaltopology/dumper.py | 79 +++++++++++++++++++++ ryu/physicaltopology/event.py | 97 ++++++++++++++++++++++++++ 3 files changed, 318 insertions(+), 0 deletions(-) create mode 100644 ryu/physicaltopology/__init__.py create mode 100644 ryu/physicaltopology/datapath.py create mode 100644 ryu/physicaltopology/dumper.py create mode 100644 ryu/physicaltopology/event.py diff --git a/ryu/physicaltopology/__init__.py b/ryu/physicaltopology/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ryu/physicaltopology/datapath.py b/ryu/physicaltopology/datapath.py new file mode 100644 index 0000000..6cb3767 --- /dev/null +++ b/ryu/physicaltopology/datapath.py @@ -0,0 +1,142 @@ +# Copyright (C) 2013 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 +from ryu.base import app_manager +from ryu.controller import ofp_event, handler + +from .event import EventDPEnter, EventDPLeave +from .event import EventDPPortAdd, EventDPPortDelete, EventDPPortModify +from .event import EventDPListRequest, EventDPListReply +LOG = logging.getLogger(__name__) + + +class DatapathDiscovery(app_manager.RyuApp): + _EVENTS = [EventDPEnter, EventDPLeave, + EventDPPortAdd, EventDPPortDelete, EventDPPortModify] + + def __init__(self): + super(DatapathDiscovery, self).__init__() + + self.dpids = [] # datapath_id + self.ports = {} # datapath_id => ports + # OFPP_MAX might be different when using multiple version of OpenFlow? + self._OFPP_MAX = {} # datapath_id => OFPP_MAX + + def _register(self, dp): + assert dp.id not in self.dpids + dpid = dp.id + + self.dpids.append(dpid) + + self._OFPP_MAX[dpid] = dp.ofproto.OFPP_MAX + + # LOG.debug('dp %s has ports %s', dpid, dp.ports) + for port_no in dp.ports.iterkeys(): + self._add_port(dpid, port_no) + + def _unregister(self, dp): + assert dp.id in self.dpids + dpid = dp.id + + self.dpids.remove(dpid) + + assert dpid in self.ports + del self.ports[dpid] + + assert dpid in self._OFPP_MAX + del self._OFPP_MAX + + def _add_port(self, dpid, port_no): + if port_no > self._OFPP_MAX[dpid]: + # LOG.debug('port %s is reserved', port_no) + return + + self.ports.setdefault(dpid, []) + self.ports[dpid].append(port_no) + + def _del_port(self, dpid, port_no): + assert dpid in self.ports + + self.ports[dpid].remove(port_no) + + def _get_ports(self, dpid): + return self.ports[dpid] + + @handler.set_ev_cls(ofp_event.EventOFPStateChange, + [handler.MAIN_DISPATCHER, handler.DEAD_DISPATCHER]) + def state_change_handler(self, ev): + dp = ev.datapath + dpid = dp.id + assert dp is not None + + if ev.state == handler.MAIN_DISPATCHER: + self._register(dp) + LOG.debug('register datapath %s, ports %s', + dpid, self._get_ports(dpid)) + self.send_event_to_observers( + EventDPEnter(dpid, self._get_ports(dpid))) + + elif ev.state == handler.DEAD_DISPATCHER: + LOG.debug('unregister datapath %s', dp.id) + self._unregister(dp) + self.send_event_to_observers( + EventDPLeave(dpid, self._get_ports(dpid))) + + @handler.set_ev_cls(ofp_event.EventOFPPortStatus, handler.MAIN_DISPATCHER) + def port_status_handler(self, ev): + msg = ev.msg + reason = msg.reason + dpid = msg.datapath.id + port_no = msg.desc.port_no + ofproto = msg.datapath.ofproto + + if reason == ofproto.OFPPR_ADD: + LOG.debug('port was added.' + + '(datapath id = %s, port number = %s)', + dpid, port_no) + self._add_port(dpid, port_no) + self.send_event_to_observers(EventDPPortAdd(dpid, port_no)) + + elif reason == ofproto.OFPPR_DELETE: + LOG.debug('port was deleted.' + + '(datapath id = %s, port number = %s)', + dpid, port_no) + self._del_port(dpid, port_no) + self.send_event_to_observers(EventDPPortDelete(dpid, port_no)) + + else: + assert reason == ofproto.OFPPR_MODIFY + LOG.debug('port was modified.' + + '(datapath id = %s, port number = %s)', + dpid, port_no) + self.send_event_to_observers(EventDPPortModify(dpid, port_no)) + + @handler.set_ev_cls(EventDPListRequest) + def request_handler(self, ev): + dpid = ev.dpid + # LOG.debug(ev) + if dpid is None: + dpids = self.dpids + ports = self.ports + elif dpid in self.dpids: + dpids = [dpid] + ports = [self.ports[dpid]] + else: + dpids = [] + ports = {} + + reply = EventDPListReply(self.name, ev, dpids, ports) + self.send_event(ev.src, reply) diff --git a/ryu/physicaltopology/dumper.py b/ryu/physicaltopology/dumper.py new file mode 100644 index 0000000..f5dcd5f --- /dev/null +++ b/ryu/physicaltopology/dumper.py @@ -0,0 +1,79 @@ +# Copyright (C) 2013 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 gevent +import gevent.queue + +from ryu.base import app_manager +from ryu.controller import handler + +from .event import EventDPEnter, EventDPLeave +from .event import EventDPPortAdd, EventDPPortDelete, EventDPPortModify +from .event import EventDPListRequest, EventDPListReply +import datapath + +LOG = logging.getLogger(__name__) + + +class DiscoveryEventDumper(app_manager.RyuApp): + _REQUEST_TIMEOUT = 30 + + def __init__(self): + super(DiscoveryEventDumper, self).__init__() + self.replys = gevent.queue.Queue() + self.threads.append(gevent.spawn_later(0, self._request_loop)) + self.is_active = True + + @handler.set_ev_cls(EventDPEnter) + def dp_enter_handler(self, ev): + LOG.debug('datapath(%s) entered', ev) + + @handler.set_ev_cls(EventDPLeave) + def dp_leave_handler(self, ev): + LOG.debug('datapath(%s) leaved', ev) + + @handler.set_ev_cls(EventDPPortAdd) + def dp_port_add_handler(self, ev): + LOG.debug('port(%s) added', ev) + + @handler.set_ev_cls(EventDPPortDelete) + def dp_port_delete_handler(self, ev): + LOG.debug('port(%s) deleted', ev) + + @handler.set_ev_cls(EventDPPortModify) + def dp_port_modify_handler(self, ev): + LOG.debug('port(%s) modified', ev) + + @handler.set_ev_cls(EventDPListReply) + def reply_handler(self, ev): + self.replys.put(ev) + + def _get_dp_list(self, dpid=None): + request = EventDPListRequest(self.name, dpid) + self.send_event(datapath.DatapathDiscovery.__name__, request) + # assume that replys are in right order + reply = self.replys.get(timeout=self._REQUEST_TIMEOUT) + assert reply.request == request + return reply + + def _request_loop(self): + while self.is_active: + dp_list_all = self._get_dp_list() + LOG.debug('ALL DPList: %s', dp_list_all) + dp1 = self._get_dp_list(1) + LOG.debug('DP1: %s', dp1) + gevent.sleep(10) diff --git a/ryu/physicaltopology/event.py b/ryu/physicaltopology/event.py new file mode 100644 index 0000000..4b85cff --- /dev/null +++ b/ryu/physicaltopology/event.py @@ -0,0 +1,97 @@ +# Copyright (C) 2013 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 +from ryu.controller import event + + +LOG = logging.getLogger(__name__) + + +class EventDPBase(event.EventBase): + def __init__(self, dpid, ports): + super(EventDPBase, self).__init__() + self.dpid = dpid + self.ports = ports # port list when enter or leave + + def __str__(self): + return 'dpid = %s, ports = %s' % (self.dpid, self.ports) + + +class EventDPEnter(EventDPBase): + def __init__(self, dpid, ports): + super(EventDPEnter, self).__init__(dpid, ports) + + +class EventDPLeave(EventDPBase): + def __init__(self, dpid, ports): + super(EventDPLeave, self).__init__(dpid, ports) + + +class EventDPPortBase(event.EventBase): + def __init__(self, dpid, port_no): + super(EventDPPortBase, self).__init__() + self.dpid = dpid + self.port_no = port_no + + def __str__(self): + return 'dpid = %s, port_no =%s' % (self.dpid, self.port_no) + + +class EventDPPortAdd(EventDPPortBase): + def __init__(self, dpid, port_no): + super(EventDPPortAdd, self).__init__(dpid, port_no) + + +class EventDPPortDelete(EventDPPortBase): + def __init__(self, dpid, port_no): + super(EventDPPortDelete, self).__init__(dpid, port_no) + + +class EventDPPortModify(EventDPPortBase): + def __init__(self, dpid, port_no): + super(EventDPPortModify, self).__init__(dpid, port_no) + + +class EventRequestBase(event.EventBase): + def __init__(self, src): + self.src = src # brick name of event source + + +class EventDPListRequest(EventRequestBase): + def __init__(self, src, dpid=None): + super(EventDPListRequest, self).__init__(src) + self.dpid = dpid + + def __str__(self): + return 'EventDPListRequest<src=%s, dpid=%s>' % (self.src, self.dpid) + + +class EventReplyBase(event.EventBase): + def __init__(self, src, request): + self.src = src # brick name of event source + assert issubclass(request.__class__, EventRequestBase) + self.request = request + + +class EventDPListReply(EventReplyBase): + def __init__(self, src, request, dpids, ports): + super(EventDPListReply, self).__init__(src, request) + self.dpids = dpids + self.ports = ports + + def __str__(self): + return 'EventDPListReply<dpids=%s, ports=%s>' % \ + (self.dpids, self.ports) -- 1.7.1 ------------------------------------------------------------------------------ Everyone hates slow websites. So do we. Make your web apps faster with AppDynamics Download AppDynamics Lite for free today: http://p.sf.net/sfu/appdyn_d2d_feb _______________________________________________ Ryu-devel mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/ryu-devel
