Bug 4577 Allow specification of a distinct schema cache directory per netconf device 13/33013/5
authorRyan Goulding <ryandgoulding@gmail.com>
Mon, 11 Jan 2016 20:02:18 +0000 (15:02 -0500)
committerRyan Goulding <ryandgoulding@gmail.com>
Tue, 19 Jan 2016 22:37:31 +0000 (17:37 -0500)
A leaf is added to odl-sal-netconf-connector-cfg.yang to allow specification of
a schema cache directory relative to the "cache" directory.  The leaf defaults to
"schema", so the default directory for loaded yang files is "cache/schema".  It
is useful to specify a distinct cache directory per netconf mount to avoid potential
model conflicts caused by revisionless import statements.

Change-Id: Id930932aa4ff17a7338ddf96e87557b3949b4204
Signed-off-by: Ryan Goulding <ryandgoulding@gmail.com>
opendaylight/netconf/netconf-topology/src/main/java/org/opendaylight/netconf/topology/AbstractNetconfTopology.java
opendaylight/netconf/netconf-topology/src/main/java/org/opendaylight/netconf/topology/impl/ClusteredNetconfTopology.java
opendaylight/netconf/sal-netconf-connector/src/main/java/org/opendaylight/controller/config/yang/md/sal/connector/netconf/NetconfConnectorModule.java
opendaylight/netconf/sal-netconf-connector/src/main/java/org/opendaylight/controller/config/yang/md/sal/connector/netconf/NetconfConnectorModuleFactory.java
opendaylight/netconf/sal-netconf-connector/src/main/yang/netconf-node-topology.yang
opendaylight/netconf/sal-netconf-connector/src/main/yang/odl-sal-netconf-connector-cfg.yang

