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