Per container save configuration is not synced in the cluster 87/1687/1
authorAlessandro Boch <aboch@cisco.com>
Sun, 6 Oct 2013 05:12:04 +0000 (22:12 -0700)
committerAlessandro Boch <aboch@cisco.com>
Sun, 6 Oct 2013 05:18:26 +0000 (22:18 -0700)
ISSUE: When container administrator saves the configuration on GUI,
       the configurations for the containers on which he has write permission
       are onlyu saved on the local controller.
CHANGE: Have ContainerConfigurationService manage its own event sync cache
        for this specific case. ContainerConfigurationService.saveConfigurations()
        gets invoked only when container adminidtrator trigger the save.For the
        save config triggered by system or network administrator, things work as
        today, it is handled by ConfigurationService which syncs the config event
        to the other ConfigurationServices in the cluster.

Change-Id: I961558579292fd10b0d8ee601aa64f3a3843b0ed
Signed-off-by: Alessandro Boch <aboch@cisco.com>
14 files changed:
opendaylight/configuration/api/src/main/java/org/opendaylight/controller/configuration/IConfigurationAware.java
opendaylight/configuration/api/src/main/java/org/opendaylight/controller/configuration/IConfigurationAwareCommon.java
opendaylight/configuration/api/src/main/java/org/opendaylight/controller/configuration/IConfigurationContainerAware.java
opendaylight/configuration/api/src/main/java/org/opendaylight/controller/configuration/IConfigurationContainerService.java
opendaylight/configuration/api/src/main/java/org/opendaylight/controller/configuration/IConfigurationService.java
opendaylight/configuration/api/src/main/java/org/opendaylight/controller/configuration/IConfigurationServiceCommon.java
opendaylight/configuration/implementation/src/main/java/org/opendaylight/controller/configuration/internal/Activator.java
opendaylight/configuration/implementation/src/main/java/org/opendaylight/controller/configuration/internal/ConfigurationContainerImpl.java [deleted file]
opendaylight/configuration/implementation/src/main/java/org/opendaylight/controller/configuration/internal/ConfigurationService.java [moved from opendaylight/configuration/implementation/src/main/java/org/opendaylight/controller/configuration/internal/ConfigurationImpl.java with 81% similarity]
opendaylight/configuration/implementation/src/main/java/org/opendaylight/controller/configuration/internal/ContainerConfigurationService.java [new file with mode: 0644]
opendaylight/configuration/implementation/src/test/java/org/opendaylight/controller/configuration/internal/ConfigurationAwareTest.java
opendaylight/configuration/implementation/src/test/java/org/opendaylight/controller/configuration/internal/ConfigurationContainerAwareTest.java
opendaylight/configuration/implementation/src/test/java/org/opendaylight/controller/configuration/internal/ConfigurationContainerImplTest.java
opendaylight/configuration/implementation/src/test/java/org/opendaylight/controller/configuration/internal/ConfigurationImplTest.java

index f06dcb3..c909bff 100644 (file)
@@ -14,13 +14,12 @@ import org.opendaylight.controller.sal.utils.Status;
 
 /**
  * Listener Interface for receiving Configuration events.
- *
- *
  */
 public interface IConfigurationAwareCommon {
-
     /**
-     * Trigger from configuration component to persist the configuration state.
+     * Trigger from Configuration Service or Container Configuration Service to
+     * persist the configuration state for this component on the local cluster
+     * node
      */
     Status saveConfiguration();
 }
index ac1aabe..f759315 100644 (file)
@@ -11,9 +11,8 @@ package org.opendaylight.controller.configuration;
 
 
 /**
- * Listener Interface for receiving Configuration events.
- *
- *
+ * Listener Interface for receiving Configuration events for components that
+ * live in a container
  */
 public interface IConfigurationContainerAware extends IConfigurationAwareCommon {
 }
