Skip to content
Snippets Groups Projects
Commit 9e9ff4ea authored by Subrata Banerjee's avatar Subrata Banerjee
Browse files

Event-history for topology related events: switch connects/disconnects, links...

Event-history for topology related events: switch connects/disconnects, links ups/downs, cluster merge/splits.
parent 3aec000e
No related branches found
No related tags found
No related merge requests found
Showing
with 442 additions and 13 deletions
......@@ -48,6 +48,12 @@ public class CoreWebRoutable implements RestletRoutable {
EventHistoryAttachmentPointResource.class);
router.attach("/event-history/packet-in/{count}/json",
EventHistoryPacketInResource.class);
router.attach("/event-history/topology-switch/{count}/json",
EventHistoryTopologySwitchResource.class);
router.attach("/event-history/topology-link/{count}/json",
EventHistoryTopologyLinkResource.class);
router.attach("/event-history/topology-cluster/{count}/json",
EventHistoryTopologyClusterResource.class);
return router;
}
}
package net.floodlightcontroller.core.web;
import net.floodlightcontroller.topology.internal.EventHistoryTopologyCluster;
import net.floodlightcontroller.topology.internal.TopologyImpl;
import net.floodlightcontroller.util.EventHistory;
import org.restlet.resource.Get;
import org.restlet.resource.ServerResource;
/**
* @author subrata
*
*/
public class EventHistoryTopologyClusterResource extends ServerResource {
@Get("json")
public EventHistory<EventHistoryTopologyCluster> handleEvHistReq() {
// Get the event history count. Last <count> events would be returned
String evHistCount = (String)getRequestAttributes().get("count");
TopologyImpl topoManager =
(TopologyImpl)getContext().getAttributes().get("topology");
return new EventHistory<EventHistoryTopologyCluster>(
topoManager.evHistTopologyCluster,
Integer.parseInt(evHistCount));
}
}
package net.floodlightcontroller.core.web;
import net.floodlightcontroller.topology.internal.EventHistoryTopologyLink;
import net.floodlightcontroller.topology.internal.TopologyImpl;
import net.floodlightcontroller.util.EventHistory;
import org.restlet.resource.Get;
import org.restlet.resource.ServerResource;
/**
* @author subrata
*
*/
public class EventHistoryTopologyLinkResource extends ServerResource {
@Get("json")
public EventHistory<EventHistoryTopologyLink> handleEvHistReq() {
// Get the event history count. Last <count> events would be returned
String evHistCount = (String)getRequestAttributes().get("count");
TopologyImpl topoManager =
(TopologyImpl)getContext().getAttributes().get("topology");
return new EventHistory<EventHistoryTopologyLink>(
topoManager.evHistTopologyLink, Integer.parseInt(evHistCount));
}
}
package net.floodlightcontroller.core.web;
import net.floodlightcontroller.topology.internal.EventHistoryTopologySwitch;
import net.floodlightcontroller.topology.internal.TopologyImpl;
import net.floodlightcontroller.util.EventHistory;
import org.restlet.resource.Get;
import org.restlet.resource.ServerResource;
/**
* @author subrata
*
*/
public class EventHistoryTopologySwitchResource extends ServerResource {
@Get("json")
public EventHistory<EventHistoryTopologySwitch> handleEvHistReq() {
// Get the event history count. Last <count> events would be returned
String evHistCount = (String)getRequestAttributes().get("count");
TopologyImpl topoManager =
(TopologyImpl)getContext().getAttributes().get("topology");
return new EventHistory<EventHistoryTopologySwitch>(
topoManager.evHistTopologySwitch,
Integer.parseInt(evHistCount));
}
}
......@@ -21,6 +21,9 @@ import java.util.List;
import net.floodlightcontroller.core.web.serializers.EventHistoryAttachmentPointJSONSerializer;
import net.floodlightcontroller.core.web.serializers.EventHistoryBaseInfoJSONSerializer;
import net.floodlightcontroller.core.web.serializers.EventHistoryTopologyClusterJSONSerializer;
import net.floodlightcontroller.core.web.serializers.EventHistoryTopologyLinkJSONSerializer;
import net.floodlightcontroller.core.web.serializers.EventHistoryTopologySwitchJSONSerializer;
import net.floodlightcontroller.core.web.serializers.LinkTupleSerializer;
import net.floodlightcontroller.core.web.serializers.OFFeaturesReplyJSONSerializer;
import net.floodlightcontroller.core.web.serializers.OFMatchJSONSerializer;
......@@ -49,7 +52,7 @@ import org.slf4j.LoggerFactory;
*
*/
public class JacksonCustomConverter extends JacksonConverter {
protected static Logger log =
protected static Logger log =
LoggerFactory.getLogger(JacksonCustomConverter.class);
protected static ObjectMapper jsonObjectMapper;
......@@ -69,9 +72,12 @@ public class JacksonCustomConverter extends JacksonConverter {
jsonModule.addSerializer(
new PerfMonCumulativeTimeBucketJSONSerializer());
jsonModule.addSerializer(new LinkTupleSerializer());
jsonModule.addSerializer(new EventHistoryTopologySwitchJSONSerializer());
jsonModule.addSerializer(new EventHistoryTopologyLinkJSONSerializer());
jsonModule.addSerializer(new EventHistoryTopologyClusterJSONSerializer());
jsonObjectMapper.registerModule(jsonModule);
}
@Override
protected <T> JacksonRepresentation<T>
create(MediaType mediaType, T source) {
......
package net.floodlightcontroller.core.web.serializers;
import java.io.IOException;
import net.floodlightcontroller.topology.internal.EventHistoryTopologyCluster;
import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.JsonProcessingException;
import org.codehaus.jackson.map.JsonSerializer;
import org.codehaus.jackson.map.SerializerProvider;
import org.openflow.util.HexString;
/**
*
* @author subrata
*
*/
public class EventHistoryTopologyClusterJSONSerializer
extends JsonSerializer<EventHistoryTopologyCluster> {
/**
* Performs the serialization of a EventHistory Topology-Switch object
*/
@Override
public void serialize(EventHistoryTopologyCluster topoCluster,
JsonGenerator jGen,
SerializerProvider serializer)
throws IOException, JsonProcessingException {
jGen.writeStartObject();
jGen.writeStringField("Switch",
HexString.toHexString(topoCluster.dpid));
jGen.writeStringField("OldClusterId",
HexString.toHexString(topoCluster.clusterIdOld));
jGen.writeStringField("NewClusterId",
HexString.toHexString(topoCluster.clusterIdNew));
jGen.writeStringField("Reason", topoCluster.reason);
jGen.writeEndObject();
}
/**
* Tells SimpleModule that we are the serializer for OFMatch
*/
@Override
public Class<EventHistoryTopologyCluster> handledType() {
return EventHistoryTopologyCluster.class;
}
}
\ No newline at end of file
package net.floodlightcontroller.core.web.serializers;
import java.io.IOException;
import net.floodlightcontroller.topology.internal.EventHistoryTopologyLink;
import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.JsonProcessingException;
import org.codehaus.jackson.map.JsonSerializer;
import org.codehaus.jackson.map.SerializerProvider;
import org.openflow.util.HexString;
/**
*
* @author subrata
*
*/
public class EventHistoryTopologyLinkJSONSerializer
extends JsonSerializer<EventHistoryTopologyLink> {
/**
* Performs the serialization of a EventHistory Topology-Switch object
*/
@Override
public void serialize(EventHistoryTopologyLink topoLnk,
JsonGenerator jGen,
SerializerProvider serializer)
throws IOException, JsonProcessingException {
jGen.writeStartObject();
jGen.writeStringField("Source-Switch",
HexString.toHexString(topoLnk.srcSwDpid));
jGen.writeNumberField("SrcPort", topoLnk.srcSwport);
jGen.writeStringField("Dest-Switch",
HexString.toHexString(topoLnk.dstSwDpid));
jGen.writeNumberField("DstPort", topoLnk.dstSwport);
jGen.writeNumberField("SrcPortState", topoLnk.srcPortState);
jGen.writeNumberField("DstPortState", topoLnk.dstPortState);
jGen.writeStringField("Reason", topoLnk.reason);
jGen.writeEndObject();
}
/**
* Tells SimpleModule that we are the serializer for OFMatch
*/
@Override
public Class<EventHistoryTopologyLink> handledType() {
return EventHistoryTopologyLink.class;
}
}
\ No newline at end of file
package net.floodlightcontroller.core.web.serializers;
import java.io.IOException;
import net.floodlightcontroller.packet.IPv4;
import net.floodlightcontroller.topology.internal.EventHistoryTopologySwitch;
import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.JsonProcessingException;
import org.codehaus.jackson.map.JsonSerializer;
import org.codehaus.jackson.map.SerializerProvider;
import org.openflow.util.HexString;
/**
*
* @author subrata
*
*/
public class EventHistoryTopologySwitchJSONSerializer
extends JsonSerializer<EventHistoryTopologySwitch> {
/**
* Performs the serialization of a EventHistory Topology-Switch object
*/
@Override
public void serialize(EventHistoryTopologySwitch topoSw,
JsonGenerator jGen,
SerializerProvider serializer)
throws IOException, JsonProcessingException {
jGen.writeStartObject();
jGen.writeStringField("Switch", HexString.toHexString(topoSw.dpid));
jGen.writeStringField("Port",
"0x"+Integer.toHexString((int)(topoSw.l4Port)).substring(4));
jGen.writeStringField("IpAddr",
IPv4.fromIPv4Address(IPv4.toIPv4Address(topoSw.ipv4Addr)));
jGen.writeStringField("Reason", topoSw.reason);
jGen.writeEndObject();
}
/**
* Tells SimpleModule that we are the serializer for OFMatch
*/
@Override
public Class<EventHistoryTopologySwitch> handledType() {
return EventHistoryTopologySwitch.class;
}
}
\ No newline at end of file
......@@ -21,6 +21,8 @@ import java.util.HashSet;
import java.util.Set;
import net.floodlightcontroller.core.IOFSwitch;
import net.floodlightcontroller.topology.internal.TopologyImpl;
import net.floodlightcontroller.util.EventHistory.EvAction;
/**
* This class represents a cluster of OpenFlow switches.
......@@ -31,10 +33,12 @@ import net.floodlightcontroller.core.IOFSwitch;
public class SwitchCluster {
private Long id; // the lowest DPID of any switches in this island
private Set<IOFSwitch> switches;
private TopologyImpl topology;
public SwitchCluster() {
public SwitchCluster(TopologyImpl topologyMgr) {
switches = new HashSet<IOFSwitch>();
id = null; // invalid
topology = topologyMgr;
}
public Long getId() {
......@@ -44,12 +48,26 @@ public class SwitchCluster {
public void add(IOFSwitch s) {
switches.add(s);
if (id == null || s.getId() < id) {
topology.evHistTopoCluster(s.getId(), // dpid
(id==null)?0:id, // old cluster id
s.getId(), // new cluster id
EvAction.CLUSTER_ID_CHANGED_FOR_CLUSTER,
"Switch Added");
id = s.getId();
for (IOFSwitch sw : switches) {
sw.setSwitchClusterId(id);
if (sw.getSwitchClusterId() != id) {
sw.setSwitchClusterId(id);
}
}
} else {
s.setSwitchClusterId(id);
Long swClusterId = s.getSwitchClusterId();
if (swClusterId != id) {
topology.evHistTopoCluster(s.getId(),
swClusterId==null?0:swClusterId, id==null?0:id,
EvAction.CLUSTER_ID_CHANGED_FOR_A_SWITCH,
"Switch Added");
s.setSwitchClusterId(id);
}
}
}
......@@ -57,6 +75,7 @@ public class SwitchCluster {
if (switches.contains(s)) {
switches.remove(s);
if (s.getId() == id) {
long oldId = id;
// Find the next lowest id
long id = Long.MAX_VALUE;
for (IOFSwitch sw : switches) {
......@@ -64,10 +83,19 @@ public class SwitchCluster {
id = sw.getId();
}
}
// Cluster ID changed for oldId to id
topology.evHistTopoCluster(s.getId(),
oldId, id,
EvAction.CLUSTER_ID_CHANGED_FOR_CLUSTER,
"Switch Removed");
// Now update the cluster id of the switches
for (IOFSwitch sw : switches) {
sw.setSwitchClusterId(id);
}
}
}
}
public Set<IOFSwitch> getSwitches() {
return switches;
}
......
package net.floodlightcontroller.topology.internal;
/***
* Topology Cluster merge/split event history related classes and members
* @author subrata
*
*/
public class EventHistoryTopologyCluster {
// The following fields are not stored as String to save memory
// They should be converted to appropriate human-readable strings by
// the front end (e.g. in cli in Python)
public long dpid;
public long clusterIdOld; // Switch with dpid moved from cluster x to y
public long clusterIdNew;
public String reason;
}
package net.floodlightcontroller.topology.internal;
/***
* Topology link up/down event history related classes and members
* @author subrata
*
*/
public class EventHistoryTopologyLink {
// The following fields are not stored as String to save memory
// They should be converted to appropriate human-readable strings by
// the front end (e.g. in cli in Python)
public long srcSwDpid;
public long dstSwDpid;
public int srcPortState;
public int dstPortState;
public short srcSwport;
public short dstSwport;
public String reason;
}
\ No newline at end of file
package net.floodlightcontroller.topology.internal;
/***
* Topology Switch event history related classes and members
* @author subrata
*
*/
public class EventHistoryTopologySwitch {
// The following fields are not stored as String to save memory
// They should be converted to appropriate human-readable strings by
// the front end (e.g. in cli in Python)
public long dpid;
public byte [] ipv4Addr;
public short l4Port;
public String reason;
}
......@@ -18,6 +18,8 @@
package net.floodlightcontroller.topology.internal;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.HashMap;
......@@ -57,7 +59,10 @@ import net.floodlightcontroller.topology.LinkTuple;
import net.floodlightcontroller.topology.SwitchCluster;
import net.floodlightcontroller.topology.SwitchPortTuple;
import net.floodlightcontroller.util.ClusterDFS;
import net.floodlightcontroller.util.EventHistory;
import net.floodlightcontroller.util.EventHistory.EvAction;
import org.openflow.protocol.OFMatch;
import org.openflow.protocol.OFMessage;
import org.openflow.protocol.OFPacketIn;
import org.openflow.protocol.OFPacketOut;
......@@ -219,6 +224,12 @@ public class TopologyImpl implements IOFMessageListener, IOFSwitchListener,
this.links = new HashMap<LinkTuple, LinkInfo>();
this.portLinks = new HashMap<SwitchPortTuple, Set<LinkTuple>>();
this.switchLinks = new HashMap<IOFSwitch, Set<LinkTuple>>();
this.evHistTopologySwitch =
new EventHistory<EventHistoryTopologySwitch>("Topology: Switch");
this.evHistTopologyLink =
new EventHistory<EventHistoryTopologyLink>("Topology: Link");
this.evHistTopologyCluster =
new EventHistory<EventHistoryTopologyCluster>("Topology: Cluster");
}
......@@ -666,6 +677,13 @@ public class TopologyImpl implements IOFMessageListener, IOFSwitchListener,
linkChanged = true;
log.info("Added link {}", lt);
// Add to event history
evHistTopoLink(lt.getSrc().getSw().getId(),
lt.getDst().getSw().getId(),
lt.getSrc().getPort(),
lt.getDst().getPort(),
srcPortState, dstPortState,
EvAction.LINK_ADDED, "LLDP Recvd");
} else {
// Only update the port states if they've changed
if (srcPortState == oldLinkInfo.getSrcPortState().intValue())
......@@ -694,6 +712,14 @@ public class TopologyImpl implements IOFMessageListener, IOFSwitchListener,
log.trace("Updated link {}", lt);
}
log.info("Updated link {}", lt);
// Add to event history
evHistTopoLink(lt.getSrc().getSw().getId(),
lt.getDst().getSw().getId(),
lt.getSrc().getPort(),
lt.getDst().getPort(),
srcPortState, dstPortState,
EvAction.LINK_PORT_STATE_UPDATED,
"LLDP Recvd");
}
}
......@@ -714,7 +740,7 @@ public class TopologyImpl implements IOFMessageListener, IOFSwitchListener,
* Removes links from memory and storage.
* @param links The List of @LinkTuple to delete.
*/
protected void deleteLinks(List<LinkTuple> links) {
protected void deleteLinks(List<LinkTuple> links, String reason) {
lock.writeLock().lock();
try {
for (LinkTuple lt : links) {
......@@ -737,7 +763,15 @@ public class TopologyImpl implements IOFMessageListener, IOFSwitchListener,
this.links.remove(lt);
updates.add(new Update(lt, 0, 0, UpdateOperation.REMOVE));
// Update Event History
evHistTopoLink(lt.getSrc().getSw().getId(),
lt.getDst().getSw().getId(),
lt.getSrc().getPort(),
lt.getDst().getPort(),
0, 0, // Port states
EvAction.LINK_DELETED, reason);
removeLink(lt);
if (log.isTraceEnabled()) {
log.trace("Deleted link {}", lt);
......@@ -790,7 +824,7 @@ public class TopologyImpl implements IOFMessageListener, IOFSwitchListener,
ps.getReason()});
}
eraseList.addAll(this.portLinks.get(tuple));
deleteLinks(eraseList);
deleteLinks(eraseList, "Port Status Changed");
topologyChanged = true;
link_deleted = true;
}
......@@ -861,6 +895,8 @@ public class TopologyImpl implements IOFMessageListener, IOFSwitchListener,
// know which switches might be connected to the new switch.
// Need to optimize when supporting a large number of switches.
sendLLDPs();
// Update event history
evHistTopoSwitch(sw, EvAction.SWITCH_CONNECTED, "None");
}
/**
......@@ -870,13 +906,15 @@ public class TopologyImpl implements IOFMessageListener, IOFSwitchListener,
*/
@Override
public void removedSwitch(IOFSwitch sw) {
// Update event history
evHistTopoSwitch(sw, EvAction.SWITCH_DISCONNECTED, "None");
List<LinkTuple> eraseList = new ArrayList<LinkTuple>();
lock.writeLock().lock();
try {
if (switchLinks.containsKey(sw)) {
// add all tuples with an endpoint on this switch to erase list
eraseList.addAll(switchLinks.get(sw));
deleteLinks(eraseList);
deleteLinks(eraseList, "Switch Removed");
updateClusters();
}
} finally {
......@@ -907,7 +945,7 @@ public class TopologyImpl implements IOFMessageListener, IOFSwitchListener,
}
if (eraseList.size() > 0) {
deleteLinks(eraseList);
deleteLinks(eraseList, "LLDP timeout");
updateClusters();
}
} finally {
......@@ -1171,7 +1209,7 @@ public class TopologyImpl implements IOFMessageListener, IOFSwitchListener,
// The cluster thus far forms a strongly connected component.
// create a new switch cluster and the switches in the current
// set to the switch cluster.
SwitchCluster sc = new SwitchCluster();
SwitchCluster sc = new SwitchCluster(this);
for(IOFSwitch sw: currSet){
sc.add(sw);
switchClusterMap.put(sw, sc);
......@@ -1465,4 +1503,68 @@ public class TopologyImpl implements IOFMessageListener, IOFSwitchListener,
public void rowsDeleted(String tableName, Set<Object> rowKeys) {
// Ignore delete events, the switch delete will do the right thing on it's own
}
// ****************************************************
// Topology Manager's Event History members and methods
// ****************************************************
// Topology Manager event history
public EventHistory<EventHistoryTopologySwitch> evHistTopologySwitch;
public EventHistory<EventHistoryTopologyLink> evHistTopologyLink;
public EventHistory<EventHistoryTopologyCluster> evHistTopologyCluster;
public EventHistoryTopologySwitch evTopoSwitch;
public EventHistoryTopologyLink evTopoLink;
public EventHistoryTopologyCluster evTopoCluster;
// Switch Added/Deleted
private void evHistTopoSwitch(IOFSwitch sw, EvAction actn, String reason) {
if (evTopoSwitch == null) {
evTopoSwitch = new EventHistoryTopologySwitch();
}
evTopoSwitch.dpid = sw.getId();
if ((sw.getChannel() != null) &&
(SocketAddress.class.isInstance(
sw.getChannel().getRemoteAddress()))) {
evTopoSwitch.ipv4Addr =
((InetSocketAddress)(sw.getChannel().
getRemoteAddress())).getAddress().getAddress();
evTopoSwitch.l4Port =
(short)(((InetSocketAddress)(sw.getChannel().
getRemoteAddress())).getPort());
} else {
byte[] zeroIpa = new byte[] {(byte)0, (byte)0, (byte)0, (byte)0};
evTopoSwitch.ipv4Addr = zeroIpa;
evTopoSwitch.l4Port = 0;
}
evTopoSwitch.reason = reason;
evTopoSwitch = evHistTopologySwitch.put(evTopoSwitch, actn);
}
private void evHistTopoLink(long srcDpid, long dstDpid, short srcPort,
short dstPort, int srcPortState, int dstPortState,
EvAction actn, String reason) {
if (evTopoLink == null) {
evTopoLink = new EventHistoryTopologyLink();
}
evTopoLink.srcSwDpid = srcDpid;
evTopoLink.dstSwDpid = dstDpid;
evTopoLink.srcSwport = srcPort;
evTopoLink.dstSwport = dstPort;
evTopoLink.srcPortState = srcPortState;
evTopoLink.dstPortState = dstPortState;
evTopoLink.reason = reason;
evTopoLink = evHistTopologyLink.put(evTopoLink, actn);
}
public void evHistTopoCluster(long dpid, long clusterIdOld,
long clusterIdNew, EvAction action, String reason) {
if (evTopoCluster == null) {
evTopoCluster = new EventHistoryTopologyCluster();
}
evTopoCluster.dpid = dpid;
evTopoCluster.clusterIdOld = clusterIdOld;
evTopoCluster.clusterIdNew = clusterIdNew;
evTopoCluster.reason = reason;
evTopoCluster = evHistTopologyCluster.put(evTopoCluster, action);
}
}
......@@ -55,6 +55,13 @@ public class EventHistory<T> {
CLEARED, // All entries are removed
PKT_IN,
PKT_OUT,
SWITCH_CONNECTED,
SWITCH_DISCONNECTED,
LINK_ADDED,
LINK_DELETED,
LINK_PORT_STATE_UPDATED,
CLUSTER_ID_CHANGED_FOR_CLUSTER,
CLUSTER_ID_CHANGED_FOR_A_SWITCH,
}
// Constructor
......
......@@ -100,7 +100,7 @@ public class TopologyImplTest extends FloodlightTestCase {
replay(sw1, sw2);
LinkTuple lt = new LinkTuple(sw1, 2, sw2, 1);
topology.addOrUpdateLink(lt, 0, 0);
topology.deleteLinks(Collections.singletonList(lt));
topology.deleteLinks(Collections.singletonList(lt), "Test");
// check invariants hold
assertNull(topology.switchLinks.get(lt.getSrc().getSw()));
......@@ -136,7 +136,7 @@ public class TopologyImplTest extends FloodlightTestCase {
replay(sw1);
LinkTuple lt = new LinkTuple(sw1, 2, sw1, 3);
topology.addOrUpdateLink(lt, 0, 0);
topology.deleteLinks(Collections.singletonList(lt));
topology.deleteLinks(Collections.singletonList(lt), "Test to self");
// check invariants hold
assertNull(topology.switchLinks.get(lt.getSrc().getSw()));
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment