--- /dev/null
+/*
+ * 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
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;
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
* 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;
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 =
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);
}
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));
}
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);
- }
- }
}
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;
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);
}
}
* 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;
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 {
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);
}
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 {
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;
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;
@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));
}
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;
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;
@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"
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) {
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());
}
@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) {