c7c6c8924d29807da61aa5896c4b2b1e1b79cc59
[controller.git] / opendaylight / protocol_plugins / openflow / src / main / java / org / opendaylight / controller / protocol_plugin / openflow / core / internal / Controller.java
1 /*
2  * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8
9 package org.opendaylight.controller.protocol_plugin.openflow.core.internal;
10
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;
18 import java.util.Map;
19 import java.util.Map.Entry;
20 import java.util.Set;
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;
26
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.opendaylight.controller.sal.connection.ConnectionConstants;
34 import org.opendaylight.controller.sal.connection.IPluginInConnectionService;
35 import org.opendaylight.controller.sal.connection.IPluginOutConnectionService;
36 import org.opendaylight.controller.sal.core.Node;
37 import org.opendaylight.controller.sal.utils.Status;
38 import org.opendaylight.controller.sal.utils.StatusCode;
39 import org.openflow.protocol.OFMessage;
40 import org.openflow.protocol.OFType;
41 import org.openflow.util.HexString;
42 import org.osgi.framework.BundleContext;
43 import org.osgi.framework.FrameworkUtil;
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
46
47 public class Controller implements IController, CommandProvider, IPluginInConnectionService {
48     private static final Logger logger = LoggerFactory
49             .getLogger(Controller.class);
50     private ControllerIO controllerIO;
51     private Thread switchEventThread;
52     private ConcurrentHashMap<Long, ISwitch> switches;
53     private BlockingQueue<SwitchEvent> switchEvents;
54     // only 1 message listener per OFType
55     private ConcurrentMap<OFType, IMessageListener> messageListeners;
56     // only 1 switch state listener
57     private ISwitchStateListener switchStateListener;
58     private AtomicInteger switchInstanceNumber;
59     private int MAXQUEUESIZE = 50000;
60
61     /*
62      * this thread monitors the switchEvents queue for new incoming events from
63      * switch
64      */
65     private class EventHandler implements Runnable {
66         @Override
67         public void run() {
68
69             while (true) {
70                 try {
71                     SwitchEvent ev = switchEvents.take();
72                     SwitchEvent.SwitchEventType eType = ev.getEventType();
73                     ISwitch sw = ev.getSwitch();
74                     switch (eType) {
75                     case SWITCH_ADD:
76                         Long sid = sw.getId();
77                         ISwitch existingSwitch = switches.get(sid);
78                         if (existingSwitch != null) {
79                             logger.info("Replacing existing {} with New {}",
80                                     existingSwitch, sw);
81                             disconnectSwitch(existingSwitch);
82                         }
83                         switches.put(sid, sw);
84                         notifySwitchAdded(sw);
85                         break;
86                     case SWITCH_DELETE:
87                         disconnectSwitch(sw);
88                         break;
89                     case SWITCH_ERROR:
90                         disconnectSwitch(sw);
91                         break;
92                     case SWITCH_MESSAGE:
93                         OFMessage msg = ev.getMsg();
94                         if (msg != null) {
95                             IMessageListener listener = messageListeners
96                                     .get(msg.getType());
97                             if (listener != null) {
98                                 listener.receive(sw, msg);
99                             }
100                         }
101                         break;
102                     default:
103                         logger.error("Unknown switch event {}", eType.ordinal());
104                     }
105                 } catch (InterruptedException e) {
106                     switchEvents.clear();
107                     return;
108                 }
109             }
110         }
111
112     }
113
114     /**
115      * Function called by the dependency manager when all the required
116      * dependencies are satisfied
117      *
118      */
119     public void init() {
120         logger.debug("Initializing!");
121         this.switches = new ConcurrentHashMap<Long, ISwitch>();
122         this.switchEvents = new LinkedBlockingQueue<SwitchEvent>(MAXQUEUESIZE);
123         this.messageListeners = new ConcurrentHashMap<OFType, IMessageListener>();
124         this.switchStateListener = null;
125         this.switchInstanceNumber = new AtomicInteger(0);
126         registerWithOSGIConsole();
127     }
128
129     /**
130      * Function called by dependency manager after "init ()" is called and after
131      * the services provided by the class are registered in the service registry
132      *
133      */
134     public void start() {
135         logger.debug("Starting!");
136         /*
137          * start a thread to handle event coming from the switch
138          */
139         switchEventThread = new Thread(new EventHandler(), "SwitchEvent Thread");
140         switchEventThread.start();
141
142         // spawn a thread to start to listen on the open flow port
143         controllerIO = new ControllerIO(this);
144         try {
145             controllerIO.start();
146         } catch (IOException ex) {
147             logger.error("Caught exception while starting:", ex);
148         }
149     }
150
151     /**
152      * Function called by the dependency manager before the services exported by
153      * the component are unregistered, this will be followed by a "destroy ()"
154      * calls
155      *
156      */
157     public void stop() {
158         for (Iterator<Entry<Long, ISwitch>> it = switches.entrySet().iterator(); it
159                 .hasNext();) {
160             Entry<Long, ISwitch> entry = it.next();
161             ((SwitchHandler) entry.getValue()).stop();
162             it.remove();
163         }
164         switchEventThread.interrupt();
165         try {
166             controllerIO.shutDown();
167         } catch (IOException ex) {
168             logger.error("Caught exception while stopping:", ex);
169         }
170     }
171
172     /**
173      * Function called by the dependency manager when at least one dependency
174      * become unsatisfied or when the component is shutting down because for
175      * example bundle is being stopped.
176      *
177      */
178     public void destroy() {
179     }
180
181     @Override
182     public void addMessageListener(OFType type, IMessageListener listener) {
183         IMessageListener currentListener = this.messageListeners.get(type);
184         if (currentListener != null) {
185             logger.warn("{} is already listened by {}", type,
186                     currentListener);
187         }
188         this.messageListeners.put(type, listener);
189         logger.debug("{} is now listened by {}", type, listener);
190     }
191
192     @Override
193     public void removeMessageListener(OFType type, IMessageListener listener) {
194         IMessageListener currentListener = this.messageListeners.get(type);
195         if ((currentListener != null) && (currentListener == listener)) {
196             logger.debug("{} listener {} is Removed", type, listener);
197             this.messageListeners.remove(type);
198         }
199     }
200
201     @Override
202     public void addSwitchStateListener(ISwitchStateListener listener) {
203         if (this.switchStateListener != null) {
204             logger.warn("Switch events are already listened by {}",
205                     this.switchStateListener);
206         }
207         this.switchStateListener = listener;
208         logger.debug("Switch events are now listened by {}", listener);
209     }
210
211     @Override
212     public void removeSwitchStateListener(ISwitchStateListener listener) {
213         if ((this.switchStateListener != null)
214                 && (this.switchStateListener == listener)) {
215             logger.debug("SwitchStateListener {} is Removed", listener);
216             this.switchStateListener = null;
217         }
218     }
219
220     public void handleNewConnection(Selector selector,
221             SelectionKey serverSelectionKey) {
222         ServerSocketChannel ssc = (ServerSocketChannel) serverSelectionKey
223                 .channel();
224         SocketChannel sc = null;
225         try {
226             sc = ssc.accept();
227             // create new switch
228             int i = this.switchInstanceNumber.addAndGet(1);
229             String instanceName = "SwitchHandler-" + i;
230             SwitchHandler switchHandler = new SwitchHandler(this, sc, instanceName);
231             switchHandler.start();
232             if (sc.isConnected()) {
233                 logger.info("Switch:{} is connected to the Controller",
234                         sc.socket().getRemoteSocketAddress()
235                         .toString().split("/")[1]);
236             }
237
238         } catch (IOException e) {
239             return;
240         }
241     }
242
243     private void disconnectSwitch(ISwitch sw) {
244         if (((SwitchHandler) sw).isOperational()) {
245             Long sid = sw.getId();
246             if (this.switches.remove(sid, sw)) {
247                 logger.warn("{} is Disconnected", sw);
248                 notifySwitchDeleted(sw);
249             }
250         }
251         ((SwitchHandler) sw).stop();
252         sw = null;
253     }
254
255     private void notifySwitchAdded(ISwitch sw) {
256         if (switchStateListener != null) {
257             switchStateListener.switchAdded(sw);
258         }
259     }
260
261     private void notifySwitchDeleted(ISwitch sw) {
262         if (switchStateListener != null) {
263             switchStateListener.switchDeleted(sw);
264         }
265     }
266
267     private synchronized void addSwitchEvent(SwitchEvent event) {
268         try {
269             this.switchEvents.put(event);
270         } catch (InterruptedException e) {
271             logger.debug("SwitchEvent caught Interrupt Exception");
272         }
273     }
274
275     public void takeSwitchEventAdd(ISwitch sw) {
276         SwitchEvent ev = new SwitchEvent(
277                 SwitchEvent.SwitchEventType.SWITCH_ADD, sw, null);
278         addSwitchEvent(ev);
279     }
280
281     public void takeSwitchEventDelete(ISwitch sw) {
282         SwitchEvent ev = new SwitchEvent(
283                 SwitchEvent.SwitchEventType.SWITCH_DELETE, sw, null);
284         addSwitchEvent(ev);
285     }
286
287     public void takeSwitchEventError(ISwitch sw) {
288         SwitchEvent ev = new SwitchEvent(
289                 SwitchEvent.SwitchEventType.SWITCH_ERROR, sw, null);
290         addSwitchEvent(ev);
291     }
292
293     public void takeSwitchEventMsg(ISwitch sw, OFMessage msg) {
294         if (messageListeners.get(msg.getType()) != null) {
295             SwitchEvent ev = new SwitchEvent(
296                     SwitchEvent.SwitchEventType.SWITCH_MESSAGE, sw, msg);
297             addSwitchEvent(ev);
298         }
299     }
300
301     @Override
302     public Map<Long, ISwitch> getSwitches() {
303         return this.switches;
304     }
305
306     @Override
307     public ISwitch getSwitch(Long switchId) {
308         return this.switches.get(switchId);
309     }
310
311     public void _controllerShowSwitches(CommandInterpreter ci) {
312         Set<Long> sids = switches.keySet();
313         StringBuffer s = new StringBuffer();
314         int size = sids.size();
315         if (size == 0) {
316             ci.print("switches: empty");
317             return;
318         }
319         Iterator<Long> iter = sids.iterator();
320         s.append("Total: " + size + " switches\n");
321         while (iter.hasNext()) {
322             Long sid = iter.next();
323             Date date = switches.get(sid).getConnectedDate();
324             String switchInstanceName = ((SwitchHandler) switches.get(sid))
325                     .getInstanceName();
326             s.append(switchInstanceName + "/" + HexString.toHexString(sid)
327                     + " connected since " + date.toString() + "\n");
328         }
329         ci.print(s.toString());
330         return;
331     }
332
333     public void _controllerReset(CommandInterpreter ci) {
334         ci.print("...Disconnecting the communication to all switches...\n");
335         stop();
336         try {
337             Thread.sleep(1000);
338         } catch (InterruptedException ie) {
339         } finally {
340             ci.print("...start to accept connections from switches...\n");
341             start();
342         }
343     }
344
345     public void _controllerShowConnConfig(CommandInterpreter ci) {
346         String str = System.getProperty("secureChannelEnabled");
347         if ((str != null) && (str.trim().equalsIgnoreCase("true"))) {
348             ci.print("The Controller and Switch should communicate through TLS connetion.\n");
349
350             String keyStoreFile = System.getProperty("controllerKeyStore");
351             String trustStoreFile = System.getProperty("controllerTrustStore");
352             if ((keyStoreFile == null) || keyStoreFile.trim().isEmpty()) {
353                 ci.print("controllerKeyStore not specified in ./configuration/config.ini\n");
354             } else {
355                 ci.print("controllerKeyStore=" + keyStoreFile + "\n");
356             }
357             if ((trustStoreFile == null) || trustStoreFile.trim().isEmpty()) {
358                 ci.print("controllerTrustStore not specified in ./configuration/config.ini\n");
359             } else {
360                 ci.print("controllerTrustStore=" + trustStoreFile + "\n");
361             }
362         } else {
363             ci.print("The Controller and Switch should communicate through TCP connetion.\n");
364         }
365     }
366
367     private void registerWithOSGIConsole() {
368         BundleContext bundleContext = FrameworkUtil.getBundle(this.getClass())
369                 .getBundleContext();
370         bundleContext.registerService(CommandProvider.class.getName(), this,
371                 null);
372     }
373
374     @Override
375     public String getHelp() {
376         StringBuffer help = new StringBuffer();
377         help.append("---Open Flow Controller---\n");
378         help.append("\t controllerShowSwitches\n");
379         help.append("\t controllerReset\n");
380         help.append("\t controllerShowConnConfig\n");
381         return help.toString();
382     }
383
384     @Override
385     public Status disconnect(Node node) {
386         ISwitch sw = getSwitch((Long) node.getID());
387         if (sw != null) disconnectSwitch(sw);
388         return new Status(StatusCode.SUCCESS);
389     }
390
391     @Override
392     public Node connect(String connectionIdentifier, Map<ConnectionConstants, String> params) {
393         return null;
394     }
395
396     /**
397      * View Change notification
398      */
399     public void notifyClusterViewChanged() {
400         for (ISwitch sw : switches.values()) {
401             notifySwitchAdded(sw);
402         }
403     }
404
405     /**
406      * Node Disconnected from the node's master controller.
407      */
408     @Override
409     public void notifyNodeDisconnectFromMaster(Node node) {
410         ISwitch sw = switches.get((Long)node.getID());
411         if (sw != null) notifySwitchAdded(sw);
412     }
413 }