Add config system API to recreate a module instance 78/38278/3
authorTom Pantelis <tpanteli@brocade.com>
Fri, 15 Apr 2016 19:19:50 +0000 (15:19 -0400)
committerTom Pantelis <tpanteli@brocade.com>
Mon, 6 Jun 2016 01:54:49 +0000 (21:54 -0400)
For the blueprint work, I need to be able to restart/recreate a
config module, ie close the previous instance and create a new instance,
when the corresponding service instance is recreated/re-advertised via
blueprint container restart.

The ConfigSubsystemFacade has no API to restart a config module. One
can push a new configuration but there has to be an actual change in
order for a new instance to be created otherwise it reuses the prior
instance.

Therefore I added a new EditStrategyType enum, recreate, with a
corresponding EditConfigStrategy class that forces re-creation of a
config module instance. This strategy calls a new
method, reCreateModule, on the ConfigTransactionController interface.

The decision logic to reuse or create a new instance is in the
AbstractModule class and generated derived classes. Therefore the
reCreateModule method sets a canReuseInstance flag on the real
AbstractModule instance. This is probably not the cleanest approach but
I wanted to avoid changing the Module interface as that looked to be too
invasive. The AbstractModule getInstance and canReuse methods check the
canReuseInstance override flag to determine if the old module/instance
can be reused.

Change-Id: I8cfb8408bae0127331676dcf32519b176f0a8844
Signed-off-by: Tom Pantelis <tpanteli@brocade.com>
opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/ConfigTransactionController.java
opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/spi/AbstractModule.java
opendaylight/config/config-manager-facade-xml/src/main/java/org/opendaylight/controller/config/facade/xml/mapping/config/Config.java
opendaylight/config/config-manager-facade-xml/src/main/java/org/opendaylight/controller/config/facade/xml/strategy/EditStrategyType.java
opendaylight/config/config-manager-facade-xml/src/main/java/org/opendaylight/controller/config/facade/xml/strategy/ReCreateEditConfigStrategy.java [new file with mode: 0644]
opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/ConfigTransactionControllerImpl.java
opendaylight/config/config-util/src/main/java/org/opendaylight/controller/config/util/ConfigTransactionJMXClient.java
opendaylight/config/config-util/src/test/java/org/opendaylight/controller/config/util/TestingConfigTransactionController.java

index 8afc80a4f36971edbf1bd562e68dd684506ff7ad..bd9002c62487d2c5506599ec35219278527b9377 100644 (file)
@@ -29,6 +29,20 @@ public interface ConfigTransactionController extends LookupRegistry, ServiceRefe
     ObjectName createModule(String moduleName, String instanceName)
             throws InstanceAlreadyExistsException;
 
     ObjectName createModule(String moduleName, String instanceName)
             throws InstanceAlreadyExistsException;
 
