Refactor NetconfRemoteSchemaYangSourceProvider 98/105798/9
authorRobert Varga <robert.varga@pantheon.tech>
Tue, 2 May 2023 23:11:55 +0000 (01:11 +0200)
committerRobert Varga <robert.varga@pantheon.tech>
Wed, 3 May 2023 09:48:49 +0000 (11:48 +0200)
This provider is based on ietf-netconf-monitoring. Split out the
YangTextSchemaSource implementation to CachedYangTextSchemaSource
and the the provider into netconf.client.mdsal, making it very clear
as to what is going on.

JIRA: NETCONF-1006
Change-Id: Ieff749ce67c94fa125ce780dd8d1fa4dfd79622d
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
plugins/netconf-client-mdsal/src/main/java/org/opendaylight/netconf/client/mdsal/CachedYangTextSchemaSource.java [new file with mode: 0644]
plugins/netconf-client-mdsal/src/main/java/org/opendaylight/netconf/client/mdsal/DeviceSourcesResolver.java
plugins/netconf-client-mdsal/src/main/java/org/opendaylight/netconf/client/mdsal/MonitoringSchemaSourceProvider.java [moved from plugins/netconf-client-mdsal/src/main/java/org/opendaylight/netconf/sal/connect/netconf/schema/NetconfRemoteSchemaYangSourceProvider.java with 79% similarity]
plugins/netconf-client-mdsal/src/main/java/org/opendaylight/netconf/sal/connect/netconf/schema/YangLibrarySchemaYangSourceProvider.java
plugins/netconf-client-mdsal/src/test/java/org/opendaylight/netconf/client/mdsal/MonitoringSchemaSourceProviderTest.java [moved from plugins/netconf-client-mdsal/src/test/java/org/opendaylight/netconf/sal/connect/netconf/schema/NetconfRemoteSchemaYangSourceProviderTest.java with 91% similarity]
plugins/netconf-client-mdsal/src/test/java/org/opendaylight/netconf/sal/connect/netconf/schema/YangLibrarySchemaYangSourceProviderTest.java
plugins/netconf-client-mdsal/src/test/java/org/opendaylight/netconf/sal/connect/netconf/schema/mapping/NetconfMessageTransformerTest.java

diff --git a/plugins/netconf-client-mdsal/src/main/java/org/opendaylight/netconf/client/mdsal/CachedYangTextSchemaSource.java b/plugins/netconf-client-mdsal/src/main/java/org/opendaylight/netconf/client/mdsal/CachedYangTextSchemaSource.java
new file mode 100644 (file)
index 0000000..d72834d
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2023 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.client.mdsal;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.MoreObjects;
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.util.Optional;
+import org.opendaylight.netconf.client.mdsal.api.RemoteDeviceId;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
+
+/**
+ * A {@link YangTextSchemaSource} cached from a remote service.
+ */
+public final class CachedYangTextSchemaSource extends YangTextSchemaSource {
+    private final RemoteDeviceId id;
+    private final byte[] schemaBytes;
+    private final String symbolicName;
+
+    public CachedYangTextSchemaSource(final RemoteDeviceId id, final SourceIdentifier sourceIdentifier,
+            final String symbolicName, final byte[] schemaBytes) {
+        super(sourceIdentifier);
+        this.symbolicName = requireNonNull(symbolicName);
+        this.id = requireNonNull(id);
+        this.schemaBytes = schemaBytes.clone();
+    }
+
+    @Override
+    protected MoreObjects.ToStringHelper addToStringAttributes(final MoreObjects.ToStringHelper toStringHelper) {
+        return toStringHelper.add("device", id);
+    }
+
+    @Override
+    public InputStream openStream() {
+        return new ByteArrayInputStream(schemaBytes);
+    }
+
+    @Override
+    public Optional<String> getSymbolicName() {
+        return Optional.of(symbolicName);
+    }
+}
\ No newline at end of file
index 73e950bc4ba9465c0b28aeb6e7d99240f1c776d6..5a91a077065d1eabef9cf615dd6070aa20017bba 100644 (file)
@@ -16,7 +16,6 @@ import org.opendaylight.netconf.client.mdsal.api.NetconfDeviceSchemasResolver;
 import org.opendaylight.netconf.client.mdsal.api.RemoteDeviceId;
 import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfSessionPreferences;
 import org.opendaylight.netconf.sal.connect.netconf.sal.NetconfDeviceRpc;
