From b2fba6c1aaa4ccc9719f8b6f513a8a7056d743ec Mon Sep 17 00:00:00 2001 From: Jakub Toth Date: Mon, 24 Jun 2019 05:17:16 -0400 Subject: [PATCH] Attempt netconf remount regardless of error-type Even if there's a 'no more sources' for schema context, go through normal remount cycle. It's added a new optional boolean to the netconf mount fields specifically for this, default to false/ current behavior. JIRA: NETCONF-611 Change-Id: If983ef0dded606fb75b5e41236c9d0f8328ab7c6 Signed-off-by: Jakub Toth --- .../topology/AbstractNetconfTopology.java | 14 ++++++-- .../sal/connect/api/RemoteDeviceHandler.java | 5 +++ .../sal/connect/netconf/NetconfDevice.java | 35 ++++++++++++++---- .../connect/netconf/NetconfDeviceBuilder.java | 24 ++++++++++++- .../netconf/sal/NetconfDeviceSalFacade.java | 9 +++++ .../sal/NetconfDeviceTopologyAdapter.java | 36 ++++++++++++------- .../src/main/yang/netconf-node-optional.yang | 27 +++++++++++++- 7 files changed, 127 insertions(+), 23 deletions(-) diff --git a/netconf/netconf-topology/src/main/java/org/opendaylight/netconf/topology/AbstractNetconfTopology.java b/netconf/netconf-topology/src/main/java/org/opendaylight/netconf/topology/AbstractNetconfTopology.java index 513693ff96..e559b6b68a 100644 --- a/netconf/netconf-topology/src/main/java/org/opendaylight/netconf/topology/AbstractNetconfTopology.java +++ b/netconf/netconf-topology/src/main/java/org/opendaylight/netconf/topology/AbstractNetconfTopology.java @@ -67,6 +67,7 @@ import org.opendaylight.netconf.topology.api.NetconfTopology; import org.opendaylight.netconf.topology.api.SchemaRepositoryProvider; 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.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; @@ -280,11 +281,12 @@ public abstract class AbstractNetconfTopology implements NetconfTopology { protected ListenableFuture setupConnection(final NodeId nodeId, final Node configNode) { final NetconfNode netconfNode = configNode.augmentation(NetconfNode.class); + final NetconfNodeAugmentedOptional nodeOptional = configNode.augmentation(NetconfNodeAugmentedOptional.class); requireNonNull(netconfNode.getHost()); requireNonNull(netconfNode.getPort()); - final NetconfConnectorDTO deviceCommunicatorDTO = createDeviceCommunicator(nodeId, netconfNode); + final NetconfConnectorDTO deviceCommunicatorDTO = createDeviceCommunicator(nodeId, netconfNode, nodeOptional); final NetconfDeviceCommunicator deviceCommunicator = deviceCommunicatorDTO.getCommunicator(); final NetconfClientSessionListener netconfClientSessionListener = deviceCommunicatorDTO.getSessionListener(); final NetconfReconnectingClientConfiguration clientConfig = @@ -311,6 +313,11 @@ public abstract class AbstractNetconfTopology implements NetconfTopology { } protected NetconfConnectorDTO createDeviceCommunicator(final NodeId nodeId, final NetconfNode node) { + return createDeviceCommunicator(nodeId, node, null); + } + + protected NetconfConnectorDTO createDeviceCommunicator(final NodeId nodeId, final NetconfNode node, + final NetconfNodeAugmentedOptional nodeOptional) { //setup default values since default value is not supported in mdsal final long defaultRequestTimeoutMillis = node.getDefaultRequestTimeoutMillis() == null ? DEFAULT_REQUEST_TIMEOUT_MILLIS : node.getDefaultRequestTimeoutMillis(); @@ -371,7 +378,10 @@ public abstract class AbstractNetconfTopology implements NetconfTopology { .setSchemaResourcesDTO(schemaResourcesDTO) .setGlobalProcessingExecutor(this.processingExecutor) .setId(remoteDeviceId) - .setSalFacade(salFacade); + .setSalFacade(salFacade) + .setNode(node) + .setEventExecutor(eventExecutor) + .setNodeOptional(nodeOptional); if (this.deviceActionFactory != null) { netconfDeviceBuilder.setDeviceActionFactory(this.deviceActionFactory); } diff --git a/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/api/RemoteDeviceHandler.java b/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/api/RemoteDeviceHandler.java index f762b889a9..9ab98f6428 100644 --- a/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/api/RemoteDeviceHandler.java +++ b/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/api/RemoteDeviceHandler.java @@ -10,6 +10,7 @@ package org.opendaylight.netconf.sal.connect.api; import org.opendaylight.mdsal.dom.api.DOMActionService; import org.opendaylight.mdsal.dom.api.DOMNotification; import org.opendaylight.mdsal.dom.api.DOMRpcService; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNode; import org.opendaylight.yangtools.yang.model.api.SchemaContext; public interface RemoteDeviceHandler extends AutoCloseable { @@ -39,6 +40,10 @@ public interface RemoteDeviceHandler extends AutoCloseable { // DO NOTHING } + default void onDeviceReconnected(final PREF netconfSessionPreferences, final NetconfNode node) { + // DO NOTHING + } + void onDeviceDisconnected(); void onDeviceFailed(Throwable throwable); 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 dfd72c60de..3746412774 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 @@ -19,6 +19,7 @@ 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; @@ -28,6 +29,7 @@ import java.util.Optional; import java.util.Set; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import org.checkerframework.checker.lock.qual.GuardedBy; import org.opendaylight.mdsal.dom.api.DOMNotification; @@ -52,6 +54,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; @@ -93,6 +97,9 @@ public class NetconfDevice private final NetconfDeviceSchemasResolver stateSchemasResolver; private final NotificationHandler notificationHandler; private final boolean reconnectOnSchemasChange; + private final NetconfNode node; + private final EventExecutor eventExecutor; + private final NetconfNodeAugmentedOptional nodeOptional; @GuardedBy("this") private boolean connected = false; @@ -120,16 +127,21 @@ public class NetconfDevice final RemoteDeviceHandler salFacade, final ListeningExecutorService globalProcessingExecutor, final boolean reconnectOnSchemasChange) { - this(schemaResourcesDTO, id, salFacade, globalProcessingExecutor, reconnectOnSchemasChange, null); + 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 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(); @@ -549,10 +561,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( diff --git a/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/NetconfDeviceBuilder.java b/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/NetconfDeviceBuilder.java index 50a1e9ade9..9d262568e9 100644 --- a/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/NetconfDeviceBuilder.java +++ b/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/NetconfDeviceBuilder.java @@ -10,10 +10,13 @@ package org.opendaylight.netconf.sal.connect.netconf; import com.google.common.base.Preconditions; import com.google.common.util.concurrent.ListeningExecutorService; +import io.netty.util.concurrent.EventExecutor; import org.opendaylight.netconf.sal.connect.api.DeviceActionFactory; import org.opendaylight.netconf.sal.connect.api.RemoteDeviceHandler; import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfSessionPreferences; import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId; +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; public class NetconfDeviceBuilder { @@ -23,6 +26,9 @@ public class NetconfDeviceBuilder { private RemoteDeviceHandler salFacade; private ListeningExecutorService globalProcessingExecutor; private DeviceActionFactory deviceActionFactory; + private NetconfNode node; + private EventExecutor eventExecutor; + private NetconfNodeAugmentedOptional nodeOptional; public NetconfDeviceBuilder() { } @@ -57,10 +63,26 @@ public class NetconfDeviceBuilder { return this; } + public NetconfDeviceBuilder setNode(final NetconfNode node) { + this.node = node; + return this; + } + + public NetconfDeviceBuilder setEventExecutor(final EventExecutor eventExecutor) { + this.eventExecutor = eventExecutor; + return this; + } + + public NetconfDeviceBuilder setNodeOptional(final NetconfNodeAugmentedOptional nodeOptional) { + this.nodeOptional = nodeOptional; + return this; + } + public NetconfDevice build() { validation(); return new NetconfDevice(this.schemaResourcesDTO, this.id, this.salFacade, this.globalProcessingExecutor, - this.reconnectOnSchemasChange, this.deviceActionFactory); + this.reconnectOnSchemasChange, this.deviceActionFactory, this.node, this.eventExecutor, + this.nodeOptional); } private void validation() { diff --git a/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/sal/NetconfDeviceSalFacade.java b/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/sal/NetconfDeviceSalFacade.java index 7f9c5cd65a..845378e5e9 100644 --- a/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/sal/NetconfDeviceSalFacade.java +++ b/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/sal/NetconfDeviceSalFacade.java @@ -28,6 +28,8 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.optional.rev19 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.optional.rev190614.netconf.node.fields.optional.topology.Node; import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.optional.rev190614.netconf.node.fields.optional.topology.NodeKey; import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.optional.rev190614.netconf.node.fields.optional.topology.node.DatastoreLock; +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.NetconfNodeConnectionStatus.ConnectionStatus; 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.yangtools.concepts.ListenerRegistration; @@ -84,6 +86,13 @@ public final class NetconfDeviceSalFacade implements AutoCloseable, RemoteDevice .updateDeviceData(true, netconfSessionPreferences.getNetconfDeviceCapabilities()); } + @Override + public synchronized void onDeviceReconnected(final NetconfSessionPreferences netconfSessionPreferences, + final NetconfNode node) { + this.salProvider.getTopologyDatastoreAdapter().updateDeviceData(ConnectionStatus.Connecting, + netconfSessionPreferences.getNetconfDeviceCapabilities(), LogicalDatastoreType.CONFIGURATION, node); + } + @Override public synchronized void onDeviceDisconnected() { salProvider.getTopologyDatastoreAdapter().updateDeviceData(false, new NetconfDeviceCapabilities()); diff --git a/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/sal/NetconfDeviceTopologyAdapter.java b/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/sal/NetconfDeviceTopologyAdapter.java index 1e65ec83df..202517fb14 100644 --- a/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/sal/NetconfDeviceTopologyAdapter.java +++ b/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/sal/NetconfDeviceTopologyAdapter.java @@ -96,20 +96,30 @@ public class NetconfDeviceTopologyAdapter implements AutoCloseable { commitTransaction(writeTx, "init"); } - public void updateDeviceData(final boolean up, final NetconfDeviceCapabilities capabilities) { - final NetconfNode data = buildDataForNetconfNode(up, capabilities); + public void updateDeviceData(final ConnectionStatus connectionStatus, + final NetconfDeviceCapabilities capabilities, final LogicalDatastoreType dsType, final NetconfNode node) { + NetconfNode data; + if (node != null && dsType == LogicalDatastoreType.CONFIGURATION) { + data = node; + } else { + data = buildDataForNetconfNode(connectionStatus, capabilities, dsType, node); + } final WriteTransaction writeTx = txChain.newWriteOnlyTransaction(); LOG.trace("{}: Update device state transaction {} merging operational data started.", id, writeTx.getIdentifier()); - writeTx.put(LogicalDatastoreType.OPERATIONAL, id.getTopologyBindingPath().augmentation(NetconfNode.class), - data, true); + writeTx.put(dsType, id.getTopologyBindingPath().augmentation(NetconfNode.class), data, true); LOG.trace("{}: Update device state transaction {} merging operational data ended.", id, writeTx.getIdentifier()); commitTransaction(writeTx, "update"); } + public void updateDeviceData(final boolean up, final NetconfDeviceCapabilities capabilities) { + updateDeviceData(up ? ConnectionStatus.Connected : ConnectionStatus.Connecting, capabilities, + LogicalDatastoreType.OPERATIONAL, null); + } + public void updateClusteredDeviceData(final boolean up, final String masterAddress, final NetconfDeviceCapabilities capabilities) { final NetconfNode data = buildDataForNetconfClusteredNode(up, masterAddress, capabilities); @@ -146,7 +156,8 @@ public class NetconfDeviceTopologyAdapter implements AutoCloseable { commitTransaction(writeTx, "update-failed-device"); } - private NetconfNode buildDataForNetconfNode(final boolean up, final NetconfDeviceCapabilities capabilities) { + private NetconfNode buildDataForNetconfNode(final ConnectionStatus connectionStatus, + final NetconfDeviceCapabilities capabilities, final LogicalDatastoreType dsType, final NetconfNode node) { List capabilityList = new ArrayList<>(); capabilityList.addAll(capabilities.getNonModuleBasedCapabilities()); capabilityList.addAll(capabilities.getResolvedCapabilities()); @@ -154,14 +165,13 @@ public class NetconfDeviceTopologyAdapter implements AutoCloseable { final AvailableCapabilitiesBuilder avCapabalitiesBuilder = new AvailableCapabilitiesBuilder(); avCapabalitiesBuilder.setAvailableCapability(capabilityList); - final NetconfNodeBuilder netconfNodeBuilder = new NetconfNodeBuilder() - .setHost(id.getHost()) - .setPort(new PortNumber(id.getAddress().getPort())) - .setConnectionStatus(up ? ConnectionStatus.Connected : ConnectionStatus.Connecting) - .setAvailableCapabilities(avCapabalitiesBuilder.build()) - .setUnavailableCapabilities(unavailableCapabilities(capabilities.getUnresolvedCapabilites())); - - return netconfNodeBuilder.build(); + return new NetconfNodeBuilder() + .setHost(id.getHost()) + .setPort(new PortNumber(id.getAddress().getPort())) + .setConnectionStatus(connectionStatus) + .setAvailableCapabilities(avCapabalitiesBuilder.build()) + .setUnavailableCapabilities(unavailableCapabilities(capabilities.getUnresolvedCapabilites())) + .build(); } private NetconfNode buildDataForNetconfClusteredNode(final boolean up, final String masterNodeAddress, diff --git a/netconf/sal-netconf-connector/src/main/yang/netconf-node-optional.yang b/netconf/sal-netconf-connector/src/main/yang/netconf-node-optional.yang index 0bcf0374ca..8e9d632a54 100644 --- a/netconf/sal-netconf-connector/src/main/yang/netconf-node-optional.yang +++ b/netconf/sal-netconf-connector/src/main/yang/netconf-node-optional.yang @@ -3,9 +3,28 @@ module netconf-node-optional { prefix "netnopt"; import network-topology { prefix nt; revision-date 2013-10-21; } + import yang-ext { prefix ext; revision-date "2013-07-09";} revision "2019-06-14" { - description "Initial revision of Node Locking model"; + description "Initial revision of Node Optional model"; + } + + grouping netconf-node-augmented-optional-fields { + container ignore-missing-schema-sources { + description "Allows mount point to reconnect on the 'missing schema sources' error. + WARNING - enabling the reconnection on the 'missing schema sources' error can lead + to unexpected errors at runtime."; + leaf allowed { + type boolean; + default false; + description "Allows reconnection of the mount point. Default false."; + } + leaf reconnect-time { + type uint32; + default 5000; + description "Time for reconnection - in units milliseconds. Default 5000 ms."; + } + } } container netconf-node-fields-optional { @@ -41,4 +60,10 @@ module netconf-node-optional { } } } + + augment "/nt:network-topology/nt:topology/nt:node/" { + when "../../nt:topology-types/topology-netconf"; + ext:augment-identifier "netconf-node-augmented-optional"; + uses netconf-node-augmented-optional-fields; + } } -- 2.36.6