Merge "Bug 2806 - Immediate and infinite reconnect attempts during negotiation" into...
authorMaros Marsalek <mmarsale@cisco.com>
Thu, 11 Feb 2016 18:49:50 +0000 (18:49 +0000)
committerGerrit Code Review <gerrit@opendaylight.org>
Thu, 11 Feb 2016 18:49:50 +0000 (18:49 +0000)
opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/netconf/mdsal/connector/TransactionProvider.java
opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/netconf/impl/osgi/AggregatedNetconfOperationServiceFactoryTest.java [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/netconf/impl/osgi/NetconfMonitoringServiceImplTest.java [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/netconf/impl/osgi/NetconfOperationRouterImplTest.java [new file with mode: 0644]
opendaylight/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/NetconfDevice.java
opendaylight/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/NetconfStateSchemas.java
opendaylight/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/schema/mapping/NetconfMessageTransformer.java
opendaylight/netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/NetconfStateSchemasTest.java
opendaylight/netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/sal/tx/NetconfDeviceWriteOnlyTxTest.java
opendaylight/netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/schema/mapping/NetconfMessageTransformerTest.java

index 1eacbf364b739a7796a31204baa8ab1fd059f3ac..0bcd7370fc58f4f9dde85a9250b599004826fa58 100644 (file)
@@ -83,11 +83,14 @@ public class TransactionProvider implements AutoCloseable{
             future.checkedGet();
         } catch (TransactionCommitFailedException e) {
             LOG.debug("Transaction {} failed on", candidateTransaction, e);
-            throw new DocumentedException("Transaction commit failed on " + e.getMessage() + " " + netconfSessionIdForReporting,
+            final String cause = e.getCause() != null ? (" Cause: " + e.getCause().getMessage()) : "";
+            throw new DocumentedException("Transaction commit failed on " + e.getMessage() + " " + netconfSessionIdForReporting +
+                    cause,
                     ErrorType.application, ErrorTag.operation_failed, ErrorSeverity.error);
+        } finally {
+            allOpenReadWriteTransactions.remove(candidateTransaction);
+            candidateTransaction = null;
         }
-        allOpenReadWriteTransactions.remove(candidateTransaction);
-        candidateTransaction = null;
 
         return true;
     }
diff --git a/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/netconf/impl/osgi/AggregatedNetconfOperationServiceFactoryTest.java b/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/netconf/impl/osgi/AggregatedNetconfOperationServiceFactoryTest.java
new file mode 100644 (file)
index 0000000..bf078e2
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.netconf.impl.osgi;
+
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import com.google.common.collect.Sets;
+import java.util.HashSet;
+import java.util.Set;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.controller.config.util.capability.BasicCapability;
+import org.opendaylight.controller.config.util.capability.Capability;
+import org.opendaylight.netconf.api.monitoring.CapabilityListener;
+import org.opendaylight.netconf.mapping.api.NetconfOperationServiceFactory;
+
+
+public class AggregatedNetconfOperationServiceFactoryTest {
+
+    private Set<Capability> factory1Caps = new HashSet<>();
+    private Set<Capability> factory2Caps = new HashSet<>();
+
+    @Mock
+    private CapabilityListener listener1;
+    @Mock
+    private CapabilityListener listener2;
+    @Mock
+    private CapabilityListener listener3;
+    @Mock
+    private NetconfOperationServiceFactory factory1;
+    @Mock
+    private NetconfOperationServiceFactory factory2;
+    @Mock
+    private AutoCloseable autoCloseable1;
+    @Mock
+    private AutoCloseable autoCloseable2;
+    @Mock
+    private AutoCloseable autoCloseable3;
+
+    private AggregatedNetconfOperationServiceFactory aggregatedFactory;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        factory1Caps.add(new BasicCapability("AAA"));
+        factory1Caps.add(new BasicCapability("BBB"));
+
+        factory2Caps.add(new BasicCapability("CCC"));
+        factory2Caps.add(new BasicCapability("DDD"));
+
+        aggregatedFactory = new AggregatedNetconfOperationServiceFactory();
+
+        aggregatedFactory.registerCapabilityListener(listener1);
+        aggregatedFactory.registerCapabilityListener(listener2);
+
+        doReturn(autoCloseable1).when(factory1).registerCapabilityListener(listener1);
+        doReturn(autoCloseable2).when(factory1).registerCapabilityListener(listener2);
+        doReturn(factory1Caps).when(factory1).getCapabilities();
+
+        doReturn(autoCloseable1).when(factory2).registerCapabilityListener(listener1);
+        doReturn(autoCloseable2).when(factory2).registerCapabilityListener(listener2);
+        doReturn(factory2Caps).when(factory2).getCapabilities();
+
+        doNothing().when(autoCloseable1).close();
+        doNothing().when(autoCloseable2).close();
+
+        doReturn(autoCloseable3).when(factory1).registerCapabilityListener(listener3);
+        doReturn(autoCloseable3).when(factory2).registerCapabilityListener(listener3);
+    }
+
+    @Test
+    public void testOnAddAndOnRemove() throws Exception {
+        aggregatedFactory.onAddNetconfOperationServiceFactory(factory1);
+        aggregatedFactory.onAddNetconfOperationServiceFactory(factory2);
+
+        verify(factory1).registerCapabilityListener(listener1);
+        verify(factory2).registerCapabilityListener(listener1);
+        verify(factory1).registerCapabilityListener(listener2);
+        verify(factory2).registerCapabilityListener(listener2);
+
+        aggregatedFactory.onRemoveNetconfOperationServiceFactory(factory1);
+        aggregatedFactory.onRemoveNetconfOperationServiceFactory(factory2);
+
+        verify(autoCloseable1, times(2)).close();
+        verify(autoCloseable2, times(2)).close();
+    }
+
+    @Test
+    public void testGetCapabilities() throws Exception {
+        aggregatedFactory.onAddNetconfOperationServiceFactory(factory1);
+        aggregatedFactory.onAddNetconfOperationServiceFactory(factory2);
+        final Set<Capability> actual = aggregatedFactory.getCapabilities();
+        Set<Capability> expected = Sets.union(factory1Caps, factory2Caps);
+        Assert.assertEquals(expected, actual);
+    }
+
+    @Test
+    public void testRegisterCapabilityListener() throws Exception {
+        aggregatedFactory.onAddNetconfOperationServiceFactory(factory1);
+        aggregatedFactory.onAddNetconfOperationServiceFactory(factory2);
+        aggregatedFactory.registerCapabilityListener(listener3);
+
+        verify(factory1).registerCapabilityListener(listener3);
+        verify(factory2).registerCapabilityListener(listener3);
+    }
+
+    @Test
+    public void testClose() throws Exception {
+        aggregatedFactory.onAddNetconfOperationServiceFactory(factory1);
+        aggregatedFactory.onAddNetconfOperationServiceFactory(factory2);
+        aggregatedFactory.close();
+        verify(autoCloseable1, times(2)).close();
+        verify(autoCloseable2, times(2)).close();
+    }
+
+}
\ No newline at end of file
diff --git a/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/netconf/impl/osgi/NetconfMonitoringServiceImplTest.java b/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/netconf/impl/osgi/NetconfMonitoringServiceImplTest.java
new file mode 100644 (file)
index 0000000..bfda1e0
--- /dev/null
@@ -0,0 +1,195 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.netconf.impl.osgi;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doReturn;
+
+import com.google.common.base.Optional;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.controller.config.util.capability.BasicCapability;
+import org.opendaylight.controller.config.util.capability.Capability;
+import org.opendaylight.controller.config.util.capability.YangModuleCapability;
+import org.opendaylight.netconf.api.monitoring.NetconfManagementSession;
+import org.opendaylight.netconf.api.monitoring.NetconfMonitoringService;
+import org.opendaylight.netconf.mapping.api.NetconfOperationServiceFactory;
+import org.opendaylight.netconf.notifications.BaseNotificationPublisherRegistration;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Uri;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.NetconfState;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.Capabilities;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.CapabilitiesBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.Schemas;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.schemas.Schema;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.sessions.SessionBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.notifications.rev120206.NetconfCapabilityChange;
+import org.opendaylight.yangtools.yang.model.api.Module;
+
+public class NetconfMonitoringServiceImplTest {
+
+    private static final String TEST_MODULE_CONTENT = "content";
+    private static final String TEST_MODULE_REV = "1970-01-01";
+    private static final  Uri TEST_MODULE_NAMESPACE = new Uri("testModuleNamespace");
+    private static final String TEST_MODULE_NAME = "testModule";
+    private static final Date TEST_MODULE_DATE = new Date(0);
+
+    private final Set<Capability> CAPABILITIES = new HashSet<>();
+
+    private NetconfMonitoringServiceImpl monitoringService;
+
+    @Mock
+    private Module moduleMock;
+    @Mock
+    private NetconfOperationServiceFactory operationServiceFactoryMock;
+    @Mock
+    private NetconfManagementSession sessionMock;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        CAPABILITIES.add(new BasicCapability("urn:ietf:params:netconf:base:1.0"));
+        CAPABILITIES.add(new BasicCapability("urn:ietf:params:netconf:base:1.1"));
+        CAPABILITIES.add(new BasicCapability("urn:ietf:params:xml:ns:yang:ietf-inet-types?module=ietf-inet-types&amp;revision=2010-09-24"));
+        doReturn(new URI(TEST_MODULE_NAMESPACE.getValue())).when(moduleMock).getNamespace();
+        doReturn(TEST_MODULE_NAME).when(moduleMock).getName();
+        doReturn(TEST_MODULE_DATE).when(moduleMock).getRevision();
+        doReturn(TEST_MODULE_NAME).when(moduleMock).getName();
+
+        CAPABILITIES.add(new YangModuleCapability(moduleMock, TEST_MODULE_CONTENT));
+        doReturn(CAPABILITIES).when(operationServiceFactoryMock).getCapabilities();
+        doReturn(null).when(operationServiceFactoryMock).registerCapabilityListener(any(NetconfMonitoringServiceImpl.class));
+
+        monitoringService = new NetconfMonitoringServiceImpl(operationServiceFactoryMock);
+        monitoringService.onCapabilitiesChanged(CAPABILITIES, new HashSet<Capability>());
+
+        doReturn(new SessionBuilder().build()).when(sessionMock).toManagementSession();
+    }
+
+    @Test
+    public void testListeners() throws Exception {
+        final AtomicInteger stateChanged = new AtomicInteger(0);
+        NetconfMonitoringService.MonitoringListener listener = getMonitoringListener(stateChanged);
+        monitoringService.registerListener(listener);
+        Assert.assertEquals(1, stateChanged.get());
+        monitoringService.onSessionUp(sessionMock);
+        Assert.assertEquals(2, stateChanged.get());
+        HashSet<Capability> added = new HashSet<>();
+        added.add(new BasicCapability("toAdd"));
+        monitoringService.onCapabilitiesChanged(added, new HashSet<Capability>());
+        Assert.assertEquals(3, stateChanged.get());
+        monitoringService.onSessionDown(sessionMock);
+        Assert.assertEquals(4, stateChanged.get());
+    }
+
+    @Test
+    public void testGetSchemas() throws Exception {
+        Schemas schemas = monitoringService.getSchemas();
+        Schema schema = schemas.getSchema().get(0);
+        Assert.assertEquals(TEST_MODULE_NAMESPACE, schema.getNamespace());
+        Assert.assertEquals(TEST_MODULE_NAME, schema.getIdentifier());
+        Assert.assertEquals(TEST_MODULE_REV, schema.getVersion());
+
+    }
+
+    @Test
+    public void testGetSchemaForCapability() throws Exception {
+        String schema = monitoringService.getSchemaForCapability(TEST_MODULE_NAME, Optional.of(TEST_MODULE_REV));
+        Assert.assertEquals(TEST_MODULE_CONTENT, schema);
+
+    }
+
+    @Test
+    public void testGetCapabilities() throws Exception {
+        Capabilities actual = monitoringService.getCapabilities();
+        List<Uri> exp = new ArrayList<>();
+        for (Capability capability : CAPABILITIES) {
+            exp.add(new Uri(capability.getCapabilityUri()));
+        }
+        exp.add(0, new Uri("urn:ietf:params:netconf:capability:candidate:1.0"));
+        Capabilities expected = new CapabilitiesBuilder().setCapability(exp).build();
+        Assert.assertEquals(new HashSet<>(expected.getCapability()), new HashSet<>(actual.getCapability()));
+    }
+
+    @Test
+    public void testClose() throws Exception {
+        monitoringService.onSessionUp(sessionMock);
+        Assert.assertFalse(monitoringService.getSessions().getSession().isEmpty());
+        Assert.assertFalse(monitoringService.getCapabilities().getCapability().isEmpty());
+        monitoringService.close();
+        Assert.assertTrue(monitoringService.getSessions().getSession().isEmpty());
+        Assert.assertTrue(monitoringService.getCapabilities().getCapability().isEmpty());
+    }
+
+    @Test
+    public void testOnCapabilitiesChanged() throws Exception {
+        final List<String> actualCapabilities = new ArrayList<>();
+        monitoringService.registerListener(new NetconfMonitoringService.MonitoringListener() {
+            @Override
+            public void onStateChanged(NetconfState state) {
+                List<Uri> capability = state.getCapabilities().getCapability();
+                for (Uri uri : capability) {
+                    actualCapabilities.add(uri.getValue());
+                }
+            }
+        });
+        HashSet<Capability> testCaps = new HashSet<>();
+        String capUri = "test";
+        testCaps.add(new BasicCapability(capUri));
+        monitoringService.onCapabilitiesChanged(testCaps, new HashSet<Capability>());
+        Assert.assertTrue(actualCapabilities.contains(capUri));
+        actualCapabilities.clear();
+        monitoringService.onCapabilitiesChanged(new HashSet<Capability>(), testCaps);
+        Assert.assertFalse(actualCapabilities.contains(capUri));
+    }
+
+    @Test
+    public void testonCapabilitiesChanged() throws Exception {
+        final String toAdd = "toAdd";
+        final String toRemove = "toRemove";
+        monitoringService.setNotificationPublisher(new BaseNotificationPublisherRegistration() {
+            @Override
+            public void onCapabilityChanged(NetconfCapabilityChange capabilityChange) {
+                Assert.assertEquals(1, capabilityChange.getAddedCapability().size());
+
+                Assert.assertEquals(toAdd, capabilityChange.getAddedCapability().get(0).getValue());
+                Assert.assertEquals(1, capabilityChange.getDeletedCapability().size());
+                Assert.assertEquals(toRemove, capabilityChange.getDeletedCapability().get(0).getValue());
+            }
+
+            @Override
+            public void close() {
+
+            }
+        });
+        Set<Capability> removed = new HashSet<>();
+        removed.add(new BasicCapability(toRemove));
+        Set<Capability> added = new HashSet<>();
+        added.add(new BasicCapability(toAdd));
+        monitoringService.onCapabilitiesChanged(added, removed);
+    }
+
+    private NetconfMonitoringService.MonitoringListener getMonitoringListener(final AtomicInteger stateChanged) {
+        return new NetconfMonitoringService.MonitoringListener() {
+            @Override
+            public void onStateChanged(NetconfState state) {
+                stateChanged.incrementAndGet();
+            }
+        };
+    }
+
+}
\ No newline at end of file
diff --git a/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/netconf/impl/osgi/NetconfOperationRouterImplTest.java b/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/netconf/impl/osgi/NetconfOperationRouterImplTest.java
new file mode 100644 (file)
index 0000000..027d175
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.netconf.impl.osgi;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.controller.config.util.xml.DocumentedException;
+import org.opendaylight.controller.config.util.xml.XmlUtil;
+import org.opendaylight.netconf.mapping.api.HandlingPriority;
+import org.opendaylight.netconf.mapping.api.NetconfOperation;
+import org.opendaylight.netconf.mapping.api.NetconfOperationChainedExecution;
+import org.opendaylight.netconf.mapping.api.NetconfOperationService;
+import org.w3c.dom.Document;
+import org.xml.sax.SAXException;
+
+public class NetconfOperationRouterImplTest {
+
+    private static final String TEST_RPC = "<rpc message-id=\"101\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\"><test/></rpc>\n";
+    private static final String MAX_PRIORITY_REPLY = "<high/>";
+    private static final String DEFAULT_PRIORITY_REPLY = "<default/>";
+
+    private static Document TEST_RPC_DOC;
+
+    @Mock
+    private NetconfOperationService operationService;
+    @Mock
+    private NetconfOperationService operationService2;
+    @Mock
+    private NetconfOperation maxPrioMock;
+    @Mock
+    private NetconfOperation defaultPrioMock;
+
+    private NetconfOperationRouterImpl operationRouter;
+    private NetconfOperationRouterImpl emptyOperationRouter;
+
+    @BeforeClass
+    public static void suiteSetUp() throws IOException, SAXException {
+        TEST_RPC_DOC = XmlUtil.readXmlToDocument(TEST_RPC);
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
+        doReturn(HandlingPriority.HANDLE_WITH_MAX_PRIORITY).when(maxPrioMock).canHandle(any(Document.class));
+        doReturn(XmlUtil.readXmlToDocument(MAX_PRIORITY_REPLY)).when(maxPrioMock).handle(any(Document.class), any(NetconfOperationChainedExecution.class));
+
+        doReturn(HandlingPriority.HANDLE_WITH_DEFAULT_PRIORITY).when(defaultPrioMock).canHandle(any(Document.class));
+        doReturn(XmlUtil.readXmlToDocument(DEFAULT_PRIORITY_REPLY)).when(defaultPrioMock).handle(any(Document.class), any(NetconfOperationChainedExecution.class));
+
+        Set<NetconfOperation> operations = new HashSet<>();
+        operations.add(maxPrioMock);
+        operations.add(defaultPrioMock);
+        doReturn(operations).when(operationService).getNetconfOperations();
+        doNothing().when(operationService).close();
+
+        operationRouter = new NetconfOperationRouterImpl(operationService, null, "session-1");
+        doReturn(Collections.EMPTY_SET).when(operationService2).getNetconfOperations();
+        emptyOperationRouter = new NetconfOperationRouterImpl(operationService2, null, "session-1");
+    }
+
+    @Test
+    public void testOnNetconfMessage() throws Exception {
+        ArgumentCaptor<NetconfOperationChainedExecution> highPriorityChainEx = ArgumentCaptor.forClass(NetconfOperationChainedExecution.class);
+        ArgumentCaptor<NetconfOperationChainedExecution> defaultPriorityChainEx = ArgumentCaptor.forClass(NetconfOperationChainedExecution.class);
+
+        final Document document = operationRouter.onNetconfMessage(TEST_RPC_DOC, null);
+
+        //max priority message is first in chain
+        verify(maxPrioMock).handle(any(Document.class), highPriorityChainEx.capture());
+        final NetconfOperationChainedExecution chainedExecution = highPriorityChainEx.getValue();
+        Assert.assertFalse(chainedExecution.isExecutionTermination());
+
+        //execute next in chain
+        final Document execute = chainedExecution.execute(XmlUtil.newDocument());
+        Assert.assertEquals(DEFAULT_PRIORITY_REPLY, XmlUtil.toString(execute).trim());
+
+        //default priority message is second and last
+        verify(defaultPrioMock).handle(any(Document.class), defaultPriorityChainEx.capture());
+        Assert.assertTrue(defaultPriorityChainEx.getValue().isExecutionTermination());
+
+        Assert.assertEquals(MAX_PRIORITY_REPLY, XmlUtil.toString(document).trim());
+    }
+
+    @Test
+    public void testOnNetconfMessageFail() throws Exception {
+        try{
+            emptyOperationRouter.onNetconfMessage(TEST_RPC_DOC, null);
+            Assert.fail("Exception expected");
+        } catch (DocumentedException e) {
+            Assert.assertEquals(e.getErrorTag(), DocumentedException.ErrorTag.operation_not_supported);
+        }
+    }
+
+    @Test
+    public void testClose() throws Exception {
+        operationRouter.close();
+        verify(operationService).close();
+    }
+
+}
\ No newline at end of file
index fd3399fb9e991136db5c73bfa82b3374efc4e87c..bb63554d5491d356cc13a5072a8563b1fb75a2ea 100644 (file)
@@ -43,10 +43,8 @@ import org.opendaylight.netconf.sal.connect.netconf.schema.NetconfRemoteSchemaYa
 import org.opendaylight.netconf.sal.connect.netconf.schema.mapping.NetconfMessageTransformer;
 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.monitoring.extension.rev131210.$YangModuleInfoImpl;
 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.topology.rev150114.netconf.node.connection.status.unavailable.capabilities.UnavailableCapability;
