Added support for annotations in generated APIs.
[controller.git] / opendaylight / protocol_plugins / openflow / src / main / java / org / opendaylight / controller / protocol_plugin / openflow / core / internal / Controller.java
1
2 /*
3  * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
4  *
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
8  */
9
10 package org.opendaylight.controller.protocol_plugin.openflow.core.internal;
11
12 import java.io.IOException;
13 import java.nio.channels.SelectionKey;
14 import java.nio.channels.Selector;
15 import java.nio.channels.ServerSocketChannel;
16 import java.nio.channels.SocketChannel;
17 import java.util.Date;
18 import java.util.Iterator;
19 import java.util.Map;
20 import java.util.Map.Entry;
21 import java.util.Set;
22 import java.util.concurrent.BlockingQueue;
23 import java.util.concurrent.ConcurrentHashMap;
24 import java.util.concurrent.ConcurrentMap;
25 import java.util.concurrent.LinkedBlockingQueue;
26 import java.util.concurrent.atomic.AtomicInteger;
27
28 import org.eclipse.osgi.framework.console.CommandInterpreter;
29 import org.eclipse.osgi.framework.console.CommandProvider;
30 import org.opendaylight.controller.protocol_plugin.openflow.core.IController;
31 import org.opendaylight.controller.protocol_plugin.openflow.core.IMessageListener;
32 import org.opendaylight.controller.protocol_plugin.openflow.core.ISwitch;
33 import org.opendaylight.controller.protocol_plugin.openflow.core.ISwitchStateListener;
34 import org.openflow.protocol.OFMessage;
35 import org.openflow.protocol.OFType;
36 import org.openflow.util.HexString;
37 import org.osgi.framework.BundleContext;
38 import org.osgi.framework.FrameworkUtil;
39 import org.slf4j.Logger;
40 import org.slf4j.LoggerFactory;
41
42 public class Controller implements IController, CommandProvider {
43     private static final Logger logger = LoggerFactory
44             .getLogger(Controller.class);
45     private ControllerIO controllerIO;
46     private Thread switchEventThread;
47     private ConcurrentHashMap<Long, ISwitch> switches;
48     private BlockingQueue<SwitchEvent> switchEvents;
49     // only 1 message listener per OFType
50     private ConcurrentMap<OFType, IMessageListener> messageListeners;
51     // only 1 switch state listener
52     private ISwitchStateListener switchStateListener;
53     private AtomicInteger switchInstanceNumber;
54
55     /*
56      * this thread monitors the switchEvents queue for new incoming events from switch
57      */
58     private class EventHandler implements Runnable {
59         @Override
60         public void run() {
61
62             while (true) {
63                 try {
64                     SwitchEvent ev = switchEvents.take();
65                     SwitchEvent.SwitchEventType eType = ev.getEventType();
66                     ISwitch sw = ev.getSwitch();
67                     if (eType != SwitchEvent.SwitchEventType.SWITCH_MESSAGE) {
68                         //logger.debug("Received " + ev.toString() + " from " + sw.toString());
69                     }
70                     switch (eType) {
71                     case SWITCH_ADD:
72                         Long sid = sw.getId();
73                         ISwitch existingSwitch = switches.get(sid);
74                         if (existingSwitch != null) {
75                             logger.info(" Replacing existing "
76                                     + existingSwitch.toString() + " with New "
77                                     + sw.toString());
78                             disconnectSwitch(existingSwitch);
79                         }
80                         switches.put(sid, sw);
81                         notifySwitchAdded(sw);
82                         break;
83                     case SWITCH_DELETE:
84                         disconnectSwitch(sw);
85                         break;
86                     case SWITCH_ERROR:
87                         disconnectSwitch(sw);
88                         break;
89                     case SWITCH_MESSAGE:
90                         OFMessage msg = ev.getMsg();
91                         if (msg != null) {
92                             IMessageListener listener = messageListeners
93                                     .get(msg.getType());
94                             if (listener != null) {
95                                 listener.receive(sw, msg);
96                             }
97                         }
98                         break;
99                     default:
100                         logger.error("unknow switch event " + eType.ordinal());
101                     }
102                 } catch (InterruptedException e) {
103                     switchEvents.clear();
104                     return;
105                 }
106             }
107         }
108
109     }
110
111     /**
112      * Function called by the dependency manager when all the required
113      * dependencies are satisfied
114      *
115      */
116     public void init() {
117         logger.debug("OpenFlowCore init");
118         this.switches = new ConcurrentHashMap<Long, ISwitch>();
119         this.switchEvents = new LinkedBlockingQueue<SwitchEvent>();
120         this.messageListeners = new ConcurrentHashMap<OFType, IMessageListener>();
121         this.switchStateListener = null;
122         this.switchInstanceNumber = new AtomicInteger(0);
123         registerWithOSGIConsole();
124     }
125
126     /**
127      * Function called by dependency manager after "init ()" is called
128      * and after the services provided by the class are registered in
129      * the service registry
130      *
131      */
132     public void start() {
133         logger.debug("OpenFlowCore start() is called");
134         /*
135          * start a thread to handle event coming from the switch
136          */
137         switchEventThread = new Thread(new EventHandler(), "SwitchEvent Thread");
138         switchEventThread.start();
139
140         // spawn a thread to start to listen on the open flow port
141         controllerIO = new ControllerIO(this);
142         try {
143             controllerIO.start();
144         } catch (IOException ex) {
145             logger.error("Caught exception: " + ex + " during start");
146         }
147     }
148     
149     /**
150      * Function called by the dependency manager before the services
151      * exported by the component are unregistered, this will be
152      * followed by a "destroy ()" calls
153      *
154      */
155     public void stop() {
156         for (Iterator<Entry<Long, ISwitch>> it = switches.entrySet().iterator(); it
157                 .hasNext();) {
158             Entry<Long, ISwitch> entry = it.next();
159             ((SwitchHandler) entry.getValue()).stop();
160             it.remove();
161         }
162         switchEventThread.interrupt();
163         try {
164                 controllerIO.shutDown();
165         } catch (IOException ex) {
166                 logger.error("Caught exception: " + ex + " during stop");
167         }
168     }
169
170     /**
171      * Function called by the dependency manager when at least one
172      * dependency become unsatisfied or when the component is shutting
173      * down because for example bundle is being stopped.
174      *
175      */
176     public void destroy() {
177     }
178
179     @Override
180     public void addMessageListener(OFType type, IMessageListener listener) {
181         IMessageListener currentListener = this.messageListeners.get(type);
182         if (currentListener != null) {
183             logger.warn(type.toString() + " already listened by "
184                     + currentListener.toString());
185         }
186         this.messageListeners.put(type, listener);
187         logger.debug(type.toString() + " is now listened by "
188                 + listener.toString());
189     }
190
191     @Override
192     public void removeMessageListener(OFType type, IMessageListener listener) {
193         IMessageListener currentListener = this.messageListeners.get(type);
194         if ((currentListener != null) && (currentListener == listener)) {
195             this.messageListeners.remove(type);
196         }
197     }
198
199     @Override
200     public void addSwitchStateListener(ISwitchStateListener listener) {
201         if (this.switchStateListener != null) {
202             logger.warn(this.switchStateListener.toString()
203                     + "already listened to switch events");
204         }
205         this.switchStateListener = listener;
206         logger.debug(listener.toString() + " now listens to switch events");
207     }
208
209     @Override
210     public void removeSwitchStateListener(ISwitchStateListener listener) {
211         if ((this.switchStateListener != null)
212                 && (this.switchStateListener == listener)) {
213             this.switchStateListener = null;
214         }
215     }
216
217     public void handleNewConnection(Selector selector,
218             SelectionKey serverSelectionKey) {
219         ServerSocketChannel ssc = (ServerSocketChannel) serverSelectionKey
220                 .channel();
221         SocketChannel sc = null;
222         try {
223             sc = ssc.accept();
224             // create new switch
225             int i = this.switchInstanceNumber.addAndGet(1);
226             String instanceName = "SwitchHandler-" + i;
227             SwitchHandler switchHandler = new SwitchHandler(this, sc,
228                     instanceName);
229             switchHandler.start();
230             logger.info(instanceName + " connected: " + sc.toString());
231         } catch (IOException e) {
232             return;
233         }
234     }
235
236     private void disconnectSwitch(ISwitch sw) {
237         if (((SwitchHandler) sw).isOperational()) {
238             Long sid = sw.getId();
239             if (this.switches.remove(sid, sw)) {
240                 logger.warn(sw.toString() + " is disconnected");
241                 notifySwitchDeleted(sw);
242             } else {
243                 //logger.warn(sw.toString() + " has been replaced by " +
244                 //      this.switches.get(sid));
245             }
246         }
247         ((SwitchHandler) sw).stop();
248         sw = null;
249     }
250
251     private void notifySwitchAdded(ISwitch sw) {
252         if (switchStateListener != null) {
253             switchStateListener.switchAdded(sw);
254         }
255     }
256
257     private void notifySwitchDeleted(ISwitch sw) {
258         if (switchStateListener != null) {
259             switchStateListener.switchDeleted(sw);
260         }
261     }
262
263     private synchronized void addSwitchEvent(SwitchEvent event) {
264         try {
265             this.switchEvents.put(event);
266         } catch (InterruptedException e) {
267             logger.debug("SwitchEvent caught Interrupt Exception");
268         }
269     }
270
271     public void takeSwtichEventAdd(ISwitch sw) {
272         SwitchEvent ev = new SwitchEvent(
273                 SwitchEvent.SwitchEventType.SWITCH_ADD, sw, null);
274         addSwitchEvent(ev);
275     }
276
277     public void takeSwitchEventDelete(ISwitch sw) {
278         SwitchEvent ev = new SwitchEvent(
279                 SwitchEvent.SwitchEventType.SWITCH_DELETE, sw, null);
280         addSwitchEvent(ev);
281     }
282
283     public void takeSwitchEventError(ISwitch sw) {
284         SwitchEvent ev = new SwitchEvent(
285                 SwitchEvent.SwitchEventType.SWITCH_ERROR, sw, null);
286         addSwitchEvent(ev);
287     }
288
289     public void takeSwitchEventMsg(ISwitch sw, OFMessage msg) {
290         if (messageListeners.get(msg.getType()) != null) {
291             SwitchEvent ev = new SwitchEvent(
292                     SwitchEvent.SwitchEventType.SWITCH_MESSAGE, sw, msg);
293             addSwitchEvent(ev);
294         }
295     }
296
297     @Override
298     public Map<Long, ISwitch> getSwitches() {
299         return this.switches;
300     }
301
302     @Override
303     public ISwitch getSwitch(Long switchId) {
304         return this.switches.get(switchId);
305     }
306
307     public void _controllerShowSwitches(CommandInterpreter ci) {
308         Set<Long> sids = switches.keySet();
309         StringBuffer s = new StringBuffer();
310         int size = sids.size();
311         if (size == 0) {
312             ci.print("switches: empty");
313             return;
314         }
315         Iterator<Long> iter = sids.iterator();
316         s.append("Total: " + size + " switches\n");
317         while (iter.hasNext()) {
318             Long sid = iter.next();
319             Date date = switches.get(sid).getConnectedDate();
320             String switchInstanceName = ((SwitchHandler) switches.get(sid)).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 }