blob: 2e50670257bba2b3a43c9c0d4f2b63b8262e2fad [file] [log] [blame]
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -08001package net.floodlightcontroller.mastership;
2
Jonathan Hartbd181b62013-02-17 16:05:38 -08003import java.io.IOException;
Jonathan Hartedd6a442013-02-20 15:22:06 -08004import java.io.UnsupportedEncodingException;
Jonathan Hartbd181b62013-02-17 16:05:38 -08005import java.net.UnknownHostException;
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -08006import java.util.ArrayList;
7import java.util.Collection;
8import 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;
11
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -080012import net.floodlightcontroller.core.module.FloodlightModuleContext;
13import net.floodlightcontroller.core.module.FloodlightModuleException;
14import net.floodlightcontroller.core.module.IFloodlightModule;
15import net.floodlightcontroller.core.module.IFloodlightService;
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -080016
Jonathan Hartedd6a442013-02-20 15:22:06 -080017import org.apache.zookeeper.CreateMode;
Jonathan Hartbd181b62013-02-17 16:05:38 -080018import org.apache.zookeeper.WatchedEvent;
Jonathan Hartc6eee9e2013-02-18 14:58:27 -080019import org.apache.zookeeper.Watcher.Event.KeeperState;
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 Hartbd181b62013-02-17 16:05:38 -080024import com.netflix.curator.RetryPolicy;
25import com.netflix.curator.framework.CuratorFramework;
26import com.netflix.curator.framework.CuratorFrameworkFactory;
27import com.netflix.curator.framework.api.CuratorWatcher;
Jonathan Hart57080fb2013-02-21 10:55:46 -080028import com.netflix.curator.framework.imps.CuratorFrameworkState;
Jonathan Hartedd6a442013-02-20 15:22:06 -080029import com.netflix.curator.framework.recipes.cache.ChildData;
30import com.netflix.curator.framework.recipes.cache.PathChildrenCache;
31import com.netflix.curator.framework.recipes.cache.PathChildrenCache.StartMode;
Jonathan Hartbd181b62013-02-17 16:05:38 -080032import com.netflix.curator.framework.recipes.leader.LeaderLatch;
33import com.netflix.curator.framework.recipes.leader.Participant;
Jonathan Hart57080fb2013-02-21 10:55:46 -080034import com.netflix.curator.retry.RetryOneTime;
Jonathan Hartbd181b62013-02-17 16:05:38 -080035
Jonathan Hartc6eee9e2013-02-18 14:58:27 -080036public class MastershipManager implements IFloodlightModule, IMastershipService {
37
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -080038 protected static Logger log = LoggerFactory.getLogger(MastershipManager.class);
Jonathan Hartc6eee9e2013-02-18 14:58:27 -080039 protected String mastershipId = null;
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -080040
Jonathan Hartbd181b62013-02-17 16:05:38 -080041 //TODO read this from configuration
42 protected String connectionString = "localhost:2181";
43 private final String namespace = "onos";
Jonathan Hartedd6a442013-02-20 15:22:06 -080044 private final String switchLatchesPath = "/switches";
Jonathan Hartbd181b62013-02-17 16:05:38 -080045
46 protected CuratorFramework client;
Jonathan Hartedd6a442013-02-20 15:22:06 -080047
48 private final String controllerPath = "/controllers";
49 protected PathChildrenCache controllerCache;
Jonathan Hartbd181b62013-02-17 16:05:38 -080050
51 protected Map<String, LeaderLatch> switchLatches;
52 protected Map<String, MastershipCallback> switchCallbacks;
53
Jonathan Hart57080fb2013-02-21 10:55:46 -080054 protected boolean moduleEnabled = false;
55
Jonathan Hartbd181b62013-02-17 16:05:38 -080056 protected class ParamaterizedCuratorWatcher implements CuratorWatcher {
57 private String dpid;
Jonathan Hartc6eee9e2013-02-18 14:58:27 -080058 private boolean isLeader = false;
Jonathan Hartbd181b62013-02-17 16:05:38 -080059 private String latchPath;
60
61 public ParamaterizedCuratorWatcher(String dpid, String latchPath){
62 this.dpid = dpid;
63 this.latchPath = latchPath;
64 }
65
66 @Override
Jonathan Hartedd6a442013-02-20 15:22:06 -080067 public synchronized void process(WatchedEvent event) throws Exception {
Jonathan Hartbd181b62013-02-17 16:05:38 -080068 log.debug("Watch Event: {}", event);
69
70 LeaderLatch latch = switchLatches.get(dpid);
71
Jonathan Hartc6eee9e2013-02-18 14:58:27 -080072 if (event.getState() == KeeperState.Disconnected){
73 if (isLeader) {
74 log.debug("Disconnected while leader - lost leadership for {}", dpid);
75
76 isLeader = false;
77 switchCallbacks.get(dpid).changeCallback(HexString.toLong(dpid), false);
78 }
79 return;
80 }
Jonathan Hartbd181b62013-02-17 16:05:38 -080081
Jonathan Hartc6eee9e2013-02-18 14:58:27 -080082 try {
Jonathan Hartedd6a442013-02-20 15:22:06 -080083
Jonathan Hartc6eee9e2013-02-18 14:58:27 -080084 Participant leader = latch.getLeader();
85
86 if (leader.getId().equals(mastershipId) && !isLeader){
87 log.debug("Became leader for {}", dpid);
88
89 isLeader = true;
90 switchCallbacks.get(dpid).changeCallback(HexString.toLong(dpid), true);
91 }
92 else if (!leader.getId().equals(mastershipId) && isLeader){
93 log.debug("Lost leadership for {}", dpid);
94
95 isLeader = false;
96 switchCallbacks.get(dpid).changeCallback(HexString.toLong(dpid), false);
97 }
98 } catch (Exception e){
99 if (isLeader){
100 log.debug("Exception checking leadership status. Assume leadship lost for {}",
101 dpid);
102
103 isLeader = false;
104 switchCallbacks.get(dpid).changeCallback(HexString.toLong(dpid), false);
105 }
Jonathan Hartbd181b62013-02-17 16:05:38 -0800106 }
107
108 client.getChildren().usingWatcher(this).inBackground().forPath(latchPath);
109 //client.getChildren().usingWatcher(this).forPath(latchPath);
110 }
Jonathan Hartc6eee9e2013-02-18 14:58:27 -0800111 }
Jonathan Hartedd6a442013-02-20 15:22:06 -0800112
Jonathan Hartbd181b62013-02-17 16:05:38 -0800113
114 @Override
Jonathan Hartc6eee9e2013-02-18 14:58:27 -0800115 public void acquireMastership(long dpid, MastershipCallback cb) throws Exception {
116
Jonathan Hart57080fb2013-02-21 10:55:46 -0800117 if (!moduleEnabled) return;
118
Jonathan Hartc6eee9e2013-02-18 14:58:27 -0800119 if (mastershipId == null){
120 throw new RuntimeException("Must set mastershipId before calling aquireMastership");
Jonathan Hartbd181b62013-02-17 16:05:38 -0800121 }
122
123 String dpidStr = HexString.toHexString(dpid);
124 String latchPath = switchLatchesPath + "/" + dpidStr;
125
Jonathan Hartc6eee9e2013-02-18 14:58:27 -0800126 if (switchLatches.get(dpidStr) != null){
127 throw new RuntimeException("Leader election for switch " + dpidStr +
128 "is already running");
129 }
130
Jonathan Hartbd181b62013-02-17 16:05:38 -0800131 LeaderLatch latch = new LeaderLatch(client, latchPath, mastershipId);
132 switchLatches.put(dpidStr, latch);
Jonathan Hartbd181b62013-02-17 16:05:38 -0800133 switchCallbacks.put(dpidStr, cb);
134
135 try {
136 //client.getChildren().usingWatcher(watcher).inBackground().forPath(singleLatchPath);
137 client.getChildren().usingWatcher(
138 new ParamaterizedCuratorWatcher(dpidStr, latchPath))
139 .inBackground().forPath(latchPath);
140 latch.start();
141 } catch (Exception e) {
Jonathan Hartc6eee9e2013-02-18 14:58:27 -0800142 log.warn("Error starting leader latch: {}", e.getMessage());
143 throw e;
Jonathan Hartbd181b62013-02-17 16:05:38 -0800144 }
145
146 }
147
148 @Override
149 public void releaseMastership(long dpid) {
Jonathan Hart57080fb2013-02-21 10:55:46 -0800150 if (!moduleEnabled) return;
151
Jonathan Hartc6eee9e2013-02-18 14:58:27 -0800152 String dpidStr = HexString.toHexString(dpid);
153
154 LeaderLatch latch = switchLatches.get(dpidStr);
Jonathan Hartbd181b62013-02-17 16:05:38 -0800155 if (latch == null) {
Jonathan Hartc6eee9e2013-02-18 14:58:27 -0800156 log.debug("Trying to release mastership for switch we are not contesting");
Jonathan Hartbd181b62013-02-17 16:05:38 -0800157 return;
158 }
159
160 try {
161 latch.close();
162 } catch (IOException e) {
Jonathan Hart1be46262013-02-20 16:43:51 -0800163 //I think it's OK not to do anything here. Either the node got deleted correctly,
164 //or the connection went down and the node got deleted.
165 } finally {
166 switchLatches.remove(dpidStr);
167 switchCallbacks.remove(dpidStr);
Jonathan Hartbd181b62013-02-17 16:05:38 -0800168 }
169 }
170
171 @Override
172 public boolean amMaster(long dpid) {
Jonathan Hart57080fb2013-02-21 10:55:46 -0800173 if (!moduleEnabled) return false;
174
Jonathan Hartbd181b62013-02-17 16:05:38 -0800175 LeaderLatch latch = switchLatches.get(HexString.toHexString(dpid));
176
177 if (latch == null) {
178 log.warn("No leader latch for dpid {}", HexString.toHexString(dpid));
179 return false;
180 }
181
Jonathan Hartbd181b62013-02-17 16:05:38 -0800182 try {
183 return latch.getLeader().getId().equals(mastershipId);
184 } catch (Exception e) {
185 //TODO swallow exception?
186 return false;
187 }
188 }
189
190 @Override
191 public void setMastershipId(String id) {
Jonathan Hartbd181b62013-02-17 16:05:38 -0800192 mastershipId = id;
193 }
194
195 @Override
196 public String getMastershipId() {
Jonathan Hartbd181b62013-02-17 16:05:38 -0800197 return mastershipId;
198 }
199
Jonathan Hartedd6a442013-02-20 15:22:06 -0800200 @Override
Jonathan Hart57080fb2013-02-21 10:55:46 -0800201 public Collection<String> getAllControllers() throws RegistryException {
202 if (!moduleEnabled) return null;
203
Jonathan Hartedd6a442013-02-20 15:22:06 -0800204 log.debug("Getting all controllers");
Jonathan Hart1be46262013-02-20 16:43:51 -0800205
Jonathan Hartedd6a442013-02-20 15:22:06 -0800206 List<String> controllers = new ArrayList<String>();
207 for (ChildData data : controllerCache.getCurrentData()){
208
209 String d = null;
210 try {
211 d = new String(data.getData(), "UTF-8");
212 } catch (UnsupportedEncodingException e) {
Jonathan Hart57080fb2013-02-21 10:55:46 -0800213 throw new RegistryException("Error encoding string", e);
Jonathan Hartedd6a442013-02-20 15:22:06 -0800214 }
215
216 controllers.add(d);
217 }
218 return controllers;
219 }
220
221 @Override
Jonathan Hart57080fb2013-02-21 10:55:46 -0800222 public void registerController(String id) throws RegistryException {
223 if (!moduleEnabled) return;
224
Jonathan Hartedd6a442013-02-20 15:22:06 -0800225 byte bytes[] = null;
226 try {
227 bytes = id.getBytes("UTF-8");
228 } catch (UnsupportedEncodingException e1) {
Jonathan Hart57080fb2013-02-21 10:55:46 -0800229 throw new RegistryException("Error encoding string", e1);
Jonathan Hartedd6a442013-02-20 15:22:06 -0800230 }
231
232 String path = controllerPath + "/" + id;
233
234 log.info("Registering controller with id {}", id);
235
Jonathan Hart57080fb2013-02-21 10:55:46 -0800236 //Create ephemeral node in controller registry
Jonathan Hartedd6a442013-02-20 15:22:06 -0800237 try {
238 client.create().withProtection().withMode(CreateMode.EPHEMERAL)
239 .forPath(path, bytes);
240 } catch (Exception e) {
Jonathan Hart57080fb2013-02-21 10:55:46 -0800241 throw new RegistryException("Error contacting the Zookeeper service", e);
Jonathan Hartedd6a442013-02-20 15:22:06 -0800242 }
243 }
244
245 @Override
Jonathan Hart57080fb2013-02-21 10:55:46 -0800246 public String getControllerForSwitch(long dpid) throws RegistryException {
247 if (!moduleEnabled) return null;
Jonathan Hartedd6a442013-02-20 15:22:06 -0800248 // TODO Work out how we should store this controller/switch data.
249 // The leader latch might be a index to the /controllers collections
250 // which holds more info on the controller (how to talk to it for example).
251
252
253 String strDpid = HexString.toHexString(dpid);
254 LeaderLatch latch = switchLatches.get(strDpid);
255
256 if (latch == null){
257 log.warn("Tried to get controller for non-existent switch");
258 return null;
259 }
260
261 Participant leader = null;
262 try {
263 leader = latch.getLeader();
264 } catch (Exception e) {
Jonathan Hart57080fb2013-02-21 10:55:46 -0800265 throw new RegistryException("Error contacting the Zookeeper service", e);
Jonathan Hartedd6a442013-02-20 15:22:06 -0800266 }
267
268 return leader.getId();
269 }
270
271 @Override
272 public Collection<Long> getSwitchesControlledByController(String controllerId) {
273 // TODO Auto-generated method stub
274 return null;
275 }
Jonathan Hartbd181b62013-02-17 16:05:38 -0800276
277 /*
278 * IFloodlightModule
279 */
280
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -0800281 @Override
282 public Collection<Class<? extends IFloodlightService>> getModuleServices() {
Jonathan Hartedd6a442013-02-20 15:22:06 -0800283 Collection<Class<? extends IFloodlightService>> l =
284 new ArrayList<Class<? extends IFloodlightService>>();
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -0800285 l.add(IMastershipService.class);
286 return l;
287 }
288
289 @Override
290 public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {
291 Map<Class<? extends IFloodlightService>, IFloodlightService> m =
292 new HashMap<Class<? extends IFloodlightService>, IFloodlightService>();
293 m.put(IMastershipService.class, this);
294 return m;
295 }
296
297 @Override
298 public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
299 // no module dependencies
300 return null;
301 }
302
303 @Override
304 public void init (FloodlightModuleContext context) throws FloodlightModuleException {
Jonathan Hart57080fb2013-02-21 10:55:46 -0800305
306 //Read config to see if we should try and connect to zookeeper
307 Map<String, String> configOptions = context.getConfigParams(this);
308 String enableZookeeper = configOptions.get("enableZookeeper");
309 if (enableZookeeper != null) {
310 log.info("Enabling Mastership module - requires Zookeeper connection");
311 moduleEnabled = true;
312 }
313 else {
314 log.info("Mastership module is disabled");
315 return;
316 }
317
Jonathan Hartbd181b62013-02-17 16:05:38 -0800318 try {
319 String localHostname = java.net.InetAddress.getLocalHost().getHostName();
320 mastershipId = localHostname;
321 log.debug("Setting mastership id to {}", mastershipId);
322 } catch (UnknownHostException e) {
323 // TODO Handle this exception
324 e.printStackTrace();
325 }
326
327 switchLatches = new HashMap<String, LeaderLatch>();
328 switchCallbacks = new HashMap<String, MastershipCallback>();
329
Jonathan Hart57080fb2013-02-21 10:55:46 -0800330 //RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
331 RetryPolicy retryPolicy = new RetryOneTime(0);
Jonathan Hartbd181b62013-02-17 16:05:38 -0800332 client = CuratorFrameworkFactory.newClient(connectionString, retryPolicy);
333
334 client.start();
335
336 client = client.usingNamespace(namespace);
337
Jonathan Hartedd6a442013-02-20 15:22:06 -0800338 controllerCache = new PathChildrenCache(client, controllerPath, true);
339
340 try {
341 controllerCache.start(StartMode.BUILD_INITIAL_CACHE);
Jonathan Hartedd6a442013-02-20 15:22:06 -0800342 } catch (Exception e) {
343 // TODO Auto-generated catch block
344 e.printStackTrace();
345 }
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -0800346 }
347
348 @Override
349 public void startUp (FloodlightModuleContext context) {
Jonathan Hartc6eee9e2013-02-18 14:58:27 -0800350 // Nothing to be done on startup
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -0800351 }
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -0800352}