- 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 <yamah...@valinux.co.jp> --- ryu/controller/network.py | 329 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 279 insertions(+), 50 deletions(-) diff --git a/ryu/controller/network.py b/ryu/controller/network.py index ce6ca02..abfd12c 100644 --- a/ryu/controller/network.py +++ b/ryu/controller/network.py @@ -16,76 +16,285 @@ import logging +import ryu.exception as ryu_exc +from ryu.app.rest_nw_id import NW_ID_UNKNOWN +from ryu.controller import dispatcher +from ryu.controller import event 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 MacAddressAlreadyExist(ryu_exc.RyuException): + message = 'port (%(dpid)s, %(port)s) has already mac %(mac_address)s' + + +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 +303,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) - # self.dpids[dpid][port] can be already deleted by port_deleted() - self.dpids[dpid].pop(port, None) + # + # 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) + + 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 +344,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 +361,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 +376,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 ------------------------------------------------------------------------------ Keep yourself connected to Go Parallel: TUNE You got it built. Now make it sing. Tune shows you how. http://goparallel.sourceforge.net _______________________________________________ Ryu-devel mailing list Ryu-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/ryu-devel