| package net.onrc.onos.core.topology; |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.concurrent.ConcurrentHashMap; |
| import java.util.concurrent.ConcurrentMap; |
| import java.util.concurrent.locks.Lock; |
| import java.util.concurrent.locks.ReadWriteLock; |
| import java.util.concurrent.locks.ReentrantReadWriteLock; |
| |
| import javax.annotation.concurrent.GuardedBy; |
| |
| import net.floodlightcontroller.util.MACAddress; |
| import net.onrc.onos.core.util.Dpid; |
| import net.onrc.onos.core.util.PortNumber; |
| import net.onrc.onos.core.util.SwitchPort; |
| |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| import com.google.common.collect.HashMultimap; |
| import com.google.common.collect.Multimap; |
| import com.google.common.collect.Multimaps; |
| |
| public class TopologyImpl implements Topology { |
| @SuppressWarnings("unused") |
| private static final Logger log = LoggerFactory.getLogger(TopologyImpl.class); |
| |
| // DPID -> Switch |
| private final ConcurrentMap<Dpid, Switch> switches; |
| // XXX may need to be careful when shallow copying. |
| private final ConcurrentMap<Dpid, ConcurrentMap<PortNumber, Port>> ports; |
| |
| // Index from Port to Device |
| private final Multimap<SwitchPort, Device> devices; |
| private final ConcurrentMap<MACAddress, Device> mac2Device; |
| |
| // SwitchPort -> (type -> Link) |
| private final ConcurrentMap<SwitchPort, ConcurrentMap<String, Link>> outgoingLinks; |
| private final ConcurrentMap<SwitchPort, ConcurrentMap<String, Link>> incomingLinks; |
| |
| private ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); |
| private Lock readLock = readWriteLock.readLock(); |
| // TODO use the write lock after refactor |
| private Lock writeLock = readWriteLock.writeLock(); |
| |
| public TopologyImpl() { |
| // TODO: Does these object need to be stored in Concurrent Collection? |
| switches = new ConcurrentHashMap<>(); |
| ports = new ConcurrentHashMap<>(); |
| devices = Multimaps.synchronizedMultimap( |
| HashMultimap.<SwitchPort, Device>create()); |
| mac2Device = new ConcurrentHashMap<>(); |
| outgoingLinks = new ConcurrentHashMap<>(); |
| incomingLinks = new ConcurrentHashMap<>(); |
| } |
| |
| @Override |
| public Switch getSwitch(Dpid dpid) { |
| // TODO Check if it is safe to directly return this Object. |
| return switches.get(dpid); |
| } |
| |
| // Only add switch. |
| protected void putSwitch(Switch sw) { |
| switches.put(sw.getDpid(), sw); |
| ports.putIfAbsent(sw.getDpid(), new ConcurrentHashMap<PortNumber, Port>()); |
| } |
| |
| // TODO remove me when ready |
| protected void removeSwitch(Long dpid) { |
| removeSwitch(new Dpid(dpid)); |
| } |
| |
| // XXX Will remove ports in snapshot as side-effect. |
| protected void removeSwitch(Dpid dpid) { |
| switches.remove(dpid); |
| ports.remove(dpid); |
| } |
| |
| // This method is expected to be serialized by writeLock. |
| protected void putPort(Port port) { |
| ConcurrentMap<PortNumber, Port> portMap = ports.get(port.getDpid()); |
| if (portMap == null) { |
| portMap = new ConcurrentHashMap<>(); |
| ConcurrentMap<PortNumber, Port> existing = |
| ports.putIfAbsent(port.getDpid(), portMap); |
| if (existing != null) { |
| // port map was added concurrently, using theirs |
| portMap = existing; |
| } |
| } |
| portMap.put(port.getNumber(), port); |
| } |
| |
| protected void removePort(Port port) { |
| ConcurrentMap<PortNumber, Port> portMap = ports.get(port.getDpid()); |
| if (portMap != null) { |
| portMap.remove(port.getNumber()); |
| } |
| } |
| |
| @Override |
| public Iterable<Switch> getSwitches() { |
| // TODO Check if it is safe to directly return this Object. |
| return Collections.unmodifiableCollection(switches.values()); |
| } |
| |
| @Override |
| public Port getPort(Dpid dpid, PortNumber number) { |
| ConcurrentMap<PortNumber, Port> portMap = ports.get(dpid); |
| if (portMap != null) { |
| return portMap.get(number); |
| } |
| return null; |
| } |
| |
| @Override |
| public Port getPort(SwitchPort port) { |
| return getPort(port.dpid(), port.port()); |
| } |
| |
| @Override |
| public Collection<Port> getPorts(Dpid dpid) { |
| ConcurrentMap<PortNumber, Port> portMap = ports.get(dpid); |
| if (portMap == null) { |
| return Collections.emptyList(); |
| } |
| return Collections.unmodifiableCollection(portMap.values()); |
| } |
| |
| @Override |
| public Link getOutgoingLink(Dpid dpid, PortNumber number) { |
| return getOutgoingLink(new SwitchPort(dpid, number)); |
| } |
| |
| @Override |
| public Link getOutgoingLink(SwitchPort port) { |
| Map<String, Link> links = outgoingLinks.get(port); |
| return getPacketLinkIfExists(links); |
| } |
| |
| // TODO remove when we no longer need packet fall back behavior |
| /** |
| * Gets the "packet" link if such exists, if not return whatever found. |
| * |
| * @param links Collection of links to search from |
| * @return Link instance found or null if no link exists |
| */ |
| private Link getPacketLinkIfExists(Map<String, Link> links) { |
| |
| if (links == null) { |
| return null; |
| } |
| |
| Link link = links.get(TopologyElement.TYPE_PACKET_LAYER); |
| if (link != null) { |
| // return packet link |
| return link; |
| } else { |
| // return whatever found |
| Iterator<Link> it = links.values().iterator(); |
| if (it.hasNext()) { |
| return it.next(); |
| } |
| } |
| return null; |
| } |
| |
| @Override |
| public Link getOutgoingLink(Dpid dpid, PortNumber number, String type) { |
| return getOutgoingLink(new SwitchPort(dpid, number), type); |
| } |
| |
| @Override |
| public Link getOutgoingLink(SwitchPort port, String type) { |
| Map<String, Link> links = outgoingLinks.get(port); |
| return links.get(type); |
| } |
| |
| @Override |
| public Collection<Link> getOutgoingLinks(SwitchPort port) { |
| return Collections.unmodifiableCollection(outgoingLinks.get(port).values()); |
| } |
| |
| @Override |
| public Link getIncomingLink(Dpid dpid, PortNumber number) { |
| return getIncomingLink(new SwitchPort(dpid, number)); |
| } |
| |
| @Override |
| public Link getIncomingLink(SwitchPort port) { |
| Map<String, Link> links = incomingLinks.get(port); |
| return getPacketLinkIfExists(links); |
| } |
| |
| @Override |
| public Link getIncomingLink(Dpid dpid, PortNumber number, String type) { |
| return getIncomingLink(new SwitchPort(dpid, number), type); |
| } |
| |
| @Override |
| public Link getIncomingLink(SwitchPort port, String type) { |
| Map<String, Link> links = incomingLinks.get(port); |
| return links.get(type); |
| } |
| |
| @Override |
| public Collection<Link> getIncomingLinks(SwitchPort port) { |
| return Collections.unmodifiableCollection(incomingLinks.get(port).values()); |
| } |
| |
| @Override |
| public Link getLink(Dpid srcDpid, PortNumber srcNumber, |
| Dpid dstDpid, PortNumber dstNumber) { |
| |
| final SwitchPort dstSwitchPort = new SwitchPort(dstDpid, dstNumber); |
| Collection<Link> links = getOutgoingLinks(new SwitchPort(srcDpid, srcNumber)); |
| for (Link link : links) { |
| if (link == null) { |
| continue; |
| } |
| if (link.getDstPort().asSwitchPort().equals(dstSwitchPort)) { |
| return link; |
| } |
| } |
| return null; |
| } |
| |
| @Override |
| public Link getLink(Dpid srcDpid, PortNumber srcNumber, |
| Dpid dstDpid, PortNumber dstNumber, |
| String type) { |
| |
| Link link = getOutgoingLink(srcDpid, srcNumber, type); |
| if (link == null) { |
| return null; |
| } |
| if (!link.getDstSwitch().getDpid().equals(dstDpid)) { |
| return null; |
| } |
| if (!link.getDstPort().getNumber().equals(dstNumber)) { |
| return null; |
| } |
| return link; |
| } |
| |
| @Override |
| public Iterable<Link> getLinks() { |
| List<Link> links = new ArrayList<>(); |
| |
| for (Map<String, Link> portLinks : outgoingLinks.values()) { |
| links.addAll(portLinks.values()); |
| } |
| return links; |
| } |
| |
| @GuardedBy("topology.writeLock") |
| protected void putLink(Link link) { |
| putLinkMap(outgoingLinks, link.getSrcPort().asSwitchPort(), link); |
| putLinkMap(incomingLinks, link.getDstPort().asSwitchPort(), link); |
| } |
| |
| /** |
| * Helper method to update outgoingLinks, incomingLinks. |
| * |
| * @param linkMap outgoingLinks or incomingLinks |
| * @param port Map key |
| * @param link Link to add |
| */ |
| @GuardedBy("topology.writeLock") |
| private void putLinkMap(ConcurrentMap<SwitchPort, ConcurrentMap<String, Link>> linkMap, |
| SwitchPort port, Link link) { |
| ConcurrentMap<String, Link> portLinks = new ConcurrentHashMap<String, Link>(3); |
| portLinks.put(link.getType(), link); |
| Map<String, Link> existing = linkMap.putIfAbsent( |
| port, |
| portLinks); |
| if (existing != null) { |
| // no conditional update here |
| existing.put(link.getType(), link); |
| } |
| } |
| |
| @GuardedBy("topology.writeLock") |
| protected void removeLink(Link link) { |
| ConcurrentMap<String, Link> portLinks = outgoingLinks.get(link.getSrcPort().asSwitchPort()); |
| if (portLinks != null) { |
| // no conditional update here |
| portLinks.remove(link.getType()); |
| } |
| portLinks = incomingLinks.get(link.getDstPort().asSwitchPort()); |
| if (portLinks != null) { |
| // no conditional update here |
| portLinks.remove(link.getType()); |
| } |
| } |
| |
| @Override |
| public Device getDeviceByMac(MACAddress address) { |
| return mac2Device.get(address); |
| } |
| |
| @Override |
| public Iterable<Device> getDevices() { |
| return Collections.unmodifiableCollection(mac2Device.values()); |
| } |
| |
| @Override |
| public Collection<Device> getDevices(SwitchPort port) { |
| return Collections.unmodifiableCollection(devices.get(port)); |
| } |
| |
| // This method is expected to be serialized by writeLock. |
| // XXX new or updated device |
| protected void putDevice(Device device) { |
| // assuming Device is immutable |
| Device oldDevice = mac2Device.get(device.getMacAddress()); |
| if (oldDevice != null) { |
| // remove old attachment point |
| removeDevice(oldDevice); |
| } |
| // add new attachment points |
| for (Port port : device.getAttachmentPoints()) { |
| // TODO Won't need remove() if we define Device equality to reflect |
| // all of it's fields. |
| devices.remove(port.asSwitchPort(), device); |
| devices.put(port.asSwitchPort(), device); |
| } |
| mac2Device.put(device.getMacAddress(), device); |
| } |
| |
| protected void removeDevice(Device device) { |
| for (Port port : device.getAttachmentPoints()) { |
| devices.remove(port.asSwitchPort(), device); |
| } |
| mac2Device.remove(device.getMacAddress()); |
| } |
| |
| @Override |
| public void acquireReadLock() { |
| readLock.lock(); |
| } |
| |
| @Override |
| public void releaseReadLock() { |
| readLock.unlock(); |
| } |
| |
| protected void acquireWriteLock() { |
| writeLock.lock(); |
| } |
| |
| protected void releaseWriteLock() { |
| writeLock.unlock(); |
| } |
| } |