Merge "Bug 164"
[controller.git] / opendaylight / forwarding / staticrouting / src / main / java / org / opendaylight / controller / forwarding / staticrouting / internal / StaticRoutingImplementation.java
index fd043fd0ac321d4cc52ce294676b5558ee72fac4..2ce2465d976e28a82a7a3bc8e66ddc9df144300c 100644 (file)
@@ -1,4 +1,3 @@
-
 /*
  * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
  *
@@ -16,7 +15,6 @@ import java.net.Inet4Address;
 import java.net.InetAddress;
 import java.nio.ByteBuffer;
 import java.util.Collections;
-import java.util.Date;
 import java.util.Dictionary;
 import java.util.EnumSet;
 import java.util.HashSet;
@@ -24,8 +22,11 @@ import java.util.Map;
 import java.util.Set;
 import java.util.Timer;
 import java.util.TimerTask;
+import java.util.concurrent.Callable;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
 import java.util.concurrent.Future;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -33,7 +34,6 @@ import java.util.regex.Pattern;
 import org.apache.felix.dm.Component;
 import org.opendaylight.controller.clustering.services.CacheConfigException;
 import org.opendaylight.controller.clustering.services.CacheExistException;
-import org.opendaylight.controller.clustering.services.ICacheUpdateAware;
 import org.opendaylight.controller.clustering.services.IClusterContainerServices;
 import org.opendaylight.controller.clustering.services.IClusterServices;
 import org.opendaylight.controller.configuration.IConfigurationContainerAware;
@@ -41,40 +41,36 @@ import org.opendaylight.controller.forwarding.staticrouting.IForwardingStaticRou
 import org.opendaylight.controller.forwarding.staticrouting.IStaticRoutingAware;
 import org.opendaylight.controller.forwarding.staticrouting.StaticRoute;
 import org.opendaylight.controller.forwarding.staticrouting.StaticRouteConfig;
+import org.opendaylight.controller.hosttracker.HostIdFactory;
+import org.opendaylight.controller.hosttracker.IHostId;
 import org.opendaylight.controller.hosttracker.IfIptoHost;
 import org.opendaylight.controller.hosttracker.IfNewHostNotify;
 import org.opendaylight.controller.hosttracker.hostAware.HostNodeConnector;
-import org.opendaylight.controller.sal.utils.StatusCode;
 import org.opendaylight.controller.sal.utils.GlobalConstants;
 import org.opendaylight.controller.sal.utils.IObjectReader;
 import org.opendaylight.controller.sal.utils.ObjectReader;
 import org.opendaylight.controller.sal.utils.ObjectWriter;
 import org.opendaylight.controller.sal.utils.Status;
+import org.opendaylight.controller.sal.utils.StatusCode;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 /**
  * Static Routing feature provides the bridge between SDN and Non-SDN networks.
- *
- *
- *
  */
