blob: e587b0fdd70fc0a6734e2caca9b56c497b1f328f [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 Hart0b3eee42013-03-16 18:20:04 -070020import org.apache.zookeeper.WatchedEvent;
21import org.apache.zookeeper.Watcher;
Jonathan Hartbd181b62013-02-17 16:05:38 -080022import org.openflow.util.HexString;
23import org.slf4j.Logger;
24import org.slf4j.LoggerFactory;
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -080025
Jonathan Hartd10008d2013-02-23 17:04:08 -080026import com.google.common.base.Charsets;
Jonathan Hartbd181b62013-02-17 16:05:38 -080027import com.netflix.curator.RetryPolicy;
28import com.netflix.curator.framework.CuratorFramework;
29import com.netflix.curator.framework.CuratorFrameworkFactory;
Jonathan Hartedd6a442013-02-20 15:22:06 -080030import com.netflix.curator.framework.recipes.cache.ChildData;
31import com.netflix.curator.framework.recipes.cache.PathChildrenCache;
32import com.netflix.curator.framework.recipes.cache.PathChildrenCache.StartMode;
Jonathan Hart3d7730a2013-02-22 11:51:17 -080033import com.netflix.curator.framework.recipes.cache.PathChildrenCacheEvent;
34import com.netflix.curator.framework.recipes.cache.PathChildrenCacheListener;
Jonathan Hartbd181b62013-02-17 16:05:38 -080035import com.netflix.curator.framework.recipes.leader.LeaderLatch;
Jonathan Hart0de09492013-03-13 14:37:21 -070036import com.netflix.curator.framework.recipes.leader.LeaderLatchEvent;
37import com.netflix.curator.framework.recipes.leader.LeaderLatchListener;
Jonathan Hartbd181b62013-02-17 16:05:38 -080038import com.netflix.curator.framework.recipes.leader.Participant;
Jonathan Hart3d7730a2013-02-22 11:51:17 -080039import com.netflix.curator.retry.ExponentialBackoffRetry;
Jonathan Hart0b3eee42013-03-16 18:20:04 -070040import com.netflix.curator.utils.ZKPaths;
Jonathan Hartbd181b62013-02-17 16:05:38 -080041
Jonathan Hart7bf62172013-02-28 13:17:18 -080042/**
43 * A registry service that uses Zookeeper. All data is stored in Zookeeper,
44 * so this can be used as a global registry in a multi-node ONOS cluster.
45 * @author jono
46 *
47 */
Jonathan Hartbd766972013-02-22 15:13:03 -080048public class ZookeeperRegistry implements IFloodlightModule, IControllerRegistryService {
Jonathan Hartc6eee9e2013-02-18 14:58:27 -080049
Jonathan Hartbd766972013-02-22 15:13:03 -080050 protected static Logger log = LoggerFactory.getLogger(ZookeeperRegistry.class);
51 protected String controllerId = null;
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -080052
Jonathan Hart3d7730a2013-02-22 11:51:17 -080053 protected IRestApiService restApi;
54
Jonathan Hart7bf62172013-02-28 13:17:18 -080055 //This is the default, it's overwritten by the connectionString configuration parameter
Jonathan Hartbd181b62013-02-17 16:05:38 -080056 protected String connectionString = "localhost:2181";
Jonathan Hart3d7730a2013-02-22 11:51:17 -080057
Jonathan Hartbd181b62013-02-17 16:05:38 -080058 private final String namespace = "onos";
Jonathan Hartedd6a442013-02-20 15:22:06 -080059 private final String switchLatchesPath = "/switches";
Jonathan Hart3d7730a2013-02-22 11:51:17 -080060 private final String controllerPath = "/controllers";
Jonathan Hartbd181b62013-02-17 16:05:38 -080061
62 protected CuratorFramework client;
Jonathan Hartedd6a442013-02-20 15:22:06 -080063
Jonathan Hartedd6a442013-02-20 15:22:06 -080064 protected PathChildrenCache controllerCache;
Jonathan Hart3d7730a2013-02-22 11:51:17 -080065 protected PathChildrenCache switchCache;
Jonathan Hartbd181b62013-02-17 16:05:38 -080066
Jonathan Hart89187372013-03-14 16:41:09 -070067 protected ConcurrentHashMap<String, SwitchLeadershipData> switches;
Jonathan Hart3d7730a2013-02-22 11:51:17 -080068 protected Map<String, PathChildrenCache> switchPathCaches;
Jonathan Hartbd181b62013-02-17 16:05:38 -080069
Jonathan Hart97801ac2013-02-26 14:29:16 -080070 //Zookeeper performance-related configuration
Jonathan Hart0b3eee42013-03-16 18:20:04 -070071 protected static final int sessionTimeout = 5000;
72 protected static final int connectionTimeout = 7000;
Jonathan Hart57080fb2013-02-21 10:55:46 -080073
Jonathan Hartbd181b62013-02-17 16:05:38 -080074
Jonathan Hart89187372013-03-14 16:41:09 -070075 protected class SwitchLeaderListener implements LeaderLatchListener{
Jonathan Hart0de09492013-03-13 14:37:21 -070076 String dpid;
77 LeaderLatch latch;
78
Jonathan Hart89187372013-03-14 16:41:09 -070079 public SwitchLeaderListener(String dpid, LeaderLatch latch){
Jonathan Hart0de09492013-03-13 14:37:21 -070080 this.dpid = dpid;
81 this.latch = latch;
82 }
83
84 @Override
85 public void leaderLatchEvent(CuratorFramework arg0,
86 LeaderLatchEvent arg1) {
Jonathan Hart89187372013-03-14 16:41:09 -070087 log.debug("Leadership changed for {}, now {}",
Jonathan Hart0de09492013-03-13 14:37:21 -070088 dpid, latch.hasLeadership());
89
Jonathan Hart89187372013-03-14 16:41:09 -070090 //Check that the leadership request is still active - the client
91 //may have since released the request or even begun another request
92 //(this is why we use == to check the object instance is the same)
93 SwitchLeadershipData swData = switches.get(dpid);
94 if (swData != null && swData.getLatch() == latch){
95 swData.getCallback().controlChanged(
96 HexString.toLong(dpid), latch.hasLeadership());
97 }
98 else {
99 log.debug("Latch for {} has changed", dpid);
100 }
Jonathan Hart0de09492013-03-13 14:37:21 -0700101 }
102 }
103
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800104
Jonathan Hart7bf62172013-02-28 13:17:18 -0800105 /**
106 * Listens for changes to the switch znodes in Zookeeper. This maintains
107 * the second level of PathChildrenCaches that hold the controllers
108 * contending for each switch - there's one for each switch.
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800109 */
110 PathChildrenCacheListener switchPathCacheListener = new PathChildrenCacheListener() {
111 @Override
112 public void childEvent(CuratorFramework client,
113 PathChildrenCacheEvent event) throws Exception {
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800114 log.debug("Root switch path cache got {} event", event.getType());
115
116 String strSwitch = null;
117 if (event.getData() != null){
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800118 String[] splitted = event.getData().getPath().split("/");
119 strSwitch = splitted[splitted.length - 1];
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800120 }
121
122 switch (event.getType()){
123 case CHILD_ADDED:
124 case CHILD_UPDATED:
125 //Check we have a PathChildrenCache for this child, add one if not
126 if (switchPathCaches.get(strSwitch) == null){
127 PathChildrenCache pc = new PathChildrenCache(client,
128 event.getData().getPath(), true);
129 pc.start(StartMode.NORMAL);
130 switchPathCaches.put(strSwitch, pc);
131 }
132 break;
133 case CHILD_REMOVED:
134 //Remove our PathChildrenCache for this child
135 PathChildrenCache pc = switchPathCaches.remove(strSwitch);
136 pc.close();
137 break;
138 default:
139 //All other events are connection status events. We need to do anything
140 //as the path cache handles these on its own.
141 break;
142 }
143
144 }
145 };
Jonathan Hartedd6a442013-02-20 15:22:06 -0800146
Jonathan Hartbd181b62013-02-17 16:05:38 -0800147
148 @Override
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800149 public void requestControl(long dpid, ControlChangeCallback cb) throws RegistryException {
Jonathan Hart7bf62172013-02-28 13:17:18 -0800150 log.info("Requesting control for {}", HexString.toHexString(dpid));
Jonathan Hartc6eee9e2013-02-18 14:58:27 -0800151
Jonathan Hartbd766972013-02-22 15:13:03 -0800152 if (controllerId == null){
153 throw new RuntimeException("Must register a controller before calling requestControl");
Jonathan Hartbd181b62013-02-17 16:05:38 -0800154 }
155
156 String dpidStr = HexString.toHexString(dpid);
157 String latchPath = switchLatchesPath + "/" + dpidStr;
158
Jonathan Hart89187372013-03-14 16:41:09 -0700159 if (switches.get(dpidStr) != null){
Jonathan Hart3c0eccd2013-03-12 22:32:50 -0700160 log.debug("Already contesting {}, returning", HexString.toHexString(dpid));
Pankaj Berdeda7187b2013-03-18 15:24:59 -0700161 throw new RegistryException("Already contesting control for " + dpidStr);
Jonathan Hartc6eee9e2013-02-18 14:58:27 -0800162 }
163
Jonathan Hartbd766972013-02-22 15:13:03 -0800164 LeaderLatch latch = new LeaderLatch(client, latchPath, controllerId);
Jonathan Hart89187372013-03-14 16:41:09 -0700165 latch.addListener(new SwitchLeaderListener(dpidStr, latch));
Jonathan Hartbd181b62013-02-17 16:05:38 -0800166
Jonathan Hart44e56fc2013-03-14 16:53:59 -0700167
Jonathan Hart89187372013-03-14 16:41:09 -0700168 SwitchLeadershipData swData = new SwitchLeadershipData(latch, cb);
169 SwitchLeadershipData oldData = switches.putIfAbsent(dpidStr, swData);
170
171 if (oldData != null){
172 //There was already data for that key in the map
173 //i.e. someone else got here first so we can't succeed
174 log.debug("Already requested control for {}", dpidStr);
175 throw new RegistryException("Already requested control for " + dpidStr);
176 }
177
178 //Now that we know we were able to add our latch to the collection,
Jonathan Hart44e56fc2013-03-14 16:53:59 -0700179 //we can start the leader election in Zookeeper. However I don't know
180 //how to handle if the start fails - the latch is already in our
181 //switches list.
182 //TODO seems like there's a Curator bug when latch.start is called when
183 //there's no Zookeeper connection which causes two znodes to be put in
184 //Zookeeper at the latch path when we reconnect to Zookeeper.
Jonathan Hartbd181b62013-02-17 16:05:38 -0800185 try {
Jonathan Hartbd181b62013-02-17 16:05:38 -0800186 latch.start();
187 } catch (Exception e) {
Jonathan Hartc6eee9e2013-02-18 14:58:27 -0800188 log.warn("Error starting leader latch: {}", e.getMessage());
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800189 throw new RegistryException("Error starting leader latch for " + dpidStr, e);
Jonathan Hartbd181b62013-02-17 16:05:38 -0800190 }
191
192 }
193
194 @Override
Jonathan Hartd82f20d2013-02-21 18:04:24 -0800195 public void releaseControl(long dpid) {
Jonathan Hart7bf62172013-02-28 13:17:18 -0800196 log.info("Releasing control for {}", HexString.toHexString(dpid));
Jonathan Hart57080fb2013-02-21 10:55:46 -0800197
Jonathan Hartc6eee9e2013-02-18 14:58:27 -0800198 String dpidStr = HexString.toHexString(dpid);
199
Jonathan Hart89187372013-03-14 16:41:09 -0700200 SwitchLeadershipData swData = switches.remove(dpidStr);
201
202 if (swData == null) {
Jonathan Hart7bf62172013-02-28 13:17:18 -0800203 log.debug("Trying to release control of a switch we are not contesting");
Jonathan Hartbd181b62013-02-17 16:05:38 -0800204 return;
205 }
Jonathan Hart89187372013-03-14 16:41:09 -0700206
Jonathan Hart89187372013-03-14 16:41:09 -0700207 LeaderLatch latch = swData.getLatch();
Jonathan Hartbd181b62013-02-17 16:05:38 -0800208
209 try {
210 latch.close();
211 } catch (IOException e) {
Jonathan Hart7bf62172013-02-28 13:17:18 -0800212 //I think it's OK not to do anything here. Either the node got
213 //deleted correctly, or the connection went down and the node got deleted.
Jonathan Hartbd181b62013-02-17 16:05:38 -0800214 }
215 }
216
217 @Override
Jonathan Hartd82f20d2013-02-21 18:04:24 -0800218 public boolean hasControl(long dpid) {
Jonathan Hart89187372013-03-14 16:41:09 -0700219 String dpidStr = HexString.toHexString(dpid);
Jonathan Hart57080fb2013-02-21 10:55:46 -0800220
Jonathan Hart89187372013-03-14 16:41:09 -0700221 SwitchLeadershipData swData = switches.get(dpidStr);
Jonathan Hartbd181b62013-02-17 16:05:38 -0800222
Jonathan Hart89187372013-03-14 16:41:09 -0700223 if (swData == null) {
224 log.warn("No leader latch for dpid {}", dpidStr);
Jonathan Hartbd181b62013-02-17 16:05:38 -0800225 return false;
226 }
227
Jonathan Hart89187372013-03-14 16:41:09 -0700228 return swData.getLatch().hasLeadership();
Jonathan Hartbd181b62013-02-17 16:05:38 -0800229 }
230
231 @Override
Jonathan Hart7bf62172013-02-28 13:17:18 -0800232 public String getControllerId() {
Jonathan Hartbd766972013-02-22 15:13:03 -0800233 return controllerId;
Jonathan Hartbd181b62013-02-17 16:05:38 -0800234 }
235
Jonathan Hartedd6a442013-02-20 15:22:06 -0800236 @Override
Jonathan Hart57080fb2013-02-21 10:55:46 -0800237 public Collection<String> getAllControllers() throws RegistryException {
Jonathan Hartedd6a442013-02-20 15:22:06 -0800238 log.debug("Getting all controllers");
Jonathan Hart1be46262013-02-20 16:43:51 -0800239
Jonathan Hartedd6a442013-02-20 15:22:06 -0800240 List<String> controllers = new ArrayList<String>();
241 for (ChildData data : controllerCache.getCurrentData()){
242
243 String d = null;
244 try {
245 d = new String(data.getData(), "UTF-8");
246 } catch (UnsupportedEncodingException e) {
Jonathan Hart57080fb2013-02-21 10:55:46 -0800247 throw new RegistryException("Error encoding string", e);
Jonathan Hartedd6a442013-02-20 15:22:06 -0800248 }
249
250 controllers.add(d);
251 }
252 return controllers;
253 }
254
255 @Override
Jonathan Hart57080fb2013-02-21 10:55:46 -0800256 public void registerController(String id) throws RegistryException {
Jonathan Hartd10008d2013-02-23 17:04:08 -0800257 if (controllerId != null) {
258 throw new RegistryException(
259 "Controller already registered with id " + controllerId);
260 }
Jonathan Hartbd766972013-02-22 15:13:03 -0800261
262 controllerId = id;
Jonathan Hart57080fb2013-02-21 10:55:46 -0800263
Jonathan Hartd10008d2013-02-23 17:04:08 -0800264 byte bytes[] = id.getBytes(Charsets.UTF_8);
Jonathan Hart0b3eee42013-03-16 18:20:04 -0700265 String path = ZKPaths.makePath(controllerPath, controllerId);
Jonathan Hartedd6a442013-02-20 15:22:06 -0800266
267 log.info("Registering controller with id {}", id);
268
Jonathan Hartedd6a442013-02-20 15:22:06 -0800269 try {
Jonathan Hart0b3eee42013-03-16 18:20:04 -0700270 //We need to set a watch to recreate the node in the controller
271 //registry if it gets deleted - e.g. on Zookeeper connection loss.
272 Watcher watcher = new Watcher(){
273 @Override
274 public void process(WatchedEvent event) {
275 log.debug("got any watch event {} ", event);
276
277 String path = ZKPaths.makePath(controllerPath, controllerId);
278 byte bytes[] = controllerId.getBytes(Charsets.UTF_8);
279
280 try {
281 if (event.getType() == Event.EventType.NodeDeleted){
282 log.debug("got a node deleted event");
283
284
285 client.create().withMode(CreateMode.EPHEMERAL)
286 .forPath(path, bytes);
287 }
288 } catch (Exception e) {
289 log.warn("Error recreating controller node for {}: {}",
290 controllerId, e.getMessage());
291 } finally {
292 try {
293 client.checkExists().usingWatcher(this).forPath(path);
294 } catch (Exception e2){
295 log.warn("Error resetting watch for {}: {}",
296 controllerId, e2.getMessage());
297 }
298 }
299 }
300 };
301
302 //Create ephemeral node in controller registry
303 //TODO Use protection
304 client.create().withMode(CreateMode.EPHEMERAL)
Jonathan Hartedd6a442013-02-20 15:22:06 -0800305 .forPath(path, bytes);
Jonathan Hart0b3eee42013-03-16 18:20:04 -0700306 client.checkExists().usingWatcher(watcher).forPath(path);
Jonathan Hartedd6a442013-02-20 15:22:06 -0800307 } catch (Exception e) {
Jonathan Hart57080fb2013-02-21 10:55:46 -0800308 throw new RegistryException("Error contacting the Zookeeper service", e);
Jonathan Hartedd6a442013-02-20 15:22:06 -0800309 }
310 }
311
312 @Override
Jonathan Hart57080fb2013-02-21 10:55:46 -0800313 public String getControllerForSwitch(long dpid) throws RegistryException {
Jonathan Hart89187372013-03-14 16:41:09 -0700314 // TODO work out synchronization
Jonathan Hartedd6a442013-02-20 15:22:06 -0800315
Jonathan Hart89187372013-03-14 16:41:09 -0700316 String dpidStr = HexString.toHexString(dpid);
Jonathan Hart44e56fc2013-03-14 16:53:59 -0700317
Jonathan Hart0b3eee42013-03-16 18:20:04 -0700318 SwitchLeadershipData swData = switches.get(dpidStr);
319 //LeaderLatch latch = (switches.get(dpidStr) != null)?switches.get(dpidStr).getLatch():null;
Pankaj Berde017960a2013-03-14 20:32:26 -0700320
Jonathan Hart0b3eee42013-03-16 18:20:04 -0700321 if (swData == null){
Jonathan Hartedd6a442013-02-20 15:22:06 -0800322 log.warn("Tried to get controller for non-existent switch");
323 return null;
324 }
325
Jonathan Hart0b3eee42013-03-16 18:20:04 -0700326 LeaderLatch latch = swData.getLatch();
327
Jonathan Hartedd6a442013-02-20 15:22:06 -0800328 Participant leader = null;
329 try {
330 leader = latch.getLeader();
331 } catch (Exception e) {
Jonathan Hart57080fb2013-02-21 10:55:46 -0800332 throw new RegistryException("Error contacting the Zookeeper service", e);
Jonathan Hartedd6a442013-02-20 15:22:06 -0800333 }
334
335 return leader.getId();
336 }
337
338 @Override
339 public Collection<Long> getSwitchesControlledByController(String controllerId) {
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800340 //TODO remove this if not needed
Jonathan Hartbd766972013-02-22 15:13:03 -0800341 throw new RuntimeException("Not yet implemented");
Jonathan Hartedd6a442013-02-20 15:22:06 -0800342 }
Jonathan Hartbd181b62013-02-17 16:05:38 -0800343
Jonathan Hartd82f20d2013-02-21 18:04:24 -0800344
Jonathan Hart89187372013-03-14 16:41:09 -0700345 //TODO what should happen when there's no ZK connection? Currently we just return
346 //the cache but this may lead to false impressions - i.e. we don't actually know
347 //what's in ZK so we shouldn't say we do
Jonathan Hartd82f20d2013-02-21 18:04:24 -0800348 @Override
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800349 public Map<String, List<ControllerRegistryEntry>> getAllSwitches() {
350 Map<String, List<ControllerRegistryEntry>> data =
351 new HashMap<String, List<ControllerRegistryEntry>>();
352
353 for (Map.Entry<String, PathChildrenCache> entry : switchPathCaches.entrySet()){
354 List<ControllerRegistryEntry> contendingControllers =
355 new ArrayList<ControllerRegistryEntry>();
356
357 if (entry.getValue().getCurrentData().size() < 1){
358 log.info("Switch entry with no leader elections: {}", entry.getKey());
359 continue;
360 }
361
362 for (ChildData d : entry.getValue().getCurrentData()) {
Jonathan Hart97801ac2013-02-26 14:29:16 -0800363
Jonathan Hartd10008d2013-02-23 17:04:08 -0800364 String controllerId = new String(d.getData(), Charsets.UTF_8);
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800365
366 String[] splitted = d.getPath().split("-");
367 int sequenceNumber = Integer.parseInt(splitted[splitted.length - 1]);
368
369 contendingControllers.add(new ControllerRegistryEntry(controllerId, sequenceNumber));
370 }
371
372 Collections.sort(contendingControllers);
373 data.put(entry.getKey(), contendingControllers);
374 }
375 return data;
Jonathan Hartd82f20d2013-02-21 18:04:24 -0800376 }
377
Jonathan Hartbd181b62013-02-17 16:05:38 -0800378 /*
379 * IFloodlightModule
380 */
381
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -0800382 @Override
383 public Collection<Class<? extends IFloodlightService>> getModuleServices() {
Jonathan Hartedd6a442013-02-20 15:22:06 -0800384 Collection<Class<? extends IFloodlightService>> l =
385 new ArrayList<Class<? extends IFloodlightService>>();
Jonathan Hartd82f20d2013-02-21 18:04:24 -0800386 l.add(IControllerRegistryService.class);
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -0800387 return l;
388 }
389
390 @Override
391 public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {
392 Map<Class<? extends IFloodlightService>, IFloodlightService> m =
393 new HashMap<Class<? extends IFloodlightService>, IFloodlightService>();
Jonathan Hartd82f20d2013-02-21 18:04:24 -0800394 m.put(IControllerRegistryService.class, this);
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -0800395 return m;
396 }
397
398 @Override
399 public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800400 Collection<Class<? extends IFloodlightService>> l =
401 new ArrayList<Class<? extends IFloodlightService>>();
402 l.add(IRestApiService.class);
403 return l;
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -0800404 }
405
Jonathan Hart89187372013-03-14 16:41:09 -0700406 //TODO currently blocks startup when it can't get a Zookeeper connection.
407 //Do we support starting up with no Zookeeper connection?
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -0800408 @Override
409 public void init (FloodlightModuleContext context) throws FloodlightModuleException {
Jonathan Hartbd766972013-02-22 15:13:03 -0800410 log.info("Initialising the Zookeeper Registry - Zookeeper connection required");
411
Jonathan Hart97801ac2013-02-26 14:29:16 -0800412 //Read the Zookeeper connection string from the config
413 Map<String, String> configParams = context.getConfigParams(this);
414 String connectionString = configParams.get("connectionString");
415 if (connectionString != null){
416 this.connectionString = connectionString;
Jonathan Hart57080fb2013-02-21 10:55:46 -0800417 }
Jonathan Hart97801ac2013-02-26 14:29:16 -0800418 log.info("Setting Zookeeper connection string to {}", this.connectionString);
Jonathan Hart57080fb2013-02-21 10:55:46 -0800419
Jonathan Hart97801ac2013-02-26 14:29:16 -0800420 restApi = context.getServiceImpl(IRestApiService.class);
Jonathan Hartbd181b62013-02-17 16:05:38 -0800421
Jonathan Hart89187372013-03-14 16:41:09 -0700422 switches = new ConcurrentHashMap<String, SwitchLeadershipData>();
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800423 switchPathCaches = new HashMap<String, PathChildrenCache>();
Jonathan Hartbd181b62013-02-17 16:05:38 -0800424
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800425 RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
Jonathan Hart97801ac2013-02-26 14:29:16 -0800426 client = CuratorFrameworkFactory.newClient(this.connectionString,
Jonathan Hartcc957a02013-02-26 10:39:04 -0800427 sessionTimeout, connectionTimeout, retryPolicy);
Jonathan Hartbd181b62013-02-17 16:05:38 -0800428
429 client.start();
Jonathan Hartbd181b62013-02-17 16:05:38 -0800430 client = client.usingNamespace(namespace);
Jonathan Hart97801ac2013-02-26 14:29:16 -0800431
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800432
Jonathan Hartedd6a442013-02-20 15:22:06 -0800433 controllerCache = new PathChildrenCache(client, controllerPath, true);
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800434 switchCache = new PathChildrenCache(client, switchLatchesPath, true);
435 switchCache.getListenable().addListener(switchPathCacheListener);
Jonathan Hartedd6a442013-02-20 15:22:06 -0800436
437 try {
438 controllerCache.start(StartMode.BUILD_INITIAL_CACHE);
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800439
440 //Don't prime the cache, we want a notification for each child node in the path
441 switchCache.start(StartMode.NORMAL);
Jonathan Hartedd6a442013-02-20 15:22:06 -0800442 } catch (Exception e) {
Jonathan Hart7bf62172013-02-28 13:17:18 -0800443 throw new FloodlightModuleException("Error initialising ZookeeperRegistry: "
444 + e.getMessage());
Jonathan Hartedd6a442013-02-20 15:22:06 -0800445 }
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -0800446 }
447
448 @Override
449 public void startUp (FloodlightModuleContext context) {
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800450 restApi.addRestletRoutable(new RegistryWebRoutable());
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -0800451 }
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -0800452}