+    /**
+     * Re-creates an existing module configuration bean.
+     *
+     * @param objectName
+     *            can be either read-only module name that can be obtained using
+     *            {@link ConfigRegistry#lookupConfigBean(String, String)} or
+     *            writable module name that must contain current transaction name.
+     * @throws InstanceNotFoundException
+     *             if module is not found
+     * @throws IllegalArgumentException
+     *             if object name contains wrong transaction name or domain
+     */
+    void reCreateModule(ObjectName objectName) throws InstanceNotFoundException;
+
     /**
      * Destroy existing module.
      *
     /**
      * Destroy existing module.
      *
index e60a047379ee73b9399807f232a08592123d0d5c..7fcc0cc5c07097d4d30419bf537f637ad01442c6 100644 (file)
@@ -27,6 +27,7 @@ public abstract class AbstractModule<M extends AbstractModule<M>> implements org
     private AutoCloseable oldInstance;
     private M oldModule;
     private AutoCloseable instance;
     private AutoCloseable oldInstance;
     private M oldModule;
     private AutoCloseable instance;
+    private boolean canReuseInstance = true;
 
     /**
      * Called when module is configured.
 
     /**
      * Called when module is configured.
@@ -58,6 +59,10 @@ public abstract class AbstractModule<M extends AbstractModule<M>> implements org
         return identifier;
     }
 
         return identifier;
     }
 
+    public final void setCanReuseInstance(boolean canReuseInstance) {
+        this.canReuseInstance = canReuseInstance;
+    }
+
     /**
      *
      * General algorithm for spawning/closing and reusing wrapped instances.
     /**
      *
      * General algorithm for spawning/closing and reusing wrapped instances.
@@ -67,7 +72,7 @@ public abstract class AbstractModule<M extends AbstractModule<M>> implements org
     @Override
     public final AutoCloseable getInstance() {
         if (instance == null) {
     @Override
     public final AutoCloseable getInstance() {
         if (instance == null) {
-            if (oldInstance != null && canReuseInstance(oldModule)) {
+            if (oldInstance != null && canReuseInstance && canReuseInstance(oldModule)) {
                 resolveDependencies();
                 instance = reuseInstance(oldInstance);
             } else {
                 resolveDependencies();
                 instance = reuseInstance(oldInstance);
             } else {
@@ -102,7 +107,7 @@ public abstract class AbstractModule<M extends AbstractModule<M>> implements org
     public final boolean canReuse(final Module oldModule) {
         // Just cast into a specific instance
         // TODO unify this method with canReuseInstance (required Module interface to be generic which requires quite a lot of changes)
     public final boolean canReuse(final Module oldModule) {
         // Just cast into a specific instance
         // TODO unify this method with canReuseInstance (required Module interface to be generic which requires quite a lot of changes)
-        return getClass().isInstance(oldModule) ? canReuseInstance((M) oldModule) : false;
+        return canReuseInstance && getClass().isInstance(oldModule) ? canReuseInstance((M) oldModule) : false;
     }
 
     /**
     }
 
     /**
index 3ecee286d3c51fc4aa4f99eb0b5b30dbfb1e4cf0..9bee7a10854beeea35bf07a10b17a89256fba65e 100644 (file)
@@ -9,7 +9,6 @@
 package org.opendaylight.controller.config.facade.xml.mapping.config;
 
 import static com.google.common.base.Preconditions.checkState;
 package org.opendaylight.controller.config.facade.xml.mapping.config;
 
 import static com.google.common.base.Preconditions.checkState;
-
 import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
 import com.google.common.collect.HashMultimap;
 import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
 import com.google.common.collect.HashMultimap;
@@ -130,6 +129,12 @@ public class Config {
         return dataElement;
     }
 
         return dataElement;
     }
 
+    public Element moduleToXml(String moduleNamespace, String factoryName, String instanceName,
+        ObjectName instanceON, Document document) {
+        ModuleConfig moduleConfig = getModuleMapping(moduleNamespace, instanceName, factoryName);
+        return moduleConfig.toXml(instanceON, document, moduleNamespace, enumResolver);
+    }
+
     // TODO refactor, replace string representing namespace with namespace class
     // TODO refactor, replace Map->Multimap with e.g. ConfigElementResolved
     // class
     // TODO refactor, replace string representing namespace with namespace class
     // TODO refactor, replace Map->Multimap with e.g. ConfigElementResolved
     // class
index c5af3ec065f3bb53b1de1a87a0d2954a614bc8ca..47f5078967068b85eb2fa359a773c408562d906c 100644 (file)
@@ -17,7 +17,7 @@ public enum EditStrategyType {
     // can be default
     merge, replace, none,
     // additional per element
     // can be default
     merge, replace, none,
     // additional per element
-    delete, remove;
+    delete, remove, recreate;
 
     private static final Set<EditStrategyType> defaultStrats = EnumSet.of(merge, replace, none);
 
 
     private static final Set<EditStrategyType> defaultStrats = EnumSet.of(merge, replace, none);
 
@@ -31,6 +31,7 @@ public enum EditStrategyType {
         case none:
         case remove:
         case delete:
         case none:
         case remove:
         case delete:
+        case recreate:
             return false;
         case replace:
             return true;
             return false;
         case replace:
             return true;
@@ -64,6 +65,8 @@ public enum EditStrategyType {
             return new DeleteEditConfigStrategy();
         case remove:
             return new RemoveEditConfigStrategy();
             return new DeleteEditConfigStrategy();
         case remove:
             return new RemoveEditConfigStrategy();
+        case recreate:
+            return new ReCreateEditConfigStrategy();
         case none:
             return new NoneEditConfigStrategy();
         default:
         case none:
             return new NoneEditConfigStrategy();
         default:
diff --git a/opendaylight/config/config-manager-facade-xml/src/main/java/org/opendaylight/controller/config/facade/xml/strategy/ReCreateEditConfigStrategy.java b/opendaylight/config/config-manager-facade-xml/src/main/java/org/opendaylight/controller/config/facade/xml/strategy/ReCreateEditConfigStrategy.java
new file mode 100644 (file)
index 0000000..5d695ac
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2016 Brocade Communications 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.config.facade.xml.strategy;
+
+import java.util.Map;
+import javax.management.InstanceNotFoundException;
+import javax.management.ObjectName;
+import org.opendaylight.controller.config.facade.xml.exception.ConfigHandlingException;
+import org.opendaylight.controller.config.facade.xml.mapping.attributes.fromxml.AttributeConfigElement;
+import org.opendaylight.controller.config.facade.xml.mapping.config.ServiceRegistryWrapper;
+import org.opendaylight.controller.config.util.ConfigTransactionClient;
+import org.opendaylight.controller.config.util.xml.DocumentedException;
+
+/**
+ * Edit strategy that forces re-creation of a module instance even if the config didn't change.
+ *
+ * @author Thomas Pantelis
+ */
+public class ReCreateEditConfigStrategy extends AbstractEditConfigStrategy {
+
+    @Override
+    void handleMissingInstance(Map<String, AttributeConfigElement> configuration, ConfigTransactionClient ta,
+            String module, String instance, ServiceRegistryWrapper services) throws ConfigHandlingException {
+        throw new ConfigHandlingException(
+                String.format("Unable to recreate %s : %s, Existing module instance not found", module, instance),
+                DocumentedException.ErrorType.application,
+                DocumentedException.ErrorTag.operation_failed,
+                DocumentedException.ErrorSeverity.error);
+    }
+
+    @Override
+    void executeStrategy(Map<String, AttributeConfigElement> configuration, ConfigTransactionClient ta,
+            ObjectName objectName, ServiceRegistryWrapper services) throws ConfigHandlingException {
+        try {
+            ta.reCreateModule(objectName);
+        } catch(InstanceNotFoundException e) {
+            throw new ConfigHandlingException(String.format("Unable to recreate instance for %s", objectName),
+                    DocumentedException.ErrorType.application,
+                    DocumentedException.ErrorTag.operation_failed,
+                    DocumentedException.ErrorSeverity.error);
+        }
+    }
+}
index ebf62665406346597baa5cfa4410cb68ef65375f..e426d70df7d28acfdd8ab5860aee962307ca9d15 100644 (file)
@@ -41,6 +41,7 @@ import org.opendaylight.controller.config.manager.impl.jmx.TransactionModuleJMXR
 import org.opendaylight.controller.config.manager.impl.jmx.TransactionModuleJMXRegistrator.TransactionModuleJMXRegistration;
 import org.opendaylight.controller.config.manager.impl.osgi.mapping.BindingContextProvider;
 import org.opendaylight.controller.config.manager.impl.util.InterfacesHelper;
 import org.opendaylight.controller.config.manager.impl.jmx.TransactionModuleJMXRegistrator.TransactionModuleJMXRegistration;
 import org.opendaylight.controller.config.manager.impl.osgi.mapping.BindingContextProvider;
 import org.opendaylight.controller.config.manager.impl.util.InterfacesHelper;
