- Factor out from network.py the logic to track network and dpid - introduce class Port to allow other info Later we'll track mac address associated to port. So allow intermediate layer to contain infos related to Port. - make network track mac address associated to port - generate events
Signed-off-by: Isaku Yamahata <[email protected]> --- ryu/controller/network.py | 325 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 275 insertions(+), 50 deletions(-) diff --git a/ryu/controller/network.py b/ryu/controller/network.py index ce6ca02..911b1fe 100644 --- a/ryu/controller/network.py +++ b/ryu/controller/network.py @@ -16,76 +16,281 @@ import logging +from ryu.app.rest_nw_id import NW_ID_UNKNOWN +from ryu.controller import dispatcher +from ryu.controller import event +from ryu.exception import MacAddressAlreadyExist from ryu.exception import NetworkNotFound, NetworkAlreadyExist from ryu.exception import PortAlreadyExist, PortNotFound, PortUnknown -from ryu.app.rest_nw_id import NW_ID_UNKNOWN + LOG = logging.getLogger('ryu.controller.network') -class Network(object): - def __init__(self, nw_id_unknown=NW_ID_UNKNOWN): - self.nw_id_unknown = nw_id_unknown - self.networks = {} - self.dpids = {} +QUEUE_NAME_NETWORK_TENANT_EV = 'network_tenant_event' +DISPATCHER_NAME_NETWORK_TENANT_EV = 'network_tenant_handler' +NETWORK_TENANT_EV_DISPATCHER = dispatcher.EventDispatcher( + DISPATCHER_NAME_NETWORK_TENANT_EV) - def _dpids_setdefault(self, dpid): - return self.dpids.setdefault(dpid, {}) - def _check_nw_id_unknown(self, network_id): - if network_id == self.nw_id_unknown: - raise NetworkAlreadyExist(network_id=network_id) +class EventNetworkDel(event.EventBase): + def __init__(self, network_id): + super(EventNetworkDel, self).__init__() + self.network_id = network_id + + +class EventNetworkPort(event.EventBase): + def __init__(self, network_id, dpid, port_no, add_del): + super(EventNetworkPort, self).__init__() + self.network_id = network_id + self.dpid = dpid + self.port_no = port_no + self.add_del = add_del + + +class EventMacAddress(event.EventBase): + def __init__(self, dpid, port_no, network_id, mac_address, add_del): + super(EventMacAddress, self).__init__() + assert network_id is not None + assert mac_address is not None + self.dpid = dpid + self.port_no = port_no + self.network_id = network_id + self.mac_address = mac_address + self.add_del = add_del + + +class Networks(dict): + "network_id -> set of (dpid, port_no)" + def __init__(self, ev_q): + super(Networks, self).__init__() + self.ev_q = ev_q def list_networks(self): - return self.networks.keys() + return self.keys() + + def has_network(self, network_id): + return network_id in self def update_network(self, network_id): - self._check_nw_id_unknown(network_id) - self.networks.setdefault(network_id, set()) + self.setdefault(network_id, set()) def create_network(self, network_id): - self._check_nw_id_unknown(network_id) - if network_id in self.networks: + if network_id in self: raise NetworkAlreadyExist(network_id=network_id) - self.networks[network_id] = set() + self[network_id] = set() def remove_network(self, network_id): try: - del(self.networks[network_id]) + network = self[network_id] except KeyError: raise NetworkNotFound(network_id=network_id) + for (dpid, port_no) in network: + self.ev_q.queue(EventNetworkPort(network_id, dpid, port_no, False)) + self.ev_q.queue(EventNetworkDel(network_id)) + del self[network_id] + def list_ports(self, network_id): try: # use list() to keep compatibility for output # set() isn't json serializable - return list(self.networks[network_id]) + return list(self[network_id]) + except KeyError: + raise NetworkNotFound(network_id=network_id) + + def add_raw(self, network_id, dpid, port_no): + self[network_id].add((dpid, port_no)) + + def add_event(self, network_id, dpid, port_no): + self.ev_q.queue(EventNetworkPort(network_id, dpid, port_no, True)) + + # def add(self, network_id, dpid, port_no): + # self.add_raw(network_id, dpid, port_no) + # self.add_event(network_id, dpid, port_no) + + def remove_raw(self, network_id, dpid, port_no): + if (dpid, port_no) in self[network_id]: + self.ev_q.queue(EventNetworkPort(network_id, dpid, port_no, False)) + self[network_id].remove((dpid, port_no)) + + def remove(self, network_id, dpid, port_no): + try: + self.remove_raw(network_id, dpid, port_no) except KeyError: raise NetworkNotFound(network_id=network_id) + except ValueError: + raise PortNotFound(network_id=network_id, dpid=dpid, port=port_no) + + def has_port(self, network_id, dpid, port): + return (dpid, port) in self[network_id] + + def get_dpids(self, network_id): + try: + ports = self[network_id] + except KeyError: + return set() + + # python 2.6 doesn't support set comprehension + # port = (dpid, port_no) + return set([port[0] for port in ports]) + + +class Port(object): + def __init__(self, port_no, network_id, mac_address=None): + super(Port, self).__init__() + self.port_no = port_no + self.network_id = network_id + self.mac_address = mac_address + + +class DPIDs(dict): + """dpid -> port_no -> network_id""" + def __init__(self, ev_q, nw_id_unknown): + super(DPIDs, self).__init__() + self.ev_q = ev_q + self.nw_id_unknown = nw_id_unknown + + def setdefault_dpid(self, dpid): + return self.setdefault(dpid, {}) + + def _setdefault_network(self, dpid, port_no, default_network_id): + dp = self.setdefault_dpid(dpid) + return dp.setdefault(port_no, Port(port_no=port_no, + network_id=default_network_id)) + + def setdefault_network(self, dpid, port_no): + self._setdefault_network(dpid, port_no, self.nw_id_unknown) + + def update_port(self, dpid, port_no, network_id): + port = self._setdefault_network(dpid, port_no, network_id) + port.network_id = network_id + + def remove_port(self, dpid, port_no): + try: + # self.dpids[dpid][port_no] can be already deleted by + # port_deleted() + port = self[dpid].get(port_no) + if port and port.network_id and port.mac_address: + self.ev_q.queue(EventMacAddress( + dpid, port_no, port.network_id, port.mac_address, False)) + + self[dpid].pop(port_no, None) + except KeyError: + raise PortNotFound(dpid=dpid, port=port_no, network_id=None) + + def get_ports(self, dpid): + return self.get(dpid, {}).values() + + def get_port(self, dpid, port_no): + try: + return self[dpid][port_no] + except KeyError: + raise PortNotFound(dpid=dpid, port=port_no, network_id=None) + + def get_network(self, dpid, port_no): + try: + return self[dpid][port_no].network_id + except KeyError: + raise PortUnknown(dpid=dpid, port=port_no) + + def get_network_safe(self, dpid, port_no): + port = self.get(dpid, {}).get(port_no) + if port is None: + return self.nw_id_unknown + return port.network_id + + def get_mac(self, dpid, port_no): + port = self.get_port(dpid, port_no) + return port.mac_address + + def _set_mac(self, network_id, dpid, port_no, port, mac_address): + if not (port.network_id is None or + port.network_id == network_id or + port.netowrk_id == self.nw_id_unknown): + raise PortNotFound(network_id=network_id, dpid=dpid, port=port_no) + + port.network_id = network_id + port.mac_address = mac_address + if port.network_id and port.mac_address: + self.ev_q.queue(EventMacAddress( + dpid, port_no, port.network_id, port.mac_address, True)) + + def set_mac(self, network_id, dpid, port_no, mac_address): + port = self.get_port(dpid, port_no) + if port.mac_address is not None: + raise MacAddressAlreadyExist(dpid=dpid, port=port_no, + mac_address=mac_address) + self._set_mac(network_id, dpid, port_no, port, mac_address) + + def update_mac(self, network_id, dpid, port_no, mac_address): + port = self.get_port(dpid, port_no) + if port.mac_address is None: + self._set_mac(network_id, dpid, port_no, port, mac_address) + return + + # For now, we don't allow changing mac address. + if port.mac_address != mac_address: + raise MacAddressAlreadyExist(dpid=dpid, port=port_no, + mac_address=port.mac_address) + + +class Network(object): + def __init__(self, nw_id_unknown=NW_ID_UNKNOWN): + super(Network, self).__init__() + self.nw_id_unknown = nw_id_unknown + ev_q = dispatcher.EventQueue(QUEUE_NAME_NETWORK_TENANT_EV, + NETWORK_TENANT_EV_DISPATCHER) + self.networks = Networks(ev_q) + self.dpids = DPIDs(ev_q, nw_id_unknown) + + def _check_nw_id_unknown(self, network_id): + if network_id == self.nw_id_unknown: + raise NetworkAlreadyExist(network_id=network_id) + + def list_networks(self): + return self.networks.list_networks() + + def update_network(self, network_id): + self._check_nw_id_unknown(network_id) + self.networks.update_network(network_id) + + def create_network(self, network_id): + self._check_nw_id_unknown(network_id) + self.networks.create_network(network_id) + + def remove_network(self, network_id): + self.networks.remove_network(network_id) + + def list_ports(self, network_id): + return self.networks.list_ports(network_id) def _update_port(self, network_id, dpid, port, port_may_exist): def _known_nw_id(nw_id): return nw_id is not None and nw_id != self.nw_id_unknown + queue_add_event = False self._check_nw_id_unknown(network_id) try: - old_network_id = self.dpids.get(dpid, {}).get(port, None) - if ((dpid, port) in self.networks[network_id] or + old_network_id = self.dpids.get_network_safe(dpid, port) + if (self.networks.has_port(network_id, dpid, port) or _known_nw_id(old_network_id)): if not port_may_exist: raise PortAlreadyExist(network_id=network_id, dpid=dpid, port=port) if old_network_id != network_id: - self.networks[network_id].add((dpid, port)) + queue_add_event = True + self.networks.add_raw(network_id, dpid, port) if _known_nw_id(old_network_id): - self.networks[old_network_id].remove((dpid, port)) + self.networks.remove_raw(old_network_id, dpid, port) except KeyError: raise NetworkNotFound(network_id=network_id) - self._dpids_setdefault(dpid) - self.dpids[dpid][port] = network_id + self.dpids.update_port(dpid, port, network_id) + if queue_add_event: + self.networks.add_event(network_id, dpid, port) def create_port(self, network_id, dpid, port): self._update_port(network_id, dpid, port, False) @@ -94,15 +299,40 @@ class Network(object): self._update_port(network_id, dpid, port, True) def remove_port(self, network_id, dpid, port): - try: - self.networks[network_id].remove((dpid, port)) - except KeyError: - raise NetworkNotFound(network_id=network_id) - except ValueError: - raise PortNotFound(network_id=network_id, dpid=dpid, port=port) + # generate event first, then do the real task + self.dpids.remove_port(dpid, port) + self.networks.remove(network_id, dpid, port) + + # + # methods for gre tunnel + # + + def get_dpids(self, network_id): + return self.networks.get_dpids(network_id) + + def has_network(self, network_id): + return self.networks.has_network(network_id) + + def create_mac(self, network_id, dpid, port_no, mac_address): + self.dpids.set_mac(network_id, dpid, port_no, mac_address) + + def update_mac(self, network_id, dpid, port_no, mac_address): + self.dpids.update_mac(network_id, dpid, port_no, mac_address) - # self.dpids[dpid][port] can be already deleted by port_deleted() - self.dpids[dpid].pop(port, None) + def get_mac(self, dpid, port_no): + return self.dpids.get_mac(dpid, port_no) + + def list_mac(self, dpid, port_no): + mac_address = self.dpids.get_mac(dpid, port_no) + if mac_address is None: + return [] + return [mac_address] + + def get_ports(self, dpid): + return self.dpids.get_ports(dpid) + + def get_port(self, dpid, port_no): + return self.dpids.get_port(dpid, port_no) # # methods for simple_isolation @@ -110,8 +340,7 @@ class Network(object): def same_network(self, dpid, nw_id, out_port, allow_nw_id_external=None): assert nw_id != self.nw_id_unknown - dp = self.dpids.get(dpid, {}) - out_nw = dp.get(out_port) + out_nw = self.dpids.get_network_safe(dpid, out_port) if nw_id == out_nw: return True @@ -128,16 +357,13 @@ class Network(object): return False def get_network(self, dpid, port): - try: - return self.dpids[dpid][port] - except KeyError: - raise PortUnknown(dpid=dpid, port=port) + return self.dpids.get_network(dpid, port) def add_datapath(self, ofp_switch_features): datapath = ofp_switch_features.datapath dpid = ofp_switch_features.datapath_id ports = ofp_switch_features.ports - self._dpids_setdefault(dpid) + self.dpids.setdefault_dpid(dpid) for port_no in ports: self.port_added(datapath, port_no) @@ -146,25 +372,24 @@ class Network(object): # skip fake output ports return - dp = self._dpids_setdefault(datapath.id) - dp.setdefault(port_no, self.nw_id_unknown) + self.dpids.setdefault_network(datapath.id, port_no) def port_deleted(self, dpid, port_no): - self.dpids[dpid].pop(port_no, None) + self.dpids.remove_port(dpid, port_no) def filter_ports(self, dpid, in_port, nw_id, allow_nw_id_external=None): assert nw_id != self.nw_id_unknown ret = [] - ports = self.dpids.get(dpid, {}) - for port_no, _nw_id in ports.items(): - if port_no == in_port: + for port in self.get_ports(dpid): + nw_id_ = port.network_id + if port.port_no == in_port: continue - if _nw_id == nw_id: - ret.append(port_no) + if nw_id_ == nw_id: + ret.append(port.port_no) elif (allow_nw_id_external is not None and - _nw_id == allow_nw_id_external): - ret.append(port_no) + nw_id_ == allow_nw_id_external): + ret.append(port.port_no) return ret -- 1.7.10.4 ------------------------------------------------------------------------------ Don't let slow site performance ruin your business. Deploy New Relic APM Deploy New Relic app performance management and know exactly what is happening inside your Ruby, Python, PHP, Java, and .NET app Try New Relic at no cost today and get our sweet Data Nerd shirt too! http://p.sf.net/sfu/newrelic-dev2dev _______________________________________________ Ryu-devel mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/ryu-devel