index b25a7bd6ae61ee1f435eb21166fceacd1efd38ce..de0dc6921577e3356c57cc7550494d409b93e9ae 100644 (file)
@@ -9,6 +9,7 @@
 package org.opendaylight.netconf.topology;
 
 import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
 import com.google.common.util.concurrent.FutureCallback;
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
@@ -19,8 +20,7 @@ import java.net.InetSocketAddress;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Set;
+import java.util.Map;
 import org.opendaylight.controller.config.threadpool.ScheduledThreadPool;
 import org.opendaylight.controller.config.threadpool.ThreadPool;
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
@@ -75,7 +75,7 @@ import org.opendaylight.yangtools.yang.parser.util.TextToASTTransformer;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-public abstract class AbstractNetconfTopology implements NetconfTopology, BindingAwareProvider, Provider{
+public abstract class AbstractNetconfTopology implements NetconfTopology, BindingAwareProvider, Provider {
 
     private static final Logger LOG = LoggerFactory.getLogger(AbstractNetconfTopology.class);
 
@@ -87,9 +87,67 @@ public abstract class AbstractNetconfTopology implements NetconfTopology, Bindin
     private static final long DEFAULT_CONNECTION_TIMEOUT_MILLIS = 20000L;
     private static final BigDecimal DEFAULT_SLEEP_FACTOR = new BigDecimal(1.5);
 
-    private static FilesystemSchemaSourceCache<YangTextSchemaSource> CACHE = null;
-    //keep track of already initialized repositories to avoid adding redundant listeners
-    private static final Set<SchemaRepository> INITIALIZED_SCHEMA_REPOSITORIES = new HashSet<>();
+    // constants related to Schema Cache(s)
+    /**
+     * Filesystem based caches are stored relative to the cache directory.
+     */
+    private static final String CACHE_DIRECTORY = "cache";
+
+    /**
+     * The default cache directory relative to <code>CACHE_DIRECTORY</code>
+     */
+    private static final String DEFAULT_CACHE_DIRECTORY = "schema";
+
+    /**
+     * The qualified schema cache directory <code>cache/schema</code>
+     */
+    private static final String QUALIFIED_DEFAULT_CACHE_DIRECTORY = CACHE_DIRECTORY + File.separator+ DEFAULT_CACHE_DIRECTORY;
+
+    /**
+     * The name for the default schema repository
+     */
+    private static final String DEFAULT_SCHEMA_REPOSITORY_NAME = "sal-netconf-connector";
+
+    /**
+     * The default schema repository in the case that one is not specified.
+     */
+    private static final SharedSchemaRepository DEFAULT_SCHEMA_REPOSITORY =
+            new SharedSchemaRepository(DEFAULT_SCHEMA_REPOSITORY_NAME);
+
+    /**
+     * The default <code>FilesystemSchemaSourceCache</code>, which stores cached files in <code>cache/schema</code>.
+     */
+    private static final FilesystemSchemaSourceCache<YangTextSchemaSource> DEFAULT_CACHE =
+            new FilesystemSchemaSourceCache<>(DEFAULT_SCHEMA_REPOSITORY, YangTextSchemaSource.class,
+                    new File(QUALIFIED_DEFAULT_CACHE_DIRECTORY));
+
+    /**
+     * The default factory for creating <code>SchemaContext</code> instances.
+     */
+    private static final SchemaContextFactory DEFAULT_SCHEMA_CONTEXT_FACTORY =
+            DEFAULT_SCHEMA_REPOSITORY.createSchemaContextFactory(SchemaSourceFilter.ALWAYS_ACCEPT);
+
+    /**
+     * Keeps track of initialized Schema resources.  A Map is maintained in which the key represents the name
+     * of the schema cache directory, and the value is a corresponding <code>SchemaResourcesDTO</code>.  The
+     * <code>SchemaResourcesDTO</code> is essentially a container that allows for the extraction of the
+     * <code>SchemaRegistry</code> and <code>SchemaContextFactory</code> which should be used for a particular
+     * Netconf mount.  Access to <code>schemaResourcesDTOs</code> should be surrounded by appropriate
+     * synchronization locks.
+     */
+    private static volatile Map<String, NetconfDevice.SchemaResourcesDTO> schemaResourcesDTOs = new HashMap<>();
+
+    // Initializes default constant instances for the case when the default schema repository
+    // directory cache/schema is used.
+    static {
+        schemaResourcesDTOs.put(DEFAULT_CACHE_DIRECTORY,
+                new NetconfDevice.SchemaResourcesDTO(DEFAULT_SCHEMA_REPOSITORY,
+                        DEFAULT_SCHEMA_CONTEXT_FACTORY,
+                        new NetconfStateSchemas.NetconfStateSchemasResolverImpl()));
+        DEFAULT_SCHEMA_REPOSITORY.registerSchemaSourceListener(DEFAULT_CACHE);
+        DEFAULT_SCHEMA_REPOSITORY.registerSchemaSourceListener(
+                TextToASTTransformer.create(DEFAULT_SCHEMA_REPOSITORY, DEFAULT_SCHEMA_REPOSITORY));
+    }
 
     protected final String topologyId;
     private final NetconfClientDispatcher clientDispatcher;
@@ -100,8 +158,8 @@ public abstract class AbstractNetconfTopology implements NetconfTopology, Bindin
     protected final ThreadPool processingExecutor;
     protected final SharedSchemaRepository sharedSchemaRepository;
 
-    protected SchemaSourceRegistry schemaRegistry = null;
-    protected SchemaContextFactory schemaContextFactory = null;
+    protected SchemaSourceRegistry schemaRegistry = DEFAULT_SCHEMA_REPOSITORY;
+    protected SchemaContextFactory schemaContextFactory = DEFAULT_SCHEMA_CONTEXT_FACTORY;
 
     protected DOMMountPointService mountPointService = null;
     protected DataBroker dataBroker = null;
@@ -119,8 +177,6 @@ public abstract class AbstractNetconfTopology implements NetconfTopology, Bindin
         this.keepaliveExecutor = keepaliveExecutor;
         this.processingExecutor = processingExecutor;
         this.sharedSchemaRepository = schemaRepositoryProvider.getSharedSchemaRepository();
-
-        initFilesystemSchemaSourceCache(sharedSchemaRepository);
     }
 
     protected void registerToSal(BindingAwareProvider baProvider, Provider provider) {
@@ -128,20 +184,6 @@ public abstract class AbstractNetconfTopology implements NetconfTopology, Bindin
         bindingAwareBroker.registerProvider(baProvider);
     }
 
-    private void initFilesystemSchemaSourceCache(SharedSchemaRepository repository) {
-        LOG.warn("Schema repository used: {}", repository.getIdentifier());
-        if (CACHE == null) {
-            CACHE = new FilesystemSchemaSourceCache<>(repository, YangTextSchemaSource.class, new File("cache/schema"));
-        }
-        if (!INITIALIZED_SCHEMA_REPOSITORIES.contains(repository)) {
-            repository.registerSchemaSourceListener(CACHE);
-            repository.registerSchemaSourceListener(TextToASTTransformer.create(repository, repository));
-            INITIALIZED_SCHEMA_REPOSITORIES.add(repository);
-        }
-        setSchemaRegistry(repository);
-        setSchemaContextFactory(repository.createSchemaContextFactory(SchemaSourceFilter.ALWAYS_ACCEPT));
-    }
-
     public void setSchemaRegistry(final SchemaSourceRegistry schemaRegistry) {
         this.schemaRegistry = schemaRegistry;
     }
@@ -237,15 +279,88 @@ public abstract class AbstractNetconfTopology implements NetconfTopology, Bindin
             salFacade = new KeepaliveSalFacade(remoteDeviceId, salFacade, keepaliveExecutor.getExecutor(), keepaliveDelay);
         }
 
