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 8afc80a..bd9002c 100644 (file)
@@ -29,6 +29,20 @@ public interface ConfigTransactionController extends LookupRegistry, ServiceRefe
     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.
      *
index e60a047..7fcc0cc 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 boolean canReuseInstance = true;
 
     /**
      * Called when module is configured.
@@ -58,6 +59,10 @@ public abstract class AbstractModule<M extends AbstractModule<M>> implements org
         return identifier;
     }
 
+    public final void setCanReuseInstance(boolean canReuseInstance) {
+        this.canReuseInstance = canReuseInstance;
+    }
+
     /**
      *
      * 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) {
-            if (oldInstance != null && canReuseInstance(oldModule)) {
+            if (oldInstance != null && canReuseInstance && canReuseInstance(oldModule)) {
                 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)
-        return getClass().isInstance(oldModule) ? canReuseInstance((M) oldModule) : false;
+        return canReuseInstance && getClass().isInstance(oldModule) ? canReuseInstance((M) oldModule) : false;
     }
 
     /**
index 3ecee28..9bee7a1 100644 (file)
@@ -9,7 +9,6 @@
 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;
@@ -130,6 +129,12 @@ public class Config {
         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
index c5af3ec..47f5078 100644 (file)
@@ -17,7 +17,7 @@ public enum EditStrategyType {
     // 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);
 
@@ -31,6 +31,7 @@ public enum EditStrategyType {
         case none:
         case remove:
         case delete:
+        case recreate:
             return false;
         case replace:
             return true;
@@ -64,6 +65,8 @@ public enum EditStrategyType {
             return new DeleteEditConfigStrategy();
         case remove:
             return new RemoveEditConfigStrategy();
+        case recreate:
+            return new ReCreateEditConfigStrategy();
         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 ebf6266..e426d70 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.spi.AbstractModule;
 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);
     }
 
+    @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,
index 26ca139..d49c37d 100644 (file)
@@ -88,6 +88,11 @@ public class ConfigTransactionJMXClient implements ConfigTransactionClient {
         return configTransactionControllerMXBeanProxy.createModule(moduleName, instanceName);
     }
 
+    @Override
+    public void reCreateModule(ObjectName objectName) throws InstanceNotFoundException {
+        configTransactionControllerMXBeanProxy.reCreateModule(objectName);
+    }
+
     @Override
     public void destroyModule(ObjectName objectName)
             throws InstanceNotFoundException {
index e690194..73ab6da 100644 (file)
@@ -62,6 +62,10 @@ public class TestingConfigTransactionController implements
                 + "=" + moduleName);
     }
 
+    @Override
+    public void reCreateModule(ObjectName objectName) {
+    }
+
     @Override
     public void destroyModule(ObjectName objectName)
             throws InstanceNotFoundException {