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