-        NetconfDevice.SchemaResourcesDTO schemaResourcesDTO =
-                new NetconfDevice.SchemaResourcesDTO(schemaRegistry, schemaContextFactory, new NetconfStateSchemas.NetconfStateSchemasResolverImpl());
+        final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = setupSchemaCacheDTO(nodeId, node);
 
-        NetconfDevice device = new NetconfDevice(schemaResourcesDTO, remoteDeviceId, salFacade,
+        final NetconfDevice device = new NetconfDevice(schemaResourcesDTO, remoteDeviceId, salFacade,
                 processingExecutor.getExecutor(), reconnectOnChangedSchema);
 
         return new NetconfConnectorDTO(new NetconfDeviceCommunicator(remoteDeviceId, device), salFacade);
     }
 
+    protected NetconfDevice.SchemaResourcesDTO setupSchemaCacheDTO(final NodeId nodeId, final NetconfNode node) {
+        // Setup information related to the SchemaRegistry, SchemaResourceFactory, etc.
+        NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = null;
+        final String moduleSchemaCacheDirectory = node.getSchemaCacheDirectory();
+        // Only checks to ensure the String is not empty or null;  further checks related to directory accessibility and file permissions
+        // are handled during the FilesystemScehamSourceCache initialization.
+        if (!Strings.isNullOrEmpty(moduleSchemaCacheDirectory)) {
+            // If a custom schema cache directory is specified, create the backing DTO; otherwise, the SchemaRegistry and
+            // SchemaContextFactory remain the default values.
+            if (!moduleSchemaCacheDirectory.equals(DEFAULT_CACHE_DIRECTORY)) {
+                // Multiple modules may be created at once;  synchronize to avoid issues with data consistency among threads.
+                synchronized(schemaResourcesDTOs) {
+                    // Look for the cached DTO to reuse SchemaRegistry and SchemaContextFactory variables if they already exist
+                    final NetconfDevice.SchemaResourcesDTO dto =
+                            schemaResourcesDTOs.get(moduleSchemaCacheDirectory);
+                    if (dto == null) {
+                        schemaResourcesDTO = createSchemaResourcesDTO(moduleSchemaCacheDirectory, nodeId.getValue());
+                        schemaRegistry.registerSchemaSourceListener(
+                                TextToASTTransformer.create((SchemaRepository) schemaRegistry, schemaRegistry));
+                        schemaResourcesDTOs.put(moduleSchemaCacheDirectory, schemaResourcesDTO);
+                    } else {
+                        setSchemaContextFactory(dto.getSchemaContextFactory());
+                        setSchemaRegistry(dto.getSchemaRegistry());
+                        schemaResourcesDTO = dto;
+                    }
+                }
+                LOG.info("Netconf connector for device {} will use schema cache directory {} instead of {}",
+                        nodeId.getValue(), moduleSchemaCacheDirectory, DEFAULT_CACHE_DIRECTORY);
+            }
+        } else {
+            LOG.warn("schema-cache-directory for {} is null or empty;  using the default {}",
+                    nodeId.getValue(), QUALIFIED_DEFAULT_CACHE_DIRECTORY);
+        }
+
+        if (schemaResourcesDTO == null) {
+            schemaResourcesDTO = new NetconfDevice.SchemaResourcesDTO(schemaRegistry, schemaContextFactory,
+                    new NetconfStateSchemas.NetconfStateSchemasResolverImpl());
+        }
+
+        return schemaResourcesDTO;
+    }
+
+    /**
+     * Creates the backing Schema classes for a particular directory.
+     *
+     * @param moduleSchemaCacheDirectory The string directory relative to "cache"
+     * @return A DTO containing the Schema classes for the Netconf mount.
+     */
+    private NetconfDevice.SchemaResourcesDTO createSchemaResourcesDTO(final String moduleSchemaCacheDirectory,
+            final String instanceName) {
+
+        final SharedSchemaRepository repository = new SharedSchemaRepository(instanceName);
+        final SchemaContextFactory schemaContextFactory
+                = repository.createSchemaContextFactory(SchemaSourceFilter.ALWAYS_ACCEPT);
+        setSchemaRegistry(repository);
+        setSchemaContextFactory(schemaContextFactory);
+        final FilesystemSchemaSourceCache<YangTextSchemaSource> deviceCache =
+                createDeviceFilesystemCache(moduleSchemaCacheDirectory);
+        repository.registerSchemaSourceListener(deviceCache);
+        return new NetconfDevice.SchemaResourcesDTO(repository, schemaContextFactory,
+                new NetconfStateSchemas.NetconfStateSchemasResolverImpl());
+    }
+
+    /**
+     * Creates a <code>FilesystemSchemaSourceCache</code> for the custom schema cache directory.
+     *
+     * @param schemaCacheDirectory The custom cache directory relative to "cache"
+     * @return A <code>FilesystemSchemaSourceCache</code> for the custom schema cache directory
+     */
+    private FilesystemSchemaSourceCache<YangTextSchemaSource> createDeviceFilesystemCache(final String schemaCacheDirectory) {
+        final String relativeSchemaCacheDirectory = CACHE_DIRECTORY + File.separator + schemaCacheDirectory;
+        return new FilesystemSchemaSourceCache<>(schemaRegistry, YangTextSchemaSource.class, new File(relativeSchemaCacheDirectory));
+    }
+
     public NetconfReconnectingClientConfiguration getClientConfig(final NetconfClientSessionListener listener, NetconfNode node) {
 
         //setup default values since default value is not supported yet in mdsal
index 275a2e1d86f1f9dfbe915f216ae0e3707ca84bb6..738728406172a4f715100c7b6f1b90afb3744aa1 100644 (file)
@@ -33,7 +33,6 @@ import org.opendaylight.netconf.client.NetconfClientDispatcher;
 import org.opendaylight.netconf.client.NetconfClientSessionListener;
 import org.opendaylight.netconf.sal.connect.api.RemoteDeviceHandler;
 import org.opendaylight.netconf.sal.connect.netconf.NetconfDevice;
-import org.opendaylight.netconf.sal.connect.netconf.NetconfStateSchemas;
 import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfSessionPreferences;
 import org.opendaylight.netconf.sal.connect.netconf.sal.KeepaliveSalFacade;
 import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId;
@@ -158,10 +157,9 @@ public class ClusteredNetconfTopology extends AbstractNetconfTopology implements
             salFacade = new KeepaliveSalFacade(remoteDeviceId, salFacade, keepaliveExecutor.getExecutor(), keepaliveDelay);
         }
 
