blob: ef45c645e78520084c4586d71b82380ab5ae9228 [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;
Pavlin Radoslavov52163ed2014-03-19 11:39:34 -070011import java.util.Random;
Jonathan Hart116b1fe2014-03-14 18:53:47 -070012import java.util.concurrent.BlockingQueue;
Jonathan Hart89187372013-03-14 16:41:09 -070013import java.util.concurrent.ConcurrentHashMap;
Jonathan Hart116b1fe2014-03-14 18:53:47 -070014import java.util.concurrent.ExecutorService;
15import java.util.concurrent.Executors;
16import java.util.concurrent.LinkedBlockingQueue;
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -080017
Pavlin Radoslavovc35229e2014-02-06 16:19:37 -080018import net.floodlightcontroller.core.IFloodlightProviderService;
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -080019import net.floodlightcontroller.core.module.FloodlightModuleContext;
20import net.floodlightcontroller.core.module.FloodlightModuleException;
21import net.floodlightcontroller.core.module.IFloodlightModule;
22import net.floodlightcontroller.core.module.IFloodlightService;
Jonathan Hart3d7730a2013-02-22 11:51:17 -080023import net.floodlightcontroller.restserver.IRestApiService;
Naoki Shiotab32edf52013-12-12 14:09:36 -080024import net.onrc.onos.registry.controller.web.RegistryWebRoutable;
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -080025
Jonathan Hart116b1fe2014-03-14 18:53:47 -070026import org.apache.curator.RetryPolicy;
27import org.apache.curator.framework.CuratorFramework;
28import org.apache.curator.framework.CuratorFrameworkFactory;
29import org.apache.curator.framework.recipes.atomic.AtomicValue;
30import org.apache.curator.framework.recipes.atomic.DistributedAtomicLong;
31import org.apache.curator.framework.recipes.cache.ChildData;
32import org.apache.curator.framework.recipes.cache.PathChildrenCache;
33import org.apache.curator.framework.recipes.cache.PathChildrenCache.StartMode;
34import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent;
35import org.apache.curator.framework.recipes.cache.PathChildrenCacheListener;
36import org.apache.curator.framework.recipes.leader.LeaderLatch;
37import org.apache.curator.framework.recipes.leader.LeaderLatchListener;
38import org.apache.curator.framework.recipes.leader.Participant;
39import org.apache.curator.retry.ExponentialBackoffRetry;
40import org.apache.curator.retry.RetryOneTime;
41import org.apache.curator.x.discovery.ServiceCache;
42import org.apache.curator.x.discovery.ServiceDiscovery;
43import org.apache.curator.x.discovery.ServiceDiscoveryBuilder;
44import org.apache.curator.x.discovery.ServiceInstance;
Jonathan Hartbd181b62013-02-17 16:05:38 -080045import org.openflow.util.HexString;
46import org.slf4j.Logger;
47import org.slf4j.LoggerFactory;
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -080048
Jonathan Hartd10008d2013-02-23 17:04:08 -080049import com.google.common.base.Charsets;
Jonathan Hartbd181b62013-02-17 16:05:38 -080050
Jonathan Hart7bf62172013-02-28 13:17:18 -080051/**
52 * A registry service that uses Zookeeper. All data is stored in Zookeeper,
53 * so this can be used as a global registry in a multi-node ONOS cluster.
54 * @author jono
55 *
56 */
Jonathan Hartbd766972013-02-22 15:13:03 -080057public class ZookeeperRegistry implements IFloodlightModule, IControllerRegistryService {
Jonathan Hartc6eee9e2013-02-18 14:58:27 -080058
Yuta HIGUCHI6ac8d182013-10-22 15:24:56 -070059 protected final static Logger log = LoggerFactory.getLogger(ZookeeperRegistry.class);
Jonathan Hartbd766972013-02-22 15:13:03 -080060 protected String controllerId = null;
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -080061
Jonathan Hart3d7730a2013-02-22 11:51:17 -080062 protected IRestApiService restApi;
63
Jonathan Hart7bf62172013-02-28 13:17:18 -080064 //This is the default, it's overwritten by the connectionString configuration parameter
Jonathan Hartbd181b62013-02-17 16:05:38 -080065 protected String connectionString = "localhost:2181";
Jonathan Hart3d7730a2013-02-22 11:51:17 -080066
Jonathan Hartbd181b62013-02-17 16:05:38 -080067 private final String namespace = "onos";
Jonathan Hartedd6a442013-02-20 15:22:06 -080068 private final String switchLatchesPath = "/switches";
Pavlin Radoslavovf1377ce2014-02-05 17:37:24 -080069 private final String CLUSTER_LEADER_PATH = "/cluster/leader";
Jonathan Hart71c0ffc2013-03-24 15:58:42 -070070
71 private final String SERVICES_PATH = "/"; //i.e. the root of our namespace
72 private final String CONTROLLER_SERVICE_NAME = "controllers";
Jonathan Hartbd181b62013-02-17 16:05:38 -080073
74 protected CuratorFramework client;
Jonathan Hartedd6a442013-02-20 15:22:06 -080075
Jonathan Hart3d7730a2013-02-22 11:51:17 -080076 protected PathChildrenCache switchCache;
Jonathan Hartbd181b62013-02-17 16:05:38 -080077
Jonathan Hart89187372013-03-14 16:41:09 -070078 protected ConcurrentHashMap<String, SwitchLeadershipData> switches;
Jonathan Hart3d7730a2013-02-22 11:51:17 -080079 protected Map<String, PathChildrenCache> switchPathCaches;
Pavlin Radoslavovf1377ce2014-02-05 17:37:24 -080080
81 protected LeaderLatch clusterLeaderLatch;
82 protected ClusterLeaderListener clusterLeaderListener;
83 private static final long CLUSTER_LEADER_ELECTION_RETRY_MS = 100;
84
Jonathan Hart1530ccc2013-04-03 19:36:02 -070085 private final String ID_COUNTER_PATH = "/flowidcounter";
86 private final Long ID_BLOCK_SIZE = 0x100000000L;
87 protected DistributedAtomicLong distributedIdCounter;
88
Jonathan Hart97801ac2013-02-26 14:29:16 -080089 //Zookeeper performance-related configuration
Jonathan Hart0b3eee42013-03-16 18:20:04 -070090 protected static final int sessionTimeout = 5000;
91 protected static final int connectionTimeout = 7000;
Pavlin Radoslavov52163ed2014-03-19 11:39:34 -070092
93 //
94 // Unique ID generation state
95 // TODO: The implementation must be updated to use the Zookeeper
96 // instead of a ramdon generator.
97 //
98 private static Random randomGenerator = new Random();
99 private static int nextUniqueIdPrefix = 0;
100 private static int nextUniqueIdSuffix = 0;
101
Jonathan Hart116b1fe2014-03-14 18:53:47 -0700102 private final BlockingQueue<SwitchLeaderEvent> switchLeadershipEvents =
103 new LinkedBlockingQueue<SwitchLeaderEvent>();
104
105 private ExecutorService eventThreadExecutorService;
106
107 private static class SwitchLeaderEvent {
108 public final long dpid;
109 public final boolean isLeader;
110
111 public SwitchLeaderEvent(long dpid, boolean isLeader) {
112 this.dpid = dpid;
113 this.isLeader = isLeader;
114 }
115 }
116
117 /*
118 * Dispatcher thread for leadership change events coming from Curator.
119 */
120 private void dispatchEvents() {
121 while (!Thread.currentThread().isInterrupted()) {
122 try {
123 SwitchLeaderEvent event = switchLeadershipEvents.take();
124 SwitchLeadershipData swData = switches.get(HexString.toHexString(event.dpid));
125 if (swData == null) {
126 log.debug("Leadership data {} not found", event.dpid);
127 continue;
128 }
129
130 swData.getCallback().controlChanged(event.dpid, event.isLeader);
131 } catch (InterruptedException e) {
132 Thread.currentThread().interrupt();
133 break;
134 } catch (Exception e) {
135 log.error("Exception in registry event thread", e);
136 }
137 }
138 }
Jonathan Hartbd181b62013-02-17 16:05:38 -0800139
Jonathan Hart89187372013-03-14 16:41:09 -0700140 protected class SwitchLeaderListener implements LeaderLatchListener{
Jonathan Hart0de09492013-03-13 14:37:21 -0700141 String dpid;
142 LeaderLatch latch;
143
Jonathan Hart89187372013-03-14 16:41:09 -0700144 public SwitchLeaderListener(String dpid, LeaderLatch latch){
Jonathan Hart0de09492013-03-13 14:37:21 -0700145 this.dpid = dpid;
146 this.latch = latch;
147 }
Jonathan Hart116b1fe2014-03-14 18:53:47 -0700148
Jonathan Hart0de09492013-03-13 14:37:21 -0700149 @Override
Jonathan Hart116b1fe2014-03-14 18:53:47 -0700150 public void isLeader() {
151 log.debug("Became leader for {}", dpid);
152
153 switchLeadershipEvents.offer(new SwitchLeaderEvent(HexString.toLong(dpid), true));
154 }
155
156 @Override
157 public void notLeader() {
158 log.debug("Lost leadership for {}", dpid);
Jonathan Hart0de09492013-03-13 14:37:21 -0700159
Jonathan Hart116b1fe2014-03-14 18:53:47 -0700160 switchLeadershipEvents.offer(new SwitchLeaderEvent(HexString.toLong(dpid), false));
Jonathan Hart0de09492013-03-13 14:37:21 -0700161 }
162 }
163
Naoki Shiotad00accf2013-06-25 14:40:37 -0700164 protected class SwitchPathCacheListener implements PathChildrenCacheListener {
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800165 @Override
166 public void childEvent(CuratorFramework client,
167 PathChildrenCacheEvent event) throws Exception {
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800168
169 String strSwitch = null;
170 if (event.getData() != null){
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800171 String[] splitted = event.getData().getPath().split("/");
172 strSwitch = splitted[splitted.length - 1];
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800173 }
174
175 switch (event.getType()){
176 case CHILD_ADDED:
177 case CHILD_UPDATED:
178 //Check we have a PathChildrenCache for this child, add one if not
Jonathan Hart4baf3be2013-03-21 18:26:13 -0700179 synchronized (switchPathCaches){
180 if (switchPathCaches.get(strSwitch) == null){
181 PathChildrenCache pc = new PathChildrenCache(client,
182 event.getData().getPath(), true);
183 pc.start(StartMode.NORMAL);
184 switchPathCaches.put(strSwitch, pc);
185 }
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800186 }
187 break;
188 case CHILD_REMOVED:
189 //Remove our PathChildrenCache for this child
Jonathan Hart4baf3be2013-03-21 18:26:13 -0700190 PathChildrenCache pc = null;
191 synchronized(switchPathCaches){
192 pc = switchPathCaches.remove(strSwitch);
193 }
194 if (pc != null){
195 pc.close();
196 }
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800197 break;
198 default:
Jonathan Hart116b1fe2014-03-14 18:53:47 -0700199 //All other switchLeadershipEvents are connection status switchLeadershipEvents. We don't need to
Jonathan Hart4baf3be2013-03-21 18:26:13 -0700200 //do anything as the path cache handles these on its own.
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800201 break;
202 }
203
204 }
205 };
Pavlin Radoslavovf1377ce2014-02-05 17:37:24 -0800206
207 protected class ClusterLeaderListener implements LeaderLatchListener {
208 LeaderLatch latch;
209
210 public ClusterLeaderListener(LeaderLatch latch) {
211 this.latch = latch;
212 }
213
Jonathan Hart116b1fe2014-03-14 18:53:47 -0700214 //
215 // NOTE: If we need to support callbacks when the
216 // leadership changes, those should be called here.
217 //
218
Pavlin Radoslavovf1377ce2014-02-05 17:37:24 -0800219 @Override
Jonathan Hart116b1fe2014-03-14 18:53:47 -0700220 public void isLeader() {
221 log.debug("Cluster leadership aquired");
222 }
223
224 @Override
225 public void notLeader() {
226 log.debug("Cluster leadership lost");
Pavlin Radoslavovf1377ce2014-02-05 17:37:24 -0800227 }
228 }
229
Naoki Shiotad00accf2013-06-25 14:40:37 -0700230 /**
231 * Listens for changes to the switch znodes in Zookeeper. This maintains
232 * the second level of PathChildrenCaches that hold the controllers
233 * contending for each switch - there's one for each switch.
234 */
235 PathChildrenCacheListener switchPathCacheListener = new SwitchPathCacheListener();
Jonathan Hart71c0ffc2013-03-24 15:58:42 -0700236 protected ServiceDiscovery<ControllerService> serviceDiscovery;
237 protected ServiceCache<ControllerService> serviceCache;
Jonathan Hartedd6a442013-02-20 15:22:06 -0800238
Jonathan Hartbd181b62013-02-17 16:05:38 -0800239
240 @Override
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800241 public void requestControl(long dpid, ControlChangeCallback cb) throws RegistryException {
Jonathan Hart7bf62172013-02-28 13:17:18 -0800242 log.info("Requesting control for {}", HexString.toHexString(dpid));
Jonathan Hartc6eee9e2013-02-18 14:58:27 -0800243
Jonathan Hartbd766972013-02-22 15:13:03 -0800244 if (controllerId == null){
245 throw new RuntimeException("Must register a controller before calling requestControl");
Jonathan Hartbd181b62013-02-17 16:05:38 -0800246 }
247
248 String dpidStr = HexString.toHexString(dpid);
249 String latchPath = switchLatchesPath + "/" + dpidStr;
250
Jonathan Hart89187372013-03-14 16:41:09 -0700251 if (switches.get(dpidStr) != null){
Jonathan Hart3c0eccd2013-03-12 22:32:50 -0700252 log.debug("Already contesting {}, returning", HexString.toHexString(dpid));
Pankaj Berdeda7187b2013-03-18 15:24:59 -0700253 throw new RegistryException("Already contesting control for " + dpidStr);
Jonathan Hartc6eee9e2013-02-18 14:58:27 -0800254 }
255
Jonathan Hartbd766972013-02-22 15:13:03 -0800256 LeaderLatch latch = new LeaderLatch(client, latchPath, controllerId);
Jonathan Hart116b1fe2014-03-14 18:53:47 -0700257 SwitchLeaderListener listener = new SwitchLeaderListener(dpidStr, latch);
258 latch.addListener(listener);
Jonathan Hartbd181b62013-02-17 16:05:38 -0800259
Jonathan Hart44e56fc2013-03-14 16:53:59 -0700260
Jonathan Hart116b1fe2014-03-14 18:53:47 -0700261 SwitchLeadershipData swData = new SwitchLeadershipData(latch, cb, listener);
Jonathan Hart89187372013-03-14 16:41:09 -0700262 SwitchLeadershipData oldData = switches.putIfAbsent(dpidStr, swData);
263
264 if (oldData != null){
265 //There was already data for that key in the map
266 //i.e. someone else got here first so we can't succeed
267 log.debug("Already requested control for {}", dpidStr);
268 throw new RegistryException("Already requested control for " + dpidStr);
269 }
270
271 //Now that we know we were able to add our latch to the collection,
Jonathan Hart44e56fc2013-03-14 16:53:59 -0700272 //we can start the leader election in Zookeeper. However I don't know
273 //how to handle if the start fails - the latch is already in our
274 //switches list.
275 //TODO seems like there's a Curator bug when latch.start is called when
276 //there's no Zookeeper connection which causes two znodes to be put in
277 //Zookeeper at the latch path when we reconnect to Zookeeper.
Jonathan Hartbd181b62013-02-17 16:05:38 -0800278 try {
Jonathan Hartbd181b62013-02-17 16:05:38 -0800279 latch.start();
280 } catch (Exception e) {
Jonathan Hartc6eee9e2013-02-18 14:58:27 -0800281 log.warn("Error starting leader latch: {}", e.getMessage());
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800282 throw new RegistryException("Error starting leader latch for " + dpidStr, e);
Jonathan Hartbd181b62013-02-17 16:05:38 -0800283 }
284
285 }
286
287 @Override
Jonathan Hartd82f20d2013-02-21 18:04:24 -0800288 public void releaseControl(long dpid) {
Jonathan Hart7bf62172013-02-28 13:17:18 -0800289 log.info("Releasing control for {}", HexString.toHexString(dpid));
Jonathan Hart57080fb2013-02-21 10:55:46 -0800290
Jonathan Hartc6eee9e2013-02-18 14:58:27 -0800291 String dpidStr = HexString.toHexString(dpid);
292
Jonathan Hart89187372013-03-14 16:41:09 -0700293 SwitchLeadershipData swData = switches.remove(dpidStr);
294
295 if (swData == null) {
Jonathan Hart7bf62172013-02-28 13:17:18 -0800296 log.debug("Trying to release control of a switch we are not contesting");
Jonathan Hartbd181b62013-02-17 16:05:38 -0800297 return;
298 }
Jonathan Hart89187372013-03-14 16:41:09 -0700299
Jonathan Hart89187372013-03-14 16:41:09 -0700300 LeaderLatch latch = swData.getLatch();
Jonathan Hartbd181b62013-02-17 16:05:38 -0800301
Jonathan Hart116b1fe2014-03-14 18:53:47 -0700302 latch.removeListener(swData.getListener());
Jonathan Hart4baf3be2013-03-21 18:26:13 -0700303
Jonathan Hartbd181b62013-02-17 16:05:38 -0800304 try {
305 latch.close();
306 } catch (IOException e) {
Jonathan Hart7bf62172013-02-28 13:17:18 -0800307 //I think it's OK not to do anything here. Either the node got
308 //deleted correctly, or the connection went down and the node got deleted.
Umesh Krishnaswamy0ef75ee2013-03-25 17:50:27 -0700309 log.debug("releaseControl: caught IOException {}", dpidStr);
Jonathan Hartbd181b62013-02-17 16:05:38 -0800310 }
311 }
312
313 @Override
Jonathan Hartd82f20d2013-02-21 18:04:24 -0800314 public boolean hasControl(long dpid) {
Jonathan Hart89187372013-03-14 16:41:09 -0700315 String dpidStr = HexString.toHexString(dpid);
Jonathan Hart57080fb2013-02-21 10:55:46 -0800316
Jonathan Hart89187372013-03-14 16:41:09 -0700317 SwitchLeadershipData swData = switches.get(dpidStr);
Jonathan Hartbd181b62013-02-17 16:05:38 -0800318
Jonathan Hart89187372013-03-14 16:41:09 -0700319 if (swData == null) {
320 log.warn("No leader latch for dpid {}", dpidStr);
Jonathan Hartbd181b62013-02-17 16:05:38 -0800321 return false;
322 }
323
Jonathan Hart89187372013-03-14 16:41:09 -0700324 return swData.getLatch().hasLeadership();
Jonathan Hartbd181b62013-02-17 16:05:38 -0800325 }
326
327 @Override
Pavlin Radoslavovf1377ce2014-02-05 17:37:24 -0800328 public boolean isClusterLeader() {
329 return clusterLeaderLatch.hasLeadership();
330 }
331
332 @Override
Jonathan Hart7bf62172013-02-28 13:17:18 -0800333 public String getControllerId() {
Jonathan Hartbd766972013-02-22 15:13:03 -0800334 return controllerId;
Jonathan Hartbd181b62013-02-17 16:05:38 -0800335 }
336
Jonathan Hartedd6a442013-02-20 15:22:06 -0800337 @Override
Jonathan Hart57080fb2013-02-21 10:55:46 -0800338 public Collection<String> getAllControllers() throws RegistryException {
Jonathan Hartedd6a442013-02-20 15:22:06 -0800339 log.debug("Getting all controllers");
Jonathan Hart1be46262013-02-20 16:43:51 -0800340
Jonathan Hartedd6a442013-02-20 15:22:06 -0800341 List<String> controllers = new ArrayList<String>();
Jonathan Hart71c0ffc2013-03-24 15:58:42 -0700342 for (ServiceInstance<ControllerService> instance : serviceCache.getInstances()){
343 String id = instance.getPayload().getControllerId();
344 if (!controllers.contains(id)){
345 controllers.add(id);
Jonathan Hartedd6a442013-02-20 15:22:06 -0800346 }
Jonathan Hartedd6a442013-02-20 15:22:06 -0800347 }
Jonathan Hart71c0ffc2013-03-24 15:58:42 -0700348
Jonathan Hartedd6a442013-02-20 15:22:06 -0800349 return controllers;
350 }
351
352 @Override
Jonathan Hart57080fb2013-02-21 10:55:46 -0800353 public void registerController(String id) throws RegistryException {
Jonathan Hartd10008d2013-02-23 17:04:08 -0800354 if (controllerId != null) {
355 throw new RegistryException(
356 "Controller already registered with id " + controllerId);
357 }
Jonathan Hartbd766972013-02-22 15:13:03 -0800358
359 controllerId = id;
Jonathan Hart57080fb2013-02-21 10:55:46 -0800360
Jonathan Hartedd6a442013-02-20 15:22:06 -0800361 try {
Jonathan Hart71c0ffc2013-03-24 15:58:42 -0700362 ServiceInstance<ControllerService> thisInstance = ServiceInstance.<ControllerService>builder()
363 .name(CONTROLLER_SERVICE_NAME)
364 .payload(new ControllerService(controllerId))
365 //.port((int)(65535 * Math.random())) // in a real application, you'd use a common port
366 //.uriSpec(uriSpec)
367 .build();
Jonathan Hart0b3eee42013-03-16 18:20:04 -0700368
Jonathan Hart71c0ffc2013-03-24 15:58:42 -0700369 serviceDiscovery.registerService(thisInstance);
Jonathan Hartedd6a442013-02-20 15:22:06 -0800370 } catch (Exception e) {
Jonathan Hart71c0ffc2013-03-24 15:58:42 -0700371 // TODO Auto-generated catch block
372 e.printStackTrace();
Jonathan Hartedd6a442013-02-20 15:22:06 -0800373 }
Jonathan Hart71c0ffc2013-03-24 15:58:42 -0700374
Jonathan Hartedd6a442013-02-20 15:22:06 -0800375 }
376
377 @Override
Jonathan Hart57080fb2013-02-21 10:55:46 -0800378 public String getControllerForSwitch(long dpid) throws RegistryException {
Jonathan Hart89187372013-03-14 16:41:09 -0700379 String dpidStr = HexString.toHexString(dpid);
Pankaj Berde017960a2013-03-14 20:32:26 -0700380
Jonathan Hart599c6b32013-03-24 22:42:02 -0700381 PathChildrenCache switchCache = switchPathCaches.get(dpidStr);
382
383 if (switchCache == null){
Jonathan Hartedd6a442013-02-20 15:22:06 -0800384 log.warn("Tried to get controller for non-existent switch");
385 return null;
386 }
387
Jonathan Hartf4e80842013-03-26 23:55:02 -0700388 try {
389 //We've seen issues with these caches get stuck out of date, so we'll have to
390 //force them to refresh before each read. This slows down the method as it
391 //blocks on a Zookeeper query, however at the moment only the cleanup thread
392 //uses this and that isn't particularly time-sensitive.
393 switchCache.rebuild();
394 } catch (Exception e) {
395 // TODO Auto-generated catch block
396 e.printStackTrace();
397 }
398
Jonathan Hart599c6b32013-03-24 22:42:02 -0700399 List<ChildData> sortedData = new ArrayList<ChildData>(switchCache.getCurrentData());
Jonathan Hart0b3eee42013-03-16 18:20:04 -0700400
Jonathan Hart599c6b32013-03-24 22:42:02 -0700401 Collections.sort(
402 sortedData,
403 new Comparator<ChildData>(){
404 private String getSequenceNumber(String path){
405 return path.substring(path.lastIndexOf('-') + 1);
406 }
407 @Override
408 public int compare(ChildData lhs, ChildData rhs) {
409 return getSequenceNumber(lhs.getPath()).
410 compareTo(getSequenceNumber(rhs.getPath()));
411 }
412 }
413 );
Jonathan Hartedd6a442013-02-20 15:22:06 -0800414
Jonathan Hart56b296e2013-03-25 13:30:10 -0700415 if (sortedData.size() == 0){
416 return null;
417 }
418
Jonathan Hart599c6b32013-03-24 22:42:02 -0700419 return new String(sortedData.get(0).getData(), Charsets.UTF_8);
Jonathan Hartedd6a442013-02-20 15:22:06 -0800420 }
421
422 @Override
423 public Collection<Long> getSwitchesControlledByController(String controllerId) {
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800424 //TODO remove this if not needed
Jonathan Hartbd766972013-02-22 15:13:03 -0800425 throw new RuntimeException("Not yet implemented");
Jonathan Hartedd6a442013-02-20 15:22:06 -0800426 }
Jonathan Hartbd181b62013-02-17 16:05:38 -0800427
Jonathan Hartd82f20d2013-02-21 18:04:24 -0800428
Jonathan Hart89187372013-03-14 16:41:09 -0700429 //TODO what should happen when there's no ZK connection? Currently we just return
430 //the cache but this may lead to false impressions - i.e. we don't actually know
431 //what's in ZK so we shouldn't say we do
Jonathan Hartd82f20d2013-02-21 18:04:24 -0800432 @Override
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800433 public Map<String, List<ControllerRegistryEntry>> getAllSwitches() {
434 Map<String, List<ControllerRegistryEntry>> data =
435 new HashMap<String, List<ControllerRegistryEntry>>();
436
437 for (Map.Entry<String, PathChildrenCache> entry : switchPathCaches.entrySet()){
438 List<ControllerRegistryEntry> contendingControllers =
439 new ArrayList<ControllerRegistryEntry>();
440
441 if (entry.getValue().getCurrentData().size() < 1){
Jonathan Hartcbb4b952013-03-18 16:15:18 -0700442 //TODO prevent even having the PathChildrenCache in this case
443 //log.info("Switch entry with no leader elections: {}", entry.getKey());
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800444 continue;
445 }
446
447 for (ChildData d : entry.getValue().getCurrentData()) {
Jonathan Hart97801ac2013-02-26 14:29:16 -0800448
Jonathan Hartd10008d2013-02-23 17:04:08 -0800449 String controllerId = new String(d.getData(), Charsets.UTF_8);
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800450
451 String[] splitted = d.getPath().split("-");
452 int sequenceNumber = Integer.parseInt(splitted[splitted.length - 1]);
453
454 contendingControllers.add(new ControllerRegistryEntry(controllerId, sequenceNumber));
455 }
456
457 Collections.sort(contendingControllers);
458 data.put(entry.getKey(), contendingControllers);
459 }
460 return data;
Jonathan Hartd82f20d2013-02-21 18:04:24 -0800461 }
462
Nick Karanatsios8abe7172014-02-19 20:31:48 -0800463 public IdBlock allocateUniqueIdBlock(long range) {
464 try {
465 AtomicValue<Long> result = null;
466 do {
467 result = distributedIdCounter.add(range);
468 } while (result == null || !result.succeeded());
469
470 return new IdBlock(result.preValue(), result.postValue() - 1, range);
471 } catch (Exception e) {
472 log.error("Error allocating ID block");
473 }
474 return null;
475 }
476
Naoki Shiotaa3b2dfa2013-06-27 13:52:24 -0700477 /**
478 * Returns a block of IDs which are unique and unused.
479 * Range of IDs is fixed size and is assigned incrementally as this method called.
480 * Since the range of IDs is managed by Zookeeper in distributed way, this method may block when
481 * requests come up simultaneously.
482 */
Nick Karanatsios8abe7172014-02-19 20:31:48 -0800483 @Override
Jonathan Hart1530ccc2013-04-03 19:36:02 -0700484 public IdBlock allocateUniqueIdBlock(){
Nick Karanatsios8abe7172014-02-19 20:31:48 -0800485 return allocateUniqueIdBlock(ID_BLOCK_SIZE);
Jonathan Hart1530ccc2013-04-03 19:36:02 -0700486 }
Pavlin Radoslavov52163ed2014-03-19 11:39:34 -0700487
488 /**
489 * Get a globally unique ID.
490 *
491 * @return a globally unique ID.
492 */
493 @Override
494 public synchronized long getNextUniqueId() {
495 //
496 // Generate the next Unique ID.
497 //
498 // TODO: For now, the higher 32 bits are random, and
499 // the lower 32 bits are sequential.
500 // The implementation must be updated to use the Zookeeper
501 // to allocate the higher 32 bits (globally unique).
502 //
503 if ((nextUniqueIdSuffix & 0xffffffffL) == 0xffffffffL) {
504 nextUniqueIdPrefix = randomGenerator.nextInt();
505 nextUniqueIdSuffix = 0;
506 } else {
507 nextUniqueIdSuffix++;
508 }
509 long result = (long)nextUniqueIdPrefix << 32;
510 result = result | (0xffffffffL & nextUniqueIdSuffix);
511 return result;
512 }
513
Jonathan Hartbd181b62013-02-17 16:05:38 -0800514 /*
515 * IFloodlightModule
516 */
517
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -0800518 @Override
519 public Collection<Class<? extends IFloodlightService>> getModuleServices() {
Jonathan Hartedd6a442013-02-20 15:22:06 -0800520 Collection<Class<? extends IFloodlightService>> l =
521 new ArrayList<Class<? extends IFloodlightService>>();
Jonathan Hartd82f20d2013-02-21 18:04:24 -0800522 l.add(IControllerRegistryService.class);
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -0800523 return l;
524 }
525
526 @Override
527 public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {
528 Map<Class<? extends IFloodlightService>, IFloodlightService> m =
529 new HashMap<Class<? extends IFloodlightService>, IFloodlightService>();
Jonathan Hartd82f20d2013-02-21 18:04:24 -0800530 m.put(IControllerRegistryService.class, this);
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -0800531 return m;
532 }
533
534 @Override
535 public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800536 Collection<Class<? extends IFloodlightService>> l =
537 new ArrayList<Class<? extends IFloodlightService>>();
Pavlin Radoslavovc35229e2014-02-06 16:19:37 -0800538 l.add(IFloodlightProviderService.class);
539 l.add(IRestApiService.class);
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800540 return l;
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -0800541 }
542
Jonathan Hart89187372013-03-14 16:41:09 -0700543 //TODO currently blocks startup when it can't get a Zookeeper connection.
544 //Do we support starting up with no Zookeeper connection?
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -0800545 @Override
546 public void init (FloodlightModuleContext context) throws FloodlightModuleException {
Jonathan Hartbd766972013-02-22 15:13:03 -0800547 log.info("Initialising the Zookeeper Registry - Zookeeper connection required");
548
Jonathan Hart97801ac2013-02-26 14:29:16 -0800549 //Read the Zookeeper connection string from the config
550 Map<String, String> configParams = context.getConfigParams(this);
551 String connectionString = configParams.get("connectionString");
552 if (connectionString != null){
553 this.connectionString = connectionString;
Jonathan Hart57080fb2013-02-21 10:55:46 -0800554 }
Jonathan Hart97801ac2013-02-26 14:29:16 -0800555 log.info("Setting Zookeeper connection string to {}", this.connectionString);
Pavlin Radoslavov52163ed2014-03-19 11:39:34 -0700556
557 //
558 // Initialize the Unique ID generator
559 // TODO: This must be replaced by Zookeeper-based allocation
560 //
561 nextUniqueIdPrefix = randomGenerator.nextInt();
562
Jonathan Hart97801ac2013-02-26 14:29:16 -0800563 restApi = context.getServiceImpl(IRestApiService.class);
Jonathan Hartbd181b62013-02-17 16:05:38 -0800564
Jonathan Hart89187372013-03-14 16:41:09 -0700565 switches = new ConcurrentHashMap<String, SwitchLeadershipData>();
Jonathan Hart4baf3be2013-03-21 18:26:13 -0700566 //switchPathCaches = new HashMap<String, PathChildrenCache>();
567 switchPathCaches = new ConcurrentHashMap<String, PathChildrenCache>();
Jonathan Hartbd181b62013-02-17 16:05:38 -0800568
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800569 RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
Jonathan Hart97801ac2013-02-26 14:29:16 -0800570 client = CuratorFrameworkFactory.newClient(this.connectionString,
Jonathan Hartcc957a02013-02-26 10:39:04 -0800571 sessionTimeout, connectionTimeout, retryPolicy);
Jonathan Hartbd181b62013-02-17 16:05:38 -0800572
573 client.start();
Jonathan Hartbd181b62013-02-17 16:05:38 -0800574 client = client.usingNamespace(namespace);
Jonathan Hart97801ac2013-02-26 14:29:16 -0800575
Jonathan Hart1530ccc2013-04-03 19:36:02 -0700576 distributedIdCounter = new DistributedAtomicLong(
577 client,
578 ID_COUNTER_PATH,
579 new RetryOneTime(100));
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800580
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800581 switchCache = new PathChildrenCache(client, switchLatchesPath, true);
582 switchCache.getListenable().addListener(switchPathCacheListener);
Jonathan Hartedd6a442013-02-20 15:22:06 -0800583
Jonathan Hart71c0ffc2013-03-24 15:58:42 -0700584 //Build the service discovery object
585 serviceDiscovery = ServiceDiscoveryBuilder.builder(ControllerService.class)
586 .client(client).basePath(SERVICES_PATH).build();
587
588 //We read the list of services very frequently (GUI periodically queries them)
589 //so we'll cache them to cut down on Zookeeper queries.
590 serviceCache = serviceDiscovery.serviceCacheBuilder()
591 .name(CONTROLLER_SERVICE_NAME).build();
592
593
Jonathan Hartedd6a442013-02-20 15:22:06 -0800594 try {
Jonathan Hart71c0ffc2013-03-24 15:58:42 -0700595 serviceDiscovery.start();
596 serviceCache.start();
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800597
598 //Don't prime the cache, we want a notification for each child node in the path
599 switchCache.start(StartMode.NORMAL);
Jonathan Hartedd6a442013-02-20 15:22:06 -0800600 } catch (Exception e) {
Jonathan Hart7bf62172013-02-28 13:17:18 -0800601 throw new FloodlightModuleException("Error initialising ZookeeperRegistry: "
602 + e.getMessage());
Jonathan Hartedd6a442013-02-20 15:22:06 -0800603 }
Jonathan Hart116b1fe2014-03-14 18:53:47 -0700604
605 eventThreadExecutorService = Executors.newSingleThreadExecutor();
606 eventThreadExecutorService.execute(
607 new Runnable() {
608 @Override
609 public void run(){
610 dispatchEvents();
611 }
612 });
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -0800613 }
Jonathan Hart116b1fe2014-03-14 18:53:47 -0700614
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -0800615 @Override
616 public void startUp (FloodlightModuleContext context) {
Pavlin Radoslavovf1377ce2014-02-05 17:37:24 -0800617 //
618 // Cluster Leader election setup.
619 // NOTE: We have to do it here, because during the init stage
620 // we don't know the Controller ID.
621 //
622 if (controllerId == null) {
623 log.error("Error on startup: unknown ControllerId");
624 }
625 clusterLeaderLatch = new LeaderLatch(client,
626 CLUSTER_LEADER_PATH,
627 controllerId);
628 clusterLeaderListener = new ClusterLeaderListener(clusterLeaderLatch);
629 clusterLeaderLatch.addListener(clusterLeaderListener);
630 try {
631 clusterLeaderLatch.start();
632 } catch (Exception e) {
633 log.error("Error on startup starting the cluster leader election: {}", e.getMessage());
634 }
635
636 // Keep trying until there is a cluster leader
637 do {
638 try {
639 Participant leader = clusterLeaderLatch.getLeader();
640 if (! leader.getId().isEmpty())
641 break;
642 Thread.sleep(CLUSTER_LEADER_ELECTION_RETRY_MS);
643 } catch (Exception e) {
644 log.error("Error on startup waiting for cluster leader election: {}", e.getMessage());
645 }
646 } while (true);
647
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800648 restApi.addRestletRoutable(new RegistryWebRoutable());
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -0800649 }
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -0800650}