blob: 494f20db22d5367ddb1aef1e9396988ead446084 [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;
22import org.apache.zookeeper.data.Stat;
Jonathan Hartbd181b62013-02-17 16:05:38 -080023import org.openflow.util.HexString;
24import org.slf4j.Logger;
25import org.slf4j.LoggerFactory;
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -080026
Jonathan Hartd10008d2013-02-23 17:04:08 -080027import com.google.common.base.Charsets;
Jonathan Hartbd181b62013-02-17 16:05:38 -080028import com.netflix.curator.RetryPolicy;
29import com.netflix.curator.framework.CuratorFramework;
30import com.netflix.curator.framework.CuratorFrameworkFactory;
Jonathan Hartedd6a442013-02-20 15:22:06 -080031import com.netflix.curator.framework.recipes.cache.ChildData;
32import com.netflix.curator.framework.recipes.cache.PathChildrenCache;
33import com.netflix.curator.framework.recipes.cache.PathChildrenCache.StartMode;
Jonathan Hart3d7730a2013-02-22 11:51:17 -080034import com.netflix.curator.framework.recipes.cache.PathChildrenCacheEvent;
35import com.netflix.curator.framework.recipes.cache.PathChildrenCacheListener;
Jonathan Hartbd181b62013-02-17 16:05:38 -080036import com.netflix.curator.framework.recipes.leader.LeaderLatch;
Jonathan Hart0de09492013-03-13 14:37:21 -070037import com.netflix.curator.framework.recipes.leader.LeaderLatchEvent;
38import com.netflix.curator.framework.recipes.leader.LeaderLatchListener;
Jonathan Hartbd181b62013-02-17 16:05:38 -080039import com.netflix.curator.framework.recipes.leader.Participant;
Jonathan Hart3d7730a2013-02-22 11:51:17 -080040import com.netflix.curator.retry.ExponentialBackoffRetry;
Jonathan Hart0b3eee42013-03-16 18:20:04 -070041import com.netflix.curator.utils.ZKPaths;
Jonathan Hartbd181b62013-02-17 16:05:38 -080042
Jonathan Hart7bf62172013-02-28 13:17:18 -080043/**
44 * A registry service that uses Zookeeper. All data is stored in Zookeeper,
45 * so this can be used as a global registry in a multi-node ONOS cluster.
46 * @author jono
47 *
48 */
Jonathan Hartbd766972013-02-22 15:13:03 -080049public class ZookeeperRegistry implements IFloodlightModule, IControllerRegistryService {
Jonathan Hartc6eee9e2013-02-18 14:58:27 -080050
Jonathan Hartbd766972013-02-22 15:13:03 -080051 protected static Logger log = LoggerFactory.getLogger(ZookeeperRegistry.class);
52 protected String controllerId = null;
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -080053
Jonathan Hart3d7730a2013-02-22 11:51:17 -080054 protected IRestApiService restApi;
55
Jonathan Hart7bf62172013-02-28 13:17:18 -080056 //This is the default, it's overwritten by the connectionString configuration parameter
Jonathan Hartbd181b62013-02-17 16:05:38 -080057 protected String connectionString = "localhost:2181";
Jonathan Hart3d7730a2013-02-22 11:51:17 -080058
Jonathan Hartbd181b62013-02-17 16:05:38 -080059 private final String namespace = "onos";
Jonathan Hartedd6a442013-02-20 15:22:06 -080060 private final String switchLatchesPath = "/switches";
Jonathan Hart3d7730a2013-02-22 11:51:17 -080061 private final String controllerPath = "/controllers";
Jonathan Hartbd181b62013-02-17 16:05:38 -080062
63 protected CuratorFramework client;
Jonathan Hartedd6a442013-02-20 15:22:06 -080064
Jonathan Hartedd6a442013-02-20 15:22:06 -080065 protected PathChildrenCache controllerCache;
Jonathan Hart3d7730a2013-02-22 11:51:17 -080066 protected PathChildrenCache switchCache;
Jonathan Hartbd181b62013-02-17 16:05:38 -080067
Jonathan Hart89187372013-03-14 16:41:09 -070068 protected ConcurrentHashMap<String, SwitchLeadershipData> switches;
Jonathan Hart3d7730a2013-02-22 11:51:17 -080069 protected Map<String, PathChildrenCache> switchPathCaches;
Jonathan Hartbd181b62013-02-17 16:05:38 -080070
Jonathan Hart97801ac2013-02-26 14:29:16 -080071 //Zookeeper performance-related configuration
Jonathan Hart0b3eee42013-03-16 18:20:04 -070072 protected static final int sessionTimeout = 5000;
73 protected static final int connectionTimeout = 7000;
Jonathan Hart57080fb2013-02-21 10:55:46 -080074
Jonathan Hartbd181b62013-02-17 16:05:38 -080075
Jonathan Hart89187372013-03-14 16:41:09 -070076 protected class SwitchLeaderListener implements LeaderLatchListener{
Jonathan Hart0de09492013-03-13 14:37:21 -070077 String dpid;
78 LeaderLatch latch;
79
Jonathan Hart89187372013-03-14 16:41:09 -070080 public SwitchLeaderListener(String dpid, LeaderLatch latch){
Jonathan Hart0de09492013-03-13 14:37:21 -070081 this.dpid = dpid;
82 this.latch = latch;
83 }
84
85 @Override
86 public void leaderLatchEvent(CuratorFramework arg0,
87 LeaderLatchEvent arg1) {
Jonathan Hart89187372013-03-14 16:41:09 -070088 log.debug("Leadership changed for {}, now {}",
Jonathan Hart0de09492013-03-13 14:37:21 -070089 dpid, latch.hasLeadership());
90
Jonathan Hart89187372013-03-14 16:41:09 -070091 //Check that the leadership request is still active - the client
92 //may have since released the request or even begun another request
93 //(this is why we use == to check the object instance is the same)
94 SwitchLeadershipData swData = switches.get(dpid);
95 if (swData != null && swData.getLatch() == latch){
96 swData.getCallback().controlChanged(
97 HexString.toLong(dpid), latch.hasLeadership());
98 }
99 else {
100 log.debug("Latch for {} has changed", dpid);
101 }
Jonathan Hart0de09492013-03-13 14:37:21 -0700102 }
103 }
104
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800105
Jonathan Hart7bf62172013-02-28 13:17:18 -0800106 /**
107 * Listens for changes to the switch znodes in Zookeeper. This maintains
108 * the second level of PathChildrenCaches that hold the controllers
109 * contending for each switch - there's one for each switch.
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800110 */
111 PathChildrenCacheListener switchPathCacheListener = new PathChildrenCacheListener() {
112 @Override
113 public void childEvent(CuratorFramework client,
114 PathChildrenCacheEvent event) throws Exception {
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800115 log.debug("Root switch path cache got {} event", event.getType());
116
117 String strSwitch = null;
118 if (event.getData() != null){
119 log.debug("Event path {}", event.getData().getPath());
120 String[] splitted = event.getData().getPath().split("/");
121 strSwitch = splitted[splitted.length - 1];
122 log.debug("Switch name is {}", strSwitch);
123 }
124
125 switch (event.getType()){
126 case CHILD_ADDED:
127 case CHILD_UPDATED:
128 //Check we have a PathChildrenCache for this child, add one if not
129 if (switchPathCaches.get(strSwitch) == null){
130 PathChildrenCache pc = new PathChildrenCache(client,
131 event.getData().getPath(), true);
132 pc.start(StartMode.NORMAL);
133 switchPathCaches.put(strSwitch, pc);
134 }
135 break;
136 case CHILD_REMOVED:
137 //Remove our PathChildrenCache for this child
138 PathChildrenCache pc = switchPathCaches.remove(strSwitch);
139 pc.close();
140 break;
141 default:
142 //All other events are connection status events. We need to do anything
143 //as the path cache handles these on its own.
144 break;
145 }
146
147 }
148 };
Jonathan Hartedd6a442013-02-20 15:22:06 -0800149
Jonathan Hartbd181b62013-02-17 16:05:38 -0800150
151 @Override
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800152 public void requestControl(long dpid, ControlChangeCallback cb) throws RegistryException {
Jonathan Hart7bf62172013-02-28 13:17:18 -0800153 log.info("Requesting control for {}", HexString.toHexString(dpid));
Jonathan Hartc6eee9e2013-02-18 14:58:27 -0800154
Jonathan Hartbd766972013-02-22 15:13:03 -0800155 if (controllerId == null){
156 throw new RuntimeException("Must register a controller before calling requestControl");
Jonathan Hartbd181b62013-02-17 16:05:38 -0800157 }
158
159 String dpidStr = HexString.toHexString(dpid);
160 String latchPath = switchLatchesPath + "/" + dpidStr;
161
Jonathan Hart89187372013-03-14 16:41:09 -0700162 if (switches.get(dpidStr) != null){
Jonathan Hart3c0eccd2013-03-12 22:32:50 -0700163 log.debug("Already contesting {}, returning", HexString.toHexString(dpid));
164 return;
Jonathan Hartc6eee9e2013-02-18 14:58:27 -0800165 }
166
Jonathan Hartbd766972013-02-22 15:13:03 -0800167 LeaderLatch latch = new LeaderLatch(client, latchPath, controllerId);
Jonathan Hart89187372013-03-14 16:41:09 -0700168 latch.addListener(new SwitchLeaderListener(dpidStr, latch));
Jonathan Hartbd181b62013-02-17 16:05:38 -0800169
Jonathan Hart44e56fc2013-03-14 16:53:59 -0700170
Jonathan Hart89187372013-03-14 16:41:09 -0700171 SwitchLeadershipData swData = new SwitchLeadershipData(latch, cb);
172 SwitchLeadershipData oldData = switches.putIfAbsent(dpidStr, swData);
173
174 if (oldData != null){
175 //There was already data for that key in the map
176 //i.e. someone else got here first so we can't succeed
177 log.debug("Already requested control for {}", dpidStr);
178 throw new RegistryException("Already requested control for " + dpidStr);
179 }
180
181 //Now that we know we were able to add our latch to the collection,
Jonathan Hart44e56fc2013-03-14 16:53:59 -0700182 //we can start the leader election in Zookeeper. However I don't know
183 //how to handle if the start fails - the latch is already in our
184 //switches list.
185 //TODO seems like there's a Curator bug when latch.start is called when
186 //there's no Zookeeper connection which causes two znodes to be put in
187 //Zookeeper at the latch path when we reconnect to Zookeeper.
Jonathan Hartbd181b62013-02-17 16:05:38 -0800188 try {
Jonathan Hartbd181b62013-02-17 16:05:38 -0800189 latch.start();
190 } catch (Exception e) {
Jonathan Hartc6eee9e2013-02-18 14:58:27 -0800191 log.warn("Error starting leader latch: {}", e.getMessage());
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800192 throw new RegistryException("Error starting leader latch for " + dpidStr, e);
Jonathan Hartbd181b62013-02-17 16:05:38 -0800193 }
194
195 }
196
197 @Override
Jonathan Hartd82f20d2013-02-21 18:04:24 -0800198 public void releaseControl(long dpid) {
Jonathan Hart7bf62172013-02-28 13:17:18 -0800199 log.info("Releasing control for {}", HexString.toHexString(dpid));
Jonathan Hart57080fb2013-02-21 10:55:46 -0800200
Jonathan Hartc6eee9e2013-02-18 14:58:27 -0800201 String dpidStr = HexString.toHexString(dpid);
202
Jonathan Hart89187372013-03-14 16:41:09 -0700203 SwitchLeadershipData swData = switches.remove(dpidStr);
204
205 if (swData == null) {
Jonathan Hart7bf62172013-02-28 13:17:18 -0800206 log.debug("Trying to release control of a switch we are not contesting");
Jonathan Hartbd181b62013-02-17 16:05:38 -0800207 return;
208 }
Jonathan Hart89187372013-03-14 16:41:09 -0700209
Jonathan Hart89187372013-03-14 16:41:09 -0700210 LeaderLatch latch = swData.getLatch();
Jonathan Hartbd181b62013-02-17 16:05:38 -0800211
212 try {
213 latch.close();
214 } catch (IOException e) {
Jonathan Hart7bf62172013-02-28 13:17:18 -0800215 //I think it's OK not to do anything here. Either the node got
216 //deleted correctly, or the connection went down and the node got deleted.
Jonathan Hartbd181b62013-02-17 16:05:38 -0800217 }
218 }
219
220 @Override
Jonathan Hartd82f20d2013-02-21 18:04:24 -0800221 public boolean hasControl(long dpid) {
Jonathan Hart89187372013-03-14 16:41:09 -0700222 String dpidStr = HexString.toHexString(dpid);
Jonathan Hart57080fb2013-02-21 10:55:46 -0800223
Jonathan Hart89187372013-03-14 16:41:09 -0700224 SwitchLeadershipData swData = switches.get(dpidStr);
Jonathan Hartbd181b62013-02-17 16:05:38 -0800225
Jonathan Hart89187372013-03-14 16:41:09 -0700226 if (swData == null) {
227 log.warn("No leader latch for dpid {}", dpidStr);
Jonathan Hartbd181b62013-02-17 16:05:38 -0800228 return false;
229 }
230
Jonathan Hart89187372013-03-14 16:41:09 -0700231 return swData.getLatch().hasLeadership();
Jonathan Hartbd181b62013-02-17 16:05:38 -0800232 }
233
234 @Override
Jonathan Hart7bf62172013-02-28 13:17:18 -0800235 public String getControllerId() {
Jonathan Hartbd766972013-02-22 15:13:03 -0800236 return controllerId;
Jonathan Hartbd181b62013-02-17 16:05:38 -0800237 }
238
Jonathan Hartedd6a442013-02-20 15:22:06 -0800239 @Override
Jonathan Hart57080fb2013-02-21 10:55:46 -0800240 public Collection<String> getAllControllers() throws RegistryException {
Jonathan Hartedd6a442013-02-20 15:22:06 -0800241 log.debug("Getting all controllers");
Jonathan Hart1be46262013-02-20 16:43:51 -0800242
Jonathan Hartedd6a442013-02-20 15:22:06 -0800243 List<String> controllers = new ArrayList<String>();
244 for (ChildData data : controllerCache.getCurrentData()){
245
246 String d = null;
247 try {
248 d = new String(data.getData(), "UTF-8");
249 } catch (UnsupportedEncodingException e) {
Jonathan Hart57080fb2013-02-21 10:55:46 -0800250 throw new RegistryException("Error encoding string", e);
Jonathan Hartedd6a442013-02-20 15:22:06 -0800251 }
252
253 controllers.add(d);
254 }
255 return controllers;
256 }
257
258 @Override
Jonathan Hart57080fb2013-02-21 10:55:46 -0800259 public void registerController(String id) throws RegistryException {
Jonathan Hartd10008d2013-02-23 17:04:08 -0800260 if (controllerId != null) {
261 throw new RegistryException(
262 "Controller already registered with id " + controllerId);
263 }
Jonathan Hartbd766972013-02-22 15:13:03 -0800264
265 controllerId = id;
Jonathan Hart57080fb2013-02-21 10:55:46 -0800266
Jonathan Hartd10008d2013-02-23 17:04:08 -0800267 byte bytes[] = id.getBytes(Charsets.UTF_8);
Jonathan Hart0b3eee42013-03-16 18:20:04 -0700268 String path = ZKPaths.makePath(controllerPath, controllerId);
Jonathan Hartedd6a442013-02-20 15:22:06 -0800269
270 log.info("Registering controller with id {}", id);
271
Jonathan Hartedd6a442013-02-20 15:22:06 -0800272 try {
Jonathan Hart0b3eee42013-03-16 18:20:04 -0700273 //We need to set a watch to recreate the node in the controller
274 //registry if it gets deleted - e.g. on Zookeeper connection loss.
275 Watcher watcher = new Watcher(){
276 @Override
277 public void process(WatchedEvent event) {
278 log.debug("got any watch event {} ", event);
279
280 String path = ZKPaths.makePath(controllerPath, controllerId);
281 byte bytes[] = controllerId.getBytes(Charsets.UTF_8);
282
283 try {
284 if (event.getType() == Event.EventType.NodeDeleted){
285 log.debug("got a node deleted event");
286
287
288 client.create().withMode(CreateMode.EPHEMERAL)
289 .forPath(path, bytes);
290 }
291 } catch (Exception e) {
292 log.warn("Error recreating controller node for {}: {}",
293 controllerId, e.getMessage());
294 } finally {
295 try {
296 client.checkExists().usingWatcher(this).forPath(path);
297 } catch (Exception e2){
298 log.warn("Error resetting watch for {}: {}",
299 controllerId, e2.getMessage());
300 }
301 }
302 }
303 };
304
305 //Create ephemeral node in controller registry
306 //TODO Use protection
307 client.create().withMode(CreateMode.EPHEMERAL)
Jonathan Hartedd6a442013-02-20 15:22:06 -0800308 .forPath(path, bytes);
Jonathan Hart0b3eee42013-03-16 18:20:04 -0700309 client.checkExists().usingWatcher(watcher).forPath(path);
Jonathan Hartedd6a442013-02-20 15:22:06 -0800310 } catch (Exception e) {
Jonathan Hart57080fb2013-02-21 10:55:46 -0800311 throw new RegistryException("Error contacting the Zookeeper service", e);
Jonathan Hartedd6a442013-02-20 15:22:06 -0800312 }
313 }
314
315 @Override
Jonathan Hart57080fb2013-02-21 10:55:46 -0800316 public String getControllerForSwitch(long dpid) throws RegistryException {
Jonathan Hart89187372013-03-14 16:41:09 -0700317 // TODO work out synchronization
Jonathan Hartedd6a442013-02-20 15:22:06 -0800318
Jonathan Hart89187372013-03-14 16:41:09 -0700319 String dpidStr = HexString.toHexString(dpid);
Jonathan Hart44e56fc2013-03-14 16:53:59 -0700320
Jonathan Hart0b3eee42013-03-16 18:20:04 -0700321 SwitchLeadershipData swData = switches.get(dpidStr);
322 //LeaderLatch latch = (switches.get(dpidStr) != null)?switches.get(dpidStr).getLatch():null;
Pankaj Berde017960a2013-03-14 20:32:26 -0700323
Jonathan Hart0b3eee42013-03-16 18:20:04 -0700324 if (swData == null){
Jonathan Hartedd6a442013-02-20 15:22:06 -0800325 log.warn("Tried to get controller for non-existent switch");
326 return null;
327 }
328
Jonathan Hart0b3eee42013-03-16 18:20:04 -0700329 LeaderLatch latch = swData.getLatch();
330
Jonathan Hartedd6a442013-02-20 15:22:06 -0800331 Participant leader = null;
332 try {
333 leader = latch.getLeader();
334 } catch (Exception e) {
Jonathan Hart57080fb2013-02-21 10:55:46 -0800335 throw new RegistryException("Error contacting the Zookeeper service", e);
Jonathan Hartedd6a442013-02-20 15:22:06 -0800336 }
337
338 return leader.getId();
339 }
340
341 @Override
342 public Collection<Long> getSwitchesControlledByController(String controllerId) {
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800343 //TODO remove this if not needed
Jonathan Hartbd766972013-02-22 15:13:03 -0800344 throw new RuntimeException("Not yet implemented");
Jonathan Hartedd6a442013-02-20 15:22:06 -0800345 }
Jonathan Hartbd181b62013-02-17 16:05:38 -0800346
Jonathan Hartd82f20d2013-02-21 18:04:24 -0800347
Jonathan Hart89187372013-03-14 16:41:09 -0700348 //TODO what should happen when there's no ZK connection? Currently we just return
349 //the cache but this may lead to false impressions - i.e. we don't actually know
350 //what's in ZK so we shouldn't say we do
Jonathan Hartd82f20d2013-02-21 18:04:24 -0800351 @Override
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800352 public Map<String, List<ControllerRegistryEntry>> getAllSwitches() {
353 Map<String, List<ControllerRegistryEntry>> data =
354 new HashMap<String, List<ControllerRegistryEntry>>();
355
356 for (Map.Entry<String, PathChildrenCache> entry : switchPathCaches.entrySet()){
357 List<ControllerRegistryEntry> contendingControllers =
358 new ArrayList<ControllerRegistryEntry>();
359
360 if (entry.getValue().getCurrentData().size() < 1){
361 log.info("Switch entry with no leader elections: {}", entry.getKey());
362 continue;
363 }
364
365 for (ChildData d : entry.getValue().getCurrentData()) {
Jonathan Hart97801ac2013-02-26 14:29:16 -0800366
Jonathan Hartd10008d2013-02-23 17:04:08 -0800367 String controllerId = new String(d.getData(), Charsets.UTF_8);
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800368
369 String[] splitted = d.getPath().split("-");
370 int sequenceNumber = Integer.parseInt(splitted[splitted.length - 1]);
371
372 contendingControllers.add(new ControllerRegistryEntry(controllerId, sequenceNumber));
373 }
374
375 Collections.sort(contendingControllers);
376 data.put(entry.getKey(), contendingControllers);
377 }
378 return data;
Jonathan Hartd82f20d2013-02-21 18:04:24 -0800379 }
380
Jonathan Hartbd181b62013-02-17 16:05:38 -0800381 /*
382 * IFloodlightModule
383 */
384
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -0800385 @Override
386 public Collection<Class<? extends IFloodlightService>> getModuleServices() {
Jonathan Hartedd6a442013-02-20 15:22:06 -0800387 Collection<Class<? extends IFloodlightService>> l =
388 new ArrayList<Class<? extends IFloodlightService>>();
Jonathan Hartd82f20d2013-02-21 18:04:24 -0800389 l.add(IControllerRegistryService.class);
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -0800390 return l;
391 }
392
393 @Override
394 public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {
395 Map<Class<? extends IFloodlightService>, IFloodlightService> m =
396 new HashMap<Class<? extends IFloodlightService>, IFloodlightService>();
Jonathan Hartd82f20d2013-02-21 18:04:24 -0800397 m.put(IControllerRegistryService.class, this);
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -0800398 return m;
399 }
400
401 @Override
402 public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800403 Collection<Class<? extends IFloodlightService>> l =
404 new ArrayList<Class<? extends IFloodlightService>>();
405 l.add(IRestApiService.class);
406 return l;
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -0800407 }
408
Jonathan Hart89187372013-03-14 16:41:09 -0700409 //TODO currently blocks startup when it can't get a Zookeeper connection.
410 //Do we support starting up with no Zookeeper connection?
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -0800411 @Override
412 public void init (FloodlightModuleContext context) throws FloodlightModuleException {
Jonathan Hartbd766972013-02-22 15:13:03 -0800413 log.info("Initialising the Zookeeper Registry - Zookeeper connection required");
414
Jonathan Hart97801ac2013-02-26 14:29:16 -0800415 //Read the Zookeeper connection string from the config
416 Map<String, String> configParams = context.getConfigParams(this);
417 String connectionString = configParams.get("connectionString");
418 if (connectionString != null){
419 this.connectionString = connectionString;
Jonathan Hart57080fb2013-02-21 10:55:46 -0800420 }
Jonathan Hart97801ac2013-02-26 14:29:16 -0800421 log.info("Setting Zookeeper connection string to {}", this.connectionString);
Jonathan Hart57080fb2013-02-21 10:55:46 -0800422
Jonathan Hart97801ac2013-02-26 14:29:16 -0800423 restApi = context.getServiceImpl(IRestApiService.class);
Jonathan Hartbd181b62013-02-17 16:05:38 -0800424
Jonathan Hart89187372013-03-14 16:41:09 -0700425 switches = new ConcurrentHashMap<String, SwitchLeadershipData>();
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800426 switchPathCaches = new HashMap<String, PathChildrenCache>();
Jonathan Hartbd181b62013-02-17 16:05:38 -0800427
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800428 RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
Jonathan Hart97801ac2013-02-26 14:29:16 -0800429 client = CuratorFrameworkFactory.newClient(this.connectionString,
Jonathan Hartcc957a02013-02-26 10:39:04 -0800430 sessionTimeout, connectionTimeout, retryPolicy);
Jonathan Hartbd181b62013-02-17 16:05:38 -0800431
432 client.start();
Jonathan Hartbd181b62013-02-17 16:05:38 -0800433 client = client.usingNamespace(namespace);
Jonathan Hart97801ac2013-02-26 14:29:16 -0800434
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800435
Jonathan Hartedd6a442013-02-20 15:22:06 -0800436 controllerCache = new PathChildrenCache(client, controllerPath, true);
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800437 switchCache = new PathChildrenCache(client, switchLatchesPath, true);
438 switchCache.getListenable().addListener(switchPathCacheListener);
Jonathan Hartedd6a442013-02-20 15:22:06 -0800439
440 try {
441 controllerCache.start(StartMode.BUILD_INITIAL_CACHE);
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800442
443 //Don't prime the cache, we want a notification for each child node in the path
444 switchCache.start(StartMode.NORMAL);
Jonathan Hartedd6a442013-02-20 15:22:06 -0800445 } catch (Exception e) {
Jonathan Hart7bf62172013-02-28 13:17:18 -0800446 throw new FloodlightModuleException("Error initialising ZookeeperRegistry: "
447 + e.getMessage());
Jonathan Hartedd6a442013-02-20 15:22:06 -0800448 }
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -0800449 }
450
451 @Override
452 public void startUp (FloodlightModuleContext context) {
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800453 restApi.addRestletRoutable(new RegistryWebRoutable());
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -0800454 }
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -0800455}