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