blob: 9186e43b38aaca0c284a9621d50aaf1e0734ab67 [file] [log] [blame]
package net.onrc.onos.ofcontroller.networkgraph;
import net.onrc.onos.datastore.topology.RCLink;
import net.onrc.onos.datastore.topology.RCPort;
import net.onrc.onos.datastore.topology.RCSwitch;
import net.onrc.onos.ofcontroller.util.Dpid;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import edu.stanford.ramcloud.JRamCloud.ObjectDoesntExistException;
/**
* The "NB" read-only Network Map.
*
* - Maintain Invariant/Relationships between Topology Objects.
*
* TODO To be synchronized based on TopologyEvent Notification.
*
* TODO TBD: Caller is expected to maintain parent/child calling order. Parent
* Object must exist before adding sub component(Add Switch -> Port). Child
* Object need to be removed before removing parent (Delete Port->Switch)
*
* TODO TBD: This class may delay the requested change to handle event
* re-ordering. e.g.) Link Add came in, but Switch was not there.
*
*/
public class NetworkGraphImpl extends AbstractNetworkGraph
implements NetworkGraphDiscoveryInterface {
private static final Logger log = LoggerFactory
.getLogger(NetworkGraphImpl.class);
private final NetworkGraphDatastore datastore;
public NetworkGraphImpl() {
super();
datastore = new NetworkGraphDatastore(this);
}
/**
* put Switch
*
* XXX Internal Invariant-maintenance method. Will not write to DB. Will not
* fire Notification.
*
* @param swEvt
*/
void putSwitch(SwitchEvent swEvt) {
if (swEvt == null) {
throw new IllegalArgumentException("Switch cannot be null");
}
Switch sw = switches.get(swEvt.getDpid());
if (sw == null) {
sw = new SwitchImpl(this, swEvt.getDpid());
Switch existing = switches.putIfAbsent(swEvt.getDpid(), sw);
if (existing != null) {
log.warn(
"Concurrent putSwitch not expected. Continuing updating {}",
existing);
sw = existing;
}
}
// Add upate when more attributes are added to Event object
// no attribute to update for now
// TODO handle child Port event properly for performance
for (PortEvent portEvt : swEvt.getPorts() ) {
putPort(portEvt);
}
}
/**
* remove Switch.
*
* XXX Internal Invariant-maintenance method. Will not write to DB. Will not
* fire Notification.
*
* @param swEvt
*/
void removeSwitch(SwitchEvent swEvt) {
if (swEvt == null) {
throw new IllegalArgumentException("Switch cannot be null");
}
// TODO handle child Port event properly for performance
for (PortEvent portEvt : swEvt.getPorts() ) {
removePort(portEvt);
}
Switch sw = switches.get(swEvt.getDpid());
if (sw == null) {
log.warn("Switch {} already removed, ignoring", swEvt);
return;
}
// Sanity check
if (!sw.getPorts().isEmpty()) {
log.warn(
"Ports on Switch {} should be removed prior to removing Switch. Removing Switch anyways",
swEvt);
// XXX Should we remove Port?
}
if (!sw.getDevices().isEmpty()) {
log.warn(
"Devices on Switch {} should be removed prior to removing Switch. Removing Switch anyways",
swEvt);
// XXX Should we remove Device to Switch relation?
}
if (!sw.getIncomingLinks().iterator().hasNext()) {
log.warn(
"IncomingLinks on Switch {} should be removed prior to removing Switch. Removing Switch anyways",
swEvt);
// XXX Should we remove Link?
}
if (!sw.getOutgoingLinks().iterator().hasNext()) {
log.warn(
"OutgoingLinks on Switch {} should be removed prior to removing Switch. Removing Switch anyways",
swEvt);
// XXX Should we remove Link?
}
boolean removed = switches.remove(swEvt.getDpid(), sw);
if (removed) {
log.warn(
"Switch instance was replaced concurrently while removing {}. Something is not right.",
sw);
}
}
/**
* put Port
*
* XXX Internal Invariant-maintenance method. Will not write to DB. Will not
* fire Notification.
*
* @param portEvt
*/
void putPort(PortEvent portEvt) {
if (portEvt == null) {
throw new IllegalArgumentException("Port cannot be null");
}
Switch sw = switches.get(portEvt.getDpid());
if (sw == null) {
throw new BrokenInvariantException(String.format(
"Switch with dpid %s did not exist.",
new Dpid(portEvt.getDpid())));
}
Port p = sw.getPort(portEvt.getNumber());
PortImpl port = null;
if (p != null) {
port = getPortImpl(p);
}
if (port == null) {
port = new PortImpl(this, sw, portEvt.getNumber());
}
// TODO update attributes
SwitchImpl s = getSwitchImpl(sw);
s.addPort(port);
}
/**
* remove Port
*
* XXX Internal Invariant-maintenance method. Will not write to DB. Will not
* fire Notification.
*
* @param portEvt
*/
void removePort(PortEvent portEvt) {
if (portEvt == null) {
throw new IllegalArgumentException("Port cannot be null");
}
Switch sw = switches.get(portEvt.getDpid());
if (sw == null) {
log.warn("Parent Switch for Port {} already removed, ignoring", portEvt);
return;
}
Port p = sw.getPort(portEvt.getNumber());
if (p == null) {
log.warn("Port {} already removed, ignoring", portEvt);
return;
}
// check if there is something referring to this Port
if (!p.getDevices().iterator().hasNext()) {
log.warn(
"Devices on Port {} should be removed prior to removing Port. Removing Port anyways",
portEvt);
// XXX Should we remove Device to Port relation?
}
if (p.getIncomingLink() != null) {
log.warn(
"IncomingLinks on Port {} should be removed prior to removing Port. Removing Port anyways",
portEvt);
// XXX Should we remove Link?
}
if (p.getOutgoingLink() != null) {
log.warn(
"OutgoingLinks on Port {} should be removed prior to removing Port. Removing Port anyways",
portEvt);
// XXX Should we remove Link?
}
// remove Port from Switch
SwitchImpl s = getSwitchImpl(sw);
s.removePort(p);
}
/**
* put Link
*
* XXX Internal Invariant-maintenance method. Will not write to DB. Will not
* fire Notification.
*
* @param linkEvt
*/
void putLink(LinkEvent linkEvt) {
if (linkEvt == null) {
throw new IllegalArgumentException("Link cannot be null");
}
Switch srcSw = switches.get(linkEvt.getSrc().dpid);
if (srcSw == null) {
throw new BrokenInvariantException(
String.format(
"Switch with dpid %s did not exist.",
new Dpid(linkEvt.getSrc().dpid)));
}
Switch dstSw = switches.get(linkEvt.getDst().dpid);
if (dstSw == null) {
throw new BrokenInvariantException(
String.format(
"Switch with dpid %s did not exist.",
new Dpid(linkEvt.getDst().dpid)));
}
Port srcPort = srcSw.getPort(linkEvt.getSrc().number);
if (srcPort == null) {
throw new BrokenInvariantException(
String.format(
"Src Port %s of a Link did not exist.",
linkEvt.getSrc() ));
}
Port dstPort = dstSw.getPort(linkEvt.getDst().number);
if (dstPort == null) {
throw new BrokenInvariantException(
String.format(
"Dst Port %s of a Link did not exist.",
linkEvt.getDst() ));
}
// getting Link instance from destination port incoming Link
Link l = dstPort.getIncomingLink();
LinkImpl link = null;
assert( l == srcPort.getOutgoingLink() );
if (l != null) {
link = getLinkImpl(l);
}
if (link == null) {
link = new LinkImpl(this, srcPort, dstPort);
}
PortImpl dstPortMem = getPortImpl(dstPort);
PortImpl srcPortMem = getPortImpl(srcPort);
// Add Link first to avoid further Device addition
// add Link to Port
dstPortMem.setIncomingLink(link);
srcPortMem.setOutgoingLink(link);
// remove Device Pointing to Port if any
for(Device d : dstPortMem.getDevices() ) {
log.error("Device {} on Port {} should have been removed prior to adding Link {}", d, dstPort, linkEvt);
DeviceImpl dev = getDeviceImpl(d);
dev.removeAttachmentPoint(dstPort);
// XXX This implies that change is made to Device Object,
// which need to be written to DB, how should that be done?
// should we write here or ignore and leave DB in inconsistent state?
}
dstPortMem.removeAllDevice();
for(Device d : srcPortMem.getDevices() ) {
log.error("Device {} on Port {} should have been removed prior to adding Link {}", d, srcPort, linkEvt);
DeviceImpl dev = getDeviceImpl(d);
dev.removeAttachmentPoint(srcPort);
// XXX This implies that change is made to Device Object,
// which need to be written to DB, how should that be done?
// should we write here or ignore and leave DB in inconsistent state?
}
srcPortMem.removeAllDevice();
}
/**
* removeLink
*
* XXX Internal Invariant-maintenance method. Will not write to DB. Will not
* fire Notification.
*
* @param linkEvt
*/
void removeLink(LinkEvent linkEvt) {
if (linkEvt == null) {
throw new IllegalArgumentException("Link cannot be null");
}
Switch srcSw = switches.get(linkEvt.getSrc().dpid);
if (srcSw == null) {
log.warn("Src Switch for Link {} already removed, ignoring", linkEvt);
return;
}
Switch dstSw = switches.get(linkEvt.getDst().dpid);
if (dstSw == null) {
log.warn("Dst Switch for Link {} already removed, ignoring", linkEvt);
return;
}
Port srcPort = srcSw.getPort(linkEvt.getSrc().number);
if (srcPort == null) {
log.warn("Src Port for Link {} already removed, ignoring", linkEvt);
return;
}
Port dstPort = dstSw.getPort(linkEvt.getDst().number);
if (dstPort == null) {
log.warn("Dst Port for Link {} already removed, ignoring", linkEvt);
return;
}
Link l = dstPort.getIncomingLink();
if ( l == null ) {
log.warn("Link {} already removed on destination Port", linkEvt);
}
l = srcPort.getOutgoingLink();
if ( l == null ) {
log.warn("Link {} already removed on src Port", linkEvt);
}
getPortImpl(dstPort).setIncomingLink(null);
getPortImpl(srcPort).setOutgoingLink(null);
}
// XXX Need to rework Device related
/**
* Add new device to DB
*
* @param device
*/
void putDevice(DeviceEvent deviceEvt) {
if (deviceEvt == null) {
throw new IllegalArgumentException("Device cannot be null");
}
// for each attachment point
/// TODO check if Link exist on that Port
// TODO Auto-generated method stub
// Device existingDevice =
// getDeviceByMac(deviceToUpdate.getMacAddress());
// if (existingDevice != deviceToUpdate) {
// throw new IllegalArgumentException(
// "Must supply Device Object in this NetworkGraph");
// }
//
// DeviceImpl device = getDeviceImpl(deviceToUpdate);
//
// // Update IP Addr
// // uniq
// Set<InetAddress> prevAddrs = new HashSet<>(
// deviceToUpdate.getIpAddress());
// Set<InetAddress> newAddrs = updatedIpAddrs;
//
// // delta
// @SuppressWarnings("unchecked")
// Collection<InetAddress> delAddr = CollectionUtils.subtract(newAddrs,
// prevAddrs);
// @SuppressWarnings("unchecked")
// Collection<InetAddress> addAddr = CollectionUtils.subtract(prevAddrs,
// newAddrs);
//
// for (InetAddress addr : delAddr) {
// Set<Device> devices = addr2Device.get(addr);
// if (devices == null) {
// continue;
// }
// devices.remove(device);
// device.removeIpAddress(addr);
// }
// for (InetAddress addr : addAddr) {
// Set<Device> devices = addr2Device.get(addr);
// if (devices == null) {
// devices = new HashSet<>();
// addr2Device.put(addr, devices);
// }
// devices.add(device);
// device.addIpAddress(addr);
// }
//
// // Update Attachment Point
// // uniq
// Set<Port> prevPorts = new HashSet<>();
// CollectionUtils.addAll(prevAddrs,
// deviceToUpdate.getAttachmentPoints()
// .iterator());
// Set<Port> newPorts = updatedAttachmentPoints;
// // delta
// @SuppressWarnings("unchecked")
// Collection<Port> delPorts = CollectionUtils.subtract(newPorts,
// prevPorts);
// @SuppressWarnings("unchecked")
// Collection<Port> addPorts = CollectionUtils.subtract(prevPorts,
// newPorts);
//
// for (Port p : delPorts) {
// device.removeAttachmentPoint(p);
// getPortImpl(p).removeDevice(device);
// }
//
// for (Port p : addPorts) {
// device.addAttachmentPoint(p);
// getPortImpl(p).addDevice(device);
// }
// TODO Auto-generated method stub
}
void removeDevice(DeviceEvent device) {
if (device == null) {
throw new IllegalArgumentException("Device cannot be null");
}
// TODO Auto-generated method stub
}
private SwitchImpl getSwitchImpl(Switch sw) {
if (sw instanceof SwitchImpl) {
return (SwitchImpl) sw;
}
throw new ClassCastException("SwitchImpl expected, but found: " + sw);
}
private PortImpl getPortImpl(Port p) {
if (p instanceof PortImpl) {
return (PortImpl) p;
}
throw new ClassCastException("PortImpl expected, but found: " + p);
}
private LinkImpl getLinkImpl(Link l) {
if (l instanceof LinkImpl) {
return (LinkImpl) l;
}
throw new ClassCastException("LinkImpl expected, but found: " + l);
}
private DeviceImpl getDeviceImpl(Device d) {
if (d instanceof DeviceImpl) {
return (DeviceImpl) d;
}
throw new ClassCastException("DeviceImpl expected, but found: " + d);
}
public void loadWholeTopologyFromDB() {
// TODO this method needs to use East-bound API if we still need this
// XXX clear everything first?
for (RCSwitch sw : RCSwitch.getAllSwitches()) {
try {
sw.read();
// TODO if there is going to be inactive Switch in DB, skip
// TODO update other attributes if there exist any
putSwitch(new SwitchEvent(sw.getDpid()));
} catch (ObjectDoesntExistException e) {
log.error("Read Switch Failed, skipping", e);
}
}
for (RCPort p : RCPort.getAllPorts()) {
try {
p.read();
Switch sw = this.getSwitch(p.getDpid());
if (sw == null) {
log.error("Switch {} missing when adding Port {}",
new Dpid(p.getDpid()), p);
continue;
}
PortEvent portEvent = new PortEvent(p.getDpid(), p.getNumber());
// TODO update other attributes if there exist any
putPort(portEvent);
} catch (ObjectDoesntExistException e) {
log.error("Read Port Failed, skipping", e);
}
}
// TODO Is Device going to be in DB? If so, read from DB.
// for (RCDevice d : RCDevice.getAllDevices()) {
// try {
// d.read();
//
// } catch (ObjectDoesntExistException e) {
// log.debug("Read Device Failed, skipping", e);
// }
// }
for (RCLink l : RCLink.getAllLinks()) {
try {
l.read();
Switch srcSw = this.getSwitch(l.getSrc().dpid);
if (srcSw == null) {
log.error("Switch {} missing when adding Link {}",
new Dpid(l.getSrc().dpid), l);
continue;
}
Switch dstSw = this.getSwitch(l.getDst().dpid);
if (dstSw == null) {
log.error("Switch {} missing when adding Link {}",
new Dpid(l.getDst().dpid), l);
continue;
}
LinkEvent linkEvent = new LinkEvent(l.getSrc().dpid,
l.getSrc().number, l.getDst().dpid, l.getDst().number);
// TODO update other attributes if there exist any
putLink(linkEvent);
} catch (ObjectDoesntExistException e) {
log.debug("Delete Link Failed", e);
}
}
}
/**
* Exception to be thrown when Modification to the Network Graph cannot be continued due to broken invariant.
*
* XXX Should this be checked exception or RuntimeException
*/
public static class BrokenInvariantException extends RuntimeException {
private static final long serialVersionUID = 1L;
public BrokenInvariantException() {
super();
}
public BrokenInvariantException(String message) {
super(message);
}
}
/* ******************************
* NetworkGraphDiscoveryInterface methods
* ******************************/
@Override
public void putSwitchEvent(SwitchEvent switchEvent) {
if (checkAddSwitchInvariant(switchEvent)) {
datastore.addSwitch(switchEvent);
putSwitch(switchEvent);
}
// TODO handle invariant violation
}
@Override
public void removeSwitchEvent(SwitchEvent switchEvent) {
if (checkRemoveSwitchInvariant(switchEvent)) {
datastore.deactivateSwitch(switchEvent);
removeSwitch(switchEvent);
}
// TODO handle invariant violation
}
@Override
public void putPortEvent(PortEvent portEvent) {
// TODO Auto-generated method stub
}
@Override
public void removePortEvent(PortEvent portEvent) {
// TODO Auto-generated method stub
}
@Override
public void putLinkEvent(LinkEvent linkEvent) {
if (checkAddLinkInvariant(linkEvent)) {
datastore.addLink(linkEvent);
putLink(linkEvent);
}
// TODO handle invariant violation
}
@Override
public void removeLinkEvent(LinkEvent linkEvent) {
if (checkRemoveLinkInvariant(linkEvent)) {
datastore.removeLink(linkEvent);
removeLink(linkEvent);
}
// TODO handle invariant violation
}
@Override
public void putDeviceEvent(DeviceEvent device) {
// TODO Auto-generated method stub
}
@Override
public void removeDeviceEvent(DeviceEvent deviceEvent) {
// TODO Auto-generated method stub
}
/* *****************
* Internal methods to check invariants of the network graph
* *****************/
private boolean checkAddSwitchInvariant(SwitchEvent swEvt) {
// TODO implement
return true;
}
private boolean checkRemoveSwitchInvariant(SwitchEvent swEvt) {
// TODO implement
return true;
}
private boolean checkAddPortInvariant(PortEvent portEvt) {
// TODO implement
return true;
}
private boolean checkRemovePortInvariant(PortEvent portEvt) {
// TODO implement
return true;
}
private boolean checkAddLinkInvariant(LinkEvent linkEvt) {
// TODO implement
return true;
}
private boolean checkRemoveLinkInvariant(LinkEvent linkEvt) {
// TODO implement
return true;
}
private boolean checkAddDeviceInvariant(DeviceEvent deviceEvt) {
// TODO implement
return true;
}
private boolean checkRemoveDeviceInvariant(DeviceEvent deviceEvt) {
// TODO implement
return true;
}
}