*/
package org.opendaylight.netconf.topology.spi;
-import static org.hamcrest.CoreMatchers.instanceOf;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import java.net.InetSocketAddress;
import java.util.List;
import java.util.concurrent.TimeUnit;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.Test;
-import org.junit.runner.RunWith;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnitRunner;
+import org.mockito.junit.jupiter.MockitoExtension;
import org.opendaylight.aaa.encrypt.AAAEncryptionService;
import org.opendaylight.netconf.api.CapabilityURN;
import org.opendaylight.netconf.client.NetconfClientFactory;
+import org.opendaylight.netconf.client.NetconfClientFactoryImpl;
import org.opendaylight.netconf.client.NetconfClientSession;
import org.opendaylight.netconf.client.mdsal.NetconfDeviceCapabilities;
import org.opendaylight.netconf.client.mdsal.NetconfDeviceSchema;
-import org.opendaylight.netconf.client.mdsal.api.BaseNetconfSchemas;
+import org.opendaylight.netconf.client.mdsal.api.BaseNetconfSchemaProvider;
import org.opendaylight.netconf.client.mdsal.api.CredentialProvider;
import org.opendaylight.netconf.client.mdsal.api.DeviceActionFactory;
import org.opendaylight.netconf.client.mdsal.api.NetconfSessionPreferences;
import org.opendaylight.netconf.client.mdsal.api.RemoteDeviceServices;
import org.opendaylight.netconf.client.mdsal.api.RemoteDeviceServices.Rpcs;
import org.opendaylight.netconf.client.mdsal.api.SchemaResourceManager;
-import org.opendaylight.netconf.client.mdsal.api.SslHandlerFactoryProvider;
-import org.opendaylight.netconf.client.mdsal.impl.DefaultBaseNetconfSchemas;
+import org.opendaylight.netconf.client.mdsal.api.SslContextFactoryProvider;
+import org.opendaylight.netconf.client.mdsal.impl.DefaultBaseNetconfSchemaProvider;
import org.opendaylight.netconf.common.NetconfTimer;
+import org.opendaylight.netconf.common.impl.DefaultNetconfTimer;
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;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.PortNumber;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev240120.credentials.credentials.LoginPwUnencryptedBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev240120.credentials.credentials.login.pw.unencrypted.LoginPasswordUnencryptedBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev231121.NetconfNodeBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev240611.credentials.credentials.KeyAuthBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev240611.credentials.credentials.LoginPwUnencryptedBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev240611.credentials.credentials.key.auth.KeyBasedBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev240611.credentials.credentials.login.pw.unencrypted.LoginPasswordUnencryptedBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev240611.NetconfNodeBuilder;
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
import org.opendaylight.yangtools.yang.common.Decimal64;
import org.opendaylight.yangtools.yang.common.Uint16;
import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
import org.opendaylight.yangtools.yang.parser.impl.DefaultYangParserFactory;
-@RunWith(MockitoJUnitRunner.StrictStubs.class)
-public class NetconfNodeHandlerTest {
+@ExtendWith(MockitoExtension.class)
+class NetconfNodeHandlerTest {
private static final RemoteDeviceId DEVICE_ID = new RemoteDeviceId("netconf-topology",
new InetSocketAddress(InetAddresses.forString("127.0.0.1"), 9999));
private static final NodeId NODE_ID = new NodeId("testing-node");
- private static BaseNetconfSchemas BASE_SCHEMAS;
+ private static BaseNetconfSchemaProvider BASE_SCHEMAS;
// Core setup
@Mock
// DefaultNetconfClientConfigurationBuilderFactory setup
@Mock
- private SslHandlerFactoryProvider sslHandlerFactoryProvider;
+ private SslContextFactoryProvider sslContextFactoryProvider;
@Mock
private AAAEncryptionService encryptionService;
@Mock
private NetconfTopologySchemaAssembler schemaAssembler;
private NetconfNodeHandler handler;
- @BeforeClass
- public static void beforeClass() throws Exception {
- BASE_SCHEMAS = new DefaultBaseNetconfSchemas(new DefaultYangParserFactory());
+ @BeforeAll
+ static void beforeClass() throws Exception {
+ BASE_SCHEMAS = new DefaultBaseNetconfSchemaProvider(new DefaultYangParserFactory());
}
- @BeforeClass
- public static void afterClass() throws Exception {
+ @AfterAll
+ static void afterClass() throws Exception {
BASE_SCHEMAS = null;
}
- @Before
- public void before() {
+ @BeforeEach
+ void before() {
schemaAssembler = new NetconfTopologySchemaAssembler(1, 1, 0, TimeUnit.SECONDS);
// Instantiate the handler
handler = new NetconfNodeHandler(clientFactory, timer, BASE_SCHEMAS, schemaManager, schemaAssembler,
new NetconfClientConfigurationBuilderFactoryImpl(encryptionService, credentialProvider,
- sslHandlerFactoryProvider),
+ sslContextFactoryProvider),
deviceActionFactory, delegate, DEVICE_ID, NODE_ID, new NetconfNodeBuilder()
.setHost(new Host(new IpAddress(new Ipv4Address("127.0.0.1"))))
.setPort(new PortNumber(Uint16.valueOf(9999)))
.build(), null);
}
- @After
- public void after() {
+ @AfterEach
+ void after() {
schemaAssembler.close();
}
@Test
- public void successfulOnDeviceConnectedPropagates() throws Exception {
+ void successfulOnDeviceConnectedPropagates() throws Exception {
assertSuccessfulConnect();
assertEquals(1, handler.attempts());
}
@Test
- public void failedSchemaCausesReconnect() throws Exception {
+ void failedSchemaCausesReconnect() throws Exception {
assertSuccessfulConnect();
assertEquals(1, handler.attempts());
}
@Test
- public void downAfterUpCausesReconnect() throws Exception {
+ void downAfterUpCausesReconnect() throws Exception {
// Let's borrow common bits
successfulOnDeviceConnectedPropagates();
}
@Test
- public void socketFailuresAreRetried() throws Exception {
+ void socketFailuresAreRetried() throws Exception {
final var firstFuture = SettableFuture.create();
final var secondFuture = SettableFuture.create();
doReturn(firstFuture, secondFuture).when(clientFactory).createClient(any());
final var throwableCaptor = ArgumentCaptor.forClass(Throwable.class);
doNothing().when(delegate).onDeviceFailed(throwableCaptor.capture());
secondFuture.setException(new AssertionError("second"));
- assertThat(throwableCaptor.getValue(), instanceOf(ConnectGivenUpException.class));
+ assertInstanceOf(ConnectGivenUpException.class, throwableCaptor.getValue());
// but nothing else happens
assertEquals(2, handler.attempts());
verify(clientFactory).createClient(any());
verifyNoInteractions(delegate);
}
+
+ @Test
+ void failToConnectOnUnsupportedConfiguration() {
+ final var defaultTimer = new DefaultNetconfTimer();
+ final var factory = new NetconfClientFactoryImpl(defaultTimer);
+
+ final var keyId = "keyId";
+ final var keyAuthHandler = new NetconfNodeHandler(factory, defaultTimer, BASE_SCHEMAS, schemaManager,
+ schemaAssembler, new NetconfClientConfigurationBuilderFactoryImpl(encryptionService, credentialProvider,
+ sslContextFactoryProvider),
+ deviceActionFactory, delegate, DEVICE_ID, NODE_ID, new NetconfNodeBuilder()
+ .setHost(new Host(new IpAddress(new Ipv4Address("127.0.0.1"))))
+ .setPort(new PortNumber(Uint16.valueOf(9999)))
+ .setReconnectOnChangedSchema(true)
+ .setSchemaless(true)
+ .setTcpOnly(false)
+ .setProtocol(null)
+ .setBackoffMultiplier(Decimal64.valueOf("1.5"))
+ .setConcurrentRpcLimit(Uint16.ONE)
+ // One reconnection attempt
+ .setMaxConnectionAttempts(Uint32.ONE)
+ .setDefaultRequestTimeoutMillis(Uint32.valueOf(1000))
+ .setMinBackoffMillis(Uint16.valueOf(100))
+ .setKeepaliveDelay(Uint32.valueOf(1000))
+ .setConnectionTimeoutMillis(Uint32.valueOf(1000))
+ .setMaxBackoffMillis(Uint32.valueOf(1000))
+ .setBackoffJitter(Decimal64.valueOf("0.0"))
+ .setCredentials(new KeyAuthBuilder()
+ .setKeyBased(new KeyBasedBuilder()
+ .setUsername("testuser")
+ .setKeyId(keyId)
+ .build())
+ .build())
+ .build(), null);
+
+ // return null when attempt to load credentials fot key id
+ doReturn(null).when(credentialProvider).credentialForId(any());
+ doNothing().when(delegate).onDeviceFailed(any());
+ keyAuthHandler.connect();
+ verify(credentialProvider).credentialForId(eq(keyId));
+ // attempt to connect fails due to unsupported configuration, and there is attempt to reconnect
+ final var captor = ArgumentCaptor.forClass(Throwable.class);
+ verify(delegate).onDeviceFailed(captor.capture());
+ assertInstanceOf(ConnectGivenUpException.class, captor.getValue());
+ assertEquals(1, keyAuthHandler.attempts());
+ }
}