-        NetconfDevice.SchemaResourcesDTO schemaResourcesDTO =
-                new NetconfDevice.SchemaResourcesDTO(schemaRegistry, schemaContextFactory, new NetconfStateSchemas.NetconfStateSchemasResolverImpl());
+        final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = setupSchemaCacheDTO(nodeId, node);
 
-        NetconfDevice device = new ClusteredNetconfDevice(schemaResourcesDTO, remoteDeviceId, salFacade,
+        final NetconfDevice device = new ClusteredNetconfDevice(schemaResourcesDTO, remoteDeviceId, salFacade,
                 processingExecutor.getExecutor(), sharedSchemaRepository, actorSystem, topologyId, nodeId.getValue(), TypedActor.context());
 
         return new NetconfConnectorDTO(new ClusteredNetconfDeviceCommunicator(remoteDeviceId, device, entityOwnershipService), salFacade);
index 9df4ad42efefcd71fdebb277d3599aa112e27d17..ac3f5a5572a70d8b224b6a22775845cdfb2a2577 100644 (file)
@@ -11,13 +11,14 @@ import static org.opendaylight.controller.config.api.JmxAttributeValidationExcep
 import static org.opendaylight.controller.config.api.JmxAttributeValidationException.checkNotNull;
 
 import com.google.common.base.Optional;
+import com.google.common.base.Strings;
 import io.netty.util.concurrent.EventExecutor;
 import java.io.File;
 import java.math.BigDecimal;
 import java.net.InetSocketAddress;
-import java.util.HashSet;
+import java.util.HashMap;
 import java.util.List;
-import java.util.Set;
+import java.util.Map;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledExecutorService;
@@ -64,26 +65,84 @@ public final class NetconfConnectorModule extends org.opendaylight.controller.co
 {
     private static final Logger LOG = LoggerFactory.getLogger(NetconfConnectorModule.class);
 
-    private static FilesystemSchemaSourceCache<YangTextSchemaSource> CACHE = null;
-    //when no topology is defined in connector config, we need to add a default schema repository to mantain backwards compatibility
-    private static SharedSchemaRepository DEFAULT_SCHEMA_REPOSITORY = null;
-
-    //keep track of already initialized repositories to avoid adding redundant listeners
-    private static final Set<SchemaRepository> INITIALIZED_SCHEMA_REPOSITORIES = new HashSet<>();
+    /**
+     * Filesystem based caches are stored relative to the cache directory.
+     */
+    private static final String CACHE_DIRECTORY = "cache";
+
+    /**
+     * The default cache directory relative to <code>CACHE_DIRECTORY</code>
+     */
+    private static final String DEFAULT_CACHE_DIRECTORY = "schema";
+
+    /**
+     * The qualified schema cache directory <code>cache/schema</code>
+     */
+    private static final String QUALIFIED_DEFAULT_CACHE_DIRECTORY = CACHE_DIRECTORY + File.separator+ DEFAULT_CACHE_DIRECTORY;
+
+    /**
+     * The name for the default schema repository
+     */
+    private static final String DEFAULT_SCHEMA_REPOSITORY_NAME = "sal-netconf-connector";
+
+    /**
+     * The default schema repository in the case that one is not specified.
+     */
+    private static final SharedSchemaRepository DEFAULT_SCHEMA_REPOSITORY =
+            new SharedSchemaRepository(DEFAULT_SCHEMA_REPOSITORY_NAME);
+
+    /**
+     * The default <code>FilesystemSchemaSourceCache</code>, which stores cached files in <code>cache/schema</code>.
+     */
+    private static final FilesystemSchemaSourceCache<YangTextSchemaSource> DEFAULT_CACHE =
+            new FilesystemSchemaSourceCache<>(DEFAULT_SCHEMA_REPOSITORY, YangTextSchemaSource.class,
+                    new File(QUALIFIED_DEFAULT_CACHE_DIRECTORY));
+
+    /**
+     * The default factory for creating <code>SchemaContext</code> instances.
+     */
+    private static final SchemaContextFactory DEFAULT_SCHEMA_CONTEXT_FACTORY =
+            DEFAULT_SCHEMA_REPOSITORY.createSchemaContextFactory(SchemaSourceFilter.ALWAYS_ACCEPT);
+
+    /**
+     * Keeps track of initialized Schema resources.  A Map is maintained in which the key represents the name
+     * of the schema cache directory, and the value is a corresponding <code>SchemaResourcesDTO</code>.  The
+     * <code>SchemaResourcesDTO</code> is essentially a container that allows for the extraction of the
+     * <code>SchemaRegistry</code> and <code>SchemaContextFactory</code> which should be used for a particular
+     * Netconf mount.  Access to <code>schemaResourcesDTOs</code> should be surrounded by appropriate
+     * synchronization locks.
+     */
+    private static volatile Map<String, NetconfDevice.SchemaResourcesDTO> schemaResourcesDTOs = new HashMap<>();
+
+    // Initializes default constant instances for the case when the default schema repository
+    // directory cache/schema is used.
+    static {
+        schemaResourcesDTOs.put(DEFAULT_CACHE_DIRECTORY,
+                new NetconfDevice.SchemaResourcesDTO(DEFAULT_SCHEMA_REPOSITORY,
+                        DEFAULT_SCHEMA_CONTEXT_FACTORY,
+                        new NetconfStateSchemas.NetconfStateSchemasResolverImpl()));
+        DEFAULT_SCHEMA_REPOSITORY.registerSchemaSourceListener(DEFAULT_CACHE);
+        DEFAULT_SCHEMA_REPOSITORY.registerSchemaSourceListener(
+                TextToASTTransformer.create(DEFAULT_SCHEMA_REPOSITORY, DEFAULT_SCHEMA_REPOSITORY));
+    }
 
     private BundleContext bundleContext;
     private Optional<NetconfSessionPreferences> userCapabilities;
-    private SchemaSourceRegistry schemaRegistry;
-    private SchemaContextFactory schemaContextFactory;
+    private SchemaSourceRegistry schemaRegistry = DEFAULT_SCHEMA_REPOSITORY;
+    private SchemaContextFactory schemaContextFactory = DEFAULT_SCHEMA_CONTEXT_FACTORY;
 
     private Broker domRegistry;
     private NetconfClientDispatcher clientDispatcher;
     private BindingAwareBroker bindingRegistry;
     private ThreadPool processingExecutor;
     private ScheduledThreadPool keepaliveExecutor;
-    private SharedSchemaRepository sharedSchemaRepository;
     private EventExecutor eventExecutor;
 
+    /**
+     * The name associated with the Netconf mount point.  This value is passed from <code>NetconfConnectorModuleFactory</code>.
+     */
+    private String instanceName;
+
     public NetconfConnectorModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier, final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
         super(identifier, dependencyResolver);
     }
@@ -142,8 +201,43 @@ public final class NetconfConnectorModule extends org.opendaylight.controller.co
             salFacade = new KeepaliveSalFacade(id, salFacade, executor, keepaliveDelay);
         }
 
