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=a6865ba6dd07465b75c91fafb3dffe554033cf26;hb=01e6f6d0f907f710db3a1606ee0632cf8eb28476;hp=fc100b3db80a2ec232db0ad3e15ceb8fdb0f7e42;hpb=1e174be6002940e17aee31d15f8914273d30f25c;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 fc100b3db8..a6865ba6dd 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; @@ -114,15 +123,29 @@ public class NetconfDevice 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); } @@ -160,7 +183,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 +196,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 +208,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 +238,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 +299,8 @@ public class NetconfDevice notificationHandler.onRemoteSchemaDown(); salFacade.onDeviceDisconnected(); - for (final SchemaSourceRegistration sourceRegistration - : sourceRegistrations) { - sourceRegistration.close(); - } + sourceRegistrations.forEach(SchemaSourceRegistration::close); + sourceRegistrations.clear(); resetMessageTransformer(); } @@ -478,6 +502,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 +527,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 +554,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(