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=e50ba00fa95073d0f400dbb2ca4185df2fae7aa9;hb=b8f5ebd2f817de1d613d84cf41f3516728767eba;hp=c9ccf93fcbcf54385d1f99179218da152c55636e;hpb=6ab1941217e38336ae5970a29505f9bb21d4ef21;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 c9ccf93fcb..e50ba00fa9 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 @@ -7,35 +7,38 @@ */ package org.opendaylight.netconf.sal.connect.netconf; -import com.google.common.base.Optional; +import static java.util.Objects.requireNonNull; + import com.google.common.base.Preconditions; import com.google.common.base.Predicates; import com.google.common.collect.Collections2; import com.google.common.collect.Lists; import com.google.common.collect.Sets; -import com.google.common.util.concurrent.CheckedFuture; +import com.google.common.util.concurrent.FluentFuture; 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.ListeningExecutorService; import com.google.common.util.concurrent.MoreExecutors; +import io.netty.util.concurrent.EventExecutor; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.LinkedList; import java.util.List; +import java.util.Optional; import java.util.Set; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; +import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import javax.annotation.Nonnull; import javax.annotation.concurrent.GuardedBy; -import org.opendaylight.controller.md.sal.dom.api.DOMNotification; -import org.opendaylight.controller.md.sal.dom.api.DOMRpcException; -import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult; -import org.opendaylight.controller.md.sal.dom.api.DOMRpcService; +import org.opendaylight.mdsal.dom.api.DOMNotification; +import org.opendaylight.mdsal.dom.api.DOMRpcResult; +import org.opendaylight.mdsal.dom.api.DOMRpcService; import org.opendaylight.netconf.api.NetconfMessage; +import org.opendaylight.netconf.sal.connect.api.DeviceActionFactory; import org.opendaylight.netconf.sal.connect.api.MessageTransformer; import org.opendaylight.netconf.sal.connect.api.NetconfDeviceSchemas; import org.opendaylight.netconf.sal.connect.api.NetconfDeviceSchemasResolver; @@ -53,6 +56,8 @@ 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.AvailableCapabilityBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.status.unavailable.capabilities.UnavailableCapability; import org.opendaylight.yangtools.yang.common.QName; @@ -62,7 +67,6 @@ import org.opendaylight.yangtools.yang.model.repo.api.RevisionSourceIdentifier; import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactory; 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.SchemaSourceRepresentation; 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; @@ -81,17 +85,22 @@ public class NetconfDevice private static final Logger LOG = LoggerFactory.getLogger(NetconfDevice.class); protected final RemoteDeviceId id; - private final boolean reconnectOnSchemasChange; - protected final SchemaContextFactory schemaContextFactory; - private final RemoteDeviceHandler salFacade; - private final ListeningExecutorService processingExecutor; protected final SchemaSourceRegistry schemaRegistry; protected final SchemaRepository schemaRepository; + + protected final List> sourceRegistrations = new ArrayList<>(); + + private final RemoteDeviceHandler salFacade; + private final ListeningExecutorService processingExecutor; + private final DeviceActionFactory deviceActionFactory; private final NetconfDeviceSchemasResolver stateSchemasResolver; private final NotificationHandler notificationHandler; - protected final List> sourceRegistrations = - new ArrayList<>(); + private final boolean reconnectOnSchemasChange; + private final NetconfNode node; + private final EventExecutor eventExecutor; + private final NetconfNodeAugmentedOptional nodeOptional; + @GuardedBy("this") private boolean connected = false; @@ -104,25 +113,41 @@ public class NetconfDevice */ static NetconfDeviceRpc getRpcForInitialization(final NetconfDeviceCommunicator listener, final boolean notificationSupport) { - final BaseSchema baseSchema = notificationSupport - ? BaseSchema.BASE_NETCONF_CTX_WITH_NOTIFICATIONS - : BaseSchema.BASE_NETCONF_CTX; + final BaseSchema baseSchema = resolveBaseSchema(notificationSupport); return new NetconfDeviceRpc(baseSchema.getSchemaContext(), listener, new NetconfMessageTransformer(baseSchema.getSchemaContext(), false, baseSchema)); } + private static BaseSchema resolveBaseSchema(final boolean notificationSupport) { + return notificationSupport ? BaseSchema.BASE_NETCONF_CTX_WITH_NOTIFICATIONS : BaseSchema.BASE_NETCONF_CTX; + } + public NetconfDevice(final SchemaResourcesDTO schemaResourcesDTO, final RemoteDeviceId id, final RemoteDeviceHandler salFacade, - final ExecutorService globalProcessingExecutor, final boolean reconnectOnSchemasChange) { + final ListeningExecutorService globalProcessingExecutor, + final boolean reconnectOnSchemasChange) { + this(schemaResourcesDTO, id, salFacade, globalProcessingExecutor, reconnectOnSchemasChange, null, null, null, + null); + } + + public NetconfDevice(final SchemaResourcesDTO schemaResourcesDTO, final RemoteDeviceId id, + final RemoteDeviceHandler salFacade, + final ListeningExecutorService globalProcessingExecutor, final boolean reconnectOnSchemasChange, + final DeviceActionFactory deviceActionFactory, final NetconfNode node, final EventExecutor eventExecutor, + final NetconfNodeAugmentedOptional nodeOptional) { this.id = id; this.reconnectOnSchemasChange = reconnectOnSchemasChange; + this.deviceActionFactory = deviceActionFactory; + this.node = node; + this.eventExecutor = eventExecutor; + this.nodeOptional = nodeOptional; this.schemaRegistry = schemaResourcesDTO.getSchemaRegistry(); this.schemaRepository = schemaResourcesDTO.getSchemaRepository(); this.schemaContextFactory = schemaResourcesDTO.getSchemaContextFactory(); this.salFacade = salFacade; this.stateSchemasResolver = schemaResourcesDTO.getStateSchemasResolver(); - this.processingExecutor = MoreExecutors.listeningDecorator(globalProcessingExecutor); + this.processingExecutor = requireNonNull(globalProcessingExecutor); this.notificationHandler = new NotificationHandler(salFacade, id); } @@ -140,7 +165,8 @@ public class NetconfDevice final NetconfDeviceRpc initRpc = getRpcForInitialization(listener, remoteSessionCapabilities.isNotificationsSupported()); final DeviceSourcesResolver task = - new DeviceSourcesResolver(remoteSessionCapabilities, id, stateSchemasResolver, initRpc); + new DeviceSourcesResolver(remoteSessionCapabilities, id, stateSchemasResolver, initRpc, + resolveBaseSchema(remoteSessionCapabilities.isNotificationsSupported()).getSchemaContext()); final ListenableFuture sourceResolverFuture = processingExecutor.submit(task); if (shouldListenOnSchemaChange(remoteSessionCapabilities)) { @@ -160,7 +186,7 @@ public class NetconfDevice @Override public void onFailure(final Throwable throwable) { - LOG.warn("{}: Unexpected error resolving device sources: {}", id, throwable); + LOG.warn("{}: Unexpected error resolving device sources", id, throwable); handleSalInitializationFailure(throwable, listener); } }; @@ -173,7 +199,7 @@ public class NetconfDevice // 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 CheckedFuture rpcResultListenableFuture = deviceRpc.invokeRpc( + final FluentFuture rpcResultListenableFuture = deviceRpc.invokeRpc( NetconfMessageTransformUtil.toPath(NetconfMessageTransformUtil.CREATE_SUBSCRIPTION_RPC_QNAME), NetconfMessageTransformUtil.CREATE_SUBSCRIPTION_RPC_CONTENT); @@ -185,7 +211,7 @@ public class NetconfDevice // Only disconnect is enough, // the reconnecting nature of the connector will take care of reconnecting listener.disconnect(); - return Optional.absent(); + return Optional.empty(); } return Optional.of(notification); } @@ -215,19 +241,22 @@ public class NetconfDevice private synchronized void handleSalInitializationSuccess(final SchemaContext result, final NetconfSessionPreferences remoteSessionCapabilities, - final DOMRpcService deviceRpc) { + final DOMRpcService deviceRpc, + final RemoteDeviceCommunicator listener) { //NetconfDevice.SchemaSetup can complete after NetconfDeviceCommunicator was closed. In that case do nothing, //since salFacade.onDeviceDisconnected was already called. if (connected) { final BaseSchema baseSchema = remoteSessionCapabilities.isNotificationsSupported() ? BaseSchema.BASE_NETCONF_CTX_WITH_NOTIFICATIONS : BaseSchema.BASE_NETCONF_CTX; - messageTransformer = new NetconfMessageTransformer(result, true, baseSchema); + this.messageTransformer = new NetconfMessageTransformer(result, true, baseSchema); - updateTransformer(messageTransformer); + updateTransformer(this.messageTransformer); // salFacade.onDeviceConnected has to be called before the notification handler is initialized - salFacade.onDeviceConnected(result, remoteSessionCapabilities, deviceRpc); - notificationHandler.onRemoteSchemaUp(messageTransformer); + this.salFacade.onDeviceConnected(result, remoteSessionCapabilities, deviceRpc, + this.deviceActionFactory == null ? null : this.deviceActionFactory.createDeviceAction( + this.messageTransformer, listener, result)); + this.notificationHandler.onRemoteSchemaUp(this.messageTransformer); LOG.info("{}: Netconf connector initialized successfully", id); } else { @@ -273,10 +302,8 @@ public class NetconfDevice notificationHandler.onRemoteSchemaDown(); salFacade.onDeviceDisconnected(); - for (final SchemaSourceRegistration sourceRegistration - : sourceRegistrations) { - sourceRegistration.close(); - } + sourceRegistrations.forEach(SchemaSourceRegistration::close); + sourceRegistrations.clear(); resetMessageTransformer(); } @@ -336,26 +363,29 @@ public class NetconfDevice private final NetconfSessionPreferences remoteSessionCapabilities; private final RemoteDeviceId id; private final NetconfDeviceSchemasResolver stateSchemasResolver; + private final SchemaContext schemaContext; DeviceSourcesResolver(final NetconfDeviceRpc deviceRpc, final NetconfSessionPreferences remoteSessionCapabilities, - final RemoteDeviceId id, final NetconfDeviceSchemasResolver stateSchemasResolver) { + final RemoteDeviceId id, final NetconfDeviceSchemasResolver stateSchemasResolver, + final SchemaContext schemaContext) { this.deviceRpc = deviceRpc; this.remoteSessionCapabilities = remoteSessionCapabilities; this.id = id; this.stateSchemasResolver = stateSchemasResolver; + this.schemaContext = schemaContext; } DeviceSourcesResolver(final NetconfSessionPreferences remoteSessionCapabilities, final RemoteDeviceId id, final NetconfDeviceSchemasResolver stateSchemasResolver, - final NetconfDeviceRpc rpcForMonitoring) { - this(rpcForMonitoring, remoteSessionCapabilities, id, stateSchemasResolver); + final NetconfDeviceRpc rpcForMonitoring, final SchemaContext schemaCtx) { + this(rpcForMonitoring, remoteSessionCapabilities, id, stateSchemasResolver, schemaCtx); } @Override - public DeviceSources call() throws Exception { + public DeviceSources call() { final NetconfDeviceSchemas availableSchemas = - stateSchemasResolver.resolve(deviceRpc, remoteSessionCapabilities, id); + stateSchemasResolver.resolve(deviceRpc, remoteSessionCapabilities, id, schemaContext); LOG.debug("{}: Schemas exposed by ietf-netconf-monitoring: {}", id, availableSchemas.getAvailableYangSchemasQNames()); @@ -478,6 +508,8 @@ public class NetconfDevice /** * Build schema context, in case of success or final failure notify device. + * + * @param requiredSources required sources */ @SuppressWarnings("checkstyle:IllegalCatch") private void setUpSchema(Collection requiredSources) { @@ -501,7 +533,8 @@ public class NetconfDevice remoteSessionCapabilities.getNonModuleBasedCapsOrigin().get(entry)).build()) .collect(Collectors.toList())); - handleSalInitializationSuccess(result, remoteSessionCapabilities, getDeviceSpecificRpc(result)); + handleSalInitializationSuccess(result, remoteSessionCapabilities, getDeviceSpecificRpc(result), + listener); return; } catch (final ExecutionException e) { // schemaBuilderFuture.checkedGet() throws only SchemaResolutionException @@ -527,10 +560,21 @@ public class NetconfDevice return; } } - // No more sources, fail - final IllegalStateException cause = new IllegalStateException(id + ": No more sources for schema context"); - handleSalInitializationFailure(cause, listener); - salFacade.onDeviceFailed(cause); + // No more sources, fail or try to reconnect + if (nodeOptional != null && nodeOptional.getIgnoreMissingSchemaSources().isAllowed()) { + 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(), TimeUnit.MILLISECONDS); + } else { + final IllegalStateException cause = + new IllegalStateException(id + ": No more sources for schema context"); + handleSalInitializationFailure(cause, listener); + salFacade.onDeviceFailed(cause); + } } private Collection handleMissingSchemaSourceException(