blob: 76b7ebdbc5e0517ec9da03a4fc6d1c41d5bd9efc [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;
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -08004import java.util.ArrayList;
5import java.util.Collection;
Jonathan Hart3d7730a2013-02-22 11:51:17 -08006import java.util.Collections;
Jonathan Hart599c6b32013-03-24 22:42:02 -07007import java.util.Comparator;
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 Hartbd181b62013-02-17 16:05:38 -080019import org.openflow.util.HexString;
20import org.slf4j.Logger;
21import org.slf4j.LoggerFactory;
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -080022
Jonathan Hartd10008d2013-02-23 17:04:08 -080023import com.google.common.base.Charsets;
Jonathan Hartbd181b62013-02-17 16:05:38 -080024import com.netflix.curator.RetryPolicy;
25import com.netflix.curator.framework.CuratorFramework;
26import com.netflix.curator.framework.CuratorFrameworkFactory;
Jonathan Hartedd6a442013-02-20 15:22:06 -080027import com.netflix.curator.framework.recipes.cache.ChildData;
28import com.netflix.curator.framework.recipes.cache.PathChildrenCache;
29import com.netflix.curator.framework.recipes.cache.PathChildrenCache.StartMode;
Jonathan Hart3d7730a2013-02-22 11:51:17 -080030import com.netflix.curator.framework.recipes.cache.PathChildrenCacheEvent;
31import com.netflix.curator.framework.recipes.cache.PathChildrenCacheListener;
Jonathan Hartbd181b62013-02-17 16:05:38 -080032import com.netflix.curator.framework.recipes.leader.LeaderLatch;
Jonathan Hart0de09492013-03-13 14:37:21 -070033import com.netflix.curator.framework.recipes.leader.LeaderLatchEvent;
34import com.netflix.curator.framework.recipes.leader.LeaderLatchListener;
Jonathan Hart3d7730a2013-02-22 11:51:17 -080035import com.netflix.curator.retry.ExponentialBackoffRetry;
Jonathan Hart71c0ffc2013-03-24 15:58:42 -070036import com.netflix.curator.x.discovery.ServiceCache;
37import com.netflix.curator.x.discovery.ServiceDiscovery;
38import com.netflix.curator.x.discovery.ServiceDiscoveryBuilder;
39import com.netflix.curator.x.discovery.ServiceInstance;
Jonathan Hartbd181b62013-02-17 16:05:38 -080040
Jonathan Hart7bf62172013-02-28 13:17:18 -080041/**
42 * A registry service that uses Zookeeper. All data is stored in Zookeeper,
43 * so this can be used as a global registry in a multi-node ONOS cluster.
44 * @author jono
45 *
46 */
Jonathan Hartbd766972013-02-22 15:13:03 -080047public class ZookeeperRegistry implements IFloodlightModule, IControllerRegistryService {
Jonathan Hartc6eee9e2013-02-18 14:58:27 -080048
Jonathan Hartbd766972013-02-22 15:13:03 -080049 protected static Logger log = LoggerFactory.getLogger(ZookeeperRegistry.class);
50 protected String controllerId = null;
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -080051
Jonathan Hart3d7730a2013-02-22 11:51:17 -080052 protected IRestApiService restApi;
53
Jonathan Hart7bf62172013-02-28 13:17:18 -080054 //This is the default, it's overwritten by the connectionString configuration parameter
Jonathan Hartbd181b62013-02-17 16:05:38 -080055 protected String connectionString = "localhost:2181";
Jonathan Hart3d7730a2013-02-22 11:51:17 -080056
Jonathan Hartbd181b62013-02-17 16:05:38 -080057 private final String namespace = "onos";
Jonathan Hartedd6a442013-02-20 15:22:06 -080058 private final String switchLatchesPath = "/switches";
Jonathan Hart71c0ffc2013-03-24 15:58:42 -070059
60 private final String SERVICES_PATH = "/"; //i.e. the root of our namespace
61 private final String CONTROLLER_SERVICE_NAME = "controllers";
Jonathan Hartbd181b62013-02-17 16:05:38 -080062
63 protected CuratorFramework client;
Jonathan Hartedd6a442013-02-20 15:22:06 -080064
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 {
Jonathan Hart4baf3be2013-03-21 18:26:13 -070099 log.debug("Latch for {} has changed: old latch {} - new latch {}",
100 new Object[]{dpid, latch, swData.getLatch()});
Jonathan Hart89187372013-03-14 16:41:09 -0700101 }
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 Hartcbb4b952013-03-18 16:15:18 -0700115 //log.debug("Root switch path cache got {} event", event.getType());
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800116
117 String strSwitch = null;
118 if (event.getData() != null){
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800119 String[] splitted = event.getData().getPath().split("/");
120 strSwitch = splitted[splitted.length - 1];
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800121 }
122
123 switch (event.getType()){
124 case CHILD_ADDED:
125 case CHILD_UPDATED:
126 //Check we have a PathChildrenCache for this child, add one if not
Jonathan Hart4baf3be2013-03-21 18:26:13 -0700127 synchronized (switchPathCaches){
128 if (switchPathCaches.get(strSwitch) == null){
129 PathChildrenCache pc = new PathChildrenCache(client,
130 event.getData().getPath(), true);
131 pc.start(StartMode.NORMAL);
132 switchPathCaches.put(strSwitch, pc);
133 }
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800134 }
135 break;
136 case CHILD_REMOVED:
137 //Remove our PathChildrenCache for this child
Jonathan Hart4baf3be2013-03-21 18:26:13 -0700138 PathChildrenCache pc = null;
139 synchronized(switchPathCaches){
140 pc = switchPathCaches.remove(strSwitch);
141 }
142 if (pc != null){
143 pc.close();
144 }
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800145 break;
146 default:
Jonathan Hart4baf3be2013-03-21 18:26:13 -0700147 //All other events are connection status events. We don't need to
148 //do anything as the path cache handles these on its own.
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800149 break;
150 }
151
152 }
153 };
Jonathan Hart71c0ffc2013-03-24 15:58:42 -0700154 protected ServiceDiscovery<ControllerService> serviceDiscovery;
155 protected ServiceCache<ControllerService> serviceCache;
Jonathan Hartedd6a442013-02-20 15:22:06 -0800156
Jonathan Hartbd181b62013-02-17 16:05:38 -0800157
158 @Override
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800159 public void requestControl(long dpid, ControlChangeCallback cb) throws RegistryException {
Jonathan Hart7bf62172013-02-28 13:17:18 -0800160 log.info("Requesting control for {}", HexString.toHexString(dpid));
Jonathan Hartc6eee9e2013-02-18 14:58:27 -0800161
Jonathan Hartbd766972013-02-22 15:13:03 -0800162 if (controllerId == null){
163 throw new RuntimeException("Must register a controller before calling requestControl");
Jonathan Hartbd181b62013-02-17 16:05:38 -0800164 }
165
166 String dpidStr = HexString.toHexString(dpid);
167 String latchPath = switchLatchesPath + "/" + dpidStr;
168
Jonathan Hart89187372013-03-14 16:41:09 -0700169 if (switches.get(dpidStr) != null){
Jonathan Hart3c0eccd2013-03-12 22:32:50 -0700170 log.debug("Already contesting {}, returning", HexString.toHexString(dpid));
Pankaj Berdeda7187b2013-03-18 15:24:59 -0700171 throw new RegistryException("Already contesting control for " + dpidStr);
Jonathan Hartc6eee9e2013-02-18 14:58:27 -0800172 }
173
Jonathan Hartbd766972013-02-22 15:13:03 -0800174 LeaderLatch latch = new LeaderLatch(client, latchPath, controllerId);
Jonathan Hart89187372013-03-14 16:41:09 -0700175 latch.addListener(new SwitchLeaderListener(dpidStr, latch));
Jonathan Hartbd181b62013-02-17 16:05:38 -0800176
Jonathan Hart44e56fc2013-03-14 16:53:59 -0700177
Jonathan Hart89187372013-03-14 16:41:09 -0700178 SwitchLeadershipData swData = new SwitchLeadershipData(latch, cb);
179 SwitchLeadershipData oldData = switches.putIfAbsent(dpidStr, swData);
180
181 if (oldData != null){
182 //There was already data for that key in the map
183 //i.e. someone else got here first so we can't succeed
184 log.debug("Already requested control for {}", dpidStr);
185 throw new RegistryException("Already requested control for " + dpidStr);
186 }
187
188 //Now that we know we were able to add our latch to the collection,
Jonathan Hart44e56fc2013-03-14 16:53:59 -0700189 //we can start the leader election in Zookeeper. However I don't know
190 //how to handle if the start fails - the latch is already in our
191 //switches list.
192 //TODO seems like there's a Curator bug when latch.start is called when
193 //there's no Zookeeper connection which causes two znodes to be put in
194 //Zookeeper at the latch path when we reconnect to Zookeeper.
Jonathan Hartbd181b62013-02-17 16:05:38 -0800195 try {
Jonathan Hartbd181b62013-02-17 16:05:38 -0800196 latch.start();
197 } catch (Exception e) {
Jonathan Hartc6eee9e2013-02-18 14:58:27 -0800198 log.warn("Error starting leader latch: {}", e.getMessage());
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800199 throw new RegistryException("Error starting leader latch for " + dpidStr, e);
Jonathan Hartbd181b62013-02-17 16:05:38 -0800200 }
201
202 }
203
204 @Override
Jonathan Hartd82f20d2013-02-21 18:04:24 -0800205 public void releaseControl(long dpid) {
Jonathan Hart7bf62172013-02-28 13:17:18 -0800206 log.info("Releasing control for {}", HexString.toHexString(dpid));
Jonathan Hart57080fb2013-02-21 10:55:46 -0800207
Jonathan Hartc6eee9e2013-02-18 14:58:27 -0800208 String dpidStr = HexString.toHexString(dpid);
209
Jonathan Hart89187372013-03-14 16:41:09 -0700210 SwitchLeadershipData swData = switches.remove(dpidStr);
211
212 if (swData == null) {
Jonathan Hart7bf62172013-02-28 13:17:18 -0800213 log.debug("Trying to release control of a switch we are not contesting");
Jonathan Hartbd181b62013-02-17 16:05:38 -0800214 return;
215 }
Jonathan Hart89187372013-03-14 16:41:09 -0700216
Jonathan Hart89187372013-03-14 16:41:09 -0700217 LeaderLatch latch = swData.getLatch();
Jonathan Hartbd181b62013-02-17 16:05:38 -0800218
Jonathan Hart4baf3be2013-03-21 18:26:13 -0700219 latch.removeAllListeners();
220
Jonathan Hartbd181b62013-02-17 16:05:38 -0800221 try {
222 latch.close();
223 } catch (IOException e) {
Jonathan Hart7bf62172013-02-28 13:17:18 -0800224 //I think it's OK not to do anything here. Either the node got
225 //deleted correctly, or the connection went down and the node got deleted.
Umesh Krishnaswamy0ef75ee2013-03-25 17:50:27 -0700226 log.debug("releaseControl: caught IOException {}", dpidStr);
Jonathan Hartbd181b62013-02-17 16:05:38 -0800227 }
228 }
229
230 @Override
Jonathan Hartd82f20d2013-02-21 18:04:24 -0800231 public boolean hasControl(long dpid) {
Jonathan Hart89187372013-03-14 16:41:09 -0700232 String dpidStr = HexString.toHexString(dpid);
Jonathan Hart57080fb2013-02-21 10:55:46 -0800233
Jonathan Hart89187372013-03-14 16:41:09 -0700234 SwitchLeadershipData swData = switches.get(dpidStr);
Jonathan Hartbd181b62013-02-17 16:05:38 -0800235
Jonathan Hart89187372013-03-14 16:41:09 -0700236 if (swData == null) {
237 log.warn("No leader latch for dpid {}", dpidStr);
Jonathan Hartbd181b62013-02-17 16:05:38 -0800238 return false;
239 }
240
Jonathan Hart89187372013-03-14 16:41:09 -0700241 return swData.getLatch().hasLeadership();
Jonathan Hartbd181b62013-02-17 16:05:38 -0800242 }
243
244 @Override
Jonathan Hart7bf62172013-02-28 13:17:18 -0800245 public String getControllerId() {
Jonathan Hartbd766972013-02-22 15:13:03 -0800246 return controllerId;
Jonathan Hartbd181b62013-02-17 16:05:38 -0800247 }
248
Jonathan Hartedd6a442013-02-20 15:22:06 -0800249 @Override
Jonathan Hart57080fb2013-02-21 10:55:46 -0800250 public Collection<String> getAllControllers() throws RegistryException {
Jonathan Hartedd6a442013-02-20 15:22:06 -0800251 log.debug("Getting all controllers");
Jonathan Hart1be46262013-02-20 16:43:51 -0800252
Jonathan Hartedd6a442013-02-20 15:22:06 -0800253 List<String> controllers = new ArrayList<String>();
Jonathan Hart71c0ffc2013-03-24 15:58:42 -0700254 for (ServiceInstance<ControllerService> instance : serviceCache.getInstances()){
255 String id = instance.getPayload().getControllerId();
256 if (!controllers.contains(id)){
257 controllers.add(id);
Jonathan Hartedd6a442013-02-20 15:22:06 -0800258 }
Jonathan Hartedd6a442013-02-20 15:22:06 -0800259 }
Jonathan Hart71c0ffc2013-03-24 15:58:42 -0700260
Jonathan Hartedd6a442013-02-20 15:22:06 -0800261 return controllers;
262 }
263
264 @Override
Jonathan Hart57080fb2013-02-21 10:55:46 -0800265 public void registerController(String id) throws RegistryException {
Jonathan Hartd10008d2013-02-23 17:04:08 -0800266 if (controllerId != null) {
267 throw new RegistryException(
268 "Controller already registered with id " + controllerId);
269 }
Jonathan Hartbd766972013-02-22 15:13:03 -0800270
271 controllerId = id;
Jonathan Hart57080fb2013-02-21 10:55:46 -0800272
Jonathan Hartedd6a442013-02-20 15:22:06 -0800273 try {
Jonathan Hart71c0ffc2013-03-24 15:58:42 -0700274 ServiceInstance<ControllerService> thisInstance = ServiceInstance.<ControllerService>builder()
275 .name(CONTROLLER_SERVICE_NAME)
276 .payload(new ControllerService(controllerId))
277 //.port((int)(65535 * Math.random())) // in a real application, you'd use a common port
278 //.uriSpec(uriSpec)
279 .build();
Jonathan Hart0b3eee42013-03-16 18:20:04 -0700280
Jonathan Hart71c0ffc2013-03-24 15:58:42 -0700281 serviceDiscovery.registerService(thisInstance);
Jonathan Hartedd6a442013-02-20 15:22:06 -0800282 } catch (Exception e) {
Jonathan Hart71c0ffc2013-03-24 15:58:42 -0700283 // TODO Auto-generated catch block
284 e.printStackTrace();
Jonathan Hartedd6a442013-02-20 15:22:06 -0800285 }
Jonathan Hart71c0ffc2013-03-24 15:58:42 -0700286
Jonathan Hartedd6a442013-02-20 15:22:06 -0800287 }
288
289 @Override
Jonathan Hart57080fb2013-02-21 10:55:46 -0800290 public String getControllerForSwitch(long dpid) throws RegistryException {
Jonathan Hart89187372013-03-14 16:41:09 -0700291 String dpidStr = HexString.toHexString(dpid);
Pankaj Berde017960a2013-03-14 20:32:26 -0700292
Jonathan Hart599c6b32013-03-24 22:42:02 -0700293 PathChildrenCache switchCache = switchPathCaches.get(dpidStr);
294
295 if (switchCache == null){
Jonathan Hartedd6a442013-02-20 15:22:06 -0800296 log.warn("Tried to get controller for non-existent switch");
297 return null;
298 }
299
Jonathan Hartf4e80842013-03-26 23:55:02 -0700300 try {
301 //We've seen issues with these caches get stuck out of date, so we'll have to
302 //force them to refresh before each read. This slows down the method as it
303 //blocks on a Zookeeper query, however at the moment only the cleanup thread
304 //uses this and that isn't particularly time-sensitive.
305 switchCache.rebuild();
306 } catch (Exception e) {
307 // TODO Auto-generated catch block
308 e.printStackTrace();
309 }
310
Jonathan Hart599c6b32013-03-24 22:42:02 -0700311 List<ChildData> sortedData = new ArrayList<ChildData>(switchCache.getCurrentData());
Jonathan Hart0b3eee42013-03-16 18:20:04 -0700312
Jonathan Hart599c6b32013-03-24 22:42:02 -0700313 Collections.sort(
314 sortedData,
315 new Comparator<ChildData>(){
316 private String getSequenceNumber(String path){
317 return path.substring(path.lastIndexOf('-') + 1);
318 }
319 @Override
320 public int compare(ChildData lhs, ChildData rhs) {
321 return getSequenceNumber(lhs.getPath()).
322 compareTo(getSequenceNumber(rhs.getPath()));
323 }
324 }
325 );
Jonathan Hartedd6a442013-02-20 15:22:06 -0800326
Jonathan Hart56b296e2013-03-25 13:30:10 -0700327 if (sortedData.size() == 0){
328 return null;
329 }
330
Jonathan Hart599c6b32013-03-24 22:42:02 -0700331 return new String(sortedData.get(0).getData(), Charsets.UTF_8);
Jonathan Hartedd6a442013-02-20 15:22:06 -0800332 }
333
334 @Override
335 public Collection<Long> getSwitchesControlledByController(String controllerId) {
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800336 //TODO remove this if not needed
Jonathan Hartbd766972013-02-22 15:13:03 -0800337 throw new RuntimeException("Not yet implemented");
Jonathan Hartedd6a442013-02-20 15:22:06 -0800338 }
Jonathan Hartbd181b62013-02-17 16:05:38 -0800339
Jonathan Hartd82f20d2013-02-21 18:04:24 -0800340
Jonathan Hart89187372013-03-14 16:41:09 -0700341 //TODO what should happen when there's no ZK connection? Currently we just return
342 //the cache but this may lead to false impressions - i.e. we don't actually know
343 //what's in ZK so we shouldn't say we do
Jonathan Hartd82f20d2013-02-21 18:04:24 -0800344 @Override
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800345 public Map<String, List<ControllerRegistryEntry>> getAllSwitches() {
346 Map<String, List<ControllerRegistryEntry>> data =
347 new HashMap<String, List<ControllerRegistryEntry>>();
348
349 for (Map.Entry<String, PathChildrenCache> entry : switchPathCaches.entrySet()){
350 List<ControllerRegistryEntry> contendingControllers =
351 new ArrayList<ControllerRegistryEntry>();
352
353 if (entry.getValue().getCurrentData().size() < 1){
Jonathan Hartcbb4b952013-03-18 16:15:18 -0700354 //TODO prevent even having the PathChildrenCache in this case
355 //log.info("Switch entry with no leader elections: {}", entry.getKey());
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800356 continue;
357 }
358
359 for (ChildData d : entry.getValue().getCurrentData()) {
Jonathan Hart97801ac2013-02-26 14:29:16 -0800360
Jonathan Hartd10008d2013-02-23 17:04:08 -0800361 String controllerId = new String(d.getData(), Charsets.UTF_8);
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800362
363 String[] splitted = d.getPath().split("-");
364 int sequenceNumber = Integer.parseInt(splitted[splitted.length - 1]);
365
366 contendingControllers.add(new ControllerRegistryEntry(controllerId, sequenceNumber));
367 }
368
369 Collections.sort(contendingControllers);
370 data.put(entry.getKey(), contendingControllers);
371 }
372 return data;
Jonathan Hartd82f20d2013-02-21 18:04:24 -0800373 }
374
Jonathan Hartbd181b62013-02-17 16:05:38 -0800375 /*
376 * IFloodlightModule
377 */
378
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -0800379 @Override
380 public Collection<Class<? extends IFloodlightService>> getModuleServices() {
Jonathan Hartedd6a442013-02-20 15:22:06 -0800381 Collection<Class<? extends IFloodlightService>> l =
382 new ArrayList<Class<? extends IFloodlightService>>();
Jonathan Hartd82f20d2013-02-21 18:04:24 -0800383 l.add(IControllerRegistryService.class);
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -0800384 return l;
385 }
386
387 @Override
388 public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {
389 Map<Class<? extends IFloodlightService>, IFloodlightService> m =
390 new HashMap<Class<? extends IFloodlightService>, IFloodlightService>();
Jonathan Hartd82f20d2013-02-21 18:04:24 -0800391 m.put(IControllerRegistryService.class, this);
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -0800392 return m;
393 }
394
395 @Override
396 public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800397 Collection<Class<? extends IFloodlightService>> l =
398 new ArrayList<Class<? extends IFloodlightService>>();
399 l.add(IRestApiService.class);
400 return l;
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -0800401 }
402
Jonathan Hart89187372013-03-14 16:41:09 -0700403 //TODO currently blocks startup when it can't get a Zookeeper connection.
404 //Do we support starting up with no Zookeeper connection?
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -0800405 @Override
406 public void init (FloodlightModuleContext context) throws FloodlightModuleException {
Jonathan Hartbd766972013-02-22 15:13:03 -0800407 log.info("Initialising the Zookeeper Registry - Zookeeper connection required");
408
Jonathan Hart97801ac2013-02-26 14:29:16 -0800409 //Read the Zookeeper connection string from the config
410 Map<String, String> configParams = context.getConfigParams(this);
411 String connectionString = configParams.get("connectionString");
412 if (connectionString != null){
413 this.connectionString = connectionString;
Jonathan Hart57080fb2013-02-21 10:55:46 -0800414 }
Jonathan Hart97801ac2013-02-26 14:29:16 -0800415 log.info("Setting Zookeeper connection string to {}", this.connectionString);
Jonathan Hart57080fb2013-02-21 10:55:46 -0800416
Jonathan Hart97801ac2013-02-26 14:29:16 -0800417 restApi = context.getServiceImpl(IRestApiService.class);
Jonathan Hartbd181b62013-02-17 16:05:38 -0800418
Jonathan Hart89187372013-03-14 16:41:09 -0700419 switches = new ConcurrentHashMap<String, SwitchLeadershipData>();
Jonathan Hart4baf3be2013-03-21 18:26:13 -0700420 //switchPathCaches = new HashMap<String, PathChildrenCache>();
421 switchPathCaches = new ConcurrentHashMap<String, PathChildrenCache>();
Jonathan Hartbd181b62013-02-17 16:05:38 -0800422
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800423 RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
Jonathan Hart97801ac2013-02-26 14:29:16 -0800424 client = CuratorFrameworkFactory.newClient(this.connectionString,
Jonathan Hartcc957a02013-02-26 10:39:04 -0800425 sessionTimeout, connectionTimeout, retryPolicy);
Jonathan Hartbd181b62013-02-17 16:05:38 -0800426
427 client.start();
Jonathan Hartbd181b62013-02-17 16:05:38 -0800428 client = client.usingNamespace(namespace);
Jonathan Hart97801ac2013-02-26 14:29:16 -0800429
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800430
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800431 switchCache = new PathChildrenCache(client, switchLatchesPath, true);
432 switchCache.getListenable().addListener(switchPathCacheListener);
Jonathan Hartedd6a442013-02-20 15:22:06 -0800433
Jonathan Hart71c0ffc2013-03-24 15:58:42 -0700434 //Build the service discovery object
435 serviceDiscovery = ServiceDiscoveryBuilder.builder(ControllerService.class)
436 .client(client).basePath(SERVICES_PATH).build();
437
438 //We read the list of services very frequently (GUI periodically queries them)
439 //so we'll cache them to cut down on Zookeeper queries.
440 serviceCache = serviceDiscovery.serviceCacheBuilder()
441 .name(CONTROLLER_SERVICE_NAME).build();
442
443
Jonathan Hartedd6a442013-02-20 15:22:06 -0800444 try {
Jonathan Hart71c0ffc2013-03-24 15:58:42 -0700445 serviceDiscovery.start();
446 serviceCache.start();
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800447
448 //Don't prime the cache, we want a notification for each child node in the path
449 switchCache.start(StartMode.NORMAL);
Jonathan Hartedd6a442013-02-20 15:22:06 -0800450 } catch (Exception e) {
Jonathan Hart7bf62172013-02-28 13:17:18 -0800451 throw new FloodlightModuleException("Error initialising ZookeeperRegistry: "
452 + e.getMessage());
Jonathan Hartedd6a442013-02-20 15:22:06 -0800453 }
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -0800454 }
455
456 @Override
457 public void startUp (FloodlightModuleContext context) {
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800458 restApi.addRestletRoutable(new RegistryWebRoutable());
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -0800459 }
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -0800460}