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
