blob: 3e4d5bf941dc4c4b6589b1df1da687acc0e21758 [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;
Naoki Shiotab32edf52013-12-12 14:09:36 -080018import net.onrc.onos.registry.controller.web.RegistryWebRoutable;
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -080019
Jonathan Hartbd181b62013-02-17 16:05:38 -080020import org.openflow.util.HexString;
21import org.slf4j.Logger;
22import org.slf4j.LoggerFactory;
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -080023
Jonathan Hartd10008d2013-02-23 17:04:08 -080024import com.google.common.base.Charsets;
Jonathan Hartbd181b62013-02-17 16:05:38 -080025import com.netflix.curator.RetryPolicy;
26import com.netflix.curator.framework.CuratorFramework;
27import com.netflix.curator.framework.CuratorFrameworkFactory;
Jonathan Hart1530ccc2013-04-03 19:36:02 -070028import com.netflix.curator.framework.recipes.atomic.AtomicValue;
29import com.netflix.curator.framework.recipes.atomic.DistributedAtomicLong;
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 Hart3d7730a2013-02-22 11:51:17 -080038import com.netflix.curator.retry.ExponentialBackoffRetry;
Jonathan Hart1530ccc2013-04-03 19:36:02 -070039import com.netflix.curator.retry.RetryOneTime;
Jonathan Hart71c0ffc2013-03-24 15:58:42 -070040import com.netflix.curator.x.discovery.ServiceCache;
41import com.netflix.curator.x.discovery.ServiceDiscovery;
42import com.netflix.curator.x.discovery.ServiceDiscoveryBuilder;
43import com.netflix.curator.x.discovery.ServiceInstance;
Jonathan Hartbd181b62013-02-17 16:05:38 -080044
Jonathan Hart7bf62172013-02-28 13:17:18 -080045/**
46 * A registry service that uses Zookeeper. All data is stored in Zookeeper,
47 * so this can be used as a global registry in a multi-node ONOS cluster.
48 * @author jono
49 *
50 */
Jonathan Hartbd766972013-02-22 15:13:03 -080051public class ZookeeperRegistry implements IFloodlightModule, IControllerRegistryService {
Jonathan Hartc6eee9e2013-02-18 14:58:27 -080052
Yuta HIGUCHI6ac8d182013-10-22 15:24:56 -070053 protected final static Logger log = LoggerFactory.getLogger(ZookeeperRegistry.class);
Jonathan Hartbd766972013-02-22 15:13:03 -080054 protected String controllerId = null;
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -080055
Jonathan Hart3d7730a2013-02-22 11:51:17 -080056 protected IRestApiService restApi;
57
Jonathan Hart7bf62172013-02-28 13:17:18 -080058 //This is the default, it's overwritten by the connectionString configuration parameter
Jonathan Hartbd181b62013-02-17 16:05:38 -080059 protected String connectionString = "localhost:2181";
Jonathan Hart3d7730a2013-02-22 11:51:17 -080060
Jonathan Hartbd181b62013-02-17 16:05:38 -080061 private final String namespace = "onos";
Jonathan Hartedd6a442013-02-20 15:22:06 -080062 private final String switchLatchesPath = "/switches";
Jonathan Hart71c0ffc2013-03-24 15:58:42 -070063
64 private final String SERVICES_PATH = "/"; //i.e. the root of our namespace
65 private final String CONTROLLER_SERVICE_NAME = "controllers";
Jonathan Hartbd181b62013-02-17 16:05:38 -080066
67 protected CuratorFramework client;
Jonathan Hartedd6a442013-02-20 15:22:06 -080068
Jonathan Hart3d7730a2013-02-22 11:51:17 -080069 protected PathChildrenCache switchCache;
Jonathan Hartbd181b62013-02-17 16:05:38 -080070
Jonathan Hart89187372013-03-14 16:41:09 -070071 protected ConcurrentHashMap<String, SwitchLeadershipData> switches;
Jonathan Hart3d7730a2013-02-22 11:51:17 -080072 protected Map<String, PathChildrenCache> switchPathCaches;
Jonathan Hartbd181b62013-02-17 16:05:38 -080073
Jonathan Hart1530ccc2013-04-03 19:36:02 -070074 private final String ID_COUNTER_PATH = "/flowidcounter";
75 private final Long ID_BLOCK_SIZE = 0x100000000L;
76 protected DistributedAtomicLong distributedIdCounter;
77
Jonathan Hart97801ac2013-02-26 14:29:16 -080078 //Zookeeper performance-related configuration
Jonathan Hart0b3eee42013-03-16 18:20:04 -070079 protected static final int sessionTimeout = 5000;
80 protected static final int connectionTimeout = 7000;
Jonathan Hart57080fb2013-02-21 10:55:46 -080081
Jonathan Hartbd181b62013-02-17 16:05:38 -080082
Jonathan Hart89187372013-03-14 16:41:09 -070083 protected class SwitchLeaderListener implements LeaderLatchListener{
Jonathan Hart0de09492013-03-13 14:37:21 -070084 String dpid;
85 LeaderLatch latch;
86
Jonathan Hart89187372013-03-14 16:41:09 -070087 public SwitchLeaderListener(String dpid, LeaderLatch latch){
Jonathan Hart0de09492013-03-13 14:37:21 -070088 this.dpid = dpid;
89 this.latch = latch;
90 }
91
92 @Override
93 public void leaderLatchEvent(CuratorFramework arg0,
94 LeaderLatchEvent arg1) {
Jonathan Hart89187372013-03-14 16:41:09 -070095 log.debug("Leadership changed for {}, now {}",
Jonathan Hart0de09492013-03-13 14:37:21 -070096 dpid, latch.hasLeadership());
97
Jonathan Hart89187372013-03-14 16:41:09 -070098 //Check that the leadership request is still active - the client
99 //may have since released the request or even begun another request
100 //(this is why we use == to check the object instance is the same)
101 SwitchLeadershipData swData = switches.get(dpid);
Naoki Shiota2999e3d2014-01-03 17:22:59 -0800102 if (swData == null) {
Naoki Shiota1a5ca912014-01-03 17:02:31 -0800103 log.debug("Leadership data {} not found", dpid);
Naoki Shiota2999e3d2014-01-03 17:22:59 -0800104 return;
Naoki Shiota1a5ca912014-01-03 17:02:31 -0800105 }
106
107 if (swData.getLatch() == latch){
Jonathan Hart89187372013-03-14 16:41:09 -0700108 swData.getCallback().controlChanged(
109 HexString.toLong(dpid), latch.hasLeadership());
110 }
111 else {
Jonathan Hart4baf3be2013-03-21 18:26:13 -0700112 log.debug("Latch for {} has changed: old latch {} - new latch {}",
113 new Object[]{dpid, latch, swData.getLatch()});
Jonathan Hart89187372013-03-14 16:41:09 -0700114 }
Jonathan Hart0de09492013-03-13 14:37:21 -0700115 }
116 }
117
Naoki Shiotad00accf2013-06-25 14:40:37 -0700118 protected class SwitchPathCacheListener implements PathChildrenCacheListener {
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800119 @Override
120 public void childEvent(CuratorFramework client,
121 PathChildrenCacheEvent event) throws Exception {
Jonathan Hartcbb4b952013-03-18 16:15:18 -0700122 //log.debug("Root switch path cache got {} event", event.getType());
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800123
124 String strSwitch = null;
125 if (event.getData() != null){
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800126 String[] splitted = event.getData().getPath().split("/");
127 strSwitch = splitted[splitted.length - 1];
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800128 }
129
130 switch (event.getType()){
131 case CHILD_ADDED:
132 case CHILD_UPDATED:
133 //Check we have a PathChildrenCache for this child, add one if not
Jonathan Hart4baf3be2013-03-21 18:26:13 -0700134 synchronized (switchPathCaches){
135 if (switchPathCaches.get(strSwitch) == null){
136 PathChildrenCache pc = new PathChildrenCache(client,
137 event.getData().getPath(), true);
138 pc.start(StartMode.NORMAL);
139 switchPathCaches.put(strSwitch, pc);
140 }
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800141 }
142 break;
143 case CHILD_REMOVED:
144 //Remove our PathChildrenCache for this child
Jonathan Hart4baf3be2013-03-21 18:26:13 -0700145 PathChildrenCache pc = null;
146 synchronized(switchPathCaches){
147 pc = switchPathCaches.remove(strSwitch);
148 }
149 if (pc != null){
150 pc.close();
151 }
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800152 break;
153 default:
Jonathan Hart4baf3be2013-03-21 18:26:13 -0700154 //All other events are connection status events. We don't need to
155 //do anything as the path cache handles these on its own.
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800156 break;
157 }
158
159 }
160 };
Naoki Shiotad00accf2013-06-25 14:40:37 -0700161 /**
162 * Listens for changes to the switch znodes in Zookeeper. This maintains
163 * the second level of PathChildrenCaches that hold the controllers
164 * contending for each switch - there's one for each switch.
165 */
166 PathChildrenCacheListener switchPathCacheListener = new SwitchPathCacheListener();
Jonathan Hart71c0ffc2013-03-24 15:58:42 -0700167 protected ServiceDiscovery<ControllerService> serviceDiscovery;
168 protected ServiceCache<ControllerService> serviceCache;
Jonathan Hartedd6a442013-02-20 15:22:06 -0800169
Jonathan Hartbd181b62013-02-17 16:05:38 -0800170
171 @Override
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800172 public void requestControl(long dpid, ControlChangeCallback cb) throws RegistryException {
Jonathan Hart7bf62172013-02-28 13:17:18 -0800173 log.info("Requesting control for {}", HexString.toHexString(dpid));
Jonathan Hartc6eee9e2013-02-18 14:58:27 -0800174
Jonathan Hartbd766972013-02-22 15:13:03 -0800175 if (controllerId == null){
176 throw new RuntimeException("Must register a controller before calling requestControl");
Jonathan Hartbd181b62013-02-17 16:05:38 -0800177 }
178
179 String dpidStr = HexString.toHexString(dpid);
180 String latchPath = switchLatchesPath + "/" + dpidStr;
181
Jonathan Hart89187372013-03-14 16:41:09 -0700182 if (switches.get(dpidStr) != null){
Jonathan Hart3c0eccd2013-03-12 22:32:50 -0700183 log.debug("Already contesting {}, returning", HexString.toHexString(dpid));
Pankaj Berdeda7187b2013-03-18 15:24:59 -0700184 throw new RegistryException("Already contesting control for " + dpidStr);
Jonathan Hartc6eee9e2013-02-18 14:58:27 -0800185 }
186
Jonathan Hartbd766972013-02-22 15:13:03 -0800187 LeaderLatch latch = new LeaderLatch(client, latchPath, controllerId);
Jonathan Hart89187372013-03-14 16:41:09 -0700188 latch.addListener(new SwitchLeaderListener(dpidStr, latch));
Jonathan Hartbd181b62013-02-17 16:05:38 -0800189
Jonathan Hart44e56fc2013-03-14 16:53:59 -0700190
Jonathan Hart89187372013-03-14 16:41:09 -0700191 SwitchLeadershipData swData = new SwitchLeadershipData(latch, cb);
192 SwitchLeadershipData oldData = switches.putIfAbsent(dpidStr, swData);
193
194 if (oldData != null){
195 //There was already data for that key in the map
196 //i.e. someone else got here first so we can't succeed
197 log.debug("Already requested control for {}", dpidStr);
198 throw new RegistryException("Already requested control for " + dpidStr);
199 }
200
201 //Now that we know we were able to add our latch to the collection,
Jonathan Hart44e56fc2013-03-14 16:53:59 -0700202 //we can start the leader election in Zookeeper. However I don't know
203 //how to handle if the start fails - the latch is already in our
204 //switches list.
205 //TODO seems like there's a Curator bug when latch.start is called when
206 //there's no Zookeeper connection which causes two znodes to be put in
207 //Zookeeper at the latch path when we reconnect to Zookeeper.
Jonathan Hartbd181b62013-02-17 16:05:38 -0800208 try {
Jonathan Hartbd181b62013-02-17 16:05:38 -0800209 latch.start();
210 } catch (Exception e) {
Jonathan Hartc6eee9e2013-02-18 14:58:27 -0800211 log.warn("Error starting leader latch: {}", e.getMessage());
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800212 throw new RegistryException("Error starting leader latch for " + dpidStr, e);
Jonathan Hartbd181b62013-02-17 16:05:38 -0800213 }
214
215 }
216
217 @Override
Jonathan Hartd82f20d2013-02-21 18:04:24 -0800218 public void releaseControl(long dpid) {
Jonathan Hart7bf62172013-02-28 13:17:18 -0800219 log.info("Releasing control for {}", HexString.toHexString(dpid));
Jonathan Hart57080fb2013-02-21 10:55:46 -0800220
Jonathan Hartc6eee9e2013-02-18 14:58:27 -0800221 String dpidStr = HexString.toHexString(dpid);
222
Jonathan Hart89187372013-03-14 16:41:09 -0700223 SwitchLeadershipData swData = switches.remove(dpidStr);
224
225 if (swData == null) {
Jonathan Hart7bf62172013-02-28 13:17:18 -0800226 log.debug("Trying to release control of a switch we are not contesting");
Jonathan Hartbd181b62013-02-17 16:05:38 -0800227 return;
228 }
Jonathan Hart89187372013-03-14 16:41:09 -0700229
Jonathan Hart89187372013-03-14 16:41:09 -0700230 LeaderLatch latch = swData.getLatch();
Jonathan Hartbd181b62013-02-17 16:05:38 -0800231
Jonathan Hart4baf3be2013-03-21 18:26:13 -0700232 latch.removeAllListeners();
233
Jonathan Hartbd181b62013-02-17 16:05:38 -0800234 try {
235 latch.close();
236 } catch (IOException e) {
Jonathan Hart7bf62172013-02-28 13:17:18 -0800237 //I think it's OK not to do anything here. Either the node got
238 //deleted correctly, or the connection went down and the node got deleted.
Umesh Krishnaswamy0ef75ee2013-03-25 17:50:27 -0700239 log.debug("releaseControl: caught IOException {}", dpidStr);
Jonathan Hartbd181b62013-02-17 16:05:38 -0800240 }
241 }
242
243 @Override
Jonathan Hartd82f20d2013-02-21 18:04:24 -0800244 public boolean hasControl(long dpid) {
Jonathan Hart89187372013-03-14 16:41:09 -0700245 String dpidStr = HexString.toHexString(dpid);
Jonathan Hart57080fb2013-02-21 10:55:46 -0800246
Jonathan Hart89187372013-03-14 16:41:09 -0700247 SwitchLeadershipData swData = switches.get(dpidStr);
Jonathan Hartbd181b62013-02-17 16:05:38 -0800248
Jonathan Hart89187372013-03-14 16:41:09 -0700249 if (swData == null) {
250 log.warn("No leader latch for dpid {}", dpidStr);
Jonathan Hartbd181b62013-02-17 16:05:38 -0800251 return false;
252 }
253
Jonathan Hart89187372013-03-14 16:41:09 -0700254 return swData.getLatch().hasLeadership();
Jonathan Hartbd181b62013-02-17 16:05:38 -0800255 }
256
257 @Override
Jonathan Hart7bf62172013-02-28 13:17:18 -0800258 public String getControllerId() {
Jonathan Hartbd766972013-02-22 15:13:03 -0800259 return controllerId;
Jonathan Hartbd181b62013-02-17 16:05:38 -0800260 }
261
Jonathan Hartedd6a442013-02-20 15:22:06 -0800262 @Override
Jonathan Hart57080fb2013-02-21 10:55:46 -0800263 public Collection<String> getAllControllers() throws RegistryException {
Jonathan Hartedd6a442013-02-20 15:22:06 -0800264 log.debug("Getting all controllers");
Jonathan Hart1be46262013-02-20 16:43:51 -0800265
Jonathan Hartedd6a442013-02-20 15:22:06 -0800266 List<String> controllers = new ArrayList<String>();
Jonathan Hart71c0ffc2013-03-24 15:58:42 -0700267 for (ServiceInstance<ControllerService> instance : serviceCache.getInstances()){
268 String id = instance.getPayload().getControllerId();
269 if (!controllers.contains(id)){
270 controllers.add(id);
Jonathan Hartedd6a442013-02-20 15:22:06 -0800271 }
Jonathan Hartedd6a442013-02-20 15:22:06 -0800272 }
Jonathan Hart71c0ffc2013-03-24 15:58:42 -0700273
Jonathan Hartedd6a442013-02-20 15:22:06 -0800274 return controllers;
275 }
276
277 @Override
Jonathan Hart57080fb2013-02-21 10:55:46 -0800278 public void registerController(String id) throws RegistryException {
Jonathan Hartd10008d2013-02-23 17:04:08 -0800279 if (controllerId != null) {
280 throw new RegistryException(
281 "Controller already registered with id " + controllerId);
282 }
Jonathan Hartbd766972013-02-22 15:13:03 -0800283
284 controllerId = id;
Jonathan Hart57080fb2013-02-21 10:55:46 -0800285
Jonathan Hartedd6a442013-02-20 15:22:06 -0800286 try {
Jonathan Hart71c0ffc2013-03-24 15:58:42 -0700287 ServiceInstance<ControllerService> thisInstance = ServiceInstance.<ControllerService>builder()
288 .name(CONTROLLER_SERVICE_NAME)
289 .payload(new ControllerService(controllerId))
290 //.port((int)(65535 * Math.random())) // in a real application, you'd use a common port
291 //.uriSpec(uriSpec)
292 .build();
Jonathan Hart0b3eee42013-03-16 18:20:04 -0700293
Jonathan Hart71c0ffc2013-03-24 15:58:42 -0700294 serviceDiscovery.registerService(thisInstance);
Jonathan Hartedd6a442013-02-20 15:22:06 -0800295 } catch (Exception e) {
Jonathan Hart71c0ffc2013-03-24 15:58:42 -0700296 // TODO Auto-generated catch block
297 e.printStackTrace();
Jonathan Hartedd6a442013-02-20 15:22:06 -0800298 }
Jonathan Hart71c0ffc2013-03-24 15:58:42 -0700299
Jonathan Hartedd6a442013-02-20 15:22:06 -0800300 }
301
302 @Override
Jonathan Hart57080fb2013-02-21 10:55:46 -0800303 public String getControllerForSwitch(long dpid) throws RegistryException {
Jonathan Hart89187372013-03-14 16:41:09 -0700304 String dpidStr = HexString.toHexString(dpid);
Pankaj Berde017960a2013-03-14 20:32:26 -0700305
Jonathan Hart599c6b32013-03-24 22:42:02 -0700306 PathChildrenCache switchCache = switchPathCaches.get(dpidStr);
307
308 if (switchCache == null){
Jonathan Hartedd6a442013-02-20 15:22:06 -0800309 log.warn("Tried to get controller for non-existent switch");
310 return null;
311 }
312
Jonathan Hartf4e80842013-03-26 23:55:02 -0700313 try {
314 //We've seen issues with these caches get stuck out of date, so we'll have to
315 //force them to refresh before each read. This slows down the method as it
316 //blocks on a Zookeeper query, however at the moment only the cleanup thread
317 //uses this and that isn't particularly time-sensitive.
318 switchCache.rebuild();
319 } catch (Exception e) {
320 // TODO Auto-generated catch block
321 e.printStackTrace();
322 }
323
Jonathan Hart599c6b32013-03-24 22:42:02 -0700324 List<ChildData> sortedData = new ArrayList<ChildData>(switchCache.getCurrentData());
Jonathan Hart0b3eee42013-03-16 18:20:04 -0700325
Jonathan Hart599c6b32013-03-24 22:42:02 -0700326 Collections.sort(
327 sortedData,
328 new Comparator<ChildData>(){
329 private String getSequenceNumber(String path){
330 return path.substring(path.lastIndexOf('-') + 1);
331 }
332 @Override
333 public int compare(ChildData lhs, ChildData rhs) {
334 return getSequenceNumber(lhs.getPath()).
335 compareTo(getSequenceNumber(rhs.getPath()));
336 }
337 }
338 );
Jonathan Hartedd6a442013-02-20 15:22:06 -0800339
Jonathan Hart56b296e2013-03-25 13:30:10 -0700340 if (sortedData.size() == 0){
341 return null;
342 }
343
Jonathan Hart599c6b32013-03-24 22:42:02 -0700344 return new String(sortedData.get(0).getData(), Charsets.UTF_8);
Jonathan Hartedd6a442013-02-20 15:22:06 -0800345 }
346
347 @Override
348 public Collection<Long> getSwitchesControlledByController(String controllerId) {
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800349 //TODO remove this if not needed
Jonathan Hartbd766972013-02-22 15:13:03 -0800350 throw new RuntimeException("Not yet implemented");
Jonathan Hartedd6a442013-02-20 15:22:06 -0800351 }
Jonathan Hartbd181b62013-02-17 16:05:38 -0800352
Jonathan Hartd82f20d2013-02-21 18:04:24 -0800353
Jonathan Hart89187372013-03-14 16:41:09 -0700354 //TODO what should happen when there's no ZK connection? Currently we just return
355 //the cache but this may lead to false impressions - i.e. we don't actually know
356 //what's in ZK so we shouldn't say we do
Jonathan Hartd82f20d2013-02-21 18:04:24 -0800357 @Override
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800358 public Map<String, List<ControllerRegistryEntry>> getAllSwitches() {
359 Map<String, List<ControllerRegistryEntry>> data =
360 new HashMap<String, List<ControllerRegistryEntry>>();
361
362 for (Map.Entry<String, PathChildrenCache> entry : switchPathCaches.entrySet()){
363 List<ControllerRegistryEntry> contendingControllers =
364 new ArrayList<ControllerRegistryEntry>();
365
366 if (entry.getValue().getCurrentData().size() < 1){
Jonathan Hartcbb4b952013-03-18 16:15:18 -0700367 //TODO prevent even having the PathChildrenCache in this case
368 //log.info("Switch entry with no leader elections: {}", entry.getKey());
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800369 continue;
370 }
371
372 for (ChildData d : entry.getValue().getCurrentData()) {
Jonathan Hart97801ac2013-02-26 14:29:16 -0800373
Jonathan Hartd10008d2013-02-23 17:04:08 -0800374 String controllerId = new String(d.getData(), Charsets.UTF_8);
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800375
376 String[] splitted = d.getPath().split("-");
377 int sequenceNumber = Integer.parseInt(splitted[splitted.length - 1]);
378
379 contendingControllers.add(new ControllerRegistryEntry(controllerId, sequenceNumber));
380 }
381
382 Collections.sort(contendingControllers);
383 data.put(entry.getKey(), contendingControllers);
384 }
385 return data;
Jonathan Hartd82f20d2013-02-21 18:04:24 -0800386 }
387
Naoki Shiotaa3b2dfa2013-06-27 13:52:24 -0700388 /**
389 * Returns a block of IDs which are unique and unused.
390 * Range of IDs is fixed size and is assigned incrementally as this method called.
391 * Since the range of IDs is managed by Zookeeper in distributed way, this method may block when
392 * requests come up simultaneously.
393 */
Jonathan Hart1530ccc2013-04-03 19:36:02 -0700394 public IdBlock allocateUniqueIdBlock(){
395 try {
396 AtomicValue<Long> result = null;
397 do {
398 result = distributedIdCounter.add(ID_BLOCK_SIZE);
399 } while (result == null || !result.succeeded());
400
401 return new IdBlock(result.preValue(), result.postValue() - 1, ID_BLOCK_SIZE);
402 } catch (Exception e) {
403 log.error("Error allocating ID block");
404 }
405
406 return null;
407 }
408
Jonathan Hartbd181b62013-02-17 16:05:38 -0800409 /*
410 * IFloodlightModule
411 */
412
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -0800413 @Override
414 public Collection<Class<? extends IFloodlightService>> getModuleServices() {
Jonathan Hartedd6a442013-02-20 15:22:06 -0800415 Collection<Class<? extends IFloodlightService>> l =
416 new ArrayList<Class<? extends IFloodlightService>>();
Jonathan Hartd82f20d2013-02-21 18:04:24 -0800417 l.add(IControllerRegistryService.class);
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -0800418 return l;
419 }
420
421 @Override
422 public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {
423 Map<Class<? extends IFloodlightService>, IFloodlightService> m =
424 new HashMap<Class<? extends IFloodlightService>, IFloodlightService>();
Jonathan Hartd82f20d2013-02-21 18:04:24 -0800425 m.put(IControllerRegistryService.class, this);
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -0800426 return m;
427 }
428
429 @Override
430 public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800431 Collection<Class<? extends IFloodlightService>> l =
432 new ArrayList<Class<? extends IFloodlightService>>();
433 l.add(IRestApiService.class);
434 return l;
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -0800435 }
436
Jonathan Hart89187372013-03-14 16:41:09 -0700437 //TODO currently blocks startup when it can't get a Zookeeper connection.
438 //Do we support starting up with no Zookeeper connection?
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -0800439 @Override
440 public void init (FloodlightModuleContext context) throws FloodlightModuleException {
Jonathan Hartbd766972013-02-22 15:13:03 -0800441 log.info("Initialising the Zookeeper Registry - Zookeeper connection required");
442
Jonathan Hart97801ac2013-02-26 14:29:16 -0800443 //Read the Zookeeper connection string from the config
444 Map<String, String> configParams = context.getConfigParams(this);
445 String connectionString = configParams.get("connectionString");
446 if (connectionString != null){
447 this.connectionString = connectionString;
Jonathan Hart57080fb2013-02-21 10:55:46 -0800448 }
Jonathan Hart97801ac2013-02-26 14:29:16 -0800449 log.info("Setting Zookeeper connection string to {}", this.connectionString);
Jonathan Hart57080fb2013-02-21 10:55:46 -0800450
Jonathan Hart97801ac2013-02-26 14:29:16 -0800451 restApi = context.getServiceImpl(IRestApiService.class);
Jonathan Hartbd181b62013-02-17 16:05:38 -0800452
Jonathan Hart89187372013-03-14 16:41:09 -0700453 switches = new ConcurrentHashMap<String, SwitchLeadershipData>();
Jonathan Hart4baf3be2013-03-21 18:26:13 -0700454 //switchPathCaches = new HashMap<String, PathChildrenCache>();
455 switchPathCaches = new ConcurrentHashMap<String, PathChildrenCache>();
Jonathan Hartbd181b62013-02-17 16:05:38 -0800456
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800457 RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
Jonathan Hart97801ac2013-02-26 14:29:16 -0800458 client = CuratorFrameworkFactory.newClient(this.connectionString,
Jonathan Hartcc957a02013-02-26 10:39:04 -0800459 sessionTimeout, connectionTimeout, retryPolicy);
Jonathan Hartbd181b62013-02-17 16:05:38 -0800460
461 client.start();
Jonathan Hartbd181b62013-02-17 16:05:38 -0800462 client = client.usingNamespace(namespace);
Jonathan Hart97801ac2013-02-26 14:29:16 -0800463
Jonathan Hart1530ccc2013-04-03 19:36:02 -0700464 distributedIdCounter = new DistributedAtomicLong(
465 client,
466 ID_COUNTER_PATH,
467 new RetryOneTime(100));
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800468
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800469 switchCache = new PathChildrenCache(client, switchLatchesPath, true);
470 switchCache.getListenable().addListener(switchPathCacheListener);
Jonathan Hartedd6a442013-02-20 15:22:06 -0800471
Jonathan Hart71c0ffc2013-03-24 15:58:42 -0700472 //Build the service discovery object
473 serviceDiscovery = ServiceDiscoveryBuilder.builder(ControllerService.class)
474 .client(client).basePath(SERVICES_PATH).build();
475
476 //We read the list of services very frequently (GUI periodically queries them)
477 //so we'll cache them to cut down on Zookeeper queries.
478 serviceCache = serviceDiscovery.serviceCacheBuilder()
479 .name(CONTROLLER_SERVICE_NAME).build();
480
481
Jonathan Hartedd6a442013-02-20 15:22:06 -0800482 try {
Jonathan Hart71c0ffc2013-03-24 15:58:42 -0700483 serviceDiscovery.start();
484 serviceCache.start();
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800485
486 //Don't prime the cache, we want a notification for each child node in the path
487 switchCache.start(StartMode.NORMAL);
Jonathan Hartedd6a442013-02-20 15:22:06 -0800488 } catch (Exception e) {
Jonathan Hart7bf62172013-02-28 13:17:18 -0800489 throw new FloodlightModuleException("Error initialising ZookeeperRegistry: "
490 + e.getMessage());
Jonathan Hartedd6a442013-02-20 15:22:06 -0800491 }
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -0800492 }
493
494 @Override
495 public void startUp (FloodlightModuleContext context) {
Jonathan Hart3d7730a2013-02-22 11:51:17 -0800496 restApi.addRestletRoutable(new RegistryWebRoutable());
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -0800497 }
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -0800498}