-        final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO =
-                new NetconfDevice.SchemaResourcesDTO(schemaRegistry, schemaContextFactory, new NetconfStateSchemas.NetconfStateSchemasResolverImpl());
+        // Setup information related to the SchemaRegistry, SchemaResourceFactory, etc.
+        NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = null;
+        final String moduleSchemaCacheDirectory = getSchemaCacheDirectory();
+        // Only checks to ensure the String is not empty or null;  further checks related to directory accessibility and file permissions
+        // are handled during the FilesystemScehamSourceCache initialization.
+        if (!Strings.isNullOrEmpty(moduleSchemaCacheDirectory)) {
+            // If a custom schema cache directory is specified, create the backing DTO; otherwise, the SchemaRegistry and
+            // SchemaContextFactory remain the default values.
+            if (!moduleSchemaCacheDirectory.equals(DEFAULT_CACHE_DIRECTORY)) {
+                // Multiple modules may be created at once;  synchronize to avoid issues with data consistency among threads.
+                synchronized(schemaResourcesDTOs) {
+                    // Look for the cached DTO to reuse SchemaRegistry and SchemaContextFactory variables if they already exist
+                    final NetconfDevice.SchemaResourcesDTO dto =
+                            schemaResourcesDTOs.get(moduleSchemaCacheDirectory);
+                    if (dto == null) {
+                        schemaResourcesDTO = createSchemaResourcesDTO(moduleSchemaCacheDirectory);
+                        schemaRegistry.registerSchemaSourceListener(
+                                TextToASTTransformer.create((SchemaRepository) schemaRegistry, schemaRegistry));
+                        schemaResourcesDTOs.put(moduleSchemaCacheDirectory, schemaResourcesDTO);
+                    } else {
+                        setSchemaContextFactory(dto.getSchemaContextFactory());
+                        setSchemaRegistry(dto.getSchemaRegistry());
+                        schemaResourcesDTO = dto;
+                    }
+                }
+                LOG.info("Netconf connector for device {} will use schema cache directory {} instead of {}",
+                        instanceName, moduleSchemaCacheDirectory, DEFAULT_CACHE_DIRECTORY);
+            }
+        } else {
+            LOG.warn("schema-cache-directory for {} is null or empty;  using the default {}",
+                    instanceName, QUALIFIED_DEFAULT_CACHE_DIRECTORY);
+        }
+
+        if (schemaResourcesDTO == null) {
+            schemaResourcesDTO = new NetconfDevice.SchemaResourcesDTO(schemaRegistry, schemaContextFactory,
+                    new NetconfStateSchemas.NetconfStateSchemasResolverImpl());
+        }
 
         final NetconfDevice device =
                 new NetconfDevice(schemaResourcesDTO, id, salFacade, globalProcessingExecutor, getReconnectOnChangedSchema());