-public class StaticRoutingImplementation implements IfNewHostNotify,
-        IForwardingStaticRouting, IObjectReader, IConfigurationContainerAware,
-        ICacheUpdateAware<Long, String> {
-    private static Logger log = LoggerFactory
-            .getLogger(StaticRoutingImplementation.class);
+public class StaticRoutingImplementation implements IfNewHostNotify, IForwardingStaticRouting, IObjectReader,
+        IConfigurationContainerAware {
+    private static Logger log = LoggerFactory.getLogger(StaticRoutingImplementation.class);
     private static String ROOT = GlobalConstants.STARTUPHOME.toString();
-    private static final String SAVE = "Save";
     ConcurrentMap<String, StaticRoute> staticRoutes;
     ConcurrentMap<String, StaticRouteConfig> staticRouteConfigs;
     private IfIptoHost hostTracker;
     private Timer gatewayProbeTimer;
     private String staticRoutesFileName = null;
-    private Map<Long, String> configSaveEvent;
     private IClusterContainerServices clusterContainerService = null;
     private Set<IStaticRoutingAware> staticRoutingAware = Collections
             .synchronizedSet(new HashSet<IStaticRoutingAware>());
+    private ExecutorService executor;
 
     void setStaticRoutingAware(IStaticRoutingAware s) {
         if (this.staticRoutingAware != null) {
@@ -99,18 +95,13 @@ public class StaticRoutingImplementation implements IfNewHostNotify,
         }
     }
 
+    @Override
     public ConcurrentMap<String, StaticRouteConfig> getStaticRouteConfigs() {
         return staticRouteConfigs;
     }
 
-    public void setStaticRouteConfigs(
-            ConcurrentMap<String, StaticRouteConfig> staticRouteConfigs) {
-        this.staticRouteConfigs = staticRouteConfigs;
-    }
-
     @Override
-    public Object readObject(ObjectInputStream ois)
-            throws FileNotFoundException, IOException, ClassNotFoundException {
+    public Object readObject(ObjectInputStream ois) throws FileNotFoundException, IOException, ClassNotFoundException {
         // Perform the class deserialization locally, from inside the package
         // where the class is defined
         return ois.readObject();
@@ -119,8 +110,8 @@ public class StaticRoutingImplementation implements IfNewHostNotify,
     @SuppressWarnings("unchecked")
     private void loadConfiguration() {
         ObjectReader objReader = new ObjectReader();
-        ConcurrentMap<String, StaticRouteConfig> confList = (ConcurrentMap<String, StaticRouteConfig>) objReader
-                .read(this, staticRoutesFileName);
+        ConcurrentMap<String, StaticRouteConfig> confList = (ConcurrentMap<String, StaticRouteConfig>) objReader.read(
+                this, staticRoutesFileName);
 
         if (confList == null) {
             return;
@@ -131,10 +122,7 @@ public class StaticRoutingImplementation implements IfNewHostNotify,
         }
     }
 
-    @Override
-    public Status saveConfig() {
-        // Publish the save config event to the cluster nodes
-        configSaveEvent.put(new Date().getTime(), SAVE);
+    private Status saveConfig() {
         return saveConfigInternal();
     }
 
@@ -142,9 +130,8 @@ public class StaticRoutingImplementation implements IfNewHostNotify,
         Status status;
         ObjectWriter objWriter = new ObjectWriter();
 
-        status = objWriter.write(
-                new ConcurrentHashMap<String, StaticRouteConfig>(
-                        staticRouteConfigs), staticRoutesFileName);
+        status = objWriter.write(new ConcurrentHashMap<String, StaticRouteConfig>(staticRouteConfigs),
+                staticRoutesFileName);
 
         if (status.isSuccess()) {
             return status;
@@ -154,27 +141,19 @@ public class StaticRoutingImplementation implements IfNewHostNotify,
     }
 
     @SuppressWarnings("deprecation")
-       private void allocateCaches() {
+    private void allocateCaches() {
         if (this.clusterContainerService == null) {
-            log
-                    .info("un-initialized clusterContainerService, can't create cache");
+            log.info("un-initialized clusterContainerService, can't create cache");
             return;
         }
 
         try {
-            clusterContainerService.createCache(
-                    "forwarding.staticrouting.routes", EnumSet
-                            .of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
-            clusterContainerService.createCache(
-                    "forwarding.staticrouting.configs", EnumSet
-                            .of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
-            clusterContainerService.createCache(
-                    "forwarding.staticrouting.configSaveEvent", EnumSet
-                            .of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
-
+            clusterContainerService.createCache("forwarding.staticrouting.routes",
+                    EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
+            clusterContainerService.createCache("forwarding.staticrouting.configs",
+                    EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
         } catch (CacheExistException cee) {
-            log
-                    .error("\nCache already exists - destroy and recreate if needed");
+            log.error("\nCache already exists - destroy and recreate if needed");
         } catch (CacheConfigException cce) {
             log.error("\nCache configuration invalid - check cache mode");
         }
@@ -183,8 +162,7 @@ public class StaticRoutingImplementation implements IfNewHostNotify,
     @SuppressWarnings({ "unchecked", "deprecation" })
     private void retrieveCaches() {
         if (this.clusterContainerService == null) {
-            log
-                    .info("un-initialized clusterContainerService, can't retrieve cache");
+            log.info("un-initialized clusterContainerService, can't retrieve cache");
             return;
         }
 
@@ -199,41 +177,6 @@ public class StaticRoutingImplementation implements IfNewHostNotify,
         if (staticRouteConfigs == null) {
             log.error("\nFailed to get rulesDB handle");
         }
-        configSaveEvent = (ConcurrentMap<Long, String>) clusterContainerService
-                .getCache("forwarding.staticrouting.configSaveEvent");
-        if (configSaveEvent == null) {
-            log.error("\nFailed to get cache for configSaveEvent");
-        }
-    }
-
-    @SuppressWarnings("deprecation")
-       private void destroyCaches() {
-        if (this.clusterContainerService == null) {
-            log
-                    .info("un-initialized clusterContainerService, can't destroy cache");
-            return;
-        }
-
-        clusterContainerService.destroyCache("forwarding.staticrouting.routes");
-        clusterContainerService
-                .destroyCache("forwarding.staticrouting.configs");
-        clusterContainerService
-                .destroyCache("forwarding.staticrouting.configSaveEvent");
-
-    }
-
-    @Override
-    public void entryCreated(Long key, String cacheName, boolean local) {
-    }
-
-    @Override
-    public void entryUpdated(Long key, String new_value, String cacheName,
-            boolean originLocal) {
-        saveConfigInternal();
-    }
-
-    @Override
-    public void entryDeleted(Long key, String cacheName, boolean originLocal) {
     }
 
     private void notifyStaticRouteUpdate(StaticRoute s, boolean update) {
@@ -244,65 +187,92 @@ public class StaticRoutingImplementation implements IfNewHostNotify,
                     try {
                         ra.staticRouteUpdate(s, update);
                     } catch (Exception e) {
-                        e.printStackTrace();
+                        log.error("", e);
                     }
                 }
             }
         }
     }
 
-    private class NotifyStaticRouteThread extends Thread {
+    private class NotifyStaticRouteWorker implements Callable<Object> {
+
+        private String name;
         private StaticRoute staticRoute;
         private boolean added;
 
-        public NotifyStaticRouteThread(StaticRoute s, boolean update) {
+        public NotifyStaticRouteWorker(String name, StaticRoute s, boolean update) {
+            this.name = name;
             this.staticRoute = s;
             this.added = update;
         }
 
-        public void run() {
-            if (!added
-                    || (staticRoute.getType() == StaticRoute.NextHopType.SWITCHPORT)) {
+        @Override
+        public Object call() throws Exception {
+            if (!added || (staticRoute.getType() == StaticRoute.NextHopType.SWITCHPORT)) {
                 notifyStaticRouteUpdate(staticRoute, added);
             } else {
-                HostNodeConnector host = hostTracker.hostQuery(staticRoute
-                        .getNextHopAddress());
+                InetAddress nh = staticRoute.getNextHopAddress();
+                // HostTracker hosts db key scheme implementation
+                IHostId id = HostIdFactory.create(nh, null);
+                HostNodeConnector host = hostTracker.hostQuery(id);
                 if (host == null) {
-                    Future<HostNodeConnector> future = hostTracker
-                            .discoverHost(staticRoute.getNextHopAddress());
+                    log.debug("Next hop {}  is not present, try to discover it", nh.getHostAddress());
+                    Future<HostNodeConnector> future = hostTracker.discoverHost(id);
                     if (future != null) {
                         try {
                             host = future.get();
+                        } catch (InterruptedException ioe) {
+                            log.trace("Thread interrupted {}", ioe);
                         } catch (Exception e) {
-                            e.printStackTrace();
+                            log.error("", e);
                         }
                     }
                 }
                 if (host != null) {
+                    log.debug("Next hop {} is found", nh.getHostAddress());
                     staticRoute.setHost(host);
+                    // static route object has changed
+                    // put the changed object back in the cache
+                    // for it to sync
+                    staticRoutes.put(name, staticRoute);
                     notifyStaticRouteUpdate(staticRoute, added);
+                } else {
+                    log.debug("Next hop {}  is still not present, try again later", nh.getHostAddress());
                 }
             }
+            return null;
         }
     }
 
-    private void checkAndUpdateListeners(StaticRoute staticRoute, boolean added) {
-        new NotifyStaticRouteThread(staticRoute, added).start();
+    private void checkAndUpdateListeners(String name, StaticRoute staticRoute, boolean added) {
+        NotifyStaticRouteWorker worker = new NotifyStaticRouteWorker(name, staticRoute, added);
+        try {
+            executor.submit(worker);
+        } catch (Exception e) {
+            log.error("got Exception ", e);
+        }
     }
 
     private void notifyHostUpdate(HostNodeConnector host, boolean added) {
-        if (host == null)
+        if (host == null) {
             return;
-        for (StaticRoute s : staticRoutes.values()) {
-            if (s.getType() == StaticRoute.NextHopType.SWITCHPORT)
+        }
+        for (Map.Entry<String, StaticRoute> s : staticRoutes.entrySet()) {
+            StaticRoute route = s.getValue();
+            if (route.getType() == StaticRoute.NextHopType.SWITCHPORT) {
                 continue;
-            if (s.getNextHopAddress().equals(host.getNetworkAddress())) {
+            }
+            if (route.getNextHopAddress().equals(host.getNetworkAddress())) {
                 if (added) {
-                    s.setHost(host);
+                    route.setHost(host);
                 } else {
-                    s.setHost(null);
+                    route.setHost(null);
                 }
-                notifyStaticRouteUpdate(s, added);
+                // static route object has changed
+                // put the changed object back in the cache
+                // for it to sync
+                staticRoutes.put(s.getKey(), route);
+                notifyStaticRouteUpdate(route, added);
             }
         }
     }
@@ -318,8 +288,9 @@ public class StaticRoutingImplementation implements IfNewHostNotify,
     }
 
     public boolean isIPv4AddressValid(String cidr) {
-        if (cidr == null)
+        if (cidr == null) {
             return false;
+        }
 
         String values[] = cidr.split("/");
         Pattern ipv4Pattern = Pattern
@@ -355,11 +326,11 @@ public class StaticRoutingImplementation implements IfNewHostNotify,
         return 0;
     }
 
+    @Override
     public StaticRoute getBestMatchStaticRoute(InetAddress ipAddress) {
         ByteBuffer bblongestPrefix = null;
         try {
-            bblongestPrefix = ByteBuffer.wrap(InetAddress.getByName("0.0.0.0")
-                    .getAddress());
+            bblongestPrefix = ByteBuffer.wrap(InetAddress.getByName("0.0.0.0").getAddress());
         } catch (Exception e) {
             return null;
         }
@@ -382,43 +353,45 @@ public class StaticRoutingImplementation implements IfNewHostNotify,
         return longestPrefixRoute;
     }
 
+    @Override
     public Status addStaticRoute(StaticRouteConfig config) {
-        Status status;
-
-        status = config.isValid();
+        Status status = config.isValid();
         if (!status.isSuccess()) {
             return status;
         }
         if (staticRouteConfigs.get(config.getName()) != null) {
-               return new Status(StatusCode.CONFLICT,
-                               "A valid Static Route configuration with this name " +
-                                               "already exists. Please use a different name");
+            return new Status(StatusCode.CONFLICT, "A valid Static Route configuration with this name "
+                    + "already exists. Please use a different name");
         }
-        for (StaticRouteConfig s : staticRouteConfigs.values()) {
-            if (s.equals(config)) {
-               return new Status(StatusCode.CONFLICT,
-                               "This conflicts with an existing Static Route " +
-                                       "Configuration. Please check the configuration " +
-                                               "and try again");
+
+        // Update database
+        StaticRoute sRoute = new StaticRoute(config);
+
+        for (Map.Entry<String, StaticRoute> entry : staticRoutes.entrySet()) {
+            if (entry.getValue().compareTo(sRoute) == 0) {
+                return new Status(StatusCode.CONFLICT, "This conflicts with an existing Static Route "
+                        + "Configuration. Please check the configuration " + "and try again");
             }
         }
+        staticRoutes.put(config.getName(), sRoute);
 
+        // Update config databse
         staticRouteConfigs.put(config.getName(), config);
-        StaticRoute sRoute = new StaticRoute(config);
-        staticRoutes.put(config.getName(), sRoute);
-        checkAndUpdateListeners(sRoute, true);
-        return status; 
+
+        // Notify
+        checkAndUpdateListeners(config.getName(), sRoute, true);
+        return status;
     }
 
+    @Override
     public Status removeStaticRoute(String name) {
         staticRouteConfigs.remove(name);
         StaticRoute sRoute = staticRoutes.remove(name);
         if (sRoute != null) {
-            checkAndUpdateListeners(sRoute, false);
+            checkAndUpdateListeners(name, sRoute, false);
             return new Status(StatusCode.SUCCESS, null);
         }
-        return new Status(StatusCode.NOTFOUND, 
-                       "Static Route with name " + name + " is not found");
+        return new Status(StatusCode.NOTFOUND, "Static Route with name " + name + " is not found");
     }
 
     void setClusterContainerService(IClusterContainerServices s) {
@@ -448,47 +421,44 @@ public class StaticRoutingImplementation implements IfNewHostNotify,
             containerName = "";
         }
 
-        staticRoutesFileName = ROOT + "staticRouting_" + containerName
-                + ".conf";
+        staticRoutesFileName = ROOT + "staticRouting_" + containerName + ".conf";
 
-        log.debug("forwarding.staticrouting starting on container "
-                + containerName);
-        //staticRoutes = new ConcurrentHashMap<String, StaticRoute>();
+        log.debug("forwarding.staticrouting starting on container {}", containerName);
         allocateCaches();
         retrieveCaches();
-
-        if (staticRouteConfigs.isEmpty())
+        this.executor = Executors.newFixedThreadPool(1);
+        if (staticRouteConfigs.isEmpty()) {
             loadConfiguration();
+        }
 
         /*
-         *  Slow probe to identify any gateway that might have silently appeared
-         *  after the Static Routing Configuration.
+         * Slow probe to identify any gateway that might have silently appeared
+         * after the Static Routing Configuration.
          */
         gatewayProbeTimer = new Timer();
         gatewayProbeTimer.schedule(new TimerTask() {
             @Override
             public void run() {
-                for (StaticRoute s : staticRoutes.values()) {
-                    if ((s.getType() == StaticRoute.NextHopType.IPADDRESS)
-                            && s.getHost() == null) {
-                        checkAndUpdateListeners(s, true);
+                for (Map.Entry<String, StaticRoute> s : staticRoutes.entrySet()) {
+                    StaticRoute route = s.getValue();
+                    if ((route.getType() == StaticRoute.NextHopType.IPADDRESS) && route.getHost() == null) {
+                        checkAndUpdateListeners(s.getKey(), route, true);
                     }
                 }
             }
         }, 60 * 1000, 60 * 1000);
+
     }
 
     /**
-     * Function called by the dependency manager when at least one
-     * dependency become unsatisfied or when the component is shutting
-     * down because for example bundle is being stopped.
+     * Function called by the dependency manager when at least one dependency
+     * become unsatisfied or when the component is shutting down because for
+     * example bundle is being stopped.
      *
      */
     void destroy() {
-        log.debug("Destroy all the Static Routing Rules given we are "
-                + "shutting down");
+        log.debug("Destroy all the Static Routing Rules given we are " + "shutting down");
 
-        destroyCaches();
         gatewayProbeTimer.cancel();
 
         // Clear the listener so to be ready in next life
@@ -496,25 +466,26 @@ public class StaticRoutingImplementation implements IfNewHostNotify,
     }
 
     /**
-     * Function called by dependency manager after "init ()" is called
-     * and after the services provided by the class are registered in
-     * the service registry
+     * Function called by dependency manager after "init ()" is called and after
+     * the services provided by the class are registered in the service registry
      *
      */
     void start() {
     }
 
     /**
-     * Function called by the dependency manager before the services
-     * exported by the component are unregistered, this will be
-     * followed by a "destroy ()" calls
+     * Function called by the dependency manager before the services exported by
+     * the component are unregistered, this will be followed by a "destroy ()"
+     * calls
      *
      */
     void stop() {
+        executor.shutdown();
     }
 
     @Override
     public Status saveConfiguration() {
         return this.saveConfig();
     }
+
 }