index fb5be35..2123f6b 100644 (file)
 package org.opendaylight.controller.configuration;
 
 /**
- * Container Manager interface
- *
- *
+ * Container configuration service
  */
-public interface IConfigurationContainerService extends
-        IConfigurationServiceCommon {
+public interface IConfigurationContainerService extends IConfigurationServiceCommon {
 }
index c76e377..05eed6f 100644 (file)
@@ -12,10 +12,19 @@ package org.opendaylight.controller.configuration;
 import org.opendaylight.controller.sal.utils.Status;
 
 /**
- * Container Manager interface
- *
- *
+ * Common configuration interface for Configuration Service and Container
+ * Configuration Service
  */
 public interface IConfigurationServiceCommon {
+    /**
+     * Represent the trigger to save the controller configuration cluster wide.
+     * When called on IConfigurationService, it will trigger a cluster wide save
+     * configuration event for all the global instance components and all
+     * components in all containers. When called on
+     * IContainerConfigurationService, it will trigger a cluster wide save
+     * configuration event for all components in the current container.
+     *
+     * @return the Status object representing the result of the saving request
+     */
     Status saveConfigurations();
 }
index 871fd07..ae05199 100644 (file)
@@ -16,6 +16,7 @@ import java.util.Set;
 
 import org.apache.felix.dm.Component;
 import org.opendaylight.controller.clustering.services.ICacheUpdateAware;
+import org.opendaylight.controller.clustering.services.IClusterContainerServices;
 import org.opendaylight.controller.clustering.services.IClusterGlobalServices;
 import org.opendaylight.controller.configuration.IConfigurationAware;
 import org.opendaylight.controller.configuration.IConfigurationContainerAware;
@@ -28,7 +29,7 @@ import org.slf4j.LoggerFactory;
 /**
  * @file Activator.java
  *
- * @brief Component Activator for Configuration Management.
+ * @brief Component Activator for ConfigurationService Management.
  *
  *
  *
@@ -47,8 +48,9 @@ public class Activator extends ComponentActivatorAbstractBase {
      * instantiated in order to get an fully working implementation
      * Object
      */
+    @Override
     public Object[] getImplementations() {
-        Object[] res = { ConfigurationContainerImpl.class };
+        Object[] res = { ContainerConfigurationService.class };
         return res;
     }
 
@@ -65,17 +67,26 @@ public class Activator extends ComponentActivatorAbstractBase {
      * also optional per-container different behavior if needed, usually
      * should not be the case though.
      */
+    @Override
     public void configureInstance(Component c, Object imp, String containerName) {
-        if (imp.equals(ConfigurationContainerImpl.class)) {
+        if (imp.equals(ContainerConfigurationService.class)) {
+            Dictionary<String, Set<String>> props = new Hashtable<String, Set<String>>();
+            Set<String> propSet = new HashSet<String>();
+            propSet.add(ContainerConfigurationService.CONTAINER_SAVE_EVENT_CACHE);
+            props.put("cachenames", propSet);
+
             // export the service
-            c.setInterface(new String[] {
-                    IConfigurationContainerService.class.getName(),
-                    IConfigurationAware.class.getName() }, null);
+            c.setInterface(
+                    new String[] { IConfigurationContainerService.class.getName(), IConfigurationAware.class.getName(),
+                            ICacheUpdateAware.class.getName() }, props);
 
             c.add(createContainerServiceDependency(containerName).setService(
                     IConfigurationContainerAware.class).setCallbacks(
                             "addConfigurationContainerAware",
                             "removeConfigurationContainerAware").setRequired(false));
+
+            c.add(createContainerServiceDependency(containerName).setService(IClusterContainerServices.class)
+                    .setCallbacks("setClusterServices", "unsetClusterServices").setRequired(true));
         }
     }
 
@@ -92,8 +103,9 @@ public class Activator extends ComponentActivatorAbstractBase {
      * @return The list of implementations the bundle will support,
      * in Global version
      */
+    @Override
     protected Object[] getGlobalImplementations() {
-        Object[] res = { ConfigurationImpl.class };
+        Object[] res = { ConfigurationService.class };
         return res;
     }
 
@@ -105,11 +117,12 @@ public class Activator extends ComponentActivatorAbstractBase {
      * @param imp implementation to be configured
      * @param containerName container on which the configuration happens
      */
+    @Override
     protected void configureGlobalInstance(Component c, Object imp) {
-        if (imp.equals(ConfigurationImpl.class)) {
+        if (imp.equals(ConfigurationService.class)) {
             Dictionary<String, Set<String>> props = new Hashtable<String, Set<String>>();
             Set<String> propSet = new HashSet<String>();
-            propSet.add("config.event.save");
+            propSet.add(ConfigurationService.SAVE_EVENT_CACHE);
             props.put("cachenames", propSet);
 
             // export the service
diff --git a/opendaylight/configuration/implementation/src/main/java/org/opendaylight/controller/configuration/internal/ConfigurationContainerImpl.java b/opendaylight/configuration/implementation/src/main/java/org/opendaylight/controller/configuration/internal/ConfigurationContainerImpl.java
deleted file mode 100644 (file)
index 9fb0c23..0000000
+++ /dev/null
@@ -1,108 +0,0 @@
-
-/*
- * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-
-package org.opendaylight.controller.configuration.internal;
-
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
-
-import org.opendaylight.controller.clustering.services.IClusterGlobalServices;
-import org.opendaylight.controller.configuration.IConfigurationAware;
-import org.opendaylight.controller.configuration.IConfigurationContainerAware;
-import org.opendaylight.controller.configuration.IConfigurationContainerService;
-import org.opendaylight.controller.sal.utils.StatusCode;
-import org.opendaylight.controller.sal.utils.Status;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * @file   ConfigurationImpl.java
- *
- * @brief  Backend functionality for all Configuration related tasks.
- *
- *
- */
-
-public class ConfigurationContainerImpl implements
-        IConfigurationContainerService, IConfigurationAware {
-    private static final Logger logger = LoggerFactory
-            .getLogger(ConfigurationContainerImpl.class);
-    private IClusterGlobalServices clusterServices;
-    /*
-     * Collection containing the configuration objects.
-     * This is configuration world: container names (also the map key)
-     * are maintained as they were configured by user, same case
-     */
-    private Set<IConfigurationContainerAware> configurationAwareList = (Set<IConfigurationContainerAware>) Collections
-            .synchronizedSet(new HashSet<IConfigurationContainerAware>());
-
-    public void addConfigurationContainerAware(
-            IConfigurationContainerAware configurationAware) {
-        if (!this.configurationAwareList.contains(configurationAware)) {
-            this.configurationAwareList.add(configurationAware);
-        }
-    }
-
-    public int getConfigurationAwareListSize() {
-        return this.configurationAwareList.size();
-    }
-
-    public void removeConfigurationContainerAware(
-            IConfigurationContainerAware configurationAware) {
-        this.configurationAwareList.remove(configurationAware);
-    }
-
-    public void setClusterServices(IClusterGlobalServices i) {
-        this.clusterServices = i;
-        logger.debug("IClusterServices set");
-    }
-
-    public void unsetClusterServices(IClusterGlobalServices i) {
-        if (this.clusterServices == i) {
-            this.clusterServices = null;
-            logger.debug("IClusterServices Unset");
-        }
-    }
-
-    public void init() {
-    }
-
-    public void destroy() {
-        // Clear local states
-        this.configurationAwareList.clear();
-    }
-
-    @Override
-    public Status saveConfiguration() {
-        boolean success = true;
-        for (IConfigurationContainerAware configurationAware : configurationAwareList) {
-            logger.info("Save Config triggered for {}",
-                      configurationAware.getClass().getSimpleName());
-
-            Status status = configurationAware.saveConfiguration();
-            if (!status.isSuccess()) {
-                success = false;
-                logger.info("Failed to save config for {}",
-                                configurationAware.getClass().getSimpleName());
-            }
-        }
-        if (success) {
-            return new Status(StatusCode.SUCCESS, null);
-        } else {
-            return new Status(StatusCode.INTERNALERROR,
-                        "Failed to Save All Configurations");
-        }
-    }
-
-    @Override
-    public Status saveConfigurations() {
-        return saveConfiguration();
-    }
-}
@@ -31,22 +31,17 @@ import org.slf4j.LoggerFactory;
 /**
  * @file   ConfigurationImpl.java
  *
- * @brief  Backend functionality for all Configuration related tasks.
- *
+ * @brief  Backend functionality for all ConfigurationService related tasks.
  *
  */
 
-public class ConfigurationImpl implements IConfigurationService, ICacheUpdateAware<ConfigurationEvent, String> {
+public class ConfigurationService implements IConfigurationService, ICacheUpdateAware<ConfigurationEvent, String> {
     private static final Logger logger = LoggerFactory
-            .getLogger(ConfigurationImpl.class);
+            .getLogger(ConfigurationService.class);
+    public static final String SAVE_EVENT_CACHE = "config.event.save";
     private IClusterGlobalServices clusterServices;
     private ConcurrentMap <ConfigurationEvent, String> configEvent;
-    /*
-     * Collection containing the configuration objects.
-     * This is configuration world: container names (also the map key)
-     * are maintained as they were configured by user, same case
-     */
-    private Set<IConfigurationAware> configurationAwareList = (Set<IConfigurationAware>) Collections
+    private Set<IConfigurationAware> configurationAwareList = Collections
             .synchronizedSet(new HashSet<IConfigurationAware>());
 
 
@@ -77,7 +72,7 @@ public class ConfigurationImpl implements IConfigurationService, ICacheUpdateAwa
     }
 
     public void init() {
-        logger.info("ContainerManager startup....");
+        logger.info("ConfigurationService Manager init");
     }
 
     public void start() {
@@ -119,13 +114,17 @@ public class ConfigurationImpl implements IConfigurationService, ICacheUpdateAwa
     @Override
     public void entryCreated(ConfigurationEvent key, String cacheName,
             boolean originLocal) {
-        if (originLocal) return;
+        if (originLocal) {
+            return;
+        }
     }
 
     @Override
     public void entryUpdated(ConfigurationEvent key, String new_value,
             String cacheName, boolean originLocal) {
-        if (originLocal) return;
+        if (originLocal) {
+            return;
+        }
         if (key == ConfigurationEvent.SAVE) {
             saveConfigurationsInternal();
         }
@@ -134,32 +133,33 @@ public class ConfigurationImpl implements IConfigurationService, ICacheUpdateAwa
     @Override
     public void entryDeleted(ConfigurationEvent key, String cacheName,
             boolean originLocal) {
-        if (originLocal) return;
+        if (originLocal) {
+            return;
+        }
     }
 
-    @SuppressWarnings("deprecation")
     private void allocateCache() {
         if (this.clusterServices == null) {
             logger.error("uninitialized clusterServices, can't create cache");
             return;
         }
         try {
-            this.clusterServices.createCache("config.event.save",
+            this.clusterServices.createCache(SAVE_EVENT_CACHE,
                     EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
         } catch (CacheConfigException cce) {
-            logger.error("Error creating Configuration cache ", cce);
+            logger.debug("Error creating ConfigurationService cache ", cce);
         } catch (CacheExistException cce) {
-            logger.error("Configuration Cache already exists, destroy and recreate ", cce);
+            logger.debug("ConfigurationService Cache already exists, destroy and recreate ", cce);
         }
     }
 
-    @SuppressWarnings({ "unchecked", "deprecation" })
+    @SuppressWarnings({ "unchecked" })
     private void retrieveCache() {
         if (this.clusterServices == null) {
             logger.error("uninitialized clusterServices, can't retrieve cache");
             return;
         }
-        configEvent = (ConcurrentMap<ConfigurationEvent, String>) this.clusterServices.getCache("config.event.save");
+        configEvent = (ConcurrentMap<ConfigurationEvent, String>) this.clusterServices.getCache(SAVE_EVENT_CACHE);
         if (configEvent == null) {
             logger.error("Failed to retrieve configuration Cache");
         }
diff --git a/opendaylight/configuration/implementation/src/main/java/org/opendaylight/controller/configuration/internal/ContainerConfigurationService.java b/opendaylight/configuration/implementation/src/main/java/org/opendaylight/controller/configuration/internal/ContainerConfigurationService.java
new file mode 100644 (file)
index 0000000..d476901
--- /dev/null
@@ -0,0 +1,174 @@
+
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.configuration.internal;
+
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.ConcurrentMap;
+
+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.ConfigurationEvent;
+import org.opendaylight.controller.configuration.IConfigurationAware;
+import org.opendaylight.controller.configuration.IConfigurationContainerAware;
+import org.opendaylight.controller.configuration.IConfigurationContainerService;
+import org.opendaylight.controller.sal.utils.Status;
+import org.opendaylight.controller.sal.utils.StatusCode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @file   ConfigurationImpl.java
+ *
+ * @brief  Backend functionality for all ConfigurationService related tasks.
+ *
+ *
+ */
+
+public class ContainerConfigurationService implements IConfigurationContainerService, IConfigurationAware,
+        ICacheUpdateAware<ConfigurationEvent, String> {
+    public static final String CONTAINER_SAVE_EVENT_CACHE = "config.container.event.save";
+    private static final Logger logger = LoggerFactory.getLogger(ContainerConfigurationService.class);
+    private IClusterContainerServices clusterServices;
+    private ConcurrentMap <ConfigurationEvent, String> containerConfigEvent;
+    /*
+     * Collection containing the configuration objects.
+     * This is configuration world: container names (also the map key)
+     * are maintained as they were configured by user, same case
+     */
+    private Set<IConfigurationContainerAware> configurationAwareList = Collections
+            .synchronizedSet(new HashSet<IConfigurationContainerAware>());
+
+    public void addConfigurationContainerAware(
+            IConfigurationContainerAware configurationAware) {
+        if (!this.configurationAwareList.contains(configurationAware)) {
+            this.configurationAwareList.add(configurationAware);
+        }
+    }
+
+    public int getConfigurationAwareListSize() {
+        return this.configurationAwareList.size();
+    }
+
+    public void removeConfigurationContainerAware(
+            IConfigurationContainerAware configurationAware) {
+        this.configurationAwareList.remove(configurationAware);
+    }
+
+    public void setClusterServices(IClusterContainerServices i) {
+        this.clusterServices = i;
+        logger.debug("IClusterServices set");
+    }
+
+    public void unsetClusterServices(IClusterContainerServices i) {
+        if (this.clusterServices == i) {
+            this.clusterServices = null;
+            logger.debug("IClusterServices Unset");
+        }
+    }
+
+    public void init() {
+    }
+
+    public void start() {
+        allocateCache();
+        retrieveCache();
+    }
+
+    public void destroy() {
+        // Clear local states
+        this.configurationAwareList.clear();
+    }
+
+    @Override
+    public Status saveConfiguration() {
+        boolean success = true;
+        for (IConfigurationContainerAware configurationAware : configurationAwareList) {
+            logger.info("Save Config triggered for {}", configurationAware.getClass().getSimpleName());
+
+            Status status = configurationAware.saveConfiguration();
+            if (!status.isSuccess()) {
+                success = false;
+                logger.info("Failed to save config for {}", configurationAware.getClass().getSimpleName());
+            }
+        }
+        if (success) {
+            return new Status(StatusCode.SUCCESS);
+        } else {
+            return new Status(StatusCode.INTERNALERROR, "Failed to Save All Configurations");
+        }
+    }
+
+    @Override
+    public Status saveConfigurations() {
+        containerConfigEvent.put(ConfigurationEvent.SAVE, "");
+        return saveConfiguration();
+    }
+
+    @Override
+    public void entryCreated(ConfigurationEvent key, String cacheName,
+            boolean originLocal) {
+        if (originLocal) {
+            return;
+        }
+    }
+
+    @Override
+    public void entryUpdated(ConfigurationEvent key, String new_value,
+            String cacheName, boolean originLocal) {
+        if (originLocal) {
+            return;
+        }
+        logger.debug("Processing {} event", key);
+        if (key == ConfigurationEvent.SAVE) {
+            saveConfiguration();
+        }
+    }
+
+    @Override
+    public void entryDeleted(ConfigurationEvent key, String cacheName,
+            boolean originLocal) {
+        if (originLocal) {
+            return;
+        }
+    }
+
+    private void allocateCache() {
+        if (this.clusterServices == null) {
+            logger.error("uninitialized clusterServices, can't create cache");
+            return;
+        }
+        try {
+            this.clusterServices.createCache(CONTAINER_SAVE_EVENT_CACHE,
+                    EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
+        } catch (CacheConfigException cce) {
+            logger.debug("Error creating ContainerConfigurationService cache ", cce);
+        } catch (CacheExistException cce) {
+            logger.debug("ConfigurationService Cache already exists, destroy and recreate ", cce);
+        }
+    }
+
+    @SuppressWarnings({ "unchecked" })
+    private void retrieveCache() {
+        if (this.clusterServices == null) {
+            logger.error("uninitialized clusterServices, can't retrieve cache");
+            return;
+        }
+        containerConfigEvent = (ConcurrentMap<ConfigurationEvent, String>) this.clusterServices.getCache(CONTAINER_SAVE_EVENT_CACHE);
+        if (containerConfigEvent == null) {
+            logger.error("Failed to retrieve configuration Cache");
+        }
+    }
+}
index c9dd5e2..185fb17 100644 (file)
@@ -15,7 +15,7 @@ import org.opendaylight.controller.sal.utils.Status;
 /**
  * @file   TestConfigurationAware.java
  *
- * @brief  Test Class to create for jUnit test cases for Configuration Implementation
+ * @brief  Test Class to create for jUnit test cases for ConfigurationService Implementation
  *
  *
  */
index c5d6bdc..ff627aa 100644 (file)
@@ -15,7 +15,7 @@ import org.opendaylight.controller.sal.utils.Status;
 /**
  * @file   TestConfigurationAware.java
  *
- * @brief  Test Class to create for jUnit test cases for Configuration Implementation
+ * @brief  Test Class to create for jUnit test cases for ConfigurationService Implementation
  *
  *
  */
index a82a9f2..7e2a139 100644 (file)
@@ -20,7 +20,7 @@ public class ConfigurationContainerImplTest {
         @Test
         public void testAddRemoveSaveConfiguration() {
 
-                ConfigurationContainerImpl configurationContainerImpl = new ConfigurationContainerImpl();
+                ContainerConfigurationService configurationContainerImpl = new ContainerConfigurationService();
                 IConfigurationContainerAware testConfigurationContainerAware = new ConfigurationContainerAwareTest();
 
                 configurationContainerImpl.addConfigurationContainerAware(testConfigurationContainerAware);
index 816c6ce..1d7bdbd 100644 (file)
@@ -18,7 +18,7 @@ public class ConfigurationImplTest {
         @Test
         public void testAddRemoveSaveConfiguration() {
 
-                ConfigurationImpl configurationImpl = new ConfigurationImpl();
+                ConfigurationService configurationImpl = new ConfigurationService();
                 IConfigurationAware testConfigurationAware = new ConfigurationAwareTest();
 
                 configurationImpl.addConfigurationAware(testConfigurationAware);