3 * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
5 * This program and the accompanying materials are made available under the
6 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
7 * and is available at http://www.eclipse.org/legal/epl-v10.html
10 package org.opendaylight.controller.protocol_plugin.openflow.core.internal;
12 import java.io.FileNotFoundException;
13 import java.io.IOException;
14 import java.nio.channels.SelectionKey;
15 import java.nio.channels.Selector;
16 import java.nio.channels.ServerSocketChannel;
17 import java.nio.channels.SocketChannel;
18 import java.util.Date;
19 import java.util.Iterator;
21 import java.util.Map.Entry;
23 import java.util.concurrent.BlockingQueue;
24 import java.util.concurrent.ConcurrentHashMap;
25 import java.util.concurrent.ConcurrentMap;
26 import java.util.concurrent.LinkedBlockingQueue;
27 import java.util.concurrent.atomic.AtomicInteger;
29 import org.eclipse.osgi.framework.console.CommandInterpreter;
30 import org.eclipse.osgi.framework.console.CommandProvider;
31 import org.opendaylight.controller.protocol_plugin.openflow.core.IController;
32 import org.opendaylight.controller.protocol_plugin.openflow.core.IMessageListener;
33 import org.opendaylight.controller.protocol_plugin.openflow.core.ISwitch;
34 import org.opendaylight.controller.protocol_plugin.openflow.core.ISwitchStateListener;
35 import org.openflow.protocol.OFMessage;
36 import org.openflow.protocol.OFType;
37 import org.openflow.util.HexString;
38 import org.osgi.framework.BundleContext;
39 import org.osgi.framework.FrameworkUtil;
40 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory;
43 public class Controller implements IController, CommandProvider {
44 private static final Logger logger = LoggerFactory
45 .getLogger(Controller.class);
46 private ControllerIO controllerIO;
47 private Thread switchEventThread;
48 private ConcurrentHashMap<Long, ISwitch> switches;
49 private BlockingQueue<SwitchEvent> switchEvents;
50 // only 1 message listener per OFType
51 private ConcurrentMap<OFType, IMessageListener> messageListeners;
52 // only 1 switch state listener
53 private ISwitchStateListener switchStateListener;
54 private AtomicInteger switchInstanceNumber;
57 * this thread monitors the switchEvents queue for new incoming events from switch
59 private class EventHandler implements Runnable {
65 SwitchEvent ev = switchEvents.take();
66 SwitchEvent.SwitchEventType eType = ev.getEventType();
67 ISwitch sw = ev.getSwitch();
68 if (eType != SwitchEvent.SwitchEventType.SWITCH_MESSAGE) {
69 //logger.debug("Received " + ev.toString() + " from " + sw.toString());
73 Long sid = sw.getId();
74 ISwitch existingSwitch = switches.get(sid);
75 if (existingSwitch != null) {
76 logger.info(" Replacing existing "
77 + existingSwitch.toString() + " with New "
79 disconnectSwitch(existingSwitch);
81 switches.put(sid, sw);
82 notifySwitchAdded(sw);
91 OFMessage msg = ev.getMsg();
93 IMessageListener listener = messageListeners
95 if (listener != null) {
96 listener.receive(sw, msg);
101 logger.error("unknow switch event " + eType.ordinal());
103 } catch (InterruptedException e) {
104 switchEvents.clear();
113 * Function called by the dependency manager when all the required
114 * dependencies are satisfied
118 logger.debug("OpenFlowCore init");
119 this.switches = new ConcurrentHashMap<Long, ISwitch>();
120 this.switchEvents = new LinkedBlockingQueue<SwitchEvent>();
121 this.messageListeners = new ConcurrentHashMap<OFType, IMessageListener>();
122 this.switchStateListener = null;
123 this.switchInstanceNumber = new AtomicInteger(0);
124 registerWithOSGIConsole();
129 * Function called by dependency manager after "init ()" is called
130 * and after the services provided by the class are registered in
131 * the service registry
134 public void start() {
135 logger.debug("OpenFlowCore start() is called");
137 * start a thread to handle event coming from the switch
139 switchEventThread = new Thread(new EventHandler(), "SwitchEvent Thread");
140 switchEventThread.start();
142 // spawn a thread to start to listen on the open flow port
143 controllerIO = new ControllerIO(this);
145 controllerIO.start();
146 } catch (IOException ex) {
147 logger.error("Caught exception: " + ex + " during start");
152 * Function called by the dependency manager before the services
153 * exported by the component are unregistered, this will be
154 * followed by a "destroy ()" calls
158 for (Iterator<Entry<Long, ISwitch>> it = switches.entrySet().iterator(); it
160 Entry<Long, ISwitch> entry = it.next();
161 ((SwitchHandler) entry.getValue()).stop();
164 switchEventThread.interrupt();
166 controllerIO.shutDown();
167 } catch (IOException ex) {
168 logger.error("Caught exception: " + ex + " during stop");
173 * Function called by the dependency manager when at least one
174 * dependency become unsatisfied or when the component is shutting
175 * down because for example bundle is being stopped.
178 public void destroy() {
182 public void addMessageListener(OFType type, IMessageListener listener) {
183 IMessageListener currentListener = this.messageListeners.get(type);
184 if (currentListener != null) {
185 logger.warn(type.toString() + " already listened by "
186 + currentListener.toString());
188 this.messageListeners.put(type, listener);
189 logger.debug(type.toString() + " is now listened by "
190 + listener.toString());
194 public void removeMessageListener(OFType type, IMessageListener listener) {
195 IMessageListener currentListener = this.messageListeners.get(type);
196 if ((currentListener != null) && (currentListener == listener)) {
197 this.messageListeners.remove(type);
202 public void addSwitchStateListener(ISwitchStateListener listener) {
203 if (this.switchStateListener != null) {
204 logger.warn(this.switchStateListener.toString()
205 + "already listened to switch events");
207 this.switchStateListener = listener;
208 logger.debug(listener.toString() + " now listens to switch events");
212 public void removeSwitchStateListener(ISwitchStateListener listener) {
213 if ((this.switchStateListener != null)
214 && (this.switchStateListener == listener)) {
215 this.switchStateListener = null;
219 public void handleNewConnection(Selector selector,
220 SelectionKey serverSelectionKey) {
221 ServerSocketChannel ssc = (ServerSocketChannel) serverSelectionKey
223 SocketChannel sc = null;
227 int i = this.switchInstanceNumber.addAndGet(1);
228 String instanceName = "SwitchHandler-" + i;
229 SwitchHandler switchHandler = new SwitchHandler(this, sc,
231 switchHandler.start();
232 logger.info(instanceName + " connected: " + sc.toString());
233 } catch (IOException e) {
238 private void disconnectSwitch(ISwitch sw) {
239 if (((SwitchHandler) sw).isOperational()) {
240 Long sid = sw.getId();
241 if (this.switches.remove(sid, sw)) {
242 logger.warn(sw.toString() + " is disconnected");
243 notifySwitchDeleted(sw);
245 //logger.warn(sw.toString() + " has been replaced by " +
246 // this.switches.get(sid));
249 ((SwitchHandler) sw).stop();
253 private void notifySwitchAdded(ISwitch sw) {
254 if (switchStateListener != null) {
255 switchStateListener.switchAdded(sw);
259 private void notifySwitchDeleted(ISwitch sw) {
260 if (switchStateListener != null) {
261 switchStateListener.switchDeleted(sw);
265 private synchronized void addSwitchEvent(SwitchEvent event) {
267 this.switchEvents.put(event);
268 } catch (InterruptedException e) {
269 logger.debug("SwitchEvent caught Interrupt Exception");
273 public void takeSwtichEventAdd(ISwitch sw) {
274 SwitchEvent ev = new SwitchEvent(
275 SwitchEvent.SwitchEventType.SWITCH_ADD, sw, null);
279 public void takeSwitchEventDelete(ISwitch sw) {
280 SwitchEvent ev = new SwitchEvent(
281 SwitchEvent.SwitchEventType.SWITCH_DELETE, sw, null);
285 public void takeSwitchEventError(ISwitch sw) {
286 SwitchEvent ev = new SwitchEvent(
287 SwitchEvent.SwitchEventType.SWITCH_ERROR, sw, null);
291 public void takeSwitchEventMsg(ISwitch sw, OFMessage msg) {
292 if (messageListeners.get(msg.getType()) != null) {
293 SwitchEvent ev = new SwitchEvent(
294 SwitchEvent.SwitchEventType.SWITCH_MESSAGE, sw, msg);
300 public Map<Long, ISwitch> getSwitches() {
301 return this.switches;
305 public ISwitch getSwitch(Long switchId) {
306 return this.switches.get(switchId);
309 public void _controllerShowSwitches(CommandInterpreter ci) {
310 Set<Long> sids = switches.keySet();
311 StringBuffer s = new StringBuffer();
312 int size = sids.size();
314 ci.print("switches: empty");
317 Iterator<Long> iter = sids.iterator();
318 s.append("Total: " + size + " switches\n");
319 while (iter.hasNext()) {
320 Long sid = iter.next();
321 Date date = switches.get(sid).getConnectedDate();
322 String switchInstanceName = ((SwitchHandler) switches.get(sid)).getInstanceName();
323 s.append(switchInstanceName + "/" + HexString.toHexString(sid)
324 + " connected since " + date.toString() + "\n");
326 ci.print(s.toString());
330 public void _controllerReset(CommandInterpreter ci) {
331 ci.print("...Disconnecting the communication to all switches...\n");
335 } catch (InterruptedException ie) {
337 ci.print("...start to accept connections from switches...\n");
342 public void _controllerShowConnConfig(CommandInterpreter ci) {
343 String str = System.getProperty("secureChannelEnabled");
344 if ((str != null) && (str.trim().equalsIgnoreCase("true"))) {
345 ci.print("The Controller and Switch should communicate through TLS connetion.\n");
347 String keyStoreFile = System.getProperty("controllerKeyStore");
348 String trustStoreFile = System.getProperty("controllerTrustStore");
349 if ((keyStoreFile == null) || keyStoreFile.trim().isEmpty()) {
350 ci.print("controllerKeyStore not specified in ./configuration/config.ini\n");
352 ci.print("controllerKeyStore=" + keyStoreFile + "\n");
354 if ((trustStoreFile == null) || trustStoreFile.trim().isEmpty()) {
355 ci.print("controllerTrustStore not specified in ./configuration/config.ini\n");
357 ci.print("controllerTrustStore=" + trustStoreFile + "\n");
360 ci.print("The Controller and Switch should communicate through TCP connetion.\n");
364 private void registerWithOSGIConsole() {
365 BundleContext bundleContext = FrameworkUtil.getBundle(this.getClass())
367 bundleContext.registerService(CommandProvider.class.getName(), this,
372 public String getHelp() {
373 StringBuffer help = new StringBuffer();
374 help.append("--Open Flow Controller --\n");
375 help.append("\tcontrollerShowSwitches\n");
376 help.append("\tcontrollerReset\n");
377 help.append("\tcontrollerShowConnConfig\n");
378 return help.toString();