32cdeaa614e55264e397085c1e9ff05174fd6aff
[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.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;
20 import java.util.Map;
21 import java.util.Map.Entry;
22 import java.util.Set;
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;
28
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;
42
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;
55
56     /*
57      * this thread monitors the switchEvents queue for new incoming events from 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                     if (eType != SwitchEvent.SwitchEventType.SWITCH_MESSAGE) {
69                         //logger.debug("Received " + ev.toString() + " from " + sw.toString());
70                     }
71                     switch (eType) {
72                     case SWITCH_ADD:
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 "
78                                     + sw.toString());
79                             disconnectSwitch(existingSwitch);
80                         }
81                         switches.put(sid, sw);
82                         notifySwitchAdded(sw);
83                         break;
84                     case SWITCH_DELETE:
85                         disconnectSwitch(sw);
86                         break;
87                     case SWITCH_ERROR:
88                         disconnectSwitch(sw);
89                         break;
90                     case SWITCH_MESSAGE:
91                         OFMessage msg = ev.getMsg();
92                         if (msg != null) {
93                             IMessageListener listener = messageListeners
94                                     .get(msg.getType());
95                             if (listener != null) {
96                                 listener.receive(sw, msg);
97                             }
98                         }
99                         break;
100                     default:
101                         logger.error("unknow switch event " + eType.ordinal());
102                     }
103                 } catch (InterruptedException e) {
104                     switchEvents.clear();
105                     return;
106                 }
107             }
108         }
109
110     }
111
112     /**
113      * Function called by the dependency manager when all the required
114      * dependencies are satisfied
115      *
116      */
117     public void init() {
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();
125
126     }
127
128     /**
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
132      *
133      */
134     public void start() {
135         logger.debug("OpenFlowCore start() is called");
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: " + ex + " during start");
148         }
149     }
150
151     /**
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
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: " + ex + " during stop");
169         }
170     }
171
172     /**
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.
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(type.toString() + " already listened by "
186                     + currentListener.toString());
187         }
188         this.messageListeners.put(type, listener);
189         logger.debug(type.toString() + " is now listened by "
190                 + listener.toString());
191     }
192
193     @Override
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);
198         }
199     }
200
201     @Override
202     public void addSwitchStateListener(ISwitchStateListener listener) {
203         if (this.switchStateListener != null) {
204             logger.warn(this.switchStateListener.toString()
205                     + "already listened to switch events");
206         }
207         this.switchStateListener = listener;
208         logger.debug(listener.toString() + " now listens to switch events");
209     }
210
211     @Override
212     public void removeSwitchStateListener(ISwitchStateListener listener) {
213         if ((this.switchStateListener != null)
214                 && (this.switchStateListener == listener)) {
215             this.switchStateListener = null;
216         }
217     }
218
219     public void handleNewConnection(Selector selector,
220             SelectionKey serverSelectionKey) {
221         ServerSocketChannel ssc = (ServerSocketChannel) serverSelectionKey
222                 .channel();
223         SocketChannel sc = null;
224         try {
225             sc = ssc.accept();
226             // create new switch
227             int i = this.switchInstanceNumber.addAndGet(1);
228             String instanceName = "SwitchHandler-" + i;
229             SwitchHandler switchHandler = new SwitchHandler(this, sc,
230                     instanceName);
231             switchHandler.start();
232             logger.info(instanceName + " connected: " + sc.toString());
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(sw.toString() + " is disconnected");
243                 notifySwitchDeleted(sw);
244             } else {
245                 //logger.warn(sw.toString() + " has been replaced by " +
246                 //      this.switches.get(sid));
247             }
248         }
249         ((SwitchHandler) sw).stop();
250         sw = null;
251     }
252
253     private void notifySwitchAdded(ISwitch sw) {
254         if (switchStateListener != null) {
255             switchStateListener.switchAdded(sw);
256         }
257     }
258
259     private void notifySwitchDeleted(ISwitch sw) {
260         if (switchStateListener != null) {
261             switchStateListener.switchDeleted(sw);
262         }
263     }
264
265     private synchronized void addSwitchEvent(SwitchEvent event) {
266         try {
267             this.switchEvents.put(event);
268         } catch (InterruptedException e) {
269             logger.debug("SwitchEvent caught Interrupt Exception");
270         }
271     }
272
273     public void takeSwtichEventAdd(ISwitch sw) {
274         SwitchEvent ev = new SwitchEvent(
275                 SwitchEvent.SwitchEventType.SWITCH_ADD, sw, null);
276         addSwitchEvent(ev);
277     }
278
279     public void takeSwitchEventDelete(ISwitch sw) {
280         SwitchEvent ev = new SwitchEvent(
281                 SwitchEvent.SwitchEventType.SWITCH_DELETE, sw, null);
282         addSwitchEvent(ev);
283     }
284
285     public void takeSwitchEventError(ISwitch sw) {
286         SwitchEvent ev = new SwitchEvent(
287                 SwitchEvent.SwitchEventType.SWITCH_ERROR, sw, null);
288         addSwitchEvent(ev);
289     }
290
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);
295             addSwitchEvent(ev);
296         }
297     }
298
299     @Override
300     public Map<Long, ISwitch> getSwitches() {
301         return this.switches;
302     }
303
304     @Override
305     public ISwitch getSwitch(Long switchId) {
306         return this.switches.get(switchId);
307     }
308
309     public void _controllerShowSwitches(CommandInterpreter ci) {
310         Set<Long> sids = switches.keySet();
311         StringBuffer s = new StringBuffer();
312         int size = sids.size();
313         if (size == 0) {
314             ci.print("switches: empty");
315             return;
316         }
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");
325         }
326         ci.print(s.toString());
327         return;
328     }
329
330     public void _controllerReset(CommandInterpreter ci) {
331         ci.print("...Disconnecting the communication to all switches...\n");
332         stop();
333         try {
334             Thread.sleep(1000);
335         } catch (InterruptedException ie) {
336         } finally {
337             ci.print("...start to accept connections from switches...\n");
338             start();
339         }
340     }
341
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");          
346
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");                                   
351                 } else {
352                 ci.print("controllerKeyStore=" + keyStoreFile + "\n");          
353                 }
354                 if ((trustStoreFile == null) || trustStoreFile.trim().isEmpty()) {      
355                 ci.print("controllerTrustStore not specified in ./configuration/config.ini\n");                                                         
356                 } else {
357                 ci.print("controllerTrustStore=" + trustStoreFile + "\n");              
358                 }
359         } else {
360             ci.print("The Controller and Switch should communicate through TCP connetion.\n");          
361         }
362     }
363
364     private void registerWithOSGIConsole() {
365         BundleContext bundleContext = FrameworkUtil.getBundle(this.getClass())
366                 .getBundleContext();
367         bundleContext.registerService(CommandProvider.class.getName(), this,
368                 null);
369     }
370
371     @Override
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();
379     }
380 }