It is sometimes commonly interesting to track datapath/port
appearance/disappearance. The applications usually want to see that ports
appear after datapath becomes ready, and ports disappear when datapath is dead.
It requires to handle properly events, hand shaking, port_mod event,
switch_feature_reply. So introduce a common layer to handle them.

GRE tunnel is interested in datapath/port appearance/disappearance.
With this, tunnel app doesn't have to handle those conditions.
Discovery is interested only in datapath/port appearance/disappearance.
With this, discovery app would not have to handle OFP events directly.

Signed-off-by: Isaku Yamahata <[email protected]>
---
Changes v4 -> v5:
- commit message

Changes v3 -> v4:
- don't use cork/uncork stuff.

Changes v2 -> v3:
- track port status change properly
  So far it isn't tracked. If port status is changed before entering
  MAIN_HANDLER after switch feature status, the controller's view and the
  actual port status doesn't match.
  So have to track ports status until MAIN_HANDLER.
- fix Port{Add, Del, Modify} race
  When dispatching those event, the thread can be switched or other
  EventQueue can be served. There was a race between
  Port{Add, Del, Modify}.

Changes v1 -> v2:
- generate port add/del event when datapath appears
  With this change, the user don't have to handle EventDP with
  'for port in ...' Thus the user code will be simplified.
- some helper functions which will be used later
---
 ryu/controller/dpset.py |  105 ++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 100 insertions(+), 5 deletions(-)

diff --git a/ryu/controller/dpset.py b/ryu/controller/dpset.py
index 80ffb6f..0d69526 100644
--- a/ryu/controller/dpset.py
+++ b/ryu/controller/dpset.py
@@ -20,7 +20,9 @@ from ryu.controller import event
 from ryu.controller import dispatcher
 from ryu.controller import dp_type
 from ryu.controller import handler
+from ryu.controller import ofp_event
 from ryu.controller.handler import set_ev_cls
+import ryu.exception as ryu_exc
 
 LOG = logging.getLogger('ryu.controller.dpset')
 
@@ -42,7 +44,43 @@ class EventDP(EventDPBase):
         # True: dp entered
         # False: dp leaving
         super(EventDP, self).__init__(dp)
-        self.enter = enter_leave
+        self.enter_leave = enter_leave
+        self.ports = []  # port list when enter or leave
+
+
+class EventPortBase(EventDPBase):
+    def __init__(self, dp, port):
+        super(EventPortBase, self).__init__(dp)
+        self.port = port
+
+
+class EventPortAdd(EventPortBase):
+    def __init__(self, dp, port):
+        super(EventPortAdd, self).__init__(dp, port)
+
+
+class EventPortDelete(EventPortBase):
+    def __init__(self, dp, port):
+        super(EventPortDelete, self).__init__(dp, port)
+
+
+class EventPortModify(EventPortBase):
+    def __init__(self, dp, new_port):
+        super(EventPortModify, self).__init__(dp, new_port)
+
+
+class PortState(dict):
+    def __init__(self):
+        super(PortState, self).__init__()
+
+    def add(self, port_no, port):
+        self[port_no] = port
+
+    def remove(self, port_no):
+        del self[port_no]
+
+    def modify(self, port_no, port):
+        self[port_no] = port
 
 
 # this depends on controller::Datapath and dispatchers in handler
@@ -55,6 +93,7 @@ class DPSet(object):
         self.dp_types = {}
 
         self.dps = {}   # datapath_id => class Datapath
+        self.port_state = {}  # datapath_id => ports
         self.ev_q = dispatcher.EventQueue(QUEUE_NAME_DPSET,
                                           DPSET_EV_DISPATCHER)
         handler.register_instance(self)
@@ -67,17 +106,30 @@ class DPSet(object):
         if dp_type_ is not None:
             dp.dp_type = dp_type_
 
-        self.ev_q.queue(EventDP(dp, True))
         self.dps[dp.id] = dp
+        self.port_state[dp.id] = PortState()
+
+        ev = EventDP(dp, True)
+        for port in dp.ports.values():
+            self._port_added(dp, port)
+            ev.ports.append(port)
+        self.ev_q.queue(ev)
 
     def unregister(self, dp):
+        # Now datapath is already dead, so port status change event doesn't
+        # interfere us.
+        ev = EventDP(dp, False)
+        for port in self.port_state.get(dp.id, {}).values():
+            self._port_deleted(dp, port)
+            ev.ports.append(port)
+        self.ev_q.queue(ev)
+
         if dp.id in self.dps:
             del self.dps[dp.id]
+            del self.port_state[dp.id]
             assert dp.id not in self.dp_types
             self.dp_types[dp.id] = getattr(dp, 'dp_type', dp_type.UNKNOWN)
 
-            self.ev_q.queue(EventDP(dp, False))
-
     def set_type(self, dp_id, dp_type_=dp_type.UNKNOWN):
         if dp_id in self.dps:
             dp = self.dps[dp_id]
@@ -87,11 +139,17 @@ class DPSet(object):
             self.dp_types[dp_id] = dp_type_
 
     def get(self, dp_id):
-        return self.dps.get(dp_id, None)
+        return self.dps.get(dp_id)
 
     def get_all(self):
         return self.dps.items()
 
+    def _port_added(self, datapath, port):
+        self.port_state[datapath.id].add(port.port_no, port)
+
+    def _port_deleted(self, datapath, port):
+        self.port_state[datapath.id].remove(port.port_no)
+
     @set_ev_cls(dispatcher.EventDispatcherChange,
                 dispatcher.QUEUE_EV_DISPATCHER)
     def dispacher_change(self, ev):
@@ -108,3 +166,40 @@ class DPSet(object):
         elif ev.new_dispatcher.name == handler.DISPATCHER_NAME_OFP_DEAD:
             LOG.debug('DPSET: unregister datapath %s', datapath)
             self.unregister(datapath)
+
+    @set_ev_cls(ofp_event.EventOFPSwitchFeatures, handler.CONFIG_DISPATCHER)
+    def switch_features_handler(self, ev):
+        msg = ev.msg
+        datapath = msg.datapath
+        datapath.ports = msg.ports
+
+    @set_ev_cls(ofp_event.EventOFPPortStatus, handler.MAIN_DISPATCHER)
+    def port_status_handler(self, ev):
+        msg = ev.msg
+        reason = msg.reason
+        datapath = msg.datapath
+        port = msg.desc
+        ofproto = datapath.ofproto
+
+        LOG.debug('port status %s', reason)
+
+        if reason == ofproto.OFPPR_ADD:
+            self._port_added(datapath, port)
+            self.ev_q.queue(EventPortAdd(datapath, port))
+        elif reason == ofproto.OFPPR_DELETE:
+            self._port_deleted(datapath, port)
+            self.ev_q.queue(EventPortDelete(datapath, port))
+        else:
+            assert reason == ofproto.OFPPR_MODIFY
+            self.port_state[datapath.id].modify(port.port_no, port)
+            self.ev_q.queue(EventPortModify(datapath, port))
+
+    def get_port(self, dpid, port_no):
+        try:
+            return self.port_state[dpid][port_no]
+        except KeyError:
+            raise ryu_exc.PortNotFound(dpid=dpid, port=port_no,
+                                       network_id=None)
+
+    def get_ports(self, dpid):
+        return self.port_state[dpid].values()
-- 
1.7.10.4


------------------------------------------------------------------------------
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_nov
_______________________________________________
Ryu-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/ryu-devel

Reply via email to