-import org.opendaylight.yangtools.sal.binding.generator.impl.ModuleInfoBackedContext;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.opendaylight.yangtools.yang.model.repo.api.MissingSchemaSourceException;
@@ -68,26 +66,6 @@ public class NetconfDevice implements RemoteDevice<NetconfSessionPreferences, Ne
 
     private static final Logger LOG = LoggerFactory.getLogger(NetconfDevice.class);
 
-    /**
-     * Initial schema context contains schemas for netconf monitoring and netconf notifications
-     */
-    public static final SchemaContext INIT_SCHEMA_CTX;
-
-    static {
-        try {
-            final ModuleInfoBackedContext moduleInfoBackedContext = ModuleInfoBackedContext.create();
-            moduleInfoBackedContext.addModuleInfos(
-                    Lists.newArrayList(
-                            $YangModuleInfoImpl.getInstance(),
-                            org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.notification._1._0.rev080714.$YangModuleInfoImpl.getInstance(),
-                            org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.base._1._0.rev110601.$YangModuleInfoImpl.getInstance()));
-            INIT_SCHEMA_CTX = moduleInfoBackedContext.tryToCreateSchemaContext().get();
-        } catch (final RuntimeException e) {
-            LOG.error("Unable to prepare schema context for netconf initialization", e);
-            throw new ExceptionInInitializerError(e);
-        }
-    }
-
     public static final Function<QName, SourceIdentifier> QNAME_TO_SOURCE_ID_FUNCTION = new Function<QName, SourceIdentifier>() {
         @Override
         public SourceIdentifier apply(final QName input) {
@@ -117,8 +95,12 @@ public class NetconfDevice implements RemoteDevice<NetconfSessionPreferences, Ne
     /**
      * Create rpc implementation capable of handling RPC for monitoring and notifications even before the schemas of remote device are downloaded
      */
-    static NetconfDeviceRpc getRpcForInitialization(final NetconfDeviceCommunicator listener) {
-        return new NetconfDeviceRpc(INIT_SCHEMA_CTX, listener, new NetconfMessageTransformer(INIT_SCHEMA_CTX, false));
+    static NetconfDeviceRpc getRpcForInitialization(final NetconfDeviceCommunicator listener, final boolean notificationSupport) {
+        NetconfMessageTransformer.BaseSchema baseSchema = notificationSupport ?
+                NetconfMessageTransformer.BaseSchema.BASE_NETCONF_CTX_WITH_NOTIFICATIONS :
+                NetconfMessageTransformer.BaseSchema.BASE_NETCONF_CTX;
+
+        return new NetconfDeviceRpc(baseSchema.getSchemaContext(), listener, new NetconfMessageTransformer(baseSchema.getSchemaContext(), false, baseSchema));
     }
 
 
@@ -145,7 +127,7 @@ public class NetconfDevice implements RemoteDevice<NetconfSessionPreferences, Ne
         // http://netty.io/wiki/thread-model.html
         LOG.debug("{}: Session to remote device established with {}", id, remoteSessionCapabilities);
 
-        final NetconfDeviceRpc initRpc = getRpcForInitialization(listener);
+        final NetconfDeviceRpc initRpc = getRpcForInitialization(listener, remoteSessionCapabilities.isNotificationsSupported());
         final DeviceSourcesResolver task = new DeviceSourcesResolver(remoteSessionCapabilities, id, stateSchemasResolver, initRpc);
         final ListenableFuture<DeviceSources> sourceResolverFuture = processingExecutor.submit(task);
 
@@ -215,7 +197,11 @@ public class NetconfDevice implements RemoteDevice<NetconfSessionPreferences, Ne
     }
 
     protected void handleSalInitializationSuccess(final SchemaContext result, final NetconfSessionPreferences remoteSessionCapabilities, final DOMRpcService deviceRpc) {
-        messageTransformer = new NetconfMessageTransformer(result, true);
+        NetconfMessageTransformer.BaseSchema baseSchema =
+                remoteSessionCapabilities.isNotificationsSupported() ?
+                NetconfMessageTransformer.BaseSchema.BASE_NETCONF_CTX_WITH_NOTIFICATIONS :
+                NetconfMessageTransformer.BaseSchema.BASE_NETCONF_CTX;
+        messageTransformer = new NetconfMessageTransformer(result, true, baseSchema);
 
         updateTransformer(messageTransformer);
         // salFacade.onDeviceConnected has to be called before the notification handler is initialized
index 0797ab505f1856305933039a8bcad5e3a19b8e4b..ee119344ebe2f9220f91a0092ab0481fc465950b 100644 (file)
@@ -27,6 +27,7 @@ import java.util.concurrent.ExecutionException;
 import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
 import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfSessionPreferences;
 import org.opendaylight.netconf.sal.connect.netconf.sal.NetconfDeviceRpc;
+import org.opendaylight.netconf.sal.connect.netconf.schema.mapping.NetconfMessageTransformer;
 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.monitoring.rev101004.NetconfState;
@@ -79,7 +80,8 @@ public final class NetconfStateSchemas {
 
     private static final ContainerNode GET_SCHEMAS_RPC;
     static {
-        final DataContainerChild<?, ?> filter = NetconfMessageTransformUtil.toFilterStructure(STATE_SCHEMAS_IDENTIFIER, NetconfDevice.INIT_SCHEMA_CTX);
+        final DataContainerChild<?, ?> filter = NetconfMessageTransformUtil.toFilterStructure(STATE_SCHEMAS_IDENTIFIER,
+                NetconfMessageTransformer.BaseSchema.BASE_NETCONF_CTX_WITH_NOTIFICATIONS.getSchemaContext());
         GET_SCHEMAS_RPC
                 = Builders.containerBuilder().withNodeIdentifier(toId(NETCONF_GET_QNAME)).withChild(filter).build();
     }
index 2ac01caffce792270c774ab1bde8ef6ddab52268..270cdbfb188c8ce0582c9fe9634f0749eec10daf 100644 (file)
@@ -8,9 +8,11 @@
 package org.opendaylight.netconf.sal.connect.netconf.schema.mapping;
 
 import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.EVENT_TIME;
+import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.IETF_NETCONF_NOTIFICATIONS;
 import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_RPC_QNAME;
 import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_URI;
 import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.toPath;
+
 import com.google.common.base.Function;
 import com.google.common.base.Preconditions;
 import com.google.common.collect.Lists;
@@ -44,7 +46,9 @@ import org.opendaylight.netconf.sal.connect.api.MessageTransformer;
 import org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil;
 import org.opendaylight.netconf.sal.connect.util.MessageCounter;
 import org.opendaylight.netconf.util.OrderedNormalizedNodeWriter;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.extension.rev131210.$YangModuleInfoImpl;
 import org.opendaylight.yangtools.sal.binding.generator.impl.ModuleInfoBackedContext;
+import org.opendaylight.yangtools.yang.binding.YangModuleInfo;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
@@ -68,6 +72,46 @@ import org.w3c.dom.Element;
 
 public class NetconfMessageTransformer implements MessageTransformer<NetconfMessage> {
 
+    public enum BaseSchema {
+
+        BASE_NETCONF_CTX(
+                Lists.newArrayList(
+                        org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.base._1._0.rev110601.$YangModuleInfoImpl.getInstance()
+                )
+        ),
+        BASE_NETCONF_CTX_WITH_NOTIFICATIONS(
+                Lists.newArrayList(
+                        $YangModuleInfoImpl.getInstance(),
+                        org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.notification._1._0.rev080714.$YangModuleInfoImpl.getInstance(),
+                        org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.base._1._0.rev110601.$YangModuleInfoImpl.getInstance(),
+                        org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.notifications.rev120206.$YangModuleInfoImpl.getInstance()
+                )
+        );
+
+        private final Map<QName, RpcDefinition> mappedRpcs;
+        private final SchemaContext schemaContext;
+
+        BaseSchema(List<YangModuleInfo> modules) {
+            try {
+                final ModuleInfoBackedContext moduleInfoBackedContext = ModuleInfoBackedContext.create();
+                moduleInfoBackedContext.addModuleInfos(modules);
+                schemaContext = moduleInfoBackedContext.tryToCreateSchemaContext().get();
+                mappedRpcs = Maps.uniqueIndex(schemaContext.getOperations(), QNAME_FUNCTION);
+            } catch (final RuntimeException e) {
+                LOG.error("Unable to prepare schema context for base netconf ops", e);
+                throw new ExceptionInInitializerError(e);
+            }
+        }
+
+        private Map<QName, RpcDefinition> getMappedRpcs() {
+            return mappedRpcs;
+        }
+
+        public SchemaContext getSchemaContext() {
+            return schemaContext;
+        }
+    }
+
     public static final String MESSAGE_ID_PREFIX = "m";
 
     private static final Logger LOG= LoggerFactory.getLogger(NetconfMessageTransformer.class);
@@ -86,34 +130,25 @@ public class NetconfMessageTransformer implements MessageTransformer<NetconfMess
             return QNAME_FUNCTION.apply(notification).withoutRevision();
         }
     };
-    private static final SchemaContext BASE_NETCONF_CTX;
-
-    static {
-        try {
-            final ModuleInfoBackedContext moduleInfoBackedContext = ModuleInfoBackedContext.create();
-            moduleInfoBackedContext.addModuleInfos(
-                    Lists.newArrayList(org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.base._1._0.rev110601.$YangModuleInfoImpl.getInstance()));
-            BASE_NETCONF_CTX = moduleInfoBackedContext.tryToCreateSchemaContext().get();
-        } catch (final RuntimeException e) {
-            LOG.error("Unable to prepare schema context for base netconf ops", e);
-            throw new ExceptionInInitializerError(e);
-        }
-    }
-    private static final Map<QName, RpcDefinition> MAPPED_BASE_RPCS = Maps.uniqueIndex(BASE_NETCONF_CTX.getOperations(), QNAME_FUNCTION);
 
     private final SchemaContext schemaContext;
+    private final BaseSchema baseSchema;
     private final MessageCounter counter;
     private final Map<QName, RpcDefinition> mappedRpcs;
     private final Multimap<QName, NotificationDefinition> mappedNotifications;
     private final DomToNormalizedNodeParserFactory parserFactory;
 
     public NetconfMessageTransformer(final SchemaContext schemaContext, final boolean strictParsing) {
+        this(schemaContext, strictParsing, BaseSchema.BASE_NETCONF_CTX);
+    }
+
+    public NetconfMessageTransformer(final SchemaContext schemaContext, final boolean strictParsing, final BaseSchema baseSchema) {
         this.counter = new MessageCounter();
         this.schemaContext = schemaContext;
         parserFactory = DomToNormalizedNodeParserFactory.getInstance(XmlUtils.DEFAULT_XML_CODEC_PROVIDER, schemaContext, strictParsing);
-
         mappedRpcs = Maps.uniqueIndex(schemaContext.getOperations(), QNAME_FUNCTION);
         mappedNotifications = Multimaps.index(schemaContext.getNotifications(), QNAME_NOREV_FUNCTION);
+        this.baseSchema = baseSchema;
     }
 
     @Override
@@ -213,9 +248,9 @@ public class NetconfMessageTransformer implements MessageTransformer<NetconfMess
 
         // Determine whether a base netconf operation is being invoked and also check if the device exposed model for base netconf
         // If no, use pre built base netconf operations model
-        final boolean needToUseBaseCtx = mappedRpcs.get(rpcQName) == null && isBaseRpc(rpcQName);
+        final boolean needToUseBaseCtx = mappedRpcs.get(rpcQName) == null && isBaseOrNotificationRpc(rpcQName);
         if(needToUseBaseCtx) {
-            currentMappedRpcs = MAPPED_BASE_RPCS;
+            currentMappedRpcs = baseSchema.getMappedRpcs();
         }
 
         Preconditions.checkNotNull(currentMappedRpcs.get(rpcQName), "Unknown rpc %s, available rpcs: %s", rpcQName, currentMappedRpcs.keySet());
@@ -234,7 +269,8 @@ public class NetconfMessageTransformer implements MessageTransformer<NetconfMess
         try {
             // If the schema context for netconf device does not contain model for base netconf operations, use default pre build context with just the base model
             // This way operations like lock/unlock are supported even if the source for base model was not provided
-            writeNormalizedRpc(((ContainerNode) payload), result, rpc, needToUseBaseCtx ? BASE_NETCONF_CTX : schemaContext);
+            SchemaContext ctx = needToUseBaseCtx ? baseSchema.getSchemaContext() : schemaContext;
+            writeNormalizedRpc(((ContainerNode) payload), result, rpc, ctx);
         } catch (final XMLStreamException | IOException | IllegalStateException e) {
             throw new IllegalStateException("Unable to serialize " + rpc, e);
         }
@@ -244,8 +280,10 @@ public class NetconfMessageTransformer implements MessageTransformer<NetconfMess
         return new NetconfMessage(node);
     }
 
-    private static boolean isBaseRpc(final QName rpc) {
-        return rpc.getNamespace().equals(NETCONF_URI);
+    private boolean isBaseOrNotificationRpc(final QName rpc) {
+        return rpc.getNamespace().equals(NETCONF_URI) ||
+                rpc.getNamespace().equals(IETF_NETCONF_NOTIFICATIONS.getNamespace()) ||
+                rpc.getNamespace().equals(NetconfMessageTransformUtil.CREATE_SUBSCRIPTION_RPC_QNAME.getNamespace());
     }
 
     private DOMResult prepareDomResultForRpcRequest(final QName rpcQName) {
@@ -304,9 +342,9 @@ public class NetconfMessageTransformer implements MessageTransformer<NetconfMess
 
             // Determine whether a base netconf operation is being invoked and also check if the device exposed model for base netconf
             // If no, use pre built base netconf operations model
-            final boolean needToUseBaseCtx = mappedRpcs.get(rpcQName) == null && isBaseRpc(rpcQName);
+            final boolean needToUseBaseCtx = mappedRpcs.get(rpcQName) == null && isBaseOrNotificationRpc(rpcQName);
             if(needToUseBaseCtx) {
-                currentMappedRpcs = MAPPED_BASE_RPCS;
+                currentMappedRpcs = baseSchema.getMappedRpcs();
             }
 
             final RpcDefinition rpcDefinition = currentMappedRpcs.get(rpcQName);
index a06bd4a05cd05b96aea49ebb68953319d46127da..ac2fd3279d298f3aa81dceb2ed3fa48414db560b 100644 (file)
@@ -17,6 +17,7 @@ import java.util.Collections;
 import java.util.Set;
 import org.junit.Test;
 import org.opendaylight.controller.config.util.xml.XmlUtil;
+import org.opendaylight.netconf.sal.connect.netconf.schema.mapping.NetconfMessageTransformer;
 import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
@@ -25,6 +26,7 @@ import org.opendaylight.yangtools.yang.data.impl.schema.transform.ToNormalizedNo
 import org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.parser.DomToNormalizedNodeParserFactory;
 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 
@@ -32,10 +34,11 @@ public class NetconfStateSchemasTest {
 
     @Test
     public void testCreate() throws Exception {
-        final DataSchemaNode schemasNode = ((ContainerSchemaNode) NetconfDevice.INIT_SCHEMA_CTX.getDataChildByName("netconf-state")).getDataChildByName("schemas");
+        final SchemaContext schemaContext = NetconfMessageTransformer.BaseSchema.BASE_NETCONF_CTX_WITH_NOTIFICATIONS.getSchemaContext();
+        final DataSchemaNode schemasNode = ((ContainerSchemaNode) schemaContext.getDataChildByName("netconf-state")).getDataChildByName("schemas");
 
         final Document schemasXml = XmlUtil.readXmlToDocument(getClass().getResourceAsStream("/netconf-state.schemas.payload.xml"));
-        final ToNormalizedNodeParser<Element, ContainerNode, ContainerSchemaNode> containerNodeParser = DomToNormalizedNodeParserFactory.getInstance(XmlUtils.DEFAULT_XML_CODEC_PROVIDER, NetconfDevice.INIT_SCHEMA_CTX, false).getContainerNodeParser();
+        final ToNormalizedNodeParser<Element, ContainerNode, ContainerSchemaNode> containerNodeParser = DomToNormalizedNodeParserFactory.getInstance(XmlUtils.DEFAULT_XML_CODEC_PROVIDER, schemaContext, false).getContainerNodeParser();
         final ContainerNode compositeNodeSchemas = containerNodeParser.parse(Collections.singleton(schemasXml.getDocumentElement()), (ContainerSchemaNode) schemasNode);
         final NetconfStateSchemas schemas = NetconfStateSchemas.create(new RemoteDeviceId("device", new InetSocketAddress(99)), compositeNodeSchemas);
 
index c708574ce133896cbc9e09b8298a209477673da8..82fe4b23e788c64ea3c1924536c1432c118c6306 100644 (file)
@@ -33,7 +33,7 @@ import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
 import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
 import org.opendaylight.controller.md.sal.dom.spi.DefaultDOMRpcResult;
-import org.opendaylight.netconf.sal.connect.netconf.NetconfDevice;
+import org.opendaylight.netconf.sal.connect.netconf.schema.mapping.NetconfMessageTransformer;
 import org.opendaylight.netconf.sal.connect.netconf.util.NetconfBaseOps;
 import org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil;
 import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId;
@@ -128,7 +128,7 @@ public class NetconfDeviceWriteOnlyTxTest {
                 .doReturn(Futures.immediateFailedCheckedFuture(new IllegalStateException("Failed tx")))
                 .when(rpc).invokeRpc(any(SchemaPath.class), any(NormalizedNode.class));
 
-        final WriteRunningTx tx = new WriteRunningTx(id, new NetconfBaseOps(rpc, NetconfDevice.INIT_SCHEMA_CTX),
+        final WriteRunningTx tx = new WriteRunningTx(id, new NetconfBaseOps(rpc, NetconfMessageTransformer.BaseSchema.BASE_NETCONF_CTX_WITH_NOTIFICATIONS.getSchemaContext()),
                 false, 60000L);
         try {
             tx.delete(LogicalDatastoreType.CONFIGURATION, yangIId);
index f8bf554c1cdd282d63f6ded564c5109156c62be4..91279cc1dfca469261b4cc4ddc2da378f9f3f204 100644 (file)
@@ -13,6 +13,8 @@ import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
+import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.CREATE_SUBSCRIPTION_RPC_CONTENT;
+import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.CREATE_SUBSCRIPTION_RPC_QNAME;
 import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.GET_SCHEMA_QNAME;
 import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_CANDIDATE_QNAME;
 import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_COMMIT_QNAME;
@@ -46,7 +48,6 @@ import org.junit.Test;
 import org.opendaylight.controller.config.util.xml.XmlUtil;
 import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
 import org.opendaylight.netconf.api.NetconfMessage;
-import org.opendaylight.netconf.sal.connect.netconf.NetconfDevice;
 import org.opendaylight.netconf.sal.connect.netconf.schema.NetconfRemoteSchemaYangSourceProvider;
 import org.opendaylight.netconf.sal.connect.netconf.util.NetconfBaseOps;
 import org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil;
@@ -97,6 +98,23 @@ public class NetconfMessageTransformerTest {
         assertThat(XmlUtil.toString(netconfMessage.getDocument()), CoreMatchers.containsString("<rpc"));
     }
 
+    @Test
+    public void testCreateSubscriberNotificationSchemaNotPresent() throws Exception {
+        final SchemaContext partialSchema = getSchema(true);
+        final NetconfMessageTransformer transformer = new NetconfMessageTransformer(
+                partialSchema,
+                true,
+                NetconfMessageTransformer.BaseSchema.BASE_NETCONF_CTX_WITH_NOTIFICATIONS
+        );
+        NetconfMessage netconfMessage = transformer.toRpcRequest(
+                toPath(CREATE_SUBSCRIPTION_RPC_QNAME),
+                CREATE_SUBSCRIPTION_RPC_CONTENT
+        );
+        String documentString = XmlUtil.toString(netconfMessage.getDocument());
+        assertThat(documentString, CoreMatchers.containsString("<create-subscription"));
+        assertThat(documentString, CoreMatchers.containsString("<rpc"));
+    }
+
     @Test
     public void tesLockSchemaRequest() throws Exception {
         final SchemaContext partialSchema = getSchema(false);
@@ -228,7 +246,7 @@ public class NetconfMessageTransformerTest {
         final MapEntryNode schemaNode = Builders.mapEntryBuilder().withNodeIdentifier(identifierWithPredicates).withValue(values).build();
 
         final YangInstanceIdentifier id = YangInstanceIdentifier.builder().node(NetconfState.QNAME).node(Schemas.QNAME).node(Schema.QNAME).nodeWithKey(Schema.QNAME, keys).build();
-        final DataContainerChild<?, ?> editConfigStructure = createEditConfigStructure(NetconfDevice.INIT_SCHEMA_CTX, id, Optional.<ModifyAction>absent(), Optional.<NormalizedNode<?, ?>>fromNullable(schemaNode));
+        final DataContainerChild<?, ?> editConfigStructure = createEditConfigStructure(NetconfMessageTransformer.BaseSchema.BASE_NETCONF_CTX_WITH_NOTIFICATIONS.getSchemaContext(), id, Optional.<ModifyAction>absent(), Optional.<NormalizedNode<?, ?>>fromNullable(schemaNode));
 
         final DataContainerChild<?, ?> target = NetconfBaseOps.getTargetNode(NETCONF_CANDIDATE_QNAME);