blob: 3120d2038207ae47a31e1b7d4a361b79b43bacbb [file] [log] [blame]
Jonathan Hartd82f20d2013-02-21 18:04:24 -08001package net.onrc.onos.registry.controller;
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -08002
Jonathan Hartbd181b62013-02-17 16:05:38 -08003import java.io.IOException;
Jonathan Hartedd6a442013-02-20 15:22:06 -08004import java.io.UnsupportedEncodingException;
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -08005import java.util.ArrayList;
6import java.util.Collection;
Jonathan Hart3d7730a2013-02-22 11:51:17 -08007import java.util.Collections;
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -08008import java.util.HashMap;
Jonathan Hartedd6a442013-02-20 15:22:06 -08009import java.util.List;
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -080010import java.util.Map;
Jonathan Hart89187372013-03-14 16:41:09 -070011import java.util.concurrent.ConcurrentHashMap;
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -080012
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -080013import net.floodlightcontroller.core.module.FloodlightModuleContext;
14import net.floodlightcontroller.core.module.FloodlightModuleException;
15import net.floodlightcontroller.core.module.IFloodlightModule;
16import net.floodlightcontroller.core.module.IFloodlightService;
Jonathan Hart3d7730a2013-02-22 11:51:17 -080017import net.floodlightcontroller.restserver.IRestApiService;
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -080018
Jonathan Hartedd6a442013-02-20 15:22:06 -080019import org.apache.zookeeper.CreateMode;
Jonathan Hartbd181b62013-02-17 16:05:38 -080020import org.openflow.util.HexString;
21import org.slf4j.Logger;
22import org.slf4j.LoggerFactory;
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -080023
Jonathan Hartd10008d2013-02-23 17:04:08 -080024import com.google.common.base.Charsets;
Jonathan Hartbd181b62013-02-17 16:05:38 -080025import com.netflix.curator.RetryPolicy;
26import com.netflix.curator.framework.CuratorFramework;
27import com.netflix.curator.framework.CuratorFrameworkFactory;
Jonathan Hartedd6a442013-02-20 15:22:06 -080028import com.netflix.curator.framework.recipes.cache.ChildData;
29import com.netflix.curator.framework.recipes.cache.PathChildrenCache;
30import com.netflix.curator.framework.recipes.cache.PathChildrenCache.StartMode;
Jonathan Hart3d7730a2013-02-22 11:51:17 -080031import com.netflix.curator.framework.recipes.cache.PathChildrenCacheEvent;
32import com.netflix.curator.framework.recipes.cache.PathChildrenCacheListener;
Jonathan Hartbd181b62013-02-17 16:05:38 -080033import com.netflix.curator.framework.recipes.leader.LeaderLatch;
Jonathan Hart0de09492013-03-13 14:37:21 -070034import com.netflix.curator.framework.recipes.leader.LeaderLatchEvent;
35import com.netflix.curator.framework.recipes.leader.LeaderLatchListener;
Jonathan Hartbd181b62013-02-17 16:05:38 -080036import com.netflix.curator.framework.recipes.leader.Participant;
Jonathan Hart3d7730a2013-02-22 11:51:17 -080037import com.netflix.curator.retry.ExponentialBackoffRetry;
Jonathan Hartbd181b62013-02-17 16:05:38 -080038
Jonathan Hart7bf62172013-02-28 13:17:18 -080039/**
40 * A registry service that uses Zookeeper. All data is stored in Zookeeper,
41 * so this can be used as a global registry in a multi-node ONOS cluster.
42 * @author jono
43 *
44 */
Jonathan Hartbd766972013-02-22 15:13:03 -080045public class ZookeeperRegistry implements IFloodlightModule, IControllerRegistryService {
Jonathan Hartc6eee9e2013-02-18 14:58:27 -080046
Jonathan Hartbd766972013-02-22 15:13:03 -080047 protected static Logger log = LoggerFactory.getLogger(ZookeeperRegistry.class);
48 protected String controllerId = null;
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -080049
Jonathan Hart3d7730a2013-02-22 11:51:17 -080050 protected IRestApiService restApi;
51
Jonathan Hart7bf62172013-02-28 13:17:18 -080052 //This is the default, it's overwritten by the connectionString configuration parameter
Jonathan Hartbd181b62013-02-17 16:05:38 -080053 protected String connectionString = "localhost:2181";
Jonathan Hart3d7730a2013-02-22 11:51:17 -080054
Jonathan Hartbd181b62013-02-17 16:05:38 -080055 private final String namespace = "onos";
Jonathan Hartedd6a442013-02-20 15:22:06 -080056 private final String switchLatchesPath = "/switches";
Jonathan Hart3d7730a2013-02-22 11:51:17 -080057 private final String controllerPath = "/controllers";
Jonathan Hartbd181b62013-02-17 16:05:38 -080058
59 protected CuratorFramework client;
Jonathan Hartedd6a442013-02-20 15:22:06 -080060
Jonathan Hartedd6a442013-02-20 15:22:06 -080061 protected PathChildrenCache controllerCache;
Jonathan Hart3d7730a2013-02-22 11:51:17 -080062 protected PathChildrenCache switchCache;
Jonathan Hartbd181b62013-02-17 16:05:38 -080063
Jonathan Hart89187372013-03-14 16:41:09 -070064 protected ConcurrentHashMap<String, SwitchLeadershipData> switches;
65 //protected Map<String, LeaderLatch> switchLatches;
66 //protected Map<String, ControlChangeCallback> switchCallbacks;
Jonathan Hart3d7730a2013-02-22 11:51:17 -080067 protected Map<String, PathChildrenCache> switchPathCaches;
Jonathan Hartbd181b62013-02-17 16:05:38 -080068
Jonathan Hart97801ac2013-02-26 14:29:16 -080069 //Zookeeper performance-related configuration
Jonathan Hartcc957a02013-02-26 10:39:04 -080070 protected static final int sessionTimeout = 2000;
71 protected static final int connectionTimeout = 4000;
Jonathan Hart57080fb2013-02-21 10:55:46 -080072
Jonathan Hart7bf62172013-02-28 13:17:18 -080073 /**
74 * Watches for changes in switch leadership election. The Curator
75 * LeaderLatch doesn't notify us when leadership changes so we set a watch
76 * on the election znodes to get leadership change events. The process
77 * method will be called whenever the switches children change in
78 * Zookeeper. We then have to work out whether to send a control-changed
79 * event to our clients and reset the watch.
80 *
81 * TODO I think it's possible to miss events that happen while we're
82 * processing the watch and before we've set a new watch. Need to think
83 * of a safer way to implement leader change notifications.
84 *
85 */
Jonathan Hart89187372013-03-14 16:41:09 -070086 /*
Jonathan Hartbd181b62013-02-17 16:05:38 -080087 protected class ParamaterizedCuratorWatcher implements CuratorWatcher {
88 private String dpid;
Jonathan Hartc6eee9e2013-02-18 14:58:27 -080089 private boolean isLeader = false;
Jonathan Hartbd181b62013-02-17 16:05:38 -080090 private String latchPath;
91
92 public ParamaterizedCuratorWatcher(String dpid, String latchPath){
93 this.dpid = dpid;
94 this.latchPath = latchPath;
95 }
96
97 @Override
Jonathan Hartedd6a442013-02-20 15:22:06 -080098 public synchronized void process(WatchedEvent event) throws Exception {
Jonathan Hartbd181b62013-02-17 16:05:38 -080099 log.debug("Watch Event: {}", event);
100
Jonathan Hartbd181b62013-02-17 16:05:38 -0800101
Jonathan Hartc6eee9e2013-02-18 14:58:27 -0800102 if (event.getState() == KeeperState.Disconnected){
103 if (isLeader) {
104 log.debug("Disconnected while leader - lost leadership for {}", dpid);
105
106 isLeader = false;
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800107 ControlChangeCallback cb = switchCallbacks.get(dpid);
108 if (cb != null) {
109 //Allow callback to be null if the requester doesn't want a callback
110 cb.controlChanged(HexString.toLong(dpid), false);
111 }
Jonathan Hartc6eee9e2013-02-18 14:58:27 -0800112 }
113 return;
Jonathan Hart7bf62172013-02-28 13:17:18 -0800114 //TODO Watcher is never reset once we reconnect to Zookeeper
115 }
116
117 LeaderLatch latch = switchLatches.get(dpid);
118 if (latch == null){
119 log.debug("In watcher process, looks like control was released for {}",
120 dpid);
121 return;
Jonathan Hartc6eee9e2013-02-18 14:58:27 -0800122 }
Jonathan Hartbd181b62013-02-17 16:05:38 -0800123
Jonathan Hartc6eee9e2013-02-18 14:58:27 -0800124 try {
Jonathan Hartedd6a442013-02-20 15:22:06 -0800125
Jonathan Hartc6eee9e2013-02-18 14:58:27 -0800126 Participant leader = latch.getLeader();
127
Jonathan Hartbd766972013-02-22 15:13:03 -0800128 if (leader.getId().equals(controllerId) && !isLeader){
Jonathan Hartc6eee9e2013-02-18 14:58:27 -0800129 log.debug("Became leader for {}", dpid);
130
131 isLeader = true;
Jonathan Hartd82f20d2013-02-21 18:04:24 -0800132 switchCallbacks.get(dpid).controlChanged(HexString.toLong(dpid), true);
Jonathan Hartc6eee9e2013-02-18 14:58:27 -0800133 }
Jonathan Hartbd766972013-02-22 15:13:03 -0800134 else if (!leader.getId().equals(controllerId) && isLeader){
Jonathan Hartc6eee9e2013-02-18 14:58:27 -0800135 log.debug("Lost leadership for {}", dpid);
136
137 isLeader = false;
Jonathan Hartd82f20d2013-02-21 18:04:24 -0800138 switchCallbacks.get(dpid).controlChanged(HexString.toLong(dpid), false);
Jonathan Hartc6eee9e2013-02-18 14:58:27 -0800139 }
140 } catch (Exception e){
141 if (isLeader){
Jonathan Hart7bf62172013-02-28 13:17:18 -0800142 log.debug("Exception checking leadership status. Assume leadership lost for {}",
Jonathan Hartc6eee9e2013-02-18 14:58:27 -0800143 dpid);
144
145 isLeader = false;
Jonathan Hartd82f20d2013-02-21 18:04:24 -0800146 switchCallbacks.get(dpid).controlChanged(HexString.toLong(dpid), false);
Jonathan Hartc6eee9e2013-02-18 14:58:27 -0800147 }
Jonathan Hart7bf62172013-02-28 13:17:18 -0800148 } finally {
149 client.getChildren().usingWatcher(this).inBackground().forPath(latchPath);
Jonathan Hartbd181b62013-02-17 16:05:38 -0800150 }
Jonathan Hartbd181b62013-02-17 16:05:38 -0800151 //client.getChildren().usingWatcher(this).forPath(latchPath);
152 }
Jonathan Hartc6eee9e2013-02-18 14:58:27 -0800153 }
Jonathan Hart89187372013-03-14 16:41:09 -0700154 */
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800155
Jonathan Hart89187372013-03-14 16:41:09 -0700156 protected class SwitchLeaderListener implements LeaderLatchListener{
Jonathan Hart0de09492013-03-13 14:37:21 -0700157 String dpid;
158 LeaderLatch latch;
159
Jonathan Hart89187372013-03-14 16:41:09 -0700160 public SwitchLeaderListener(String dpid, LeaderLatch latch){
Jonathan Hart0de09492013-03-13 14:37:21 -0700161 this.dpid = dpid;
162 this.latch = latch;
163 }
164
165 @Override
166 public void leaderLatchEvent(CuratorFramework arg0,
167 LeaderLatchEvent arg1) {
Jonathan Hart89187372013-03-14 16:41:09 -0700168 log.debug("Leadership changed for {}, now {}",
Jonathan Hart0de09492013-03-13 14:37:21 -0700169 dpid, latch.hasLeadership());
170
Jonathan Hart89187372013-03-14 16:41:09 -0700171 //Check that the leadership request is still active - the client
172 //may have since released the request or even begun another request
173 //(this is why we use == to check the object instance is the same)
174 SwitchLeadershipData swData = switches.get(dpid);
175 if (swData != null && swData.getLatch() == latch){
176 swData.getCallback().controlChanged(
177 HexString.toLong(dpid), latch.hasLeadership());
178 }
179 else {
180 log.debug("Latch for {} has changed", dpid);
181 }
Jonathan Hart0de09492013-03-13 14:37:21 -0700182 }
183 }
184
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800185
Jonathan Hart7bf62172013-02-28 13:17:18 -0800186 /**
187 * Listens for changes to the switch znodes in Zookeeper. This maintains
188 * the second level of PathChildrenCaches that hold the controllers
189 * contending for each switch - there's one for each switch.
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800190 */
191 PathChildrenCacheListener switchPathCacheListener = new PathChildrenCacheListener() {
192 @Override
193 public void childEvent(CuratorFramework client,
194 PathChildrenCacheEvent event) throws Exception {
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800195 log.debug("Root switch path cache got {} event", event.getType());
196
197 String strSwitch = null;
198 if (event.getData() != null){
199 log.debug("Event path {}", event.getData().getPath());
200 String[] splitted = event.getData().getPath().split("/");
201 strSwitch = splitted[splitted.length - 1];
202 log.debug("Switch name is {}", strSwitch);
203 }
204
205 switch (event.getType()){
206 case CHILD_ADDED:
207 case CHILD_UPDATED:
208 //Check we have a PathChildrenCache for this child, add one if not
209 if (switchPathCaches.get(strSwitch) == null){
210 PathChildrenCache pc = new PathChildrenCache(client,
211 event.getData().getPath(), true);
212 pc.start(StartMode.NORMAL);
213 switchPathCaches.put(strSwitch, pc);
214 }
215 break;
216 case CHILD_REMOVED:
217 //Remove our PathChildrenCache for this child
218 PathChildrenCache pc = switchPathCaches.remove(strSwitch);
219 pc.close();
220 break;
221 default:
222 //All other events are connection status events. We need to do anything
223 //as the path cache handles these on its own.
224 break;
225 }
226
227 }
228 };
Jonathan Hartedd6a442013-02-20 15:22:06 -0800229
Jonathan Hartbd181b62013-02-17 16:05:38 -0800230
231 @Override
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800232 public void requestControl(long dpid, ControlChangeCallback cb) throws RegistryException {
Jonathan Hart7bf62172013-02-28 13:17:18 -0800233 log.info("Requesting control for {}", HexString.toHexString(dpid));
Jonathan Hartc6eee9e2013-02-18 14:58:27 -0800234
Jonathan Hartbd766972013-02-22 15:13:03 -0800235 if (controllerId == null){
236 throw new RuntimeException("Must register a controller before calling requestControl");
Jonathan Hartbd181b62013-02-17 16:05:38 -0800237 }
238
239 String dpidStr = HexString.toHexString(dpid);
240 String latchPath = switchLatchesPath + "/" + dpidStr;
241
Jonathan Hart89187372013-03-14 16:41:09 -0700242 //if (switchLatches.get(dpidStr) != null){
243 if (switches.get(dpidStr) != null){
Jonathan Hart3c0eccd2013-03-12 22:32:50 -0700244 //throw new RuntimeException("Leader election for switch " + dpidStr +
245 // "is already running");
246 log.debug("Already contesting {}, returning", HexString.toHexString(dpid));
247 return;
Jonathan Hartc6eee9e2013-02-18 14:58:27 -0800248 }
249
Jonathan Hartbd766972013-02-22 15:13:03 -0800250 LeaderLatch latch = new LeaderLatch(client, latchPath, controllerId);
Jonathan Hart89187372013-03-14 16:41:09 -0700251 latch.addListener(new SwitchLeaderListener(dpidStr, latch));
Jonathan Hartbd181b62013-02-17 16:05:38 -0800252
Jonathan Hart89187372013-03-14 16:41:09 -0700253 //switchLatches.put(dpidStr, latch);
254 //switchCallbacks.put(dpidStr, cb);
Jonathan Hart0de09492013-03-13 14:37:21 -0700255
Jonathan Hart89187372013-03-14 16:41:09 -0700256 //Don't put the entry in the map if we can't start the latch
257 SwitchLeadershipData swData = new SwitchLeadershipData(latch, cb);
258 SwitchLeadershipData oldData = switches.putIfAbsent(dpidStr, swData);
259
260 if (oldData != null){
261 //There was already data for that key in the map
262 //i.e. someone else got here first so we can't succeed
263 log.debug("Already requested control for {}", dpidStr);
264 throw new RegistryException("Already requested control for " + dpidStr);
265 }
266
267 //Now that we know we were able to add our latch to the collection,
268 //we can start the leader election in Zookeeper
Jonathan Hartbd181b62013-02-17 16:05:38 -0800269 try {
270 //client.getChildren().usingWatcher(watcher).inBackground().forPath(singleLatchPath);
Jonathan Hart0de09492013-03-13 14:37:21 -0700271 //client.getChildren().usingWatcher(
272 // new ParamaterizedCuratorWatcher(dpidStr, latchPath))
273 // .inBackground().forPath(latchPath);
Jonathan Hartbd181b62013-02-17 16:05:38 -0800274 latch.start();
275 } catch (Exception e) {
Jonathan Hartc6eee9e2013-02-18 14:58:27 -0800276 log.warn("Error starting leader latch: {}", e.getMessage());
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800277 throw new RegistryException("Error starting leader latch for " + dpidStr, e);
Jonathan Hartbd181b62013-02-17 16:05:38 -0800278 }
279
280 }
281
282 @Override
Jonathan Hartd82f20d2013-02-21 18:04:24 -0800283 public void releaseControl(long dpid) {
Jonathan Hart7bf62172013-02-28 13:17:18 -0800284 log.info("Releasing control for {}", HexString.toHexString(dpid));
Jonathan Hart57080fb2013-02-21 10:55:46 -0800285
Jonathan Hartc6eee9e2013-02-18 14:58:27 -0800286 String dpidStr = HexString.toHexString(dpid);
287
Jonathan Hart89187372013-03-14 16:41:09 -0700288 SwitchLeadershipData swData = switches.remove(dpidStr);
289
290 if (swData == null) {
Jonathan Hart7bf62172013-02-28 13:17:18 -0800291 log.debug("Trying to release control of a switch we are not contesting");
Jonathan Hartbd181b62013-02-17 16:05:38 -0800292 return;
293 }
Jonathan Hart89187372013-03-14 16:41:09 -0700294
295 //LeaderLatch latch = switches.get(dpidStr).getLatch();
296 //LeaderLatch latch = switches.remove(dpidStr).getLatch();
297 LeaderLatch latch = swData.getLatch();
Jonathan Hartbd181b62013-02-17 16:05:38 -0800298
299 try {
300 latch.close();
301 } catch (IOException e) {
Jonathan Hart7bf62172013-02-28 13:17:18 -0800302 //I think it's OK not to do anything here. Either the node got
303 //deleted correctly, or the connection went down and the node got deleted.
Jonathan Hart1be46262013-02-20 16:43:51 -0800304 } finally {
Jonathan Hart89187372013-03-14 16:41:09 -0700305 //switches.remove(dpidStr);
306 //switchLatches.remove(dpidStr);
307 //switchCallbacks.remove(dpidStr);
Jonathan Hartbd181b62013-02-17 16:05:38 -0800308 }
309 }
310
311 @Override
Jonathan Hartd82f20d2013-02-21 18:04:24 -0800312 public boolean hasControl(long dpid) {
Jonathan Hart89187372013-03-14 16:41:09 -0700313 //LeaderLatch latch = switchLatches.get(HexString.toHexString(dpid));
314 String dpidStr = HexString.toHexString(dpid);
Jonathan Hart57080fb2013-02-21 10:55:46 -0800315
Jonathan Hart89187372013-03-14 16:41:09 -0700316 SwitchLeadershipData swData = switches.get(dpidStr);
Jonathan Hartbd181b62013-02-17 16:05:38 -0800317
Jonathan Hart89187372013-03-14 16:41:09 -0700318 if (swData == null) {
319 log.warn("No leader latch for dpid {}", dpidStr);
Jonathan Hartbd181b62013-02-17 16:05:38 -0800320 return false;
321 }
322
Jonathan Hart89187372013-03-14 16:41:09 -0700323 return swData.getLatch().hasLeadership();
324
325 //try {
326 //return latch.getLeader().getId().equals(controllerId);
327
328 //} catch (Exception e) {
Jonathan Hartbd181b62013-02-17 16:05:38 -0800329 //TODO swallow exception?
Jonathan Hart89187372013-03-14 16:41:09 -0700330 //return false;
331 //}
Jonathan Hartbd181b62013-02-17 16:05:38 -0800332 }
333
334 @Override
Jonathan Hart7bf62172013-02-28 13:17:18 -0800335 public String getControllerId() {
Jonathan Hartbd766972013-02-22 15:13:03 -0800336 return controllerId;
Jonathan Hartbd181b62013-02-17 16:05:38 -0800337 }
338
Jonathan Hartedd6a442013-02-20 15:22:06 -0800339 @Override
Jonathan Hart57080fb2013-02-21 10:55:46 -0800340 public Collection<String> getAllControllers() throws RegistryException {
Jonathan Hartedd6a442013-02-20 15:22:06 -0800341 log.debug("Getting all controllers");
Jonathan Hart1be46262013-02-20 16:43:51 -0800342
Jonathan Hartedd6a442013-02-20 15:22:06 -0800343 List<String> controllers = new ArrayList<String>();
344 for (ChildData data : controllerCache.getCurrentData()){
345
346 String d = null;
347 try {
348 d = new String(data.getData(), "UTF-8");
349 } catch (UnsupportedEncodingException e) {
Jonathan Hart57080fb2013-02-21 10:55:46 -0800350 throw new RegistryException("Error encoding string", e);
Jonathan Hartedd6a442013-02-20 15:22:06 -0800351 }
352
353 controllers.add(d);
354 }
355 return controllers;
356 }
357
358 @Override
Jonathan Hart57080fb2013-02-21 10:55:46 -0800359 public void registerController(String id) throws RegistryException {
Jonathan Hartd10008d2013-02-23 17:04:08 -0800360 if (controllerId != null) {
361 throw new RegistryException(
362 "Controller already registered with id " + controllerId);
363 }
Jonathan Hartbd766972013-02-22 15:13:03 -0800364
365 controllerId = id;
Jonathan Hart57080fb2013-02-21 10:55:46 -0800366
Jonathan Hartd10008d2013-02-23 17:04:08 -0800367 byte bytes[] = id.getBytes(Charsets.UTF_8);
Jonathan Hartedd6a442013-02-20 15:22:06 -0800368
369 String path = controllerPath + "/" + id;
370
371 log.info("Registering controller with id {}", id);
372
Jonathan Hart57080fb2013-02-21 10:55:46 -0800373 //Create ephemeral node in controller registry
Jonathan Hartedd6a442013-02-20 15:22:06 -0800374 try {
375 client.create().withProtection().withMode(CreateMode.EPHEMERAL)
376 .forPath(path, bytes);
377 } catch (Exception e) {
Jonathan Hart57080fb2013-02-21 10:55:46 -0800378 throw new RegistryException("Error contacting the Zookeeper service", e);
Jonathan Hartedd6a442013-02-20 15:22:06 -0800379 }
380 }
381
382 @Override
Jonathan Hart57080fb2013-02-21 10:55:46 -0800383 public String getControllerForSwitch(long dpid) throws RegistryException {
Jonathan Hart89187372013-03-14 16:41:09 -0700384 // TODO work out synchronization
Jonathan Hartedd6a442013-02-20 15:22:06 -0800385
Jonathan Hart89187372013-03-14 16:41:09 -0700386 String dpidStr = HexString.toHexString(dpid);
387 //LeaderLatch latch = switchLatches.get(strDpid);
388 LeaderLatch latch = switches.get(dpidStr).getLatch();
Jonathan Hartedd6a442013-02-20 15:22:06 -0800389
390 if (latch == null){
391 log.warn("Tried to get controller for non-existent switch");
392 return null;
393 }
394
395 Participant leader = null;
396 try {
397 leader = latch.getLeader();
398 } catch (Exception e) {
Jonathan Hart57080fb2013-02-21 10:55:46 -0800399 throw new RegistryException("Error contacting the Zookeeper service", e);
Jonathan Hartedd6a442013-02-20 15:22:06 -0800400 }
401
402 return leader.getId();
403 }
404
405 @Override
406 public Collection<Long> getSwitchesControlledByController(String controllerId) {
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800407 //TODO remove this if not needed
Jonathan Hartbd766972013-02-22 15:13:03 -0800408 throw new RuntimeException("Not yet implemented");
Jonathan Hartedd6a442013-02-20 15:22:06 -0800409 }
Jonathan Hartbd181b62013-02-17 16:05:38 -0800410
Jonathan Hartd82f20d2013-02-21 18:04:24 -0800411
Jonathan Hart89187372013-03-14 16:41:09 -0700412 //TODO what should happen when there's no ZK connection? Currently we just return
413 //the cache but this may lead to false impressions - i.e. we don't actually know
414 //what's in ZK so we shouldn't say we do
Jonathan Hartd82f20d2013-02-21 18:04:24 -0800415 @Override
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800416 public Map<String, List<ControllerRegistryEntry>> getAllSwitches() {
417 Map<String, List<ControllerRegistryEntry>> data =
418 new HashMap<String, List<ControllerRegistryEntry>>();
419
420 for (Map.Entry<String, PathChildrenCache> entry : switchPathCaches.entrySet()){
421 List<ControllerRegistryEntry> contendingControllers =
422 new ArrayList<ControllerRegistryEntry>();
423
424 if (entry.getValue().getCurrentData().size() < 1){
425 log.info("Switch entry with no leader elections: {}", entry.getKey());
426 continue;
427 }
428
429 for (ChildData d : entry.getValue().getCurrentData()) {
Jonathan Hart97801ac2013-02-26 14:29:16 -0800430
Jonathan Hartd10008d2013-02-23 17:04:08 -0800431 String controllerId = new String(d.getData(), Charsets.UTF_8);
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800432
433 String[] splitted = d.getPath().split("-");
434 int sequenceNumber = Integer.parseInt(splitted[splitted.length - 1]);
435
436 contendingControllers.add(new ControllerRegistryEntry(controllerId, sequenceNumber));
437 }
438
439 Collections.sort(contendingControllers);
440 data.put(entry.getKey(), contendingControllers);
441 }
442 return data;
Jonathan Hartd82f20d2013-02-21 18:04:24 -0800443 }
444
Jonathan Hartbd181b62013-02-17 16:05:38 -0800445 /*
446 * IFloodlightModule
447 */
448
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -0800449 @Override
450 public Collection<Class<? extends IFloodlightService>> getModuleServices() {
Jonathan Hartedd6a442013-02-20 15:22:06 -0800451 Collection<Class<? extends IFloodlightService>> l =
452 new ArrayList<Class<? extends IFloodlightService>>();
Jonathan Hartd82f20d2013-02-21 18:04:24 -0800453 l.add(IControllerRegistryService.class);
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -0800454 return l;
455 }
456
457 @Override
458 public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {
459 Map<Class<? extends IFloodlightService>, IFloodlightService> m =
460 new HashMap<Class<? extends IFloodlightService>, IFloodlightService>();
Jonathan Hartd82f20d2013-02-21 18:04:24 -0800461 m.put(IControllerRegistryService.class, this);
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -0800462 return m;
463 }
464
465 @Override
466 public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800467 Collection<Class<? extends IFloodlightService>> l =
468 new ArrayList<Class<? extends IFloodlightService>>();
469 l.add(IRestApiService.class);
470 return l;
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -0800471 }
472
Jonathan Hart89187372013-03-14 16:41:09 -0700473 //TODO currently blocks startup when it can't get a Zookeeper connection.
474 //Do we support starting up with no Zookeeper connection?
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -0800475 @Override
476 public void init (FloodlightModuleContext context) throws FloodlightModuleException {
Jonathan Hartbd766972013-02-22 15:13:03 -0800477 log.info("Initialising the Zookeeper Registry - Zookeeper connection required");
478
Jonathan Hart97801ac2013-02-26 14:29:16 -0800479 //Read the Zookeeper connection string from the config
480 Map<String, String> configParams = context.getConfigParams(this);
481 String connectionString = configParams.get("connectionString");
482 if (connectionString != null){
483 this.connectionString = connectionString;
Jonathan Hart57080fb2013-02-21 10:55:46 -0800484 }
Jonathan Hart97801ac2013-02-26 14:29:16 -0800485 log.info("Setting Zookeeper connection string to {}", this.connectionString);
Jonathan Hart57080fb2013-02-21 10:55:46 -0800486
Jonathan Hart97801ac2013-02-26 14:29:16 -0800487 restApi = context.getServiceImpl(IRestApiService.class);
Jonathan Hartbd181b62013-02-17 16:05:38 -0800488
Jonathan Hart89187372013-03-14 16:41:09 -0700489 switches = new ConcurrentHashMap<String, SwitchLeadershipData>();
490
491 //switchLatches = new HashMap<String, LeaderLatch>();
492 //switchCallbacks = new HashMap<String, ControlChangeCallback>();
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800493 switchPathCaches = new HashMap<String, PathChildrenCache>();
Jonathan Hartbd181b62013-02-17 16:05:38 -0800494
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800495 RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
Jonathan Hart97801ac2013-02-26 14:29:16 -0800496 client = CuratorFrameworkFactory.newClient(this.connectionString,
Jonathan Hartcc957a02013-02-26 10:39:04 -0800497 sessionTimeout, connectionTimeout, retryPolicy);
Jonathan Hartbd181b62013-02-17 16:05:38 -0800498
499 client.start();
500
501 client = client.usingNamespace(namespace);
Jonathan Hart97801ac2013-02-26 14:29:16 -0800502
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800503
Jonathan Hartedd6a442013-02-20 15:22:06 -0800504 controllerCache = new PathChildrenCache(client, controllerPath, true);
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800505 switchCache = new PathChildrenCache(client, switchLatchesPath, true);
506 switchCache.getListenable().addListener(switchPathCacheListener);
Jonathan Hartedd6a442013-02-20 15:22:06 -0800507
508 try {
509 controllerCache.start(StartMode.BUILD_INITIAL_CACHE);
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800510
511 //Don't prime the cache, we want a notification for each child node in the path
512 switchCache.start(StartMode.NORMAL);
Jonathan Hartedd6a442013-02-20 15:22:06 -0800513 } catch (Exception e) {
Jonathan Hart7bf62172013-02-28 13:17:18 -0800514 throw new FloodlightModuleException("Error initialising ZookeeperRegistry: "
515 + e.getMessage());
Jonathan Hartedd6a442013-02-20 15:22:06 -0800516 }
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -0800517 }
518
519 @Override
520 public void startUp (FloodlightModuleContext context) {
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800521 restApi.addRestletRoutable(new RegistryWebRoutable());
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -0800522 }
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -0800523}