1 # Copyright (C) 2011 Nippon Telegraph and Telephone Corporation.
2 # Copyright (C) 2011 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.
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
26 NW_ID_UNKNOWN = '__NW_ID_UNKNOWN__'
29 class MacAddressAlreadyExist(ryu_exc.RyuException):
30 message = 'port (%(dpid)s, %(port)s) has already mac %(mac_address)s'
33 class EventNetworkDel(event.EventBase):
35 An event class for network deletion.
37 This event is generated when a network is deleted by the REST API.
38 An instance has at least the following attributes.
40 ========== ===================================================================
42 ========== ===================================================================
44 ========== ===================================================================
47 def __init__(self, network_id):
48 super(EventNetworkDel, self).__init__()
49 self.network_id = network_id
52 class EventNetworkPort(event.EventBase):
54 An event class for notification of port arrival and deperture.
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.
60 ========== ================================================================
62 ========== ================================================================
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 ========== ================================================================
70 def __init__(self, network_id, dpid, port_no, add_del):
71 super(EventNetworkPort, self).__init__()
72 self.network_id = network_id
74 self.port_no = port_no
75 self.add_del = add_del
78 class EventMacAddress(event.EventBase):
80 An event class for end-point MAC address registration.
82 This event is generated when a end-point MAC address is updated
84 An instance has at least the following attributes.
86 =========== ===============================================================
88 =========== ===============================================================
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
94 add_del False if this event is a result of a port removal. Otherwise
96 =========== ===============================================================
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
104 self.port_no = port_no
105 self.network_id = network_id
106 self.mac_address = mac_address
107 self.add_del = add_del
110 class Networks(dict):
111 "network_id -> set of (dpid, port_no)"
113 def __init__(self, f):
114 super(Networks, self).__init__()
117 def list_networks(self):
118 return list(self.keys())
120 def has_network(self, network_id):
121 return network_id in self
123 def update_network(self, network_id):
124 self.setdefault(network_id, set())
126 def create_network(self, network_id):
127 if network_id in self:
128 raise NetworkAlreadyExist(network_id=network_id)
130 self[network_id] = set()
132 def remove_network(self, network_id):
134 ports = self[network_id]
136 raise NetworkNotFound(network_id=network_id)
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))
144 def list_ports(self, network_id):
146 # use list() to keep compatibility for output
147 # set() isn't json serializable
148 return list(self[network_id])
150 raise NetworkNotFound(network_id=network_id)
152 def add_raw(self, network_id, dpid, port_no):
153 self[network_id].add((dpid, port_no))
155 def add_event(self, network_id, dpid, port_no):
157 EventNetworkPort(network_id, dpid, port_no, True))
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)
163 def _remove_event(self, network_id, dpid, port_no):
164 self.send_event(EventNetworkPort(network_id, dpid, port_no, False))
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)
172 def remove(self, network_id, dpid, port_no):
174 self.remove_raw(network_id, dpid, port_no)
176 raise NetworkNotFound(network_id=network_id)
178 raise PortNotFound(network_id=network_id, dpid=dpid, port=port_no)
180 def has_port(self, network_id, dpid, port):
181 return (dpid, port) in self[network_id]
183 def get_dpids(self, network_id):
185 ports = self[network_id]
189 # python 2.6 doesn't support set comprehension
190 # port = (dpid, port_no)
191 return set([port[0] for port in ports])
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
203 """dpid -> port_no -> Port(port_no, network_id, mac_address)"""
205 def __init__(self, f, nw_id_unknown):
206 super(DPIDs, self).__init__()
208 self.nw_id_unknown = nw_id_unknown
210 def setdefault_dpid(self, dpid):
211 return self.setdefault(dpid, {})
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))
218 def setdefault_network(self, dpid, port_no):
219 self._setdefault_network(dpid, port_no, self.nw_id_unknown)
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
225 def remove_port(self, dpid, port_no):
227 # self.dpids[dpid][port_no] can be already deleted by
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,
236 raise PortNotFound(dpid=dpid, port=port_no, network_id=None)
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]
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]
249 def get_port(self, dpid, port_no):
251 return self[dpid][port_no]
253 raise PortNotFound(dpid=dpid, port=port_no, network_id=None)
255 def get_network(self, dpid, port_no):
257 return self[dpid][port_no].network_id
259 raise PortUnknown(dpid=dpid, port=port_no)
261 def get_networks(self, dpid):
262 return set(self[dpid].values())
264 def get_network_safe(self, dpid, port_no):
265 port = self.get(dpid, {}).get(port_no)
267 return self.nw_id_unknown
268 return port.network_id
270 def get_mac(self, dpid, port_no):
271 port = self.get_port(dpid, port_no)
272 return port.mac_address
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)
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,
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)
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)
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)
306 MacPort = collections.namedtuple('MacPort', ('dpid', 'port_no'))
309 class MacToPort(collections.defaultdict):
310 """mac_address -> set of MacPort(dpid, port_no)"""
313 super(MacToPort, self).__init__(set)
315 def add_port(self, dpid, port_no, mac_address):
316 self[mac_address].add(MacPort(dpid, port_no))
318 def remove_port(self, dpid, port_no, mac_address):
319 ports = self[mac_address]
320 ports.discard(MacPort(dpid, port_no))
322 del self[mac_address]
324 def get_ports(self, mac_address):
325 return self[mac_address]
328 class MacAddresses(dict):
329 """network_id -> mac_address -> set of (dpid, port_no)"""
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)
335 def remove_port(self, network_id, dpid, port_no, mac_address):
336 mac2port = self.get(network_id)
339 mac2port.remove_port(dpid, port_no, mac_address)
343 def get_ports(self, network_id, mac_address):
344 mac2port = self.get(network_id)
347 return mac2port.get_ports(mac_address)
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()
359 def _check_nw_id_unknown(self, network_id):
360 if network_id == self.nw_id_unknown:
361 raise NetworkAlreadyExist(network_id=network_id)
363 def list_networks(self):
364 return self.networks.list_networks()
366 def update_network(self, network_id):
367 self._check_nw_id_unknown(network_id)
368 self.networks.update_network(network_id)
370 def create_network(self, network_id):
371 self._check_nw_id_unknown(network_id)
372 self.networks.create_network(network_id)
374 def remove_network(self, network_id):
375 self.networks.remove_network(network_id)
377 def list_ports(self, network_id):
378 return self.networks.list_ports(network_id)
380 def list_ports_noraise(self, network_id):
382 return self.list_ports(network_id)
383 except NetworkNotFound:
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
390 queue_add_event = False
391 self._check_nw_id_unknown(network_id)
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)
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)
406 raise NetworkNotFound(network_id=network_id)
408 self.dpids.update_port(dpid, port, network_id)
410 self.networks.add_event(network_id, dpid, port)
412 def create_port(self, network_id, dpid, port):
413 self._update_port(network_id, dpid, port, False)
415 def update_port(self, network_id, dpid, port):
416 self._update_port(network_id, dpid, port, True)
418 def _get_old_mac(self, network_id, dpid, port_no):
420 port = self.dpids.get_port(dpid, port_no)
424 if port.network_id == network_id:
425 return port.mac_address
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)
432 self.dpids.remove_port(dpid, port_no)
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)
439 if old_mac_address is not None:
440 self.mac_addresses.remove_port(network_id, dpid, port_no,
444 # methods for gre tunnel
447 def get_dpids(self, network_id):
448 return self.networks.get_dpids(network_id)
450 def has_network(self, network_id):
451 return self.networks.has_network(network_id)
453 def get_networks(self, dpid):
454 return self.dpids.get_networks(dpid)
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)
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)
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,
467 self.mac_addresses.add_port(network_id, dpid, port_no, mac_address)
469 def get_mac(self, dpid, port_no):
470 return self.dpids.get_mac(dpid, port_no)
472 def list_mac(self, dpid, port_no):
473 mac_address = self.dpids.get_mac(dpid, port_no)
474 if mac_address is None:
478 def get_ports(self, dpid, network_id=None, mac_address=None):
479 return self.dpids.get_ports(dpid, network_id, mac_address)
481 def get_port(self, dpid, port_no):
482 return self.dpids.get_port(dpid, port_no)
484 def get_ports_with_mac(self, network_id, mac_address):
485 return self.mac_addresses.get_ports(network_id, mac_address)
488 # methods for simple_isolation
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)
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
504 self.logger.debug('blocked dpid %s nw_id %s out_port %d out_nw %s'
506 dpid, nw_id, out_port, out_nw, allow_nw_id_external)
509 def get_network(self, dpid, port):
510 return self.dpids.get_network(dpid, port)
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)
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
525 self.dpids.setdefault_network(datapath.id, port_no)
527 def port_deleted(self, dpid, port_no):
528 self.dpids.remove_port(dpid, port_no)
530 def filter_ports(self, dpid, in_port, nw_id, allow_nw_id_external=None):
531 assert nw_id != self.nw_id_unknown
534 for port in self.get_ports(dpid):
535 nw_id_ = port.network_id
536 if port.port_no == in_port:
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)