2 * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
9 package org.opendaylight.controller.protocol_plugin.openflow.core.internal;
11 import java.io.IOException;
12 import java.nio.channels.SelectionKey;
13 import java.nio.channels.Selector;
14 import java.nio.channels.ServerSocketChannel;
15 import java.nio.channels.SocketChannel;
16 import java.util.Date;
17 import java.util.Iterator;
19 import java.util.Map.Entry;
21 import java.util.concurrent.BlockingQueue;
22 import java.util.concurrent.ConcurrentHashMap;
23 import java.util.concurrent.ConcurrentMap;
24 import java.util.concurrent.LinkedBlockingQueue;
25 import java.util.concurrent.atomic.AtomicInteger;
27 import org.eclipse.osgi.framework.console.CommandInterpreter;
28 import org.eclipse.osgi.framework.console.CommandProvider;
29 import org.opendaylight.controller.protocol_plugin.openflow.core.IController;
30 import org.opendaylight.controller.protocol_plugin.openflow.core.IMessageListener;
31 import org.opendaylight.controller.protocol_plugin.openflow.core.ISwitch;
32 import org.opendaylight.controller.protocol_plugin.openflow.core.ISwitchStateListener;
33 import org.openflow.protocol.OFMessage;
34 import org.openflow.protocol.OFType;
35 import org.openflow.util.HexString;
36 import org.osgi.framework.BundleContext;
37 import org.osgi.framework.FrameworkUtil;
38 import org.slf4j.Logger;
39 import org.slf4j.LoggerFactory;
41 public class Controller implements IController, CommandProvider {
42 private static final Logger logger = LoggerFactory
43 .getLogger(Controller.class);
44 private ControllerIO controllerIO;
45 private Thread switchEventThread;
46 private ConcurrentHashMap<Long, ISwitch> switches;
47 private BlockingQueue<SwitchEvent> switchEvents;
48 // only 1 message listener per OFType
49 private ConcurrentMap<OFType, IMessageListener> messageListeners;
50 // only 1 switch state listener
51 private ISwitchStateListener switchStateListener;
52 private AtomicInteger switchInstanceNumber;
55 * this thread monitors the switchEvents queue for new incoming events from
58 private class EventHandler implements Runnable {
64 SwitchEvent ev = switchEvents.take();
65 SwitchEvent.SwitchEventType eType = ev.getEventType();
66 ISwitch sw = ev.getSwitch();
69 Long sid = sw.getId();
70 ISwitch existingSwitch = switches.get(sid);
71 if (existingSwitch != null) {
72 logger.info("Replacing existing {} with New {}",
74 disconnectSwitch(existingSwitch);
76 switches.put(sid, sw);
77 notifySwitchAdded(sw);
86 OFMessage msg = ev.getMsg();
88 IMessageListener listener = messageListeners
90 if (listener != null) {
91 listener.receive(sw, msg);
96 logger.error("Unknown switch event {}", eType.ordinal());
98 } catch (InterruptedException e) {
108 * Function called by the dependency manager when all the required
109 * dependencies are satisfied
113 logger.debug("Initializing!");
114 this.switches = new ConcurrentHashMap<Long, ISwitch>();
115 this.switchEvents = new LinkedBlockingQueue<SwitchEvent>();
116 this.messageListeners = new ConcurrentHashMap<OFType, IMessageListener>();
117 this.switchStateListener = null;
118 this.switchInstanceNumber = new AtomicInteger(0);
119 registerWithOSGIConsole();
123 * Function called by dependency manager after "init ()" is called and after
124 * the services provided by the class are registered in the service registry
127 public void start() {
128 logger.debug("Starting!");
130 * start a thread to handle event coming from the switch
132 switchEventThread = new Thread(new EventHandler(), "SwitchEvent Thread");
133 switchEventThread.start();
135 // spawn a thread to start to listen on the open flow port
136 controllerIO = new ControllerIO(this);
138 controllerIO.start();
139 } catch (IOException ex) {
140 logger.error("Caught exception while starting:", ex);
145 * Function called by the dependency manager before the services exported by
146 * the component are unregistered, this will be followed by a "destroy ()"
151 for (Iterator<Entry<Long, ISwitch>> it = switches.entrySet().iterator(); it
153 Entry<Long, ISwitch> entry = it.next();
154 ((SwitchHandler) entry.getValue()).stop();
157 switchEventThread.interrupt();
159 controllerIO.shutDown();
160 } catch (IOException ex) {
161 logger.error("Caught exception while stopping:", ex);
166 * Function called by the dependency manager when at least one dependency
167 * become unsatisfied or when the component is shutting down because for
168 * example bundle is being stopped.
171 public void destroy() {
175 public void addMessageListener(OFType type, IMessageListener listener) {
176 IMessageListener currentListener = this.messageListeners.get(type);
177 if (currentListener != null) {
178 logger.warn("{} is already listened by {}", type,
181 this.messageListeners.put(type, listener);
182 logger.debug("{} is now listened by {}", type, listener);
186 public void removeMessageListener(OFType type, IMessageListener listener) {
187 IMessageListener currentListener = this.messageListeners.get(type);
188 if ((currentListener != null) && (currentListener == listener)) {
189 logger.debug("{} listener {} is Removed", type, listener);
190 this.messageListeners.remove(type);
195 public void addSwitchStateListener(ISwitchStateListener listener) {
196 if (this.switchStateListener != null) {
197 logger.warn("Switch events are already listened by {}",
198 this.switchStateListener);
200 this.switchStateListener = listener;
201 logger.debug("Switch events are now listened by {}", listener);
205 public void removeSwitchStateListener(ISwitchStateListener listener) {
206 if ((this.switchStateListener != null)
207 && (this.switchStateListener == listener)) {
208 logger.debug("SwitchStateListener {} is Removed", listener);
209 this.switchStateListener = null;
213 public void handleNewConnection(Selector selector,
214 SelectionKey serverSelectionKey) {
215 ServerSocketChannel ssc = (ServerSocketChannel) serverSelectionKey
217 SocketChannel sc = null;
221 int i = this.switchInstanceNumber.addAndGet(1);
222 String instanceName = "SwitchHandler-" + i;
223 SwitchHandler switchHandler = new SwitchHandler(this, sc,
225 switchHandler.start();
226 if (sc.isConnected()) {
227 logger.info("Switch:{} is connected to the Controller",
228 sc.socket().getRemoteSocketAddress()
229 .toString().split("/")[1]);
232 } catch (IOException e) {
237 private void disconnectSwitch(ISwitch sw) {
238 if (((SwitchHandler) sw).isOperational()) {
239 Long sid = sw.getId();
240 if (this.switches.remove(sid, sw)) {
241 logger.warn("{} is Disconnected", sw);
242 notifySwitchDeleted(sw);
245 ((SwitchHandler) sw).stop();
249 private void notifySwitchAdded(ISwitch sw) {
250 if (switchStateListener != null) {
251 switchStateListener.switchAdded(sw);
255 private void notifySwitchDeleted(ISwitch sw) {
256 if (switchStateListener != null) {
257 switchStateListener.switchDeleted(sw);
261 private synchronized void addSwitchEvent(SwitchEvent event) {
263 this.switchEvents.put(event);
264 } catch (InterruptedException e) {
265 logger.debug("SwitchEvent caught Interrupt Exception");
269 public void takeSwitchEventAdd(ISwitch sw) {
270 SwitchEvent ev = new SwitchEvent(
271 SwitchEvent.SwitchEventType.SWITCH_ADD, sw, null);
275 public void takeSwitchEventDelete(ISwitch sw) {
276 SwitchEvent ev = new SwitchEvent(
277 SwitchEvent.SwitchEventType.SWITCH_DELETE, sw, null);
281 public void takeSwitchEventError(ISwitch sw) {
282 SwitchEvent ev = new SwitchEvent(
283 SwitchEvent.SwitchEventType.SWITCH_ERROR, sw, null);
287 public void takeSwitchEventMsg(ISwitch sw, OFMessage msg) {
288 if (messageListeners.get(msg.getType()) != null) {
289 SwitchEvent ev = new SwitchEvent(
290 SwitchEvent.SwitchEventType.SWITCH_MESSAGE, sw, msg);
296 public Map<Long, ISwitch> getSwitches() {
297 return this.switches;
301 public ISwitch getSwitch(Long switchId) {
302 return this.switches.get(switchId);
305 public void _controllerShowSwitches(CommandInterpreter ci) {
306 Set<Long> sids = switches.keySet();
307 StringBuffer s = new StringBuffer();
308 int size = sids.size();
310 ci.print("switches: empty");
313 Iterator<Long> iter = sids.iterator();
314 s.append("Total: " + size + " switches\n");
315 while (iter.hasNext()) {
316 Long sid = iter.next();
317 Date date = switches.get(sid).getConnectedDate();
318 String switchInstanceName = ((SwitchHandler) switches.get(sid))
320 s.append(switchInstanceName + "/" + HexString.toHexString(sid)
321 + " connected since " + date.toString() + "\n");
323 ci.print(s.toString());
327 public void _controllerReset(CommandInterpreter ci) {
328 ci.print("...Disconnecting the communication to all switches...\n");
332 } catch (InterruptedException ie) {
334 ci.print("...start to accept connections from switches...\n");
339 public void _controllerShowConnConfig(CommandInterpreter ci) {
340 String str = System.getProperty("secureChannelEnabled");
341 if ((str != null) && (str.trim().equalsIgnoreCase("true"))) {
342 ci.print("The Controller and Switch should communicate through TLS connetion.\n");
344 String keyStoreFile = System.getProperty("controllerKeyStore");
345 String trustStoreFile = System.getProperty("controllerTrustStore");
346 if ((keyStoreFile == null) || keyStoreFile.trim().isEmpty()) {
347 ci.print("controllerKeyStore not specified in ./configuration/config.ini\n");
349 ci.print("controllerKeyStore=" + keyStoreFile + "\n");
351 if ((trustStoreFile == null) || trustStoreFile.trim().isEmpty()) {
352 ci.print("controllerTrustStore not specified in ./configuration/config.ini\n");
354 ci.print("controllerTrustStore=" + trustStoreFile + "\n");
357 ci.print("The Controller and Switch should communicate through TCP connetion.\n");
361 private void registerWithOSGIConsole() {
362 BundleContext bundleContext = FrameworkUtil.getBundle(this.getClass())
364 bundleContext.registerService(CommandProvider.class.getName(), this,
369 public String getHelp() {
370 StringBuffer help = new StringBuffer();
371 help.append("---Open Flow Controller---\n");
372 help.append("\t controllerShowSwitches\n");
373 help.append("\t controllerReset\n");
374 help.append("\t controllerShowConnConfig\n");
375 return help.toString();