1 # Copyright (C) 2012, 2013 Nippon Telegraph and Telephone Corporation.
2 # Copyright (C) 2012 Isaku Yamahata <yamahata at valinux co jp>
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
8 # http://www.apache.org/licenses/LICENSE-2.0
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
20 Planned to be replaced by ryu/topology.
26 from ryu.base import app_manager
27 from ryu.controller import event
28 from ryu.controller import handler
29 from ryu.controller import ofp_event
30 from ryu.controller.handler import set_ev_cls
31 import ryu.exception as ryu_exc
33 from ryu.lib.dpid import dpid_to_str
35 LOG = logging.getLogger('ryu.controller.dpset')
37 DPSET_EV_DISPATCHER = "dpset"
40 class EventDPBase(event.EventBase):
41 def __init__(self, dp):
42 super(EventDPBase, self).__init__()
46 class EventDP(EventDPBase):
48 An event class to notify connect/disconnect of a switch.
50 For OpenFlow switches, one can get the same notification by observing
51 ryu.controller.ofp_event.EventOFPStateChange.
52 An instance has at least the following attributes.
54 ========= =================================================================
56 ========= =================================================================
57 dp A ryu.controller.controller.Datapath instance of the switch
58 enter True when the switch connected to our controller. False for
60 ports A list of port instances.
61 ========= =================================================================
64 def __init__(self, dp, enter_leave):
68 super(EventDP, self).__init__(dp)
69 self.enter = enter_leave
70 self.ports = [] # port list when enter or leave
73 class EventDPReconnected(EventDPBase):
74 def __init__(self, dp):
75 super(EventDPReconnected, self).__init__(dp)
76 # port list, which should not change across reconnects
80 class EventPortBase(EventDPBase):
81 def __init__(self, dp, port):
82 super(EventPortBase, self).__init__(dp)
86 class EventPortAdd(EventPortBase):
88 An event class for switch port status "ADD" notification.
90 This event is generated when a new port is added to a switch.
91 For OpenFlow switches, one can get the same notification by observing
92 ryu.controller.ofp_event.EventOFPPortStatus.
93 An instance has at least the following attributes.
95 ========= =================================================================
97 ========= =================================================================
98 dp A ryu.controller.controller.Datapath instance of the switch
100 ========= =================================================================
103 def __init__(self, dp, port):
104 super(EventPortAdd, self).__init__(dp, port)
107 class EventPortDelete(EventPortBase):
109 An event class for switch port status "DELETE" notification.
111 This event is generated when a port is removed from a switch.
112 For OpenFlow switches, one can get the same notification by observing
113 ryu.controller.ofp_event.EventOFPPortStatus.
114 An instance has at least the following attributes.
116 ========= =================================================================
117 Attribute Description
118 ========= =================================================================
119 dp A ryu.controller.controller.Datapath instance of the switch
121 ========= =================================================================
124 def __init__(self, dp, port):
125 super(EventPortDelete, self).__init__(dp, port)
128 class EventPortModify(EventPortBase):
130 An event class for switch port status "MODIFY" notification.
132 This event is generated when some attribute of a port is changed.
133 For OpenFlow switches, one can get the same notification by observing
134 ryu.controller.ofp_event.EventOFPPortStatus.
135 An instance has at least the following attributes.
137 ========= ====================================================================
138 Attribute Description
139 ========= ====================================================================
140 dp A ryu.controller.controller.Datapath instance of the switch
142 ========= ====================================================================
145 def __init__(self, dp, new_port):
146 super(EventPortModify, self).__init__(dp, new_port)
149 class PortState(dict):
151 super(PortState, self).__init__()
153 def add(self, port_no, port):
156 def remove(self, port_no):
159 def modify(self, port_no, port):
163 # this depends on controller::Datapath and dispatchers in handler
164 class DPSet(app_manager.RyuApp):
166 DPSet application manages a set of switches (datapaths)
167 connected to this controller.
172 from ryu.controller import dpset
175 class MyApp(app_manager.RyuApp):
177 'dpset': dpset.DPSet,
180 def __init__(self, *args, **kwargs):
181 super(MyApp, self).__init__(*args, **kwargs)
182 # Stores DPSet instance to call its API in this app
183 self.dpset = kwargs['dpset']
185 def _my_handler(self):
186 # Get the datapath object which has the given dpid
188 dp = self.dpset.get(dpid)
190 self.logger.info('No such datapath: dpid=%d', dpid)
193 def __init__(self, *args, **kwargs):
194 super(DPSet, self).__init__(*args, **kwargs)
197 self.dps = {} # datapath_id => class Datapath
198 self.port_state = {} # datapath_id => ports
200 def _register(self, dp):
201 LOG.debug('DPSET: register datapath %s', dp)
202 assert dp.id is not None
204 # while dpid should be unique, we need to handle duplicates here
205 # because it's entirely possible for a switch to reconnect us
206 # before we notice the drop of the previous connection.
208 # - forget the older connection as it likely will disappear soon
209 # - do not send EventDP leave/enter events
210 # - keep the PortState for the dpid
211 send_dp_reconnected = False
212 if dp.id in self.dps:
213 self.logger.warning('DPSET: Multiple connections from %s',
215 self.logger.debug('DPSET: Forgetting datapath %s', self.dps[dp.id])
216 (self.dps[dp.id]).close()
217 self.logger.debug('DPSET: New datapath %s', dp)
218 send_dp_reconnected = True
220 if dp.id not in self.port_state:
221 self.port_state[dp.id] = PortState()
222 ev = EventDP(dp, True)
223 with warnings.catch_warnings():
224 warnings.simplefilter('ignore')
225 for port in dp.ports.values():
226 self._port_added(dp, port)
227 ev.ports.append(port)
228 self.send_event_to_observers(ev)
229 if send_dp_reconnected:
230 ev = EventDPReconnected(dp)
231 ev.ports = self.port_state.get(dp.id, {}).values()
232 self.send_event_to_observers(ev)
234 def _unregister(self, dp):
235 # see the comment in _register().
236 if dp not in self.dps.values():
238 LOG.debug('DPSET: unregister datapath %s', dp)
239 assert self.dps[dp.id] == dp
241 # Now datapath is already dead, so port status change event doesn't
243 ev = EventDP(dp, False)
244 for port in list(self.port_state.get(dp.id, {}).values()):
245 self._port_deleted(dp, port)
246 ev.ports.append(port)
248 self.send_event_to_observers(ev)
251 del self.port_state[dp.id]
253 def get(self, dp_id):
255 This method returns the ryu.controller.controller.Datapath
256 instance for the given Datapath ID.
258 return self.dps.get(dp_id)
262 This method returns a list of tuples which represents
263 instances for switches connected to this controller.
264 The tuple consists of a Datapath ID and an instance of
265 ryu.controller.controller.Datapath.
267 A return value looks like the following::
269 [ (dpid_A, Datapath_A), (dpid_B, Datapath_B), ... ]
271 return list(self.dps.items())
273 def _port_added(self, datapath, port):
274 self.port_state[datapath.id].add(port.port_no, port)
276 def _port_deleted(self, datapath, port):
277 self.port_state[datapath.id].remove(port.port_no)
279 @set_ev_cls(ofp_event.EventOFPStateChange,
280 [handler.MAIN_DISPATCHER, handler.DEAD_DISPATCHER])
281 def dispatcher_change(self, ev):
282 datapath = ev.datapath
283 assert datapath is not None
284 if ev.state == handler.MAIN_DISPATCHER:
285 self._register(datapath)
286 elif ev.state == handler.DEAD_DISPATCHER:
287 self._unregister(datapath)
289 @set_ev_cls(ofp_event.EventOFPSwitchFeatures, handler.CONFIG_DISPATCHER)
290 def switch_features_handler(self, ev):
292 datapath = msg.datapath
293 # ofp_handler.py does the following so we could remove...
294 if datapath.ofproto.OFP_VERSION < 0x04:
295 datapath.ports = msg.ports
297 @set_ev_cls(ofp_event.EventOFPPortStatus, handler.MAIN_DISPATCHER)
298 def port_status_handler(self, ev):
301 datapath = msg.datapath
303 ofproto = datapath.ofproto
305 if reason == ofproto.OFPPR_ADD:
306 LOG.debug('DPSET: A port was added.' +
307 '(datapath id = %s, port number = %s)',
308 dpid_to_str(datapath.id), port.port_no)
309 self._port_added(datapath, port)
310 self.send_event_to_observers(EventPortAdd(datapath, port))
311 elif reason == ofproto.OFPPR_DELETE:
312 LOG.debug('DPSET: A port was deleted.' +
313 '(datapath id = %s, port number = %s)',
314 dpid_to_str(datapath.id), port.port_no)
315 self._port_deleted(datapath, port)
316 self.send_event_to_observers(EventPortDelete(datapath, port))
318 assert reason == ofproto.OFPPR_MODIFY
319 LOG.debug('DPSET: A port was modified.' +
320 '(datapath id = %s, port number = %s)',
321 dpid_to_str(datapath.id), port.port_no)
322 self.port_state[datapath.id].modify(port.port_no, port)
323 self.send_event_to_observers(EventPortModify(datapath, port))
325 def get_port(self, dpid, port_no):
327 This method returns the ryu.controller.dpset.PortState
328 instance for the given Datapath ID and the port number.
329 Raises ryu_exc.PortNotFound if no such a datapath connected to
330 this controller or no such a port exists.
333 return self.port_state[dpid][port_no]
335 raise ryu_exc.PortNotFound(dpid=dpid, port=port_no,
338 def get_ports(self, dpid):
340 This method returns a list of ryu.controller.dpset.PortState
341 instances for the given Datapath ID.
342 Raises KeyError if no such a datapath connected to this controller.
344 return list(self.port_state[dpid].values())
347 handler.register_service('ryu.controller.dpset')