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