@@ -161,6 +255,36 @@ public final class NetconfConnectorModule extends org.opendaylight.controller.co
         return new SalConnectorCloseable(listener, salFacade);
     }
 
+    /**
+     * Creates the backing Schema classes for a particular directory.
+     *
+     * @param moduleSchemaCacheDirectory The string directory relative to "cache"
+     * @return A DTO containing the Schema classes for the Netconf mount.
+     */
+    private NetconfDevice.SchemaResourcesDTO createSchemaResourcesDTO(final String moduleSchemaCacheDirectory) {
+        final SharedSchemaRepository repository = new SharedSchemaRepository(instanceName);
+        final SchemaContextFactory schemaContextFactory
+                = repository.createSchemaContextFactory(SchemaSourceFilter.ALWAYS_ACCEPT);
+        setSchemaRegistry(repository);
+        setSchemaContextFactory(schemaContextFactory);
+        final FilesystemSchemaSourceCache<YangTextSchemaSource> deviceCache =
+                createDeviceFilesystemCache(moduleSchemaCacheDirectory);
+        repository.registerSchemaSourceListener(deviceCache);
+        return new NetconfDevice.SchemaResourcesDTO(repository, schemaContextFactory,
+                new NetconfStateSchemas.NetconfStateSchemasResolverImpl());
+    }
+
+    /**
+     * Creates a <code>FilesystemSchemaSourceCache</code> for the custom schema cache directory.
+     *
+     * @param schemaCacheDirectory The custom cache directory relative to "cache"
+     * @return A <code>FilesystemSchemaSourceCache</code> for the custom schema cache directory
+     */
+    private FilesystemSchemaSourceCache<YangTextSchemaSource> createDeviceFilesystemCache(final String schemaCacheDirectory) {
+        final String relativeSchemaCacheDirectory = CACHE_DIRECTORY + File.separator + schemaCacheDirectory;
+        return new FilesystemSchemaSourceCache<>(schemaRegistry, YangTextSchemaSource.class, new File(relativeSchemaCacheDirectory));
+    }
+
     private void initDependencies() {
         domRegistry = getDomRegistryDependency();
         clientDispatcher = getClientDispatcherDependency();
@@ -186,25 +310,6 @@ public final class NetconfConnectorModule extends org.opendaylight.controller.co
         } else {
             keepaliveExecutor = getKeepaliveExecutorDependency();
         }
