From 1c14293bfd0c0c752ac040094e2fd6e9680cb849 Mon Sep 17 00:00:00 2001 From: Robert Varga Date: Sun, 28 Jan 2024 12:32:11 +0100 Subject: [PATCH] Disconnect NetconfDeviceRpc from DOMRpcService DOMService is about to get very picky about class hierarchy, let's make sure we do not attempt to combine multiple DOMServices. This adds a bit of indirection, but opens up the possibility to properly separate invocation paths and their expectations. Change-Id: Iecdb60e9664a97c400eab5100ef05a84e3f555e1 Signed-off-by: Robert Varga --- .../singleton/impl/ProxyDOMRpcService.java | 4 +- .../impl/actors/NetconfNodeActor.java | 14 ++-- .../impl/MountPointEndToEndTest.java | 15 +--- .../singleton/impl/NetconfNodeActorTest.java | 14 ++-- .../client/mdsal/DeviceSourcesResolver.java | 2 +- .../client/mdsal/LibraryModulesSchemas.java | 3 +- .../netconf/client/mdsal/NetconfDevice.java | 11 +-- .../NetconfStateSchemasResolverImpl.java | 2 +- .../mdsal/api/RemoteDeviceServices.java | 13 +-- .../client/mdsal/spi/KeepaliveSalFacade.java | 46 ++++++++++- .../mdsal/spi/NetconfDeviceDOMRpcService.java | 79 +++++++++++++++++++ .../client/mdsal/spi/NetconfDeviceMount.java | 4 +- .../client/mdsal/spi/NetconfDeviceRpc.java | 65 ++------------- .../mdsal/spi/SchemalessNetconfDeviceRpc.java | 10 ++- ...KeepaliveSalFacadeResponseWaitingTest.java | 12 ++- .../mdsal/spi/KeepaliveSalFacadeTest.java | 8 +- .../client/mdsal/spi/MountInstanceTest.java | 4 +- .../mdsal/spi/NetconfDeviceRpcTest.java | 9 ++- .../spi/SchemalessNetconfDeviceRpcTest.java | 6 +- 19 files changed, 196 insertions(+), 125 deletions(-) create mode 100644 plugins/netconf-client-mdsal/src/main/java/org/opendaylight/netconf/client/mdsal/spi/NetconfDeviceDOMRpcService.java diff --git a/apps/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/ProxyDOMRpcService.java b/apps/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/ProxyDOMRpcService.java index b779b9a38f..d527704e2a 100644 --- a/apps/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/ProxyDOMRpcService.java +++ b/apps/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/ProxyDOMRpcService.java @@ -18,9 +18,9 @@ import com.google.common.util.concurrent.SettableFuture; import java.util.Collection; import org.opendaylight.mdsal.dom.api.DOMRpcAvailabilityListener; import org.opendaylight.mdsal.dom.api.DOMRpcResult; +import org.opendaylight.mdsal.dom.api.DOMRpcService; import org.opendaylight.mdsal.dom.spi.DefaultDOMRpcResult; import org.opendaylight.netconf.client.mdsal.api.RemoteDeviceId; -import org.opendaylight.netconf.client.mdsal.api.RemoteDeviceServices.Rpcs; import org.opendaylight.netconf.topology.singleton.impl.utils.ClusteringRpcException; import org.opendaylight.netconf.topology.singleton.messages.NormalizedNodeMessage; import org.opendaylight.netconf.topology.singleton.messages.SchemaPathMessage; @@ -36,7 +36,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import scala.concurrent.Future; -public class ProxyDOMRpcService implements Rpcs.Normalized { +public class ProxyDOMRpcService implements DOMRpcService { private static final Logger LOG = LoggerFactory.getLogger(ProxyDOMRpcService.class); private final ActorRef masterActorRef; diff --git a/apps/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/actors/NetconfNodeActor.java b/apps/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/actors/NetconfNodeActor.java index 679c63bfbc..1cf783775f 100644 --- a/apps/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/actors/NetconfNodeActor.java +++ b/apps/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/actors/NetconfNodeActor.java @@ -129,7 +129,7 @@ public class NetconfNodeActor extends AbstractUntypedActor { readTxActor = context().actorOf(ReadTransactionActor.props(tx)); final var deviceServices = masterActorData.getDeviceServices(); - deviceRpc = deviceServices.rpcs() instanceof Rpcs.Normalized normalized ? normalized : null; + deviceRpc = deviceServices.rpcs() instanceof Rpcs.Normalized normalized ? normalized.domRpcService() : null; deviceAction = deviceServices.actions() instanceof Actions.Normalized normalized ? normalized : null; sender().tell(new MasterActorDataInitialized(), self()); @@ -213,10 +213,8 @@ public class NetconfNodeActor extends AbstractUntypedActor { } private void sendYangTextSchemaSourceProxy(final SourceIdentifier sourceIdentifier, final ActorRef sender) { - final ListenableFuture schemaSourceFuture = - schemaRepository.getSchemaSource(sourceIdentifier, YangTextSchemaSource.class); - - Futures.addCallback(schemaSourceFuture, new FutureCallback() { + final var schemaSourceFuture = schemaRepository.getSchemaSource(sourceIdentifier, YangTextSchemaSource.class); + Futures.addCallback(schemaSourceFuture, new FutureCallback<>() { @Override public void onSuccess(final YangTextSchemaSource yangTextSchemaSource) { try { @@ -332,7 +330,7 @@ public class NetconfNodeActor extends AbstractUntypedActor { final SlaveSalFacade localSlaveSalManager, final ActorRef masterReference, final int tries) { final ListenableFuture schemaContextFuture = schemaContextFactory.createEffectiveModelContext(sourceIdentifiers); - Futures.addCallback(schemaContextFuture, new FutureCallback() { + Futures.addCallback(schemaContextFuture, new FutureCallback<>() { @Override public void onSuccess(final EffectiveModelContext result) { executeInSelf(() -> { @@ -342,8 +340,10 @@ public class NetconfNodeActor extends AbstractUntypedActor { LOG.info("{}: Schema context resolved: {} - registering slave mount point", id, result.getModules()); final var actorSystem = setup.getActorSystem(); + final var rpcProxy = new ProxyDOMRpcService(actorSystem, masterReference, id, + actorResponseWaitTime); slaveSalManager.registerSlaveMountPoint(result, masterReference, new RemoteDeviceServices( - new ProxyDOMRpcService(actorSystem, masterReference, id, actorResponseWaitTime), + (Rpcs.Normalized) () -> rpcProxy, new ProxyDOMActionService(actorSystem, masterReference, id, actorResponseWaitTime))); } }); diff --git a/apps/netconf-topology-singleton/src/test/java/org/opendaylight/netconf/topology/singleton/impl/MountPointEndToEndTest.java b/apps/netconf-topology-singleton/src/test/java/org/opendaylight/netconf/topology/singleton/impl/MountPointEndToEndTest.java index 8c280a68e2..8cc00f1e92 100644 --- a/apps/netconf-topology-singleton/src/test/java/org/opendaylight/netconf/topology/singleton/impl/MountPointEndToEndTest.java +++ b/apps/netconf-topology-singleton/src/test/java/org/opendaylight/netconf/topology/singleton/impl/MountPointEndToEndTest.java @@ -73,7 +73,6 @@ import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteTransaction; import org.opendaylight.mdsal.dom.api.DOMMountPoint; import org.opendaylight.mdsal.dom.api.DOMMountPointListener; import org.opendaylight.mdsal.dom.api.DOMMountPointService; -import org.opendaylight.mdsal.dom.api.DOMRpcAvailabilityListener; import org.opendaylight.mdsal.dom.api.DOMRpcIdentifier; import org.opendaylight.mdsal.dom.api.DOMRpcImplementation; import org.opendaylight.mdsal.dom.api.DOMRpcResult; @@ -130,7 +129,6 @@ 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.Node; import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeBuilder; import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey; -import org.opendaylight.yangtools.concepts.ListenerRegistration; import org.opendaylight.yangtools.concepts.Registration; import org.opendaylight.yangtools.util.concurrent.FluentFutures; import org.opendaylight.yangtools.yang.binding.DataObject; @@ -258,18 +256,7 @@ public class MountPointEndToEndTest extends AbstractBaseSchemasTest { DOMRpcIdentifier.create(putTopRpcSchemaPath), DOMRpcIdentifier.create(getTopRpcSchemaPath)); final var rpcService = router.getRpcService(); - deviceRpcService = new Rpcs.Normalized() { - @Override - public ListenableFuture invokeRpc(final QName type, final ContainerNode input) { - return rpcService.invokeRpc(type, input); - } - - @Override - public ListenerRegistration registerRpcListener( - final T listener) { - return rpcService.registerRpcListener(listener); - } - }; + deviceRpcService = () -> rpcService; builderFactory = new NetconfClientConfigurationBuilderFactoryImpl(mockEncryptionService, credentialProvider, sslHandlerFactoryProvider); diff --git a/apps/netconf-topology-singleton/src/test/java/org/opendaylight/netconf/topology/singleton/impl/NetconfNodeActorTest.java b/apps/netconf-topology-singleton/src/test/java/org/opendaylight/netconf/topology/singleton/impl/NetconfNodeActorTest.java index 93b339576e..53104ca035 100644 --- a/apps/netconf-topology-singleton/src/test/java/org/opendaylight/netconf/topology/singleton/impl/NetconfNodeActorTest.java +++ b/apps/netconf-topology-singleton/src/test/java/org/opendaylight/netconf/topology/singleton/impl/NetconfNodeActorTest.java @@ -142,7 +142,9 @@ public class NetconfNodeActorTest extends AbstractBaseSchemasTest { private final SharedSchemaRepository masterSchemaRepository = new SharedSchemaRepository("master"); @Mock - private Rpcs.Normalized mockDOMRpcService; + private Rpcs.Normalized mockRpc; + @Mock + private DOMRpcService mockDOMRpcService; @Mock private Actions.Normalized mockDOMActionService; @Mock @@ -198,8 +200,10 @@ public class NetconfNodeActorTest extends AbstractBaseSchemasTest { doReturn(mockSchemaSourceReg1).when(mockRegistry).registerSchemaSource(any(), withSourceId(SOURCE_IDENTIFIER1)); doReturn(mockSchemaSourceReg2).when(mockRegistry).registerSchemaSource(any(), withSourceId(SOURCE_IDENTIFIER2)); - doReturn(mockSchemaContextFactory).when(mockSchemaRepository) - .createEffectiveModelContextFactory(); + doReturn(mockSchemaContextFactory).when(mockSchemaRepository).createEffectiveModelContextFactory(); + + doReturn(mockDOMRpcService).when(mockRpc).domRpcService(); + } @After @@ -598,7 +602,7 @@ public class NetconfNodeActorTest extends AbstractBaseSchemasTest { initializeMaster(List.of()); registerSlaveMountPoint(); - ArgumentCaptor domDataBrokerCaptor = ArgumentCaptor.forClass(DOMDataBroker.class); + final var domDataBrokerCaptor = ArgumentCaptor.forClass(DOMDataBroker.class); verify(mockMountPointBuilder).addService(eq(DOMDataBroker.class), domDataBrokerCaptor.capture()); final DOMDataBroker slaveDOMDataBroker = domDataBrokerCaptor.getValue(); @@ -688,7 +692,7 @@ public class NetconfNodeActorTest extends AbstractBaseSchemasTest { private void initializeMaster(final List sourceIdentifiers) { masterRef.tell(new CreateInitialMasterActorData(mockDOMDataBroker, netconfService, sourceIdentifiers, - new RemoteDeviceServices(mockDOMRpcService, mockDOMActionService)), testKit.getRef()); + new RemoteDeviceServices(mockRpc, mockDOMActionService)), testKit.getRef()); testKit.expectMsgClass(MasterActorDataInitialized.class); } diff --git a/plugins/netconf-client-mdsal/src/main/java/org/opendaylight/netconf/client/mdsal/DeviceSourcesResolver.java b/plugins/netconf-client-mdsal/src/main/java/org/opendaylight/netconf/client/mdsal/DeviceSourcesResolver.java index f2691a27a2..57b5050978 100644 --- a/plugins/netconf-client-mdsal/src/main/java/org/opendaylight/netconf/client/mdsal/DeviceSourcesResolver.java +++ b/plugins/netconf-client-mdsal/src/main/java/org/opendaylight/netconf/client/mdsal/DeviceSourcesResolver.java @@ -74,7 +74,7 @@ final class DeviceSourcesResolver implements Callable { final var sourceProvider = availableSchemas instanceof LibraryModulesSchemas libraryModule ? new LibrarySchemaSourceProvider(id, libraryModule.getAvailableModels()) - : new MonitoringSchemaSourceProvider(id, deviceRpc); + : new MonitoringSchemaSourceProvider(id, deviceRpc.domRpcService()); return new DeviceSources(requiredSources, providedSources, sourceProvider); } } \ No newline at end of file diff --git a/plugins/netconf-client-mdsal/src/main/java/org/opendaylight/netconf/client/mdsal/LibraryModulesSchemas.java b/plugins/netconf-client-mdsal/src/main/java/org/opendaylight/netconf/client/mdsal/LibraryModulesSchemas.java index 0a4288c12e..abe1626465 100644 --- a/plugins/netconf-client-mdsal/src/main/java/org/opendaylight/netconf/client/mdsal/LibraryModulesSchemas.java +++ b/plugins/netconf-client-mdsal/src/main/java/org/opendaylight/netconf/client/mdsal/LibraryModulesSchemas.java @@ -163,7 +163,8 @@ public final class LibraryModulesSchemas implements NetconfDeviceSchemas { public static LibraryModulesSchemas create(final NetconfDeviceRpc deviceRpc, final RemoteDeviceId deviceId) { final DOMRpcResult moduleListNodeResult; try { - moduleListNodeResult = deviceRpc.invokeRpc(Get.QNAME, GET_MODULES_STATE_MODULE_LIST_RPC).get(); + moduleListNodeResult = deviceRpc.domRpcService().invokeRpc(Get.QNAME, GET_MODULES_STATE_MODULE_LIST_RPC) + .get(); } catch (final InterruptedException e) { Thread.currentThread().interrupt(); throw new IllegalStateException(deviceId + ": Interrupted while waiting for response to " diff --git a/plugins/netconf-client-mdsal/src/main/java/org/opendaylight/netconf/client/mdsal/NetconfDevice.java b/plugins/netconf-client-mdsal/src/main/java/org/opendaylight/netconf/client/mdsal/NetconfDevice.java index d87e37d63a..d349afd2f3 100644 --- a/plugins/netconf-client-mdsal/src/main/java/org/opendaylight/netconf/client/mdsal/NetconfDevice.java +++ b/plugins/netconf-client-mdsal/src/main/java/org/opendaylight/netconf/client/mdsal/NetconfDevice.java @@ -187,10 +187,11 @@ public class NetconfDevice implements RemoteDevice { // TODO check whether the model describing create subscription is present in schema // Perhaps add a default schema context to support create-subscription if the model was not provided // (same as what we do for base netconf operations in transformer) - final var rpcResultListenableFuture = deviceRpc.invokeRpc(CreateSubscription.QNAME, Builders.containerBuilder() - .withNodeIdentifier(NodeIdentifier.create(CreateSubscriptionInput.QNAME)) - // Note: default 'stream' is 'NETCONF', we do not need to create an explicit leaf - .build()); + final var rpcResultListenableFuture = deviceRpc.domRpcService() + .invokeRpc(CreateSubscription.QNAME, Builders.containerBuilder() + .withNodeIdentifier(NodeIdentifier.create(CreateSubscriptionInput.QNAME)) + // Note: default 'stream' is 'NETCONF', we do not need to create an explicit leaf + .build()); Futures.addCallback(rpcResultListenableFuture, new FutureCallback() { @Override @@ -287,7 +288,7 @@ public class NetconfDevice implements RemoteDevice { final NetconfDeviceRpc deviceRpc = new NetconfDeviceRpc(schemaContext, listener, new NetconfMessageTransformer(emptyContext, false, baseSchema)); - return Futures.transform(deviceRpc.invokeRpc(Get.QNAME, Builders.containerBuilder() + return Futures.transform(deviceRpc.domRpcService().invokeRpc(Get.QNAME, Builders.containerBuilder() .withNodeIdentifier(NETCONF_GET_NODEID) .withChild(NetconfMessageTransformUtil.toFilterStructure(RFC8528_SCHEMA_MOUNTS, schemaContext)) .build()), rpcResult -> processSchemaMounts(rpcResult, emptyContext), MoreExecutors.directExecutor()); diff --git a/plugins/netconf-client-mdsal/src/main/java/org/opendaylight/netconf/client/mdsal/NetconfStateSchemasResolverImpl.java b/plugins/netconf-client-mdsal/src/main/java/org/opendaylight/netconf/client/mdsal/NetconfStateSchemasResolverImpl.java index bfe53ad72c..481a78408c 100644 --- a/plugins/netconf-client-mdsal/src/main/java/org/opendaylight/netconf/client/mdsal/NetconfStateSchemasResolverImpl.java +++ b/plugins/netconf-client-mdsal/src/main/java/org/opendaylight/netconf/client/mdsal/NetconfStateSchemasResolverImpl.java @@ -32,7 +32,7 @@ public final class NetconfStateSchemasResolverImpl implements NetconfDeviceSchem final RemoteDeviceId id, final EffectiveModelContext schemaContext) { // FIXME: I think we should prefer YANG library here if (remoteSessionCapabilities.isMonitoringSupported()) { - return NetconfStateSchemas.create(deviceRpc, remoteSessionCapabilities, id, schemaContext); + return NetconfStateSchemas.create(deviceRpc.domRpcService(), remoteSessionCapabilities, id, schemaContext); } if (remoteSessionCapabilities.containsModuleCapability(RFC8525_YANG_LIBRARY_CAPABILITY) || remoteSessionCapabilities.containsModuleCapability(RFC7895_YANG_LIBRARY_CAPABILITY)) { diff --git a/plugins/netconf-client-mdsal/src/main/java/org/opendaylight/netconf/client/mdsal/api/RemoteDeviceServices.java b/plugins/netconf-client-mdsal/src/main/java/org/opendaylight/netconf/client/mdsal/api/RemoteDeviceServices.java index ec1e5e9667..64d40aec8f 100644 --- a/plugins/netconf-client-mdsal/src/main/java/org/opendaylight/netconf/client/mdsal/api/RemoteDeviceServices.java +++ b/plugins/netconf-client-mdsal/src/main/java/org/opendaylight/netconf/client/mdsal/api/RemoteDeviceServices.java @@ -15,8 +15,6 @@ import org.eclipse.jdt.annotation.Nullable; import org.opendaylight.mdsal.dom.api.DOMActionService; import org.opendaylight.mdsal.dom.api.DOMRpcResult; import org.opendaylight.mdsal.dom.api.DOMRpcService; -import org.opendaylight.netconf.client.mdsal.api.RemoteDeviceServices.Actions; -import org.opendaylight.netconf.client.mdsal.api.RemoteDeviceServices.Rpcs; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; @@ -36,19 +34,22 @@ public record RemoteDeviceServices(@NonNull Rpcs rpcs, @Nullable Actions actions /** * NETCONF device RPCs operating just as any other {@link DOMRpcService}. */ - non-sealed interface Normalized extends Rpcs, DOMRpcService { + non-sealed interface Normalized extends Rpcs { @Override default ListenableFuture invokeNetconf(final QName type, final ContainerNode input) { - return invokeRpc(type, input); + return domRpcService().invokeRpc(type, input); } + + @NonNull DOMRpcService domRpcService(); } /** * NETCONF device RPCs operating in terms of {@link SchemalessRpcService}. */ - non-sealed interface Schemaless extends Rpcs, SchemalessRpcService { - // Just an interface combination + non-sealed interface Schemaless extends Rpcs { + + @NonNull SchemalessRpcService schemalessRpcService(); } } diff --git a/plugins/netconf-client-mdsal/src/main/java/org/opendaylight/netconf/client/mdsal/spi/KeepaliveSalFacade.java b/plugins/netconf-client-mdsal/src/main/java/org/opendaylight/netconf/client/mdsal/spi/KeepaliveSalFacade.java index d9551a7b02..8517cbf70a 100644 --- a/plugins/netconf-client-mdsal/src/main/java/org/opendaylight/netconf/client/mdsal/spi/KeepaliveSalFacade.java +++ b/plugins/netconf-client-mdsal/src/main/java/org/opendaylight/netconf/client/mdsal/spi/KeepaliveSalFacade.java @@ -28,6 +28,7 @@ import org.eclipse.jdt.annotation.NonNull; import org.opendaylight.mdsal.dom.api.DOMNotification; import org.opendaylight.mdsal.dom.api.DOMRpcAvailabilityListener; import org.opendaylight.mdsal.dom.api.DOMRpcResult; +import org.opendaylight.mdsal.dom.api.DOMRpcService; import org.opendaylight.netconf.client.mdsal.NetconfDeviceCommunicator; import org.opendaylight.netconf.client.mdsal.NetconfDeviceSchema; import org.opendaylight.netconf.client.mdsal.api.NetconfSessionPreferences; @@ -35,6 +36,7 @@ import org.opendaylight.netconf.client.mdsal.api.RemoteDeviceHandler; import org.opendaylight.netconf.client.mdsal.api.RemoteDeviceId; import org.opendaylight.netconf.client.mdsal.api.RemoteDeviceServices; import org.opendaylight.netconf.client.mdsal.api.RemoteDeviceServices.Rpcs; +import org.opendaylight.netconf.client.mdsal.api.SchemalessRpcService; import org.opendaylight.netconf.client.mdsal.impl.NetconfMessageTransformUtil; import org.opendaylight.netconf.common.NetconfTimer; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.base._1._0.rev110601.GetConfig; @@ -356,10 +358,32 @@ public final class KeepaliveSalFacade implements RemoteDeviceHandler { * invocation. Version for {@link Rpcs.Normalized}. */ private final class NormalizedKeepaliveRpcs implements Rpcs.Normalized { + private final @NonNull KeepaliveDOMRpcService domRpcService; private final Rpcs.Normalized delegate; NormalizedKeepaliveRpcs(final Rpcs.Normalized delegate) { this.delegate = requireNonNull(delegate); + domRpcService = new KeepaliveDOMRpcService(delegate.domRpcService()); + } + + @Override + public ListenableFuture invokeNetconf(final QName type, final ContainerNode input) { + // FIXME: what happens if we disable keepalive and then invokeRpc() throws? + disableKeepalive(); + return scheduleTimeout(delegate.invokeNetconf(type, input)); + } + + @Override + public DOMRpcService domRpcService() { + return domRpcService; + } + } + + private final class KeepaliveDOMRpcService implements DOMRpcService { + private final @NonNull DOMRpcService delegate; + + KeepaliveDOMRpcService(final DOMRpcService delegate) { + this.delegate = requireNonNull(delegate); } @Override @@ -371,10 +395,11 @@ public final class KeepaliveSalFacade implements RemoteDeviceHandler { @Override public ListenerRegistration registerRpcListener( - final T rpcListener) { + final T rpcListener) { // There is no real communication with the device (yet), hence no recordActivity() or anything return delegate.registerRpcListener(rpcListener); } + } /** @@ -382,10 +407,12 @@ public final class KeepaliveSalFacade implements RemoteDeviceHandler { * invocation. Version for {@link Rpcs.Schemaless}. */ private final class SchemalessKeepaliveRpcs implements Rpcs.Schemaless { + private final @NonNull KeepaliveSchemalessRpcService schemalessRpcService; private final Rpcs.Schemaless delegate; SchemalessKeepaliveRpcs(final Rpcs.Schemaless delegate) { this.delegate = requireNonNull(delegate); + schemalessRpcService = new KeepaliveSchemalessRpcService(delegate.schemalessRpcService()); } @Override @@ -396,10 +423,23 @@ public final class KeepaliveSalFacade implements RemoteDeviceHandler { } @Override - public ListenableFuture invokeRpc(final QName type, final DOMSource input) { + public SchemalessRpcService schemalessRpcService() { + return schemalessRpcService; + } + } + + private final class KeepaliveSchemalessRpcService implements SchemalessRpcService { + private final SchemalessRpcService delegate; + + KeepaliveSchemalessRpcService(final SchemalessRpcService delegate) { + this.delegate = requireNonNull(delegate); + } + + @Override + public ListenableFuture invokeRpc(final QName type, final DOMSource payload) { // FIXME: what happens if we disable keepalive and then invokeRpc() throws? disableKeepalive(); - return scheduleTimeout(delegate.invokeRpc(type, input)); + return scheduleTimeout(delegate.invokeRpc(type, payload)); } } } diff --git a/plugins/netconf-client-mdsal/src/main/java/org/opendaylight/netconf/client/mdsal/spi/NetconfDeviceDOMRpcService.java b/plugins/netconf-client-mdsal/src/main/java/org/opendaylight/netconf/client/mdsal/spi/NetconfDeviceDOMRpcService.java new file mode 100644 index 0000000000..3ef69252f7 --- /dev/null +++ b/plugins/netconf-client-mdsal/src/main/java/org/opendaylight/netconf/client/mdsal/spi/NetconfDeviceDOMRpcService.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2024 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.spi; + +import static java.util.Objects.requireNonNull; + +import com.google.common.collect.Collections2; +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.MoreExecutors; +import com.google.common.util.concurrent.SettableFuture; +import org.opendaylight.mdsal.dom.api.DOMRpcAvailabilityListener; +import org.opendaylight.mdsal.dom.api.DOMRpcIdentifier; +import org.opendaylight.mdsal.dom.api.DOMRpcImplementationNotAvailableException; +import org.opendaylight.mdsal.dom.api.DOMRpcResult; +import org.opendaylight.mdsal.dom.api.DOMRpcService; +import org.opendaylight.mdsal.dom.api.DefaultDOMRpcException; +import org.opendaylight.netconf.api.messages.NetconfMessage; +import org.opendaylight.netconf.client.mdsal.api.RemoteDeviceCommunicator; +import org.opendaylight.netconf.client.mdsal.api.RpcTransformer; +import org.opendaylight.yangtools.concepts.ListenerRegistration; +import org.opendaylight.yangtools.concepts.NoOpListenerRegistration; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.common.RpcResult; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; +import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; + +record NetconfDeviceDOMRpcService( + EffectiveModelContext modelContext, + RemoteDeviceCommunicator communicator, + RpcTransformer transformer) implements DOMRpcService { + NetconfDeviceDOMRpcService { + requireNonNull(modelContext); + } + + @Override + @SuppressWarnings("checkstyle:IllegalCatch") + public ListenableFuture invokeRpc(final QName type, final ContainerNode input) { + final var delegateFuture = communicator.sendRequest(transformer.toRpcRequest(type, input), type); + + final var ret = SettableFuture.create(); + Futures.addCallback(delegateFuture, new FutureCallback<>() { + @Override + public void onSuccess(final RpcResult result) { + final DOMRpcResult rpcResult; + try { + rpcResult = transformer.toRpcResult(result, type); + } catch (Exception cause) { + ret.setException(new DefaultDOMRpcException( + "Unable to parse rpc reply. type: " + type + " input: " + input, cause)); + return; + } + + ret.set(rpcResult); + } + + @Override + public void onFailure(final Throwable cause) { + ret.setException(new DOMRpcImplementationNotAvailableException(cause, "Unable to invoke rpc %s", + type)); + } + + }, MoreExecutors.directExecutor()); + return ret; + } + + @Override + public ListenerRegistration registerRpcListener(final T listener) { + listener.onRpcAvailable(Collections2.transform(modelContext.getOperations(), + input -> DOMRpcIdentifier.create(input.getQName()))); + return NoOpListenerRegistration.of(listener); + } +} \ No newline at end of file diff --git a/plugins/netconf-client-mdsal/src/main/java/org/opendaylight/netconf/client/mdsal/spi/NetconfDeviceMount.java b/plugins/netconf-client-mdsal/src/main/java/org/opendaylight/netconf/client/mdsal/spi/NetconfDeviceMount.java index eddce47171..0be46207c1 100644 --- a/plugins/netconf-client-mdsal/src/main/java/org/opendaylight/netconf/client/mdsal/spi/NetconfDeviceMount.java +++ b/plugins/netconf-client-mdsal/src/main/java/org/opendaylight/netconf/client/mdsal/spi/NetconfDeviceMount.java @@ -69,9 +69,9 @@ public class NetconfDeviceMount implements AutoCloseable { final var rpcs = services.rpcs(); mountBuilder.addService(NetconfRpcService.class, rpcs); if (rpcs instanceof Rpcs.Normalized normalized) { - mountBuilder.addService(DOMRpcService.class, normalized); + mountBuilder.addService(DOMRpcService.class, normalized.domRpcService()); } else if (rpcs instanceof Rpcs.Schemaless schemaless) { - mountBuilder.addService(SchemalessRpcService.class, schemaless); + mountBuilder.addService(SchemalessRpcService.class, schemaless.schemalessRpcService()); } if (services.actions() instanceof Actions.Normalized normalized) { mountBuilder.addService(DOMActionService.class, normalized); diff --git a/plugins/netconf-client-mdsal/src/main/java/org/opendaylight/netconf/client/mdsal/spi/NetconfDeviceRpc.java b/plugins/netconf-client-mdsal/src/main/java/org/opendaylight/netconf/client/mdsal/spi/NetconfDeviceRpc.java index 565ddabb50..63e965a05a 100644 --- a/plugins/netconf-client-mdsal/src/main/java/org/opendaylight/netconf/client/mdsal/spi/NetconfDeviceRpc.java +++ b/plugins/netconf-client-mdsal/src/main/java/org/opendaylight/netconf/client/mdsal/spi/NetconfDeviceRpc.java @@ -7,27 +7,12 @@ */ package org.opendaylight.netconf.client.mdsal.spi; -import static java.util.Objects.requireNonNull; - -import com.google.common.collect.Collections2; -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.MoreExecutors; -import com.google.common.util.concurrent.SettableFuture; -import org.opendaylight.mdsal.dom.api.DOMRpcAvailabilityListener; -import org.opendaylight.mdsal.dom.api.DOMRpcIdentifier; -import org.opendaylight.mdsal.dom.api.DOMRpcImplementationNotAvailableException; +import org.eclipse.jdt.annotation.NonNull; import org.opendaylight.mdsal.dom.api.DOMRpcResult; -import org.opendaylight.mdsal.dom.api.DefaultDOMRpcException; -import org.opendaylight.netconf.api.messages.NetconfMessage; +import org.opendaylight.mdsal.dom.api.DOMRpcService; import org.opendaylight.netconf.client.mdsal.api.RemoteDeviceCommunicator; import org.opendaylight.netconf.client.mdsal.api.RemoteDeviceServices.Rpcs; import org.opendaylight.netconf.client.mdsal.api.RpcTransformer; -import org.opendaylight.yangtools.concepts.ListenerRegistration; -import org.opendaylight.yangtools.concepts.NoOpListenerRegistration; -import org.opendaylight.yangtools.yang.common.QName; -import org.opendaylight.yangtools.yang.common.RpcResult; import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; @@ -36,53 +21,15 @@ import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; * {@link ContainerNode}. */ public final class NetconfDeviceRpc implements Rpcs.Normalized { - private final RemoteDeviceCommunicator communicator; - private final RpcTransformer transformer; - private final EffectiveModelContext modelContext; + private final @NonNull NetconfDeviceDOMRpcService domRpcService; public NetconfDeviceRpc(final EffectiveModelContext modelContext, final RemoteDeviceCommunicator communicator, final RpcTransformer transformer) { - this.modelContext = requireNonNull(modelContext); - this.communicator = communicator; - this.transformer = transformer; + domRpcService = new NetconfDeviceDOMRpcService(modelContext, communicator, transformer); } @Override - @SuppressWarnings("checkstyle:IllegalCatch") - public ListenableFuture invokeRpc(final QName type, final ContainerNode input) { - final var delegateFuture = communicator.sendRequest(transformer.toRpcRequest(type, input), type); - - final var ret = SettableFuture.create(); - Futures.addCallback(delegateFuture, new FutureCallback<>() { - @Override - public void onSuccess(final RpcResult result) { - final DOMRpcResult rpcResult; - try { - rpcResult = transformer.toRpcResult(result, type); - } catch (Exception cause) { - ret.setException(new DefaultDOMRpcException( - "Unable to parse rpc reply. type: " + type + " input: " + input, cause)); - return; - } - - ret.set(rpcResult); - } - - @Override - public void onFailure(final Throwable cause) { - ret.setException(new DOMRpcImplementationNotAvailableException(cause, "Unable to invoke rpc %s", type)); - } - - }, MoreExecutors.directExecutor()); - return ret; - } - - @Override - public ListenerRegistration registerRpcListener(final T listener) { - listener.onRpcAvailable(Collections2.transform(modelContext.getOperations(), - input -> DOMRpcIdentifier.create(input.getQName()))); - - // NOOP, no rpcs appear and disappear in this implementation - return NoOpListenerRegistration.of(listener); + public DOMRpcService domRpcService() { + return domRpcService; } } diff --git a/plugins/netconf-client-mdsal/src/main/java/org/opendaylight/netconf/client/mdsal/spi/SchemalessNetconfDeviceRpc.java b/plugins/netconf-client-mdsal/src/main/java/org/opendaylight/netconf/client/mdsal/spi/SchemalessNetconfDeviceRpc.java index 4008f7f1af..139a5752dc 100644 --- a/plugins/netconf-client-mdsal/src/main/java/org/opendaylight/netconf/client/mdsal/spi/SchemalessNetconfDeviceRpc.java +++ b/plugins/netconf-client-mdsal/src/main/java/org/opendaylight/netconf/client/mdsal/spi/SchemalessNetconfDeviceRpc.java @@ -12,7 +12,6 @@ import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; import com.google.common.util.concurrent.SettableFuture; -import javax.xml.transform.dom.DOMSource; import org.eclipse.jdt.annotation.NonNull; import org.opendaylight.mdsal.dom.api.DOMRpcImplementationNotAvailableException; import org.opendaylight.mdsal.dom.api.DOMRpcResult; @@ -21,6 +20,7 @@ import org.opendaylight.netconf.client.mdsal.api.RemoteDeviceCommunicator; import org.opendaylight.netconf.client.mdsal.api.RemoteDeviceId; import org.opendaylight.netconf.client.mdsal.api.RemoteDeviceServices.Rpcs; import org.opendaylight.netconf.client.mdsal.api.RpcTransformer; +import org.opendaylight.netconf.client.mdsal.api.SchemalessRpcService; import org.opendaylight.netconf.client.mdsal.impl.BaseRpcSchemalessTransformer; import org.opendaylight.netconf.client.mdsal.impl.SchemalessMessageTransformer; import org.opendaylight.yangtools.yang.common.QName; @@ -29,9 +29,10 @@ import org.opendaylight.yangtools.yang.common.YangConstants; import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; /** - * Invokes RPC by sending netconf message via listener. Also transforms result from NetconfMessage to CompositeNode. + * Invokes RPC by sending NETCONF message via listener. Also transforms result from NetconfMessage to CompositeNode. */ public final class SchemalessNetconfDeviceRpc implements Rpcs.Schemaless { + private final @NonNull SchemalessRpcService schemalessRpcService; private final RemoteDeviceCommunicator listener; private final BaseRpcSchemalessTransformer baseRpcTransformer; private final SchemalessMessageTransformer schemalessTransformer; @@ -44,6 +45,7 @@ public final class SchemalessNetconfDeviceRpc implements Rpcs.Schemaless { this.listener = listener; this.baseRpcTransformer = baseRpcTransformer; schemalessTransformer = messageTransformer; + schemalessRpcService = (type, input) -> handleRpc(type, input, schemalessTransformer); } @Override @@ -55,8 +57,8 @@ public final class SchemalessNetconfDeviceRpc implements Rpcs.Schemaless { } @Override - public ListenableFuture invokeRpc(final QName type, final DOMSource input) { - return handleRpc(type, input, schemalessTransformer); + public SchemalessRpcService schemalessRpcService() { + return schemalessRpcService; } private @NonNull ListenableFuture handleRpc(final @NonNull QName type, diff --git a/plugins/netconf-client-mdsal/src/test/java/org/opendaylight/netconf/client/mdsal/spi/KeepaliveSalFacadeResponseWaitingTest.java b/plugins/netconf-client-mdsal/src/test/java/org/opendaylight/netconf/client/mdsal/spi/KeepaliveSalFacadeResponseWaitingTest.java index b6fe782770..a73e74b0df 100644 --- a/plugins/netconf-client-mdsal/src/test/java/org/opendaylight/netconf/client/mdsal/spi/KeepaliveSalFacadeResponseWaitingTest.java +++ b/plugins/netconf-client-mdsal/src/test/java/org/opendaylight/netconf/client/mdsal/spi/KeepaliveSalFacadeResponseWaitingTest.java @@ -23,6 +23,7 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.opendaylight.mdsal.dom.api.DOMNotification; import org.opendaylight.mdsal.dom.api.DOMRpcResult; +import org.opendaylight.mdsal.dom.api.DOMRpcService; import org.opendaylight.mdsal.dom.spi.DefaultDOMRpcResult; import org.opendaylight.netconf.client.mdsal.NetconfDeviceCommunicator; import org.opendaylight.netconf.client.mdsal.NetconfDeviceSchema; @@ -50,6 +51,8 @@ class KeepaliveSalFacadeResponseWaitingTest { @Mock private Rpcs.Normalized deviceRpc; @Mock + private DOMRpcService deviceDomRpc; + @Mock private NetconfDeviceCommunicator listener; private DefaultNetconfTimer timer; @@ -78,7 +81,8 @@ class KeepaliveSalFacadeResponseWaitingTest { //This settable future object will be never set to any value. The test wants to simulate waiting for the result //of the future object. final var settableFuture = SettableFuture.create(); - doReturn(settableFuture).when(deviceRpc).invokeRpc(null, null); + doReturn(settableFuture).when(deviceDomRpc).invokeRpc(null, null); + doReturn(deviceDomRpc).when(deviceRpc).domRpcService(); //This settable future will be used to check the invokation of keepalive RPC. Should be never invoked. final var keepaliveSettableFuture = SettableFuture.create(); @@ -94,10 +98,10 @@ class KeepaliveSalFacadeResponseWaitingTest { underlyingSalFacade.invokeNullRpc(); //Invoking of general RPC. - verify(deviceRpc, after(2000).times(1)).invokeRpc(null, null); + verify(deviceDomRpc, after(2000).times(1)).invokeRpc(null, null); //verify the keepalive RPC invoke. Should be never happen. - verify(deviceRpc, after(2000).never()).invokeRpc(GetConfig.QNAME, KEEPALIVE_PAYLOAD); + verify(deviceDomRpc, after(2000).never()).invokeRpc(GetConfig.QNAME, KEEPALIVE_PAYLOAD); } private static final class LocalNetconfSalFacade implements RemoteDeviceHandler { @@ -132,7 +136,7 @@ class KeepaliveSalFacadeResponseWaitingTest { public void invokeNullRpc() { final var local = rpcs; if (local != null) { - local.invokeRpc(null, null); + local.domRpcService().invokeRpc(null, null); } } } diff --git a/plugins/netconf-client-mdsal/src/test/java/org/opendaylight/netconf/client/mdsal/spi/KeepaliveSalFacadeTest.java b/plugins/netconf-client-mdsal/src/test/java/org/opendaylight/netconf/client/mdsal/spi/KeepaliveSalFacadeTest.java index cc438d0ce1..25e88c05a7 100644 --- a/plugins/netconf-client-mdsal/src/test/java/org/opendaylight/netconf/client/mdsal/spi/KeepaliveSalFacadeTest.java +++ b/plugins/netconf-client-mdsal/src/test/java/org/opendaylight/netconf/client/mdsal/spi/KeepaliveSalFacadeTest.java @@ -28,6 +28,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import org.opendaylight.mdsal.dom.api.DOMRpcService; import org.opendaylight.mdsal.dom.spi.DefaultDOMRpcResult; import org.opendaylight.netconf.client.mdsal.NetconfDeviceCommunicator; import org.opendaylight.netconf.client.mdsal.api.RemoteDeviceHandler; @@ -52,6 +53,8 @@ class KeepaliveSalFacadeTest { private NetconfDeviceCommunicator listener; @Mock private Rpcs.Normalized deviceRpc; + @Mock + private DOMRpcService deviceDomRpc; private DefaultNetconfTimer timer; private KeepaliveSalFacade keepaliveSalFacade; @@ -62,6 +65,7 @@ class KeepaliveSalFacadeTest { timer = new DefaultNetconfTimer(); keepaliveSalFacade = new KeepaliveSalFacade(REMOTE_DEVICE_ID, underlyingSalFacade, timer, 1L, 1L); keepaliveSalFacade.setListener(listener); + doReturn(deviceDomRpc).when(deviceRpc).domRpcService(); } @AfterEach @@ -119,14 +123,14 @@ class KeepaliveSalFacadeTest { doAnswer(invocation -> proxyRpc = invocation.getArgument(2, RemoteDeviceServices.class).rpcs()) .when(underlyingSalFacade).onDeviceConnected(isNull(), isNull(), any(RemoteDeviceServices.class)); doReturn(Futures.immediateFailedFuture(new IllegalStateException("illegal-state"))) - .when(deviceRpc).invokeRpc(any(), any()); + .when(deviceDomRpc).invokeRpc(any(), any()); keepaliveSalFacade = new KeepaliveSalFacade(REMOTE_DEVICE_ID, underlyingSalFacade, timer, 100L, 1L); keepaliveSalFacade.setListener(listener); keepaliveSalFacade.onDeviceConnected(null, null, new RemoteDeviceServices(deviceRpc, null)); - assertInstanceOf(Rpcs.Normalized.class, proxyRpc) + assertInstanceOf(Rpcs.Normalized.class, proxyRpc).domRpcService() .invokeRpc(QName.create("foo", "bar"), mock(ContainerNode.class)); verify(listener, times(1)).disconnect(); diff --git a/plugins/netconf-client-mdsal/src/test/java/org/opendaylight/netconf/client/mdsal/spi/MountInstanceTest.java b/plugins/netconf-client-mdsal/src/test/java/org/opendaylight/netconf/client/mdsal/spi/MountInstanceTest.java index 880601e134..1b08d398d6 100644 --- a/plugins/netconf-client-mdsal/src/test/java/org/opendaylight/netconf/client/mdsal/spi/MountInstanceTest.java +++ b/plugins/netconf-client-mdsal/src/test/java/org/opendaylight/netconf/client/mdsal/spi/MountInstanceTest.java @@ -80,7 +80,7 @@ public class MountInstanceTest { notificationService, broker, null); verify(mountPointBuilder).addService(eq(DOMSchemaService.class), any()); verify(mountPointBuilder).addService(DOMDataBroker.class, broker); - verify(mountPointBuilder).addService(DOMRpcService.class, rpcService); + verify(mountPointBuilder).addService(DOMRpcService.class, rpcService.domRpcService()); verify(mountPointBuilder).addService(DOMNotificationService.class, notificationService); } @@ -90,7 +90,7 @@ public class MountInstanceTest { notificationService, null, netconfService); verify(mountPointBuilder).addService(eq(DOMSchemaService.class), any()); verify(mountPointBuilder).addService(NetconfDataTreeService.class, netconfService); - verify(mountPointBuilder).addService(DOMRpcService.class, rpcService); + verify(mountPointBuilder).addService(DOMRpcService.class, rpcService.domRpcService()); verify(mountPointBuilder).addService(DOMNotificationService.class, notificationService); } diff --git a/plugins/netconf-client-mdsal/src/test/java/org/opendaylight/netconf/client/mdsal/spi/NetconfDeviceRpcTest.java b/plugins/netconf-client-mdsal/src/test/java/org/opendaylight/netconf/client/mdsal/spi/NetconfDeviceRpcTest.java index 67b66cc372..1d319e8bf5 100644 --- a/plugins/netconf-client-mdsal/src/test/java/org/opendaylight/netconf/client/mdsal/spi/NetconfDeviceRpcTest.java +++ b/plugins/netconf-client-mdsal/src/test/java/org/opendaylight/netconf/client/mdsal/spi/NetconfDeviceRpcTest.java @@ -104,7 +104,8 @@ public class NetconfDeviceRpcTest extends AbstractBaseSchemasTest { final RpcResult result = RpcResultBuilder.success(msg).build(); when(communicatorMock.sendRequest(any(), any())).thenReturn(Futures.immediateFuture(result)); when(failingTransformer.toRpcResult(any(), any())).thenThrow(new RuntimeException("FAIL")); - final NetconfDeviceRpc failingRpc = new NetconfDeviceRpc(SCHEMA_CONTEXT, communicatorMock, failingTransformer); + final var failingRpc = new NetconfDeviceRpc(SCHEMA_CONTEXT, communicatorMock, failingTransformer) + .domRpcService(); assertThrows(ExecutionException.class, () -> failingRpc.invokeRpc(type, mock(ContainerNode.class)).get()); assertThrows(ExecutionException.class, () -> failingRpc.invokeRpc(type, null).get()); } @@ -112,7 +113,7 @@ public class NetconfDeviceRpcTest extends AbstractBaseSchemasTest { @Test public void testInvokeRpc() throws Exception { ContainerNode input = createNode("urn:ietf:params:xml:ns:netconf:base:1.0", "2011-06-01", "filter"); - final DOMRpcResult result = rpc.invokeRpc(type, input).get(); + final DOMRpcResult result = rpc.domRpcService().invokeRpc(type, input).get(); assertEquals(expectedReply.value().name(), result.value().name()); assertEquals(resolveNode(expectedReply), resolveNode(result)); } @@ -127,9 +128,9 @@ public class NetconfDeviceRpcTest extends AbstractBaseSchemasTest { @Test public void testRegisterRpcListener() throws Exception { - ArgumentCaptor argument = ArgumentCaptor.forClass(Collection.class); + final var argument = ArgumentCaptor.forClass(Collection.class); - rpc.registerRpcListener(listener); + rpc.domRpcService().registerRpcListener(listener); verify(listener).onRpcAvailable(argument.capture()); final Collection argValue = argument.getValue(); diff --git a/plugins/netconf-client-mdsal/src/test/java/org/opendaylight/netconf/client/mdsal/spi/SchemalessNetconfDeviceRpcTest.java b/plugins/netconf-client-mdsal/src/test/java/org/opendaylight/netconf/client/mdsal/spi/SchemalessNetconfDeviceRpcTest.java index a36bffcc5c..6568ddd7db 100644 --- a/plugins/netconf-client-mdsal/src/test/java/org/opendaylight/netconf/client/mdsal/spi/SchemalessNetconfDeviceRpcTest.java +++ b/plugins/netconf-client-mdsal/src/test/java/org/opendaylight/netconf/client/mdsal/spi/SchemalessNetconfDeviceRpcTest.java @@ -69,9 +69,9 @@ public class SchemalessNetconfDeviceRpcTest extends AbstractBaseSchemasTest { + " \n" + " \n" + " ")); - deviceRpc.invokeRpc(qName, src); - ArgumentCaptor msgCaptor = ArgumentCaptor.forClass(NetconfMessage.class); - ArgumentCaptor qnameCaptor = ArgumentCaptor.forClass(QName.class); + deviceRpc.schemalessRpcService().invokeRpc(qName, src); + final var msgCaptor = ArgumentCaptor.forClass(NetconfMessage.class); + final var qnameCaptor = ArgumentCaptor.forClass(QName.class); verify(listener).sendRequest(msgCaptor.capture(), qnameCaptor.capture()); LOG.info(XmlUtil.toString(msgCaptor.getValue().getDocument())); } -- 2.36.6