backing up
[vsorcdistro/.git] / ryu / build / lib.linux-armv7l-2.7 / ryu / controller / network.py
1 # Copyright (C) 2011 Nippon Telegraph and Telephone Corporation.
2 # Copyright (C) 2011 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 import collections
18
19 from ryu.base import app_manager
20 import ryu.exception as ryu_exc
21 from ryu.controller import event
22 from ryu.exception import NetworkNotFound, NetworkAlreadyExist
23 from ryu.exception import PortAlreadyExist, PortNotFound, PortUnknown
24
25
26 NW_ID_UNKNOWN = '__NW_ID_UNKNOWN__'
27
28
29 class MacAddressAlreadyExist(ryu_exc.RyuException):
30     message = 'port (%(dpid)s, %(port)s) has already mac %(mac_address)s'
31
32
33 class EventNetworkDel(event.EventBase):
34     """
35     An event class for network deletion.
36
37     This event is generated when a network is deleted by the REST API.
38     An instance has at least the following attributes.
39
40     ========== ===================================================================
41     Attribute  Description
42     ========== ===================================================================
43     network_id Network ID
44     ========== ===================================================================
45     """
46
47     def __init__(self, network_id):
48         super(EventNetworkDel, self).__init__()
49         self.network_id = network_id
50
51
52 class EventNetworkPort(event.EventBase):
53     """
54     An event class for notification of port arrival and deperture.
55
56     This event is generated when a port is introduced to or removed from a
57     network by the REST API.
58     An instance has at least the following attributes.
59
60     ========== ================================================================
61     Attribute  Description
62     ========== ================================================================
63     network_id Network ID
64     dpid       OpenFlow Datapath ID of the switch to which the port belongs.
65     port_no    OpenFlow port number of the port
66     add_del    True for adding a port.  False for removing a port.
67     ========== ================================================================
68     """
69
70     def __init__(self, network_id, dpid, port_no, add_del):
71         super(EventNetworkPort, self).__init__()
72         self.network_id = network_id
73         self.dpid = dpid
74         self.port_no = port_no
75         self.add_del = add_del
76
77
78 class EventMacAddress(event.EventBase):
79     """
80     An event class for end-point MAC address registration.
81
82     This event is generated when a end-point MAC address is updated
83     by the REST API.
84     An instance has at least the following attributes.
85
86     =========== ===============================================================
87     Attribute   Description
88     =========== ===============================================================
89     network_id  Network ID
90     dpid        OpenFlow Datapath ID of the switch to which the port belongs.
91     port_no     OpenFlow port number of the port
92     mac_address The old MAC address of the port if add_del is False.  Otherwise
93                 the new MAC address.
94     add_del     False if this event is a result of a port removal.  Otherwise
95                 True.
96     =========== ===============================================================
97     """
98
99     def __init__(self, dpid, port_no, network_id, mac_address, add_del):
100         super(EventMacAddress, self).__init__()
101         assert network_id is not None
102         assert mac_address is not None
103         self.dpid = dpid
104         self.port_no = port_no
105         self.network_id = network_id
106         self.mac_address = mac_address
107         self.add_del = add_del
108
109
110 class Networks(dict):
111     "network_id -> set of (dpid, port_no)"
112
113     def __init__(self, f):
114         super(Networks, self).__init__()
115         self.send_event = f
116
117     def list_networks(self):
118         return list(self.keys())
119
120     def has_network(self, network_id):
121         return network_id in self
122
123     def update_network(self, network_id):
124         self.setdefault(network_id, set())
125
126     def create_network(self, network_id):
127         if network_id in self:
128             raise NetworkAlreadyExist(network_id=network_id)
129
130         self[network_id] = set()
131
132     def remove_network(self, network_id):
133         try:
134             ports = self[network_id]
135         except KeyError:
136             raise NetworkNotFound(network_id=network_id)
137
138         while ports:
139             (dpid, port_no) = ports.pop()
140             self._remove_event(network_id, dpid, port_no)
141         if self.pop(network_id, None) is not None:
142             self.send_event(EventNetworkDel(network_id))
143
144     def list_ports(self, network_id):
145         try:
146             # use list() to keep compatibility for output
147             # set() isn't json serializable
148             return list(self[network_id])
149         except KeyError:
150             raise NetworkNotFound(network_id=network_id)
151
152     def add_raw(self, network_id, dpid, port_no):
153         self[network_id].add((dpid, port_no))
154
155     def add_event(self, network_id, dpid, port_no):
156         self.send_event(
157             EventNetworkPort(network_id, dpid, port_no, True))
158
159     # def add(self, network_id, dpid, port_no):
160     #     self.add_raw(network_id, dpid, port_no)
161     #     self.add_event(network_id, dpid, port_no)
162
163     def _remove_event(self, network_id, dpid, port_no):
164         self.send_event(EventNetworkPort(network_id, dpid, port_no, False))
165
166     def remove_raw(self, network_id, dpid, port_no):
167         ports = self[network_id]
168         if (dpid, port_no) in ports:
169             ports.remove((dpid, port_no))
170             self._remove_event(network_id, dpid, port_no)
171
172     def remove(self, network_id, dpid, port_no):
173         try:
174             self.remove_raw(network_id, dpid, port_no)
175         except KeyError:
176             raise NetworkNotFound(network_id=network_id)
177         except ValueError:
178             raise PortNotFound(network_id=network_id, dpid=dpid, port=port_no)
179
180     def has_port(self, network_id, dpid, port):
181         return (dpid, port) in self[network_id]
182
183     def get_dpids(self, network_id):
184         try:
185             ports = self[network_id]
186         except KeyError:
187             return set()
188
189         # python 2.6 doesn't support set comprehension
190         # port = (dpid, port_no)
191         return set([port[0] for port in ports])
192
193
194 class Port(object):
195     def __init__(self, port_no, network_id, mac_address=None):
196         super(Port, self).__init__()
197         self.port_no = port_no
198         self.network_id = network_id
199         self.mac_address = mac_address
200
201
202 class DPIDs(dict):
203     """dpid -> port_no -> Port(port_no, network_id, mac_address)"""
204
205     def __init__(self, f, nw_id_unknown):
206         super(DPIDs, self).__init__()
207         self.send_event = f
208         self.nw_id_unknown = nw_id_unknown
209
210     def setdefault_dpid(self, dpid):
211         return self.setdefault(dpid, {})
212
213     def _setdefault_network(self, dpid, port_no, default_network_id):
214         dp = self.setdefault_dpid(dpid)
215         return dp.setdefault(port_no, Port(port_no=port_no,
216                                            network_id=default_network_id))
217
218     def setdefault_network(self, dpid, port_no):
219         self._setdefault_network(dpid, port_no, self.nw_id_unknown)
220
221     def update_port(self, dpid, port_no, network_id):
222         port = self._setdefault_network(dpid, port_no, network_id)
223         port.network_id = network_id
224
225     def remove_port(self, dpid, port_no):
226         try:
227             # self.dpids[dpid][port_no] can be already deleted by
228             # port_deleted()
229             port = self[dpid].pop(port_no, None)
230             if port and port.network_id and port.mac_address:
231                 self.send_event(EventMacAddress(dpid, port_no,
232                                                 port.network_id,
233                                                 port.mac_address,
234                                                 False))
235         except KeyError:
236             raise PortNotFound(dpid=dpid, port=port_no, network_id=None)
237
238     def get_ports(self, dpid, network_id=None, mac_address=None):
239         if network_id is None:
240             return list(self.get(dpid, {}).values())
241         if mac_address is None:
242             return [p for p in self.get(dpid, {}).values()
243                     if p.network_id == network_id]
244
245         # live-migration: There can be two ports that have same mac address.
246         return [p for p in self.get(dpid, {}).values()
247                 if p.network_id == network_id and p.mac_address == mac_address]
248
249     def get_port(self, dpid, port_no):
250         try:
251             return self[dpid][port_no]
252         except KeyError:
253             raise PortNotFound(dpid=dpid, port=port_no, network_id=None)
254
255     def get_network(self, dpid, port_no):
256         try:
257             return self[dpid][port_no].network_id
258         except KeyError:
259             raise PortUnknown(dpid=dpid, port=port_no)
260
261     def get_networks(self, dpid):
262         return set(self[dpid].values())
263
264     def get_network_safe(self, dpid, port_no):
265         port = self.get(dpid, {}).get(port_no)
266         if port is None:
267             return self.nw_id_unknown
268         return port.network_id
269
270     def get_mac(self, dpid, port_no):
271         port = self.get_port(dpid, port_no)
272         return port.mac_address
273
274     def _set_mac(self, network_id, dpid, port_no, port, mac_address):
275         if not (port.network_id is None or
276                 port.network_id == network_id or
277                 port.network_id == self.nw_id_unknown):
278             raise PortNotFound(network_id=network_id, dpid=dpid, port=port_no)
279
280         port.network_id = network_id
281         port.mac_address = mac_address
282         if port.network_id and port.mac_address:
283             self.send_event(EventMacAddress(
284                             dpid, port_no, port.network_id, port.mac_address,
285                             True))
286
287     def set_mac(self, network_id, dpid, port_no, mac_address):
288         port = self.get_port(dpid, port_no)
289         if port.mac_address is not None:
290             raise MacAddressAlreadyExist(dpid=dpid, port=port_no,
291                                          mac_address=mac_address)
292         self._set_mac(network_id, dpid, port_no, port, mac_address)
293
294     def update_mac(self, network_id, dpid, port_no, mac_address):
295         port = self.get_port(dpid, port_no)
296         if port.mac_address is None:
297             self._set_mac(network_id, dpid, port_no, port, mac_address)
298             return
299
300         # For now, we don't allow changing mac address.
301         if port.mac_address != mac_address:
302             raise MacAddressAlreadyExist(dpid=dpid, port=port_no,
303                                          mac_address=port.mac_address)
304
305
306 MacPort = collections.namedtuple('MacPort', ('dpid', 'port_no'))
307
308
309 class MacToPort(collections.defaultdict):
310     """mac_address -> set of MacPort(dpid, port_no)"""
311
312     def __init__(self):
313         super(MacToPort, self).__init__(set)
314
315     def add_port(self, dpid, port_no, mac_address):
316         self[mac_address].add(MacPort(dpid, port_no))
317
318     def remove_port(self, dpid, port_no, mac_address):
319         ports = self[mac_address]
320         ports.discard(MacPort(dpid, port_no))
321         if not ports:
322             del self[mac_address]
323
324     def get_ports(self, mac_address):
325         return self[mac_address]
326
327
328 class MacAddresses(dict):
329     """network_id -> mac_address -> set of (dpid, port_no)"""
330
331     def add_port(self, network_id, dpid, port_no, mac_address):
332         mac2port = self.setdefault(network_id, MacToPort())
333         mac2port.add_port(dpid, port_no, mac_address)
334
335     def remove_port(self, network_id, dpid, port_no, mac_address):
336         mac2port = self.get(network_id)
337         if mac2port is None:
338             return
339         mac2port.remove_port(dpid, port_no, mac_address)
340         if not mac2port:
341             del self[network_id]
342
343     def get_ports(self, network_id, mac_address):
344         mac2port = self.get(network_id)
345         if not mac2port:
346             return set()
347         return mac2port.get_ports(mac_address)
348
349
350 class Network(app_manager.RyuApp):
351     def __init__(self, nw_id_unknown=NW_ID_UNKNOWN):
352         super(Network, self).__init__()
353         self.name = 'network'
354         self.nw_id_unknown = nw_id_unknown
355         self.networks = Networks(self.send_event_to_observers)
356         self.dpids = DPIDs(self.send_event_to_observers, nw_id_unknown)
357         self.mac_addresses = MacAddresses()
358
359     def _check_nw_id_unknown(self, network_id):
360         if network_id == self.nw_id_unknown:
361             raise NetworkAlreadyExist(network_id=network_id)
362
363     def list_networks(self):
364         return self.networks.list_networks()
365
366     def update_network(self, network_id):
367         self._check_nw_id_unknown(network_id)
368         self.networks.update_network(network_id)
369
370     def create_network(self, network_id):
371         self._check_nw_id_unknown(network_id)
372         self.networks.create_network(network_id)
373
374     def remove_network(self, network_id):
375         self.networks.remove_network(network_id)
376
377     def list_ports(self, network_id):
378         return self.networks.list_ports(network_id)
379
380     def list_ports_noraise(self, network_id):
381         try:
382             return self.list_ports(network_id)
383         except NetworkNotFound:
384             return []
385
386     def _update_port(self, network_id, dpid, port, port_may_exist):
387         def _known_nw_id(nw_id):
388             return nw_id is not None and nw_id != self.nw_id_unknown
389
390         queue_add_event = False
391         self._check_nw_id_unknown(network_id)
392         try:
393             old_network_id = self.dpids.get_network_safe(dpid, port)
394             if (self.networks.has_port(network_id, dpid, port) or
395                     _known_nw_id(old_network_id)):
396                 if not port_may_exist:
397                     raise PortAlreadyExist(network_id=network_id,
398                                            dpid=dpid, port=port)
399
400             if old_network_id != network_id:
401                 queue_add_event = True
402                 self.networks.add_raw(network_id, dpid, port)
403                 if _known_nw_id(old_network_id):
404                     self.networks.remove_raw(old_network_id, dpid, port)
405         except KeyError:
406             raise NetworkNotFound(network_id=network_id)
407
408         self.dpids.update_port(dpid, port, network_id)
409         if queue_add_event:
410             self.networks.add_event(network_id, dpid, port)
411
412     def create_port(self, network_id, dpid, port):
413         self._update_port(network_id, dpid, port, False)
414
415     def update_port(self, network_id, dpid, port):
416         self._update_port(network_id, dpid, port, True)
417
418     def _get_old_mac(self, network_id, dpid, port_no):
419         try:
420             port = self.dpids.get_port(dpid, port_no)
421         except PortNotFound:
422             pass
423         else:
424             if port.network_id == network_id:
425                 return port.mac_address
426         return None
427
428     def remove_port(self, network_id, dpid, port_no):
429         # generate event first, then do the real task
430         old_mac_address = self._get_old_mac(network_id, dpid, port_no)
431
432         self.dpids.remove_port(dpid, port_no)
433         try:
434             self.networks.remove(network_id, dpid, port_no)
435         except NetworkNotFound:
436             # port deletion can be called after network deletion
437             # due to Openstack auto deletion port.(dhcp/router port)
438             pass
439         if old_mac_address is not None:
440             self.mac_addresses.remove_port(network_id, dpid, port_no,
441                                            old_mac_address)
442
443     #
444     # methods for gre tunnel
445     #
446
447     def get_dpids(self, network_id):
448         return self.networks.get_dpids(network_id)
449
450     def has_network(self, network_id):
451         return self.networks.has_network(network_id)
452
453     def get_networks(self, dpid):
454         return self.dpids.get_networks(dpid)
455
456     def create_mac(self, network_id, dpid, port_no, mac_address):
457         self.mac_addresses.add_port(network_id, dpid, port_no, mac_address)
458         self.dpids.set_mac(network_id, dpid, port_no, mac_address)
459
460     def update_mac(self, network_id, dpid, port_no, mac_address):
461         old_mac_address = self._get_old_mac(network_id, dpid, port_no)
462
463         self.dpids.update_mac(network_id, dpid, port_no, mac_address)
464         if old_mac_address is not None:
465             self.mac_addresses.remove_port(network_id, dpid, port_no,
466                                            old_mac_address)
467         self.mac_addresses.add_port(network_id, dpid, port_no, mac_address)
468
469     def get_mac(self, dpid, port_no):
470         return self.dpids.get_mac(dpid, port_no)
471
472     def list_mac(self, dpid, port_no):
473         mac_address = self.dpids.get_mac(dpid, port_no)
474         if mac_address is None:
475             return []
476         return [mac_address]
477
478     def get_ports(self, dpid, network_id=None, mac_address=None):
479         return self.dpids.get_ports(dpid, network_id, mac_address)
480
481     def get_port(self, dpid, port_no):
482         return self.dpids.get_port(dpid, port_no)
483
484     def get_ports_with_mac(self, network_id, mac_address):
485         return self.mac_addresses.get_ports(network_id, mac_address)
486
487     #
488     # methods for simple_isolation
489     #
490
491     def same_network(self, dpid, nw_id, out_port, allow_nw_id_external=None):
492         assert nw_id != self.nw_id_unknown
493         out_nw = self.dpids.get_network_safe(dpid, out_port)
494
495         if nw_id == out_nw:
496             return True
497
498         if (allow_nw_id_external is not None and
499                 (allow_nw_id_external == nw_id or
500                     allow_nw_id_external == out_nw)):
501             # allow external network -> known network id
502             return True
503
504         self.logger.debug('blocked dpid %s nw_id %s out_port %d out_nw %s'
505                           'external %s',
506                           dpid, nw_id, out_port, out_nw, allow_nw_id_external)
507         return False
508
509     def get_network(self, dpid, port):
510         return self.dpids.get_network(dpid, port)
511
512     def add_datapath(self, ofp_switch_features):
513         datapath = ofp_switch_features.datapath
514         dpid = ofp_switch_features.datapath_id
515         ports = ofp_switch_features.ports
516         self.dpids.setdefault_dpid(dpid)
517         for port_no in ports:
518             self.port_added(datapath, port_no)
519
520     def port_added(self, datapath, port_no):
521         if port_no == 0 or port_no >= datapath.ofproto.OFPP_MAX:
522             # skip fake output ports
523             return
524
525         self.dpids.setdefault_network(datapath.id, port_no)
526
527     def port_deleted(self, dpid, port_no):
528         self.dpids.remove_port(dpid, port_no)
529
530     def filter_ports(self, dpid, in_port, nw_id, allow_nw_id_external=None):
531         assert nw_id != self.nw_id_unknown
532         ret = []
533
534         for port in self.get_ports(dpid):
535             nw_id_ = port.network_id
536             if port.port_no == in_port:
537                 continue
538
539             if nw_id_ == nw_id:
540                 ret.append(port.port_no)
541             elif (allow_nw_id_external is not None and
542                   nw_id_ == allow_nw_id_external):
543                 ret.append(port.port_no)
544
545         return ret