-
-        if (DEFAULT_SCHEMA_REPOSITORY == null) {
-            DEFAULT_SCHEMA_REPOSITORY = new SharedSchemaRepository("default shared schema repo");
-        }
-        initFilesystemSchemaSourceCache(DEFAULT_SCHEMA_REPOSITORY);
-    }
-
-    private void initFilesystemSchemaSourceCache(SharedSchemaRepository repository) {
-        LOG.warn("Schema repository used: {}", repository.getIdentifier());
-        if (CACHE == null) {
-            CACHE = new FilesystemSchemaSourceCache<>(repository, YangTextSchemaSource.class, new File("cache/schema"));
-        }
-        if (!INITIALIZED_SCHEMA_REPOSITORIES.contains(repository)) {
-            repository.registerSchemaSourceListener(CACHE);
-            repository.registerSchemaSourceListener(TextToASTTransformer.create(repository, repository));
-            INITIALIZED_SCHEMA_REPOSITORIES.add(repository);
-        }
-        setSchemaRegistry(repository);
-        setSchemaContextFactory(repository.createSchemaContextFactory(SchemaSourceFilter.ALWAYS_ACCEPT));
     }
 
     private boolean shouldSendKeepalive() {
@@ -315,4 +420,8 @@ public final class NetconfConnectorModule extends org.opendaylight.controller.co
     public void setSchemaContextFactory(final SchemaContextFactory schemaContextFactory) {
         this.schemaContextFactory = schemaContextFactory;
     }
+
+    public void setInstanceName(final String instanceName) {
+        this.instanceName = instanceName;
+    }
 }
