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;
}
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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&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
--- /dev/null
+/*
+ * 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
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;
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) {
/**
* 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));
}
// 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);
}
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
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;
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();
}
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;
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;
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);
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
// 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());
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);
}
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) {
// 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);
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;
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;
@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);
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;
.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);
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;
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;
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);
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);