-import org.opendaylight.netconf.sal.connect.netconf.schema.NetconfRemoteSchemaYangSourceProvider;
 import org.opendaylight.netconf.sal.connect.netconf.schema.YangLibrarySchemaYangSourceProvider;
 import org.opendaylight.netconf.sal.connect.netconf.schema.mapping.BaseSchema;
 import org.slf4j.Logger;
@@ -76,7 +75,7 @@ final class DeviceSourcesResolver implements Callable<DeviceSources> {
 
         final var sourceProvider = availableSchemas instanceof LibraryModulesSchemas libraryModule
             ? new YangLibrarySchemaYangSourceProvider(id, libraryModule.getAvailableModels())
-                : new NetconfRemoteSchemaYangSourceProvider(id, deviceRpc);
+                : new MonitoringSchemaSourceProvider(id, deviceRpc);
         return new DeviceSources(requiredSources, providedSources, sourceProvider);
     }
 }
\ No newline at end of file
@@ -5,19 +5,16 @@
  * 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.netconf.schema;
+package org.opendaylight.netconf.client.mdsal;
 
 import static com.google.common.base.Preconditions.checkState;
 import static java.util.Objects.requireNonNull;
 import static org.opendaylight.netconf.common.mdsal.NormalizedDataUtil.NETCONF_DATA_QNAME;
 import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.GET_SCHEMA_QNAME;
 
-import com.google.common.base.MoreObjects;
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
 import com.google.common.util.concurrent.MoreExecutors;
-import java.io.ByteArrayInputStream;
-import java.io.InputStream;
 import java.nio.charset.StandardCharsets;
 import java.util.Optional;
 import javax.xml.transform.dom.DOMSource;
@@ -42,8 +39,13 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.w3c.dom.Element;
 
-public final class NetconfRemoteSchemaYangSourceProvider implements SchemaSourceProvider<YangTextSchemaSource> {
-    private static final Logger LOG = LoggerFactory.getLogger(NetconfRemoteSchemaYangSourceProvider.class);
+/**
+ * A {@link SchemaSourceProvider} producing {@link YangTextSchemaSource}s based on a device's
+ * {@code ietf-netconf-monitoring} interface. The set of available sources is not pre-determined and each request is
+ * dispatched to the device, i.e. this provider reflects real-time updates to available schemas.
+ */
+public final class MonitoringSchemaSourceProvider implements SchemaSourceProvider<YangTextSchemaSource> {
+    private static final Logger LOG = LoggerFactory.getLogger(MonitoringSchemaSourceProvider.class);
     private static final NodeIdentifier FORMAT_PATHARG =
             NodeIdentifier.create(QName.create(NetconfMessageTransformUtil.GET_SCHEMA_QNAME, "format").intern());
     private static final NodeIdentifier GET_SCHEMA_PATHARG =
@@ -61,8 +63,8 @@ public final class NetconfRemoteSchemaYangSourceProvider implements SchemaSource
     private final DOMRpcService rpc;
     private final RemoteDeviceId id;
 
-    public NetconfRemoteSchemaYangSourceProvider(final RemoteDeviceId id, final DOMRpcService rpc) {
-        this.id = id;
+    public MonitoringSchemaSourceProvider(final RemoteDeviceId id, final DOMRpcService rpc) {
+        this.id = requireNonNull(id);
         this.rpc = requireNonNull(rpc);
     }
 
@@ -108,7 +110,7 @@ public final class NetconfRemoteSchemaYangSourceProvider implements SchemaSource
                             id + ": Unexpected response to get-schema, schema not present in message for: "
                                 + sourceIdentifier));
                     LOG.debug("{}: YANG Schema successfully retrieved for {}:{}", id, moduleName, revision);
-                    return new NetconfYangTextSchemaSource(id, sourceIdentifier, moduleName,
+                    return new CachedYangTextSchemaSource(id, sourceIdentifier, moduleName,
                         schemaString.getBytes(StandardCharsets.UTF_8));
                 }
 
@@ -119,33 +121,4 @@ public final class NetconfRemoteSchemaYangSourceProvider implements SchemaSource
                     input.errors()));
             }, MoreExecutors.directExecutor());
     }