+import org.opendaylight.controller.config.spi.AbstractModule;
 import org.opendaylight.controller.config.spi.Module;
 import org.opendaylight.controller.config.spi.ModuleFactory;
 import org.opendaylight.yangtools.concepts.Identifiable;
 import org.opendaylight.controller.config.spi.Module;
 import org.opendaylight.controller.config.spi.ModuleFactory;
 import org.opendaylight.yangtools.concepts.Identifiable;
@@ -242,6 +243,21 @@ class ConfigTransactionControllerImpl implements
                 moduleFactory, null, dependencyResolver, defaultBean, bundleContext);
     }
 
                 moduleFactory, null, dependencyResolver, defaultBean, bundleContext);
     }
 
+    @Override
+    public synchronized void reCreateModule(ObjectName objectName) throws InstanceNotFoundException {
+        transactionStatus.checkNotCommitStarted();
+        transactionStatus.checkNotAborted();
+        checkTransactionName(objectName);
+        ObjectNameUtil.checkDomain(objectName);
+        ModuleIdentifier moduleIdentifier = ObjectNameUtil.fromON(objectName, ObjectNameUtil.TYPE_MODULE);
+
+        ModuleInternalTransactionalInfo txInfo = dependencyResolverManager.findModuleInternalTransactionalInfo(moduleIdentifier);
+        Module realModule = txInfo.getRealModule();
+        if(realModule instanceof AbstractModule) {
+            ((AbstractModule<?>)realModule).setCanReuseInstance(false);
+        }
+    }
+
     private synchronized ObjectName putConfigBeanToJMXAndInternalMaps(
             ModuleIdentifier moduleIdentifier, Module module,
             ModuleFactory moduleFactory,
     private synchronized ObjectName putConfigBeanToJMXAndInternalMaps(
             ModuleIdentifier moduleIdentifier, Module module,
             ModuleFactory moduleFactory,
index 26ca1391ad4bf32eea9708e2db47f21cc33bdcfa..d49c37ddd80914248c7894c58e7bdc5895c28aec 100644 (file)
@@ -88,6 +88,11 @@ public class ConfigTransactionJMXClient implements ConfigTransactionClient {
         return configTransactionControllerMXBeanProxy.createModule(moduleName, instanceName);
     }
 
         return configTransactionControllerMXBeanProxy.createModule(moduleName, instanceName);
     }
 
+    @Override
+    public void reCreateModule(ObjectName objectName) throws InstanceNotFoundException {
+        configTransactionControllerMXBeanProxy.reCreateModule(objectName);
+    }
+
     @Override
     public void destroyModule(ObjectName objectName)
             throws InstanceNotFoundException {
     @Override
     public void destroyModule(ObjectName objectName)
             throws InstanceNotFoundException {
index e69019405dec6e1be983c153eeab7efbe126d2da..73ab6dad7b06bb8099c3f8e6c5dbf297a56ea525 100644 (file)
@@ -62,6 +62,10 @@ public class TestingConfigTransactionController implements
                 + "=" + moduleName);
     }
 
                 + "=" + moduleName);
     }
 
+    @Override
+    public void reCreateModule(ObjectName objectName) {
+    }
+
     @Override
     public void destroyModule(ObjectName objectName)
             throws InstanceNotFoundException {
     @Override
     public void destroyModule(ObjectName objectName)
             throws InstanceNotFoundException {