X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=netconf%2Fsal-netconf-connector%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fnetconf%2Fsal%2Fconnect%2Fnetconf%2FNetconfDevice.java;h=741850a43fa9683ee7348c141e2dced058af82be;hb=e016165a2c2580a8759e6629b1cdb1950059e36a;hp=6e05e89a56cfd3c3d4ce1d5ee25eb560975935b1;hpb=4fd4d3e0089a0f574cf444510639abbc2a9bd485;p=netconf.git diff --git a/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/NetconfDevice.java b/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/NetconfDevice.java index 6e05e89a56..741850a43f 100644 --- a/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/NetconfDevice.java +++ b/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/NetconfDevice.java @@ -13,6 +13,8 @@ import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTr import com.google.common.base.Predicates; import com.google.common.collect.Collections2; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; @@ -20,20 +22,19 @@ import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListeningExecutorService; import com.google.common.util.concurrent.MoreExecutors; import com.google.common.util.concurrent.SettableFuture; -import io.netty.util.concurrent.EventExecutor; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; -import java.util.Collections; -import java.util.LinkedList; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Objects; -import java.util.Optional; import java.util.Set; import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import org.checkerframework.checker.lock.qual.GuardedBy; +import org.eclipse.jdt.annotation.NonNull; import org.opendaylight.mdsal.dom.api.DOMRpcResult; import org.opendaylight.mdsal.dom.api.DOMRpcService; import org.opendaylight.netconf.api.NetconfMessage; @@ -44,6 +45,7 @@ import org.opendaylight.netconf.sal.connect.api.NetconfDeviceSchemasResolver; import org.opendaylight.netconf.sal.connect.api.RemoteDevice; import org.opendaylight.netconf.sal.connect.api.RemoteDeviceCommunicator; import org.opendaylight.netconf.sal.connect.api.RemoteDeviceHandler; +import org.opendaylight.netconf.sal.connect.api.RemoteDeviceServices; import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCapabilities; import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCommunicator; import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfSessionPreferences; @@ -54,10 +56,11 @@ import org.opendaylight.netconf.sal.connect.netconf.schema.mapping.NetconfMessag import org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil; import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.notifications.rev120206.NetconfCapabilityChange; -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.status.available.capabilities.AvailableCapability; import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.status.available.capabilities.AvailableCapabilityBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.status.unavailable.capabilities.UnavailableCapability; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.status.unavailable.capabilities.UnavailableCapability.FailureReason; +import org.opendaylight.yangtools.concepts.Registration; import org.opendaylight.yangtools.rfc8528.data.api.MountPointContext; import org.opendaylight.yangtools.rfc8528.data.util.EmptyMountPointContext; import org.opendaylight.yangtools.rfc8528.model.api.SchemaMountConstants; @@ -75,9 +78,6 @@ import org.opendaylight.yangtools.yang.model.repo.api.SchemaRepository; import org.opendaylight.yangtools.yang.model.repo.api.SchemaResolutionException; import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier; import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource; -import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource; -import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceProvider; -import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistration; import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -85,7 +85,7 @@ import org.slf4j.LoggerFactory; /** * This is a mediator between NetconfDeviceCommunicator and NetconfDeviceSalFacade. */ -public class NetconfDevice implements RemoteDevice { +public class NetconfDevice implements RemoteDevice { private static final Logger LOG = LoggerFactory.getLogger(NetconfDevice.class); private static final QName RFC8528_SCHEMA_MOUNTS_QNAME = QName.create( @@ -98,44 +98,36 @@ public class NetconfDevice implements RemoteDevice> sourceRegistrations = new ArrayList<>(); + protected final List sourceRegistrations = new ArrayList<>(); - private final RemoteDeviceHandler salFacade; + private final RemoteDeviceHandler salFacade; private final ListeningExecutorService processingExecutor; private final DeviceActionFactory deviceActionFactory; private final NetconfDeviceSchemasResolver stateSchemasResolver; private final NotificationHandler notificationHandler; private final boolean reconnectOnSchemasChange; private final BaseNetconfSchemas baseSchemas; - private final NetconfNode node; - private final EventExecutor eventExecutor; - private final NetconfNodeAugmentedOptional nodeOptional; @GuardedBy("this") private boolean connected = false; // Message transformer is constructed once the schemas are available - private MessageTransformer messageTransformer; + private MessageTransformer messageTransformer; public NetconfDevice(final SchemaResourcesDTO schemaResourcesDTO, final BaseNetconfSchemas baseSchemas, - final RemoteDeviceId id, final RemoteDeviceHandler salFacade, + final RemoteDeviceId id, final RemoteDeviceHandler salFacade, final ListeningExecutorService globalProcessingExecutor, final boolean reconnectOnSchemasChange) { - this(schemaResourcesDTO, baseSchemas, id, salFacade, globalProcessingExecutor, reconnectOnSchemasChange, null, - null, null, null); + this(schemaResourcesDTO, baseSchemas, id, salFacade, globalProcessingExecutor, reconnectOnSchemasChange, null); } public NetconfDevice(final SchemaResourcesDTO schemaResourcesDTO, final BaseNetconfSchemas baseSchemas, - final RemoteDeviceId id, final RemoteDeviceHandler salFacade, + final RemoteDeviceId id, final RemoteDeviceHandler salFacade, final ListeningExecutorService globalProcessingExecutor, final boolean reconnectOnSchemasChange, - final DeviceActionFactory deviceActionFactory, final NetconfNode node, final EventExecutor eventExecutor, - final NetconfNodeAugmentedOptional nodeOptional) { + final DeviceActionFactory deviceActionFactory) { this.baseSchemas = requireNonNull(baseSchemas); this.id = id; this.reconnectOnSchemasChange = reconnectOnSchemasChange; this.deviceActionFactory = deviceActionFactory; - this.node = node; - this.eventExecutor = eventExecutor; - this.nodeOptional = nodeOptional; schemaRegistry = schemaResourcesDTO.getSchemaRegistry(); schemaRepository = schemaResourcesDTO.getSchemaRepository(); schemaContextFactory = schemaResourcesDTO.getSchemaContextFactory(); @@ -167,39 +159,27 @@ public class NetconfDevice implements RemoteDevice futureSchema = Futures.transformAsync(sourceResolverFuture, + final ListenableFuture futureSchema = Futures.transformAsync(sourceResolverFuture, deviceSources -> assembleSchemaContext(deviceSources, remoteSessionCapabilities), processingExecutor); // Potentially acquire mount point list and interpret it - final ListenableFuture futureContext = Futures.transformAsync(futureSchema, - schemaContext -> createMountPointContext(schemaContext, baseSchema, listener), processingExecutor); + final ListenableFuture futureContext = Futures.transformAsync(futureSchema, + result -> Futures.transform(createMountPointContext(result.modelContext(), baseSchema, listener), + mount -> new NetconfDeviceSchema(result.capabilities(), mount), processingExecutor), + processingExecutor); - Futures.addCallback(futureContext, new FutureCallback() { + Futures.addCallback(futureContext, new FutureCallback<>() { @Override - public void onSuccess(final MountPointContext result) { + public void onSuccess(final NetconfDeviceSchema result) { handleSalInitializationSuccess(result, remoteSessionCapabilities, - getDeviceSpecificRpc(result, listener, baseSchema), listener); + getDeviceSpecificRpc(result.mountContext(), listener, baseSchema), listener); } @Override public void onFailure(final Throwable cause) { LOG.warn("{}: Unexpected error resolving device sources", id, cause); - - // No more sources, fail or try to reconnect - if (cause instanceof EmptySchemaContextException) { - if (nodeOptional != null && nodeOptional.getIgnoreMissingSchemaSources().getAllowed()) { - eventExecutor.schedule(() -> { - LOG.warn("Reconnection is allowed! This can lead to unexpected errors at runtime."); - LOG.warn("{} : No more sources for schema context.", id); - LOG.info("{} : Try to remount device.", id); - onRemoteSessionDown(); - salFacade.onDeviceReconnected(remoteSessionCapabilities, node); - }, nodeOptional.getIgnoreMissingSchemaSources().getReconnectTime().toJava(), - TimeUnit.MILLISECONDS); - return; - } - } - + // FIXME: this causes salFacade to see onDeviceDisconnected() and then onDeviceFailed(), which is quite + // weird handleSalInitializationFailure(cause, listener); salFacade.onDeviceFailed(cause); } @@ -224,9 +204,9 @@ public class NetconfDevice implements RemoteDevice transformer) { + private synchronized void updateTransformer(final MessageTransformer transformer) { messageTransformer = transformer; } @@ -285,21 +267,18 @@ public class NetconfDevice implements RemoteDevice assembleSchemaContext(final DeviceSources deviceSources, + private ListenableFuture assembleSchemaContext(final DeviceSources deviceSources, final NetconfSessionPreferences remoteSessionCapabilities) { LOG.debug("{}: Resolved device sources to {}", id, deviceSources); - final SchemaSourceProvider yangProvider = deviceSources.getSourceProvider(); - for (final SourceIdentifier sourceId : deviceSources.getProvidedSources()) { - sourceRegistrations.add(schemaRegistry.registerSchemaSource(yangProvider, - PotentialSchemaSource.create(sourceId, YangTextSchemaSource.class, - PotentialSchemaSource.Costs.REMOTE_IO.getValue()))); - } + + sourceRegistrations.addAll(deviceSources.register(schemaRegistry)); return new SchemaSetup(deviceSources, remoteSessionCapabilities).startResolution(); } - private ListenableFuture createMountPointContext(final EffectiveModelContext schemaContext, - final BaseSchema baseSchema, final NetconfDeviceCommunicator listener) { + private ListenableFuture<@NonNull MountPointContext> createMountPointContext( + final EffectiveModelContext schemaContext, final BaseSchema baseSchema, + final NetconfDeviceCommunicator listener) { final MountPointContext emptyContext = new EmptyMountPointContext(schemaContext); if (schemaContext.findModule(SchemaMountConstants.RFC8528_MODULE).isEmpty()) { return Futures.immediateFuture(emptyContext); @@ -340,7 +319,7 @@ public class NetconfDevice implements RemoteDevice { - private final SettableFuture resultFuture = SettableFuture.create(); + private final SettableFuture resultFuture = SettableFuture.create(); + + private final Set nonModuleBasedCapabilities = new HashSet<>(); + private final Map unresolvedCapabilites = new HashMap<>(); + private final Set resolvedCapabilities = new HashSet<>(); private final DeviceSources deviceSources; private final NetconfSessionPreferences remoteSessionCapabilities; - private final NetconfDeviceCapabilities capabilities; private Collection requiredSources; SchemaSetup(final DeviceSources deviceSources, final NetconfSessionPreferences remoteSessionCapabilities) { this.deviceSources = deviceSources; this.remoteSessionCapabilities = remoteSessionCapabilities; - capabilities = remoteSessionCapabilities.getNetconfDeviceCapabilities(); // If device supports notifications and does not contain necessary modules, add them automatically if (remoteSessionCapabilities.containsNonModuleCapability( @@ -448,12 +440,12 @@ public class NetconfDevice implements RemoteDevice missingSources = filterMissingSources(requiredSources); - capabilities.addUnresolvedCapabilities(getQNameFromSourceIdentifiers(missingSources), - UnavailableCapability.FailureReason.MissingSource); + addUnresolvedCapabilities(getQNameFromSourceIdentifiers(missingSources), + UnavailableCapability.FailureReason.MissingSource); requiredSources.removeAll(missingSources); } - ListenableFuture startResolution() { + ListenableFuture startResolution() { trySetupSchema(); return resultFuture; } @@ -463,19 +455,24 @@ public class NetconfDevice implements RemoteDevice filteredQNames = Sets.difference(deviceSources.getRequiredSourcesQName(), - capabilities.getUnresolvedCapabilites().keySet()); - capabilities.addCapabilities(filteredQNames.stream().map(entry -> new AvailableCapabilityBuilder() - .setCapability(entry.toString()).setCapabilityOrigin( - remoteSessionCapabilities.getModuleBasedCapsOrigin().get(entry)).build()) - .collect(Collectors.toList())); - - capabilities.addNonModuleBasedCapabilities(remoteSessionCapabilities - .getNonModuleCaps().stream().map(entry -> new AvailableCapabilityBuilder() - .setCapability(entry).setCapabilityOrigin( - remoteSessionCapabilities.getNonModuleBasedCapsOrigin().get(entry)).build()) - .collect(Collectors.toList())); - - resultFuture.set(result); + unresolvedCapabilites.keySet()); + resolvedCapabilities.addAll(filteredQNames.stream() + .map(capability -> new AvailableCapabilityBuilder() + .setCapability(capability.toString()) + .setCapabilityOrigin(remoteSessionCapabilities.capabilityOrigin(capability)) + .build()) + .collect(Collectors.toList())); + + nonModuleBasedCapabilities.addAll(remoteSessionCapabilities.nonModuleCaps().keySet().stream() + .map(capability -> new AvailableCapabilityBuilder() + .setCapability(capability) + .setCapabilityOrigin(remoteSessionCapabilities.capabilityOrigin(capability)) + .build()) + .collect(Collectors.toList())); + + + resultFuture.set(new SchemaResult(new NetconfDeviceCapabilities(ImmutableMap.copyOf(unresolvedCapabilites), + ImmutableSet.copyOf(resolvedCapabilities), ImmutableSet.copyOf(nonModuleBasedCapabilities)), result)); } @Override @@ -509,7 +506,7 @@ public class NetconfDevice implements RemoteDevice filterMissingSources(final Collection origSources) { + private List filterMissingSources(final Collection origSources) { return origSources.parallelStream().filter(sourceIdentifier -> { try { schemaRepository.getSchemaSource(sourceIdentifier, YangTextSchemaSource.class).get(); @@ -520,7 +517,13 @@ public class NetconfDevice implements RemoteDevice handleMissingSchemaSourceException( + private void addUnresolvedCapabilities(final Collection capabilities, final FailureReason reason) { + for (QName s : capabilities) { + unresolvedCapabilites.put(s, reason); + } + } + + private List handleMissingSchemaSourceException( final MissingSchemaSourceException exception) { // In case source missing, try without it final SourceIdentifier missingSource = exception.getSourceId(); @@ -528,11 +531,9 @@ public class NetconfDevice implements RemoteDevice qNameOfMissingSource = - getQNameFromSourceIdentifiers(Sets.newHashSet(missingSource)); + final var qNameOfMissingSource = getQNameFromSourceIdentifiers(Sets.newHashSet(missingSource)); if (!qNameOfMissingSource.isEmpty()) { - capabilities.addUnresolvedCapabilities( - qNameOfMissingSource, UnavailableCapability.FailureReason.MissingSource); + addUnresolvedCapabilities(qNameOfMissingSource, UnavailableCapability.FailureReason.MissingSource); } return stripUnavailableSource(missingSource); } @@ -549,14 +550,13 @@ public class NetconfDevice implements RemoteDevice unresolvedSources = resolutionException.getUnsatisfiedImports().keySet(); - capabilities.addUnresolvedCapabilities(getQNameFromSourceIdentifiers(unresolvedSources), + addUnresolvedCapabilities( + getQNameFromSourceIdentifiers(resolutionException.getUnsatisfiedImports().keySet()), UnavailableCapability.FailureReason.UnableToResolve); LOG.warn("{}: Unable to build schema context, unsatisfied imports {}, will reattempt with resolved only", id, resolutionException.getUnsatisfiedImports()); @@ -565,11 +565,11 @@ public class NetconfDevice implements RemoteDevice stripUnavailableSource(final SourceIdentifier sourceIdToRemove) { - final LinkedList sourceIdentifiers = new LinkedList<>(requiredSources); - checkState(sourceIdentifiers.remove(sourceIdToRemove), - "%s: Trying to remove %s from %s failed", id, sourceIdToRemove, requiredSources); - return sourceIdentifiers; + private List stripUnavailableSource(final SourceIdentifier sourceIdToRemove) { + final var tmp = new ArrayList<>(requiredSources); + checkState(tmp.remove(sourceIdToRemove), "%s: Trying to remove %s from %s failed", id, sourceIdToRemove, + requiredSources); + return tmp; } private Collection getQNameFromSourceIdentifiers(final Collection identifiers) {