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