Update and rename MantenerFIFO to MantenerFIFO.md
[vsorcdistro/.git] / ryu / ryu / controller / dpset.py
1 # Copyright (C) 2012, 2013 Nippon Telegraph and Telephone Corporation.
2 # Copyright (C) 2012 Isaku Yamahata <yamahata at valinux co jp>
3 #
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
7 #
8 #    http://www.apache.org/licenses/LICENSE-2.0
9 #
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
13 # implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16
17 """
18 Manage switches.
19
20 Planned to be replaced by ryu/topology.
21 """
22
23 import logging
24 import warnings
25
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
32
33 from ryu.lib.dpid import dpid_to_str
34
35 LOG = logging.getLogger('ryu.controller.dpset')
36
37 DPSET_EV_DISPATCHER = "dpset"
38
39
40 class EventDPBase(event.EventBase):
41     def __init__(self, dp):
42         super(EventDPBase, self).__init__()
43         self.dp = dp
44
45
46 class EventDP(EventDPBase):
47     """
48     An event class to notify connect/disconnect of a switch.
49
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.
53
54     ========= =================================================================
55     Attribute Description
56     ========= =================================================================
57     dp        A ryu.controller.controller.Datapath instance of the switch
58     enter     True when the switch connected to our controller.  False for
59               disconnect.
60     ports     A list of port instances.
61     ========= =================================================================
62     """
63
64     def __init__(self, dp, enter_leave):
65         # enter_leave
66         # True: dp entered
67         # False: dp leaving
68         super(EventDP, self).__init__(dp)
69         self.enter = enter_leave
70         self.ports = []  # port list when enter or leave
71
72
73 class EventDPReconnected(EventDPBase):
74     def __init__(self, dp):
75         super(EventDPReconnected, self).__init__(dp)
76         # port list, which should not change across reconnects
77         self.ports = []
78
79
80 class EventPortBase(EventDPBase):
81     def __init__(self, dp, port):
82         super(EventPortBase, self).__init__(dp)
83         self.port = port
84
85
86 class EventPortAdd(EventPortBase):
87     """
88     An event class for switch port status "ADD" notification.
89
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.
94
95     ========= =================================================================
96     Attribute Description
97     ========= =================================================================
98     dp        A ryu.controller.controller.Datapath instance of the switch
99     port      port number
100     ========= =================================================================
101     """
102
103     def __init__(self, dp, port):
104         super(EventPortAdd, self).__init__(dp, port)
105
106
107 class EventPortDelete(EventPortBase):
108     """
109     An event class for switch port status "DELETE" notification.
110
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.
115
116     ========= =================================================================
117     Attribute Description
118     ========= =================================================================
119     dp        A ryu.controller.controller.Datapath instance of the switch
120     port      port number
121     ========= =================================================================
122     """
123
124     def __init__(self, dp, port):
125         super(EventPortDelete, self).__init__(dp, port)
126
127
128 class EventPortModify(EventPortBase):
129     """
130     An event class for switch port status "MODIFY" notification.
131
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.
136
137     ========= ====================================================================
138     Attribute Description
139     ========= ====================================================================
140     dp        A ryu.controller.controller.Datapath instance of the switch
141     port      port number
142     ========= ====================================================================
143     """
144
145     def __init__(self, dp, new_port):
146         super(EventPortModify, self).__init__(dp, new_port)
147
148
149 class PortState(dict):
150     def __init__(self):
151         super(PortState, self).__init__()
152
153     def add(self, port_no, port):
154         self[port_no] = port
155
156     def remove(self, port_no):
157         del self[port_no]
158
159     def modify(self, port_no, port):
160         self[port_no] = port
161
162
163 # this depends on controller::Datapath and dispatchers in handler
164 class DPSet(app_manager.RyuApp):
165     """
166     DPSet application manages a set of switches (datapaths)
167     connected to this controller.
168
169     Usage Example::
170
171         # ...(snip)...
172         from ryu.controller import dpset
173
174
175         class MyApp(app_manager.RyuApp):
176             _CONTEXTS = {
177                 'dpset': dpset.DPSet,
178             }
179
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']
184
185             def _my_handler(self):
186                 # Get the datapath object which has the given dpid
187                 dpid = 1
188                 dp = self.dpset.get(dpid)
189                 if dp is None:
190                     self.logger.info('No such datapath: dpid=%d', dpid)
191     """
192
193     def __init__(self, *args, **kwargs):
194         super(DPSet, self).__init__(*args, **kwargs)
195         self.name = 'dpset'
196
197         self.dps = {}   # datapath_id => class Datapath
198         self.port_state = {}  # datapath_id => ports
199
200     def _register(self, dp):
201         LOG.debug('DPSET: register datapath %s', dp)
202         assert dp.id is not None
203
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.
207         # in that case,
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',
214                                 dpid_to_str(dp.id))
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
219         self.dps[dp.id] = dp
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)
233
234     def _unregister(self, dp):
235         # see the comment in _register().
236         if dp not in self.dps.values():
237             return
238         LOG.debug('DPSET: unregister datapath %s', dp)
239         assert self.dps[dp.id] == dp
240
241         # Now datapath is already dead, so port status change event doesn't
242         # interfere us.
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)
247
248         self.send_event_to_observers(ev)
249
250         del self.dps[dp.id]
251         del self.port_state[dp.id]
252
253     def get(self, dp_id):
254         """
255         This method returns the ryu.controller.controller.Datapath
256         instance for the given Datapath ID.
257         """
258         return self.dps.get(dp_id)
259
260     def get_all(self):
261         """
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.
266
267         A return value looks like the following::
268
269             [ (dpid_A, Datapath_A), (dpid_B, Datapath_B), ... ]
270         """
271         return list(self.dps.items())
272
273     def _port_added(self, datapath, port):
274         self.port_state[datapath.id].add(port.port_no, port)
275
276     def _port_deleted(self, datapath, port):
277         self.port_state[datapath.id].remove(port.port_no)
278
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)
288
289     @set_ev_cls(ofp_event.EventOFPSwitchFeatures, handler.CONFIG_DISPATCHER)
290     def switch_features_handler(self, ev):
291         msg = ev.msg
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
296
297     @set_ev_cls(ofp_event.EventOFPPortStatus, handler.MAIN_DISPATCHER)
298     def port_status_handler(self, ev):
299         msg = ev.msg
300         reason = msg.reason
301         datapath = msg.datapath
302         port = msg.desc
303         ofproto = datapath.ofproto
304
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))
317         else:
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))
324
325     def get_port(self, dpid, port_no):
326         """
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.
331         """
332         try:
333             return self.port_state[dpid][port_no]
334         except KeyError:
335             raise ryu_exc.PortNotFound(dpid=dpid, port=port_no,
336                                        network_id=None)
337
338     def get_ports(self, dpid):
339         """
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.
343         """
344         return list(self.port_state[dpid].values())
345
346
347 handler.register_service('ryu.controller.dpset')