index 2b31fe05d0e8bba5aea24b984ea62ae287de4b62..4132e1cd89a74078e9f31a95f4699ab694a79f55 100644 (file)
@@ -23,6 +23,7 @@ public class NetconfConnectorModuleFactory extends
             final DynamicMBeanWithInstance old, final BundleContext bundleContext) throws Exception {
         final NetconfConnectorModule module = (NetconfConnectorModule) super.createModule(instanceName, dependencyResolver,
                 old, bundleContext);
+        module.setInstanceName(instanceName);
         return module;
     }
 
@@ -30,6 +31,7 @@ public class NetconfConnectorModuleFactory extends
     public Module createModule(final String instanceName, final DependencyResolver dependencyResolver, final BundleContext bundleContext) {
         final NetconfConnectorModule module = (NetconfConnectorModule) super.createModule(instanceName, dependencyResolver,
                 bundleContext);
+        module.setInstanceName(instanceName);
         return module;
     }
 }
index ca3b81d0a746cce2231dcf51b40631cc8ff18090..bc0983dea1b9a862bc4965f19ddc9fc585c82d8b 100644 (file)
@@ -174,6 +174,16 @@ module netconf-node-topology {
 
     }
 
+    grouping netconf-schema-storage {
+        leaf schema-cache-directory {
+            config true;
+            type string;
+            default "schema";
+            description "The destination schema repository for yang files relative to the cache directory.  This may be specified per netconf mount
+                         so that the loaded yang files are stored to a distinct directory to avoid potential conflict.";
+        }
+    }
+
     grouping netconf-node-fields {
 
         uses netconf-node-credentials;
@@ -182,6 +192,8 @@ module netconf-node-topology {
 
         uses netconf-node-connection-status;
 
+        uses netconf-schema-storage;
+
     }
 
     augment "/nt:network-topology/nt:topology/nt:node" {
index da197e31cf318fdf225dfe8b67e43358fb0a86b0..b19597052854a42d89927d1b5c4639a4bbd45331 100644 (file)
@@ -63,6 +63,13 @@ module odl-sal-netconf-connector-cfg {
                 type string;
             }
 
+            leaf schema-cache-directory {
+                type string;
+                default "schema";
+                description "The destination schema repository for yang files relative to the cache directory.  This may be specified per netconf mount
+                             so that the loaded yang files are stored to a distinct directory to avoid potential conflict.";
+            }
+
             container yang-module-capabilities {
                 leaf-list capability {
                     type string;