import static java.util.Objects.requireNonNull;
import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.Optional;
+import java.util.concurrent.ExecutionException;
import org.opendaylight.aaa.encrypt.AAAEncryptionService;
import org.opendaylight.controller.config.threadpool.ScheduledThreadPool;
import org.opendaylight.controller.config.threadpool.ThreadPool;
import org.opendaylight.mdsal.binding.api.DataBroker;
+import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
import org.opendaylight.mdsal.dom.api.DOMMountPointService;
import org.opendaylight.netconf.client.NetconfClientDispatcher;
import org.opendaylight.netconf.client.NetconfClientSessionListener;
import org.opendaylight.netconf.sal.connect.api.DeviceActionFactory;
import org.opendaylight.netconf.sal.connect.api.RemoteDevice;
import org.opendaylight.netconf.sal.connect.api.RemoteDeviceHandler;
+import org.opendaylight.netconf.sal.connect.api.RemoteDeviceId;
import org.opendaylight.netconf.sal.connect.api.SchemaResourceManager;
import org.opendaylight.netconf.sal.connect.netconf.LibraryModulesSchemas;
import org.opendaylight.netconf.sal.connect.netconf.NetconfDevice.SchemaResourcesDTO;
import org.opendaylight.netconf.sal.connect.netconf.SchemalessNetconfDevice;
import org.opendaylight.netconf.sal.connect.netconf.auth.DatastoreBackedPublicKeyAuth;
import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCommunicator;
-import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfSessionPreferences;
-import org.opendaylight.netconf.sal.connect.netconf.listener.UserPreferences;
import org.opendaylight.netconf.sal.connect.netconf.sal.KeepaliveSalFacade;
import org.opendaylight.netconf.sal.connect.netconf.sal.NetconfKeystoreAdapter;
import org.opendaylight.netconf.sal.connect.netconf.schema.YangLibrarySchemaYangSourceProvider;
import org.opendaylight.netconf.sal.connect.netconf.schema.mapping.BaseNetconfSchemas;
-import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId;
import org.opendaylight.netconf.sal.connect.util.SslHandlerFactoryImpl;
import org.opendaylight.netconf.topology.api.NetconfTopology;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Uri;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.optional.rev190614.NetconfNodeAugmentedOptional;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNode;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.parameters.Protocol;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.parameters.Protocol.Name;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.status.available.capabilities.AvailableCapability.CapabilityOrigin;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.Credentials;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.KeyAuth;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPw;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPwUnencrypted;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.key.auth.KeyBased;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.login.pw.LoginPassword;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.login.pw.unencrypted.LoginPasswordUnencrypted;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.schema.storage.YangLibrary;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev221225.connection.parameters.Protocol.Name;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev221225.credentials.Credentials;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev221225.credentials.credentials.KeyAuth;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev221225.credentials.credentials.LoginPw;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev221225.credentials.credentials.LoginPwUnencrypted;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.optional.rev221225.NetconfNodeAugmentedOptional;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev221225.NetconfNode;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyBuilder;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.common.Empty;
import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
final AAAEncryptionService encryptionService,
final DeviceActionFactory deviceActionFactory,
final BaseNetconfSchemas baseSchemas) {
- this.topologyId = topologyId;
+ this.topologyId = requireNonNull(topologyId);
this.clientDispatcher = clientDispatcher;
this.eventExecutor = eventExecutor;
this.keepaliveExecutor = keepaliveExecutor;
this.processingExecutor = MoreExecutors.listeningDecorator(processingExecutor.getExecutor());
this.schemaManager = requireNonNull(schemaManager);
this.deviceActionFactory = deviceActionFactory;
- this.dataBroker = dataBroker;
+ this.dataBroker = requireNonNull(dataBroker);
this.mountPointService = mountPointService;
this.encryptionService = encryptionService;
this.baseSchemas = requireNonNull(baseSchemas);
keystoreAdapter = new NetconfKeystoreAdapter(dataBroker);
+
+ // FIXME: this should be a put(), as we are initializing and will be re-populating the datastore with all the
+ // devices. Whatever has been there before should be nuked to properly re-align lifecycle.
+ final var wtx = dataBroker.newWriteOnlyTransaction();
+ wtx.merge(LogicalDatastoreType.OPERATIONAL, InstanceIdentifier.builder(NetworkTopology.class)
+ .child(Topology.class, new TopologyKey(new TopologyId(topologyId)))
+ .build(), new TopologyBuilder().setTopologyId(new TopologyId(topologyId)).build());
+ final var future = wtx.commit();
+ try {
+ future.get();
+ } catch (InterruptedException | ExecutionException e) {
+ LOG.error("Unable to initialize topology {}", topologyId, e);
+ throw new IllegalStateException(e);
+ }
+
+ LOG.debug("Topology {} initialized", topologyId);
}
@Override
@Override
public ListenableFuture<Empty> disconnectNode(final NodeId nodeId) {
- LOG.debug("Disconnecting RemoteDevice{{}}", nodeId.getValue());
+ final var nodeName = nodeId.getValue();
+ LOG.debug("Disconnecting RemoteDevice{{}}", nodeName);
final NetconfConnectorDTO connectorDTO = activeConnectors.remove(nodeId);
if (connectorDTO == null) {
return Futures.immediateFailedFuture(
- new IllegalStateException("Unable to disconnect device that is not connected"));
+ new IllegalStateException("Cannot disconnect " + nodeName + " as it is not connected"));
}
connectorDTO.close();
protected NetconfConnectorDTO createDeviceCommunicator(final NodeId nodeId, final NetconfNode node,
final NetconfNodeAugmentedOptional nodeOptional) {
- final RemoteDeviceId remoteDeviceId = NetconfNodeUtils.toRemoteDeviceId(nodeId, node);
-
+ final var deviceId = NetconfNodeUtils.toRemoteDeviceId(nodeId, node);
final long keepaliveDelay = node.requireKeepaliveDelay().toJava();
- RemoteDeviceHandler salFacade = createSalFacade(remoteDeviceId);
+
+ final var deviceSalFacade = new NetconfTopologyDeviceSalFacade(deviceId, mountPointService,
+ node.requireLockDatastore(), dataBroker);
+ // The facade we are going it present to NetconfDevice
+ RemoteDeviceHandler salFacade;
+ final KeepaliveSalFacade keepAliveFacade;
if (keepaliveDelay > 0) {
LOG.info("Adding keepalive facade, for device {}", nodeId);
- salFacade = new KeepaliveSalFacade(remoteDeviceId, salFacade, keepaliveExecutor.getExecutor(),
- keepaliveDelay, node.requireDefaultRequestTimeoutMillis().toJava());
+ salFacade = keepAliveFacade = new KeepaliveSalFacade(deviceId, deviceSalFacade,
+ keepaliveExecutor.getExecutor(), keepaliveDelay, node.requireDefaultRequestTimeoutMillis().toJava());
+ } else {
+ salFacade = deviceSalFacade;
+ keepAliveFacade = null;
+ }
+
+ // Setup reconnection on empty context, if so configured
+ if (nodeOptional != null && nodeOptional.getIgnoreMissingSchemaSources().getAllowed()) {
+ LOG.warn("Ignoring missing schema sources is not currently implemented for {}", deviceId);
}
final RemoteDevice<NetconfDeviceCommunicator> device;
final List<SchemaSourceRegistration<?>> yanglibRegistrations;
if (node.requireSchemaless()) {
- device = new SchemalessNetconfDevice(baseSchemas, remoteDeviceId, salFacade);
+ device = new SchemalessNetconfDevice(baseSchemas, deviceId, salFacade);
yanglibRegistrations = List.of();
} else {
- final boolean reconnectOnChangedSchema = node.requireReconnectOnChangedSchema();
final SchemaResourcesDTO resources = schemaManager.getSchemaResources(node.getSchemaCacheDirectory(),
nodeId.getValue());
device = new NetconfDeviceBuilder()
- .setReconnectOnSchemasChange(reconnectOnChangedSchema)
+ .setReconnectOnSchemasChange(node.requireReconnectOnChangedSchema())
.setSchemaResourcesDTO(resources)
.setGlobalProcessingExecutor(processingExecutor)
- .setId(remoteDeviceId)
+ .setId(deviceId)
.setSalFacade(salFacade)
- .setNode(node)
- .setEventExecutor(eventExecutor)
- .setNodeOptional(nodeOptional)
.setDeviceActionFactory(deviceActionFactory)
.setBaseSchemas(baseSchemas)
.build();
- yanglibRegistrations = registerDeviceSchemaSources(remoteDeviceId, node, resources);
+ yanglibRegistrations = registerDeviceSchemaSources(deviceId, node, resources);
}
- final Optional<UserPreferences> userCapabilities = getUserCapabilities(node);
final int rpcMessageLimit = node.requireConcurrentRpcLimit().toJava();
if (rpcMessageLimit < 1) {
- LOG.info("Concurrent rpc limit is smaller than 1, no limit will be enforced for device {}", remoteDeviceId);
+ LOG.info("Concurrent rpc limit is smaller than 1, no limit will be enforced for device {}", deviceId);
}
- final NetconfDeviceCommunicator netconfDeviceCommunicator =
- userCapabilities.isPresent() ? new NetconfDeviceCommunicator(remoteDeviceId, device,
- userCapabilities.get(), rpcMessageLimit)
- : new NetconfDeviceCommunicator(remoteDeviceId, device, rpcMessageLimit);
+ final var netconfDeviceCommunicator = new NetconfDeviceCommunicator(deviceId, device, rpcMessageLimit,
+ NetconfNodeUtils.extractUserCapabilities(node));
- if (salFacade instanceof KeepaliveSalFacade) {
- ((KeepaliveSalFacade)salFacade).setListener(netconfDeviceCommunicator);
+ if (keepAliveFacade != null) {
+ keepAliveFacade.setListener(netconfDeviceCommunicator);
}
return new NetconfConnectorDTO(netconfDeviceCommunicator, salFacade, yanglibRegistrations);
private static List<SchemaSourceRegistration<?>> registerDeviceSchemaSources(final RemoteDeviceId remoteDeviceId,
final NetconfNode node, final SchemaResourcesDTO resources) {
- final YangLibrary yangLibrary = node.getYangLibrary();
+ final var yangLibrary = node.getYangLibrary();
if (yangLibrary != null) {
final Uri uri = yangLibrary.getYangLibraryUrl();
if (uri != null) {
node.requireMaxConnectionAttempts().toJava(), node.requireBetweenAttemptsTimeoutMillis().toJava(),
node.requireSleepFactor().decimalValue());
final NetconfReconnectingClientConfigurationBuilder reconnectingClientConfigurationBuilder;
- final Protocol protocol = node.getProtocol();
+ final var protocol = node.getProtocol();
if (node.requireTcpOnly()) {
reconnectingClientConfigurationBuilder = NetconfReconnectingClientConfigurationBuilder.create()
.withProtocol(NetconfClientConfiguration.NetconfClientProtocol.TCP)
private AuthenticationHandler getHandlerFromCredentials(final Credentials credentials) {
if (credentials
- instanceof org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology
- .rev150114.netconf.node.credentials.credentials.LoginPassword loginPassword) {
+ instanceof org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev221225
+ .credentials.credentials.LoginPassword loginPassword) {
return new LoginPasswordHandler(loginPassword.getUsername(), loginPassword.getPassword());
}
- if (credentials instanceof LoginPwUnencrypted) {
- final LoginPasswordUnencrypted loginPassword =
- ((LoginPwUnencrypted) credentials).getLoginPasswordUnencrypted();
+ if (credentials instanceof LoginPwUnencrypted unencrypted) {
+ final var loginPassword = unencrypted.getLoginPasswordUnencrypted();
return new LoginPasswordHandler(loginPassword.getUsername(), loginPassword.getPassword());
}
- if (credentials instanceof LoginPw) {
- final LoginPassword loginPassword = ((LoginPw) credentials).getLoginPassword();
+ if (credentials instanceof LoginPw loginPw) {
+ final var loginPassword = loginPw.getLoginPassword();
return new LoginPasswordHandler(loginPassword.getUsername(),
encryptionService.decrypt(loginPassword.getPassword()));
}
- if (credentials instanceof KeyAuth) {
- final KeyBased keyPair = ((KeyAuth) credentials).getKeyBased();
+ if (credentials instanceof KeyAuth keyAuth) {
+ final var keyPair = keyAuth.getKeyBased();
return new DatastoreBackedPublicKeyAuth(keyPair.getUsername(), keyPair.getKeyId(),
keystoreAdapter, encryptionService);
}
throw new IllegalStateException("Unsupported credential type: " + credentials.getClass());
}
-
- protected abstract RemoteDeviceHandler createSalFacade(RemoteDeviceId id);
-
- private static Optional<UserPreferences> getUserCapabilities(final NetconfNode node) {
- // if none of yang-module-capabilities or non-module-capabilities is specified
- // just return absent
- if (node.getYangModuleCapabilities() == null && node.getNonModuleCapabilities() == null) {
- return Optional.empty();
- }
-
- final List<String> capabilities = new ArrayList<>();
-
- boolean overrideYangModuleCaps = false;
- if (node.getYangModuleCapabilities() != null) {
- capabilities.addAll(node.getYangModuleCapabilities().getCapability());
- overrideYangModuleCaps = node.getYangModuleCapabilities().getOverride();
- }
-
- //non-module capabilities should not exist in yang module capabilities
- final NetconfSessionPreferences netconfSessionPreferences = NetconfSessionPreferences.fromStrings(capabilities);
- Preconditions.checkState(netconfSessionPreferences.getNonModuleCaps().isEmpty(),
- "List yang-module-capabilities/capability should contain only module based capabilities. "
- + "Non-module capabilities used: " + netconfSessionPreferences.getNonModuleCaps());
-
- boolean overrideNonModuleCaps = false;
- if (node.getNonModuleCapabilities() != null) {
- capabilities.addAll(node.getNonModuleCapabilities().getCapability());
- overrideNonModuleCaps = node.getNonModuleCapabilities().getOverride();
- }
-
- return Optional.of(new UserPreferences(NetconfSessionPreferences
- .fromStrings(capabilities, CapabilityOrigin.UserDefined), overrideYangModuleCaps, overrideNonModuleCaps));
- }
}