Add session-id to the operational datastore 90/91290/31
authortobias.pobocik <tobias.pobocik@pantheon.tech>
Mon, 13 Jul 2020 08:46:42 +0000 (10:46 +0200)
committerRobert Varga <nite@hq.sk>
Wed, 8 Feb 2023 09:49:27 +0000 (09:49 +0000)
Expose non-zero uint32 session-id provided by the device
in the HELLO-MESSAGE via operational datastore.

When device is disconnected null is used to indicate
a non-NETCONF entity session.

Session id can be used to retrieve session-specific info of the device.

JIRA: NETCONF-710
Change-Id: I1995e825cf32462232e164727a469a7b47d3acd0
Signed-off-by: Tibor Král <tibor.kral@pantheon.tech>
Signed-off-by: tobias.pobocik <tobias.pobocik@pantheon.tech>
Signed-off-by: Ivan Hrasko <ivan.hrasko@pantheon.tech>
Signed-off-by: Peter Suna <peter.suna@pantheon.tech>
apps/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/MasterSalFacade.java
apps/netconf-topology/src/main/java/org/opendaylight/netconf/topology/spi/NetconfDeviceTopologyAdapter.java
apps/netconf-topology/src/main/java/org/opendaylight/netconf/topology/spi/NetconfNodeUtils.java
apps/netconf-topology/src/main/java/org/opendaylight/netconf/topology/spi/NetconfTopologyDeviceSalFacade.java
apps/netconf-topology/src/test/java/org/opendaylight/netconf/topology/spi/NetconfDeficeTopologyAdapterIntegrationTest.java
apps/netconf-topology/src/test/java/org/opendaylight/netconf/topology/spi/NetconfDeviceTopologyAdapterTest.java
plugins/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/listener/NetconfSessionPreferences.java
plugins/sal-netconf-connector/src/main/yang/odl-netconf-device.yang
plugins/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/listener/NetconfDeviceCommunicatorTest.java

index 517a0d22e0e95f129cd95a89c34a264b1906fabc..81f49e10caa7160f9e23cb5ce3740d3a43aa02e0 100644 (file)
@@ -105,7 +105,7 @@ class MasterSalFacade implements RemoteDeviceHandler, AutoCloseable {
     @Override
     public void onDeviceDisconnected() {
         LOG.info("Device {} disconnected - unregistering master mount point", id);
-        datastoreAdapter.updateDeviceData(false, NetconfDeviceCapabilities.empty());
+        datastoreAdapter.updateDeviceData(false, NetconfDeviceCapabilities.empty(), null);
         mount.onDeviceDisconnected();
     }
 
@@ -173,6 +173,7 @@ class MasterSalFacade implements RemoteDeviceHandler, AutoCloseable {
     private void updateDeviceData() {
         final String masterAddress = Cluster.get(actorSystem).selfAddress().toString();
         LOG.debug("{}: updateDeviceData with master address {}", id, masterAddress);
-        datastoreAdapter.updateClusteredDeviceData(true, masterAddress, currentSchema.capabilities());
+        datastoreAdapter.updateClusteredDeviceData(true, masterAddress, currentSchema.capabilities(),
+                netconfSessionPreferences.sessionId());
     }
 }
index ad3ba42a02d6d1813baf58783501a068a36a6ddc..b5e2714d4c25d687fda2a1fa0c765e7a83b6b25e 100644 (file)
@@ -14,7 +14,6 @@ import com.google.common.util.concurrent.FutureCallback;
 import com.google.common.util.concurrent.MoreExecutors;
 import com.google.common.util.concurrent.SettableFuture;
 import java.util.concurrent.ExecutionException;
-import java.util.stream.Collectors;
 import org.eclipse.jdt.annotation.NonNull;
 import org.opendaylight.mdsal.binding.api.DataBroker;
 import org.opendaylight.mdsal.binding.api.Transaction;
@@ -44,6 +43,7 @@ import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
 import org.opendaylight.yangtools.yang.common.Empty;
 import org.opendaylight.yangtools.yang.common.Uint16;
+import org.opendaylight.yangtools.yang.common.Uint32;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -88,14 +88,15 @@ public final class NetconfDeviceTopologyAdapter implements TransactionChainListe
         return nodePath().augmentation(NetconfNode.class);
     }
 