-
-    static class NetconfYangTextSchemaSource extends YangTextSchemaSource {
-        private final RemoteDeviceId id;
-        private final byte[] schemaBytes;
-        private final String symbolicName;
-
-        NetconfYangTextSchemaSource(final RemoteDeviceId id, final SourceIdentifier sourceIdentifier,
-                final String symbolicName, final byte[] schemaBytes) {
-            super(sourceIdentifier);
-            this.symbolicName = requireNonNull(symbolicName);
-            this.id = id;
-            this.schemaBytes = schemaBytes.clone();
-        }
-
-        @Override
-        protected MoreObjects.ToStringHelper addToStringAttributes(final MoreObjects.ToStringHelper toStringHelper) {
-            return toStringHelper.add("device", id);
-        }
-
-        @Override
-        public InputStream openStream() {
-            return new ByteArrayInputStream(schemaBytes);
-        }
-
-        @Override
-        public Optional<String> getSymbolicName() {
-            return Optional.of(symbolicName);
-        }
-    }
 }
index cbb4a5397f7bfcf39a69d507bf681af8fe1d77aa..4acccce413233e13034e6d3d7810e333eb90a50f 100644 (file)
@@ -14,11 +14,10 @@ import com.google.common.collect.ImmutableMap;
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
 import java.io.IOException;
-import java.io.InputStream;
 import java.net.URL;
 import java.util.Map;
+import org.opendaylight.netconf.client.mdsal.CachedYangTextSchemaSource;
 import org.opendaylight.netconf.client.mdsal.api.RemoteDeviceId;
-import org.opendaylight.netconf.sal.connect.netconf.schema.NetconfRemoteSchemaYangSourceProvider.NetconfYangTextSchemaSource;
 import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceException;
 import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
 import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
