Implemented the API method to get all switch-controller information from Zookeeper. Also implemented the informational REST APIs for the Registry service
diff --git a/src/main/java/net/onrc/onos/registry/controller/RegistryManager.java b/src/main/java/net/onrc/onos/registry/controller/RegistryManager.java
index 84f17c3..a5a3576 100644
--- a/src/main/java/net/onrc/onos/registry/controller/RegistryManager.java
+++ b/src/main/java/net/onrc/onos/registry/controller/RegistryManager.java
@@ -5,6 +5,7 @@
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -13,7 +14,9 @@
import net.floodlightcontroller.core.module.FloodlightModuleException;
import net.floodlightcontroller.core.module.IFloodlightModule;
import net.floodlightcontroller.core.module.IFloodlightService;
+import net.floodlightcontroller.restserver.IRestApiService;
+import org.apache.commons.lang.NotImplementedException;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher.Event.KeeperState;
@@ -28,27 +31,35 @@
import com.netflix.curator.framework.recipes.cache.ChildData;
import com.netflix.curator.framework.recipes.cache.PathChildrenCache;
import com.netflix.curator.framework.recipes.cache.PathChildrenCache.StartMode;
+import com.netflix.curator.framework.recipes.cache.PathChildrenCacheEvent;
+import com.netflix.curator.framework.recipes.cache.PathChildrenCacheListener;
import com.netflix.curator.framework.recipes.leader.LeaderLatch;
import com.netflix.curator.framework.recipes.leader.Participant;
-import com.netflix.curator.retry.RetryOneTime;
+import com.netflix.curator.retry.ExponentialBackoffRetry;
public class RegistryManager implements IFloodlightModule, IControllerRegistryService {
protected static Logger log = LoggerFactory.getLogger(RegistryManager.class);
protected String mastershipId = null;
+ protected IRestApiService restApi;
+
//TODO read this from configuration
protected String connectionString = "localhost:2181";
+
+
private final String namespace = "onos";
private final String switchLatchesPath = "/switches";
+ private final String controllerPath = "/controllers";
protected CuratorFramework client;
- private final String controllerPath = "/controllers";
protected PathChildrenCache controllerCache;
+ protected PathChildrenCache switchCache;
protected Map<String, LeaderLatch> switchLatches;
protected Map<String, ControlChangeCallback> switchCallbacks;
+ protected Map<String, PathChildrenCache> switchPathCaches;
protected boolean moduleEnabled = false;
@@ -73,7 +84,11 @@
log.debug("Disconnected while leader - lost leadership for {}", dpid);
isLeader = false;
- switchCallbacks.get(dpid).controlChanged(HexString.toLong(dpid), false);
+ ControlChangeCallback cb = switchCallbacks.get(dpid);
+ if (cb != null) {
+ //Allow callback to be null if the requester doesn't want a callback
+ cb.controlChanged(HexString.toLong(dpid), false);
+ }
}
return;
}
@@ -108,10 +123,56 @@
//client.getChildren().usingWatcher(this).forPath(latchPath);
}
}
+
+
+ /*
+ * Listens for changes to the switch znodes in Zookeeper. This maintains the second level of
+ * PathChildrenCaches that hold the controllers contending for each switch - there's one for
+ * each switch.
+ */
+ PathChildrenCacheListener switchPathCacheListener = new PathChildrenCacheListener() {
+ @Override
+ public void childEvent(CuratorFramework client,
+ PathChildrenCacheEvent event) throws Exception {
+ // TODO Auto-generated method stub
+ log.debug("Root switch path cache got {} event", event.getType());
+
+ String strSwitch = null;
+ if (event.getData() != null){
+ log.debug("Event path {}", event.getData().getPath());
+ String[] splitted = event.getData().getPath().split("/");
+ strSwitch = splitted[splitted.length - 1];
+ log.debug("Switch name is {}", strSwitch);
+ }
+
+ switch (event.getType()){
+ case CHILD_ADDED:
+ case CHILD_UPDATED:
+ //Check we have a PathChildrenCache for this child, add one if not
+ if (switchPathCaches.get(strSwitch) == null){
+ PathChildrenCache pc = new PathChildrenCache(client,
+ event.getData().getPath(), true);
+ pc.start(StartMode.NORMAL);
+ switchPathCaches.put(strSwitch, pc);
+ }
+ break;
+ case CHILD_REMOVED:
+ //Remove our PathChildrenCache for this child
+ PathChildrenCache pc = switchPathCaches.remove(strSwitch);
+ pc.close();
+ break;
+ default:
+ //All other events are connection status events. We need to do anything
+ //as the path cache handles these on its own.
+ break;
+ }
+
+ }
+ };
@Override
- public void requestControl(long dpid, ControlChangeCallback cb) throws Exception {
+ public void requestControl(long dpid, ControlChangeCallback cb) throws RegistryException {
if (!moduleEnabled) return;
@@ -139,7 +200,7 @@
latch.start();
} catch (Exception e) {
log.warn("Error starting leader latch: {}", e.getMessage());
- throw e;
+ throw new RegistryException("Error starting leader latch for " + dpidStr, e);
}
}
@@ -269,15 +330,50 @@
@Override
public Collection<Long> getSwitchesControlledByController(String controllerId) {
- // TODO Auto-generated method stub
- return null;
+ //TODO remove this if not needed
+ throw new NotImplementedException();
}
@Override
- public Collection<Map<String, String>> getAllSwitches() {
- // TODO Auto-generated method stub
- return null;
+ public Map<String, List<ControllerRegistryEntry>> getAllSwitches() {
+ Map<String, List<ControllerRegistryEntry>> data =
+ new HashMap<String, List<ControllerRegistryEntry>>();
+
+ for (Map.Entry<String, PathChildrenCache> entry : switchPathCaches.entrySet()){
+ List<ControllerRegistryEntry> contendingControllers =
+ new ArrayList<ControllerRegistryEntry>();
+
+ if (entry.getValue().getCurrentData().size() < 1){
+ log.info("Switch entry with no leader elections: {}", entry.getKey());
+ continue;
+ }
+
+ for (ChildData d : entry.getValue().getCurrentData()) {
+ /*
+ if (d.getPath().length() < 1){
+ log.info("Switch entry with no leader elections: {}", d.getPath());
+ continue;
+ }
+ */
+
+ String controllerId = null;
+ try {
+ controllerId = new String(d.getData(), "UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ log.warn("Encoding exception: {}", e.getMessage());
+ }
+
+ String[] splitted = d.getPath().split("-");
+ int sequenceNumber = Integer.parseInt(splitted[splitted.length - 1]);
+
+ contendingControllers.add(new ControllerRegistryEntry(controllerId, sequenceNumber));
+ }
+
+ Collections.sort(contendingControllers);
+ data.put(entry.getKey(), contendingControllers);
+ }
+ return data;
}
/*
@@ -302,13 +398,17 @@
@Override
public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
- // no module dependencies
- return null;
+ Collection<Class<? extends IFloodlightService>> l =
+ new ArrayList<Class<? extends IFloodlightService>>();
+ l.add(IRestApiService.class);
+ return l;
}
@Override
public void init (FloodlightModuleContext context) throws FloodlightModuleException {
+ restApi = context.getServiceImpl(IRestApiService.class);
+
//Read config to see if we should try and connect to zookeeper
Map<String, String> configOptions = context.getConfigParams(this);
String enableZookeeper = configOptions.get("enableZookeeper");
@@ -332,19 +432,34 @@
switchLatches = new HashMap<String, LeaderLatch>();
switchCallbacks = new HashMap<String, ControlChangeCallback>();
+ switchPathCaches = new HashMap<String, PathChildrenCache>();
- //RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
- RetryPolicy retryPolicy = new RetryOneTime(0);
+ RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
+ //RetryPolicy retryPolicy = new RetryOneTime(0);
client = CuratorFrameworkFactory.newClient(connectionString, retryPolicy);
client.start();
client = client.usingNamespace(namespace);
+ //Put some data in for testing
+ try {
+ registerController(mastershipId);
+ requestControl(2L, null);
+ } catch (RegistryException e1) {
+ // TODO Auto-generated catch block
+ e1.printStackTrace();
+ }
+
controllerCache = new PathChildrenCache(client, controllerPath, true);
+ switchCache = new PathChildrenCache(client, switchLatchesPath, true);
+ switchCache.getListenable().addListener(switchPathCacheListener);
try {
controllerCache.start(StartMode.BUILD_INITIAL_CACHE);
+
+ //Don't prime the cache, we want a notification for each child node in the path
+ switchCache.start(StartMode.NORMAL);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
@@ -353,7 +468,7 @@
@Override
public void startUp (FloodlightModuleContext context) {
- // Nothing to be done on startup
+ restApi.addRestletRoutable(new RegistryWebRoutable());
}
}