-    public void updateDeviceData(final boolean up, final NetconfDeviceCapabilities capabilities) {
+    public void updateDeviceData(final boolean up, final NetconfDeviceCapabilities capabilities,
+            final Uint32 sessionId) {
         final WriteTransaction writeTx = txChain.newWriteOnlyTransaction();
         LOG.trace("{}: Update device state transaction {} merging operational data started.",
                 id, writeTx.getIdentifier());
 
         // FIXME: this needs to be tied together with node's operational existence
         writeTx.mergeParentStructurePut(LogicalDatastoreType.OPERATIONAL, netconfNodePath(),
-            newNetconfNodeBuilder(up, capabilities).build());
+            newNetconfNodeBuilder(up, capabilities, sessionId).build());
         LOG.trace("{}: Update device state transaction {} merging operational data ended.",
                 id, writeTx.getIdentifier());
 
@@ -103,12 +104,12 @@ public final class NetconfDeviceTopologyAdapter implements TransactionChainListe
     }
 
     public void updateClusteredDeviceData(final boolean up, final String masterAddress,
-                                          final NetconfDeviceCapabilities capabilities) {
+            final NetconfDeviceCapabilities capabilities, final Uint32 sessionId) {
         final WriteTransaction writeTx = txChain.newWriteOnlyTransaction();
         LOG.trace("{}: Update device state transaction {} merging operational data started.",
                 id, writeTx.getIdentifier());
         writeTx.mergeParentStructurePut(LogicalDatastoreType.OPERATIONAL, netconfNodePath(),
-            newNetconfNodeBuilder(up, capabilities)
+            newNetconfNodeBuilder(up, capabilities, sessionId)
                 .setClusteredConnectionStatus(new ClusteredConnectionStatusBuilder()
                     .setNetconfMasterNode(masterAddress)
                     .build())
@@ -156,7 +157,8 @@ public final class NetconfDeviceTopologyAdapter implements TransactionChainListe
         commitTransaction(writeTx, "update-failed-device");
     }
 
-    private NetconfNodeBuilder newNetconfNodeBuilder(final boolean up, final NetconfDeviceCapabilities capabilities) {
+    private NetconfNodeBuilder newNetconfNodeBuilder(final boolean up, final NetconfDeviceCapabilities capabilities,
+            final Uint32 sessionId) {
         return new NetconfNodeBuilder()
             .setHost(id.host())
             .setPort(new PortNumber(Uint16.valueOf(id.address().getPort())))
@@ -174,8 +176,9 @@ public final class NetconfDeviceTopologyAdapter implements TransactionChainListe
                         .setCapability(unresolved.getKey().toString())
                         .setFailureReason(unresolved.getValue())
                         .build())
-                    .collect(Collectors.toUnmodifiableList()))
-                .build());
+                    .toList())
+                .build())
+            .setSessionId(sessionId);
     }
 
     private void commitTransaction(final WriteTransaction transaction, final String txType) {
index ce6b1132c0997d54d920d0bdceb6e905fb8fb7fa..2326567b31042b76f400e8f7fc912242e0282475 100644 (file)
@@ -103,7 +103,7 @@ public final class NetconfNodeUtils {
 
         //non-module capabilities should not exist in yang module capabilities
         final var sessionPreferences = NetconfSessionPreferences.fromStrings(capabilities,
-            CapabilityOrigin.DeviceAdvertised);
+            CapabilityOrigin.DeviceAdvertised, node.getSessionId());
         final var nonModulePrefs = sessionPreferences.nonModuleCaps();
         if (!nonModulePrefs.isEmpty()) {
             throw new IllegalArgumentException("List yang-module-capabilities/capability should contain only module "
@@ -121,8 +121,8 @@ public final class NetconfNodeUtils {
         // FIXME: UserPreferences is constructor parameter of NetconfDeviceCommunicator and NetconfSessionPreferences
         // are created in NetconfDeviceCommunicator#onSessionUp from session. What are we doing here?
         // IMO we should rework UserPreferences and NetconfSessionPreferences and this method.
-        return new UserPreferences(NetconfSessionPreferences.fromStrings(capabilities, CapabilityOrigin.UserDefined),
-            overrideYangModuleCaps, overrideNonModuleCaps);
+        return new UserPreferences(NetconfSessionPreferences.fromStrings(capabilities, CapabilityOrigin.UserDefined,
+                node.getSessionId()), overrideYangModuleCaps, overrideNonModuleCaps);
     }
 
     @Deprecated(forRemoval = true)
index 5c7f11870382cc1bca5233bedfaee5330ce47619..c258b629a6894ef4047b45a4705e11a3adcc5a98 100644 (file)
@@ -32,13 +32,12 @@ public class NetconfTopologyDeviceSalFacade extends NetconfDeviceSalFacade {
     public synchronized void onDeviceConnected(final NetconfDeviceSchema deviceSchema,
             final NetconfSessionPreferences sessionPreferences, final RemoteDeviceServices services) {
         super.onDeviceConnected(deviceSchema, sessionPreferences, services);
-        datastoreAdapter.updateDeviceData(true, deviceSchema.capabilities());
-
+        datastoreAdapter.updateDeviceData(true, deviceSchema.capabilities(), sessionPreferences.sessionId());
     }
 
     @Override
     public synchronized void onDeviceDisconnected() {
-        datastoreAdapter.updateDeviceData(false, NetconfDeviceCapabilities.empty());
+        datastoreAdapter.updateDeviceData(false, NetconfDeviceCapabilities.empty(), null);
         super.onDeviceDisconnected();
     }
 
index d3677ceaddd1134dd1da6cff652a330115ac54e9..a1a4fb0c5980815bb8cb3f3d757752f7d949ba63 100644 (file)
@@ -38,6 +38,7 @@ import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey;
 import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
 import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.Uint32;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
@@ -114,7 +115,7 @@ public class NetconfDeficeTopologyAdapterIntegrationTest {
         wtx.put(LogicalDatastoreType.OPERATIONAL, pathToAugmentedLeaf, augmentNode);
         wtx.commit().get(5, TimeUnit.SECONDS);
 
-        adapter.updateDeviceData(true, NetconfDeviceCapabilities.empty());
+        adapter.updateDeviceData(true, NetconfDeviceCapabilities.empty(), Uint32.ONE);
 
         assertEquals(Optional.of(dataTestId), domDataBroker.newReadOnlyTransaction()
             .read(LogicalDatastoreType.OPERATIONAL, pathToAugmentedLeaf)
index c86bb3c8e62b26a935eb2766b2b5a746133d9f33..8fc65fa2d5f7203d3dba412aa99c9d28a38582b9 100644 (file)
@@ -36,6 +36,7 @@ import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.Uint32;
 
 @RunWith(MockitoJUnitRunner.StrictStubs.class)
 public class NetconfDeviceTopologyAdapterTest {
@@ -85,7 +86,7 @@ public class NetconfDeviceTopologyAdapterTest {
 
     @Test
     public void testDeviceUpdate() throws Exception {
-        adapter.updateDeviceData(true, NetconfDeviceCapabilities.empty());
+        adapter.updateDeviceData(true, NetconfDeviceCapabilities.empty(), Uint32.ONE);
 
         verify(mockChain, times(2)).newWriteOnlyTransaction();
         verify(mockTx, times(1)).put(any(LogicalDatastoreType.class), any(InstanceIdentifier.class), any(Node.class));
index 32aa0a3a68e5344b6b4a50636f96a29f3ecf9be7..ff29a186867278a5cc39bf2f56a29ce3a0b750b7 100644 (file)
@@ -26,6 +26,7 @@ import org.opendaylight.netconf.client.NetconfClientSession;
 import org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev221225.connection.oper.available.capabilities.AvailableCapability.CapabilityOrigin;
 import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.Uint32;
 import org.opendaylight.yangtools.yang.common.XMLNamespace;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -33,7 +34,8 @@ import org.slf4j.LoggerFactory;
 // FIXME: propagate to API with immutable semantics
 public record NetconfSessionPreferences(
         @NonNull ImmutableMap<String, CapabilityOrigin> nonModuleCaps,
-        @NonNull ImmutableMap<QName, CapabilityOrigin> moduleBasedCaps) {
+        @NonNull ImmutableMap<QName, CapabilityOrigin> moduleBasedCaps,
+        @Nullable Uint32 sessionId) {
     private static final Logger LOG = LoggerFactory.getLogger(NetconfSessionPreferences.class);
     private static final ParameterMatcher MODULE_PARAM = new ParameterMatcher("module=");
     private static final ParameterMatcher REVISION_PARAM = new ParameterMatcher("revision=");
@@ -46,17 +48,17 @@ public record NetconfSessionPreferences(
     }
 
     public static @NonNull NetconfSessionPreferences fromNetconfSession(final NetconfClientSession session) {
-        return fromStrings(session.getServerCapabilities());
+        return fromStrings(session.getServerCapabilities(), CapabilityOrigin.DeviceAdvertised,
+            Uint32.valueOf(session.getSessionId()));
     }
 
     @VisibleForTesting
     public static @NonNull NetconfSessionPreferences fromStrings(final Collection<String> capabilities) {
-        // we do not know origin of capabilities from only Strings, so we set it to default value
-        return fromStrings(capabilities, CapabilityOrigin.DeviceAdvertised);
+        return fromStrings(capabilities, CapabilityOrigin.DeviceAdvertised, null);
     }
 
     public static @NonNull NetconfSessionPreferences fromStrings(final Collection<String> capabilities,
-            final CapabilityOrigin capabilityOrigin) {
+            final CapabilityOrigin capabilityOrigin, final Uint32 sessionId) {
         final var moduleBasedCaps = new HashMap<QName, CapabilityOrigin>();
         final var nonModuleCaps = new HashMap<String, CapabilityOrigin>();
 
@@ -104,7 +106,8 @@ public record NetconfSessionPreferences(
                     cachedQName(namespace, moduleName), capabilityOrigin);
         }
 
-        return new NetconfSessionPreferences(ImmutableMap.copyOf(nonModuleCaps), ImmutableMap.copyOf(moduleBasedCaps));
+        return new NetconfSessionPreferences(ImmutableMap.copyOf(nonModuleCaps), ImmutableMap.copyOf(moduleBasedCaps),
+                sessionId);
     }
 
     public @Nullable CapabilityOrigin capabilityOrigin(final QName capability) {
@@ -180,7 +183,8 @@ public record NetconfSessionPreferences(
                 + netconfSessionModuleCapabilities.moduleBasedCaps.size());
         mergedCaps.putAll(moduleBasedCaps);
         mergedCaps.putAll(netconfSessionModuleCapabilities.moduleBasedCaps);
-        return new NetconfSessionPreferences(nonModuleCaps, ImmutableMap.copyOf(mergedCaps));
+        return new NetconfSessionPreferences(nonModuleCaps, ImmutableMap.copyOf(mergedCaps),
+                netconfSessionModuleCapabilities.sessionId());
     }
 
     /**
@@ -190,11 +194,12 @@ public record NetconfSessionPreferences(
      * @return new instance of preferences with replaced module-based capabilities
      */
     public NetconfSessionPreferences replaceModuleCaps(final NetconfSessionPreferences netconfSessionPreferences) {
-        return new NetconfSessionPreferences(nonModuleCaps, netconfSessionPreferences.moduleBasedCaps);
+        return new NetconfSessionPreferences(nonModuleCaps, netconfSessionPreferences.moduleBasedCaps,
+                netconfSessionPreferences.sessionId());
     }
 
     public NetconfSessionPreferences replaceModuleCaps(final Map<QName, CapabilityOrigin> newModuleBasedCaps) {
-        return new NetconfSessionPreferences(nonModuleCaps, ImmutableMap.copyOf(newModuleBasedCaps));
+        return new NetconfSessionPreferences(nonModuleCaps, ImmutableMap.copyOf(newModuleBasedCaps), sessionId());
     }
 
     /**
@@ -209,7 +214,8 @@ public record NetconfSessionPreferences(
                 nonModuleCaps.size() + netconfSessionNonModuleCapabilities.nonModuleCaps.size());
         mergedCaps.putAll(nonModuleCaps);
         mergedCaps.putAll(netconfSessionNonModuleCapabilities.nonModuleCaps);
-        return new NetconfSessionPreferences(ImmutableMap.copyOf(mergedCaps), moduleBasedCaps);
+        return new NetconfSessionPreferences(ImmutableMap.copyOf(mergedCaps), moduleBasedCaps,
+                netconfSessionNonModuleCapabilities.sessionId());
     }
 
     /**
@@ -219,7 +225,8 @@ public record NetconfSessionPreferences(
      * @return new instance of preferences with replaced non-module based capabilities
      */
     public NetconfSessionPreferences replaceNonModuleCaps(final NetconfSessionPreferences netconfSessionPreferences) {
-        return new NetconfSessionPreferences(netconfSessionPreferences.nonModuleCaps, moduleBasedCaps);
+        return new NetconfSessionPreferences(netconfSessionPreferences.nonModuleCaps, moduleBasedCaps,
+                netconfSessionPreferences.sessionId());
     }
 
     private static QName cachedQName(final String namespace, final String revision, final String moduleName) {
index 4c676548f794e23c62d132b11b28aa844683c8f9..857dcaf7cedc5cd2eb031f878a4f6bf2ee88d144 100644 (file)
@@ -219,6 +219,13 @@ module odl-netconf-device {
   }
 
   grouping connection-oper {
+    leaf session-id {
+      config false;
+      type uint32 {
+        range "1..max";
+      }
+    }
+
     leaf connection-status {
       config false;
       type enumeration {
index 20f231c100366293653709fb2d7c741b09af2750..d60a188aeca04b7d31b9ae86ced236c4d1843d3a 100644 (file)
@@ -27,6 +27,7 @@ import static org.opendaylight.netconf.api.xml.XmlNetconfConstants.URN_IETF_PARA
 import com.google.common.base.CharMatcher;
 import com.google.common.base.Strings;
 import com.google.common.util.concurrent.ListenableFuture;
+import io.netty.channel.Channel;
 import io.netty.channel.ChannelFuture;
 import io.netty.channel.EventLoopGroup;
 import io.netty.channel.nio.NioEventLoopGroup;
@@ -38,7 +39,6 @@ import io.netty.util.concurrent.GlobalEventExecutor;
 import java.io.ByteArrayInputStream;
 import java.net.InetSocketAddress;
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.Set;
 import java.util.UUID;
 import java.util.concurrent.TimeUnit;
@@ -55,6 +55,7 @@ import org.opendaylight.netconf.api.NetconfTerminationReason;
 import org.opendaylight.netconf.api.xml.XmlNetconfConstants;
 import org.opendaylight.netconf.client.NetconfClientDispatcherImpl;
 import org.opendaylight.netconf.client.NetconfClientSession;
+import org.opendaylight.netconf.client.NetconfClientSessionListener;
 import org.opendaylight.netconf.client.conf.NetconfClientConfiguration;
 import org.opendaylight.netconf.client.conf.NetconfReconnectingClientConfiguration;
 import org.opendaylight.netconf.client.conf.NetconfReconnectingClientConfigurationBuilder;
@@ -71,6 +72,7 @@ import org.opendaylight.yangtools.yang.common.ErrorType;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.common.RpcError;
 import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.common.Uint32;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.w3c.dom.Document;
@@ -80,26 +82,26 @@ import org.w3c.dom.Element;
 public class NetconfDeviceCommunicatorTest {
 
     private static final Logger LOG = LoggerFactory.getLogger(NetconfDeviceCommunicatorTest.class);
-
-    @Mock
-    NetconfClientSession mockSession;
+    private static final Uint32 SESSION_ID = Uint32.ONE;
 
     @Mock
     RemoteDevice<NetconfDeviceCommunicator> mockDevice;
 
-    NetconfDeviceCommunicator communicator;
+    private NetconfClientSession spySession;
+    private NetconfDeviceCommunicator communicator;
 
     @Before
     public void setUp() throws Exception {
         communicator = new NetconfDeviceCommunicator(
                 new RemoteDeviceId("test", InetSocketAddress.createUnresolved("localhost", 22)), mockDevice, 10);
+        spySession = spy(new NetconfClientSession(mock(NetconfClientSessionListener.class), mock(Channel.class),
+                SESSION_ID.toJava(), Set.of()));
     }
 
     void setupSession() {
-        doReturn(Collections.<String>emptySet()).when(mockSession).getServerCapabilities();
         doNothing().when(mockDevice).onRemoteSessionUp(any(NetconfSessionPreferences.class),
                 any(NetconfDeviceCommunicator.class));
-        communicator.onSessionUp(mockSession);
+        communicator.onSessionUp(spySession);
     }
 
     private ListenableFuture<RpcResult<NetconfMessage>> sendRequest() throws Exception {
@@ -118,7 +120,7 @@ public class NetconfDeviceCommunicatorTest {
         ChannelFuture mockChannelFuture = mock(ChannelFuture.class);
         doReturn(mockChannelFuture).when(mockChannelFuture)
                 .addListener(any(GenericFutureListener.class));
-        doReturn(mockChannelFuture).when(mockSession).sendMessage(same(message));
+        doReturn(mockChannelFuture).when(spySession).sendMessage(same(message));
 
         ListenableFuture<RpcResult<NetconfMessage>> resultFuture =
                 communicator.sendRequest(message, QName.create("", "mockRpc"));
@@ -135,14 +137,14 @@ public class NetconfDeviceCommunicatorTest {
             NetconfMessageTransformUtil.NETCONF_ROLLBACK_ON_ERROR_URI.toString(),
             NetconfMessageTransformUtil.IETF_NETCONF_MONITORING.getNamespace().toString(),
             testCapability);
-        doReturn(serverCapabilities).when(mockSession).getServerCapabilities();
+        doReturn(serverCapabilities).when(spySession).getServerCapabilities();
 
         final var netconfSessionPreferences = ArgumentCaptor.forClass(NetconfSessionPreferences.class);
         doNothing().when(mockDevice).onRemoteSessionUp(netconfSessionPreferences.capture(), eq(communicator));
 
-        communicator.onSessionUp(mockSession);
+        communicator.onSessionUp(spySession);
 
-        verify(mockSession).getServerCapabilities();
+        verify(spySession).getServerCapabilities();
         verify(mockDevice).onRemoteSessionUp(netconfSessionPreferences.capture(), eq(communicator));
 
         NetconfSessionPreferences actualCapabilites = netconfSessionPreferences.getValue();
@@ -153,6 +155,7 @@ public class NetconfDeviceCommunicatorTest {
                 actualCapabilites.moduleBasedCaps().keySet());
         assertTrue(actualCapabilites.isRollbackSupported());
         assertTrue(actualCapabilites.isMonitoringSupported());
+        assertEquals(SESSION_ID, actualCapabilites.sessionId());
     }
 
     @SuppressWarnings("unchecked")
@@ -165,7 +168,7 @@ public class NetconfDeviceCommunicatorTest {
 
         doNothing().when(mockDevice).onRemoteSessionDown();
 
-        communicator.onSessionDown(mockSession, new Exception("mock ex"));
+        communicator.onSessionDown(spySession, new Exception("mock ex"));
 
         verifyErrorRpcResult(resultFuture1.get(), ErrorType.TRANSPORT, ErrorTag.OPERATION_FAILED);
         verifyErrorRpcResult(resultFuture2.get(), ErrorType.TRANSPORT, ErrorTag.OPERATION_FAILED);
@@ -174,7 +177,7 @@ public class NetconfDeviceCommunicatorTest {
 
         reset(mockDevice);
 
-        communicator.onSessionDown(mockSession, new Exception("mock ex"));
+        communicator.onSessionDown(spySession, new Exception("mock ex"));
 
         verify(mockDevice, never()).onRemoteSessionDown();
     }
@@ -189,7 +192,7 @@ public class NetconfDeviceCommunicatorTest {
 
         String reasonText = "testing terminate";
         NetconfTerminationReason reason = new NetconfTerminationReason(reasonText);
-        communicator.onSessionTerminated(mockSession, reason);
+        communicator.onSessionTerminated(spySession, reason);
 
         RpcError rpcError = verifyErrorRpcResult(resultFuture.get(), ErrorType.TRANSPORT, ErrorTag.OPERATION_FAILED);
         assertEquals("RpcError message", reasonText, rpcError.getMessage());
@@ -216,11 +219,11 @@ public class NetconfDeviceCommunicatorTest {
 
         ChannelFuture mockChannelFuture = mock(ChannelFuture.class);
         doReturn(mockChannelFuture).when(mockChannelFuture).addListener(futureListener.capture());
-        doReturn(mockChannelFuture).when(mockSession).sendMessage(same(message));
+        doReturn(mockChannelFuture).when(spySession).sendMessage(same(message));
 
         ListenableFuture<RpcResult<NetconfMessage>> resultFuture = communicator.sendRequest(message, rpc);
 
-        verify(mockSession).sendMessage(same(message));
+        verify(spySession).sendMessage(same(message));
 
         assertNotNull("ListenableFuture is null", resultFuture);
 
@@ -278,7 +281,7 @@ public class NetconfDeviceCommunicatorTest {
 
         ChannelFuture mockChannelFuture = mock(ChannelFuture.class);
         doReturn(mockChannelFuture).when(mockChannelFuture).addListener(futureListener.capture());
-        doReturn(mockChannelFuture).when(mockSession).sendMessage(same(message));
+        doReturn(mockChannelFuture).when(spySession).sendMessage(same(message));
 
         ListenableFuture<RpcResult<NetconfMessage>> resultFuture = communicator.sendRequest(message, rpc);
 
@@ -315,7 +318,7 @@ public class NetconfDeviceCommunicatorTest {
         ListenableFuture<RpcResult<NetconfMessage>> resultFuture3 = sendRequest(messageID3, true);
 
         //response messages 1,2 are omitted
-        communicator.onMessage(mockSession, createSuccessResponseMessage(messageID3));
+        communicator.onMessage(spySession, createSuccessResponseMessage(messageID3));
 
         verifyResponseMessage(resultFuture3.get(), messageID3);
     }
@@ -330,8 +333,8 @@ public class NetconfDeviceCommunicatorTest {
         String messageID2 = UUID.randomUUID().toString();
         final ListenableFuture<RpcResult<NetconfMessage>> resultFuture2 = sendRequest(messageID2, true);
 
-        communicator.onMessage(mockSession, createSuccessResponseMessage(messageID1));
-        communicator.onMessage(mockSession, createSuccessResponseMessage(messageID2));
+        communicator.onMessage(spySession, createSuccessResponseMessage(messageID1));
+        communicator.onMessage(spySession, createSuccessResponseMessage(messageID2));
 
         verifyResponseMessage(resultFuture1.get(), messageID1);
         verifyResponseMessage(resultFuture2.get(), messageID2);
@@ -344,7 +347,7 @@ public class NetconfDeviceCommunicatorTest {
         String messageID = UUID.randomUUID().toString();
         ListenableFuture<RpcResult<NetconfMessage>> resultFuture = sendRequest(messageID, true);
 
-        communicator.onMessage(mockSession, createErrorResponseMessage(messageID));
+        communicator.onMessage(spySession, createErrorResponseMessage(messageID));
 
         RpcError rpcError = verifyErrorRpcResult(resultFuture.get(), ErrorType.RPC, ErrorTag.MISSING_ATTRIBUTE);
         assertEquals("RpcError message", "Missing attribute", rpcError.getMessage());
@@ -362,7 +365,7 @@ public class NetconfDeviceCommunicatorTest {
         String messageID = UUID.randomUUID().toString();
         ListenableFuture<RpcResult<NetconfMessage>> resultFuture = sendRequest(messageID, true);
 
-        communicator.onMessage(mockSession, createMultiErrorResponseMessage(messageID));
+        communicator.onMessage(spySession, createMultiErrorResponseMessage(messageID));
 
         RpcError rpcError = verifyErrorRpcResult(resultFuture.get(), ErrorType.PROTOCOL, ErrorTag.OPERATION_FAILED);
 
@@ -438,7 +441,7 @@ public class NetconfDeviceCommunicatorTest {
         String messageID = UUID.randomUUID().toString();
         ListenableFuture<RpcResult<NetconfMessage>> resultFuture = sendRequest(messageID, true);
 
-        communicator.onMessage(mockSession, createSuccessResponseMessage(UUID.randomUUID().toString()));
+        communicator.onMessage(spySession, createSuccessResponseMessage(UUID.randomUUID().toString()));
 
         RpcError rpcError = verifyErrorRpcResult(resultFuture.get(), ErrorType.PROTOCOL, ErrorTag.BAD_ATTRIBUTE);
         assertFalse("RpcError message non-empty", Strings.isNullOrEmpty(rpcError.getMessage()));
@@ -464,7 +467,7 @@ public class NetconfDeviceCommunicatorTest {
         ListenableFuture<RpcResult<NetconfMessage>> resultFuture = sendRequest(notWorkingMessageID, false);
         assertEquals("ListenableFuture is null", false, resultFuture instanceof UncancellableFuture);
 
-        communicator.onMessage(mockSession, createSuccessResponseMessage(messageID.get(0)));
+        communicator.onMessage(spySession, createSuccessResponseMessage(messageID.get(0)));
 
         resultFuture = sendRequest(messageID.get(0), false);
         assertNotNull("ListenableFuture is null", resultFuture);