@@ -32,29 +31,32 @@ import org.slf4j.LoggerFactory;
 public final class YangLibrarySchemaYangSourceProvider implements SchemaSourceProvider<YangTextSchemaSource> {
     private static final Logger LOG = LoggerFactory.getLogger(YangLibrarySchemaYangSourceProvider.class);
 
-    private final Map<SourceIdentifier, URL> availableSources;
+    private final ImmutableMap<SourceIdentifier, URL> availableSources;
     private final RemoteDeviceId id;
 
     public YangLibrarySchemaYangSourceProvider(final RemoteDeviceId id,
             final Map<SourceIdentifier, URL> availableSources) {
-        this.id = id;
+        this.id = requireNonNull(id);
         this.availableSources = ImmutableMap.copyOf(availableSources);
     }
 
     @Override
     public ListenableFuture<? extends YangTextSchemaSource> getSource(final SourceIdentifier sourceIdentifier) {
-        final URL url = availableSources.get(requireNonNull(sourceIdentifier));
+        final var url = availableSources.get(requireNonNull(sourceIdentifier));
         checkArgument(url != null);
-        try (InputStream in = url.openStream()) {
-            final byte[] schemaContent = in.readAllBytes();
-            final NetconfYangTextSchemaSource yangSource = new NetconfYangTextSchemaSource(id, sourceIdentifier,
-                url.toString(), schemaContent);
-            LOG.debug("Source {} downloaded from a yang library's url {}", sourceIdentifier, url);
-            return Futures.immediateFuture(yangSource);
+
+        final byte[] schemaContent;
+        try (var in = url.openStream()) {
+            schemaContent = in.readAllBytes();
         } catch (IOException e) {
             LOG.warn("Unable to download source {} from a yang library's url {}", sourceIdentifier, url, e);
             return Futures.immediateFailedFuture(new SchemaSourceException(
                 "Unable to download remote schema for " + sourceIdentifier + " from " + url, e));
         }
+
+        final var yangSource = new CachedYangTextSchemaSource(id, sourceIdentifier,
+            url.toString(), schemaContent);
+        LOG.debug("Source {} downloaded from a yang library's url {}", sourceIdentifier, url);
+        return Futures.immediateFuture(yangSource);
     }
 }
@@ -5,7 +5,7 @@
  * 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.netconf.schema;
+package org.opendaylight.netconf.client.mdsal;
 
 import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
@@ -40,12 +40,11 @@ import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 
 @RunWith(MockitoJUnitRunner.StrictStubs.class)
-public class NetconfRemoteSchemaYangSourceProviderTest {
-
+public class MonitoringSchemaSourceProviderTest {
     @Mock
     private DOMRpcService service;
 
-    private NetconfRemoteSchemaYangSourceProvider provider;
+    private MonitoringSchemaSourceProvider provider;
 
     @Before
     public void setUp() throws Exception {
@@ -53,7 +52,7 @@ public class NetconfRemoteSchemaYangSourceProviderTest {
         final FluentFuture<DOMRpcResult> response = FluentFutures.immediateFluentFuture(value);
         doReturn(response).when(service).invokeRpc(any(QName.class), any(ContainerNode.class));
 
-        provider = new NetconfRemoteSchemaYangSourceProvider(
+        provider = new MonitoringSchemaSourceProvider(
                 new RemoteDeviceId("device1", InetSocketAddress.createUnresolved("localhost", 17830)), service);
     }
 
@@ -63,7 +62,7 @@ public class NetconfRemoteSchemaYangSourceProviderTest {
         final YangTextSchemaSource source = provider.getSource(identifier).get();
         assertEquals(identifier, source.getIdentifier());
         verify(service).invokeRpc(NetconfMessageTransformUtil.GET_SCHEMA_QNAME,
-                NetconfRemoteSchemaYangSourceProvider.createGetSchemaRequest("test", Optional.of("2016-02-08")));
+                MonitoringSchemaSourceProvider.createGetSchemaRequest("test", Optional.of("2016-02-08")));
     }
 
     private static ContainerNode getNode() throws ParserConfigurationException {
index 835c37c8743dab42f12b1fdf0a4c5d2e71393bd9..07be479e1a958c52638dcffd7a0c556169e1f73a 100644 (file)
@@ -12,12 +12,10 @@ import static org.hamcrest.CoreMatchers.instanceOf;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertThrows;
 
-import com.google.common.util.concurrent.ListenableFuture;
 import java.net.InetSocketAddress;
 import java.net.MalformedURLException;
 import java.net.URL;
 import java.nio.charset.StandardCharsets;
-import java.util.Collections;
 import java.util.Map;
 import java.util.concurrent.ExecutionException;
 import org.junit.Before;
@@ -25,7 +23,6 @@ import org.junit.Test;
 import org.opendaylight.netconf.client.mdsal.api.RemoteDeviceId;
 import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceException;
 import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
-import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
 
 public class YangLibrarySchemaYangSourceProviderTest {
     private SourceIdentifier workingSid;
@@ -33,32 +30,27 @@ public class YangLibrarySchemaYangSourceProviderTest {
 
     @Before
     public void setUp() throws Exception {
-        final URL url = getClass().getResource("/schemas/config-test-rpc.yang");
         workingSid = new SourceIdentifier("abc");
-        final Map<SourceIdentifier, URL> sourceIdentifierURLMap = Collections.singletonMap(workingSid, url);
-        final RemoteDeviceId id = new RemoteDeviceId("id", new InetSocketAddress("localhost", 22));
-        yangLibrarySchemaYangSourceProvider = new YangLibrarySchemaYangSourceProvider(id, sourceIdentifierURLMap);
+        yangLibrarySchemaYangSourceProvider = new YangLibrarySchemaYangSourceProvider(
+            new RemoteDeviceId("id", new InetSocketAddress("localhost", 22)),
+            Map.of(workingSid, getClass().getResource("/schemas/config-test-rpc.yang")));
     }
 
     @Test
     public void testGetSource() throws Exception {
-        ListenableFuture<? extends YangTextSchemaSource> source = yangLibrarySchemaYangSourceProvider
-                .getSource(workingSid);
-
+        var source = yangLibrarySchemaYangSourceProvider.getSource(workingSid);
         final String x = source.get().asCharSource(StandardCharsets.UTF_8).read();
         assertThat(x, containsString("module config-test-rpc"));
     }
 
     @Test
     public void testGetSourceFailure() throws InterruptedException, MalformedURLException {
-        final URL url = new URL("http://non-existing-entity.yang");
-        final Map<SourceIdentifier, URL> sourceIdentifierURLMap = Collections.singletonMap(workingSid, url);
-        final RemoteDeviceId id = new RemoteDeviceId("id", new InetSocketAddress("localhost", 22));
-        final YangLibrarySchemaYangSourceProvider failingYangLibrarySchemaYangSourceProvider =
-                new YangLibrarySchemaYangSourceProvider(id, sourceIdentifierURLMap);
+        final var sourceIdentifierURLMap = Map.of(workingSid, new URL("http://non-existing-entity.yang"));
+        final var failingYangLibrarySchemaYangSourceProvider = new YangLibrarySchemaYangSourceProvider(
+            new RemoteDeviceId("id", new InetSocketAddress("localhost", 22)), sourceIdentifierURLMap);
 
-        final ListenableFuture<?> future = failingYangLibrarySchemaYangSourceProvider.getSource(workingSid);
-        final ExecutionException ex = assertThrows(ExecutionException.class, () -> future.get());
+        final var future = failingYangLibrarySchemaYangSourceProvider.getSource(workingSid);
+        final var ex = assertThrows(ExecutionException.class, () -> future.get());
         assertThat(ex.getCause(), instanceOf(SchemaSourceException.class));
     }
 
index b33a7969b662ae9ef76f21186924c5efdd6a3007..b2b882a3a054b469a01280e9f2bfd14ec7bd5c2b 100644 (file)
@@ -29,8 +29,6 @@ import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTr
 import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.toFilterStructure;
 import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.toId;
 
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -55,8 +53,8 @@ import org.opendaylight.mdsal.dom.api.DOMRpcResult;
 import org.opendaylight.netconf.api.NetconfMessage;
 import org.opendaylight.netconf.api.xml.XmlUtil;
 import org.opendaylight.netconf.client.mdsal.AbstractBaseSchemasTest;
+import org.opendaylight.netconf.client.mdsal.MonitoringSchemaSourceProvider;
 import org.opendaylight.netconf.common.mdsal.NormalizedDataUtil;
-import org.opendaylight.netconf.sal.connect.netconf.schema.NetconfRemoteSchemaYangSourceProvider;
 import org.opendaylight.netconf.sal.connect.netconf.util.FieldsFilter;
 import org.opendaylight.netconf.sal.connect.netconf.util.NetconfBaseOps;
 import org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil;
@@ -266,7 +264,7 @@ public class NetconfMessageTransformerTest extends AbstractBaseSchemasTest {
     @Test
     public void testGetSchemaRequest() throws Exception {
         final NetconfMessage netconfMessage = netconfMessageTransformer.toRpcRequest(GET_SCHEMA_QNAME,
-                NetconfRemoteSchemaYangSourceProvider.createGetSchemaRequest("module", Optional.of("2012-12-12")));
+                MonitoringSchemaSourceProvider.createGetSchemaRequest("module", Optional.of("2012-12-12")));
         assertSimilarXml(netconfMessage, "<rpc message-id=\"m-0\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n"
                 + "<get-schema xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring\">\n"
                 + "<format>yang</format>\n"
@@ -323,9 +321,8 @@ public class NetconfMessageTransformerTest extends AbstractBaseSchemasTest {
         assertTrue(compositeNodeRpcResult.errors().isEmpty());
         assertNotNull(compositeNodeRpcResult.value());
 
-        final List<DataContainerChild> values = Lists.newArrayList(
-                NetconfRemoteSchemaYangSourceProvider
-                        .createGetSchemaRequest("module", Optional.of("2012-12-12")).body());
+        final var values = MonitoringSchemaSourceProvider.createGetSchemaRequest(
+            "module", Optional.of("2012-12-12")).body();
 
         final Map<QName, Object> keys = new HashMap<>();
         for (final DataContainerChild value : values) {
@@ -345,7 +342,7 @@ public class NetconfMessageTransformerTest extends AbstractBaseSchemasTest {
         final ContainerNode state = (ContainerNode) result.getChildByArg(toId(NetconfState.QNAME));
         final ContainerNode schemas = (ContainerNode) state.getChildByArg(toId(Schemas.QNAME));
         final MapNode schemaParent = (MapNode) schemas.getChildByArg(toId(Schema.QNAME));
-        assertEquals(1, Iterables.size(schemaParent.body()));
+        assertEquals(1, schemaParent.body().size());
 
         assertEquals(schemaNode, schemaParent.body().iterator().next());
     }
@@ -406,9 +403,8 @@ public class NetconfMessageTransformerTest extends AbstractBaseSchemasTest {
 
     @Test
     public void testEditConfigRequest() throws Exception {
-        final List<DataContainerChild> values = Lists.newArrayList(
-                NetconfRemoteSchemaYangSourceProvider
-                        .createGetSchemaRequest("module", Optional.of("2012-12-12")).body());
+        final var values = MonitoringSchemaSourceProvider.createGetSchemaRequest(
+            "module", Optional.of("2012-12-12")).body();
 
         final Map<QName, Object> keys = new HashMap<>();
         for (final DataContainerChild value : values) {