From 651edfb5421fdab4244008dff950fac6710aeb6b Mon Sep 17 00:00:00 2001 From: Robert Varga Date: Sat, 25 Apr 2020 11:46:44 +0200 Subject: [PATCH] Factor out SchemaResourceManager Management of schema cache on the filesystem is copy&pasted in two places, which creates a compatibility problem. Furthermore current code relies on static wiring, which is going to be non-workable very soon. Refactor AbstractTopology to split out schema management into a separate component within sal-netconf-connector, which is injected into both AbstractTopology subclasses and clustered topology. JIRA: NETCONF-672 Change-Id: If1284a08f9525a3396f2d39e2a4399366edee7ae Signed-off-by: Robert Varga --- .../callhome/mount/BaseCallHomeTopology.java | 4 +- .../mount/CallHomeMountDispatcher.java | 8 +- .../callhome/mount/CallHomeTopology.java | 6 +- .../OSGI-INF/blueprint/callhome-topology.xml | 8 +- .../mount/CallHomeMountDispatcherTest.java | 6 +- .../topology/impl/NetconfTopologyImpl.java | 6 +- .../OSGI-INF/blueprint/netconf-topology.xml | 8 +- .../impl/NetconfTopologyImplTest.java | 38 +-- .../impl/NetconfTopologyManager.java | 14 +- .../impl/utils/NetconfTopologyUtils.java | 166 ---------- .../blueprint/netconf-topology-singleton.xml | 3 + .../impl/MountPointEndToEndTest.java | 30 +- .../singleton/impl/NetconfNodeActorTest.java | 7 +- .../impl/NetconfTopologyManagerTest.java | 3 +- .../impl/RemoteDeviceConnectorImplTest.java | 5 +- .../api/SchemaRepositoryProvider.java | 16 - .../topology/spi/AbstractNetconfTopology.java | 289 ++++-------------- .../spi/SharedSchemaRepositoryProvider.java | 25 -- netconf/sal-netconf-connector/pom.xml | 6 + .../connect/api/SchemaResourceManager.java | 20 ++ .../impl/DefaultSchemaResourceManager.java | 120 ++++++++ .../blueprint/sal-netconf-connector.xml | 7 + 22 files changed, 292 insertions(+), 503 deletions(-) delete mode 100644 netconf/netconf-topology/src/main/java/org/opendaylight/netconf/topology/api/SchemaRepositoryProvider.java delete mode 100644 netconf/netconf-topology/src/main/java/org/opendaylight/netconf/topology/spi/SharedSchemaRepositoryProvider.java create mode 100644 netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/api/SchemaResourceManager.java create mode 100644 netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/impl/DefaultSchemaResourceManager.java diff --git a/netconf/callhome-provider/src/main/java/org/opendaylight/netconf/callhome/mount/BaseCallHomeTopology.java b/netconf/callhome-provider/src/main/java/org/opendaylight/netconf/callhome/mount/BaseCallHomeTopology.java index 1405267c2d..844e474586 100644 --- a/netconf/callhome-provider/src/main/java/org/opendaylight/netconf/callhome/mount/BaseCallHomeTopology.java +++ b/netconf/callhome-provider/src/main/java/org/opendaylight/netconf/callhome/mount/BaseCallHomeTopology.java @@ -15,7 +15,7 @@ import org.opendaylight.mdsal.binding.api.DataBroker; import org.opendaylight.mdsal.dom.api.DOMMountPointService; import org.opendaylight.netconf.client.NetconfClientDispatcher; import org.opendaylight.netconf.sal.connect.api.DeviceActionFactory; -import org.opendaylight.netconf.topology.api.SchemaRepositoryProvider; +import org.opendaylight.netconf.sal.connect.api.SchemaResourceManager; import org.opendaylight.netconf.topology.spi.AbstractNetconfTopology; abstract class BaseCallHomeTopology extends AbstractNetconfTopology { @@ -23,7 +23,7 @@ abstract class BaseCallHomeTopology extends AbstractNetconfTopology { final EventExecutor eventExecutor, final ScheduledThreadPool keepaliveExecutor, final ThreadPool processingExecutor, - final SchemaRepositoryProvider schemaRepositoryProvider, + final SchemaResourceManager schemaRepositoryProvider, final DataBroker dataBroker, final DOMMountPointService mountPointService, final AAAEncryptionService encryptionService, diff --git a/netconf/callhome-provider/src/main/java/org/opendaylight/netconf/callhome/mount/CallHomeMountDispatcher.java b/netconf/callhome-provider/src/main/java/org/opendaylight/netconf/callhome/mount/CallHomeMountDispatcher.java index b73cddfda4..de843caab2 100644 --- a/netconf/callhome-provider/src/main/java/org/opendaylight/netconf/callhome/mount/CallHomeMountDispatcher.java +++ b/netconf/callhome-provider/src/main/java/org/opendaylight/netconf/callhome/mount/CallHomeMountDispatcher.java @@ -25,7 +25,7 @@ import org.opendaylight.netconf.client.NetconfClientSession; import org.opendaylight.netconf.client.conf.NetconfClientConfiguration; import org.opendaylight.netconf.client.conf.NetconfReconnectingClientConfiguration; import org.opendaylight.netconf.sal.connect.api.DeviceActionFactory; -import org.opendaylight.netconf.topology.api.SchemaRepositoryProvider; +import org.opendaylight.netconf.sal.connect.api.SchemaResourceManager; import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId; import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node; import org.slf4j.Logger; @@ -39,7 +39,7 @@ public class CallHomeMountDispatcher implements NetconfClientDispatcher, CallHom private final EventExecutor eventExecutor; private final ScheduledThreadPool keepaliveExecutor; private final ThreadPool processingExecutor; - private final SchemaRepositoryProvider schemaRepositoryProvider; + private final SchemaResourceManager schemaRepositoryProvider; private final CallHomeMountSessionManager sessionManager; private final DataBroker dataBroker; private final DOMMountPointService mountService; @@ -56,7 +56,7 @@ public class CallHomeMountDispatcher implements NetconfClientDispatcher, CallHom public CallHomeMountDispatcher(final String topologyId, final EventExecutor eventExecutor, final ScheduledThreadPool keepaliveExecutor, final ThreadPool processingExecutor, - final SchemaRepositoryProvider schemaRepositoryProvider, final DataBroker dataBroker, + final SchemaResourceManager schemaRepositoryProvider, final DataBroker dataBroker, final DOMMountPointService mountService, final AAAEncryptionService encryptionService) { this(topologyId, eventExecutor, keepaliveExecutor, processingExecutor, schemaRepositoryProvider, dataBroker, @@ -65,7 +65,7 @@ public class CallHomeMountDispatcher implements NetconfClientDispatcher, CallHom public CallHomeMountDispatcher(final String topologyId, final EventExecutor eventExecutor, final ScheduledThreadPool keepaliveExecutor, final ThreadPool processingExecutor, - final SchemaRepositoryProvider schemaRepositoryProvider, final DataBroker dataBroker, + final SchemaResourceManager schemaRepositoryProvider, final DataBroker dataBroker, final DOMMountPointService mountService, final AAAEncryptionService encryptionService, final DeviceActionFactory deviceActionFactory) { this.topologyId = topologyId; diff --git a/netconf/callhome-provider/src/main/java/org/opendaylight/netconf/callhome/mount/CallHomeTopology.java b/netconf/callhome-provider/src/main/java/org/opendaylight/netconf/callhome/mount/CallHomeTopology.java index e032637cd8..f6f185dc05 100644 --- a/netconf/callhome-provider/src/main/java/org/opendaylight/netconf/callhome/mount/CallHomeTopology.java +++ b/netconf/callhome-provider/src/main/java/org/opendaylight/netconf/callhome/mount/CallHomeTopology.java @@ -16,17 +16,17 @@ import org.opendaylight.mdsal.dom.api.DOMMountPointService; import org.opendaylight.netconf.client.NetconfClientDispatcher; import org.opendaylight.netconf.sal.connect.api.DeviceActionFactory; import org.opendaylight.netconf.sal.connect.api.RemoteDeviceHandler; +import org.opendaylight.netconf.sal.connect.api.SchemaResourceManager; import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfSessionPreferences; import org.opendaylight.netconf.sal.connect.netconf.sal.NetconfDeviceSalFacade; import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId; -import org.opendaylight.netconf.topology.api.SchemaRepositoryProvider; public class CallHomeTopology extends BaseCallHomeTopology { public CallHomeTopology(final String topologyId, final NetconfClientDispatcher clientDispatcher, final EventExecutor eventExecutor, final ScheduledThreadPool keepaliveExecutor, final ThreadPool processingExecutor, - final SchemaRepositoryProvider schemaRepositoryProvider, + final SchemaResourceManager schemaRepositoryProvider, final DataBroker dataBroker, final DOMMountPointService mountPointService, final AAAEncryptionService encryptionService) { this(topologyId, clientDispatcher, eventExecutor, @@ -37,7 +37,7 @@ public class CallHomeTopology extends BaseCallHomeTopology { public CallHomeTopology(final String topologyId, final NetconfClientDispatcher clientDispatcher, final EventExecutor eventExecutor, final ScheduledThreadPool keepaliveExecutor, final ThreadPool processingExecutor, - final SchemaRepositoryProvider schemaRepositoryProvider, + final SchemaResourceManager schemaRepositoryProvider, final DataBroker dataBroker, final DOMMountPointService mountPointService, final AAAEncryptionService encryptionService, final DeviceActionFactory deviceActionFactory) { diff --git a/netconf/callhome-provider/src/main/resources/OSGI-INF/blueprint/callhome-topology.xml b/netconf/callhome-provider/src/main/resources/OSGI-INF/blueprint/callhome-topology.xml index d4cd6195a3..c3a6c2dfd1 100644 --- a/netconf/callhome-provider/src/main/resources/OSGI-INF/blueprint/callhome-topology.xml +++ b/netconf/callhome-provider/src/main/resources/OSGI-INF/blueprint/callhome-topology.xml @@ -27,10 +27,8 @@ interface="org.opendaylight.aaa.encrypt.AAAEncryptionService" /> - - - - + - + diff --git a/netconf/callhome-provider/src/test/java/org/opendaylight/netconf/callhome/mount/CallHomeMountDispatcherTest.java b/netconf/callhome-provider/src/test/java/org/opendaylight/netconf/callhome/mount/CallHomeMountDispatcherTest.java index ecac3cc607..d2037dcf33 100644 --- a/netconf/callhome-provider/src/test/java/org/opendaylight/netconf/callhome/mount/CallHomeMountDispatcherTest.java +++ b/netconf/callhome-provider/src/test/java/org/opendaylight/netconf/callhome/mount/CallHomeMountDispatcherTest.java @@ -35,7 +35,7 @@ import org.opendaylight.netconf.client.conf.NetconfClientConfiguration; import org.opendaylight.netconf.client.conf.NetconfClientConfigurationBuilder; import org.opendaylight.netconf.nettyutil.ReconnectStrategy; import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.AuthenticationHandler; -import org.opendaylight.netconf.topology.api.SchemaRepositoryProvider; +import org.opendaylight.netconf.sal.connect.api.SchemaResourceManager; import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId; import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node; @@ -44,7 +44,7 @@ public class CallHomeMountDispatcherTest { private EventExecutor mockExecutor; private ScheduledThreadPool mockKeepAlive; private ThreadPool mockProcessingExecutor; - private SchemaRepositoryProvider mockSchemaRepoProvider; + private SchemaResourceManager mockSchemaRepoProvider; private CallHomeMountDispatcher instance; private DataBroker mockDataBroker; @@ -61,7 +61,7 @@ public class CallHomeMountDispatcherTest { mockExecutor = mock(EventExecutor.class); mockKeepAlive = mock(ScheduledThreadPool.class); mockProcessingExecutor = mock(ThreadPool.class); - mockSchemaRepoProvider = mock(SchemaRepositoryProvider.class); + mockSchemaRepoProvider = mock(SchemaResourceManager.class); mockDataBroker = mock(DataBroker.class); mockMount = mock(DOMMountPointService.class); mockSessMgr = mock(CallHomeMountSessionManager.class); diff --git a/netconf/netconf-topology-impl/src/main/java/org/opendaylight/netconf/topology/impl/NetconfTopologyImpl.java b/netconf/netconf-topology-impl/src/main/java/org/opendaylight/netconf/topology/impl/NetconfTopologyImpl.java index dc17c5105d..1a3a443b23 100644 --- a/netconf/netconf-topology-impl/src/main/java/org/opendaylight/netconf/topology/impl/NetconfTopologyImpl.java +++ b/netconf/netconf-topology-impl/src/main/java/org/opendaylight/netconf/topology/impl/NetconfTopologyImpl.java @@ -27,10 +27,10 @@ import org.opendaylight.mdsal.dom.api.DOMMountPointService; import org.opendaylight.netconf.client.NetconfClientDispatcher; import org.opendaylight.netconf.sal.connect.api.DeviceActionFactory; import org.opendaylight.netconf.sal.connect.api.RemoteDeviceHandler; +import org.opendaylight.netconf.sal.connect.api.SchemaResourceManager; import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfSessionPreferences; import org.opendaylight.netconf.sal.connect.netconf.sal.NetconfDeviceSalFacade; import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId; -import org.opendaylight.netconf.topology.api.SchemaRepositoryProvider; import org.opendaylight.netconf.topology.spi.AbstractNetconfTopology; import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology; import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopologyBuilder; @@ -58,7 +58,7 @@ public class NetconfTopologyImpl extends AbstractNetconfTopology public NetconfTopologyImpl(final String topologyId, final NetconfClientDispatcher clientDispatcher, final EventExecutor eventExecutor, final ScheduledThreadPool keepaliveExecutor, final ThreadPool processingExecutor, - final SchemaRepositoryProvider schemaRepositoryProvider, + final SchemaResourceManager schemaRepositoryProvider, final DataBroker dataBroker, final DOMMountPointService mountPointService, final AAAEncryptionService encryptionService) { this(topologyId, clientDispatcher, eventExecutor, keepaliveExecutor, processingExecutor, @@ -68,7 +68,7 @@ public class NetconfTopologyImpl extends AbstractNetconfTopology public NetconfTopologyImpl(final String topologyId, final NetconfClientDispatcher clientDispatcher, final EventExecutor eventExecutor, final ScheduledThreadPool keepaliveExecutor, final ThreadPool processingExecutor, - final SchemaRepositoryProvider schemaRepositoryProvider, + final SchemaResourceManager schemaRepositoryProvider, final DataBroker dataBroker, final DOMMountPointService mountPointService, final AAAEncryptionService encryptionService, final DeviceActionFactory deviceActionFactory) { diff --git a/netconf/netconf-topology-impl/src/main/resources/OSGI-INF/blueprint/netconf-topology.xml b/netconf/netconf-topology-impl/src/main/resources/OSGI-INF/blueprint/netconf-topology.xml index e95c26948a..06b7c19899 100644 --- a/netconf/netconf-topology-impl/src/main/resources/OSGI-INF/blueprint/netconf-topology.xml +++ b/netconf/netconf-topology-impl/src/main/resources/OSGI-INF/blueprint/netconf-topology.xml @@ -32,10 +32,8 @@ interface="org.opendaylight.aaa.encrypt.AAAEncryptionService" /> - - - - + @@ -54,7 +52,7 @@ - + diff --git a/netconf/netconf-topology-impl/src/test/java/org/opendaylight/netconf/topology/impl/NetconfTopologyImplTest.java b/netconf/netconf-topology-impl/src/test/java/org/opendaylight/netconf/topology/impl/NetconfTopologyImplTest.java index 372dc443ea..96a6696152 100644 --- a/netconf/netconf-topology-impl/src/test/java/org/opendaylight/netconf/topology/impl/NetconfTopologyImplTest.java +++ b/netconf/netconf-topology-impl/src/test/java/org/opendaylight/netconf/topology/impl/NetconfTopologyImplTest.java @@ -21,16 +21,14 @@ import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; import io.netty.util.concurrent.EventExecutor; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.ImmediateEventExecutor; -import io.netty.util.concurrent.SucceededFuture; import java.util.Collection; import java.util.HashSet; import org.junit.Assert; import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; import org.mockito.Mock; -import org.mockito.MockitoAnnotations; +import org.mockito.junit.MockitoJUnitRunner; import org.opendaylight.aaa.encrypt.AAAEncryptionService; import org.opendaylight.controller.config.threadpool.ScheduledThreadPool; import org.opendaylight.controller.config.threadpool.ThreadPool; @@ -44,8 +42,8 @@ import org.opendaylight.netconf.client.NetconfClientDispatcher; import org.opendaylight.netconf.client.NetconfClientSessionListener; import org.opendaylight.netconf.client.conf.NetconfClientConfiguration; import org.opendaylight.netconf.client.conf.NetconfReconnectingClientConfiguration; +import org.opendaylight.netconf.sal.connect.api.SchemaResourceManager; import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCapabilities; -import org.opendaylight.netconf.topology.api.SchemaRepositoryProvider; import org.opendaylight.netconf.topology.spi.AbstractNetconfTopology; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Host; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress; @@ -68,10 +66,12 @@ import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology. import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey; import org.opendaylight.yangtools.yang.binding.DataObject; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.IdentifiableItem; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument; import org.opendaylight.yangtools.yang.common.Uint16; import org.opendaylight.yangtools.yang.common.Uint32; -import org.opendaylight.yangtools.yang.parser.repo.SharedSchemaRepository; +@RunWith(MockitoJUnitRunner.StrictStubs.class) public class NetconfTopologyImplTest { private static final NodeId NODE_ID = new NodeId("testing-node"); @@ -90,7 +90,7 @@ public class NetconfTopologyImplTest { private ThreadPool mockedProcessingExecutor; @Mock - private SchemaRepositoryProvider mockedSchemaRepositoryProvider; + private SchemaResourceManager mockedResourceManager; @Mock private DataBroker dataBroker; @@ -106,18 +106,11 @@ public class NetconfTopologyImplTest { @Before public void setUp() { - MockitoAnnotations.initMocks(this); - - when(mockedSchemaRepositoryProvider.getSharedSchemaRepository()) - .thenReturn(new SharedSchemaRepository("testingSharedSchemaRepo")); when(mockedProcessingExecutor.getExecutor()).thenReturn(MoreExecutors.newDirectExecutorService()); - final Future future = new SucceededFuture(ImmediateEventExecutor.INSTANCE, new NetconfDeviceCapabilities()); - when(mockedClientDispatcher.createReconnectingClient(any(NetconfReconnectingClientConfiguration.class))) - .thenReturn(future); - topology = new TestingNetconfTopologyImpl(TOPOLOGY_ID, mockedClientDispatcher, - mockedEventExecutor, mockedKeepaliveExecutor, mockedProcessingExecutor, mockedSchemaRepositoryProvider, - dataBroker, mountPointService, encryptionService); + topology = new TestingNetconfTopologyImpl(TOPOLOGY_ID, mockedClientDispatcher, mockedEventExecutor, + mockedKeepaliveExecutor, mockedProcessingExecutor, mockedResourceManager, dataBroker, mountPointService, + encryptionService); spyTopology = spy(topology); } @@ -150,14 +143,7 @@ public class NetconfTopologyImplTest { final DataObjectModification newNode = mock(DataObjectModification.class); when(newNode.getModificationType()).thenReturn(DataObjectModification.ModificationType.WRITE); - InstanceIdentifier.PathArgument pa = null; - - for (final InstanceIdentifier.PathArgument p - : NetconfTopologyImpl.createTopologyListPath(TOPOLOGY_ID) - .child(Node.class, new NodeKey(NODE_ID)).getPathArguments()) { - pa = p; - } - + PathArgument pa = IdentifiableItem.of(Node.class, new NodeKey(NODE_ID)); when(newNode.getIdentifier()).thenReturn(pa); @@ -283,7 +269,7 @@ public class NetconfTopologyImplTest { final EventExecutor eventExecutor, final ScheduledThreadPool keepaliveExecutor, final ThreadPool processingExecutor, - final SchemaRepositoryProvider schemaRepositoryProvider, + final SchemaResourceManager schemaRepositoryProvider, final DataBroker dataBroker, final DOMMountPointService mountPointService, final AAAEncryptionService encryptionService) { super(topologyId, clientDispatcher, eventExecutor, keepaliveExecutor, diff --git a/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/NetconfTopologyManager.java b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/NetconfTopologyManager.java index c36d69acfc..ce4f0868b5 100644 --- a/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/NetconfTopologyManager.java +++ b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/NetconfTopologyManager.java @@ -41,6 +41,8 @@ import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonServiceRegist import org.opendaylight.mdsal.singleton.common.api.ServiceGroupIdentifier; import org.opendaylight.netconf.client.NetconfClientDispatcher; import org.opendaylight.netconf.sal.connect.api.DeviceActionFactory; +import org.opendaylight.netconf.sal.connect.api.SchemaResourceManager; +import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId; import org.opendaylight.netconf.topology.singleton.api.NetconfTopologySingletonService; import org.opendaylight.netconf.topology.singleton.impl.utils.NetconfTopologySetup; import org.opendaylight.netconf.topology.singleton.impl.utils.NetconfTopologySetup.NetconfTopologySetupBuilder; @@ -84,6 +86,7 @@ public class NetconfTopologyManager private final DOMMountPointService mountPointService; private final AAAEncryptionService encryptionService; private final DeviceActionFactory deviceActionFactory; + private final SchemaResourceManager resourceManager; private ListenerRegistration dataChangeListenerRegistration; private String privateKeyPath; private String privateKeyPassphrase; @@ -97,8 +100,8 @@ public class NetconfTopologyManager final String topologyId, final Config config, final DOMMountPointService mountPointService, final AAAEncryptionService encryptionService, - final DeviceActionFactory deviceActionFactory) { - + final DeviceActionFactory deviceActionFactory, + final SchemaResourceManager resourceManager) { this.dataBroker = requireNonNull(dataBroker); this.rpcProviderRegistry = requireNonNull(rpcProviderRegistry); this.actionProviderRegistry = requireNonNull(actionProviderService); @@ -113,7 +116,7 @@ public class NetconfTopologyManager this.mountPointService = mountPointService; this.encryptionService = requireNonNull(encryptionService); this.deviceActionFactory = requireNonNull(deviceActionFactory); - + this.resourceManager = requireNonNull(resourceManager); } // Blueprint init method @@ -282,6 +285,9 @@ public class NetconfTopologyManager } private NetconfTopologySetup createSetup(final InstanceIdentifier instanceIdentifier, final Node node) { + final NetconfNode netconfNode = node.augmentation(NetconfNode.class); + final RemoteDeviceId deviceId = NetconfTopologyUtils.createRemoteDeviceId(node.getNodeId(), netconfNode); + final NetconfTopologySetupBuilder builder = NetconfTopologySetupBuilder.create() .setClusterSingletonServiceProvider(clusterSingletonServiceProvider) .setDataBroker(dataBroker) @@ -295,7 +301,7 @@ public class NetconfTopologyManager .setProcessingExecutor(processingExecutor) .setTopologyId(topologyId) .setNetconfClientDispatcher(clientDispatcher) - .setSchemaResourceDTO(NetconfTopologyUtils.setupSchemaCacheDTO(node)) + .setSchemaResourceDTO(resourceManager.getSchemaResources(netconfNode, deviceId)) .setIdleTimeout(writeTxIdleTimeout) .setPrivateKeyPath(privateKeyPath) .setPrivateKeyPassphrase(privateKeyPassphrase) diff --git a/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/utils/NetconfTopologyUtils.java b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/utils/NetconfTopologyUtils.java index 022005d6f5..a005f53d24 100644 --- a/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/utils/NetconfTopologyUtils.java +++ b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/utils/NetconfTopologyUtils.java @@ -8,17 +8,9 @@ package org.opendaylight.netconf.topology.singleton.impl.utils; -import com.google.common.base.Strings; -import com.google.common.util.concurrent.Uninterruptibles; -import java.io.File; import java.math.BigDecimal; import java.net.InetSocketAddress; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.TimeUnit; import org.opendaylight.netconf.api.DocumentedException; -import org.opendaylight.netconf.sal.connect.netconf.NetconfDevice; -import org.opendaylight.netconf.sal.connect.netconf.NetconfStateSchemasResolverImpl; import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress; import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNode; @@ -32,23 +24,12 @@ import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology. import org.opendaylight.yangtools.yang.binding.Identifier; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier; -import org.opendaylight.yangtools.yang.model.repo.api.EffectiveModelContextFactory; -import org.opendaylight.yangtools.yang.model.repo.api.SchemaRepository; -import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource; -import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry; -import org.opendaylight.yangtools.yang.model.repo.util.FilesystemSchemaSourceCache; -import org.opendaylight.yangtools.yang.model.repo.util.InMemorySchemaSourceCache; -import org.opendaylight.yangtools.yang.parser.repo.SharedSchemaRepository; -import org.opendaylight.yangtools.yang.parser.rfc7950.repo.ASTSchemaSource; -import org.opendaylight.yangtools.yang.parser.rfc7950.repo.TextToASTTransformer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public final class NetconfTopologyUtils { private static final Logger LOG = LoggerFactory.getLogger(NetconfTopologyUtils.class); - private static final String DEFAULT_SCHEMA_REPOSITORY_NAME = "sal-netconf-connector"; - public static final long DEFAULT_REQUEST_TIMEOUT_MILLIS = 60000L; public static final int DEFAULT_KEEPALIVE_DELAY = 0; public static final boolean DEFAULT_RECONNECT_ON_CHANGED_SCHEMA = false; @@ -59,157 +40,10 @@ public final class NetconfTopologyUtils { public static final long DEFAULT_CONNECTION_TIMEOUT_MILLIS = 20000L; public static final BigDecimal DEFAULT_SLEEP_FACTOR = new BigDecimal(1.5); - - // The default cache directory relative to CACHE_DIRECTORY - - public static final String DEFAULT_CACHE_DIRECTORY = "schema"; - - // Filesystem based caches are stored relative to the cache directory. - public static final String CACHE_DIRECTORY = "cache"; - - // The qualified schema cache directory cache/schema - public static final String QUALIFIED_DEFAULT_CACHE_DIRECTORY = - CACHE_DIRECTORY + File.separator + DEFAULT_CACHE_DIRECTORY; - - // The default schema repository in the case that one is not specified. - public static final SharedSchemaRepository DEFAULT_SCHEMA_REPOSITORY = - new SharedSchemaRepository(DEFAULT_SCHEMA_REPOSITORY_NAME); - - public static final InMemorySchemaSourceCache DEFAULT_AST_CACHE = - InMemorySchemaSourceCache.createSoftCache(DEFAULT_SCHEMA_REPOSITORY, ASTSchemaSource.class); - - // The default factory for creating SchemaContext instances. - public static final EffectiveModelContextFactory DEFAULT_SCHEMA_CONTEXT_FACTORY = - DEFAULT_SCHEMA_REPOSITORY.createEffectiveModelContextFactory(); - - /** - * 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 SchemaResourcesDTO. The - * SchemaResourcesDTO is essentially a container that allows for the extraction of the - * SchemaRegistry and SchemaContextFactory which should be used for a particular - * Netconf mount. Access to SCHEMA_RESOURCES_DTO_MAP should be surrounded by appropriate - * synchronization locks. - */ - private static final Map SCHEMA_RESOURCES_DTO_MAP = new HashMap<>(); - - // Initializes default constant instances for the case when the default schema repository - // directory cache/schema is used. - static { - SCHEMA_RESOURCES_DTO_MAP.put(DEFAULT_CACHE_DIRECTORY, - new NetconfDevice.SchemaResourcesDTO(DEFAULT_SCHEMA_REPOSITORY, DEFAULT_SCHEMA_REPOSITORY, - DEFAULT_SCHEMA_CONTEXT_FACTORY, new NetconfStateSchemasResolverImpl())); - DEFAULT_SCHEMA_REPOSITORY.registerSchemaSourceListener(DEFAULT_AST_CACHE); - DEFAULT_SCHEMA_REPOSITORY.registerSchemaSourceListener( - TextToASTTransformer.create(DEFAULT_SCHEMA_REPOSITORY, DEFAULT_SCHEMA_REPOSITORY)); - - /* - * Create the default FilesystemSchemaSourceCache, which stores cached files - * in cache/schema. Try up to 3 times - we've seen intermittent failures on jenkins where - * FilesystemSchemaSourceCache throws an IAE due to mkdirs failure. The theory is that there's a race - * creating the dir and it already exists when mkdirs is called (mkdirs returns false in this case). In this - * scenario, a retry should succeed. - */ - int tries = 1; - while (true) { - try { - FilesystemSchemaSourceCache defaultCache = - new FilesystemSchemaSourceCache<>(DEFAULT_SCHEMA_REPOSITORY, YangTextSchemaSource.class, - new File(QUALIFIED_DEFAULT_CACHE_DIRECTORY)); - DEFAULT_SCHEMA_REPOSITORY.registerSchemaSourceListener(defaultCache); - break; - } catch (IllegalArgumentException e) { - if (tries++ >= 3) { - LOG.error("Error creating default schema cache", e); - break; - } - Uninterruptibles.sleepUninterruptibly(100, TimeUnit.MILLISECONDS); - } - } - } - private NetconfTopologyUtils() { } - public static NetconfDevice.SchemaResourcesDTO setupSchemaCacheDTO(final Node node) { - final NetconfNode netconfNode = node.augmentation(NetconfNode.class); - final String moduleSchemaCacheDirectory = netconfNode.getSchemaCacheDirectory(); - final RemoteDeviceId deviceId = createRemoteDeviceId(node.getNodeId(), netconfNode); - - // Setup information related to the SchemaRegistry, SchemaResourceFactory, etc. - NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = null; - // Only checks to ensure the String is not empty or null; further checks related to directory accessibility - // and file permissions are handled during the FilesystemSchemaSourceCache 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 (SCHEMA_RESOURCES_DTO_MAP) { - // Look for the cached DTO to reuse SchemaRegistry and SchemaContextFactory variables if - // they already exist - schemaResourcesDTO = SCHEMA_RESOURCES_DTO_MAP.get(moduleSchemaCacheDirectory); - if (schemaResourcesDTO == null) { - schemaResourcesDTO = createSchemaResourcesDTO(moduleSchemaCacheDirectory); - schemaResourcesDTO.getSchemaRegistry().registerSchemaSourceListener( - TextToASTTransformer.create((SchemaRepository) schemaResourcesDTO.getSchemaRegistry(), - schemaResourcesDTO.getSchemaRegistry()) - ); - SCHEMA_RESOURCES_DTO_MAP.put(moduleSchemaCacheDirectory, schemaResourcesDTO); - } - } - LOG.info("{} : netconf connector will use schema cache directory {} instead of {}", - deviceId, moduleSchemaCacheDirectory, DEFAULT_CACHE_DIRECTORY); - } - } - - if (schemaResourcesDTO == null) { - synchronized (SCHEMA_RESOURCES_DTO_MAP) { - schemaResourcesDTO = SCHEMA_RESOURCES_DTO_MAP.get(DEFAULT_CACHE_DIRECTORY); - } - LOG.info("{} : using the default directory {}", - deviceId, QUALIFIED_DEFAULT_CACHE_DIRECTORY); - } - - 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 static NetconfDevice.SchemaResourcesDTO createSchemaResourcesDTO(final String moduleSchemaCacheDirectory) { - final SharedSchemaRepository repository = new SharedSchemaRepository(moduleSchemaCacheDirectory); - final EffectiveModelContextFactory schemaContextFactory - = repository.createEffectiveModelContextFactory(); - - final FilesystemSchemaSourceCache deviceCache = - createDeviceFilesystemCache(moduleSchemaCacheDirectory, repository); - repository.registerSchemaSourceListener(deviceCache); - repository.registerSchemaSourceListener(InMemorySchemaSourceCache.createSoftCache(repository, - ASTSchemaSource.class)); - return new NetconfDevice.SchemaResourcesDTO(repository, repository, schemaContextFactory, - new NetconfStateSchemasResolverImpl()); - } - - /** - * Creates a FilesystemSchemaSourceCache for the custom schema cache directory. - * - * @param schemaCacheDirectory The custom cache directory relative to "cache" - * @return A FilesystemSchemaSourceCache for the custom schema cache directory - */ - private static FilesystemSchemaSourceCache createDeviceFilesystemCache( - final String schemaCacheDirectory, final SchemaSourceRegistry schemaRegistry) { - final String relativeSchemaCacheDirectory = - NetconfTopologyUtils.CACHE_DIRECTORY + File.separator + schemaCacheDirectory; - return new FilesystemSchemaSourceCache<>(schemaRegistry, YangTextSchemaSource.class, - new File(relativeSchemaCacheDirectory)); - } - - public static RemoteDeviceId createRemoteDeviceId(final NodeId nodeId, final NetconfNode node) { final IpAddress ipAddress = node.getHost().getIpAddress(); final InetSocketAddress address = new InetSocketAddress(ipAddress.getIpv4Address() != null diff --git a/netconf/netconf-topology-singleton/src/main/resources/OSGI-INF/blueprint/netconf-topology-singleton.xml b/netconf/netconf-topology-singleton/src/main/resources/OSGI-INF/blueprint/netconf-topology-singleton.xml index 91ef4f842b..b2e2e59dd0 100644 --- a/netconf/netconf-topology-singleton/src/main/resources/OSGI-INF/blueprint/netconf-topology-singleton.xml +++ b/netconf/netconf-topology-singleton/src/main/resources/OSGI-INF/blueprint/netconf-topology-singleton.xml @@ -39,6 +39,8 @@ and is available at http://www.eclipse.org/legal/epl-v10.html odl:type="default"/> + + diff --git a/netconf/netconf-topology-singleton/src/test/java/org/opendaylight/netconf/topology/singleton/impl/MountPointEndToEndTest.java b/netconf/netconf-topology-singleton/src/test/java/org/opendaylight/netconf/topology/singleton/impl/MountPointEndToEndTest.java index 638de1831b..9ff232a71b 100644 --- a/netconf/netconf-topology-singleton/src/test/java/org/opendaylight/netconf/topology/singleton/impl/MountPointEndToEndTest.java +++ b/netconf/netconf-topology-singleton/src/test/java/org/opendaylight/netconf/topology/singleton/impl/MountPointEndToEndTest.java @@ -21,7 +21,6 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.verify; -import static org.mockito.MockitoAnnotations.initMocks; import akka.actor.ActorSystem; import akka.testkit.javadsl.TestKit; @@ -52,7 +51,9 @@ import org.apache.commons.io.FileUtils; import org.junit.After; import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; import org.opendaylight.aaa.encrypt.AAAEncryptionService; import org.opendaylight.controller.cluster.ActorSystemProvider; import org.opendaylight.controller.config.threadpool.ScheduledThreadPool; @@ -96,6 +97,9 @@ import org.opendaylight.mdsal.singleton.common.api.ServiceGroupIdentifier; import org.opendaylight.mdsal.singleton.dom.impl.DOMClusterSingletonServiceProviderImpl; import org.opendaylight.netconf.client.NetconfClientDispatcher; import org.opendaylight.netconf.sal.connect.api.DeviceActionFactory; +import org.opendaylight.netconf.sal.connect.api.SchemaResourceManager; +import org.opendaylight.netconf.sal.connect.impl.DefaultSchemaResourceManager; +import org.opendaylight.netconf.sal.connect.netconf.NetconfDevice.SchemaResourcesDTO; import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfSessionPreferences; import org.opendaylight.netconf.topology.singleton.impl.utils.ClusteringRpcException; import org.opendaylight.netconf.topology.singleton.impl.utils.NetconfTopologySetup; @@ -156,6 +160,7 @@ import org.slf4j.LoggerFactory; * * @author Thomas Pantelis */ +@RunWith(MockitoJUnitRunner.class) public class MountPointEndToEndTest { private static final Logger LOG = LoggerFactory.getLogger(MountPointEndToEndTest.class); @@ -166,6 +171,9 @@ public class MountPointEndToEndTest { private static final InstanceIdentifier NODE_INSTANCE_ID = NetconfTopologyUtils.createTopologyNodeListPath( new NodeKey(NODE_ID), TOPOLOGY_ID); + private static final String TEST_ROOT_DIRECTORY = "test-cache-root"; + private static final String TEST_DEFAULT_SUBDIR = "test-schema"; + @Mock private DOMRpcProviderService mockRpcProviderRegistry; @Mock private DOMActionProviderService mockActionProviderRegistry; @Mock private NetconfClientDispatcher mockClientDispatcher; @@ -206,13 +214,15 @@ public class MountPointEndToEndTest { private YangInstanceIdentifier yangNodeInstanceId; private final TopDOMRpcImplementation topRpcImplementation = new TopDOMRpcImplementation(); + private SchemaResourceManager resourceManager; + @SuppressWarnings({ "unchecked", "rawtypes" }) @Before public void setUp() throws Exception { - initMocks(this); - deleteCacheDir(); + resourceManager = new DefaultSchemaResourceManager(TEST_ROOT_DIRECTORY, TEST_DEFAULT_SUBDIR); + topModuleInfo = BindingReflections.getModuleInfo(Top.class); final ModuleInfoBackedContext moduleContext = ModuleInfoBackedContext.create(); @@ -240,7 +250,7 @@ public class MountPointEndToEndTest { } private static void deleteCacheDir() { - FileUtils.deleteQuietly(new File(NetconfTopologyUtils.CACHE_DIRECTORY)); + FileUtils.deleteQuietly(new File(TEST_ROOT_DIRECTORY)); } @After @@ -266,7 +276,9 @@ public class MountPointEndToEndTest { doReturn(MoreExecutors.newDirectExecutorService()).when(mockThreadPool).getExecutor(); - NetconfTopologyUtils.DEFAULT_SCHEMA_REPOSITORY.registerSchemaSource( + final SchemaResourcesDTO resources = resourceManager.getSchemaResources( + new NetconfNodeBuilder().setSchemaCacheDirectory(TEST_DEFAULT_SUBDIR).build(), "test"); + resources.getSchemaRegistry().registerSchemaSource( id -> Futures.immediateFuture(YangTextSchemaSource.delegateForByteSource(id, topModuleInfo.getYangTextByteSource())), PotentialSchemaSource.create(RevisionSourceIdentifier.create(TOP_MODULE_NAME, @@ -275,7 +287,7 @@ public class MountPointEndToEndTest { masterNetconfTopologyManager = new NetconfTopologyManager(masterDataBroker, mockRpcProviderRegistry, mockActionProviderRegistry, masterClusterSingletonServiceProvider, mockKeepaliveExecutor, mockThreadPool, mockMasterActorSystemProvider, eventExecutor, mockClientDispatcher, TOPOLOGY_ID, config, - masterMountPointService, mockEncryptionService, deviceActionFactory) { + masterMountPointService, mockEncryptionService, deviceActionFactory, resourceManager) { @Override protected NetconfTopologyContext newNetconfTopologyContext(final NetconfTopologySetup setup, final ServiceGroupIdentifier serviceGroupIdent, final Timeout actorResponseWaitTime, @@ -314,7 +326,7 @@ public class MountPointEndToEndTest { slaveNetconfTopologyManager = new NetconfTopologyManager(slaveDataBroker, mockRpcProviderRegistry, mockActionProviderRegistry, mockSlaveClusterSingletonServiceProvider, mockKeepaliveExecutor, mockThreadPool, mockSlaveActorSystemProvider, eventExecutor, mockClientDispatcher, TOPOLOGY_ID, config, - slaveMountPointService, mockEncryptionService, deviceActionFactory) { + slaveMountPointService, mockEncryptionService, deviceActionFactory, resourceManager) { @Override protected NetconfTopologyContext newNetconfTopologyContext(final NetconfTopologySetup setup, final ServiceGroupIdentifier serviceGroupIdent, final Timeout actorResponseWaitTime, @@ -360,7 +372,7 @@ public class MountPointEndToEndTest { private MasterSalFacade testMaster() throws InterruptedException, ExecutionException, TimeoutException { LOG.info("****** Testing master"); - writeNetconfNode(NetconfTopologyUtils.DEFAULT_CACHE_DIRECTORY, masterDataBroker); + writeNetconfNode(TEST_DEFAULT_SUBDIR, masterDataBroker); final MasterSalFacade masterSalFacade = masterSalFacadeFuture.get(5, TimeUnit.SECONDS); @@ -433,7 +445,7 @@ public class MountPointEndToEndTest { slaveMountPointService.registerProvisionListener(slaveMountPointListener); masterSalFacadeFuture = SettableFuture.create(); - writeNetconfNode(NetconfTopologyUtils.DEFAULT_CACHE_DIRECTORY, masterDataBroker); + writeNetconfNode(TEST_DEFAULT_SUBDIR, masterDataBroker); verify(masterMountPointListener, timeout(5000)).onMountPointRemoved(yangNodeInstanceId); diff --git a/netconf/netconf-topology-singleton/src/test/java/org/opendaylight/netconf/topology/singleton/impl/NetconfNodeActorTest.java b/netconf/netconf-topology-singleton/src/test/java/org/opendaylight/netconf/topology/singleton/impl/NetconfNodeActorTest.java index 6e6d3d6f5e..8fc8473b45 100644 --- a/netconf/netconf-topology-singleton/src/test/java/org/opendaylight/netconf/topology/singleton/impl/NetconfNodeActorTest.java +++ b/netconf/netconf-topology-singleton/src/test/java/org/opendaylight/netconf/topology/singleton/impl/NetconfNodeActorTest.java @@ -26,7 +26,6 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.MockitoAnnotations.initMocks; -import static org.opendaylight.netconf.topology.singleton.impl.utils.NetconfTopologyUtils.DEFAULT_SCHEMA_REPOSITORY; import akka.actor.ActorRef; import akka.actor.ActorSystem; @@ -408,9 +407,11 @@ public class NetconfNodeActorTest { @Test(expected = MissingSchemaSourceException.class) public void testMissingSchemaSourceOnMissingProvider() throws Exception { + final SharedSchemaRepository repository = new SharedSchemaRepository("test"); + SchemaResourcesDTO schemaResourceDTO2 = mock(SchemaResourcesDTO.class); - doReturn(DEFAULT_SCHEMA_REPOSITORY).when(schemaResourceDTO2).getSchemaRegistry(); - doReturn(DEFAULT_SCHEMA_REPOSITORY).when(schemaResourceDTO2).getSchemaRepository(); + doReturn(repository).when(schemaResourceDTO2).getSchemaRegistry(); + doReturn(repository).when(schemaResourceDTO2).getSchemaRepository(); final NetconfTopologySetup setup = NetconfTopologySetupBuilder.create().setActorSystem(system) .setSchemaResourceDTO(schemaResourceDTO2).setIdleTimeout(Duration.apply(1, TimeUnit.SECONDS)).build(); final Props props = NetconfNodeActor.props(setup, remoteDeviceId, TIMEOUT, mockMountPointService); diff --git a/netconf/netconf-topology-singleton/src/test/java/org/opendaylight/netconf/topology/singleton/impl/NetconfTopologyManagerTest.java b/netconf/netconf-topology-singleton/src/test/java/org/opendaylight/netconf/topology/singleton/impl/NetconfTopologyManagerTest.java index 82b2d325cb..fe0e001268 100644 --- a/netconf/netconf-topology-singleton/src/test/java/org/opendaylight/netconf/topology/singleton/impl/NetconfTopologyManagerTest.java +++ b/netconf/netconf-topology-singleton/src/test/java/org/opendaylight/netconf/topology/singleton/impl/NetconfTopologyManagerTest.java @@ -60,6 +60,7 @@ import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonServiceRegist import org.opendaylight.mdsal.singleton.common.api.ServiceGroupIdentifier; import org.opendaylight.netconf.client.NetconfClientDispatcher; import org.opendaylight.netconf.sal.connect.api.DeviceActionFactory; +import org.opendaylight.netconf.sal.connect.impl.DefaultSchemaResourceManager; import org.opendaylight.netconf.topology.singleton.impl.utils.NetconfTopologySetup; import org.opendaylight.netconf.topology.singleton.impl.utils.NetconfTopologyUtils; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Host; @@ -130,7 +131,7 @@ public class NetconfTopologyManagerTest { netconfTopologyManager = new NetconfTopologyManager(dataBroker, rpcProviderRegistry, actionProviderRegistry, clusterSingletonServiceProvider, keepaliveExecutor, processingThreadPool, actorSystemProvider, eventExecutor, clientDispatcher, TOPOLOGY_ID, config, - mountPointService, encryptionService, deviceActionFactory) { + mountPointService, encryptionService, deviceActionFactory, new DefaultSchemaResourceManager()) { @Override protected NetconfTopologyContext newNetconfTopologyContext(final NetconfTopologySetup setup, final ServiceGroupIdentifier serviceGroupIdent, final Timeout actorResponseWaitTime, diff --git a/netconf/netconf-topology-singleton/src/test/java/org/opendaylight/netconf/topology/singleton/impl/RemoteDeviceConnectorImplTest.java b/netconf/netconf-topology-singleton/src/test/java/org/opendaylight/netconf/topology/singleton/impl/RemoteDeviceConnectorImplTest.java index 8fa8ee6357..48dfab7f5f 100644 --- a/netconf/netconf-topology-singleton/src/test/java/org/opendaylight/netconf/topology/singleton/impl/RemoteDeviceConnectorImplTest.java +++ b/netconf/netconf-topology-singleton/src/test/java/org/opendaylight/netconf/topology/singleton/impl/RemoteDeviceConnectorImplTest.java @@ -44,13 +44,13 @@ import org.opendaylight.netconf.client.conf.NetconfClientConfiguration; import org.opendaylight.netconf.client.conf.NetconfReconnectingClientConfiguration; import org.opendaylight.netconf.sal.connect.api.DeviceActionFactory; import org.opendaylight.netconf.sal.connect.api.RemoteDeviceHandler; +import org.opendaylight.netconf.sal.connect.impl.DefaultSchemaResourceManager; import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCommunicator; 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; import org.opendaylight.netconf.topology.singleton.impl.utils.NetconfConnectorDTO; import org.opendaylight.netconf.topology.singleton.impl.utils.NetconfTopologySetup; -import org.opendaylight.netconf.topology.singleton.impl.utils.NetconfTopologyUtils; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Host; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address; @@ -188,7 +188,8 @@ public class RemoteDeviceConnectorImplTest { .build(); final Node node = new NodeBuilder().setNodeId(NODE_ID).addAugmentation(NetconfNode.class, netconfNode).build(); - builder.setSchemaResourceDTO(NetconfTopologyUtils.setupSchemaCacheDTO(node)); + + builder.setSchemaResourceDTO(new DefaultSchemaResourceManager().getSchemaResources(netconfNode, "foo")); final RemoteDeviceConnectorImpl remoteDeviceConnection = new RemoteDeviceConnectorImpl(builder.build(), remoteDeviceId, deviceActionFactory); diff --git a/netconf/netconf-topology/src/main/java/org/opendaylight/netconf/topology/api/SchemaRepositoryProvider.java b/netconf/netconf-topology/src/main/java/org/opendaylight/netconf/topology/api/SchemaRepositoryProvider.java deleted file mode 100644 index c0c38cf711..0000000000 --- a/netconf/netconf-topology/src/main/java/org/opendaylight/netconf/topology/api/SchemaRepositoryProvider.java +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright (c) 2015 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.netconf.topology.api; - -import org.eclipse.jdt.annotation.NonNull; -import org.opendaylight.yangtools.yang.parser.repo.SharedSchemaRepository; - -public interface SchemaRepositoryProvider { - - @NonNull SharedSchemaRepository getSharedSchemaRepository(); -} diff --git a/netconf/netconf-topology/src/main/java/org/opendaylight/netconf/topology/spi/AbstractNetconfTopology.java b/netconf/netconf-topology/src/main/java/org/opendaylight/netconf/topology/spi/AbstractNetconfTopology.java index fd31e9a758..478ca7f6e3 100644 --- a/netconf/netconf-topology/src/main/java/org/opendaylight/netconf/topology/spi/AbstractNetconfTopology.java +++ b/netconf/netconf-topology/src/main/java/org/opendaylight/netconf/topology/spi/AbstractNetconfTopology.java @@ -11,16 +11,13 @@ import static java.util.Objects.requireNonNull; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; -import com.google.common.base.Strings; import com.google.common.collect.Lists; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListeningExecutorService; import com.google.common.util.concurrent.MoreExecutors; -import com.google.common.util.concurrent.Uninterruptibles; import io.netty.util.concurrent.EventExecutor; -import java.io.File; import java.math.BigDecimal; import java.net.InetSocketAddress; import java.net.URL; @@ -29,7 +26,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.concurrent.TimeUnit; import org.opendaylight.aaa.encrypt.AAAEncryptionService; import org.opendaylight.controller.config.threadpool.ScheduledThreadPool; import org.opendaylight.controller.config.threadpool.ThreadPool; @@ -48,10 +44,11 @@ import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.LoginPasswo import org.opendaylight.netconf.sal.connect.api.DeviceActionFactory; import org.opendaylight.netconf.sal.connect.api.RemoteDevice; import org.opendaylight.netconf.sal.connect.api.RemoteDeviceHandler; +import org.opendaylight.netconf.sal.connect.api.SchemaResourceManager; import org.opendaylight.netconf.sal.connect.netconf.LibraryModulesSchemas; import org.opendaylight.netconf.sal.connect.netconf.NetconfDevice; +import org.opendaylight.netconf.sal.connect.netconf.NetconfDevice.SchemaResourcesDTO; import org.opendaylight.netconf.sal.connect.netconf.NetconfDeviceBuilder; -import org.opendaylight.netconf.sal.connect.netconf.NetconfStateSchemasResolverImpl; import org.opendaylight.netconf.sal.connect.netconf.SchemalessNetconfDevice; import org.opendaylight.netconf.sal.connect.netconf.auth.DatastoreBackedPublicKeyAuth; import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCapabilities; @@ -64,9 +61,9 @@ import org.opendaylight.netconf.sal.connect.netconf.schema.YangLibrarySchemaYang import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId; import org.opendaylight.netconf.sal.connect.util.SslHandlerFactoryImpl; import org.opendaylight.netconf.topology.api.NetconfTopology; -import org.opendaylight.netconf.topology.api.SchemaRepositoryProvider; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Host; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Uri; import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.optional.rev190614.NetconfNodeAugmentedOptional; import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNode; import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.parameters.Protocol; @@ -79,27 +76,18 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev15 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.key.auth.KeyBased; import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.login.pw.LoginPassword; import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.login.pw.unencrypted.LoginPasswordUnencrypted; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.schema.storage.YangLibrary; import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId; import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node; -import org.opendaylight.yangtools.yang.model.repo.api.EffectiveModelContextFactory; -import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactory; -import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactoryConfiguration; -import org.opendaylight.yangtools.yang.model.repo.api.SchemaRepository; import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier; import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource; import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource; import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistration; import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry; -import org.opendaylight.yangtools.yang.model.repo.util.FilesystemSchemaSourceCache; -import org.opendaylight.yangtools.yang.model.repo.util.InMemorySchemaSourceCache; -import org.opendaylight.yangtools.yang.parser.repo.SharedSchemaRepository; -import org.opendaylight.yangtools.yang.parser.rfc7950.repo.ASTSchemaSource; -import org.opendaylight.yangtools.yang.parser.rfc7950.repo.TextToASTTransformer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public abstract class AbstractNetconfTopology implements NetconfTopology { - private static final Logger LOG = LoggerFactory.getLogger(AbstractNetconfTopology.class); protected static final long DEFAULT_REQUEST_TIMEOUT_MILLIS = 60000L; @@ -112,103 +100,18 @@ public abstract class AbstractNetconfTopology implements NetconfTopology { private static final long DEFAULT_CONNECTION_TIMEOUT_MILLIS = 20000L; private static final BigDecimal DEFAULT_SLEEP_FACTOR = new BigDecimal(1.5); - // 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 CACHE_DIRECTORY. - */ - private static final String DEFAULT_CACHE_DIRECTORY = "schema"; - - /** - * The qualified schema cache directory cache/schema. - */ - 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); - - public static final InMemorySchemaSourceCache DEFAULT_AST_CACHE = - InMemorySchemaSourceCache.createSoftCache(DEFAULT_SCHEMA_REPOSITORY, ASTSchemaSource.class); - - /** - * The default factory for creating SchemaContext instances. - */ - private static final EffectiveModelContextFactory DEFAULT_SCHEMA_CONTEXT_FACTORY = - DEFAULT_SCHEMA_REPOSITORY.createEffectiveModelContextFactory( - SchemaContextFactoryConfiguration.getDefault()); - - /** - * 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 SchemaResourcesDTO. The - * SchemaResourcesDTO is essentially a container that allows for the extraction of the - * SchemaRegistry and SchemaContextFactory which should be used for a particular - * Netconf mount. Access to SCHEMA_RESOURCES_DTO_MAP should be surrounded by appropriate - * synchronization locks. - */ - private static final Map SCHEMA_RESOURCES_DTO_MAP = new HashMap<>(); - - // Initializes default constant instances for the case when the default schema repository - // directory cache/schema is used. - static { - SCHEMA_RESOURCES_DTO_MAP.put(DEFAULT_CACHE_DIRECTORY, - new NetconfDevice.SchemaResourcesDTO(DEFAULT_SCHEMA_REPOSITORY, DEFAULT_SCHEMA_REPOSITORY, - DEFAULT_SCHEMA_CONTEXT_FACTORY, - new NetconfStateSchemasResolverImpl())); - DEFAULT_SCHEMA_REPOSITORY.registerSchemaSourceListener(DEFAULT_AST_CACHE); - DEFAULT_SCHEMA_REPOSITORY.registerSchemaSourceListener( - TextToASTTransformer.create(DEFAULT_SCHEMA_REPOSITORY, DEFAULT_SCHEMA_REPOSITORY)); - - /* - * Create the default FilesystemSchemaSourceCache, which stores cached files - * in cache/schema. Try up to 3 times - we've seen intermittent failures on jenkins where - * FilesystemSchemaSourceCache throws an IAE due to mkdirs failure. The theory is that there's a race - * creating the dir and it already exists when mkdirs is called (mkdirs returns false in this case). In this - * scenario, a retry should succeed. - */ - int tries = 1; - while (true) { - try { - FilesystemSchemaSourceCache defaultCache = - new FilesystemSchemaSourceCache<>(DEFAULT_SCHEMA_REPOSITORY, YangTextSchemaSource.class, - new File(QUALIFIED_DEFAULT_CACHE_DIRECTORY)); - DEFAULT_SCHEMA_REPOSITORY.registerSchemaSourceListener(defaultCache); - break; - } catch (IllegalArgumentException e) { - if (tries++ >= 3) { - LOG.error("Error creating default schema cache", e); - break; - } - Uninterruptibles.sleepUninterruptibly(100, TimeUnit.MILLISECONDS); - } - } - } private final NetconfClientDispatcher clientDispatcher; private final EventExecutor eventExecutor; private final DeviceActionFactory deviceActionFactory; private final NetconfKeystoreAdapter keystoreAdapter; + private final SchemaResourceManager schemaManager; + protected final ScheduledThreadPool keepaliveExecutor; protected final ListeningExecutorService processingExecutor; - protected final SharedSchemaRepository sharedSchemaRepository; protected final DataBroker dataBroker; protected final DOMMountPointService mountPointService; protected final String topologyId; - protected SchemaSourceRegistry schemaRegistry = DEFAULT_SCHEMA_REPOSITORY; - protected SchemaRepository schemaRepository = DEFAULT_SCHEMA_REPOSITORY; - protected SchemaContextFactory schemaContextFactory = DEFAULT_SCHEMA_CONTEXT_FACTORY; protected String privateKeyPath; protected String privateKeyPassphrase; protected final AAAEncryptionService encryptionService; @@ -216,8 +119,7 @@ public abstract class AbstractNetconfTopology implements NetconfTopology { protected AbstractNetconfTopology(final String topologyId, final NetconfClientDispatcher clientDispatcher, final EventExecutor eventExecutor, final ScheduledThreadPool keepaliveExecutor, - final ThreadPool processingExecutor, - final SchemaRepositoryProvider schemaRepositoryProvider, + final ThreadPool processingExecutor, final SchemaResourceManager schemaManager, final DataBroker dataBroker, final DOMMountPointService mountPointService, final AAAEncryptionService encryptionService, final DeviceActionFactory deviceActionFactory) { @@ -226,8 +128,8 @@ public abstract class AbstractNetconfTopology implements NetconfTopology { this.eventExecutor = eventExecutor; this.keepaliveExecutor = keepaliveExecutor; this.processingExecutor = MoreExecutors.listeningDecorator(processingExecutor.getExecutor()); + this.schemaManager = requireNonNull(schemaManager); this.deviceActionFactory = deviceActionFactory; - this.sharedSchemaRepository = schemaRepositoryProvider.getSharedSchemaRepository(); this.dataBroker = dataBroker; this.mountPointService = mountPointService; this.encryptionService = encryptionService; @@ -235,14 +137,6 @@ public abstract class AbstractNetconfTopology implements NetconfTopology { this.keystoreAdapter = new NetconfKeystoreAdapter(dataBroker); } - public void setSchemaRegistry(final SchemaSourceRegistry schemaRegistry) { - this.schemaRegistry = schemaRegistry; - } - - public void setSchemaContextFactory(final SchemaContextFactory schemaContextFactory) { - this.schemaContextFactory = schemaContextFactory; - } - @Override public ListenableFuture connectNode(final NodeId nodeId, final Node configNode) { LOG.info("Connecting RemoteDevice{{}} , with config {}", nodeId, hideCredentials(configNode)); @@ -323,8 +217,6 @@ public abstract class AbstractNetconfTopology implements NetconfTopology { ? DEFAULT_REQUEST_TIMEOUT_MILLIS : node.getDefaultRequestTimeoutMillis().toJava(); final long keepaliveDelay = node.getKeepaliveDelay() == null ? DEFAULT_KEEPALIVE_DELAY : node.getKeepaliveDelay().toJava(); - final boolean reconnectOnChangedSchema = node.isReconnectOnChangedSchema() == null - ? DEFAULT_RECONNECT_ON_CHANGED_SCHEMA : node.isReconnectOnChangedSchema(); final IpAddress ipAddress = node.getHost().getIpAddress(); final InetSocketAddress address = new InetSocketAddress(ipAddress.getIpv4Address() != null @@ -332,8 +224,7 @@ public abstract class AbstractNetconfTopology implements NetconfTopology { node.getPort().getValue().toJava()); final RemoteDeviceId remoteDeviceId = new RemoteDeviceId(nodeId.getValue(), address); - RemoteDeviceHandler salFacade = - createSalFacade(remoteDeviceId); + RemoteDeviceHandler salFacade = createSalFacade(remoteDeviceId); if (keepaliveDelay > 0) { LOG.warn("Adding keepalive facade, for device {}", nodeId); @@ -341,49 +232,11 @@ public abstract class AbstractNetconfTopology implements NetconfTopology { keepaliveDelay, defaultRequestTimeoutMillis); } - // pre register yang library sources as fallback schemas to schema registry - final List> registeredYangLibSources = Lists.newArrayList(); - if (node.getYangLibrary() != null) { - final String yangLibURL = node.getYangLibrary().getYangLibraryUrl().getValue(); - final String yangLibUsername = node.getYangLibrary().getUsername(); - final String yangLigPassword = node.getYangLibrary().getPassword(); - - final LibraryModulesSchemas libraryModulesSchemas; - if (yangLibURL != null) { - if (yangLibUsername != null && yangLigPassword != null) { - libraryModulesSchemas = LibraryModulesSchemas.create(yangLibURL, yangLibUsername, yangLigPassword); - } else { - libraryModulesSchemas = LibraryModulesSchemas.create(yangLibURL); - } - - for (final Map.Entry sourceIdentifierURLEntry - : libraryModulesSchemas.getAvailableModels().entrySet()) { - registeredYangLibSources - .add(schemaRegistry.registerSchemaSource( - new YangLibrarySchemaYangSourceProvider(remoteDeviceId, - libraryModulesSchemas.getAvailableModels()), - PotentialSchemaSource.create(sourceIdentifierURLEntry.getKey(), - YangTextSchemaSource.class, PotentialSchemaSource.Costs.REMOTE_IO.getValue()))); - } - } - } - - final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = setupSchemaCacheDTO(nodeId, node); final RemoteDevice device; if (node.isSchemaless()) { device = new SchemalessNetconfDevice(remoteDeviceId, salFacade); } else { - NetconfDeviceBuilder netconfDeviceBuilder = new NetconfDeviceBuilder() - .setReconnectOnSchemasChange(reconnectOnChangedSchema) - .setSchemaResourcesDTO(schemaResourcesDTO) - .setGlobalProcessingExecutor(this.processingExecutor) - .setId(remoteDeviceId) - .setSalFacade(salFacade) - .setNode(node) - .setEventExecutor(eventExecutor) - .setNodeOptional(nodeOptional) - .setDeviceActionFactory(deviceActionFactory); - device = netconfDeviceBuilder.build(); + device = createNetconfDevice(remoteDeviceId, salFacade, nodeId, node, nodeOptional); } final Optional userCapabilities = getUserCapabilities(node); @@ -405,79 +258,63 @@ public abstract class AbstractNetconfTopology implements NetconfTopology { return new NetconfConnectorDTO(netconfDeviceCommunicator, 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 permissionsare handled during the FilesystemSchemaSourceCache 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 (SCHEMA_RESOURCES_DTO_MAP) { - // Look for the cached DTO to reuse SchemaRegistry and SchemaContextFactory variables - // if they already exist - schemaResourcesDTO = SCHEMA_RESOURCES_DTO_MAP.get(moduleSchemaCacheDirectory); - if (schemaResourcesDTO == null) { - schemaResourcesDTO = createSchemaResourcesDTO(moduleSchemaCacheDirectory); - schemaResourcesDTO.getSchemaRegistry().registerSchemaSourceListener( - TextToASTTransformer.create((SchemaRepository) schemaResourcesDTO.getSchemaRegistry(), - schemaResourcesDTO.getSchemaRegistry()) - ); - SCHEMA_RESOURCES_DTO_MAP.put(moduleSchemaCacheDirectory, schemaResourcesDTO); - } - } - 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); - } + private NetconfDevice createNetconfDevice(final RemoteDeviceId remoteDeviceId, + final RemoteDeviceHandler salFacade, final NodeId nodeId, final NetconfNode node, + final NetconfNodeAugmentedOptional nodeOptional) { + final boolean reconnectOnChangedSchema = node.isReconnectOnChangedSchema() == null + ? DEFAULT_RECONNECT_ON_CHANGED_SCHEMA : node.isReconnectOnChangedSchema(); - if (schemaResourcesDTO == null) { - schemaResourcesDTO = new NetconfDevice.SchemaResourcesDTO(schemaRegistry, schemaRepository, - schemaContextFactory, new NetconfStateSchemasResolverImpl()); - } + final SchemaResourcesDTO resources = schemaManager.getSchemaResources(node, nodeId.getValue()); + + final NetconfDevice device = new NetconfDeviceBuilder() + .setReconnectOnSchemasChange(reconnectOnChangedSchema) + .setSchemaResourcesDTO(resources) + .setGlobalProcessingExecutor(this.processingExecutor) + .setId(remoteDeviceId) + .setSalFacade(salFacade) + .setNode(node) + .setEventExecutor(eventExecutor) + .setNodeOptional(nodeOptional) + .setDeviceActionFactory(deviceActionFactory) + .build(); - return schemaResourcesDTO; - } + final YangLibrary yangLibrary = node.getYangLibrary(); + if (yangLibrary != null) { + final Uri uri = yangLibrary.getYangLibraryUrl(); + if (uri != null) { + // FIXME: NETCONF-675: these registrations need to be torn down with the device. This does not look + // quite right, though, as we can end up adding a lot of registrations on a + // per-device basis. + // This leak is also detected by SpotBugs as soon as this initialization is switched + // to proper "new ArrayList<>" and hence we really need to attach these somewhere + // else. + // It seems we should be subclassing NetconfConnectorDTO for this purpose as a + // first step and then perhaps do some refcounting or similar based on the + // schemaRegistry instance. + final List> registeredYangLibSources = Lists.newArrayList(); + final String yangLibURL = uri.getValue(); + final SchemaSourceRegistry schemaRegistry = resources.getSchemaRegistry(); + + // pre register yang library sources as fallback schemas to schema registry + final LibraryModulesSchemas schemas; + final String yangLibUsername = yangLibrary.getUsername(); + final String yangLigPassword = yangLibrary.getPassword(); + if (yangLibUsername != null && yangLigPassword != null) { + schemas = LibraryModulesSchemas.create(yangLibURL, yangLibUsername, yangLigPassword); + } else { + schemas = LibraryModulesSchemas.create(yangLibURL); + } - /** - * 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(moduleSchemaCacheDirectory); - final EffectiveModelContextFactory contextFactory - = repository.createEffectiveModelContextFactory(SchemaContextFactoryConfiguration.getDefault()); - setSchemaRegistry(repository); - setSchemaContextFactory(contextFactory); - final FilesystemSchemaSourceCache deviceCache = - createDeviceFilesystemCache(moduleSchemaCacheDirectory); - repository.registerSchemaSourceListener(deviceCache); - repository.registerSchemaSourceListener( - InMemorySchemaSourceCache.createSoftCache(repository, ASTSchemaSource.class)); - return new NetconfDevice.SchemaResourcesDTO(repository, repository, contextFactory, - new NetconfStateSchemasResolverImpl()); - } + for (final Map.Entry entry : schemas.getAvailableModels().entrySet()) { + registeredYangLibSources.add(schemaRegistry.registerSchemaSource( + new YangLibrarySchemaYangSourceProvider(remoteDeviceId, schemas.getAvailableModels()), + PotentialSchemaSource.create(entry.getKey(), YangTextSchemaSource.class, + PotentialSchemaSource.Costs.REMOTE_IO.getValue()))); + } + } + } - /** - * Creates a FilesystemSchemaSourceCache for the custom schema cache directory. - * - * @param schemaCacheDirectory The custom cache directory relative to "cache" - * @return A FilesystemSchemaSourceCache for the custom schema cache directory - */ - private FilesystemSchemaSourceCache createDeviceFilesystemCache( - final String schemaCacheDirectory) { - final String relativeSchemaCacheDirectory = CACHE_DIRECTORY + File.separator + schemaCacheDirectory; - return new FilesystemSchemaSourceCache<>(schemaRegistry, YangTextSchemaSource.class, - new File(relativeSchemaCacheDirectory)); + return device; } /** diff --git a/netconf/netconf-topology/src/main/java/org/opendaylight/netconf/topology/spi/SharedSchemaRepositoryProvider.java b/netconf/netconf-topology/src/main/java/org/opendaylight/netconf/topology/spi/SharedSchemaRepositoryProvider.java deleted file mode 100644 index 36d0d28969..0000000000 --- a/netconf/netconf-topology/src/main/java/org/opendaylight/netconf/topology/spi/SharedSchemaRepositoryProvider.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2016 Inocybe Technologies 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.netconf.topology.spi; - -import org.eclipse.jdt.annotation.NonNull; -import org.opendaylight.netconf.topology.api.SchemaRepositoryProvider; -import org.opendaylight.yangtools.yang.parser.repo.SharedSchemaRepository; - -public class SharedSchemaRepositoryProvider implements SchemaRepositoryProvider { - private final @NonNull SharedSchemaRepository schemaRepository; - - public SharedSchemaRepositoryProvider(final String moduleName) { - schemaRepository = new SharedSchemaRepository(moduleName); - } - - @Override - public SharedSchemaRepository getSharedSchemaRepository() { - return schemaRepository; - } -} \ No newline at end of file diff --git a/netconf/sal-netconf-connector/pom.xml b/netconf/sal-netconf-connector/pom.xml index 7921f94b63..58f5472978 100644 --- a/netconf/sal-netconf-connector/pom.xml +++ b/netconf/sal-netconf-connector/pom.xml @@ -103,6 +103,12 @@ org.slf4j slf4j-api + + javax.inject + javax.inject + provided + true + org.xmlunit xmlunit-legacy diff --git a/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/api/SchemaResourceManager.java b/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/api/SchemaResourceManager.java new file mode 100644 index 0000000000..19048d8f88 --- /dev/null +++ b/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/api/SchemaResourceManager.java @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2020 PANTHEON.tech, s.r.o. 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.netconf.sal.connect.api; + +import com.google.common.annotations.Beta; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.opendaylight.netconf.sal.connect.netconf.NetconfDevice.SchemaResourcesDTO; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNode; + +@Beta +@NonNullByDefault +public interface SchemaResourceManager { + + SchemaResourcesDTO getSchemaResources(NetconfNode node, Object nodeId); +} diff --git a/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/impl/DefaultSchemaResourceManager.java b/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/impl/DefaultSchemaResourceManager.java new file mode 100644 index 0000000000..60f4cf556f --- /dev/null +++ b/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/impl/DefaultSchemaResourceManager.java @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2020 PANTHEON.tech, s.r.o. 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.netconf.sal.connect.impl; + +import static java.util.Objects.requireNonNull; + +import com.google.common.annotations.Beta; +import com.google.common.base.Strings; +import java.io.File; +import java.util.HashMap; +import java.util.Map; +import javax.inject.Inject; +import javax.inject.Singleton; +import org.checkerframework.checker.lock.qual.GuardedBy; +import org.eclipse.jdt.annotation.NonNull; +import org.opendaylight.netconf.sal.connect.api.SchemaResourceManager; +import org.opendaylight.netconf.sal.connect.netconf.NetconfDevice.SchemaResourcesDTO; +import org.opendaylight.netconf.sal.connect.netconf.NetconfStateSchemasResolverImpl; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNode; +import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactoryConfiguration; +import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource; +import org.opendaylight.yangtools.yang.model.repo.util.FilesystemSchemaSourceCache; +import org.opendaylight.yangtools.yang.model.repo.util.InMemorySchemaSourceCache; +import org.opendaylight.yangtools.yang.parser.repo.SharedSchemaRepository; +import org.opendaylight.yangtools.yang.parser.rfc7950.repo.ASTSchemaSource; +import org.opendaylight.yangtools.yang.parser.rfc7950.repo.TextToASTTransformer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Simple single-node implementation of the {@link SchemaResourceManager} contract. Operates on the specified base + * root directory, where a number of independent subdirectories are created, each for a global default and anything + * encountered based on configuration. + */ +@Beta +@Singleton +public final class DefaultSchemaResourceManager implements SchemaResourceManager { + private static final Logger LOG = LoggerFactory.getLogger(DefaultSchemaResourceManager.class); + + @GuardedBy("this") + private final Map resources = new HashMap<>(); + private final @NonNull SchemaResourcesDTO defaultResources; + private final String defaultSubdirectory; + private final String rootDirectory; + + @Inject + public DefaultSchemaResourceManager() { + this("cache", "schema"); + } + + public DefaultSchemaResourceManager(final String rootDirectory, final String defaultSubdirectory) { + this.rootDirectory = requireNonNull(rootDirectory); + this.defaultSubdirectory = requireNonNull(defaultSubdirectory); + this.defaultResources = createResources(defaultSubdirectory); + } + + @Override + public SchemaResourcesDTO getSchemaResources(final NetconfNode node, final Object nodeId) { + final String subdir = node.getSchemaCacheDirectory(); + if (defaultSubdirectory.equals(subdir)) { + // Fast path for default devices + return defaultResources; + } + if (Strings.isNullOrEmpty(subdir)) { + // FIXME: we probably want to change semantics here: + // - update model to not allow empty name + // - silently default to the default + LOG.warn("schema-cache-directory for {} is null or empty; using the default {}", nodeId, + defaultSubdirectory); + return defaultResources; + } + + LOG.info("Netconf connector for device {} will use schema cache directory {} instead of {}", nodeId, subdir, + defaultSubdirectory); + return getSchemaResources(subdir); + } + + private synchronized @NonNull SchemaResourcesDTO getSchemaResources(final String subdir) { + // Fast path for unusual devices + final SchemaResourcesDTO existing = resources.get(subdir); + if (existing != null) { + return existing; + } + + final SchemaResourcesDTO created = createResources(subdir); + resources.put(subdir, created); + return created; + } + + private @NonNull SchemaResourcesDTO createResources(final String subdir) { + // Setup the baseline empty registry + // FIXME: add YangParserFactory argument + final SharedSchemaRepository repository = new SharedSchemaRepository(subdir); + + // Teach the registry how to transform YANG text to ASTSchemaSource internally + repository.registerSchemaSourceListener(TextToASTTransformer.create(repository, repository)); + + // Attach a soft cache of ASTSchemaSource instances. This is important during convergence when we are fishing + // for a consistent set of modules, as it skips the need to re-parse the text sources multiple times. It also + // helps establishing different sets of contexts, as they can share this pre-made cache. + repository.registerSchemaSourceListener( + // FIXME: add knobs to control cache lifetime explicitly + InMemorySchemaSourceCache.createSoftCache(repository, ASTSchemaSource.class)); + + // Attach the filesystem cache, providing persistence capability, so that restarts do not require us to + // re-populate the cache. This also acts as a side-load capability, as anything pre-populated into that + // directory will not be fetched from the device. + repository.registerSchemaSourceListener(new FilesystemSchemaSourceCache<>(repository, + YangTextSchemaSource.class, new File(rootDirectory + File.separator + subdir))); + + return new SchemaResourcesDTO(repository, repository, + repository.createEffectiveModelContextFactory(SchemaContextFactoryConfiguration.getDefault()), + new NetconfStateSchemasResolverImpl()); + } +} diff --git a/netconf/sal-netconf-connector/src/main/resources/OSGI-INF/blueprint/sal-netconf-connector.xml b/netconf/sal-netconf-connector/src/main/resources/OSGI-INF/blueprint/sal-netconf-connector.xml index 6916c588fe..d21a6d7291 100644 --- a/netconf/sal-netconf-connector/src/main/resources/OSGI-INF/blueprint/sal-netconf-connector.xml +++ b/netconf/sal-netconf-connector/src/main/resources/OSGI-INF/blueprint/sal-netconf-connector.xml @@ -18,4 +18,11 @@ and is available at http://www.eclipse.org/legal/epl-v10.html + + + + + \ No newline at end of file -- 2.36.6