blob: 5f8a156a661127f726fafe69ba7d0611adb63913 [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 Hartedd6a442013-02-20 15:22:06 -080028import com.netflix.curator.framework.recipes.cache.ChildData;
29import com.netflix.curator.framework.recipes.cache.PathChildrenCache;
30import com.netflix.curator.framework.recipes.cache.PathChildrenCache.StartMode;
Jonathan Hartbd181b62013-02-17 16:05:38 -080031import com.netflix.curator.framework.recipes.leader.LeaderLatch;
32import com.netflix.curator.framework.recipes.leader.Participant;
Jonathan Hartbd181b62013-02-17 16:05:38 -080033import com.netflix.curator.retry.ExponentialBackoffRetry;
34
Jonathan Hartc6eee9e2013-02-18 14:58:27 -080035public class MastershipManager implements IFloodlightModule, IMastershipService {
36
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -080037 protected static Logger log = LoggerFactory.getLogger(MastershipManager.class);
Jonathan Hartc6eee9e2013-02-18 14:58:27 -080038 protected String mastershipId = null;
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -080039
Jonathan Hartbd181b62013-02-17 16:05:38 -080040 //TODO read this from configuration
41 protected String connectionString = "localhost:2181";
42 private final String namespace = "onos";
Jonathan Hartedd6a442013-02-20 15:22:06 -080043 private final String switchLatchesPath = "/switches";
Jonathan Hartbd181b62013-02-17 16:05:38 -080044
45 protected CuratorFramework client;
Jonathan Hartedd6a442013-02-20 15:22:06 -080046
47 private final String controllerPath = "/controllers";
48 protected PathChildrenCache controllerCache;
Jonathan Hartbd181b62013-02-17 16:05:38 -080049
50 protected Map<String, LeaderLatch> switchLatches;
51 protected Map<String, MastershipCallback> switchCallbacks;
52
Jonathan Hartbd181b62013-02-17 16:05:38 -080053 protected class ParamaterizedCuratorWatcher implements CuratorWatcher {
54 private String dpid;
Jonathan Hartc6eee9e2013-02-18 14:58:27 -080055 private boolean isLeader = false;
Jonathan Hartbd181b62013-02-17 16:05:38 -080056 private String latchPath;
57
58 public ParamaterizedCuratorWatcher(String dpid, String latchPath){
59 this.dpid = dpid;
60 this.latchPath = latchPath;
61 }
62
63 @Override
Jonathan Hartedd6a442013-02-20 15:22:06 -080064 public synchronized void process(WatchedEvent event) throws Exception {
Jonathan Hartbd181b62013-02-17 16:05:38 -080065 log.debug("Watch Event: {}", event);
66
67 LeaderLatch latch = switchLatches.get(dpid);
68
Jonathan Hartc6eee9e2013-02-18 14:58:27 -080069 if (event.getState() == KeeperState.Disconnected){
70 if (isLeader) {
71 log.debug("Disconnected while leader - lost leadership for {}", dpid);
72
73 isLeader = false;
74 switchCallbacks.get(dpid).changeCallback(HexString.toLong(dpid), false);
75 }
76 return;
77 }
Jonathan Hartbd181b62013-02-17 16:05:38 -080078
Jonathan Hartc6eee9e2013-02-18 14:58:27 -080079 try {
Jonathan Hartedd6a442013-02-20 15:22:06 -080080
Jonathan Hartc6eee9e2013-02-18 14:58:27 -080081 Participant leader = latch.getLeader();
82
83 if (leader.getId().equals(mastershipId) && !isLeader){
84 log.debug("Became leader for {}", dpid);
85
86 isLeader = true;
87 switchCallbacks.get(dpid).changeCallback(HexString.toLong(dpid), true);
88 }
89 else if (!leader.getId().equals(mastershipId) && isLeader){
90 log.debug("Lost leadership for {}", dpid);
91
92 isLeader = false;
93 switchCallbacks.get(dpid).changeCallback(HexString.toLong(dpid), false);
94 }
95 } catch (Exception e){
96 if (isLeader){
97 log.debug("Exception checking leadership status. Assume leadship lost for {}",
98 dpid);
99
100 isLeader = false;
101 switchCallbacks.get(dpid).changeCallback(HexString.toLong(dpid), false);
102 }
Jonathan Hartbd181b62013-02-17 16:05:38 -0800103 }
104
105 client.getChildren().usingWatcher(this).inBackground().forPath(latchPath);
106 //client.getChildren().usingWatcher(this).forPath(latchPath);
107 }
Jonathan Hartc6eee9e2013-02-18 14:58:27 -0800108 }
Jonathan Hartedd6a442013-02-20 15:22:06 -0800109
Jonathan Hartbd181b62013-02-17 16:05:38 -0800110
111 @Override
Jonathan Hartc6eee9e2013-02-18 14:58:27 -0800112 public void acquireMastership(long dpid, MastershipCallback cb) throws Exception {
113
114 if (mastershipId == null){
115 throw new RuntimeException("Must set mastershipId before calling aquireMastership");
Jonathan Hartbd181b62013-02-17 16:05:38 -0800116 }
117
118 String dpidStr = HexString.toHexString(dpid);
119 String latchPath = switchLatchesPath + "/" + dpidStr;
120
Jonathan Hartc6eee9e2013-02-18 14:58:27 -0800121 if (switchLatches.get(dpidStr) != null){
122 throw new RuntimeException("Leader election for switch " + dpidStr +
123 "is already running");
124 }
125
Jonathan Hartbd181b62013-02-17 16:05:38 -0800126 LeaderLatch latch = new LeaderLatch(client, latchPath, mastershipId);
127 switchLatches.put(dpidStr, latch);
Jonathan Hartbd181b62013-02-17 16:05:38 -0800128 switchCallbacks.put(dpidStr, cb);
129
130 try {
131 //client.getChildren().usingWatcher(watcher).inBackground().forPath(singleLatchPath);
132 client.getChildren().usingWatcher(
133 new ParamaterizedCuratorWatcher(dpidStr, latchPath))
134 .inBackground().forPath(latchPath);
135 latch.start();
136 } catch (Exception e) {
Jonathan Hartc6eee9e2013-02-18 14:58:27 -0800137 log.warn("Error starting leader latch: {}", e.getMessage());
138 throw e;
Jonathan Hartbd181b62013-02-17 16:05:38 -0800139 }
140
141 }
142
143 @Override
144 public void releaseMastership(long dpid) {
Jonathan Hartc6eee9e2013-02-18 14:58:27 -0800145 String dpidStr = HexString.toHexString(dpid);
146
147 LeaderLatch latch = switchLatches.get(dpidStr);
Jonathan Hartbd181b62013-02-17 16:05:38 -0800148 if (latch == null) {
Jonathan Hartc6eee9e2013-02-18 14:58:27 -0800149 log.debug("Trying to release mastership for switch we are not contesting");
Jonathan Hartbd181b62013-02-17 16:05:38 -0800150 return;
151 }
152
153 try {
154 latch.close();
155 } catch (IOException e) {
Jonathan Hart1be46262013-02-20 16:43:51 -0800156 //I think it's OK not to do anything here. Either the node got deleted correctly,
157 //or the connection went down and the node got deleted.
158 } finally {
159 switchLatches.remove(dpidStr);
160 switchCallbacks.remove(dpidStr);
Jonathan Hartbd181b62013-02-17 16:05:38 -0800161 }
162 }
163
164 @Override
165 public boolean amMaster(long dpid) {
166 LeaderLatch latch = switchLatches.get(HexString.toHexString(dpid));
167
168 if (latch == null) {
169 log.warn("No leader latch for dpid {}", HexString.toHexString(dpid));
170 return false;
171 }
172
Jonathan Hartbd181b62013-02-17 16:05:38 -0800173 try {
174 return latch.getLeader().getId().equals(mastershipId);
175 } catch (Exception e) {
176 //TODO swallow exception?
177 return false;
178 }
179 }
180
181 @Override
182 public void setMastershipId(String id) {
Jonathan Hartbd181b62013-02-17 16:05:38 -0800183 mastershipId = id;
184 }
185
186 @Override
187 public String getMastershipId() {
Jonathan Hartbd181b62013-02-17 16:05:38 -0800188 return mastershipId;
189 }
190
Jonathan Hartedd6a442013-02-20 15:22:06 -0800191 @Override
Jonathan Hart1be46262013-02-20 16:43:51 -0800192 public Collection<String> getAllControllers() throws RegistryException {
Jonathan Hartedd6a442013-02-20 15:22:06 -0800193 log.debug("Getting all controllers");
Jonathan Hart1be46262013-02-20 16:43:51 -0800194
Jonathan Hartedd6a442013-02-20 15:22:06 -0800195 List<String> controllers = new ArrayList<String>();
196 for (ChildData data : controllerCache.getCurrentData()){
197
198 String d = null;
199 try {
200 d = new String(data.getData(), "UTF-8");
201 } catch (UnsupportedEncodingException e) {
Jonathan Hart1be46262013-02-20 16:43:51 -0800202 throw new RegistryException("Error encoding string", e);
Jonathan Hartedd6a442013-02-20 15:22:06 -0800203 }
204
205 controllers.add(d);
206 }
207 return controllers;
208 }
209
210 @Override
Jonathan Hart1be46262013-02-20 16:43:51 -0800211 public void registerController(String id) throws RegistryException {
Jonathan Hartedd6a442013-02-20 15:22:06 -0800212 byte bytes[] = null;
213 try {
214 bytes = id.getBytes("UTF-8");
215 } catch (UnsupportedEncodingException e1) {
Jonathan Hart1be46262013-02-20 16:43:51 -0800216 throw new RegistryException("Error encoding string", e1);
Jonathan Hartedd6a442013-02-20 15:22:06 -0800217 }
218
219 String path = controllerPath + "/" + id;
220
221 log.info("Registering controller with id {}", id);
222
223 //Create ephemeral node with my id
224 try {
225 client.create().withProtection().withMode(CreateMode.EPHEMERAL)
226 .forPath(path, bytes);
227 } catch (Exception e) {
Jonathan Hart1be46262013-02-20 16:43:51 -0800228 throw new RegistryException("Error contacting the Zookeeper service", e);
Jonathan Hartedd6a442013-02-20 15:22:06 -0800229 }
230 }
231
232 @Override
Jonathan Hart1be46262013-02-20 16:43:51 -0800233 public String getControllerForSwitch(long dpid) throws RegistryException {
Jonathan Hartedd6a442013-02-20 15:22:06 -0800234 // TODO Work out how we should store this controller/switch data.
235 // The leader latch might be a index to the /controllers collections
236 // which holds more info on the controller (how to talk to it for example).
237
238
239 String strDpid = HexString.toHexString(dpid);
240 LeaderLatch latch = switchLatches.get(strDpid);
241
242 if (latch == null){
243 log.warn("Tried to get controller for non-existent switch");
244 return null;
245 }
246
247 Participant leader = null;
248 try {
249 leader = latch.getLeader();
250 } catch (Exception e) {
Jonathan Hart1be46262013-02-20 16:43:51 -0800251 throw new RegistryException("Error contacting the Zookeeper service", e);
Jonathan Hartedd6a442013-02-20 15:22:06 -0800252 }
253
254 return leader.getId();
255 }
256
257 @Override
258 public Collection<Long> getSwitchesControlledByController(String controllerId) {
259 // TODO Auto-generated method stub
260 return null;
261 }
Jonathan Hartbd181b62013-02-17 16:05:38 -0800262
263 /*
264 * IFloodlightModule
265 */
266
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -0800267 @Override
268 public Collection<Class<? extends IFloodlightService>> getModuleServices() {
Jonathan Hartedd6a442013-02-20 15:22:06 -0800269 Collection<Class<? extends IFloodlightService>> l =
270 new ArrayList<Class<? extends IFloodlightService>>();
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -0800271 l.add(IMastershipService.class);
272 return l;
273 }
274
275 @Override
276 public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {
277 Map<Class<? extends IFloodlightService>, IFloodlightService> m =
278 new HashMap<Class<? extends IFloodlightService>, IFloodlightService>();
279 m.put(IMastershipService.class, this);
280 return m;
281 }
282
283 @Override
284 public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
285 // no module dependencies
286 return null;
287 }
288
289 @Override
290 public void init (FloodlightModuleContext context) throws FloodlightModuleException {
Jonathan Hart1be46262013-02-20 16:43:51 -0800291 /*
Jonathan Hartbd181b62013-02-17 16:05:38 -0800292 try {
293 String localHostname = java.net.InetAddress.getLocalHost().getHostName();
294 mastershipId = localHostname;
295 log.debug("Setting mastership id to {}", mastershipId);
296 } catch (UnknownHostException e) {
297 // TODO Handle this exception
298 e.printStackTrace();
299 }
300
301 switchLatches = new HashMap<String, LeaderLatch>();
302 switchCallbacks = new HashMap<String, MastershipCallback>();
303
304 RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
305 client = CuratorFrameworkFactory.newClient(connectionString, retryPolicy);
306
307 client.start();
308
309 client = client.usingNamespace(namespace);
310
Jonathan Hartedd6a442013-02-20 15:22:06 -0800311 controllerCache = new PathChildrenCache(client, controllerPath, true);
312
313 try {
314 controllerCache.start(StartMode.BUILD_INITIAL_CACHE);
315
316
317 } catch (Exception e) {
318 // TODO Auto-generated catch block
319 e.printStackTrace();
320 }
Jonathan Hart1be46262013-02-20 16:43:51 -0800321 */
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -0800322 }
323
324 @Override
325 public void startUp (FloodlightModuleContext context) {
Jonathan Hartc6eee9e2013-02-18 14:58:27 -0800326 // Nothing to be done on startup
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -0800327 }
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -0800328}