Bug 1029: Remove dead code: samples/clustersession
[controller.git] / opendaylight / adsal / forwarding / staticrouting / src / main / java / org / opendaylight / controller / forwarding / staticrouting / internal / StaticRoutingImplementation.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.forwarding.staticrouting.internal;
10
11 import java.io.FileNotFoundException;
12 import java.io.IOException;
13 import java.io.ObjectInputStream;
14 import java.net.Inet4Address;
15 import java.net.InetAddress;
16 import java.nio.ByteBuffer;
17 import java.util.ArrayList;
18 import java.util.Collections;
19 import java.util.Dictionary;
20 import java.util.EnumSet;
21 import java.util.HashSet;
22 import java.util.Map;
23 import java.util.Set;
24 import java.util.Timer;
25 import java.util.TimerTask;
26 import java.util.concurrent.Callable;
27 import java.util.concurrent.ConcurrentMap;
28 import java.util.concurrent.ExecutorService;
29 import java.util.concurrent.Executors;
30 import java.util.concurrent.Future;
31 import java.util.regex.Matcher;
32 import java.util.regex.Pattern;
33
34 import org.apache.felix.dm.Component;
35 import org.opendaylight.controller.clustering.services.CacheConfigException;
36 import org.opendaylight.controller.clustering.services.CacheExistException;
37 import org.opendaylight.controller.clustering.services.IClusterContainerServices;
38 import org.opendaylight.controller.clustering.services.IClusterServices;
39 import org.opendaylight.controller.configuration.ConfigurationObject;
40 import org.opendaylight.controller.configuration.IConfigurationContainerAware;
41 import org.opendaylight.controller.configuration.IConfigurationContainerService;
42 import org.opendaylight.controller.forwarding.staticrouting.IForwardingStaticRouting;
43 import org.opendaylight.controller.forwarding.staticrouting.IStaticRoutingAware;
44 import org.opendaylight.controller.forwarding.staticrouting.StaticRoute;
45 import org.opendaylight.controller.forwarding.staticrouting.StaticRouteConfig;
46 import org.opendaylight.controller.hosttracker.HostIdFactory;
47 import org.opendaylight.controller.hosttracker.IHostId;
48 import org.opendaylight.controller.hosttracker.IfIptoHost;
49 import org.opendaylight.controller.hosttracker.IfNewHostNotify;
50 import org.opendaylight.controller.hosttracker.hostAware.HostNodeConnector;
51 import org.opendaylight.controller.sal.utils.IObjectReader;
52 import org.opendaylight.controller.sal.utils.Status;
53 import org.opendaylight.controller.sal.utils.StatusCode;
54 import org.slf4j.Logger;
55 import org.slf4j.LoggerFactory;
56
57 /**
58  * Static Routing feature provides the bridge between SDN and Non-SDN networks.
59  */
60 public class StaticRoutingImplementation implements IfNewHostNotify, IForwardingStaticRouting, IObjectReader,
61         IConfigurationContainerAware {
62     private static Logger log = LoggerFactory.getLogger(StaticRoutingImplementation.class);
63     private static final String STATIC_ROUTES_FILE_NAME = "staticRouting.conf";
64     ConcurrentMap<String, StaticRoute> staticRoutes;
65     ConcurrentMap<String, StaticRouteConfig> staticRouteConfigs;
66     private IfIptoHost hostTracker;
67     private Timer gatewayProbeTimer;
68     private IClusterContainerServices clusterContainerService = null;
69     private IConfigurationContainerService configurationService;
70     private Set<IStaticRoutingAware> staticRoutingAware = Collections
71             .synchronizedSet(new HashSet<IStaticRoutingAware>());
72     private ExecutorService executor;
73
74     void setStaticRoutingAware(IStaticRoutingAware s) {
75         if (this.staticRoutingAware != null) {
76             this.staticRoutingAware.add(s);
77         }
78     }
79
80     void unsetStaticRoutingAware(IStaticRoutingAware s) {
81         if (this.staticRoutingAware != null) {
82             this.staticRoutingAware.remove(s);
83         }
84     }
85
86     public void setHostTracker(IfIptoHost hostTracker) {
87         log.debug("Setting HostTracker");
88         this.hostTracker = hostTracker;
89     }
90
91     public void unsetHostTracker(IfIptoHost hostTracker) {
92         if (this.hostTracker == hostTracker) {
93             this.hostTracker = null;
94         }
95     }
96
97     public void setConfigurationContainerService(IConfigurationContainerService service) {
98         log.trace("Got configuration service set request {}", service);
99         this.configurationService = service;
100     }
101
102     public void unsetConfigurationContainerService(IConfigurationContainerService service) {
103         log.trace("Got configuration service UNset request");
104         this.configurationService = null;
105     }
106
107     @Override
108     public ConcurrentMap<String, StaticRouteConfig> getStaticRouteConfigs() {
109         return staticRouteConfigs;
110     }
111
112     @Override
113     public Object readObject(ObjectInputStream ois) throws FileNotFoundException, IOException, ClassNotFoundException {
114         // Perform the class deserialization locally, from inside the package
115         // where the class is defined
116         return ois.readObject();
117     }
118
119
120     private void loadConfiguration() {
121         for (ConfigurationObject conf : configurationService.retrieveConfiguration(this, STATIC_ROUTES_FILE_NAME)) {
122             addStaticRoute((StaticRouteConfig) conf);
123         }
124     }
125
126     private Status saveConfig() {
127         return saveConfigInternal();
128     }
129
130     public Status saveConfigInternal() {
131         Status status = configurationService.persistConfiguration(
132                 new ArrayList<ConfigurationObject>(staticRouteConfigs.values()), STATIC_ROUTES_FILE_NAME);
133
134         if (status.isSuccess()) {
135             return status;
136         } else {
137             return new Status(StatusCode.INTERNALERROR, "Save failed");
138         }
139     }
140
141     private void allocateCaches() {
142         if (this.clusterContainerService == null) {
143             log.trace("un-initialized clusterContainerService, can't create cache");
144             return;
145         }
146
147         try {
148             clusterContainerService.createCache("forwarding.staticrouting.routes",
149                     EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
150             clusterContainerService.createCache("forwarding.staticrouting.configs",
151                     EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
152         } catch (CacheExistException cee) {
153             log.error("\nCache already exists - destroy and recreate if needed");
154         } catch (CacheConfigException cce) {
155             log.error("\nCache configuration invalid - check cache mode");
156         }
157     }
158
159     @SuppressWarnings({ "unchecked" })
160     private void retrieveCaches() {
161         if (this.clusterContainerService == null) {
162             log.warn("un-initialized clusterContainerService, can't retrieve cache");
163             return;
164         }
165
166         staticRoutes = (ConcurrentMap<String, StaticRoute>) clusterContainerService
167                 .getCache("forwarding.staticrouting.routes");
168         if (staticRoutes == null) {
169             log.error("\nFailed to get rulesDB handle");
170         }
171
172         staticRouteConfigs = (ConcurrentMap<String, StaticRouteConfig>) clusterContainerService
173                 .getCache("forwarding.staticrouting.configs");
174         if (staticRouteConfigs == null) {
175             log.error("\nFailed to get rulesDB handle");
176         }
177     }
178
179     private void notifyStaticRouteUpdate(StaticRoute s, boolean update) {
180         if (this.staticRoutingAware != null) {
181             log.trace("Invoking StaticRoutingAware listeners");
182             synchronized (this.staticRoutingAware) {
183                 for (IStaticRoutingAware ra : this.staticRoutingAware) {
184                     try {
185                         ra.staticRouteUpdate(s, update);
186                     } catch (Exception e) {
187                         log.error("", e);
188                     }
189                 }
190             }
191         }
192     }
193
194     private class NotifyStaticRouteWorker implements Callable<Object> {
195
196         private String name;
197         private StaticRoute staticRoute;
198         private boolean added;
199
200         public NotifyStaticRouteWorker(String name, StaticRoute s, boolean update) {
201             this.name = name;
202             this.staticRoute = s;
203             this.added = update;
204         }
205
206         @Override
207         public Object call() throws Exception {
208             if (!added || (staticRoute.getType() == StaticRoute.NextHopType.SWITCHPORT)) {
209                 notifyStaticRouteUpdate(staticRoute, added);
210             } else {
211                 InetAddress nh = staticRoute.getNextHopAddress();
212                 // HostTracker hosts db key scheme implementation
213                 IHostId id = HostIdFactory.create(nh, null);
214                 HostNodeConnector host = hostTracker.hostQuery(id);
215                 if (host == null) {
216                     log.debug("Next hop {}  is not present, try to discover it", nh.getHostAddress());
217                     Future<HostNodeConnector> future = hostTracker.discoverHost(id);
218                     if (future != null) {
219                         try {
220                             host = future.get();
221                         } catch (InterruptedException ioe) {
222                             log.trace("Thread interrupted {}", ioe);
223                         } catch (Exception e) {
224                             log.error("", e);
225                         }
226                     }
227                 }
228                 if (host != null) {
229                     log.debug("Next hop {} is found", nh.getHostAddress());
230                     staticRoute.setHost(host);
231                     // static route object has changed
232                     // put the changed object back in the cache
233                     // for it to sync
234                     staticRoutes.put(name, staticRoute);
235                     notifyStaticRouteUpdate(staticRoute, added);
236                 } else {
237                     log.debug("Next hop {}  is still not present, try again later", nh.getHostAddress());
238                 }
239             }
240             return null;
241         }
242     }
243
244     private void checkAndUpdateListeners(String name, StaticRoute staticRoute, boolean added) {
245         NotifyStaticRouteWorker worker = new NotifyStaticRouteWorker(name, staticRoute, added);
246         try {
247             executor.submit(worker);
248         } catch (Exception e) {
249             log.error("got Exception ", e);
250         }
251     }
252
253     private void notifyHostUpdate(HostNodeConnector host, boolean added) {
254         if (host == null) {
255             return;
256         }
257         for (Map.Entry<String, StaticRoute> s : staticRoutes.entrySet()) {
258             StaticRoute route = s.getValue();
259             if (route.getType() == StaticRoute.NextHopType.SWITCHPORT) {
260                 continue;
261             }
262             if (route.getNextHopAddress().equals(host.getNetworkAddress())) {
263                 if (added) {
264                     route.setHost(host);
265                 } else {
266                     route.setHost(null);
267                 }
268                 // static route object has changed
269                 // put the changed object back in the cache
270                 // for it to sync
271                 staticRoutes.put(s.getKey(), route);
272                 notifyStaticRouteUpdate(route, added);
273             }
274         }
275     }
276
277     @Override
278     public void notifyHTClient(HostNodeConnector host) {
279         notifyHostUpdate(host, true);
280     }
281
282     @Override
283     public void notifyHTClientHostRemoved(HostNodeConnector host) {
284         notifyHostUpdate(host, false);
285     }
286
287     public boolean isIPv4AddressValid(String cidr) {
288         if (cidr == null) {
289             return false;
290         }
291
292         String values[] = cidr.split("/");
293         Pattern ipv4Pattern = Pattern
294                 .compile("(([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.){3}([01]?\\d\\d?|2[0-4]\\d|25[0-5])");
295         Matcher mm = ipv4Pattern.matcher(values[0]);
296         if (!mm.matches()) {
297             log.debug("IPv4 source address {} is not valid", cidr);
298             return false;
299         }
300         if (values.length >= 2) {
301             int prefix = Integer.valueOf(values[1]);
302             if ((prefix < 0) || (prefix > 32)) {
303                 log.debug("prefix {} is not valid", prefix);
304                 return false;
305             }
306         }
307         return true;
308     }
309
310     public static short getUnsignedByte(ByteBuffer bb, int position) {
311         return ((short) (bb.get(position) & (short) 0xff));
312     }
313
314     public static int compareByteBuffers(ByteBuffer buf1, ByteBuffer buf2) {
315         for (int i = 0; i < buf1.array().length; i++) {
316             if (getUnsignedByte(buf1, i) > getUnsignedByte(buf2, i)) {
317                 return 1;
318             } else if (getUnsignedByte(buf1, i) < getUnsignedByte(buf2, i)) {
319                 return -1;
320             }
321         }
322
323         return 0;
324     }
325
326     @Override
327     public StaticRoute getBestMatchStaticRoute(InetAddress ipAddress) {
328         ByteBuffer bblongestPrefix = null;
329         try {
330             bblongestPrefix = ByteBuffer.wrap(InetAddress.getByName("0.0.0.0").getAddress());
331         } catch (Exception e) {
332             return null;
333         }
334
335         if (staticRoutes == null) {
336             return null;
337         }
338
339         StaticRoute longestPrefixRoute = null;
340         for (StaticRoute s : staticRoutes.values()) {
341             InetAddress prefix = s.longestPrefixMatch(ipAddress);
342             if ((prefix != null) && (prefix instanceof Inet4Address)) {
343                 ByteBuffer bbtmp = ByteBuffer.wrap(prefix.getAddress());
344                 if (compareByteBuffers(bbtmp, bblongestPrefix) > 0) {
345                     bblongestPrefix = bbtmp;
346                     longestPrefixRoute = s;
347                 }
348             }
349         }
350         return longestPrefixRoute;
351     }
352
353     @Override
354     public Status addStaticRoute(StaticRouteConfig config) {
355         Status status = config.isValid();
356         if (!status.isSuccess()) {
357             return status;
358         }
359         if (staticRouteConfigs.get(config.getName()) != null) {
360             return new Status(StatusCode.CONFLICT, "A valid Static Route configuration with this name "
361                     + "already exists. Please use a different name");
362         }
363
364         // Update database
365         StaticRoute sRoute = new StaticRoute(config);
366
367         for (Map.Entry<String, StaticRoute> entry : staticRoutes.entrySet()) {
368             if (entry.getValue().compareTo(sRoute) == 0) {
369                 return new Status(StatusCode.CONFLICT, "This conflicts with an existing Static Route "
370                         + "Configuration. Please check the configuration " + "and try again");
371             }
372         }
373         staticRoutes.put(config.getName(), sRoute);
374
375         // Update config databse
376         staticRouteConfigs.put(config.getName(), config);
377
378         // Notify
379         checkAndUpdateListeners(config.getName(), sRoute, true);
380         return status;
381     }
382
383     @Override
384     public Status removeStaticRoute(String name) {
385         staticRouteConfigs.remove(name);
386         StaticRoute sRoute = staticRoutes.remove(name);
387         if (sRoute != null) {
388             checkAndUpdateListeners(name, sRoute, false);
389             return new Status(StatusCode.SUCCESS, null);
390         }
391         return new Status(StatusCode.NOTFOUND, "Static Route with name " + name + " is not found");
392     }
393
394     void setClusterContainerService(IClusterContainerServices s) {
395         log.debug("Cluster Service set");
396         this.clusterContainerService = s;
397     }
398
399     void unsetClusterContainerService(IClusterContainerServices s) {
400         if (this.clusterContainerService == s) {
401             log.debug("Cluster Service removed!");
402             this.clusterContainerService = null;
403         }
404     }
405
406     /**
407      * Function called by the dependency manager when all the required
408      * dependencies are satisfied
409      *
410      */
411     void init(Component c) {
412         String containerName = null;
413         Dictionary props = c.getServiceProperties();
414         if (props != null) {
415             containerName = (String) props.get("containerName");
416         } else {
417             // In the Global instance case the containerName is empty
418             containerName = "";
419         }
420
421         log.debug("forwarding.staticrouting starting on container {}", containerName);
422         allocateCaches();
423         retrieveCaches();
424         this.executor = Executors.newFixedThreadPool(1);
425         loadConfiguration();
426
427         /*
428          * Slow probe to identify any gateway that might have silently appeared
429          * after the Static Routing Configuration.
430          */
431         gatewayProbeTimer = new Timer();
432         gatewayProbeTimer.schedule(new TimerTask() {
433             @Override
434             public void run() {
435                 for (Map.Entry<String, StaticRoute> s : staticRoutes.entrySet()) {
436                     StaticRoute route = s.getValue();
437                     if ((route.getType() == StaticRoute.NextHopType.IPADDRESS) && route.getHost() == null) {
438                         checkAndUpdateListeners(s.getKey(), route, true);
439                     }
440                 }
441             }
442         }, 60 * 1000, 60 * 1000);
443
444     }
445
446     /**
447      * Function called by the dependency manager when at least one dependency
448      * become unsatisfied or when the component is shutting down because for
449      * example bundle is being stopped.
450      *
451      */
452     void destroy() {
453         log.debug("Destroy all the Static Routing Rules given we are " + "shutting down");
454
455         gatewayProbeTimer.cancel();
456
457         // Clear the listener so to be ready in next life
458         this.staticRoutingAware.clear();
459     }
460
461     /**
462      * Function called by dependency manager after "init ()" is called and after
463      * the services provided by the class are registered in the service registry
464      *
465      */
466     void start() {
467     }
468
469     /**
470      * Function called by the dependency manager before the services exported by
471      * the component are unregistered, this will be followed by a "destroy ()"
472      * calls
473      *
474      */
475     void stop() {
476         executor.shutdown();
477     }
478
479     @Override
480     public Status saveConfiguration() {
481         return this.saveConfig();
482     }
483
484 }