From 978f3e5c0d668982cd27dd67ab2b3c4caffd1ec2 Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Tue, 5 Feb 2013 20:10:43 +0900 Subject: [PATCH] dpset: add port event support Based on the following patch: From: Isaku Yamahata Subject: dpset: add port{add, delete, modify} event for convenience and helper functions 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 Signed-off-by: FUJITA Tomonori --- ryu/controller/dpset.py | 108 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 100 insertions(+), 8 deletions(-) diff --git a/ryu/controller/dpset.py b/ryu/controller/dpset.py index c960906c..1660410a 100644 --- a/ryu/controller/dpset.py +++ b/ryu/controller/dpset.py @@ -1,4 +1,4 @@ -# Copyright (C) 2012 Nippon Telegraph and Telephone Corporation. +# Copyright (C) 2012, 2013 Nippon Telegraph and Telephone Corporation. # Copyright (C) 2012 Isaku Yamahata # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -21,14 +21,13 @@ from ryu.controller import event from ryu.controller import ofp_event 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') -DPSET_EV_DISPATCHER = 'dpset' - - class EventDPBase(event.EventBase): def __init__(self, dp): super(EventDPBase, self).__init__() @@ -42,6 +41,42 @@ class EventDP(EventDPBase): # False: dp leaving super(EventDP, self).__init__(dp) self.enter = 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 +90,7 @@ class DPSet(app_manager.RyuApp): self.dp_types = {} self.dps = {} # datapath_id => class Datapath + self.port_state = {} # datapath_id => ports def register(self, dp): assert dp.id is not None @@ -65,16 +101,29 @@ class DPSet(app_manager.RyuApp): dp.dp_type = dp_type_ self.dps[dp.id] = dp - self.send_event_to_observers(EventDP(dp, True)) + 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.send_event_to_observers(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.send_event_to_observers(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.send_event_to_observers(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] @@ -84,11 +133,17 @@ class DPSet(app_manager.RyuApp): 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(ofp_event.EventOFPStateChange, [handler.MAIN_DISPATCHER, handler.DEAD_DISPATCHER]) def dispacher_change(self, ev): @@ -100,3 +155,40 @@ class DPSet(app_manager.RyuApp): elif ev.state == handler.DEAD_DISPATCHER: 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.send_event_to_observers(EventPortAdd(datapath, port)) + elif reason == ofproto.OFPPR_DELETE: + self._port_deleted(datapath, port) + self.send_event_to_observers(EventPortDelete(datapath, port)) + else: + assert reason == ofproto.OFPPR_MODIFY + self.port_state[datapath.id].modify(port.port_no, port) + self.send_event_to_observers(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()