From: Maros Marsalek Date: Tue, 24 Feb 2015 12:17:00 +0000 (+0100) Subject: BUG-2314 Migrate netconf-connector to NormalizedNode X-Git-Tag: release/lithium~444 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=commitdiff_plain;h=8ce853c0627e829d40fe18e550bc807efbcbafee BUG-2314 Migrate netconf-connector to NormalizedNode CompositeNodes are wiped out from sal-netconf-connector. The inital operations performed on a remote netconf device (e.g. Schema download) is performed using a special schema context that contains base netconf, netconf monitoring and netconf notification schemas. With this schema context, it is possible to also use normalized nodes. YangInstanceIdentifier -> NormalizedNode filter structure is handled by a class named InstanceIdToFilter that was extracted from deprecated DataNormalizer. Change-Id: Ibcc399c3ef9413aa4f96dba5b4bb2611db7123a8 Signed-off-by: Maros Marsalek --- diff --git a/opendaylight/md-sal/sal-netconf-connector/pom.xml b/opendaylight/md-sal/sal-netconf-connector/pom.xml index 61c83a68de..40cf5a3955 100644 --- a/opendaylight/md-sal/sal-netconf-connector/pom.xml +++ b/opendaylight/md-sal/sal-netconf-connector/pom.xml @@ -12,11 +12,6 @@ bundle - - ${project.groupId} - netconf-client - ${netconf.version} - ${project.groupId} netconf-config-dispatcher @@ -53,6 +48,10 @@ org.opendaylight.controller sal-binding-api + + org.opendaylight.yangtools + binding-generator-impl + org.opendaylight.controller sal-binding-config @@ -89,7 +88,11 @@ org.slf4j slf4j-api - + + xmlunit + xmlunit + + ${project.groupId} config-api ${netconf.version} diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/config/yang/md/sal/connector/netconf/NetconfConnectorModule.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/config/yang/md/sal/connector/netconf/NetconfConnectorModule.java index 44b2435da2..4465e93dbf 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/config/yang/md/sal/connector/netconf/NetconfConnectorModule.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/config/yang/md/sal/connector/netconf/NetconfConnectorModule.java @@ -29,7 +29,6 @@ import org.opendaylight.controller.sal.connect.netconf.NetconfStateSchemas; import org.opendaylight.controller.sal.connect.netconf.listener.NetconfDeviceCommunicator; import org.opendaylight.controller.sal.connect.netconf.listener.NetconfSessionPreferences; import org.opendaylight.controller.sal.connect.netconf.sal.NetconfDeviceSalFacade; -import org.opendaylight.controller.sal.connect.netconf.schema.mapping.NetconfMessageTransformer; import org.opendaylight.controller.sal.connect.util.RemoteDeviceId; import org.opendaylight.controller.sal.core.api.Broker; import org.opendaylight.protocol.framework.ReconnectStrategy; @@ -105,13 +104,13 @@ public final class NetconfConnectorModule extends org.opendaylight.controller.co final BindingAwareBroker bindingBroker = getBindingRegistryDependency(); final RemoteDeviceHandler salFacade - = new NetconfDeviceSalFacade(id, domBroker, bindingBroker, bundleContext, globalProcessingExecutor); + = new NetconfDeviceSalFacade(id, domBroker, bindingBroker, bundleContext); final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = new NetconfDevice.SchemaResourcesDTO(schemaRegistry, schemaContextFactory, new NetconfStateSchemas.NetconfStateSchemasResolverImpl()); final NetconfDevice device = - new NetconfDevice(schemaResourcesDTO, id, salFacade, globalProcessingExecutor, new NetconfMessageTransformer(), getReconnectOnChangedSchema()); + new NetconfDevice(schemaResourcesDTO, id, salFacade, globalProcessingExecutor, getReconnectOnChangedSchema()); final NetconfDeviceCommunicator listener = userCapabilities.isPresent() ? new NetconfDeviceCommunicator(id, device, userCapabilities.get()) : new NetconfDeviceCommunicator(id, device); diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/api/MessageTransformer.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/api/MessageTransformer.java index 7a392a8769..6f81ce06cd 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/api/MessageTransformer.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/api/MessageTransformer.java @@ -7,17 +7,16 @@ */ package org.opendaylight.controller.sal.connect.api; -import org.opendaylight.yangtools.yang.common.QName; -import org.opendaylight.yangtools.yang.common.RpcResult; -import org.opendaylight.yangtools.yang.data.api.CompositeNode; -import org.opendaylight.yangtools.yang.model.api.SchemaContextListener; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; +import org.opendaylight.yangtools.yang.model.api.SchemaPath; -public interface MessageTransformer extends SchemaContextListener { +public interface MessageTransformer { - CompositeNode toNotification(M message); + ContainerNode toNotification(M message); - M toRpcRequest(QName rpc, CompositeNode node); + M toRpcRequest(SchemaPath rpc, ContainerNode node); - RpcResult toRpcResult(M message, QName rpc); + DOMRpcResult toRpcResult(M message, SchemaPath rpc); } diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/api/RemoteDeviceHandler.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/api/RemoteDeviceHandler.java index c5a0ae2544..02f45e5bc3 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/api/RemoteDeviceHandler.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/api/RemoteDeviceHandler.java @@ -7,20 +7,20 @@ */ package org.opendaylight.controller.sal.connect.api; -import org.opendaylight.controller.sal.core.api.RpcImplementation; -import org.opendaylight.yangtools.yang.data.api.CompositeNode; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcService; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; import org.opendaylight.yangtools.yang.model.api.SchemaContext; public interface RemoteDeviceHandler extends AutoCloseable { void onDeviceConnected(SchemaContext remoteSchemaContext, - PREF netconfSessionPreferences, RpcImplementation deviceRpc); + PREF netconfSessionPreferences, DOMRpcService deviceRpc); void onDeviceDisconnected(); void onDeviceFailed(Throwable throwable); - void onNotification(CompositeNode domNotification); + void onNotification(ContainerNode domNotification); void close(); } diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDevice.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDevice.java index 281f82ba2a..b57a8912cc 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDevice.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDevice.java @@ -7,6 +7,7 @@ */ package org.opendaylight.controller.sal.connect.netconf; +import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Function; import com.google.common.base.Optional; import com.google.common.base.Preconditions; @@ -26,6 +27,9 @@ import java.util.List; import java.util.Set; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcException; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcService; import org.opendaylight.controller.netconf.api.NetconfMessage; import org.opendaylight.controller.sal.connect.api.MessageTransformer; import org.opendaylight.controller.sal.connect.api.RemoteDevice; @@ -36,13 +40,15 @@ import org.opendaylight.controller.sal.connect.netconf.listener.NetconfDeviceCom import org.opendaylight.controller.sal.connect.netconf.listener.NetconfSessionPreferences; import org.opendaylight.controller.sal.connect.netconf.sal.NetconfDeviceRpc; import org.opendaylight.controller.sal.connect.netconf.schema.NetconfRemoteSchemaYangSourceProvider; +import org.opendaylight.controller.sal.connect.netconf.schema.mapping.NetconfMessageTransformer; import org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil; import org.opendaylight.controller.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.fields.unavailable.capabilities.UnavailableCapability; +import org.opendaylight.yangtools.sal.binding.generator.impl.ModuleInfoBackedContext; import org.opendaylight.yangtools.yang.common.QName; -import org.opendaylight.yangtools.yang.common.RpcResult; -import org.opendaylight.yangtools.yang.data.api.CompositeNode; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; import org.opendaylight.yangtools.yang.model.api.SchemaContext; import org.opendaylight.yangtools.yang.model.repo.api.MissingSchemaSourceException; import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactory; @@ -63,6 +69,26 @@ public final class NetconfDevice implements RemoteDevice QNAME_TO_SOURCE_ID_FUNCTION = new Function() { @Override public SourceIdentifier apply(final QName input) { @@ -77,28 +103,37 @@ public final class NetconfDevice implements RemoteDevice salFacade; private final ListeningExecutorService processingExecutor; private final SchemaSourceRegistry schemaRegistry; - private final MessageTransformer messageTransformer; private final NetconfStateSchemas.NetconfStateSchemasResolver stateSchemasResolver; private final NotificationHandler notificationHandler; private final List> sourceRegistrations = Lists.newArrayList(); + // Message transformer is constructed once the schemas are available + private MessageTransformer messageTransformer; + public NetconfDevice(final SchemaResourcesDTO schemaResourcesDTO, final RemoteDeviceId id, final RemoteDeviceHandler salFacade, - final ExecutorService globalProcessingExecutor, final MessageTransformer messageTransformer) { - this(schemaResourcesDTO, id, salFacade, globalProcessingExecutor, messageTransformer, false); + final ExecutorService globalProcessingExecutor) { + this(schemaResourcesDTO, id, salFacade, globalProcessingExecutor, false); } + /** + * 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)); + } + + // FIXME reduce parameters public NetconfDevice(final SchemaResourcesDTO schemaResourcesDTO, final RemoteDeviceId id, final RemoteDeviceHandler salFacade, - final ExecutorService globalProcessingExecutor, final MessageTransformer messageTransformer, final boolean reconnectOnSchemasChange) { + final ExecutorService globalProcessingExecutor, final boolean reconnectOnSchemasChange) { this.id = id; this.reconnectOnSchemasChange = reconnectOnSchemasChange; this.schemaRegistry = schemaResourcesDTO.getSchemaRegistry(); - this.messageTransformer = messageTransformer; this.schemaContextFactory = schemaResourcesDTO.getSchemaContextFactory(); this.salFacade = salFacade; this.stateSchemasResolver = schemaResourcesDTO.getStateSchemasResolver(); this.processingExecutor = MoreExecutors.listeningDecorator(globalProcessingExecutor); - this.notificationHandler = new NotificationHandler(salFacade, messageTransformer, id); + this.notificationHandler = new NotificationHandler(salFacade, id); } @Override @@ -111,24 +146,23 @@ public final class NetconfDevice implements RemoteDevice sourceResolverFuture = processingExecutor.submit(task); if(shouldListenOnSchemaChange(remoteSessionCapabilities)) { - registerToBaseNetconfStream(deviceRpc, listener); + registerToBaseNetconfStream(initRpc, listener); } final FutureCallback resolvedSourceCallback = new FutureCallback() { @Override public void onSuccess(final DeviceSources result) { - addProvidedSourcesToSchemaRegistry(deviceRpc, result); + addProvidedSourcesToSchemaRegistry(initRpc, result); setUpSchema(result); } private void setUpSchema(final DeviceSources result) { - processingExecutor.submit(new RecursiveSchemaSetup(result, remoteSessionCapabilities, deviceRpc, listener)); + processingExecutor.submit(new RecursiveSchemaSetup(result, remoteSessionCapabilities, listener)); } @Override @@ -139,33 +173,32 @@ public final class NetconfDevice implements RemoteDevice> rpcResultListenableFuture = - deviceRpc.invokeRpc(NetconfMessageTransformUtil.CREATE_SUBSCRIPTION_RPC_QNAME, NetconfMessageTransformUtil.CREATE_SUBSCRIPTION_RPC_CONTENT); + final CheckedFuture rpcResultListenableFuture = + deviceRpc.invokeRpc(NetconfMessageTransformUtil.toPath(NetconfMessageTransformUtil.CREATE_SUBSCRIPTION_RPC_QNAME), NetconfMessageTransformUtil.CREATE_SUBSCRIPTION_RPC_CONTENT); final NotificationHandler.NotificationFilter filter = new NotificationHandler.NotificationFilter() { @Override - public Optional filterNotification(final CompositeNode notification) { + public Optional> filterNotification(final NormalizedNode notification) { if (isCapabilityChanged(notification)) { logger.info("{}: Schemas change detected, reconnecting", id); // Only disconnect is enough, the reconnecting nature of the connector will take care of reconnecting listener.disconnect(); return Optional.absent(); } - return Optional.of(notification); + return Optional.>of(notification); } - private boolean isCapabilityChanged(final CompositeNode notification) { + private boolean isCapabilityChanged(final NormalizedNode notification) { return notification.getNodeType().equals(NetconfCapabilityChange.QNAME); } }; - Futures.addCallback(rpcResultListenableFuture, new FutureCallback>() { + Futures.addCallback(rpcResultListenableFuture, new FutureCallback() { @Override - public void onSuccess(final RpcResult result) { + public void onSuccess(final DOMRpcResult domRpcResult) { notificationHandler.addNotificationFilter(filter); } @@ -180,10 +213,13 @@ public final class NetconfDevice implements RemoteDevice transformer) { + messageTransformer = transformer; } private void addProvidedSourcesToSchemaRegistry(final NetconfDeviceRpc deviceRpc, final DeviceSources deviceSources) { @@ -217,10 +250,6 @@ public final class NetconfDevice implements RemoteDevice listener) { - return new NetconfDeviceRpc(listener, messageTransformer); - } - @Override public void onRemoteSessionDown() { notificationHandler.onRemoteSchemaDown(); @@ -233,7 +262,7 @@ public final class NetconfDevice implements RemoteDevice { + private final NetconfDeviceRpc deviceRpc; private final NetconfSessionPreferences remoteSessionCapabilities; private final RemoteDeviceId id; private final NetconfStateSchemas.NetconfStateSchemasResolver stateSchemasResolver; - public DeviceSourcesResolver(final NetconfDeviceRpc deviceRpc, final NetconfSessionPreferences remoteSessionCapabilities, final RemoteDeviceId id, final NetconfStateSchemas.NetconfStateSchemasResolver stateSchemasResolver) { + DeviceSourcesResolver(final NetconfDeviceRpc deviceRpc, final NetconfSessionPreferences remoteSessionCapabilities, + final RemoteDeviceId id, final NetconfStateSchemas.NetconfStateSchemasResolver stateSchemasResolver) { this.deviceRpc = deviceRpc; this.remoteSessionCapabilities = remoteSessionCapabilities; this.id = id; this.stateSchemasResolver = stateSchemasResolver; } + public DeviceSourcesResolver(final NetconfSessionPreferences remoteSessionCapabilities, final RemoteDeviceId id, final NetconfStateSchemas.NetconfStateSchemasResolver stateSchemasResolver, final NetconfDeviceRpc rpcForMonitoring) { + this(rpcForMonitoring, remoteSessionCapabilities, id, stateSchemasResolver); + } + @Override public DeviceSources call() throws Exception { @@ -307,10 +342,10 @@ public final class NetconfDevice implements RemoteDevice providedSourcesNotRequired = Sets.difference(providedSources, requiredSources); if (!providedSourcesNotRequired.isEmpty()) { logger.warn("{}: Netconf device provides additional yang models not reported in hello message capabilities: {}", @@ -326,7 +361,6 @@ public final class NetconfDevice implements RemoteDevice requiredSources; @@ -353,14 +387,12 @@ public final class NetconfDevice implements RemoteDevice listener; - private NetconfDeviceCapabilities capabilities; + private final NetconfDeviceCapabilities capabilities; - public RecursiveSchemaSetup(final DeviceSources deviceSources, final NetconfSessionPreferences remoteSessionCapabilities, final NetconfDeviceRpc deviceRpc, final RemoteDeviceCommunicator listener) { + public RecursiveSchemaSetup(final DeviceSources deviceSources, final NetconfSessionPreferences remoteSessionCapabilities, final RemoteDeviceCommunicator listener) { this.deviceSources = deviceSources; this.remoteSessionCapabilities = remoteSessionCapabilities; - this.deviceRpc = deviceRpc; this.listener = listener; this.capabilities = remoteSessionCapabilities.getNetconfDeviceCapabilities(); } @@ -390,10 +422,10 @@ public final class NetconfDevice implements RemoteDevice filteredQNames = Sets.difference(remoteSessionCapabilities.getModuleBasedCaps(), capabilities.getUnresolvedCapabilites().keySet()); + final Collection filteredQNames = Sets.difference(remoteSessionCapabilities.getModuleBasedCaps(), capabilities.getUnresolvedCapabilites().keySet()); capabilities.addCapabilities(filteredQNames); capabilities.addNonModuleBasedCapabilities(remoteSessionCapabilities.getNonModuleCaps()); - handleSalInitializationSuccess(result, remoteSessionCapabilities, deviceRpc); + handleSalInitializationSuccess(result, remoteSessionCapabilities, getDeviceSpecificRpc(result)); } @Override @@ -423,6 +455,10 @@ public final class NetconfDevice implements RemoteDevice stripMissingSource(final Collection requiredSources, final SourceIdentifier sIdToRemove) { final LinkedList sourceIdentifiers = Lists.newLinkedList(requiredSources); final boolean removed = sourceIdentifiers.remove(sIdToRemove); @@ -430,10 +466,10 @@ public final class NetconfDevice implements RemoteDevice getQNameFromSourceIdentifiers(Collection identifiers) { - Collection qNames = new HashSet<>(); - for (SourceIdentifier source : identifiers) { - Optional qname = getQNameFromSourceIdentifier(source); + private Collection getQNameFromSourceIdentifiers(final Collection identifiers) { + final Collection qNames = new HashSet<>(); + for (final SourceIdentifier source : identifiers) { + final Optional qname = getQNameFromSourceIdentifier(source); if (qname.isPresent()) { qNames.add(qname.get()); } @@ -444,8 +480,8 @@ public final class NetconfDevice implements RemoteDevice getQNameFromSourceIdentifier(SourceIdentifier identifier) { - for (QName qname : remoteSessionCapabilities.getModuleBasedCaps()) { + private Optional getQNameFromSourceIdentifier(final SourceIdentifier identifier) { + for (final QName qname : remoteSessionCapabilities.getModuleBasedCaps()) { if (qname.getLocalName().equals(identifier.getName()) && qname.getFormattedRevision().equals(identifier.getRevision())) { return Optional.of(qname); diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfStateSchemas.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfStateSchemas.java index 68c1a5c6a8..aa0897e021 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfStateSchemas.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfStateSchemas.java @@ -1,16 +1,22 @@ package org.opendaylight.controller.sal.connect.netconf; +import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_DATA_QNAME; +import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_GET_QNAME; +import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.toId; +import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.toPath; + import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Function; import com.google.common.base.Optional; import com.google.common.base.Preconditions; +import com.google.common.base.Strings; import com.google.common.collect.Collections2; -import com.google.common.collect.Lists; import com.google.common.collect.Sets; import java.net.URI; import java.util.Collections; import java.util.Set; import java.util.concurrent.ExecutionException; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult; import org.opendaylight.controller.sal.connect.netconf.listener.NetconfSessionPreferences; import org.opendaylight.controller.sal.connect.netconf.sal.NetconfDeviceRpc; import org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil; @@ -20,12 +26,16 @@ import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.mon 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.yangtools.yang.common.QName; -import org.opendaylight.yangtools.yang.common.RpcResult; -import org.opendaylight.yangtools.yang.data.api.CompositeNode; -import org.opendaylight.yangtools.yang.data.api.Node; -import org.opendaylight.yangtools.yang.data.api.SimpleNode; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; -import org.opendaylight.yangtools.yang.data.impl.NodeFactory; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; +import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild; +import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode; +import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode; +import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode; +import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode; +import org.opendaylight.yangtools.yang.data.api.schema.MapNode; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.impl.schema.Builders; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -34,7 +44,7 @@ import org.slf4j.LoggerFactory; */ public final class NetconfStateSchemas { - private static final Logger logger = LoggerFactory.getLogger(NetconfStateSchemas.class); + private static final Logger LOG = LoggerFactory.getLogger(NetconfStateSchemas.class); /** * Factory for NetconfStateSchemas @@ -58,15 +68,12 @@ public final class NetconfStateSchemas { private static final YangInstanceIdentifier STATE_SCHEMAS_IDENTIFIER = YangInstanceIdentifier.builder().node(NetconfState.QNAME).node(Schemas.QNAME).build(); - private static final YangInstanceIdentifier DATA_STATE_SCHEMAS_IDENTIFIER = - YangInstanceIdentifier.builder().node(NetconfMessageTransformUtil.NETCONF_DATA_QNAME) - .node(NetconfState.QNAME).node(Schemas.QNAME).build(); - private static final CompositeNode GET_SCHEMAS_RPC; + private static final ContainerNode GET_SCHEMAS_RPC; static { - final Node filter = NetconfMessageTransformUtil.toFilterStructure(STATE_SCHEMAS_IDENTIFIER); + final DataContainerChild filter = NetconfMessageTransformUtil.toFilterStructure(STATE_SCHEMAS_IDENTIFIER, NetconfDevice.INIT_SCHEMA_CTX); GET_SCHEMAS_RPC - = NodeFactory.createImmutableCompositeNode(NetconfMessageTransformUtil.NETCONF_GET_QNAME, null, Lists.>newArrayList(filter)); + = Builders.containerBuilder().withNodeIdentifier(toId(NETCONF_GET_QNAME)).withChild(filter).build(); } private final Set availableYangSchemas; @@ -93,45 +100,69 @@ public final class NetconfStateSchemas { */ private static NetconfStateSchemas create(final NetconfDeviceRpc deviceRpc, final NetconfSessionPreferences remoteSessionCapabilities, final RemoteDeviceId id) { if(remoteSessionCapabilities.isMonitoringSupported() == false) { - logger.warn("{}: Netconf monitoring not supported on device, cannot detect provided schemas"); + LOG.warn("{}: Netconf monitoring not supported on device, cannot detect provided schemas"); return EMPTY; } - final RpcResult schemasNodeResult; + final DOMRpcResult schemasNodeResult; try { - schemasNodeResult = deviceRpc.invokeRpc(NetconfMessageTransformUtil.NETCONF_GET_QNAME, GET_SCHEMAS_RPC).get(); + schemasNodeResult = deviceRpc.invokeRpc(toPath(NETCONF_GET_QNAME), GET_SCHEMAS_RPC).get(); } catch (final InterruptedException e) { Thread.currentThread().interrupt(); throw new RuntimeException(id + ": Interrupted while waiting for response to " + STATE_SCHEMAS_IDENTIFIER, e); } catch (final ExecutionException e) { - logger.warn("{}: Unable to detect available schemas, get to {} failed", id, STATE_SCHEMAS_IDENTIFIER, e); + LOG.warn("{}: Unable to detect available schemas, get to {} failed", id, STATE_SCHEMAS_IDENTIFIER, e); return EMPTY; } - if(schemasNodeResult.isSuccessful() == false) { - logger.warn("{}: Unable to detect available schemas, get to {} failed, {}", id, STATE_SCHEMAS_IDENTIFIER, schemasNodeResult.getErrors()); + if(schemasNodeResult.getErrors().isEmpty() == false) { + LOG.warn("{}: Unable to detect available schemas, get to {} failed, {}", id, STATE_SCHEMAS_IDENTIFIER, schemasNodeResult.getErrors()); return EMPTY; } - final CompositeNode schemasNode = - (CompositeNode) NetconfMessageTransformUtil.findNode(schemasNodeResult.getResult(), DATA_STATE_SCHEMAS_IDENTIFIER); - if(schemasNode == null) { - logger.warn("{}: Unable to detect available schemas, get to {} was empty", id, STATE_SCHEMAS_IDENTIFIER); + final Optional> schemasNode = findSchemasNode(schemasNodeResult.getResult()); + + if(schemasNode.isPresent()) { + Preconditions.checkState(schemasNode.get() instanceof ContainerNode, + "Expecting container containing schemas, but was %s", schemasNode.get()); + return create(id, ((ContainerNode) schemasNode.get())); + } else { + LOG.warn("{}: Unable to detect available schemas, get to {} was empty", id, STATE_SCHEMAS_IDENTIFIER); return EMPTY; } + } - return create(id, schemasNode); + private static Optional> findSchemasNode(final NormalizedNode result) { + if(result == null) { + return Optional.absent(); + } + final Optional> dataNode = ((DataContainerNode) result).getChild(toId(NETCONF_DATA_QNAME)); + if(dataNode.isPresent() == false) { + return Optional.absent(); + } + + final Optional> nStateNode = + ((DataContainerNode) dataNode.get()).getChild(toId(NetconfState.QNAME)); + if(nStateNode.isPresent() == false) { + return Optional.absent(); + } + + return ((DataContainerNode) nStateNode.get()).getChild(toId(Schemas.QNAME)); } /** * Parse response of get(netconf-state/schemas) to find all schemas under netconf-state/schemas */ @VisibleForTesting - protected static NetconfStateSchemas create(final RemoteDeviceId id, final CompositeNode schemasNode) { + protected static NetconfStateSchemas create(final RemoteDeviceId id, final ContainerNode schemasNode) { final Set availableYangSchemas = Sets.newHashSet(); - for (final CompositeNode schemaNode : schemasNode.getCompositesByName(Schema.QNAME.withoutRevision())) { - final Optional fromCompositeNode = RemoteYangSchema.createFromCompositeNode(id, schemaNode); + final Optional> child = schemasNode.getChild(toId(Schema.QNAME)); + Preconditions.checkState(child.isPresent(), "Unable to find list: %s in response: %s", Schema.QNAME.withoutRevision(), schemasNode); + Preconditions.checkState(child.get() instanceof MapNode, "Unexpected structure for container: %s in response: %s. Expecting a list", Schema.QNAME.withoutRevision(), schemasNode); + + for (final MapEntryNode schemaNode : ((MapNode) child.get()).getValue()) { + final Optional fromCompositeNode = RemoteYangSchema.createFromNormalizedNode(id, schemaNode); if(fromCompositeNode.isPresent()) { availableYangSchemas.add(fromCompositeNode.get()); } @@ -151,64 +182,70 @@ public final class NetconfStateSchemas { return qname; } - static Optional createFromCompositeNode(final RemoteDeviceId id, final CompositeNode schemaNode) { - Preconditions.checkArgument(schemaNode.getKey().equals(Schema.QNAME.withoutRevision()), "Wrong QName %s", schemaNode.getKey()); + static Optional createFromNormalizedNode(final RemoteDeviceId id, final MapEntryNode schemaNode) { + Preconditions.checkArgument(schemaNode.getNodeType().equals(Schema.QNAME), "Wrong QName %s", schemaNode.getNodeType()); - QName childNode = NetconfMessageTransformUtil.IETF_NETCONF_MONITORING_SCHEMA_FORMAT.withoutRevision(); + QName childNode = NetconfMessageTransformUtil.IETF_NETCONF_MONITORING_SCHEMA_FORMAT; String formatAsString = getSingleChildNodeValue(schemaNode, childNode).get(); //This is HotFix for situations where format statement in netconf-monitoring might be passed with prefix. if (formatAsString.contains(":")) { - String[] prefixedString = formatAsString.split(":"); + final String[] prefixedString = formatAsString.split(":"); //FIXME: might be good idea to check prefix against model namespace formatAsString = prefixedString[1]; } if(formatAsString.equals(Yang.QNAME.getLocalName()) == false) { - logger.debug("{}: Ignoring schema due to unsupported format: {}", id, formatAsString); + LOG.debug("{}: Ignoring schema due to unsupported format: {}", id, formatAsString); return Optional.absent(); } - childNode = NetconfMessageTransformUtil.IETF_NETCONF_MONITORING_SCHEMA_LOCATION.withoutRevision(); + childNode = NetconfMessageTransformUtil.IETF_NETCONF_MONITORING_SCHEMA_LOCATION; final Set locationsAsString = getAllChildNodeValues(schemaNode, childNode); if(locationsAsString.contains(Schema.Location.Enumeration.NETCONF.toString()) == false) { - logger.debug("{}: Ignoring schema due to unsupported location: {}", id, locationsAsString); + LOG.debug("{}: Ignoring schema due to unsupported location: {}", id, locationsAsString); return Optional.absent(); } - childNode = NetconfMessageTransformUtil.IETF_NETCONF_MONITORING_SCHEMA_NAMESPACE.withoutRevision(); + childNode = NetconfMessageTransformUtil.IETF_NETCONF_MONITORING_SCHEMA_NAMESPACE; final String namespaceAsString = getSingleChildNodeValue(schemaNode, childNode).get(); - childNode = NetconfMessageTransformUtil.IETF_NETCONF_MONITORING_SCHEMA_VERSION.withoutRevision(); + childNode = NetconfMessageTransformUtil.IETF_NETCONF_MONITORING_SCHEMA_VERSION; // Revision does not have to be filled final Optional revisionAsString = getSingleChildNodeValue(schemaNode, childNode); - childNode = NetconfMessageTransformUtil.IETF_NETCONF_MONITORING_SCHEMA_IDENTIFIER.withoutRevision(); + childNode = NetconfMessageTransformUtil.IETF_NETCONF_MONITORING_SCHEMA_IDENTIFIER; final String moduleNameAsString = getSingleChildNodeValue(schemaNode, childNode).get(); final QName moduleQName = revisionAsString.isPresent() ? QName.create(namespaceAsString, revisionAsString.get(), moduleNameAsString) - : QName.create(URI.create(namespaceAsString), null, moduleNameAsString).withoutRevision(); + : QName.create(URI.create(namespaceAsString), null, moduleNameAsString); return Optional.of(new RemoteYangSchema(moduleQName)); } - private static Set getAllChildNodeValues(final CompositeNode schemaNode, final QName childNodeQName) { + /** + * Extracts all values of a leaf-list node as a set of strings + */ + private static Set getAllChildNodeValues(final DataContainerNode schemaNode, final QName childNodeQName) { final Set extractedValues = Sets.newHashSet(); - for (final SimpleNode childNode : schemaNode.getSimpleNodesByName(childNodeQName)) { - extractedValues.add(getValueOfSimpleNode(childNodeQName, childNode).get()); + final Optional> child = schemaNode.getChild(toId(childNodeQName)); + Preconditions.checkArgument(child.isPresent(), "Child nodes %s not present", childNodeQName); + Preconditions.checkArgument(child.get() instanceof LeafSetNode, "Child nodes %s not present", childNodeQName); + for (final LeafSetEntryNode childNode : ((LeafSetNode) child.get()).getValue()) { + extractedValues.add(getValueOfSimpleNode(childNode).get()); } return extractedValues; } - private static Optional getSingleChildNodeValue(final CompositeNode schemaNode, final QName childNode) { - final SimpleNode node = schemaNode.getFirstSimpleByName(childNode); - return getValueOfSimpleNode(childNode, node); + private static Optional getSingleChildNodeValue(final DataContainerNode schemaNode, final QName childNode) { + final Optional> node = schemaNode.getChild(toId(childNode)); + Preconditions.checkArgument(node.isPresent(), "Child node %s not present", childNode); + return getValueOfSimpleNode(node.get()); } - private static Optional getValueOfSimpleNode(final QName childNode, final SimpleNode node) { - Preconditions.checkNotNull(node, "Child node %s not present", childNode); + private static Optional getValueOfSimpleNode(final NormalizedNode node) { final Object value = node.getValue(); - return value == null ? Optional.absent() : Optional.of(value.toString().trim()); + return value == null || Strings.isNullOrEmpty(value.toString()) ? Optional.absent() : Optional.of(value.toString().trim()); } @Override diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NotificationHandler.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NotificationHandler.java index bc3326e1ae..481afa5c83 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NotificationHandler.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NotificationHandler.java @@ -16,7 +16,8 @@ import org.opendaylight.controller.netconf.util.xml.XmlUtil; import org.opendaylight.controller.sal.connect.api.MessageTransformer; import org.opendaylight.controller.sal.connect.api.RemoteDeviceHandler; import org.opendaylight.controller.sal.connect.util.RemoteDeviceId; -import org.opendaylight.yangtools.yang.data.api.CompositeNode; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -29,14 +30,14 @@ final class NotificationHandler { private final RemoteDeviceHandler salFacade; private final List queue = new LinkedList<>(); - private final MessageTransformer messageTransformer; private final RemoteDeviceId id; private boolean passNotifications = false; + private NotificationFilter filter; + private MessageTransformer messageTransformer; - NotificationHandler(final RemoteDeviceHandler salFacade, final MessageTransformer messageTransformer, final RemoteDeviceId id) { + NotificationHandler(final RemoteDeviceHandler salFacade, final RemoteDeviceId id) { this.salFacade = Preconditions.checkNotNull(salFacade); - this.messageTransformer = Preconditions.checkNotNull(messageTransformer); this.id = Preconditions.checkNotNull(id); } @@ -50,8 +51,11 @@ final class NotificationHandler { /** * Forward all cached notifications and pass all notifications from this point directly to sal facade. + * @param messageTransformer */ - synchronized void onRemoteSchemaUp() { + synchronized void onRemoteSchemaUp(final MessageTransformer messageTransformer) { + this.messageTransformer = Preconditions.checkNotNull(messageTransformer); + passNotifications = true; for (final NetconfMessage cachedNotification : queue) { @@ -61,8 +65,8 @@ final class NotificationHandler { queue.clear(); } - private CompositeNode transformNotification(final NetconfMessage cachedNotification) { - final CompositeNode parsedNotification = messageTransformer.toNotification(cachedNotification); + private ContainerNode transformNotification(final NetconfMessage cachedNotification) { + final ContainerNode parsedNotification = messageTransformer.toNotification(cachedNotification); Preconditions.checkNotNull(parsedNotification, "%s: Unable to parse received notification: %s", id, cachedNotification); return parsedNotification; } @@ -78,7 +82,7 @@ final class NotificationHandler { queue.add(notification); } - private synchronized void passNotification(final CompositeNode parsedNotification) { + private synchronized void passNotification(final ContainerNode parsedNotification) { logger.debug("{}: Forwarding notification {}", id, parsedNotification); if(filter == null || filter.filterNotification(parsedNotification).isPresent()) { @@ -93,10 +97,11 @@ final class NotificationHandler { synchronized void onRemoteSchemaDown() { queue.clear(); passNotifications = false; + messageTransformer = null; } static interface NotificationFilter { - Optional filterNotification(CompositeNode notification); + Optional> filterNotification(NormalizedNode notification); } } diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceDataBroker.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceDataBroker.java index 87ca11de87..091c6b42e4 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceDataBroker.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceDataBroker.java @@ -10,22 +10,21 @@ package org.opendaylight.controller.sal.connect.netconf.sal; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener; -import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizer; import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker; import org.opendaylight.controller.md.sal.dom.api.DOMDataChangeListener; import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction; import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction; import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcService; import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain; import org.opendaylight.controller.sal.connect.netconf.listener.NetconfSessionPreferences; import org.opendaylight.controller.sal.connect.netconf.sal.tx.ReadOnlyTx; import org.opendaylight.controller.sal.connect.netconf.sal.tx.ReadWriteTx; -import org.opendaylight.controller.sal.connect.netconf.sal.tx.WriteCandidateTx; import org.opendaylight.controller.sal.connect.netconf.sal.tx.WriteCandidateRunningTx; +import org.opendaylight.controller.sal.connect.netconf.sal.tx.WriteCandidateTx; import org.opendaylight.controller.sal.connect.netconf.sal.tx.WriteRunningTx; import org.opendaylight.controller.sal.connect.netconf.util.NetconfBaseOps; import org.opendaylight.controller.sal.connect.util.RemoteDeviceId; -import org.opendaylight.controller.sal.core.api.RpcImplementation; import org.opendaylight.yangtools.concepts.ListenerRegistration; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.model.api.SchemaContext; @@ -34,18 +33,16 @@ final class NetconfDeviceDataBroker implements DOMDataBroker { private final RemoteDeviceId id; private final NetconfBaseOps netconfOps; private final NetconfSessionPreferences netconfSessionPreferences; - private final DataNormalizer normalizer; - public NetconfDeviceDataBroker(final RemoteDeviceId id, final RpcImplementation rpc, final SchemaContext schemaContext, final NetconfSessionPreferences netconfSessionPreferences) { + public NetconfDeviceDataBroker(final RemoteDeviceId id, final SchemaContext schemaContext, final DOMRpcService rpc, final NetconfSessionPreferences netconfSessionPreferences) { this.id = id; - this.netconfOps = new NetconfBaseOps(rpc); + this.netconfOps = new NetconfBaseOps(rpc, schemaContext); this.netconfSessionPreferences = netconfSessionPreferences; - normalizer = new DataNormalizer(schemaContext); } @Override public DOMDataReadOnlyTransaction newReadOnlyTransaction() { - return new ReadOnlyTx(netconfOps, normalizer, id); + return new ReadOnlyTx(netconfOps, id); } @Override @@ -57,12 +54,12 @@ final class NetconfDeviceDataBroker implements DOMDataBroker { public DOMDataWriteTransaction newWriteOnlyTransaction() { if(netconfSessionPreferences.isCandidateSupported()) { if(netconfSessionPreferences.isRunningWritable()) { - return new WriteCandidateRunningTx(id, netconfOps, normalizer, netconfSessionPreferences); + return new WriteCandidateRunningTx(id, netconfOps, netconfSessionPreferences); } else { - return new WriteCandidateTx(id, netconfOps, normalizer, netconfSessionPreferences); + return new WriteCandidateTx(id, netconfOps, netconfSessionPreferences); } } else { - return new WriteRunningTx(id, netconfOps, normalizer, netconfSessionPreferences); + return new WriteRunningTx(id, netconfOps, netconfSessionPreferences); } } diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceDatastoreAdapter.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceDatastoreAdapter.java index 3715969b2b..8590c491e4 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceDatastoreAdapter.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceDatastoreAdapter.java @@ -37,6 +37,8 @@ import org.slf4j.LoggerFactory; * Asynchronous (Binding-aware) adapter over datastore subtree for netconf device. * * All data changes are submitted to an ExecutorService to avoid Thread blocking while sal is waiting for schema. + * + * @deprecated Data is pushed into Topology instead if Inventory model */ @Deprecated final class NetconfDeviceDatastoreAdapter implements AutoCloseable { diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceNotificationService.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceNotificationService.java new file mode 100644 index 0000000000..bfcb4caddb --- /dev/null +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceNotificationService.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2015 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.controller.sal.connect.netconf.sal; + +import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.toPath; + +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Lists; +import com.google.common.collect.Multimap; +import java.util.Collection; +import javax.annotation.Nonnull; +import org.opendaylight.controller.md.sal.dom.api.DOMNotification; +import org.opendaylight.controller.md.sal.dom.api.DOMNotificationListener; +import org.opendaylight.controller.md.sal.dom.api.DOMNotificationService; +import org.opendaylight.yangtools.concepts.ListenerRegistration; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; +import org.opendaylight.yangtools.yang.model.api.SchemaPath; + +class NetconfDeviceNotificationService implements DOMNotificationService { + + private final Multimap listeners = HashMultimap.create(); + + // Notification publish is very simple and hijacks the thread of the caller + // TODO shouldnt we reuse the implementation for notification router from sal-broker-impl ? + public synchronized void publishNotification(final ContainerNode notification) { + final SchemaPath schemaPath = toPath(notification.getNodeType()); + for (final DOMNotificationListener domNotificationListener : listeners.get(schemaPath)) { + domNotificationListener.onNotification(new DOMNotification() { + @Nonnull + @Override + public SchemaPath getType() { + return schemaPath; + } + + @Nonnull + @Override + public ContainerNode getBody() { + return notification; + } + }); + } + } + + @Override + public synchronized ListenerRegistration registerNotificationListener(@Nonnull final T listener, @Nonnull final Collection types) { + for (final SchemaPath type : types) { + listeners.put(type, listener); + } + + // FIXME this should invoke create-subscription rpc on the remote device for a given notification + + return new ListenerRegistration() { + @Override + public void close() { + for (final SchemaPath type : types) { + listeners.remove(type, listener); + } + } + + @Override + public T getInstance() { + return listener; + } + }; + } + + @Override + public synchronized ListenerRegistration registerNotificationListener(@Nonnull final T listener, final SchemaPath... types) { + return registerNotificationListener(listener, Lists.newArrayList(types)); + } +} diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceRpc.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceRpc.java index a0453bce55..108b33ad14 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceRpc.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceRpc.java @@ -8,71 +8,102 @@ package org.opendaylight.controller.sal.connect.netconf.sal; import com.google.common.base.Function; +import com.google.common.base.Preconditions; +import com.google.common.collect.Collections2; +import com.google.common.util.concurrent.CheckedFuture; import com.google.common.util.concurrent.Futures; - -import java.util.Collections; -import java.util.Set; - +import com.google.common.util.concurrent.ListenableFuture; +import java.util.Collection; +import javax.annotation.Nonnull; import javax.annotation.Nullable; - +import org.opendaylight.controller.md.sal.dom.api.DOMRpcAvailabilityListener; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcException; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcIdentifier; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementationNotAvailableException; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcService; +import org.opendaylight.controller.md.sal.dom.spi.DefaultDOMRpcResult; import org.opendaylight.controller.netconf.api.NetconfMessage; import org.opendaylight.controller.sal.connect.api.MessageTransformer; import org.opendaylight.controller.sal.connect.api.RemoteDeviceCommunicator; -import org.opendaylight.controller.sal.core.api.RpcImplementation; -import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.concepts.ListenerRegistration; import org.opendaylight.yangtools.yang.common.RpcResult; -import org.opendaylight.yangtools.yang.common.RpcResultBuilder; -import org.opendaylight.yangtools.yang.data.api.CompositeNode; - -import com.google.common.util.concurrent.ListenableFuture; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.model.api.RpcDefinition; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.model.api.SchemaPath; /** * Invokes RPC by sending netconf message via listener. Also transforms result from NetconfMessage to CompositeNode. */ -public final class NetconfDeviceRpc implements RpcImplementation { +public final class NetconfDeviceRpc implements DOMRpcService { + + private static final Function RPC_TO_RPC_IDENTIFIER = new Function() { + @Override + public DOMRpcIdentifier apply(final RpcDefinition input) { + // TODO add support for routed rpcs ... is it necessary in this case ? + return DOMRpcIdentifier.create(input.getPath()); + } + }; + private final RemoteDeviceCommunicator listener; private final MessageTransformer transformer; + private final Collection availableRpcs; - public NetconfDeviceRpc(final RemoteDeviceCommunicator listener, final MessageTransformer transformer) { + public NetconfDeviceRpc(final SchemaContext schemaContext, final RemoteDeviceCommunicator listener, final MessageTransformer transformer) { this.listener = listener; this.transformer = transformer; - } - @Override - public Set getSupportedRpcs() { - // TODO is this correct ? - return Collections.emptySet(); + availableRpcs = Collections2.transform(schemaContext.getOperations(), RPC_TO_RPC_IDENTIFIER); } - // TODO change this to work with NormalizedNode api. Then we can loose DataNormalizer from Transactions - + @Nonnull @Override - public ListenableFuture> invokeRpc(final QName rpc, final CompositeNode input) { - final NetconfMessage message = transformRequest(rpc, input); - final ListenableFuture> delegateFutureWithPureResult = listener.sendRequest( - message, rpc); + public CheckedFuture invokeRpc(@Nonnull final SchemaPath type, @Nullable final NormalizedNode input) { + Preconditions.checkArgument(input instanceof ContainerNode, "Epc payload has to be a %s, was %s", ContainerNode.class, input); + final NetconfMessage message = transformer.toRpcRequest(type, (ContainerNode) input); + final ListenableFuture> delegateFutureWithPureResult = listener.sendRequest(message, type.getLastComponent()); - return Futures.transform(delegateFutureWithPureResult, new Function, RpcResult>() { + final ListenableFuture transformed = Futures.transform(delegateFutureWithPureResult, new Function, DOMRpcResult>() { @Override - public RpcResult apply(@Nullable final RpcResult input) { - return transformResult(input, rpc); + public DOMRpcResult apply(final RpcResult input) { + if (input.isSuccessful()) { + return transformer.toRpcResult(input.getResult(), type); + } else { + // TODO check whether the listener sets errors properly + return new DefaultDOMRpcResult(input.getErrors()); + } } }); - } - private NetconfMessage transformRequest(final QName rpc, final CompositeNode input) { - return transformer.toRpcRequest(rpc, input); + return Futures.makeChecked(transformed, new Function() { + @Nullable + @Override + public DOMRpcException apply(@Nullable final Exception e) { + // FIXME what other possible exceptions are there ? + return new DOMRpcImplementationNotAvailableException(e, "Unable to invoke rpc %s", type); + } + }); } - private RpcResult transformResult(final RpcResult netconfMessageRpcResult, - final QName rpc) { - if (netconfMessageRpcResult.isSuccessful()) { - return transformer.toRpcResult(netconfMessageRpcResult.getResult(), rpc); - } else { - return RpcResultBuilder. failed() - .withRpcErrors(netconfMessageRpcResult.getErrors()).build(); - } - } + @Nonnull + @Override + public ListenerRegistration registerRpcListener(@Nonnull final T listener) { + + listener.onRpcAvailable(availableRpcs); + return new ListenerRegistration() { + @Override + public void close() { + // NOOP, no rpcs appear and disappear in this implementation + } + + @Override + public T getInstance() { + return listener; + } + }; + } } diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceSalFacade.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceSalFacade.java index db8a238242..ad16532bad 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceSalFacade.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceSalFacade.java @@ -8,31 +8,19 @@ package org.opendaylight.controller.sal.connect.netconf.sal; import com.google.common.collect.Lists; -import com.google.common.collect.Maps; import java.util.Collections; import java.util.List; -import java.util.Map; -import java.util.concurrent.ExecutorService; import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcService; import org.opendaylight.controller.sal.binding.api.BindingAwareBroker; import org.opendaylight.controller.sal.connect.api.RemoteDeviceHandler; import org.opendaylight.controller.sal.connect.netconf.listener.NetconfDeviceCapabilities; import org.opendaylight.controller.sal.connect.netconf.listener.NetconfSessionPreferences; import org.opendaylight.controller.sal.connect.util.RemoteDeviceId; import org.opendaylight.controller.sal.core.api.Broker; -import org.opendaylight.controller.sal.core.api.RpcImplementation; -import org.opendaylight.controller.sal.core.api.RpcProvisionRegistry; -import org.opendaylight.controller.sal.core.api.notify.NotificationListener; -import org.opendaylight.controller.sal.core.api.notify.NotificationPublishService; -import org.opendaylight.controller.sal.dom.broker.impl.NotificationRouterImpl; -import org.opendaylight.controller.sal.dom.broker.impl.SchemaAwareRpcBroker; -import org.opendaylight.controller.sal.dom.broker.spi.NotificationRouter; -import org.opendaylight.yangtools.concepts.ListenerRegistration; import org.opendaylight.yangtools.yang.common.QName; -import org.opendaylight.yangtools.yang.data.api.CompositeNode; -import org.opendaylight.yangtools.yang.model.api.RpcDefinition; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; import org.opendaylight.yangtools.yang.model.api.SchemaContext; -import org.opendaylight.yangtools.yang.model.api.SchemaContextProvider; import org.osgi.framework.BundleContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -46,9 +34,9 @@ public final class NetconfDeviceSalFacade implements AutoCloseable, RemoteDevice private final List salRegistrations = Lists.newArrayList(); - public NetconfDeviceSalFacade(final RemoteDeviceId id, final Broker domBroker, final BindingAwareBroker bindingBroker, final BundleContext bundleContext, final ExecutorService executor) { + public NetconfDeviceSalFacade(final RemoteDeviceId id, final Broker domBroker, final BindingAwareBroker bindingBroker, final BundleContext bundleContext) { this.id = id; - this.salProvider = new NetconfDeviceSalProvider(id, executor); + this.salProvider = new NetconfDeviceSalProvider(id); registerToSal(domBroker, bindingBroker, bundleContext); } @@ -58,43 +46,21 @@ public final class NetconfDeviceSalFacade implements AutoCloseable, RemoteDevice } @Override - public synchronized void onNotification(final CompositeNode domNotification) { + public synchronized void onNotification(final ContainerNode domNotification) { salProvider.getMountInstance().publish(domNotification); } @Override public synchronized void onDeviceConnected(final SchemaContext schemaContext, - final NetconfSessionPreferences netconfSessionPreferences, final RpcImplementation deviceRpc) { + final NetconfSessionPreferences netconfSessionPreferences, final DOMRpcService deviceRpc) { - // TODO move SchemaAwareRpcBroker from sal-broker-impl, now we have depend on the whole sal-broker-impl - final RpcProvisionRegistry rpcRegistry = new SchemaAwareRpcBroker(id.getPath().toString(), new SchemaContextProvider() { - @Override - public SchemaContext getSchemaContext() { - return schemaContext; - } - }); - registerRpcsToSal(schemaContext, rpcRegistry, deviceRpc); - final DOMDataBroker domBroker = new NetconfDeviceDataBroker(id, deviceRpc, schemaContext, netconfSessionPreferences); - - // TODO NotificationPublishService and NotificationRouter have the same interface - final NotificationPublishService notificationService = new NotificationPublishService() { - - private final NotificationRouter innerRouter = new NotificationRouterImpl(); - - @Override - public void publish(final CompositeNode notification) { - innerRouter.publish(notification); - } + final DOMDataBroker domBroker = new NetconfDeviceDataBroker(id, schemaContext, deviceRpc, netconfSessionPreferences); - @Override - public ListenerRegistration addNotificationListener(final QName notification, final NotificationListener listener) { - return innerRouter.addNotificationListener(notification, listener); - } - }; + final NetconfDeviceNotificationService notificationService = new NetconfDeviceNotificationService(); - salProvider.getMountInstance().onDeviceConnected(schemaContext, domBroker, rpcRegistry, notificationService); + salProvider.getMountInstance().onDeviceConnected(schemaContext, domBroker, deviceRpc, notificationService); salProvider.getDatastoreAdapter().updateDeviceState(true, netconfSessionPreferences.getModuleBasedCaps()); - salProvider.getMountInstance().onTopologyDeviceConnected(schemaContext, domBroker, rpcRegistry, notificationService); + salProvider.getMountInstance().onTopologyDeviceConnected(schemaContext, domBroker, deviceRpc, notificationService); salProvider.getTopologyDatastoreAdapter().updateDeviceData(true, netconfSessionPreferences.getNetconfDeviceCapabilities()); } @@ -107,35 +73,12 @@ public final class NetconfDeviceSalFacade implements AutoCloseable, RemoteDevice } @Override - public void onDeviceFailed(Throwable throwable) { + public void onDeviceFailed(final Throwable throwable) { salProvider.getTopologyDatastoreAdapter().setDeviceAsFailed(throwable); salProvider.getMountInstance().onDeviceDisconnected(); salProvider.getMountInstance().onTopologyDeviceDisconnected(); } - private void registerRpcsToSal(final SchemaContext schemaContext, final RpcProvisionRegistry rpcRegistry, final RpcImplementation deviceRpc) { - final Map failedRpcs = Maps.newHashMap(); - for (final RpcDefinition rpcDef : schemaContext.getOperations()) { - try { - salRegistrations.add(rpcRegistry.addRpcImplementation(rpcDef.getQName(), deviceRpc)); - logger.debug("{}: Rpc {} from netconf registered successfully", id, rpcDef.getQName()); - } catch (final Exception e) { - // Only debug per rpc, warn for all of them at the end to pollute log a little less (e.g. routed rpcs) - logger.debug("{}: Unable to register rpc {} from netconf device. This rpc will not be available", id, - rpcDef.getQName(), e); - failedRpcs.put(rpcDef.getQName(), e.getClass() + ":" + e.getMessage()); - } - } - - if (failedRpcs.isEmpty() == false) { - if (logger.isDebugEnabled()) { - logger.warn("{}: Some rpcs from netconf device were not registered: {}", id, failedRpcs); - } else { - logger.warn("{}: Some rpcs from netconf device were not registered: {}", id, failedRpcs.keySet()); - } - } - } - @Override public void close() { for (final AutoCloseable reg : Lists.reverse(salRegistrations)) { @@ -153,4 +96,5 @@ public final class NetconfDeviceSalFacade implements AutoCloseable, RemoteDevice } } } + } diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceSalProvider.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceSalProvider.java index 568ebde0d3..3246f51904 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceSalProvider.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceSalProvider.java @@ -10,20 +10,19 @@ package org.opendaylight.controller.sal.connect.netconf.sal; import com.google.common.base.Preconditions; import java.util.Collection; import java.util.Collections; -import java.util.concurrent.ExecutorService; import org.opendaylight.controller.md.sal.binding.api.DataBroker; import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker; import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint; import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService; +import org.opendaylight.controller.md.sal.dom.api.DOMNotificationService; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcService; import org.opendaylight.controller.sal.binding.api.BindingAwareBroker; import org.opendaylight.controller.sal.binding.api.BindingAwareProvider; import org.opendaylight.controller.sal.connect.util.RemoteDeviceId; import org.opendaylight.controller.sal.core.api.Broker; import org.opendaylight.controller.sal.core.api.Provider; -import org.opendaylight.controller.sal.core.api.RpcProvisionRegistry; -import org.opendaylight.controller.sal.core.api.notify.NotificationPublishService; import org.opendaylight.yangtools.concepts.ObjectRegistration; -import org.opendaylight.yangtools.yang.data.api.CompositeNode; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; import org.opendaylight.yangtools.yang.model.api.SchemaContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -33,15 +32,13 @@ final class NetconfDeviceSalProvider implements AutoCloseable, Provider, Binding private static final Logger logger = LoggerFactory.getLogger(NetconfDeviceSalProvider.class); private final RemoteDeviceId id; - private final ExecutorService executor; private volatile NetconfDeviceDatastoreAdapter datastoreAdapter; private MountInstance mountInstance; private volatile NetconfDeviceTopologyAdapter topologyDatastoreAdapter; - public NetconfDeviceSalProvider(final RemoteDeviceId deviceId, final ExecutorService executor) { + public NetconfDeviceSalProvider(final RemoteDeviceId deviceId) { this.id = deviceId; - this.executor = executor; } public MountInstance getMountInstance() { @@ -100,7 +97,7 @@ final class NetconfDeviceSalProvider implements AutoCloseable, Provider, Binding private DOMMountPointService mountService; private final RemoteDeviceId id; private ObjectRegistration registration; - private NotificationPublishService notificationSerivce; + private NetconfDeviceNotificationService notificationService; private ObjectRegistration topologyRegistration; @@ -111,8 +108,8 @@ final class NetconfDeviceSalProvider implements AutoCloseable, Provider, Binding @Deprecated synchronized void onDeviceConnected(final SchemaContext initialCtx, - final DOMDataBroker broker, final RpcProvisionRegistry rpc, - final NotificationPublishService notificationSerivce) { + final DOMDataBroker broker, final DOMRpcService rpc, + final NetconfDeviceNotificationService notificationService) { Preconditions.checkNotNull(mountService, "Closed"); Preconditions.checkState(registration == null, "Already initialized"); @@ -121,9 +118,9 @@ final class NetconfDeviceSalProvider implements AutoCloseable, Provider, Binding mountBuilder.addInitialSchemaContext(initialCtx); mountBuilder.addService(DOMDataBroker.class, broker); - mountBuilder.addService(RpcProvisionRegistry.class, rpc); - this.notificationSerivce = notificationSerivce; - mountBuilder.addService(NotificationPublishService.class, notificationSerivce); + mountBuilder.addService(DOMRpcService.class, rpc); + mountBuilder.addService(DOMNotificationService.class, notificationService); + this.notificationService = notificationService; registration = mountBuilder.register(); } @@ -145,8 +142,8 @@ final class NetconfDeviceSalProvider implements AutoCloseable, Provider, Binding } synchronized void onTopologyDeviceConnected(final SchemaContext initialCtx, - final DOMDataBroker broker, final RpcProvisionRegistry rpc, - final NotificationPublishService notificationSerivce) { + final DOMDataBroker broker, final DOMRpcService rpc, + final NetconfDeviceNotificationService notificationService) { Preconditions.checkNotNull(mountService, "Closed"); Preconditions.checkState(topologyRegistration == null, "Already initialized"); @@ -155,9 +152,8 @@ final class NetconfDeviceSalProvider implements AutoCloseable, Provider, Binding mountBuilder.addInitialSchemaContext(initialCtx); mountBuilder.addService(DOMDataBroker.class, broker); - mountBuilder.addService(RpcProvisionRegistry.class, rpc); - this.notificationSerivce = notificationSerivce; - mountBuilder.addService(NotificationPublishService.class, notificationSerivce); + mountBuilder.addService(DOMRpcService.class, rpc); + mountBuilder.addService(DOMNotificationService.class, notificationService); topologyRegistration = mountBuilder.register(); } @@ -186,9 +182,9 @@ final class NetconfDeviceSalProvider implements AutoCloseable, Provider, Binding mountService = null; } - public synchronized void publish(final CompositeNode domNotification) { - Preconditions.checkNotNull(notificationSerivce, "Device not set up yet, cannot handle notification {}", domNotification); - notificationSerivce.publish(domNotification); + public synchronized void publish(final ContainerNode domNotification) { + Preconditions.checkNotNull(notificationService, "Device not set up yet, cannot handle notification {}", domNotification); + notificationService.publishNotification(domNotification); } } diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceTopologyAdapter.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceTopologyAdapter.java index 83664e440f..24b6205cdc 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceTopologyAdapter.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceTopologyAdapter.java @@ -153,9 +153,8 @@ final class NetconfDeviceTopologyAdapter implements AutoCloseable { .setUnavailableCapabilities(unavailableCapabilities); final NodeBuilder nodeBuilder = getNodeIdBuilder(id); - final Node node = nodeBuilder.addAugmentation(NetconfNode.class, netconfNodeBuilder.build()).build(); - return node; + return nodeBuilder.addAugmentation(NetconfNode.class, netconfNodeBuilder.build()).build(); } public void removeDeviceConfiguration() { diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/AbstractWriteTx.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/AbstractWriteTx.java index 435ef9915d..ee0a774dfd 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/AbstractWriteTx.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/AbstractWriteTx.java @@ -1,7 +1,5 @@ package org.opendaylight.controller.sal.connect.netconf.sal.tx; -import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.createEditConfigStructure; - import com.google.common.base.Function; import com.google.common.base.Optional; import com.google.common.base.Preconditions; @@ -11,34 +9,39 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import org.opendaylight.controller.md.sal.common.api.TransactionStatus; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; -import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizer; import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult; import org.opendaylight.controller.netconf.api.NetconfDocumentedException; import org.opendaylight.controller.sal.connect.netconf.listener.NetconfSessionPreferences; import org.opendaylight.controller.sal.connect.netconf.util.NetconfBaseOps; import org.opendaylight.controller.sal.connect.util.RemoteDeviceId; import org.opendaylight.yangtools.yang.common.RpcResult; -import org.opendaylight.yangtools.yang.data.api.CompositeNode; import org.opendaylight.yangtools.yang.data.api.ModifyAction; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; public abstract class AbstractWriteTx implements DOMDataWriteTransaction { + + private static final long DEFAULT_REQUEST_TIMEOUT_MINUTES = 1L; + protected final RemoteDeviceId id; protected final NetconfBaseOps netOps; - protected final DataNormalizer normalizer; protected final NetconfSessionPreferences netconfSessionPreferences; // Allow commit to be called only once protected boolean finished = false; - public AbstractWriteTx(final NetconfBaseOps netOps, final RemoteDeviceId id, final DataNormalizer normalizer, final NetconfSessionPreferences netconfSessionPreferences) { + public AbstractWriteTx(final NetconfBaseOps netOps, final RemoteDeviceId id, final NetconfSessionPreferences netconfSessionPreferences) { this.netOps = netOps; this.id = id; - this.normalizer = normalizer; this.netconfSessionPreferences = netconfSessionPreferences; init(); } + static boolean isSuccess(final DOMRpcResult result) { + return result.getErrors().isEmpty(); + } + protected void checkNotFinished() { Preconditions.checkState(!isFinished(), "%s: Transaction %s already finished", id, getIdentifier()); } @@ -47,10 +50,10 @@ public abstract class AbstractWriteTx implements DOMDataWriteTransaction { return finished; } - protected void invokeBlocking(final String msg, final Function>> op) throws NetconfDocumentedException { + protected void invokeBlocking(final String msg, final Function> op) throws NetconfDocumentedException { try { - final RpcResult compositeNodeRpcResult = op.apply(netOps).get(1L, TimeUnit.MINUTES); - if(compositeNodeRpcResult.isSuccessful() == false) { + final DOMRpcResult compositeNodeRpcResult = op.apply(netOps).get(DEFAULT_REQUEST_TIMEOUT_MINUTES, TimeUnit.MINUTES); + if(isSuccess(compositeNodeRpcResult) == false) { throw new NetconfDocumentedException(id + ": " + msg + " failed: " + compositeNodeRpcResult.getErrors(), NetconfDocumentedException.ErrorType.application, NetconfDocumentedException.ErrorTag.operation_failed, NetconfDocumentedException.ErrorSeverity.warning); } @@ -88,10 +91,8 @@ public abstract class AbstractWriteTx implements DOMDataWriteTransaction { checkEditable(store); try { - final YangInstanceIdentifier legacyPath = ReadOnlyTx.toLegacyPath(normalizer, path, id); - final CompositeNode legacyData = normalizer.toLegacy(path, data); editConfig( - createEditConfigStructure(legacyPath, Optional.of(ModifyAction.REPLACE), Optional.fromNullable(legacyData)), Optional.of(ModifyAction.NONE)); + netOps.createEditConfigStrcture(Optional.>fromNullable(data), Optional.of(ModifyAction.REPLACE), path), Optional.of(ModifyAction.NONE)); } catch (final NetconfDocumentedException e) { handleEditException(path, data, e, "putting"); } @@ -105,10 +106,8 @@ public abstract class AbstractWriteTx implements DOMDataWriteTransaction { checkEditable(store); try { - final YangInstanceIdentifier legacyPath = ReadOnlyTx.toLegacyPath(normalizer, path, id); - final CompositeNode legacyData = normalizer.toLegacy(path, data); editConfig( - createEditConfigStructure(legacyPath, Optional.absent(), Optional.fromNullable(legacyData)), Optional.absent()); + netOps.createEditConfigStrcture(Optional.>fromNullable(data), Optional.absent(), path), Optional.absent()); } catch (final NetconfDocumentedException e) { handleEditException(path, data, e, "merge"); } @@ -119,9 +118,8 @@ public abstract class AbstractWriteTx implements DOMDataWriteTransaction { checkEditable(store); try { - editConfig(createEditConfigStructure( - ReadOnlyTx.toLegacyPath(normalizer, path, id), Optional.of(ModifyAction.DELETE), - Optional.absent()), Optional.of(ModifyAction.NONE)); + editConfig( + netOps.createEditConfigStrcture(Optional.>absent(), Optional.of(ModifyAction.DELETE), path), Optional.of(ModifyAction.NONE)); } catch (final NetconfDocumentedException e) { handleDeleteException(path, e); } @@ -142,5 +140,5 @@ public abstract class AbstractWriteTx implements DOMDataWriteTransaction { Preconditions.checkArgument(store == LogicalDatastoreType.CONFIGURATION, "Can edit only configuration data, not %s", store); } - protected abstract void editConfig(CompositeNode editStructure, Optional defaultOperation) throws NetconfDocumentedException; + protected abstract void editConfig(DataContainerChild editStructure, Optional defaultOperation) throws NetconfDocumentedException; } diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/ReadOnlyTx.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/ReadOnlyTx.java index 00bdbb6db0..e08731ed40 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/ReadOnlyTx.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/ReadOnlyTx.java @@ -7,8 +7,6 @@ */ package org.opendaylight.controller.sal.connect.netconf.sal.tx; -import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_DATA_QNAME; - import com.google.common.base.Function; import com.google.common.base.Optional; import com.google.common.base.Preconditions; @@ -19,17 +17,17 @@ import com.google.common.util.concurrent.ListenableFuture; import java.util.concurrent.ExecutionException; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; -import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizationException; -import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizer; import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult; import org.opendaylight.controller.sal.connect.netconf.util.NetconfBaseOps; import org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil; import org.opendaylight.controller.sal.connect.util.RemoteDeviceId; import org.opendaylight.yangtools.util.concurrent.MappingCheckedFuture; -import org.opendaylight.yangtools.yang.common.RpcResult; -import org.opendaylight.yangtools.yang.data.api.CompositeNode; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; +import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -39,19 +37,18 @@ public final class ReadOnlyTx implements DOMDataReadOnlyTransaction { private static final Logger LOG = LoggerFactory.getLogger(ReadOnlyTx.class); private final NetconfBaseOps netconfOps; - private final DataNormalizer normalizer; private final RemoteDeviceId id; - private final FutureCallback> loggingCallback; + private final FutureCallback loggingCallback; - public ReadOnlyTx(final NetconfBaseOps netconfOps, final DataNormalizer normalizer, final RemoteDeviceId id) { + public ReadOnlyTx(final NetconfBaseOps netconfOps, final RemoteDeviceId id) { this.netconfOps = netconfOps; - this.normalizer = normalizer; this.id = id; + // Simple logging callback to log result of read operation - loggingCallback = new FutureCallback>() { + loggingCallback = new FutureCallback() { @Override - public void onSuccess(final RpcResult result) { - if(result.isSuccessful()) { + public void onSuccess(final DOMRpcResult result) { + if(AbstractWriteTx.isSuccess(result)) { LOG.trace("{}: Reading data successful", id); } else { LOG.warn("{}: Reading data unsuccessful: {}", id, result.getErrors()); @@ -68,62 +65,46 @@ public final class ReadOnlyTx implements DOMDataReadOnlyTransaction { private CheckedFuture>, ReadFailedException> readConfigurationData( final YangInstanceIdentifier path) { - final ListenableFuture> configRunning = netconfOps.getConfigRunning(loggingCallback, Optional.fromNullable(path)); - // Find data node and normalize its content - final ListenableFuture>> transformedFuture = Futures.transform(configRunning, new Function, Optional>>() { + final ListenableFuture configRunning = netconfOps.getConfigRunning(loggingCallback, Optional.fromNullable(path)); + + final ListenableFuture>> transformedFuture = Futures.transform(configRunning, new Function>>() { @Override - public Optional> apply(final RpcResult result) { + public Optional> apply(final DOMRpcResult result) { checkReadSuccess(result, path); - final CompositeNode data = result.getResult().getFirstCompositeByName(NETCONF_DATA_QNAME); - final CompositeNode node = (CompositeNode) NetconfMessageTransformUtil.findNode(data, path); - - return data == null ? - Optional.>absent() : - transform(path, node); + final DataContainerChild dataNode = findDataNode(result); + return NormalizedNodes.findNode(dataNode, path.getPathArguments()); } }); return MappingCheckedFuture.create(transformedFuture, ReadFailedException.MAPPER); } - private void checkReadSuccess(final RpcResult result, final YangInstanceIdentifier path) { - try { - Preconditions.checkArgument(result.isSuccessful(), "%s: Unable to read data: %s, errors: %s", id, path, result.getErrors()); - } catch (final IllegalArgumentException e) { - LOG.warn("{}: Unable to read data: {}, errors: {}", id, path, result.getErrors()); - throw e; - } + private DataContainerChild findDataNode(final DOMRpcResult result) { + return ((ContainerNode) result.getResult()).getChild(NetconfMessageTransformUtil.toId(NetconfMessageTransformUtil.NETCONF_DATA_QNAME)).get(); } - private Optional> transform(final YangInstanceIdentifier path, final CompositeNode node) { - if(node == null) { - return Optional.absent(); - } + private void checkReadSuccess(final DOMRpcResult result, final YangInstanceIdentifier path) { try { - return Optional.>of(normalizer.toNormalized(path, node).getValue()); - } catch (final Exception e) { - LOG.error("{}: Unable to normalize data for {}, data: {}", id, path, node, e); + Preconditions.checkArgument(AbstractWriteTx.isSuccess(result), "%s: Unable to read data: %s, errors: %s", id, path, result.getErrors()); + } catch (final IllegalArgumentException e) { + LOG.warn("{}: Unable to read data: {}, errors: {}", id, path, result.getErrors()); throw e; } } private CheckedFuture>, ReadFailedException> readOperationalData( final YangInstanceIdentifier path) { - final ListenableFuture> configCandidate = netconfOps.get(loggingCallback, Optional.fromNullable(path)); + final ListenableFuture configCandidate = netconfOps.get(loggingCallback, Optional.fromNullable(path)); // Find data node and normalize its content - final ListenableFuture>> transformedFuture = Futures.transform(configCandidate, new Function, Optional>>() { + final ListenableFuture>> transformedFuture = Futures.transform(configCandidate, new Function>>() { @Override - public Optional> apply(final RpcResult result) { + public Optional> apply(final DOMRpcResult result) { checkReadSuccess(result, path); - final CompositeNode data = result.getResult().getFirstCompositeByName(NETCONF_DATA_QNAME); - final CompositeNode node = (CompositeNode) NetconfMessageTransformUtil.findNode(data, path); - - return data == null ? - Optional.>absent() : - transform(path, node); + final DataContainerChild dataNode = findDataNode(result); + return NormalizedNodes.findNode(dataNode, path.getPathArguments()); } }); @@ -138,14 +119,12 @@ public final class ReadOnlyTx implements DOMDataReadOnlyTransaction { @Override public CheckedFuture>, ReadFailedException> read( final LogicalDatastoreType store, final YangInstanceIdentifier path) { - final YangInstanceIdentifier legacyPath = toLegacyPath(normalizer, path, id); - switch (store) { case CONFIGURATION : { - return readConfigurationData(legacyPath); + return readConfigurationData(path); } case OPERATIONAL : { - return readOperationalData(legacyPath); + return readOperationalData(path); } } @@ -154,8 +133,7 @@ public final class ReadOnlyTx implements DOMDataReadOnlyTransaction { @Override public CheckedFuture exists(final LogicalDatastoreType store, final YangInstanceIdentifier path) { - final CheckedFuture>, ReadFailedException> - data = read(store, path); + final CheckedFuture>, ReadFailedException> data = read(store, path); try { return Futures.immediateCheckedFuture(data.get().isPresent()); @@ -164,14 +142,6 @@ public final class ReadOnlyTx implements DOMDataReadOnlyTransaction { } } - static YangInstanceIdentifier toLegacyPath(final DataNormalizer normalizer, final YangInstanceIdentifier path, final RemoteDeviceId id) { - try { - return normalizer.toLegacy(path); - } catch (final DataNormalizationException e) { - throw new IllegalArgumentException(id + ": Cannot normalize path " + path, e); - } - } - @Override public Object getIdentifier() { return this; diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/WriteCandidateRunningTx.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/WriteCandidateRunningTx.java index 710700b362..33ca5f469b 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/WriteCandidateRunningTx.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/WriteCandidateRunningTx.java @@ -10,14 +10,12 @@ package org.opendaylight.controller.sal.connect.netconf.sal.tx; import com.google.common.base.Function; import com.google.common.util.concurrent.ListenableFuture; -import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizer; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult; import org.opendaylight.controller.netconf.api.NetconfDocumentedException; import org.opendaylight.controller.sal.connect.netconf.listener.NetconfSessionPreferences; import org.opendaylight.controller.sal.connect.netconf.util.NetconfBaseOps; import org.opendaylight.controller.sal.connect.netconf.util.NetconfRpcFutureCallback; import org.opendaylight.controller.sal.connect.util.RemoteDeviceId; -import org.opendaylight.yangtools.yang.common.RpcResult; -import org.opendaylight.yangtools.yang.data.api.CompositeNode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -32,8 +30,8 @@ public class WriteCandidateRunningTx extends WriteCandidateTx { private static final Logger LOG = LoggerFactory.getLogger(WriteCandidateRunningTx.class); - public WriteCandidateRunningTx(final RemoteDeviceId id, final NetconfBaseOps netOps, final DataNormalizer normalizer, final NetconfSessionPreferences netconfSessionPreferences) { - super(id, netOps, normalizer, netconfSessionPreferences); + public WriteCandidateRunningTx(final RemoteDeviceId id, final NetconfBaseOps netOps, final NetconfSessionPreferences netconfSessionPreferences) { + super(id, netOps, netconfSessionPreferences); } @Override @@ -50,9 +48,9 @@ public class WriteCandidateRunningTx extends WriteCandidateTx { private void lockRunning() { try { - invokeBlocking("Lock running", new Function>>() { + invokeBlocking("Lock running", new Function>() { @Override - public ListenableFuture> apply(final NetconfBaseOps input) { + public ListenableFuture apply(final NetconfBaseOps input) { return input.lockRunning(new NetconfRpcFutureCallback("Lock running", id)); } }); diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/WriteCandidateTx.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/WriteCandidateTx.java index f9bf3c75fd..cad65ff0be 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/WriteCandidateTx.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/WriteCandidateTx.java @@ -15,7 +15,7 @@ import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import org.opendaylight.controller.md.sal.common.api.TransactionStatus; import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; -import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizer; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult; import org.opendaylight.controller.netconf.api.NetconfDocumentedException; import org.opendaylight.controller.sal.connect.netconf.listener.NetconfSessionPreferences; import org.opendaylight.controller.sal.connect.netconf.util.NetconfBaseOps; @@ -24,9 +24,9 @@ import org.opendaylight.controller.sal.connect.util.RemoteDeviceId; import org.opendaylight.yangtools.yang.common.RpcError; import org.opendaylight.yangtools.yang.common.RpcResult; import org.opendaylight.yangtools.yang.common.RpcResultBuilder; -import org.opendaylight.yangtools.yang.data.api.CompositeNode; import org.opendaylight.yangtools.yang.data.api.ModifyAction; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -53,10 +53,10 @@ public class WriteCandidateTx extends AbstractWriteTx { private static final Logger LOG = LoggerFactory.getLogger(WriteCandidateTx.class); - private static final Function, RpcResult> RPC_RESULT_TO_TX_STATUS = new Function, RpcResult>() { + private static final Function> RPC_RESULT_TO_TX_STATUS = new Function>() { @Override - public RpcResult apply(final RpcResult input) { - if (input.isSuccessful()) { + public RpcResult apply(final DOMRpcResult input) { + if (isSuccess(input)) { return RpcResultBuilder.success(TransactionStatus.COMMITED).build(); } else { final RpcResultBuilder failed = RpcResultBuilder.failed(); @@ -69,8 +69,8 @@ public class WriteCandidateTx extends AbstractWriteTx { } }; - public WriteCandidateTx(final RemoteDeviceId id, final NetconfBaseOps rpc, final DataNormalizer normalizer, final NetconfSessionPreferences netconfSessionPreferences) { - super(rpc, id, normalizer, netconfSessionPreferences); + public WriteCandidateTx(final RemoteDeviceId id, final NetconfBaseOps rpc, final NetconfSessionPreferences netconfSessionPreferences) { + super(rpc, id, netconfSessionPreferences); } @Override @@ -94,9 +94,9 @@ public class WriteCandidateTx extends AbstractWriteTx { private void lock() throws NetconfDocumentedException { try { - invokeBlocking("Lock candidate", new Function>>() { + invokeBlocking("Lock candidate", new Function>() { @Override - public ListenableFuture> apply(final NetconfBaseOps input) { + public ListenableFuture apply(final NetconfBaseOps input) { return input.lockCandidate(new NetconfRpcFutureCallback("Lock candidate", id)); } }); @@ -152,16 +152,16 @@ public class WriteCandidateTx extends AbstractWriteTx { @Override public synchronized ListenableFuture> performCommit() { - final ListenableFuture> rpcResult = netOps.commit(new NetconfRpcFutureCallback("Commit", id) { + final ListenableFuture rpcResult = netOps.commit(new NetconfRpcFutureCallback("Commit", id) { @Override - public void onSuccess(final RpcResult result) { + public void onSuccess(final DOMRpcResult result) { super.onSuccess(result); LOG.debug("{}: Write successful, transaction: {}. Unlocking", id, getIdentifier()); cleanupOnSuccess(); } @Override - protected void onUnsuccess(final RpcResult result) { + protected void onUnsuccess(final DOMRpcResult result) { LOG.error("{}: Write failed, transaction {}, discarding changes, unlocking: {}", id, getIdentifier(), result.getErrors()); cleanup(); } @@ -181,10 +181,10 @@ public class WriteCandidateTx extends AbstractWriteTx { } @Override - protected void editConfig(final CompositeNode editStructure, final Optional defaultOperation) throws NetconfDocumentedException { - invokeBlocking("Edit candidate", new Function>>() { + protected void editConfig(final DataContainerChild editStructure, final Optional defaultOperation) throws NetconfDocumentedException { + invokeBlocking("Edit candidate", new Function>() { @Override - public ListenableFuture> apply(final NetconfBaseOps input) { + public ListenableFuture apply(final NetconfBaseOps input) { return defaultOperation.isPresent() ? input.editConfigCandidate(new NetconfRpcFutureCallback("Edit candidate", id), editStructure, defaultOperation.get(), netconfSessionPreferences.isRollbackSupported()) diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/WriteRunningTx.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/WriteRunningTx.java index f92e40fb57..00236803eb 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/WriteRunningTx.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/WriteRunningTx.java @@ -15,7 +15,7 @@ import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import org.opendaylight.controller.md.sal.common.api.TransactionStatus; import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; -import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizer; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult; import org.opendaylight.controller.netconf.api.NetconfDocumentedException; import org.opendaylight.controller.sal.connect.netconf.listener.NetconfSessionPreferences; import org.opendaylight.controller.sal.connect.netconf.util.NetconfBaseOps; @@ -23,9 +23,9 @@ import org.opendaylight.controller.sal.connect.netconf.util.NetconfRpcFutureCall import org.opendaylight.controller.sal.connect.util.RemoteDeviceId; import org.opendaylight.yangtools.yang.common.RpcResult; import org.opendaylight.yangtools.yang.common.RpcResultBuilder; -import org.opendaylight.yangtools.yang.data.api.CompositeNode; import org.opendaylight.yangtools.yang.data.api.ModifyAction; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -50,8 +50,8 @@ public class WriteRunningTx extends AbstractWriteTx { private static final Logger LOG = LoggerFactory.getLogger(WriteRunningTx.class); public WriteRunningTx(final RemoteDeviceId id, final NetconfBaseOps netOps, - final DataNormalizer normalizer, final NetconfSessionPreferences netconfSessionPreferences) { - super(netOps, id, normalizer, netconfSessionPreferences); + final NetconfSessionPreferences netconfSessionPreferences) { + super(netOps, id, netconfSessionPreferences); } @Override @@ -61,9 +61,9 @@ public class WriteRunningTx extends AbstractWriteTx { private void lock() { try { - invokeBlocking("Lock running", new Function>>() { + invokeBlocking("Lock running", new Function>() { @Override - public ListenableFuture> apply(final NetconfBaseOps input) { + public ListenableFuture apply(final NetconfBaseOps input) { return input.lockRunning(new NetconfRpcFutureCallback("Lock running", id)); } }); @@ -117,10 +117,10 @@ public class WriteRunningTx extends AbstractWriteTx { } @Override - protected void editConfig(final CompositeNode editStructure, final Optional defaultOperation) throws NetconfDocumentedException { - invokeBlocking("Edit running", new Function>>() { + protected void editConfig(final DataContainerChild editStructure, final Optional defaultOperation) throws NetconfDocumentedException { + invokeBlocking("Edit running", new Function>() { @Override - public ListenableFuture> apply(final NetconfBaseOps input) { + public ListenableFuture apply(final NetconfBaseOps input) { return defaultOperation.isPresent() ? input.editConfigRunning(new NetconfRpcFutureCallback("Edit running", id), editStructure, defaultOperation.get(), netconfSessionPreferences.isRollbackSupported()) @@ -132,9 +132,9 @@ public class WriteRunningTx extends AbstractWriteTx { private void unlock() { try { - invokeBlocking("Unlocking running", new Function>>() { + invokeBlocking("Unlocking running", new Function>() { @Override - public ListenableFuture> apply(final NetconfBaseOps input) { + public ListenableFuture apply(final NetconfBaseOps input) { return input.unlockRunning(new NetconfRpcFutureCallback("Unlock running", id)); } }); diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/schema/NetconfRemoteSchemaYangSourceProvider.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/schema/NetconfRemoteSchemaYangSourceProvider.java index 3565ba1113..fdb1d3d7a6 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/schema/NetconfRemoteSchemaYangSourceProvider.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/schema/NetconfRemoteSchemaYangSourceProvider.java @@ -7,6 +7,10 @@ */ package org.opendaylight.controller.sal.connect.netconf.schema; +import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.GET_SCHEMA_QNAME; +import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_DATA_QNAME; +import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.toId; + import com.google.common.base.Function; import com.google.common.base.MoreObjects; import com.google.common.base.Optional; @@ -17,16 +21,22 @@ import com.google.common.util.concurrent.ListenableFuture; import java.io.IOException; import java.io.InputStream; import org.apache.commons.io.IOUtils; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcService; import org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil; import org.opendaylight.controller.sal.connect.util.RemoteDeviceId; -import org.opendaylight.controller.sal.core.api.RpcImplementation; import org.opendaylight.yangtools.util.concurrent.ExceptionMapper; import org.opendaylight.yangtools.yang.common.QName; -import org.opendaylight.yangtools.yang.common.RpcResult; -import org.opendaylight.yangtools.yang.data.api.CompositeNode; -import org.opendaylight.yangtools.yang.data.api.SimpleNode; -import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode; -import org.opendaylight.yangtools.yang.data.impl.util.CompositeNodeBuilder; +import org.opendaylight.yangtools.yang.data.api.Node; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.AnyXmlNode; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; +import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild; +import org.opendaylight.yangtools.yang.data.api.schema.LeafNode; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.impl.schema.Builders; +import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder; +import org.opendaylight.yangtools.yang.model.api.SchemaPath; import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceException; import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier; import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource; @@ -36,9 +46,6 @@ import org.slf4j.LoggerFactory; public final class NetconfRemoteSchemaYangSourceProvider implements SchemaSourceProvider { - public static final QName GET_SCHEMA_QNAME = QName.create(NetconfMessageTransformUtil.IETF_NETCONF_MONITORING,"get-schema"); - public static final QName GET_DATA_QNAME = QName.create(NetconfMessageTransformUtil.IETF_NETCONF_MONITORING, "data"); - private static final Logger logger = LoggerFactory.getLogger(NetconfRemoteSchemaYangSourceProvider.class); private static final ExceptionMapper MAPPER = new ExceptionMapper( @@ -49,35 +56,55 @@ public final class NetconfRemoteSchemaYangSourceProvider implements SchemaSource } }; - private final RpcImplementation rpc; + private final DOMRpcService rpc; private final RemoteDeviceId id; - public NetconfRemoteSchemaYangSourceProvider(final RemoteDeviceId id, final RpcImplementation rpc) { + public NetconfRemoteSchemaYangSourceProvider(final RemoteDeviceId id, final DOMRpcService rpc) { this.id = id; this.rpc = Preconditions.checkNotNull(rpc); } - private ImmutableCompositeNode createGetSchemaRequest(final String moduleName, final Optional revision) { - final CompositeNodeBuilder request = ImmutableCompositeNode.builder(); - request.setQName(GET_SCHEMA_QNAME).addLeaf("identifier", moduleName); - if (revision.isPresent()) { - request.addLeaf("version", revision.get()); + public static ContainerNode createGetSchemaRequest(final String moduleName, final Optional revision) { + final QName identifierQName = QName.cachedReference(QName.create(NetconfMessageTransformUtil.GET_SCHEMA_QNAME, "identifier")); + final YangInstanceIdentifier.NodeIdentifier identifierId = new YangInstanceIdentifier.NodeIdentifier(identifierQName); + final LeafNode identifier = Builders.leafBuilder().withNodeIdentifier(identifierId).withValue(moduleName).build(); + + final QName formatQName = QName.cachedReference(QName.create(NetconfMessageTransformUtil.GET_SCHEMA_QNAME, "format")); + final YangInstanceIdentifier.NodeIdentifier formatId = new YangInstanceIdentifier.NodeIdentifier(formatQName); + final LeafNode format = Builders.leafBuilder().withNodeIdentifier(formatId).withValue("yang").build(); + + final DataContainerNodeAttrBuilder builder = Builders.containerBuilder(); + + builder.withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(NetconfMessageTransformUtil.GET_SCHEMA_QNAME)) + .withChild(identifier) + .withChild(format); + + if(revision.isPresent()) { + final QName revisionQName = QName.cachedReference(QName.create(NetconfMessageTransformUtil.GET_SCHEMA_QNAME, "version")); + final YangInstanceIdentifier.NodeIdentifier revisionId = new YangInstanceIdentifier.NodeIdentifier(revisionQName); + final LeafNode revisionNode = Builders.leafBuilder().withNodeIdentifier(revisionId).withValue(revision.get()).build(); + + builder.withChild(revisionNode); } - request.addLeaf("format", "yang"); - return request.toInstance(); + + return builder.build(); } - private static Optional getSchemaFromRpc(final RemoteDeviceId id, final CompositeNode result) { + private static Optional getSchemaFromRpc(final RemoteDeviceId id, final NormalizedNode result) { if (result == null) { return Optional.absent(); } - final SimpleNode simpleNode = result.getFirstSimpleByName(GET_DATA_QNAME.withoutRevision()); - Preconditions.checkNotNull(simpleNode, + final QName schemaWrapperNode = QName.cachedReference(QName.create(GET_SCHEMA_QNAME, NETCONF_DATA_QNAME.getLocalName())); + final Optional> child = ((ContainerNode) result).getChild(toId(schemaWrapperNode)); + + Preconditions.checkState(child.isPresent() && child.get() instanceof AnyXmlNode, "%s Unexpected response to get-schema, expected response with one child %s, but was %s", id, - GET_DATA_QNAME.withoutRevision(), result); + schemaWrapperNode, result); + + final Node wrappedNode = (Node) child.get().getValue(); + final Object potential = wrappedNode.getValue(); - final Object potential = simpleNode.getValue(); return potential instanceof String ? Optional.of((String) potential) : Optional. absent(); } @@ -88,18 +115,19 @@ public final class NetconfRemoteSchemaYangSourceProvider implements SchemaSource // If formatted revision is SourceIdentifier.NOT_PRESENT_FORMATTED_REVISION, we have to omit it from request final String formattedRevision = sourceIdentifier.getRevision().equals(SourceIdentifier.NOT_PRESENT_FORMATTED_REVISION) ? null : sourceIdentifier.getRevision(); final Optional revision = Optional.fromNullable(formattedRevision); - final ImmutableCompositeNode getSchemaRequest = createGetSchemaRequest(moduleName, revision); + final NormalizedNode getSchemaRequest = createGetSchemaRequest(moduleName, revision); logger.trace("{}: Loading YANG schema source for {}:{}", id, moduleName, revision); final ListenableFuture transformed = Futures.transform( - rpc.invokeRpc(GET_SCHEMA_QNAME, getSchemaRequest), + rpc.invokeRpc(SchemaPath.create(true, NetconfMessageTransformUtil.GET_SCHEMA_QNAME), getSchemaRequest), new ResultToYangSourceTransformer(id, sourceIdentifier, moduleName, revision)); final CheckedFuture checked = Futures.makeChecked(transformed, MAPPER); // / FIXME remove this get, it is only present to wait until source is retrieved // (goal is to limit concurrent schema download, since NetconfDevice listener does not handle concurrent messages properly) + // TODO retest this try { logger.trace("{}: Blocking for {}", id, sourceIdentifier); checked.checkedGet(); @@ -114,7 +142,7 @@ public final class NetconfRemoteSchemaYangSourceProvider implements SchemaSource * Transform composite node to string schema representation and then to ASTSchemaSource */ private static final class ResultToYangSourceTransformer implements - Function, YangTextSchemaSource> { + Function { private final RemoteDeviceId id; private final SourceIdentifier sourceIdentifier; @@ -130,9 +158,9 @@ public final class NetconfRemoteSchemaYangSourceProvider implements SchemaSource } @Override - public YangTextSchemaSource apply(final RpcResult input) { + public YangTextSchemaSource apply(final DOMRpcResult input) { - if (input.isSuccessful()) { + if (input.getErrors().isEmpty()) { final Optional schemaString = getSchemaFromRpc(id, input.getResult()); @@ -140,7 +168,6 @@ public final class NetconfRemoteSchemaYangSourceProvider implements SchemaSource "%s: Unexpected response to get-schema, schema not present in message for: %s", id, sourceIdentifier); logger.debug("{}: YANG Schema successfully retrieved for {}:{}", id, moduleName, revision); - return new NetconfYangTextSchemaSource(id, sourceIdentifier, schemaString); } @@ -150,7 +177,6 @@ public final class NetconfRemoteSchemaYangSourceProvider implements SchemaSource throw new IllegalStateException(String.format( "%s: YANG schema was not successfully retrieved for %s. Errors: %s", id, sourceIdentifier, input.getErrors())); - } } diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/schema/mapping/NetconfMessageTransformer.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/schema/mapping/NetconfMessageTransformer.java index 2971865a70..cfb302d871 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/schema/mapping/NetconfMessageTransformer.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/schema/mapping/NetconfMessageTransformer.java @@ -7,130 +7,420 @@ */ package org.opendaylight.controller.sal.connect.netconf.schema.mapping; +import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_CONFIG_QNAME; +import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_FILTER_QNAME; +import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_RPC_QNAME; +import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_TYPE_QNAME; +import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_URI; +import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.toId; + +import com.google.common.base.Function; import com.google.common.base.Optional; +import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; +import com.google.common.collect.Multimaps; +import java.io.IOException; +import java.util.Collection; +import java.util.Collections; import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; import java.util.Set; -import javax.activation.UnsupportedDataTypeException; +import javax.xml.stream.XMLOutputFactory; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamWriter; +import javax.xml.transform.dom.DOMResult; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult; +import org.opendaylight.controller.md.sal.dom.spi.DefaultDOMRpcResult; import org.opendaylight.controller.netconf.api.NetconfMessage; +import org.opendaylight.controller.netconf.util.exception.MissingNameSpaceException; +import org.opendaylight.controller.netconf.util.xml.XmlElement; +import org.opendaylight.controller.netconf.util.xml.XmlUtil; import org.opendaylight.controller.sal.connect.api.MessageTransformer; import org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil; import org.opendaylight.controller.sal.connect.util.MessageCounter; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.base._1._0.rev110601.edit.config.input.EditContent; +import org.opendaylight.yangtools.sal.binding.generator.impl.ModuleInfoBackedContext; import org.opendaylight.yangtools.yang.common.QName; -import org.opendaylight.yangtools.yang.common.RpcResult; -import org.opendaylight.yangtools.yang.common.RpcResultBuilder; -import org.opendaylight.yangtools.yang.data.api.CompositeNode; -import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl; -import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode; -import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlCodecProvider; -import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlDocumentUtils; -import org.opendaylight.yangtools.yang.data.impl.util.CompositeNodeBuilder; -import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; +import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter; +import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter; +import org.opendaylight.yangtools.yang.data.impl.codec.xml.XMLStreamNormalizedNodeStreamWriter; +import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlUtils; +import org.opendaylight.yangtools.yang.data.impl.schema.Builders; +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.NotificationDefinition; import org.opendaylight.yangtools.yang.model.api.RpcDefinition; import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.model.api.SchemaNode; +import org.opendaylight.yangtools.yang.model.api.SchemaPath; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.w3c.dom.Document; import org.w3c.dom.Element; +import org.w3c.dom.Node; public class NetconfMessageTransformer implements MessageTransformer { public static final String MESSAGE_ID_PREFIX = "m"; - private Optional schemaContext = Optional.absent(); + private static final Logger LOG= LoggerFactory.getLogger(NetconfMessageTransformer.class); + + private static final DomToNormalizedNodeParserFactory NORMALIZED_NODE_PARSER_FACTORY = DomToNormalizedNodeParserFactory.getInstance(XmlUtils.DEFAULT_XML_CODEC_PROVIDER); + + private static final Function QNAME_FUNCTION = new Function() { + @Override + public QName apply(final SchemaNode rpcDefinition) { + return rpcDefinition.getQName(); + } + }; + + private static final Function QNAME_NOREV_FUNCTION = new Function() { + @Override + public QName apply(final SchemaNode notification) { + return QNAME_FUNCTION.apply(notification).withoutRevision(); + } + }; + private static final SchemaContext BASE_NETCONF_CTX; + + static { + try { + final ModuleInfoBackedContext moduleInfoBackedContext = ModuleInfoBackedContext.create(); + // TODO this should be used only if the base is not present + 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 final SchemaContext schemaContext; private final MessageCounter counter; + private final Map mappedRpcs; + private final Multimap mappedNotifications; - public NetconfMessageTransformer() { + public NetconfMessageTransformer(final SchemaContext schemaContext) { this.counter = new MessageCounter(); + this.schemaContext = schemaContext; + + mappedRpcs = Maps.uniqueIndex(schemaContext.getOperations(), QNAME_FUNCTION); + mappedNotifications = Multimaps.index(schemaContext.getNotifications(), QNAME_NOREV_FUNCTION); } @Override - public synchronized CompositeNode toNotification(final NetconfMessage message) { - if(schemaContext.isPresent()) { - return toNotification(message, schemaContext.get()); - } else { - return XmlDocumentUtils.notificationToDomNodes(message.getDocument(), Optional.>absent()); + public synchronized ContainerNode toNotification(final NetconfMessage message) { + final XmlElement stripped = stripNotification(message); + final QName notificationNoRev; + try { + // How to construct QName with no revision ? + notificationNoRev = QName.cachedReference(QName.create(stripped.getNamespace(), "0000-00-00", stripped.getName()).withoutRevision()); + } catch (final MissingNameSpaceException e) { + throw new IllegalArgumentException("Unable to parse notification " + message + ", cannot find namespace", e); } + + final Collection notificationDefinitions = mappedNotifications.get(notificationNoRev); + Preconditions.checkArgument(notificationDefinitions.size() > 0, + "Unable to parse notification %s, unknown notification. Available notifications: %s", notificationDefinitions, mappedNotifications.keySet()); + + // FIXME if multiple revisions for same notifications are present, we should pick the most recent. Or ? + // We should probably just put the most recent notification versions into our map. We can expect that the device sends the data according to the latest available revision of a model. + final NotificationDefinition next = notificationDefinitions.iterator().next(); + + // We wrap the notification as a container node in order to reuse the parsers and builders for container node + final ContainerSchemaNode notificationAsContainerSchemaNode = NetconfMessageTransformUtil.createSchemaForNotification(next); + return NORMALIZED_NODE_PARSER_FACTORY.getContainerNodeParser().parse(Collections.singleton(stripped.getDomElement()), notificationAsContainerSchemaNode); } - private static CompositeNode toNotification(final NetconfMessage message, final SchemaContext ctx) { - final Set notifications = ctx.getNotifications(); - final Document document = message.getDocument(); - return XmlDocumentUtils.notificationToDomNodes(document, Optional.fromNullable(notifications), ctx); + // FIXME move somewhere to util + private static XmlElement stripNotification(final NetconfMessage message) { + final XmlElement xmlElement = XmlElement.fromDomDocument(message.getDocument()); + final List childElements = xmlElement.getChildElements(); + Preconditions.checkArgument(childElements.size() == 2, "Unable to parse notification %s, unexpected format", message); + try { + return Iterables.find(childElements, new Predicate() { + @Override + public boolean apply(final XmlElement xmlElement) { + return !xmlElement.getName().equals("eventTime"); + } + }); + } catch (final NoSuchElementException e) { + throw new IllegalArgumentException("Unable to parse notification " + message + ", cannot strip notification metadata", e); + } } @Override - public NetconfMessage toRpcRequest(final QName rpc, final CompositeNode node) { - final CompositeNodeTOImpl rpcPayload = NetconfMessageTransformUtil.wrap( - NetconfMessageTransformUtil.NETCONF_RPC_QNAME, NetconfMessageTransformUtil.flattenInput(node)); - final Document w3cPayload; + public NetconfMessage toRpcRequest(SchemaPath rpc, final ContainerNode payload) { + // In case no input for rpc is defined, we can simply construct the payload here + final QName rpcQName = rpc.getLastComponent(); + Preconditions.checkNotNull(mappedRpcs.get(rpcQName), "Unknown rpc %s, available rpcs: %s", rpcQName, mappedRpcs.keySet()); + if(mappedRpcs.get(rpcQName).getInput() == null) { + final Document document = XmlUtil.newDocument(); + final Element elementNS = document.createElementNS(rpcQName.getNamespace().toString(), rpcQName.getLocalName()); + document.appendChild(elementNS); + return new NetconfMessage(document); + } + + // Set the path to the input of rpc for the node stream writer + rpc = rpc.createChild(QName.cachedReference(QName.create(rpcQName, "input"))); + final DOMResult result = prepareDomResultForRpcRequest(rpcQName); + + try { + final SchemaContext baseNetconfCtx = schemaContext.findModuleByNamespace(NETCONF_URI).isEmpty() ? BASE_NETCONF_CTX : schemaContext; + if(NetconfMessageTransformUtil.isDataEditOperation(rpcQName)) { + writeNormalizedEdit(payload, result, rpc, baseNetconfCtx); + } else if(NetconfMessageTransformUtil.isDataRetrievalOperation(rpcQName)) { + writeNormalizedGet(payload, result, rpc, baseNetconfCtx); + } else { + writeNormalizedRpc(payload, result, rpc, schemaContext); + } + } catch (final XMLStreamException | IOException | IllegalStateException e) { + throw new IllegalStateException("Unable to serialize " + rpc, e); + } + + final Document node = result.getNode().getOwnerDocument(); + + node.getDocumentElement().setAttribute(NetconfMessageTransformUtil.MESSAGE_ID_ATTR, counter.getNewMessageId(MESSAGE_ID_PREFIX)); + return new NetconfMessage(node); + } + + private DOMResult prepareDomResultForRpcRequest(final QName rpcQName) { + final Document document = XmlUtil.newDocument(); + final Element rpcNS = document.createElementNS(NETCONF_RPC_QNAME.getNamespace().toString(), NETCONF_RPC_QNAME.getLocalName()); + final Element elementNS = document.createElementNS(rpcQName.getNamespace().toString(), rpcQName.getLocalName()); + rpcNS.appendChild(elementNS); + document.appendChild(rpcNS); + return new DOMResult(elementNS); + } + + static final XMLOutputFactory XML_FACTORY; + static { + XML_FACTORY = XMLOutputFactory.newFactory(); + XML_FACTORY.setProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES, true); + } + + // FIXME similar code is in netconf-notifications-impl , DRY + private void writeNormalizedNode(final NormalizedNode normalized, final DOMResult result, final SchemaPath schemaPath, final SchemaContext context) + throws IOException, XMLStreamException { + NormalizedNodeWriter normalizedNodeWriter = null; + NormalizedNodeStreamWriter normalizedNodeStreamWriter = null; + XMLStreamWriter writer = null; + try { + writer = XML_FACTORY.createXMLStreamWriter(result); + normalizedNodeStreamWriter = XMLStreamNormalizedNodeStreamWriter.create(writer, context, schemaPath); + normalizedNodeWriter = NormalizedNodeWriter.forStreamWriter(normalizedNodeStreamWriter); + + normalizedNodeWriter.write(normalized); + + normalizedNodeWriter.flush(); + } finally { + try { + if(normalizedNodeWriter != null) { + normalizedNodeWriter.close(); + } + if(normalizedNodeStreamWriter != null) { + normalizedNodeStreamWriter.close(); + } + if(writer != null) { + writer.close(); + } + } catch (final Exception e) { + LOG.warn("Unable to close resource properly", e); + } + } + } + + private void writeNormalizedEdit(final ContainerNode normalized, final DOMResult result, final SchemaPath schemaPath, final SchemaContext baseNetconfCtx) throws IOException, XMLStreamException { + final NormalizedNodeWriter normalizedNodeWriter; + NormalizedNodeStreamWriter normalizedNodeStreamWriter = null; + XMLStreamWriter writer = null; try { - final XmlCodecProvider codecProvider = XmlDocumentUtils.defaultValueCodecProvider(); - if(schemaContext.isPresent()) { - if (NetconfMessageTransformUtil.isDataEditOperation(rpc)) { - final DataNodeContainer schemaForEdit = NetconfMessageTransformUtil.createSchemaForEdit(schemaContext.get()); - w3cPayload = XmlDocumentUtils.toDocument(rpcPayload, schemaContext.get(), schemaForEdit, codecProvider); - } else if (NetconfMessageTransformUtil.isGetOperation(rpc)) { - final DataNodeContainer schemaForGet = NetconfMessageTransformUtil.createSchemaForGet(schemaContext.get()); - w3cPayload = XmlDocumentUtils.toDocument(rpcPayload, schemaContext.get(), schemaForGet, codecProvider); - } else if (NetconfMessageTransformUtil.isGetConfigOperation(rpc)) { - final DataNodeContainer schemaForGetConfig = NetconfMessageTransformUtil.createSchemaForGetConfig(schemaContext.get()); - w3cPayload = XmlDocumentUtils.toDocument(rpcPayload, schemaContext.get(), schemaForGetConfig, codecProvider); + writer = XML_FACTORY.createXMLStreamWriter(result); + normalizedNodeStreamWriter = XMLStreamNormalizedNodeStreamWriter.create(writer, baseNetconfCtx, schemaPath); + normalizedNodeWriter = NormalizedNodeWriter.forStreamWriter(normalizedNodeStreamWriter); + + Optional> editDataElements = Optional.absent(); + for (final DataContainerChild editElement : normalized.getValue()) { + if(editElement.getNodeType().getLocalName().equals(EditContent.QNAME.getLocalName())) { + Preconditions.checkState(editElement instanceof ChoiceNode, + "Edit content element is expected to be %s, not %s", ChoiceNode.class, editElement); + final Optional> configContentHolder = + ((ChoiceNode) editElement).getChild(toId(NETCONF_CONFIG_QNAME)); + // TODO The config element inside the EditContent should be AnyXml not Container, but AnyXml is based on outdated API + Preconditions.checkState(configContentHolder.isPresent() && configContentHolder.get() instanceof ContainerNode, + "Edit content/config element is expected to be present as a container node"); + normalizedNodeStreamWriter.startChoiceNode(toId(editElement.getNodeType()), 1); + normalizedNodeStreamWriter.anyxmlNode(toId(NETCONF_CONFIG_QNAME), null); + normalizedNodeStreamWriter.endNode(); + + editDataElements = Optional.of(serializeAnyXmlAccordingToSchema(((ContainerNode) configContentHolder.get()).getValue())); } else { - final Optional schemaForRpc = NetconfMessageTransformUtil.findSchemaForRpc(rpc, schemaContext.get()); - if(schemaForRpc.isPresent()) { - final DataNodeContainer schemaForGetConfig = NetconfMessageTransformUtil.createSchemaForRpc(schemaForRpc.get()); - w3cPayload = XmlDocumentUtils.toDocument(rpcPayload, schemaContext.get(), schemaForGetConfig, codecProvider); - } else { - w3cPayload = toRpcRequestWithoutSchema(rpcPayload, codecProvider); - } + normalizedNodeWriter.write(editElement); } - } else { - w3cPayload = toRpcRequestWithoutSchema(rpcPayload, codecProvider); } - } catch (final UnsupportedDataTypeException e) { - throw new IllegalArgumentException("Unable to create message", e); + + normalizedNodeWriter.flush(); + + // FIXME this is a workaround for filter content serialization + // Any xml is not supported properly by the stream writer + if(editDataElements.isPresent()) { + appendEditData(result, editDataElements.get()); + } + } finally { + try { + if(normalizedNodeStreamWriter != null) { + normalizedNodeStreamWriter.close(); + } + if(writer != null) { + writer.close(); + } + } catch (final Exception e) { + LOG.warn("Unable to close resource properly", e); + } } - w3cPayload.getDocumentElement().setAttribute("message-id", counter.getNewMessageId(MESSAGE_ID_PREFIX)); - return new NetconfMessage(w3cPayload); } - private Document toRpcRequestWithoutSchema(final CompositeNodeTOImpl rpcPayload, final XmlCodecProvider codecProvider) { - return XmlDocumentUtils.toDocument(rpcPayload, codecProvider); + private void writeNormalizedRpc(final ContainerNode normalized, final DOMResult result, final SchemaPath schemaPath, final SchemaContext baseNetconfCtx) throws IOException, XMLStreamException { + final NormalizedNodeWriter normalizedNodeWriter; + NormalizedNodeStreamWriter normalizedNodeStreamWriter = null; + XMLStreamWriter writer = null; + try { + writer = XML_FACTORY.createXMLStreamWriter(result); + normalizedNodeStreamWriter = XMLStreamNormalizedNodeStreamWriter.create(writer, baseNetconfCtx, schemaPath); + normalizedNodeWriter = NormalizedNodeWriter.forStreamWriter(normalizedNodeStreamWriter); + + for (final DataContainerChild editElement : normalized.getValue()) { + normalizedNodeWriter.write(editElement); + } + normalizedNodeWriter.flush(); + } finally { + try { + if(normalizedNodeStreamWriter != null) { + normalizedNodeStreamWriter.close(); + } + if(writer != null) { + writer.close(); + } + } catch (final Exception e) { + LOG.warn("Unable to close resource properly", e); + } + } } - @Override - public synchronized RpcResult toRpcResult(final NetconfMessage message, final QName rpc) { - if(schemaContext.isPresent()) { - return toRpcResult(message, rpc, schemaContext.get()); - } else { - final CompositeNode node = (CompositeNode) XmlDocumentUtils.toDomNode(message.getDocument()); - return RpcResultBuilder.success(node).build(); + private void writeNormalizedGet(final ContainerNode normalized, final DOMResult result, final SchemaPath schemaPath, final SchemaContext baseNetconfCtx) throws IOException, XMLStreamException { + final NormalizedNodeWriter normalizedNodeWriter; + NormalizedNodeStreamWriter normalizedNodeStreamWriter = null; + XMLStreamWriter writer = null; + try { + writer = XML_FACTORY.createXMLStreamWriter(result); + normalizedNodeStreamWriter = XMLStreamNormalizedNodeStreamWriter.create(writer, baseNetconfCtx, schemaPath); + normalizedNodeWriter = NormalizedNodeWriter.forStreamWriter(normalizedNodeStreamWriter); + + Optional> filterElements = Optional.absent(); + + for (final DataContainerChild editElement : normalized.getValue()) { + Preconditions.checkState(editElement instanceof ContainerNode); + if(editElement.getNodeType().getLocalName().equals(NETCONF_FILTER_QNAME.getLocalName())) { + Preconditions.checkState(editElement instanceof ContainerNode, + "Filter element is expected to be %s, not %s", ContainerNode.class, editElement); + normalizedNodeStreamWriter.anyxmlNode(toId(editElement.getNodeType()), null); + filterElements = Optional.of(serializeAnyXmlAccordingToSchema(((ContainerNode) editElement).getValue())); + } else { + normalizedNodeWriter.write(editElement); + } + } + + normalizedNodeWriter.flush(); + + // FIXME this is a workaround for filter content serialization + // Any xml is not supported properly by the stream writer + if(filterElements.isPresent()) { + appendFilter(result, filterElements.get()); + } + } finally { + try { + if(normalizedNodeStreamWriter != null) { + normalizedNodeStreamWriter.close(); + } + if(writer != null) { + writer.close(); + } + } catch (final Exception e) { + LOG.warn("Unable to close resource properly", e); + } + } + } + + private void appendFilter(final DOMResult result, final Iterable filterElements) { + final Element rpcElement = ((Element) result.getNode()); + final Node filterParent = rpcElement.getElementsByTagNameNS(NETCONF_FILTER_QNAME.getNamespace().toString(), NETCONF_FILTER_QNAME.getLocalName()).item(0); + final Document ownerDocument = rpcElement.getOwnerDocument(); + // TODO workaround, add subtree attribute, since it is not serialized by the caller of this method + ((Element) filterParent).setAttributeNS(NETCONF_TYPE_QNAME.getNamespace().toString(), NETCONF_TYPE_QNAME.getLocalName(), "subtree"); + for (final Element element : filterElements) { + filterParent.appendChild(ownerDocument.importNode(element, true)); } } - private static RpcResult toRpcResult(final NetconfMessage message, final QName rpc, final SchemaContext context) { - final CompositeNode compositeNode; - if (NetconfMessageTransformUtil.isDataRetrievalOperation(rpc)) { + private void appendEditData(final DOMResult result, final Iterable filterElements) { + final Element rpcElement = ((Element) result.getNode()); + final Node configParent = rpcElement.getElementsByTagNameNS(NETCONF_CONFIG_QNAME.getNamespace().toString(), NETCONF_CONFIG_QNAME.getLocalName()).item(0); + for (final Element element : filterElements) { + configParent.appendChild(rpcElement.getOwnerDocument().importNode(element, true)); + } + } + + private Iterable serializeAnyXmlAccordingToSchema(final Iterable> values) throws IOException, XMLStreamException { + return Iterables.transform(values, new Function, Element>() { + @Override + public Element apply(final DataContainerChild input) { + final DOMResult domResult = new DOMResult(XmlUtil.newDocument()); + try { + writeNormalizedNode(input, domResult, SchemaPath.ROOT, schemaContext); + } catch (IOException | XMLStreamException e) { + throw new IllegalStateException(e); + } + return ((Document) domResult.getNode()).getDocumentElement(); + } + }); + } + + @Override + public synchronized DOMRpcResult toRpcResult(final NetconfMessage message, final SchemaPath rpc) { + final NormalizedNode normalizedNode; + if (NetconfMessageTransformUtil.isDataRetrievalOperation(rpc.getLastComponent())) { final Element xmlData = NetconfMessageTransformUtil.getDataSubtree(message.getDocument()); - final List> dataNodes = XmlDocumentUtils.toDomNodes(xmlData, - Optional.of(context.getDataDefinitions()), context); + final ContainerSchemaNode schemaForDataRead = NetconfMessageTransformUtil.createSchemaForDataRead(schemaContext); + final ContainerNode dataNode = NORMALIZED_NODE_PARSER_FACTORY.getContainerNodeParser().parse(Collections.singleton(xmlData), schemaForDataRead); - final CompositeNodeBuilder it = ImmutableCompositeNode.builder(); - it.setQName(NetconfMessageTransformUtil.NETCONF_RPC_REPLY_QNAME); - it.add(ImmutableCompositeNode.create(NetconfMessageTransformUtil.NETCONF_DATA_QNAME, dataNodes)); - compositeNode = it.toInstance(); + // TODO check if the response is wrapper correctly + normalizedNode = Builders.containerBuilder().withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(NetconfMessageTransformUtil.NETCONF_RPC_REPLY_QNAME)) + .withChild(dataNode).build(); } else { - final CompositeNode rpcReply = XmlDocumentUtils.rpcReplyToDomNodes(message.getDocument(), rpc, context); - if (rpcReply != null) { - compositeNode = rpcReply; + final Set documentElement = Collections.singleton(message.getDocument().getDocumentElement()); + final RpcDefinition rpcDefinition = mappedRpcs.get(rpc.getLastComponent()); + Preconditions.checkArgument(rpcDefinition != null, "Unable to parse response of %s, the rpc is unknown", rpc.getLastComponent()); + + // In case no input for rpc is defined, we can simply construct the payload here + if(rpcDefinition.getOutput() == null) { + Preconditions.checkArgument(XmlElement.fromDomDocument(message.getDocument()).getOnlyChildElementWithSameNamespaceOptionally("ok").isPresent(), + "Unexpected content in response of rpc: %s, %s", rpcDefinition.getQName(), message); + normalizedNode = null; } else { - compositeNode = (CompositeNode) XmlDocumentUtils.toDomNode(message.getDocument()); + normalizedNode = NORMALIZED_NODE_PARSER_FACTORY.getContainerNodeParser().parse(documentElement, rpcDefinition.getOutput()); } } - return RpcResultBuilder.success( compositeNode ).build(); + return new DefaultDOMRpcResult(normalizedNode); } - @Override - public synchronized void onGlobalContextUpdated(final SchemaContext schemaContext) { - this.schemaContext = Optional.fromNullable(schemaContext); - } } diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/util/InstanceIdToNodes.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/util/InstanceIdToNodes.java new file mode 100644 index 0000000000..cb17b35a23 --- /dev/null +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/util/InstanceIdToNodes.java @@ -0,0 +1,594 @@ +/* + * Copyright (c) 2015 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.controller.sal.connect.netconf.util; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; +import com.google.common.collect.FluentIterable; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizationException; +import org.opendaylight.yangtools.concepts.Identifiable; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.ModifyAction; +import org.opendaylight.yangtools.yang.data.api.Node; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; +import org.opendaylight.yangtools.yang.data.api.schema.AnyXmlNode; +import org.opendaylight.yangtools.yang.data.api.schema.LeafNode; +import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode; +import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.impl.schema.Builders; +import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.AttributesBuilder; +import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder; +import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeAttrBuilder; +import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeContainerBuilder; +import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode; +import org.opendaylight.yangtools.yang.model.api.AugmentationSchema; +import org.opendaylight.yangtools.yang.model.api.AugmentationTarget; +import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode; +import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode; +import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; +import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; +import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode; +import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode; +import org.opendaylight.yangtools.yang.model.api.ListSchemaNode; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; + +/** + * Transforms an instance of yang instance identifier to a filter like structure in normalized node format. Can be also used to nest the edit-config rpc content. + * For each argument of the id, a specific normalized node is created to ensure schema context conformance. + */ +public abstract class InstanceIdToNodes implements Identifiable { + + private final T identifier; + + @Override + public T getIdentifier() { + return identifier; + } + + protected InstanceIdToNodes(final T identifier) { + this.identifier = identifier; + } + + abstract InstanceIdToNodes getChild(final PathArgument child) throws DataNormalizationException; + + public abstract NormalizedNode create(YangInstanceIdentifier legacyData, Optional> deepestChild, Optional operation); + + private static abstract class SimpleTypeNormalization extends InstanceIdToNodes { + + protected SimpleTypeNormalization(final T identifier) { + super(identifier); + } + + @Override + public NormalizedNode create(final YangInstanceIdentifier id, final Optional> deepestChild, final Optional operation) { + checkNotNull(id); + final PathArgument pathArgument = Iterables.get(id.getPathArguments(), 0); + final NormalizedNodeAttrBuilder> builder = getBuilder(pathArgument); + + if(deepestChild.isPresent()) { + builder.withValue(deepestChild.get().getValue()); + } + + addModifyOpIfPresent(operation, builder); + return builder.build(); + } + + protected abstract NormalizedNodeAttrBuilder> getBuilder(PathArgument node); + + @Override + public InstanceIdToNodes getChild(final PathArgument child) { + return null; + } + } + + + public void addModifyOpIfPresent(final Optional operation, final AttributesBuilder builder) { + if(operation.isPresent()) { + builder.withAttributes(Collections.singletonMap(NetconfMessageTransformUtil.NETCONF_OPERATION_QNAME, NetconfMessageTransformUtil.modifyOperationToXmlString(operation.get()))); + } + } + + private static final class LeafNormalization extends SimpleTypeNormalization { + + protected LeafNormalization(final LeafSchemaNode potential) { + super(new NodeIdentifier(potential.getQName())); + } + + @Override + protected NormalizedNodeAttrBuilder> getBuilder(final PathArgument node) { + return Builders.leafBuilder().withNodeIdentifier(getIdentifier()); + } + } + + private static final class LeafListEntryNormalization extends SimpleTypeNormalization { + + public LeafListEntryNormalization(final LeafListSchemaNode potential) { + super(new NodeWithValue(potential.getQName(), null)); + } + + @Override + protected NormalizedNodeAttrBuilder> getBuilder(final PathArgument node) { + Preconditions.checkArgument(node instanceof NodeWithValue); + return Builders.leafSetEntryBuilder().withNodeIdentifier((NodeWithValue) node).withValue(((NodeWithValue) node).getValue()); + } + + } + + private static abstract class CompositeNodeNormalizationOperation extends + InstanceIdToNodes { + + protected CompositeNodeNormalizationOperation(final T identifier) { + super(identifier); + } + + @Override + @SuppressWarnings("unchecked") + public final NormalizedNode create(final YangInstanceIdentifier id, final Optional> lastChild, final Optional operation) { + checkNotNull(id); + final Iterator iterator = id.getPathArguments().iterator(); + final PathArgument legacyData = iterator.next(); + + if (!isMixin(this) && getIdentifier().getNodeType() != null) { + checkArgument(getIdentifier().getNodeType().equals(legacyData.getNodeType()), + "Node QName must be %s was %s", getIdentifier().getNodeType(), legacyData.getNodeType()); + } + final NormalizedNodeContainerBuilder builder = createBuilder(legacyData); + + if (iterator.hasNext()) { + final PathArgument childPath = iterator.next(); + final InstanceIdToNodes childOp = getChildOperation(childPath); + + final YangInstanceIdentifier childId = YangInstanceIdentifier.create(Iterables.skip(id.getPathArguments(), 1)); + builder.addChild(childOp.create(childId, lastChild, operation)); + } else if(lastChild.isPresent()) { + builder.withValue(Lists.newArrayList((Collection) lastChild.get().getValue())); + if(operation.isPresent()) { + Preconditions.checkArgument(builder instanceof AttributesBuilder); + addModifyOpIfPresent(operation, ((AttributesBuilder) builder)); + } + } + + return builder.build(); + } + + private InstanceIdToNodes getChildOperation(final PathArgument childPath) { + final InstanceIdToNodes childOp; + try { + childOp = getChild(childPath); + } catch (final DataNormalizationException e) { + throw new IllegalArgumentException(String.format("Failed to process child node %s", childPath), e); + } + checkArgument(childOp != null, "Node %s is not allowed inside %s", childPath, getIdentifier()); + return childOp; + } + + @SuppressWarnings("rawtypes") + protected abstract NormalizedNodeContainerBuilder createBuilder(final PathArgument compositeNode); + } + + static boolean isMixin(final InstanceIdToNodes op) { + return op instanceof MixinNormalizationOp; + } + + private static abstract class DataContainerNormalizationOperation extends + CompositeNodeNormalizationOperation { + + private final DataNodeContainer schema; + private final Map> byArg; + + protected DataContainerNormalizationOperation(final T identifier, final DataNodeContainer schema) { + super(identifier); + this.schema = schema; + this.byArg = new ConcurrentHashMap<>(); + } + + @Override + public InstanceIdToNodes getChild(final PathArgument child) throws DataNormalizationException { + InstanceIdToNodes potential = byArg.get(child); + if (potential != null) { + return potential; + } + potential = fromLocalSchema(child); + return register(potential); + } + + private InstanceIdToNodes fromLocalSchema(final PathArgument child) throws DataNormalizationException { + if (child instanceof AugmentationIdentifier) { + return fromSchemaAndQNameChecked(schema, ((AugmentationIdentifier) child).getPossibleChildNames() + .iterator().next()); + } + return fromSchemaAndQNameChecked(schema, child.getNodeType()); + } + + private InstanceIdToNodes register(final InstanceIdToNodes potential) { + if (potential != null) { + byArg.put(potential.getIdentifier(), potential); + } + return potential; + } + } + + private static final class ListItemNormalization extends + DataContainerNormalizationOperation { + + protected ListItemNormalization(final NodeIdentifierWithPredicates identifier, final ListSchemaNode schema) { + super(identifier, schema); + } + + @Override + protected NormalizedNodeContainerBuilder createBuilder(final PathArgument currentArg) { + final DataContainerNodeAttrBuilder builder = Builders + .mapEntryBuilder().withNodeIdentifier((NodeIdentifierWithPredicates) currentArg); + for (final Entry keyValue : ((NodeIdentifierWithPredicates) currentArg).getKeyValues().entrySet()) { + builder.addChild(Builders.leafBuilder() + // + .withNodeIdentifier(new NodeIdentifier(keyValue.getKey())).withValue(keyValue.getValue()) + .build()); + } + return builder; + } + + } + + private static final class UnkeyedListItemNormalization extends DataContainerNormalizationOperation { + + protected UnkeyedListItemNormalization(final ListSchemaNode schema) { + super(new NodeIdentifier(schema.getQName()), schema); + } + + @Override + protected NormalizedNodeContainerBuilder createBuilder(final PathArgument compositeNode) { + return Builders.unkeyedListEntryBuilder().withNodeIdentifier(getIdentifier()); + } + + } + + private static final class ContainerTransformation extends DataContainerNormalizationOperation { + + protected ContainerTransformation(final ContainerSchemaNode schema) { + super(new NodeIdentifier(schema.getQName()), schema); + } + + @Override + protected NormalizedNodeContainerBuilder createBuilder(final PathArgument compositeNode) { + return Builders.containerBuilder().withNodeIdentifier(getIdentifier()); + } + } + + /** + * Marker interface for Mixin nodes normalization operations + */ + private interface MixinNormalizationOp {} + + + private static final class OrderedLeafListMixinNormalization extends UnorderedLeafListMixinNormalization { + + + public OrderedLeafListMixinNormalization(final LeafListSchemaNode potential) { + super(potential); + } + + @Override + protected NormalizedNodeContainerBuilder createBuilder(final PathArgument compositeNode) { + return Builders.orderedLeafSetBuilder().withNodeIdentifier(getIdentifier()); + } + } + + private static class UnorderedLeafListMixinNormalization extends CompositeNodeNormalizationOperation implements MixinNormalizationOp { + + private final InstanceIdToNodes innerOp; + + public UnorderedLeafListMixinNormalization(final LeafListSchemaNode potential) { + super(new NodeIdentifier(potential.getQName())); + innerOp = new LeafListEntryNormalization(potential); + } + + @Override + protected NormalizedNodeContainerBuilder createBuilder(final PathArgument compositeNode) { + return Builders.leafSetBuilder().withNodeIdentifier(getIdentifier()); + } + + @Override + public InstanceIdToNodes getChild(final PathArgument child) { + if (child instanceof NodeWithValue) { + return innerOp; + } + return null; + } + } + + private static final class AugmentationNormalization extends DataContainerNormalizationOperation implements MixinNormalizationOp { + + public AugmentationNormalization(final AugmentationSchema augmentation, final DataNodeContainer schema) { + //super(); + super(augmentationIdentifierFrom(augmentation), augmentationProxy(augmentation, schema)); + } + + @Override + protected NormalizedNodeContainerBuilder createBuilder(final PathArgument compositeNode) { + return Builders.augmentationBuilder().withNodeIdentifier(getIdentifier()); + } + } + + private static class UnorderedMapMixinNormalization extends CompositeNodeNormalizationOperation implements MixinNormalizationOp { + + private final ListItemNormalization innerNode; + + public UnorderedMapMixinNormalization(final ListSchemaNode list) { + super(new NodeIdentifier(list.getQName())); + this.innerNode = new ListItemNormalization(new NodeIdentifierWithPredicates(list.getQName(), + Collections.emptyMap()), list); + } + + @Override + protected NormalizedNodeContainerBuilder createBuilder(final PathArgument compositeNode) { + return Builders.mapBuilder().withNodeIdentifier(getIdentifier()); + } + + @Override + public InstanceIdToNodes getChild(final PathArgument child) { + if (child.getNodeType().equals(getIdentifier().getNodeType())) { + return innerNode; + } + return null; + } + } + + private static class UnkeyedListMixinNormalization extends CompositeNodeNormalizationOperation implements MixinNormalizationOp { + + private final UnkeyedListItemNormalization innerNode; + + public UnkeyedListMixinNormalization(final ListSchemaNode list) { + super(new NodeIdentifier(list.getQName())); + this.innerNode = new UnkeyedListItemNormalization(list); + } + + @Override + protected NormalizedNodeContainerBuilder createBuilder(final PathArgument compositeNode) { + return Builders.unkeyedListBuilder().withNodeIdentifier(getIdentifier()); + } + + @Override + public InstanceIdToNodes getChild(final PathArgument child) { + if (child.getNodeType().equals(getIdentifier().getNodeType())) { + return innerNode; + } + return null; + } + + } + + private static final class OrderedMapMixinNormalization extends UnorderedMapMixinNormalization { + + public OrderedMapMixinNormalization(final ListSchemaNode list) { + super(list); + } + + @Override + protected NormalizedNodeContainerBuilder createBuilder(final PathArgument compositeNode) { + return Builders.orderedMapBuilder().withNodeIdentifier(getIdentifier()); + } + + } + + private static class ChoiceNodeNormalization extends CompositeNodeNormalizationOperation implements MixinNormalizationOp { + + private final ImmutableMap> byArg; + + protected ChoiceNodeNormalization(final org.opendaylight.yangtools.yang.model.api.ChoiceNode schema) { + super(new NodeIdentifier(schema.getQName())); + final ImmutableMap.Builder> byArgBuilder = ImmutableMap.builder(); + + for (final ChoiceCaseNode caze : schema.getCases()) { + for (final DataSchemaNode cazeChild : caze.getChildNodes()) { + final InstanceIdToNodes childOp = fromDataSchemaNode(cazeChild); + byArgBuilder.put(childOp.getIdentifier(), childOp); + } + } + byArg = byArgBuilder.build(); + } + + @Override + public InstanceIdToNodes getChild(final PathArgument child) { + return byArg.get(child); + } + + @Override + protected NormalizedNodeContainerBuilder createBuilder(final PathArgument compositeNode) { + return Builders.choiceBuilder().withNodeIdentifier(getIdentifier()); + } + } + + private static class AnyXmlNormalization extends InstanceIdToNodes { + + protected AnyXmlNormalization(final AnyXmlSchemaNode schema) { + super(new NodeIdentifier(schema.getQName())); + } + + @Override + public InstanceIdToNodes getChild(final PathArgument child) throws DataNormalizationException { + return null; + } + + @Override + public NormalizedNode create(final YangInstanceIdentifier legacyData, final Optional> deepestChild, final Optional operation) { + if(deepestChild.isPresent()) { + Preconditions.checkState(deepestChild instanceof AnyXmlNode); + final NormalizedNodeAttrBuilder, AnyXmlNode> anyXmlBuilder = + Builders.anyXmlBuilder().withNodeIdentifier(getIdentifier()).withValue(((AnyXmlNode) deepestChild).getValue()); + addModifyOpIfPresent(operation, anyXmlBuilder); + return anyXmlBuilder.build(); + } + + final NormalizedNodeAttrBuilder, AnyXmlNode> builder = + Builders.anyXmlBuilder().withNodeIdentifier(getIdentifier()); + return builder.build(); + } + + } + + private static Optional findChildSchemaNode(final DataNodeContainer parent, final QName child) { + DataSchemaNode potential = parent.getDataChildByName(child); + if (potential == null) { + final Iterable choices = FluentIterable.from( + parent.getChildNodes()).filter(org.opendaylight.yangtools.yang.model.api.ChoiceNode.class); + potential = findChoice(choices, child); + } + return Optional.fromNullable(potential); + } + + private static InstanceIdToNodes fromSchemaAndQNameChecked(final DataNodeContainer schema, final QName child) throws DataNormalizationException { + final Optional potential = findChildSchemaNode(schema, child); + if (!potential.isPresent()) { + throw new DataNormalizationException(String.format("Supplied QName %s is not valid according to schema %s, potential children nodes: %s", child, schema, schema.getChildNodes())); + } + + final DataSchemaNode result = potential.get(); + // We try to look up if this node was added by augmentation + if ((schema instanceof DataSchemaNode) && result.isAugmenting()) { + return fromAugmentation(schema, (AugmentationTarget) schema, result); + } + return fromDataSchemaNode(result); + } + + private static org.opendaylight.yangtools.yang.model.api.ChoiceNode findChoice( + final Iterable choices, final QName child) { + org.opendaylight.yangtools.yang.model.api.ChoiceNode foundChoice = null; + choiceLoop: + for (final org.opendaylight.yangtools.yang.model.api.ChoiceNode choice : choices) { + for (final ChoiceCaseNode caze : choice.getCases()) { + if (findChildSchemaNode(caze, child).isPresent()) { + foundChoice = choice; + break choiceLoop; + } + } + } + return foundChoice; + } + + private static AugmentationIdentifier augmentationIdentifierFrom(final AugmentationSchema augmentation) { + final ImmutableSet.Builder potentialChildren = ImmutableSet.builder(); + for (final DataSchemaNode child : augmentation.getChildNodes()) { + potentialChildren.add(child.getQName()); + } + return new AugmentationIdentifier(potentialChildren.build()); + } + + private static DataNodeContainer augmentationProxy(final AugmentationSchema augmentation, final DataNodeContainer schema) { + final Set children = new HashSet<>(); + for (final DataSchemaNode augNode : augmentation.getChildNodes()) { + children.add(schema.getDataChildByName(augNode.getQName())); + } + return new NodeContainerProxy(null, children); + } + + /** + * Returns a SchemaPathUtil for provided child node + *

+ * If supplied child is added by Augmentation this operation returns + * a SchemaPathUtil for augmentation, + * otherwise returns a SchemaPathUtil for child as + * call for {@link #fromDataSchemaNode(org.opendaylight.yangtools.yang.model.api.DataSchemaNode)}. + */ + private static InstanceIdToNodes fromAugmentation(final DataNodeContainer parent, + final AugmentationTarget parentAug, final DataSchemaNode child) { + AugmentationSchema augmentation = null; + for (final AugmentationSchema aug : parentAug.getAvailableAugmentations()) { + final DataSchemaNode potential = aug.getDataChildByName(child.getQName()); + if (potential != null) { + augmentation = aug; + break; + } + + } + if (augmentation != null) { + return new AugmentationNormalization(augmentation, parent); + } else { + return fromDataSchemaNode(child); + } + } + + private static InstanceIdToNodes fromDataSchemaNode(final DataSchemaNode potential) { + if (potential instanceof ContainerSchemaNode) { + return new ContainerTransformation((ContainerSchemaNode) potential); + } else if (potential instanceof ListSchemaNode) { + return fromListSchemaNode((ListSchemaNode) potential); + } else if (potential instanceof LeafSchemaNode) { + return new LeafNormalization((LeafSchemaNode) potential); + } else if (potential instanceof org.opendaylight.yangtools.yang.model.api.ChoiceNode) { + return new ChoiceNodeNormalization((org.opendaylight.yangtools.yang.model.api.ChoiceNode) potential); + } else if (potential instanceof LeafListSchemaNode) { + return fromLeafListSchemaNode((LeafListSchemaNode) potential); + } else if (potential instanceof AnyXmlSchemaNode) { + return new AnyXmlNormalization((AnyXmlSchemaNode) potential); + } + return null; + } + + private static InstanceIdToNodes fromListSchemaNode(final ListSchemaNode potential) { + final List keyDefinition = potential.getKeyDefinition(); + if (keyDefinition == null || keyDefinition.isEmpty()) { + return new UnkeyedListMixinNormalization(potential); + } + if (potential.isUserOrdered()) { + return new OrderedMapMixinNormalization(potential); + } + return new UnorderedMapMixinNormalization(potential); + } + + private static InstanceIdToNodes fromLeafListSchemaNode(final LeafListSchemaNode potential) { + if (potential.isUserOrdered()) { + return new OrderedLeafListMixinNormalization(potential); + } + return new UnorderedLeafListMixinNormalization(potential); + } + + public static NormalizedNode serialize(final SchemaContext ctx, final YangInstanceIdentifier id) { + return serialize(ctx, id, Optional.>absent(), Optional.absent()); + } + + public static NormalizedNode serialize(final SchemaContext ctx, final YangInstanceIdentifier id, final NormalizedNode deepestElement) { + return serialize(ctx, id, Optional.>of(deepestElement), Optional.absent()); + } + + public static NormalizedNode serialize(final SchemaContext ctx, final YangInstanceIdentifier id, final Optional> deepestElement, final Optional operation) { + Preconditions.checkNotNull(ctx); + Preconditions.checkNotNull(id); + final PathArgument topLevelElement = id.getPathArguments().iterator().next(); + final DataSchemaNode dataChildByName = ctx.getDataChildByName(topLevelElement.getNodeType()); + Preconditions.checkNotNull(dataChildByName, "Cannot find %s node in schema context. Instance identifier has to start from root", topLevelElement); + try { + final InstanceIdToNodes instanceIdToNodes = fromSchemaAndQNameChecked(ctx, topLevelElement.getNodeType()); + return instanceIdToNodes.create(id, deepestElement, operation); + } catch (final DataNormalizationException e) { + throw new IllegalArgumentException("Unable to serialize: " + id, e); + } + } +} diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/util/NetconfBaseOps.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/util/NetconfBaseOps.java index 40e890e80c..7b231f989e 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/util/NetconfBaseOps.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/util/NetconfBaseOps.java @@ -10,6 +10,7 @@ package org.opendaylight.controller.sal.connect.netconf.util; import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.DISCARD_CHANGES_RPC_CONTENT; import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_CANDIDATE_QNAME; +import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_COPY_CONFIG_QNAME; import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_DEFAULT_OPERATION_QNAME; import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_DISCARD_CHANGES_QNAME; import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_EDIT_CONFIG_QNAME; @@ -24,26 +25,28 @@ import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessag import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_VALIDATE_QNAME; import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.ROLLBACK_ON_ERROR_OPTION; import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.toFilterStructure; +import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.toId; +import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.toPath; import com.google.common.base.Optional; import com.google.common.base.Preconditions; -import com.google.common.collect.Lists; +import com.google.common.collect.Iterables; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; -import java.util.Collections; -import org.opendaylight.controller.sal.core.api.RpcImplementation; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcService; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.base._1._0.rev110601.copy.config.input.target.ConfigTarget; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.base._1._0.rev110601.get.config.input.source.ConfigSource; import org.opendaylight.yangtools.yang.common.QName; -import org.opendaylight.yangtools.yang.common.RpcResult; -import org.opendaylight.yangtools.yang.data.api.CompositeNode; import org.opendaylight.yangtools.yang.data.api.ModifyAction; -import org.opendaylight.yangtools.yang.data.api.Node; -import org.opendaylight.yangtools.yang.data.api.SimpleNode; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; -import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode; -import org.opendaylight.yangtools.yang.data.impl.NodeFactory; -import org.opendaylight.yangtools.yang.data.impl.SimpleNodeTOImpl; -import org.opendaylight.yangtools.yang.data.impl.util.CompositeNodeBuilder; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; +import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.impl.schema.Builders; +import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; /** * Provides base operations for netconf e.g. get, get-config, edit-config, (un)lock, commit etc. @@ -51,114 +54,117 @@ import org.opendaylight.yangtools.yang.data.impl.util.CompositeNodeBuilder; */ public final class NetconfBaseOps { - private final RpcImplementation rpc; + private final DOMRpcService rpc; + private final SchemaContext schemaContext; - public NetconfBaseOps(final RpcImplementation rpc) { + public NetconfBaseOps(final DOMRpcService rpc, final SchemaContext schemaContext) { this.rpc = rpc; + this.schemaContext = schemaContext; } - public ListenableFuture> lock(final FutureCallback> callback, final QName datastore) { + public ListenableFuture lock(final FutureCallback callback, final QName datastore) { Preconditions.checkNotNull(callback); Preconditions.checkNotNull(datastore); - final ListenableFuture> future = rpc.invokeRpc(NETCONF_LOCK_QNAME, getLockContent(datastore)); + final ListenableFuture future = rpc.invokeRpc(toPath(NETCONF_LOCK_QNAME), getLockContent(datastore)); Futures.addCallback(future, callback); return future; } - public ListenableFuture> lockCandidate(final FutureCallback> callback) { - final ListenableFuture> future = rpc.invokeRpc(NETCONF_LOCK_QNAME, getLockContent(NETCONF_CANDIDATE_QNAME)); + public ListenableFuture lockCandidate(final FutureCallback callback) { + final ListenableFuture future = rpc.invokeRpc(toPath(NETCONF_LOCK_QNAME), getLockContent(NETCONF_CANDIDATE_QNAME)); Futures.addCallback(future, callback); return future; } - public ListenableFuture> lockRunning(final FutureCallback> callback) { - final ListenableFuture> future = rpc.invokeRpc(NETCONF_LOCK_QNAME, getLockContent(NETCONF_RUNNING_QNAME)); + public ListenableFuture lockRunning(final FutureCallback callback) { + final ListenableFuture future = rpc.invokeRpc(toPath(NETCONF_LOCK_QNAME), getLockContent(NETCONF_RUNNING_QNAME)); Futures.addCallback(future, callback); return future; } - public ListenableFuture> unlock(final FutureCallback> callback, final QName datastore) { + public ListenableFuture unlock(final FutureCallback callback, final QName datastore) { Preconditions.checkNotNull(callback); Preconditions.checkNotNull(datastore); - final ListenableFuture> future = rpc.invokeRpc(NETCONF_UNLOCK_QNAME, getUnLockContent(datastore)); + final ListenableFuture future = rpc.invokeRpc(toPath(NETCONF_UNLOCK_QNAME), getUnLockContent(datastore)); Futures.addCallback(future, callback); return future; } - public ListenableFuture> unlockRunning(final FutureCallback> callback) { - final ListenableFuture> future = rpc.invokeRpc(NETCONF_UNLOCK_QNAME, getUnLockContent(NETCONF_RUNNING_QNAME)); + public ListenableFuture unlockRunning(final FutureCallback callback) { + final ListenableFuture future = rpc.invokeRpc(toPath(NETCONF_UNLOCK_QNAME), getUnLockContent(NETCONF_RUNNING_QNAME)); Futures.addCallback(future, callback); return future; } - public ListenableFuture> unlockCandidate(final FutureCallback> callback) { - final ListenableFuture> future = rpc.invokeRpc(NETCONF_UNLOCK_QNAME, getUnLockContent(NETCONF_CANDIDATE_QNAME)); + public ListenableFuture unlockCandidate(final FutureCallback callback) { + final ListenableFuture future = rpc.invokeRpc(toPath(NETCONF_UNLOCK_QNAME), getUnLockContent(NETCONF_CANDIDATE_QNAME)); Futures.addCallback(future, callback); return future; } - public ListenableFuture> discardChanges(final FutureCallback> callback) { + public ListenableFuture discardChanges(final FutureCallback callback) { Preconditions.checkNotNull(callback); - final ListenableFuture> future = rpc.invokeRpc(NETCONF_DISCARD_CHANGES_QNAME, DISCARD_CHANGES_RPC_CONTENT); + final ListenableFuture future = rpc.invokeRpc(toPath(NETCONF_DISCARD_CHANGES_QNAME), DISCARD_CHANGES_RPC_CONTENT); Futures.addCallback(future, callback); return future; } - public ListenableFuture> commit(final FutureCallback> callback) { + public ListenableFuture commit(final FutureCallback callback) { Preconditions.checkNotNull(callback); - final ListenableFuture> future = rpc.invokeRpc(NetconfMessageTransformUtil.NETCONF_COMMIT_QNAME, NetconfMessageTransformUtil.COMMIT_RPC_CONTENT); + final ListenableFuture future = rpc.invokeRpc(toPath(NetconfMessageTransformUtil.NETCONF_COMMIT_QNAME), NetconfMessageTransformUtil.COMMIT_RPC_CONTENT); Futures.addCallback(future, callback); return future; } - public ListenableFuture> validate(final FutureCallback> callback, final QName datastore) { + public ListenableFuture validate(final FutureCallback callback, final QName datastore) { Preconditions.checkNotNull(callback); Preconditions.checkNotNull(datastore); - final ListenableFuture> future = rpc.invokeRpc(NetconfMessageTransformUtil.NETCONF_VALIDATE_QNAME, getValidateContent(datastore)); + final ListenableFuture future = rpc.invokeRpc(toPath(NetconfMessageTransformUtil.NETCONF_VALIDATE_QNAME), getValidateContent(datastore)); Futures.addCallback(future, callback); return future; } - public ListenableFuture> validateCandidate(final FutureCallback> callback) { + public ListenableFuture validateCandidate(final FutureCallback callback) { return validate(callback, NETCONF_CANDIDATE_QNAME); } - public ListenableFuture> validateRunning(final FutureCallback> callback) { + public ListenableFuture validateRunning(final FutureCallback callback) { return validate(callback, NETCONF_RUNNING_QNAME); } - public ListenableFuture> copyConfig(final FutureCallback> callback, final QName source, final QName target) { + public ListenableFuture copyConfig(final FutureCallback callback, final QName source, final QName target) { Preconditions.checkNotNull(callback); Preconditions.checkNotNull(source); Preconditions.checkNotNull(target); - final ListenableFuture> future = rpc.invokeRpc(NetconfMessageTransformUtil.NETCONF_COPY_CONFIG_QNAME, getCopyConfigContent(source, target)); + final ListenableFuture future = rpc.invokeRpc(toPath(NetconfMessageTransformUtil.NETCONF_COPY_CONFIG_QNAME), getCopyConfigContent(source, target)); Futures.addCallback(future, callback); return future; } - public ListenableFuture> copyRunningToCandidate(final FutureCallback> callback) { + public ListenableFuture copyRunningToCandidate(final FutureCallback callback) { return copyConfig(callback, NETCONF_RUNNING_QNAME, NETCONF_CANDIDATE_QNAME); } - public ListenableFuture> getConfig(final FutureCallback> callback, final QName datastore, final Optional filterPath) { + public ListenableFuture getConfig(final FutureCallback callback, final QName datastore, final Optional filterPath) { Preconditions.checkNotNull(callback); Preconditions.checkNotNull(datastore); - final ListenableFuture> future; - if (filterPath.isPresent()) { - final Node node = toFilterStructure(filterPath.get()); - future = rpc.invokeRpc(NETCONF_GET_CONFIG_QNAME, + final ListenableFuture future; + if (isFilterPresent(filterPath)) { + // FIXME the source node has to be wrapped in a choice + final DataContainerChild node = toFilterStructure(filterPath.get(), schemaContext); + future = rpc.invokeRpc(toPath(NETCONF_GET_CONFIG_QNAME), NetconfMessageTransformUtil.wrap(NETCONF_GET_CONFIG_QNAME, getSourceNode(datastore), node)); } else { - future = rpc.invokeRpc(NETCONF_GET_CONFIG_QNAME, + future = rpc.invokeRpc(toPath(NETCONF_GET_CONFIG_QNAME), NetconfMessageTransformUtil.wrap(NETCONF_GET_CONFIG_QNAME, getSourceNode(datastore))); } @@ -166,104 +172,117 @@ public final class NetconfBaseOps { return future; } - public ListenableFuture> getConfigRunning(final FutureCallback> callback, final Optional filterPath) { + public ListenableFuture getConfigRunning(final FutureCallback callback, final Optional filterPath) { return getConfig(callback, NETCONF_RUNNING_QNAME, filterPath); } - public ListenableFuture> getConfigCandidate(final FutureCallback> callback, final Optional filterPath) { + public ListenableFuture getConfigCandidate(final FutureCallback callback, final Optional filterPath) { return getConfig(callback, NETCONF_CANDIDATE_QNAME, filterPath); } - public ListenableFuture> get(final FutureCallback> callback, final Optional filterPath) { + public ListenableFuture get(final FutureCallback callback, final Optional filterPath) { Preconditions.checkNotNull(callback); - final ListenableFuture> future; - final Node node = filterPath.isPresent() ? toFilterStructure(filterPath.get()) : NetconfMessageTransformUtil.GET_RPC_CONTENT; - future = rpc.invokeRpc(NETCONF_GET_QNAME, NetconfMessageTransformUtil.wrap(NETCONF_GET_QNAME, node)); + final ListenableFuture future; + + future = isFilterPresent(filterPath) ? + rpc.invokeRpc(toPath(NETCONF_GET_QNAME), NetconfMessageTransformUtil.wrap(NETCONF_GET_QNAME, toFilterStructure(filterPath.get(), schemaContext))) : + rpc.invokeRpc(toPath(NETCONF_GET_QNAME), NetconfMessageTransformUtil.GET_RPC_CONTENT); Futures.addCallback(future, callback); return future; } - public ListenableFuture> editConfigCandidate(final FutureCallback> callback, final CompositeNode editStructure, final ModifyAction modifyAction, final boolean rollback) { + private boolean isFilterPresent(final Optional filterPath) { + return filterPath.isPresent() && Iterables.isEmpty(filterPath.get().getPathArguments()) == false; + } + + public ListenableFuture editConfigCandidate(final FutureCallback callback, final DataContainerChild editStructure, final ModifyAction modifyAction, final boolean rollback) { return editConfig(callback, NETCONF_CANDIDATE_QNAME, editStructure, Optional.of(modifyAction), rollback); } - public ListenableFuture> editConfigCandidate(final FutureCallback> callback, final CompositeNode editStructure, final boolean rollback) { + public ListenableFuture editConfigCandidate(final FutureCallback callback, final DataContainerChild editStructure, final boolean rollback) { return editConfig(callback, NETCONF_CANDIDATE_QNAME, editStructure, Optional.absent(), rollback); } - public ListenableFuture> editConfigRunning(final FutureCallback> callback, final CompositeNode editStructure, final ModifyAction modifyAction, final boolean rollback) { + public ListenableFuture editConfigRunning(final FutureCallback callback, final DataContainerChild editStructure, final ModifyAction modifyAction, final boolean rollback) { return editConfig(callback, NETCONF_RUNNING_QNAME, editStructure, Optional.of(modifyAction), rollback); } - public ListenableFuture> editConfigRunning(final FutureCallback> callback, final CompositeNode editStructure, final boolean rollback) { + public ListenableFuture editConfigRunning(final FutureCallback callback, final DataContainerChild editStructure, final boolean rollback) { return editConfig(callback, NETCONF_RUNNING_QNAME, editStructure, Optional.absent(), rollback); } - public ListenableFuture> editConfig(final FutureCallback> callback, final QName datastore, final CompositeNode editStructure, final Optional modifyAction, final boolean rollback) { + public ListenableFuture editConfig(final FutureCallback callback, final QName datastore, final DataContainerChild editStructure, final Optional modifyAction, final boolean rollback) { Preconditions.checkNotNull(editStructure); Preconditions.checkNotNull(callback); Preconditions.checkNotNull(datastore); - final ListenableFuture> future = rpc.invokeRpc(NETCONF_EDIT_CONFIG_QNAME, getEditConfigContent(datastore, editStructure, modifyAction, rollback)); + final ListenableFuture future = rpc.invokeRpc(toPath(NETCONF_EDIT_CONFIG_QNAME), getEditConfigContent(datastore, editStructure, modifyAction, rollback)); Futures.addCallback(future, callback); return future; } - private CompositeNode getEditConfigContent(final QName datastore, final CompositeNode editStructure, final Optional defaultOperation, final boolean rollback) { - final CompositeNodeBuilder ret = ImmutableCompositeNode.builder(); + public DataContainerChild createEditConfigStrcture(final Optional> lastChild, final Optional operation, final YangInstanceIdentifier dataPath) { + return NetconfMessageTransformUtil.createEditConfigStructure(schemaContext, dataPath, operation, lastChild); + } + + private ContainerNode getEditConfigContent(final QName datastore, final DataContainerChild editStructure, final Optional defaultOperation, final boolean rollback) { + final DataContainerNodeAttrBuilder editBuilder = Builders.containerBuilder().withNodeIdentifier(toId(NETCONF_EDIT_CONFIG_QNAME)); // Target - ret.add(getTargetNode(datastore)); + editBuilder.withChild(getTargetNode(datastore)); // Default operation if(defaultOperation.isPresent()) { - final SimpleNode defOp = NodeFactory.createImmutableSimpleNode(NETCONF_DEFAULT_OPERATION_QNAME, null, NetconfMessageTransformUtil.modifyOperationToXmlString(defaultOperation.get())); - ret.add(defOp); + editBuilder.withChild(Builders.leafBuilder().withNodeIdentifier(toId(NETCONF_DEFAULT_OPERATION_QNAME)).withValue(NetconfMessageTransformUtil.modifyOperationToXmlString(defaultOperation.get())).build()); } // Error option if(rollback) { - ret.addLeaf(NETCONF_ERROR_OPTION_QNAME, ROLLBACK_ON_ERROR_OPTION); + editBuilder.withChild(Builders.leafBuilder().withNodeIdentifier(toId(NETCONF_ERROR_OPTION_QNAME)).withValue(ROLLBACK_ON_ERROR_OPTION).build()); } - ret.setQName(NETCONF_EDIT_CONFIG_QNAME); // Edit content - ret.add(editStructure); - return ret.toInstance(); + editBuilder.withChild(editStructure); + return editBuilder.build(); } - private static CompositeNode getSourceNode(final QName datastore) { - return NodeFactory.createImmutableCompositeNode(NETCONF_SOURCE_QNAME, null, - Collections.> singletonList(new SimpleNodeTOImpl<>(datastore, null, null))); + public static DataContainerChild getSourceNode(final QName datastore) { + return Builders.containerBuilder().withNodeIdentifier(toId(NETCONF_SOURCE_QNAME)) + .withChild( + Builders.choiceBuilder().withNodeIdentifier(toId(ConfigSource.QNAME)).withChild( + Builders.leafBuilder().withNodeIdentifier(toId(datastore)).build()).build() + ).build(); } - - public static CompositeNode getLockContent(final QName datastore) { - return NodeFactory.createImmutableCompositeNode(NETCONF_LOCK_QNAME, null, Collections.>singletonList( - getTargetNode(datastore))); + public static NormalizedNode getLockContent(final QName datastore) { + return Builders.containerBuilder().withNodeIdentifier(toId(NETCONF_LOCK_QNAME)) + .withChild(getTargetNode(datastore)).build(); } - private static CompositeNode getTargetNode(final QName datastore) { - return NodeFactory.createImmutableCompositeNode(NETCONF_TARGET_QNAME, null, Collections.>singletonList( - NodeFactory.createImmutableSimpleNode(datastore, null, null) - )); + public static DataContainerChild getTargetNode(final QName datastore) { + return Builders.containerBuilder().withNodeIdentifier(toId(NETCONF_TARGET_QNAME)) + .withChild( + Builders.choiceBuilder().withNodeIdentifier(toId(ConfigTarget.QNAME)).withChild( + Builders.leafBuilder().withNodeIdentifier(toId(datastore)).build()).build() + ).build(); } - public static CompositeNode getCopyConfigContent(final QName source, final QName target) { - return NodeFactory.createImmutableCompositeNode(NETCONF_LOCK_QNAME, null, - Lists.> newArrayList(getTargetNode(target), getSourceNode(source))); + public static NormalizedNode getCopyConfigContent(final QName source, final QName target) { + return Builders.containerBuilder().withNodeIdentifier(toId(NETCONF_COPY_CONFIG_QNAME)) + .withChild(getTargetNode(target)).withChild(getSourceNode(source)).build(); } - public static CompositeNode getValidateContent(final QName source) { - return NodeFactory.createImmutableCompositeNode(NETCONF_VALIDATE_QNAME, null, Lists.> newArrayList(getSourceNode(source))); + public static NormalizedNode getValidateContent(final QName source) { + return Builders.containerBuilder().withNodeIdentifier(toId(NETCONF_VALIDATE_QNAME)) + .withChild(getSourceNode(source)).build(); } - public static CompositeNode getUnLockContent(final QName preferedDatastore) { - return NodeFactory.createImmutableCompositeNode(NETCONF_UNLOCK_QNAME, null, Collections.>singletonList( - getTargetNode(preferedDatastore))); + public static NormalizedNode getUnLockContent(final QName datastore) { + return Builders.containerBuilder().withNodeIdentifier(toId(NETCONF_UNLOCK_QNAME)) + .withChild(getTargetNode(datastore)).build(); } } diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/util/NetconfMessageTransformUtil.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/util/NetconfMessageTransformUtil.java index 371907e0fe..20085bb275 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/util/NetconfMessageTransformUtil.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/util/NetconfMessageTransformUtil.java @@ -9,23 +9,18 @@ package org.opendaylight.controller.sal.connect.netconf.util; import com.google.common.base.Optional; import com.google.common.base.Preconditions; -import com.google.common.base.Predicate; -import com.google.common.collect.Collections2; -import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import java.net.URI; -import java.util.ArrayList; import java.util.Collections; -import java.util.List; import java.util.Map; import java.util.Map.Entry; -import javax.annotation.Nullable; import org.opendaylight.controller.netconf.api.NetconfDocumentedException; import org.opendaylight.controller.netconf.api.NetconfMessage; import org.opendaylight.controller.netconf.util.messages.NetconfMessageUtil; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.base._1._0.rev110601.edit.config.input.EditContent; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.notification._1._0.rev080714.CreateSubscriptionInput; 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.notifications.rev120206.NetconfCapabilityChange; @@ -33,20 +28,20 @@ import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.common.RpcError; import org.opendaylight.yangtools.yang.common.RpcError.ErrorSeverity; import org.opendaylight.yangtools.yang.common.RpcResultBuilder; -import org.opendaylight.yangtools.yang.data.api.CompositeNode; import org.opendaylight.yangtools.yang.data.api.ModifyAction; -import org.opendaylight.yangtools.yang.data.api.Node; -import org.opendaylight.yangtools.yang.data.api.SimpleNode; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; -import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl; -import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode; -import org.opendaylight.yangtools.yang.data.impl.NodeFactory; -import org.opendaylight.yangtools.yang.data.impl.SimpleNodeTOImpl; -import org.opendaylight.yangtools.yang.data.impl.util.CompositeNodeBuilder; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; +import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.impl.schema.Builders; +import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder; +import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode; import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; +import org.opendaylight.yangtools.yang.model.api.NotificationDefinition; import org.opendaylight.yangtools.yang.model.api.RpcDefinition; import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.model.api.SchemaPath; import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -54,10 +49,13 @@ public class NetconfMessageTransformUtil { public static final String MESSAGE_ID_ATTR = "message-id"; public static final QName CREATE_SUBSCRIPTION_RPC_QNAME = QName.cachedReference(QName.create(CreateSubscriptionInput.QNAME, "create-subscription")); + private static final String SUBTREE = "subtree"; private NetconfMessageTransformUtil() {} public static final QName IETF_NETCONF_MONITORING = QName.create(NetconfState.QNAME, "ietf-netconf-monitoring"); + public static final QName GET_DATA_QNAME = QName.create(IETF_NETCONF_MONITORING, "data"); + public static final QName GET_SCHEMA_QNAME = QName.create(IETF_NETCONF_MONITORING, "get-schema"); public static final QName IETF_NETCONF_MONITORING_SCHEMA_FORMAT = QName.create(IETF_NETCONF_MONITORING, "format"); public static final QName IETF_NETCONF_MONITORING_SCHEMA_LOCATION = QName.create(IETF_NETCONF_MONITORING, "location"); public static final QName IETF_NETCONF_MONITORING_SCHEMA_IDENTIFIER = QName.create(IETF_NETCONF_MONITORING, "identifier"); @@ -67,9 +65,10 @@ public class NetconfMessageTransformUtil { public static final QName IETF_NETCONF_NOTIFICATIONS = QName.create(NetconfCapabilityChange.QNAME, "ietf-netconf-notifications"); public static URI NETCONF_URI = URI.create("urn:ietf:params:xml:ns:netconf:base:1.0"); - public static QName NETCONF_QNAME = QName.create(NETCONF_URI, null, "netconf"); + public static QName NETCONF_QNAME = QName.create(NETCONF_URI.toString(), "2011-06-01", "netconf"); public static QName NETCONF_DATA_QNAME = QName.create(NETCONF_QNAME, "data"); public static QName NETCONF_RPC_REPLY_QNAME = QName.create(NETCONF_QNAME, "rpc-reply"); + public static QName NETCONF_OK_QNAME = QName.create(NETCONF_QNAME, "ok"); public static QName NETCONF_ERROR_OPTION_QNAME = QName.create(NETCONF_QNAME, "error-option"); public static QName NETCONF_RUNNING_QNAME = QName.create(NETCONF_QNAME, "running"); public static QName NETCONF_SOURCE_QNAME = QName.create(NETCONF_QNAME, "source"); @@ -106,46 +105,29 @@ public class NetconfMessageTransformUtil { public static QName NETCONF_UNLOCK_QNAME = QName.create(NETCONF_QNAME, "unlock"); // Discard changes message - public static final CompositeNode DISCARD_CHANGES_RPC_CONTENT = - NodeFactory.createImmutableCompositeNode(NETCONF_DISCARD_CHANGES_QNAME, null, Collections.>emptyList()); + public static final ContainerNode DISCARD_CHANGES_RPC_CONTENT = + Builders.containerBuilder().withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(NETCONF_DISCARD_CHANGES_QNAME)).build(); // Commit changes message - public static final CompositeNode COMMIT_RPC_CONTENT = - NodeFactory.createImmutableCompositeNode(NETCONF_COMMIT_QNAME, null, Collections.>emptyList()); + public static final ContainerNode COMMIT_RPC_CONTENT = + Builders.containerBuilder().withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(NETCONF_COMMIT_QNAME)).build(); // Get message - public static final CompositeNode GET_RPC_CONTENT = - NodeFactory.createImmutableCompositeNode(NETCONF_GET_QNAME, null, Collections.>emptyList()); + public static final ContainerNode GET_RPC_CONTENT = + Builders.containerBuilder().withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(NETCONF_GET_QNAME)).build(); // Create-subscription changes message - public static final CompositeNode CREATE_SUBSCRIPTION_RPC_CONTENT = - NodeFactory.createImmutableCompositeNode(CREATE_SUBSCRIPTION_RPC_QNAME, null, Collections.>emptyList()); + public static final ContainerNode CREATE_SUBSCRIPTION_RPC_CONTENT = + Builders.containerBuilder().withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(CREATE_SUBSCRIPTION_RPC_QNAME)).build(); - public static Node toFilterStructure(final YangInstanceIdentifier identifier) { - Node previous = null; - if (Iterables.isEmpty(identifier.getPathArguments())) { - return null; - } + public static DataContainerChild toFilterStructure(final YangInstanceIdentifier identifier, final SchemaContext ctx) { + final DataContainerNodeAttrBuilder filterBuilder = Builders.containerBuilder().withNodeIdentifier(toId(NETCONF_FILTER_QNAME)); + filterBuilder.withAttributes(Collections.singletonMap(NETCONF_TYPE_QNAME, SUBTREE)); - for (final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument component : identifier.getReversePathArguments()) { - if (component instanceof YangInstanceIdentifier.NodeIdentifierWithPredicates) { - previous = toNode((YangInstanceIdentifier.NodeIdentifierWithPredicates)component, previous); - } else { - previous = toNode(component, previous); - } + if (Iterables.isEmpty(identifier.getPathArguments()) == false) { + filterBuilder.withChild((DataContainerChild) InstanceIdToNodes.serialize(ctx, identifier)); } - return filter("subtree", previous); - } - - static Node toNode(final YangInstanceIdentifier.NodeIdentifierWithPredicates argument, final Node node) { - final List> list = new ArrayList<>(); - for (final Map.Entry arg : argument.getKeyValues().entrySet()) { - list.add(new SimpleNodeTOImpl(arg.getKey(), null, arg.getValue())); - } - if (node != null) { - list.add(node); - } - return new CompositeNodeTOImpl(argument.getNodeType(), null, list); + return filterBuilder.build(); } public static void checkValidReply(final NetconfMessage input, final NetconfMessage output) @@ -154,7 +136,7 @@ public class NetconfMessageTransformUtil { final String outputMsgId = output.getDocument().getDocumentElement().getAttribute(MESSAGE_ID_ATTR); if(inputMsgId.equals(outputMsgId) == false) { - Map errorInfo = ImmutableMap.builder() + final Map errorInfo = ImmutableMap.builder() .put( "actual-message-id", outputMsgId ) .put( "expected-message-id", inputMsgId ) .build(); @@ -168,24 +150,22 @@ public class NetconfMessageTransformUtil { public static void checkSuccessReply(final NetconfMessage output) throws NetconfDocumentedException { if(NetconfMessageUtil.isErrorMessage(output)) { - throw NetconfDocumentedException.fromXMLDocument( output.getDocument() ); + throw NetconfDocumentedException.fromXMLDocument(output.getDocument()); } } - public static RpcError toRpcError( final NetconfDocumentedException ex ) - { - StringBuilder infoBuilder = new StringBuilder(); - Map errorInfo = ex.getErrorInfo(); - if( errorInfo != null ) - { - for( Entry e: errorInfo.entrySet() ) { + public static RpcError toRpcError( final NetconfDocumentedException ex ) { + final StringBuilder infoBuilder = new StringBuilder(); + final Map errorInfo = ex.getErrorInfo(); + if(errorInfo != null) { + for( final Entry e: errorInfo.entrySet() ) { infoBuilder.append( '<' ).append( e.getKey() ).append( '>' ).append( e.getValue() ) .append( "' ); } } - ErrorSeverity severity = toRpcErrorSeverity( ex.getErrorSeverity() ); + final ErrorSeverity severity = toRpcErrorSeverity( ex.getErrorSeverity() ); return severity == ErrorSeverity.ERROR ? RpcResultBuilder.newError( toRpcErrorType( ex.getErrorType() ), ex.getErrorTag().getTagValue(), @@ -204,8 +184,7 @@ public class NetconfMessageTransformUtil { } } - private static RpcError.ErrorType toRpcErrorType( final NetconfDocumentedException.ErrorType type ) - { + private static RpcError.ErrorType toRpcErrorType(final NetconfDocumentedException.ErrorType type) { switch( type ) { case protocol: return RpcError.ErrorType.PROTOCOL; @@ -218,36 +197,12 @@ public class NetconfMessageTransformUtil { } } - public static CompositeNode flattenInput(final CompositeNode node) { - final QName inputQName = QName.create(node.getNodeType(), "input"); - final CompositeNode input = node.getFirstCompositeByName(inputQName); - if (input == null) { - return node; - } - if (input instanceof CompositeNode) { - - final List> nodes = ImmutableList.> builder() // - .addAll(input.getValue()) // - .addAll(Collections2.filter(node.getValue(), new Predicate>() { - @Override - public boolean apply(@Nullable final Node input) { - return !inputQName.equals(input.getNodeType()); - } - })) // - .build(); - - return ImmutableCompositeNode.create(node.getNodeType(), nodes); - } - - return input; + public static YangInstanceIdentifier.NodeIdentifier toId(final YangInstanceIdentifier.PathArgument qname) { + return toId(qname.getNodeType()); } - static Node toNode(final YangInstanceIdentifier.PathArgument argument, final Node node) { - if (node != null) { - return new CompositeNodeTOImpl(argument.getNodeType(), null, Collections.> singletonList(node)); - } else { - return new SimpleNodeTOImpl(argument.getNodeType(), null, null); - } + public static YangInstanceIdentifier.NodeIdentifier toId(final QName nodeType) { + return new YangInstanceIdentifier.NodeIdentifier(nodeType); } public static Element getDataSubtree(final Document doc) { @@ -260,14 +215,6 @@ public class NetconfMessageTransformUtil { NETCONF_GET_QNAME.getLocalName())); } - public static boolean isGetOperation(final QName rpc) { - return NETCONF_URI.equals(rpc.getNamespace()) && rpc.getLocalName().equals(NETCONF_GET_QNAME.getLocalName()); - } - - public static boolean isGetConfigOperation(final QName rpc) { - return NETCONF_URI.equals(rpc.getNamespace()) && rpc.getLocalName().equals(NETCONF_GET_CONFIG_QNAME.getLocalName()); - } - public static boolean isDataEditOperation(final QName rpc) { return NETCONF_URI.equals(rpc.getNamespace()) && rpc.getLocalName().equals(NETCONF_EDIT_CONFIG_QNAME.getLocalName()); @@ -298,6 +245,16 @@ public class NetconfMessageTransformUtil { return new NodeContainerProxy(NETCONF_RPC_QNAME, Sets.newHashSet(editConfigProxy)); } + public static ContainerSchemaNode createSchemaForDataRead(final SchemaContext schemaContext) { + final QName config = QName.create(NETCONF_EDIT_CONFIG_QNAME, "data"); + return new NodeContainerProxy(config, schemaContext.getChildNodes()); + } + + + public static ContainerSchemaNode createSchemaForNotification(final NotificationDefinition next) { + return new NodeContainerProxy(next.getQName(), next.getChildNodes(), next.getAvailableAugmentations()); + } + /** * Creates artificial schema node for edit-config rpc. This artificial schema looks like: *

@@ -348,7 +305,6 @@ public class NetconfMessageTransformUtil {
         return new NodeContainerProxy(NETCONF_RPC_QNAME, Sets.newHashSet(editConfigProxy));
     }
 
-
     public static Optional findSchemaForRpc(final QName rpcName, final SchemaContext schemaContext) {
         Preconditions.checkNotNull(rpcName);
         Preconditions.checkNotNull(schemaContext);
@@ -382,94 +338,29 @@ public class NetconfMessageTransformUtil {
         return new NodeContainerProxy(NETCONF_RPC_QNAME, Sets.newHashSet(rpcBodyProxy));
     }
 
-    public static CompositeNodeTOImpl wrap(final QName name, final Node node) {
-        if (node != null) {
-            return new CompositeNodeTOImpl(name, null, Collections.> singletonList(node));
-        } else {
-            return new CompositeNodeTOImpl(name, null, Collections.> emptyList());
-        }
+    public static ContainerNode wrap(final QName name, final DataContainerChild... node) {
+        return Builders.containerBuilder().withNodeIdentifier(toId(name)).withValue(Lists.newArrayList(node)).build();
     }
 
-    public static CompositeNodeTOImpl wrap(final QName name, final Node additional, final Node node) {
-        if (node != null) {
-            return new CompositeNodeTOImpl(name, null, ImmutableList.of(additional, node));
+    public static DataContainerChild createEditConfigStructure(final SchemaContext ctx, final YangInstanceIdentifier dataPath,
+                                                                     final Optional operation, final Optional> lastChildOverride) {
+        // TODO The config element inside the EditContent should be AnyXml not Container, but AnyXml is based on outdated API
+        if(Iterables.isEmpty(dataPath.getPathArguments())) {
+            Preconditions.checkArgument(lastChildOverride.isPresent(), "Data has to be present when creating structure for top level element");
+            Preconditions.checkArgument(lastChildOverride.get() instanceof DataContainerChild,
+                    "Data has to be either container or a list node when creating structure for top level element, but was: %s", lastChildOverride.get());
+            return Builders.choiceBuilder().withNodeIdentifier(toId(EditContent.QNAME)).withChild(
+                    wrap(NETCONF_CONFIG_QNAME, ((DataContainerChild) lastChildOverride.get()))).build();
         } else {
-            return new CompositeNodeTOImpl(name, null, ImmutableList.> of(additional));
+            return Builders.choiceBuilder().withNodeIdentifier(toId(EditContent.QNAME)).withChild(
+                    wrap(NETCONF_CONFIG_QNAME, (DataContainerChild) InstanceIdToNodes.serialize(ctx, dataPath, lastChildOverride, operation))).build();
         }
     }
 
-    static ImmutableCompositeNode filter(final String type, final Node node) {
-        final CompositeNodeBuilder it = ImmutableCompositeNode.builder(); //
-        it.setQName(NETCONF_FILTER_QNAME);
-        it.setAttribute(NETCONF_TYPE_QNAME, type);
-        if (node != null) {
-            return it.add(node).toInstance();
-        } else {
-            return it.toInstance();
-        }
-    }
-
-    public static Node findNode(final CompositeNode node, final YangInstanceIdentifier identifier) {
-
-        Node current = node;
-        for (final YangInstanceIdentifier.PathArgument arg : identifier.getPathArguments()) {
-            if (current instanceof SimpleNode) {
-                return null;
-            } else if (current instanceof CompositeNode) {
-                final CompositeNode currentComposite = (CompositeNode) current;
-
-                current = currentComposite.getFirstCompositeByName(arg.getNodeType());
-                if (current == null) {
-                    current = currentComposite.getFirstCompositeByName(arg.getNodeType().withoutRevision());
-                }
-                if (current == null) {
-                    current = currentComposite.getFirstSimpleByName(arg.getNodeType());
-                }
-                if (current == null) {
-                    current = currentComposite.getFirstSimpleByName(arg.getNodeType().withoutRevision());
-                }
-                if (current == null) {
-                    return null;
-                }
-            }
-        }
-        return current;
-    }
-
-    public static String modifyOperationToXmlString(final ModifyAction operation) {
-        return operation.name().toLowerCase();
-    }
-
-
-    public static CompositeNode createEditConfigStructure(final YangInstanceIdentifier dataPath, final Optional operation,
-                                                    final Optional lastChildOverride) {
-        Preconditions.checkArgument(Iterables.isEmpty(dataPath.getPathArguments()) == false, "Instance identifier with empty path %s", dataPath);
-
-        List reversedPath = Lists.reverse(dataPath.getPath());
-
-        // Create deepest edit element with expected edit operation
-        CompositeNode previous = getDeepestEditElement(reversedPath.get(0), operation, lastChildOverride);
-
-        // Remove already processed deepest child
-        reversedPath = Lists.newArrayList(reversedPath);
-        reversedPath.remove(0);
-
-        // Create edit structure in reversed order
-        for (final YangInstanceIdentifier.PathArgument arg : reversedPath) {
-            final CompositeNodeBuilder builder = ImmutableCompositeNode.builder();
-            builder.setQName(arg.getNodeType());
-
-            addPredicatesToCompositeNodeBuilder(getPredicates(arg), builder);
-
-            builder.add(previous);
-            previous = builder.toInstance();
-        }
-        return ImmutableCompositeNode.create(NETCONF_CONFIG_QNAME, ImmutableList.>of(previous));
-    }
-
-    public static void addPredicatesToCompositeNodeBuilder(final Map predicates, final CompositeNodeBuilder builder) {
+    public static void addPredicatesToCompositeNodeBuilder(final Map predicates,
+                                                           final DataContainerNodeAttrBuilder builder) {
         for (final Map.Entry entry : predicates.entrySet()) {
-            builder.addLeaf(entry.getKey(), entry.getValue());
+            builder.withChild(Builders.leafBuilder().withNodeIdentifier(toId(entry.getKey())).withValue(entry.getValue()).build());
         }
     }
 
@@ -481,25 +372,11 @@ public class NetconfMessageTransformUtil {
         return predicates;
     }
 
-    public static CompositeNode getDeepestEditElement(final YangInstanceIdentifier.PathArgument arg, final Optional operation, final Optional lastChildOverride) {
-        final CompositeNodeBuilder builder = ImmutableCompositeNode.builder();
-        builder.setQName(arg.getNodeType());
-
-        final Map predicates = getPredicates(arg);
-        addPredicatesToCompositeNodeBuilder(predicates, builder);
-
-        if (operation.isPresent()) {
-            builder.setAttribute(NETCONF_OPERATION_QNAME, modifyOperationToXmlString(operation.get()));
-        }
-        if (lastChildOverride.isPresent()) {
-            final List> children = lastChildOverride.get().getValue();
-            for(final Node child : children) {
-                if(!predicates.containsKey(child.getKey())) {
-                    builder.add(child);
-                }
-            }
-        }
+    public static SchemaPath toPath(final QName rpc) {
+        return SchemaPath.create(true, rpc);
+    }
 
-        return builder.toInstance();
+    public static String modifyOperationToXmlString(final ModifyAction operation) {
+        return operation.name().toLowerCase();
     }
 }
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/util/NetconfRpcFutureCallback.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/util/NetconfRpcFutureCallback.java
index 6e1e9d764f..83c8803ecd 100644
--- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/util/NetconfRpcFutureCallback.java
+++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/util/NetconfRpcFutureCallback.java
@@ -9,17 +9,16 @@
 package org.opendaylight.controller.sal.connect.netconf.util;
 
 import com.google.common.util.concurrent.FutureCallback;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
 import org.opendaylight.controller.sal.connect.netconf.sal.tx.WriteRunningTx;
 import org.opendaylight.controller.sal.connect.util.RemoteDeviceId;
-import org.opendaylight.yangtools.yang.common.RpcResult;
-import org.opendaylight.yangtools.yang.data.api.CompositeNode;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 /**
  * Simple Netconf rpc logging callback
  */
-public class NetconfRpcFutureCallback implements FutureCallback> {
+public class NetconfRpcFutureCallback implements FutureCallback {
     private static final Logger LOG  = LoggerFactory.getLogger(WriteRunningTx.class);
 
     private final String type;
@@ -31,15 +30,15 @@ public class NetconfRpcFutureCallback implements FutureCallback result) {
-        if(result.isSuccessful()) {
+    public void onSuccess(final DOMRpcResult result) {
+        if(result.getErrors().isEmpty()) {
             LOG.trace("{}: " + type + " invoked successfully", id);
         } else {
             onUnsuccess(result);
         }
     }
 
-    protected void onUnsuccess(final RpcResult result) {
+    protected void onUnsuccess(final DOMRpcResult result) {
         LOG.warn("{}: " + type + " invoked unsuccessfully: {}", id, result.getErrors());
     }
 
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/util/NodeContainerProxy.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/util/NodeContainerProxy.java
index 1896e69f32..b499bd8f6f 100644
--- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/util/NodeContainerProxy.java
+++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/util/NodeContainerProxy.java
@@ -7,6 +7,7 @@
  */
 package org.opendaylight.controller.sal.connect.netconf.util;
 
+import com.google.common.base.Function;
 import com.google.common.base.Preconditions;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
@@ -31,22 +32,29 @@ class NodeContainerProxy implements ContainerSchemaNode {
 
     private final Map childNodes;
     private final QName qName;
+    private final Set availableAugmentations;
 
-    public NodeContainerProxy(final QName qName, final Map childNodes) {
+    public NodeContainerProxy(final QName qName, final Map childNodes, final Set availableAugmentations) {
+        this.availableAugmentations = availableAugmentations;
         this.childNodes = Preconditions.checkNotNull(childNodes, "childNodes");
-        this.qName = Preconditions.checkNotNull(qName, "qName");
+        this.qName = qName;
     }
 
     public NodeContainerProxy(final QName qName, final Collection childNodes) {
-        this(qName, asMap(childNodes));
+        this(qName, asMap(childNodes), Collections.emptySet());
+    }
+
+    public NodeContainerProxy(final QName qName, final Collection childNodes, final Set availableAugmentations) {
+        this(qName, asMap(childNodes), availableAugmentations);
     }
 
     private static Map asMap(final Collection childNodes) {
-        final Map mapped = Maps.newHashMap();
-        for (final DataSchemaNode childNode : childNodes) {
-            mapped.put(childNode.getQName(), childNode);
-        }
-        return mapped;
+        return Maps.uniqueIndex(childNodes, new Function() {
+            @Override
+            public QName apply(final DataSchemaNode input) {
+                return input.getQName();
+            }
+        });
     }
 
     @Override
@@ -86,7 +94,7 @@ class NodeContainerProxy implements ContainerSchemaNode {
 
     @Override
     public Set getAvailableAugmentations() {
-        throw new UnsupportedOperationException();
+        return availableAugmentations;
     }
 
     @Override
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/NetconfDeviceTest.java b/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/NetconfDeviceTest.java
index ec945e050b..61bd73a0a8 100644
--- a/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/NetconfDeviceTest.java
+++ b/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/NetconfDeviceTest.java
@@ -1,10 +1,10 @@
 /*
- * Copyright (c) 2014 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
- */
+* Copyright (c) 2014 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.controller.sal.connect.netconf;
 
 import static org.mockito.Matchers.any;
@@ -34,8 +34,12 @@ import org.junit.Test;
 import org.mockito.Mockito;
 import org.mockito.invocation.InvocationOnMock;
 import org.mockito.stubbing.Answer;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
+import org.opendaylight.controller.md.sal.dom.spi.DefaultDOMRpcResult;
 import org.opendaylight.controller.netconf.api.NetconfMessage;
 import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
 import org.opendaylight.controller.sal.connect.api.MessageTransformer;
 import org.opendaylight.controller.sal.connect.api.RemoteDeviceHandler;
 import org.opendaylight.controller.sal.connect.api.SchemaSourceProviderFactory;
@@ -45,13 +49,11 @@ import org.opendaylight.controller.sal.connect.netconf.sal.NetconfDeviceRpc;
 import org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil;
 import org.opendaylight.controller.sal.connect.util.RemoteDeviceId;
 import org.opendaylight.controller.sal.core.api.RpcImplementation;
-import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.common.RpcResult;
-import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
-import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
 import org.opendaylight.yangtools.yang.model.api.Module;
 import org.opendaylight.yangtools.yang.model.api.ModuleImport;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
 import org.opendaylight.yangtools.yang.model.repo.api.MissingSchemaSourceException;
 import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactory;
 import org.opendaylight.yangtools.yang.model.repo.api.SchemaResolutionException;
@@ -64,20 +66,24 @@ import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
 
 public class NetconfDeviceTest {
 
-    private static final NetconfMessage netconfMessage;
-    private static final CompositeNode compositeNode;
+    private static final NetconfMessage notification;
+
+    private static final ContainerNode compositeNode;
 
     static {
         try {
-            netconfMessage = mockClass(NetconfMessage.class);
-            compositeNode = mockClass(CompositeNode.class);
+            compositeNode = mockClass(ContainerNode.class);
         } catch (final Exception e) {
             throw new RuntimeException(e);
         }
+        try {
+            notification = new NetconfMessage(XmlUtil.readXmlToDocument(NetconfDeviceTest.class.getResourceAsStream("/notification-payload.xml")));
+        } catch (Exception e) {
+            throw new ExceptionInInitializerError(e);
+        }
     }
 
-    private static final  RpcResult rpcResult = RpcResultBuilder.success(netconfMessage).build();
-    private static final  RpcResult rpcResultC = RpcResultBuilder.success(compositeNode).build();
+    private static final DOMRpcResult rpcResultC = new DefaultDOMRpcResult(compositeNode);
 
     public static final String TEST_NAMESPACE = "test:namespace";
     public static final String TEST_MODULE = "test-module";
@@ -115,7 +121,7 @@ public class NetconfDeviceTest {
 
         final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO
                 = new NetconfDevice.SchemaResourcesDTO(getSchemaRegistry(), schemaFactory, stateSchemasResolver);
-        final NetconfDevice device = new NetconfDevice(schemaResourcesDTO, getId(), facade, getExecutor(), getMessageTransformer(), true);
+        final NetconfDevice device = new NetconfDevice(schemaResourcesDTO, getId(), facade, getExecutor(),true);
         // Monitoring not supported
         final NetconfSessionPreferences sessionCaps = getSessionCaps(false, capList);
         device.onRemoteSessionUp(sessionCaps, listener);
@@ -147,12 +153,12 @@ public class NetconfDeviceTest {
 
         final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO
                 = new NetconfDevice.SchemaResourcesDTO(getSchemaRegistry(), schemaFactory, stateSchemasResolver);
-        final NetconfDevice device = new NetconfDevice(schemaResourcesDTO, getId(), facade, getExecutor(), getMessageTransformer(), true);
+        final NetconfDevice device = new NetconfDevice(schemaResourcesDTO, getId(), facade, getExecutor(), true);
         // Monitoring supported
         final NetconfSessionPreferences sessionCaps = getSessionCaps(true, Lists.newArrayList(TEST_CAPABILITY, TEST_CAPABILITY2));
         device.onRemoteSessionUp(sessionCaps, listener);
 
-        Mockito.verify(facade, Mockito.timeout(5000)).onDeviceConnected(any(SchemaContext.class), any(NetconfSessionPreferences.class), any(RpcImplementation.class));
+        Mockito.verify(facade, Mockito.timeout(5000)).onDeviceConnected(any(SchemaContext.class), any(NetconfSessionPreferences.class), any(NetconfDeviceRpc.class));
         Mockito.verify(schemaFactory, times(2)).createSchemaContext(anyCollectionOf(SourceIdentifier.class));
     }
 
@@ -169,28 +175,26 @@ public class NetconfDeviceTest {
         final RemoteDeviceHandler facade = getFacade();
         final NetconfDeviceCommunicator listener = getListener();
 
-        final MessageTransformer messageTransformer = getMessageTransformer();
-
         final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO
                 = new NetconfDevice.SchemaResourcesDTO(getSchemaRegistry(), getSchemaFactory(), stateSchemasResolver);
-        final NetconfDevice device = new NetconfDevice(schemaResourcesDTO, getId(), facade, getExecutor(), messageTransformer, true);
+        final NetconfDevice device = new NetconfDevice(schemaResourcesDTO, getId(), facade, getExecutor(), true);
 
-        device.onNotification(netconfMessage);
-        device.onNotification(netconfMessage);
+        device.onNotification(notification);
+        device.onNotification(notification);
 
-        verify(facade, times(0)).onNotification(any(CompositeNode.class));
+        verify(facade, times(0)).onNotification(any(ContainerNode.class));
 
         final NetconfSessionPreferences sessionCaps = getSessionCaps(true,
                 Lists.newArrayList(TEST_CAPABILITY));
 
-        device.onRemoteSessionUp(sessionCaps, listener);
+        final DOMRpcService deviceRpc = mock(DOMRpcService.class);
+
+        device.handleSalInitializationSuccess(NetconfToNotificationTest.getNotificationSchemaContext(getClass()), sessionCaps, deviceRpc);
 
-        verify(messageTransformer, timeout(10000).times(2)).toNotification(netconfMessage);
-        verify(facade, timeout(10000).times(2)).onNotification(compositeNode);
+        verify(facade, timeout(10000).times(2)).onNotification(any(ContainerNode.class));
 
-        device.onNotification(netconfMessage);
-        verify(messageTransformer, timeout(10000).times(3)).toNotification(netconfMessage);
-        verify(facade, timeout(10000).times(3)).onNotification(compositeNode);
+        device.onNotification(notification);
+        verify(facade, timeout(10000).times(3)).onNotification(any(ContainerNode.class));
     }
 
     @Test
@@ -199,18 +203,16 @@ public class NetconfDeviceTest {
         final NetconfDeviceCommunicator listener = getListener();
 
         final SchemaContextFactory schemaContextProviderFactory = getSchemaFactory();
-        final MessageTransformer messageTransformer = getMessageTransformer();
 
         final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO
                 = new NetconfDevice.SchemaResourcesDTO(getSchemaRegistry(), schemaContextProviderFactory, stateSchemasResolver);
-        final NetconfDevice device = new NetconfDevice(schemaResourcesDTO, getId(), facade, getExecutor(), messageTransformer, true);
+        final NetconfDevice device = new NetconfDevice(schemaResourcesDTO, getId(), facade, getExecutor(), true);
         final NetconfSessionPreferences sessionCaps = getSessionCaps(true,
                 Lists.newArrayList(TEST_NAMESPACE + "?module=" + TEST_MODULE + "&revision=" + TEST_REVISION));
         device.onRemoteSessionUp(sessionCaps, listener);
 
         verify(schemaContextProviderFactory, timeout(5000)).createSchemaContext(any(Collection.class));
-        verify(messageTransformer, timeout(5000)).onGlobalContextUpdated(any(SchemaContext.class));
-        verify(facade, timeout(5000)).onDeviceConnected(any(SchemaContext.class), any(NetconfSessionPreferences.class), any(RpcImplementation.class));
+        verify(facade, timeout(5000)).onDeviceConnected(any(SchemaContext.class), any(NetconfSessionPreferences.class), any(DOMRpcService.class));
 
         device.onRemoteSessionDown();
         verify(facade, timeout(5000)).onDeviceDisconnected();
@@ -218,8 +220,7 @@ public class NetconfDeviceTest {
         device.onRemoteSessionUp(sessionCaps, listener);
 
         verify(schemaContextProviderFactory, timeout(5000).times(2)).createSchemaContext(any(Collection.class));
-        verify(messageTransformer, timeout(5000).times(3)).onGlobalContextUpdated(any(SchemaContext.class));
-        verify(facade, timeout(5000).times(2)).onDeviceConnected(any(SchemaContext.class), any(NetconfSessionPreferences.class), any(RpcImplementation.class));
+        verify(facade, timeout(5000).times(2)).onDeviceConnected(any(SchemaContext.class), any(NetconfSessionPreferences.class), any(DOMRpcService.class));
     }
 
     private SchemaContextFactory getSchemaFactory() {
@@ -239,9 +240,9 @@ public class NetconfDeviceTest {
 
     private RemoteDeviceHandler getFacade() throws Exception {
         final RemoteDeviceHandler remoteDeviceHandler = mockCloseableClass(RemoteDeviceHandler.class);
-        doNothing().when(remoteDeviceHandler).onDeviceConnected(any(SchemaContext.class), any(NetconfSessionPreferences.class), any(RpcImplementation.class));
+        doNothing().when(remoteDeviceHandler).onDeviceConnected(any(SchemaContext.class), any(NetconfSessionPreferences.class), any(NetconfDeviceRpc.class));
         doNothing().when(remoteDeviceHandler).onDeviceDisconnected();
-        doNothing().when(remoteDeviceHandler).onNotification(any(CompositeNode.class));
+        doNothing().when(remoteDeviceHandler).onNotification(any(ContainerNode.class));
         return remoteDeviceHandler;
     }
 
@@ -277,10 +278,9 @@ public class NetconfDeviceTest {
 
     public MessageTransformer getMessageTransformer() throws Exception {
         final MessageTransformer messageTransformer = mockClass(MessageTransformer.class);
-        doReturn(netconfMessage).when(messageTransformer).toRpcRequest(any(QName.class), any(CompositeNode.class));
-        doReturn(rpcResultC).when(messageTransformer).toRpcResult(any(NetconfMessage.class), any(QName.class));
+        doReturn(notification).when(messageTransformer).toRpcRequest(any(SchemaPath.class), any(ContainerNode.class));
+        doReturn(rpcResultC).when(messageTransformer).toRpcResult(any(NetconfMessage.class), any(SchemaPath.class));
         doReturn(compositeNode).when(messageTransformer).toNotification(any(NetconfMessage.class));
-        doNothing().when(messageTransformer).onGlobalContextUpdated(any(SchemaContext.class));
         return messageTransformer;
     }
 
@@ -301,7 +301,7 @@ public class NetconfDeviceTest {
 
     public NetconfDeviceCommunicator getListener() throws Exception {
         final NetconfDeviceCommunicator remoteDeviceCommunicator = mockCloseableClass(NetconfDeviceCommunicator.class);
-        doReturn(Futures.immediateFuture(rpcResult)).when(remoteDeviceCommunicator).sendRequest(any(NetconfMessage.class), any(QName.class));
+//        doReturn(Futures.immediateFuture(rpcResult)).when(remoteDeviceCommunicator).sendRequest(any(NetconfMessage.class), any(QName.class));
         return remoteDeviceCommunicator;
     }
 }
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/NetconfStateSchemasTest.java b/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/NetconfStateSchemasTest.java
index ccae5d4be8..405579e85c 100644
--- a/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/NetconfStateSchemasTest.java
+++ b/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/NetconfStateSchemasTest.java
@@ -1,25 +1,35 @@
 package org.opendaylight.controller.sal.connect.netconf;
 
+import static org.hamcrest.CoreMatchers.hasItem;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertThat;
-import static org.hamcrest.CoreMatchers.hasItem;
 
+import java.net.InetSocketAddress;
+import java.util.Collections;
 import java.util.Set;
 import org.junit.Test;
 import org.opendaylight.controller.netconf.util.xml.XmlUtil;
 import org.opendaylight.controller.sal.connect.util.RemoteDeviceId;
 import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.data.api.CompositeNode;
-import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlDocumentUtils;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlUtils;
+import org.opendaylight.yangtools.yang.data.impl.schema.transform.ToNormalizedNodeParser;
+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.w3c.dom.Document;
+import org.w3c.dom.Element;
 
 public class NetconfStateSchemasTest {
 
     @Test
     public void testCreate() throws Exception {
+        final DataSchemaNode schemasNode = ((ContainerSchemaNode) NetconfDevice.INIT_SCHEMA_CTX.getDataChildByName("netconf-state")).getDataChildByName("schemas");
+
         final Document schemasXml = XmlUtil.readXmlToDocument(getClass().getResourceAsStream("/netconf-state.schemas.payload.xml"));
-        final CompositeNode compositeNodeSchemas = (CompositeNode) XmlDocumentUtils.toDomNode(schemasXml);
-        final NetconfStateSchemas schemas = NetconfStateSchemas.create(new RemoteDeviceId("device"), compositeNodeSchemas);
+        final ToNormalizedNodeParser containerNodeParser = DomToNormalizedNodeParserFactory.getInstance(XmlUtils.DEFAULT_XML_CODEC_PROVIDER).getContainerNodeParser();
+        final ContainerNode compositeNodeSchemas = containerNodeParser.parse(Collections.singleton(schemasXml.getDocumentElement()), (ContainerSchemaNode) schemasNode);
+        final NetconfStateSchemas schemas = NetconfStateSchemas.create(new RemoteDeviceId("device", new InetSocketAddress(99)), compositeNodeSchemas);
 
         final Set availableYangSchemasQNames = schemas.getAvailableYangSchemasQNames();
         assertEquals(73, availableYangSchemasQNames.size());
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/NetconfToNotificationTest.java b/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/NetconfToNotificationTest.java
index 5d19188b12..157a3b719e 100644
--- a/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/NetconfToNotificationTest.java
+++ b/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/NetconfToNotificationTest.java
@@ -3,6 +3,8 @@ package org.opendaylight.controller.sal.connect.netconf;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
+
+import com.google.common.collect.Iterables;
 import java.io.InputStream;
 import java.util.Collections;
 import java.util.List;
@@ -13,7 +15,7 @@ import org.junit.Test;
 import org.opendaylight.controller.netconf.api.NetconfMessage;
 import org.opendaylight.controller.netconf.util.xml.XmlUtil;
 import org.opendaylight.controller.sal.connect.netconf.schema.mapping.NetconfMessageTransformer;
-import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
 import org.opendaylight.yangtools.yang.model.api.Module;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.opendaylight.yangtools.yang.model.parser.api.YangContextParser;
@@ -32,15 +34,9 @@ public class NetconfToNotificationTest {
     @SuppressWarnings("deprecation")
     @Before
     public void setup() throws Exception {
-        final List modelsToParse = Collections.singletonList(getClass().getResourceAsStream("/schemas/user-notification.yang"));
-        final YangContextParser parser = new YangParserImpl();
-        final Set modules = parser.parseYangModelsFromStreams(modelsToParse);
-        assertTrue(!modules.isEmpty());
-        final SchemaContext schemaContext = parser.resolveSchemaContext(modules);
-        assertNotNull(schemaContext);
+        final SchemaContext schemaContext = getNotificationSchemaContext(getClass());
 
-        messageTransformer = new NetconfMessageTransformer();
-        messageTransformer.onGlobalContextUpdated(schemaContext);
+        messageTransformer = new NetconfMessageTransformer(schemaContext);
 
         final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
         factory.setNamespaceAware(true);
@@ -52,12 +48,21 @@ public class NetconfToNotificationTest {
         userNotification = new NetconfMessage(doc);
     }
 
+    static SchemaContext getNotificationSchemaContext(Class loadClass) {
+        final List modelsToParse = Collections.singletonList(loadClass.getResourceAsStream("/schemas/user-notification.yang"));
+        final YangContextParser parser = new YangParserImpl();
+        final Set modules = parser.parseYangModelsFromStreams(modelsToParse);
+        assertTrue(!modules.isEmpty());
+        final SchemaContext schemaContext = parser.resolveSchemaContext(modules);
+        assertNotNull(schemaContext);
+        return schemaContext;
+    }
+
     @Test
     public void test() throws Exception {
-        final CompositeNode root = messageTransformer.toNotification(userNotification);
-
+        final ContainerNode root = messageTransformer.toNotification(userNotification);
         assertNotNull(root);
-        assertEquals(6, root.size());
-        assertEquals("user-visited-page", root.getKey().getLocalName());
+        assertEquals(6, Iterables.size(root.getValue()));
+        assertEquals("user-visited-page", root.getNodeType().getLocalName());
     }
 }
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/NetconfToRpcRequestTest.java b/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/NetconfToRpcRequestTest.java
index 3425739afd..49f0abafc7 100644
--- a/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/NetconfToRpcRequestTest.java
+++ b/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/NetconfToRpcRequestTest.java
@@ -3,7 +3,10 @@ package org.opendaylight.controller.sal.connect.netconf;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
+import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.toId;
+import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.toPath;
 
+import com.google.common.collect.Sets;
 import java.io.InputStream;
 import java.util.Collections;
 import java.util.List;
@@ -13,13 +16,12 @@ import org.junit.Test;
 import org.opendaylight.controller.netconf.api.NetconfMessage;
 import org.opendaylight.controller.netconf.util.xml.XmlUtil;
 import org.opendaylight.controller.sal.connect.netconf.schema.mapping.NetconfMessageTransformer;
-import org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil;
 import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.common.RpcResult;
-import org.opendaylight.yangtools.yang.data.api.CompositeNode;
-import org.opendaylight.yangtools.yang.data.api.Node;
-import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
-import org.opendaylight.yangtools.yang.data.impl.util.CompositeNodeBuilder;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
 import org.opendaylight.yangtools.yang.model.api.Module;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.opendaylight.yangtools.yang.model.parser.api.YangContextParser;
@@ -48,7 +50,6 @@ public class NetconfToRpcRequestTest {
     private final static QName GET_QNAME = QName.create(CONFIG_TEST_NAMESPACE, CONFIG_TEST_REVISION, "get");
     private final static QName GET_CONFIG_QNAME = QName.create(CONFIG_TEST_NAMESPACE, CONFIG_TEST_REVISION, "get-config");
 
-    static SchemaContext notifCtx;
     static SchemaContext cfgCtx;
     static NetconfMessageTransformer messageTransformer;
 
@@ -61,187 +62,29 @@ public class NetconfToRpcRequestTest {
         final Set notifModules = parser.parseYangModelsFromStreams(modelsToParse);
         assertTrue(!notifModules.isEmpty());
 
-        notifCtx = parser.resolveSchemaContext(notifModules);
-        assertNotNull(notifCtx);
-
         modelsToParse = Collections
             .singletonList(NetconfToRpcRequestTest.class.getResourceAsStream("/schemas/config-test-rpc.yang"));
         parser = new YangParserImpl();
         final Set configModules = parser.parseYangModelsFromStreams(modelsToParse);
-        cfgCtx = parser.resolveSchemaContext(configModules);
+        cfgCtx = parser.resolveSchemaContext(Sets.union(configModules, notifModules));
         assertNotNull(cfgCtx);
 
-        messageTransformer = new NetconfMessageTransformer();
-    }
-
-    @Test
-    public void testIsDataEditOperation() throws Exception {
-        messageTransformer.onGlobalContextUpdated(cfgCtx);
-
-        final CompositeNodeBuilder rootBuilder = ImmutableCompositeNode.builder();
-        rootBuilder.setQName(EDIT_CONFIG_QNAME);
-
-        final CompositeNodeBuilder inputBuilder = ImmutableCompositeNode.builder();
-        inputBuilder.setQName(QName.create(CONFIG_TEST_NAMESPACE, CONFIG_TEST_REVISION, "input"));
-
-        final CompositeNodeBuilder targetBuilder = ImmutableCompositeNode.builder();
-        targetBuilder.setQName(QName.create(CONFIG_TEST_NAMESPACE, CONFIG_TEST_REVISION, "target"));
-        targetBuilder.addLeaf(QName.create(CONFIG_TEST_NAMESPACE, CONFIG_TEST_REVISION, "running"), null);
-
-        final CompositeNodeBuilder configBuilder = ImmutableCompositeNode.builder();
-        configBuilder.setQName(QName.create(CONFIG_TEST_NAMESPACE, CONFIG_TEST_REVISION, "config"));
-
-        final CompositeNodeBuilder anyxmlTopBuilder = ImmutableCompositeNode.builder();
-        anyxmlTopBuilder.setQName(QName.create(CONFIG_TEST_NAMESPACE, CONFIG_TEST_REVISION, "top"));
-
-        final CompositeNodeBuilder anyxmlInterfBuilder = ImmutableCompositeNode.builder();
-        anyxmlInterfBuilder.setQName(QName.create(CONFIG_TEST_NAMESPACE, CONFIG_TEST_REVISION, "interface"));
-
-        anyxmlInterfBuilder.addLeaf(QName.create(CONFIG_TEST_NAMESPACE, CONFIG_TEST_REVISION, "name"), "Ethernet0/0");
-        anyxmlInterfBuilder.addLeaf(QName.create(CONFIG_TEST_NAMESPACE, CONFIG_TEST_REVISION, "mtu"), "1500");
-
-        anyxmlTopBuilder.add(anyxmlInterfBuilder.toInstance());
-        configBuilder.add(anyxmlTopBuilder.toInstance());
-
-        inputBuilder.add(targetBuilder.toInstance());
-        inputBuilder.add(configBuilder.toInstance());
-
-        rootBuilder.add(inputBuilder.toInstance());
-        final ImmutableCompositeNode root = rootBuilder.toInstance();
-
-        final NetconfMessage message = messageTransformer.toRpcRequest(EDIT_CONFIG_QNAME, root);
-        assertNotNull(message);
-
-        final Document xmlDoc = message.getDocument();
-        org.w3c.dom.Node rpcChild = xmlDoc.getFirstChild();
-        assertEquals(rpcChild.getLocalName(), "rpc");
-
-        final org.w3c.dom.Node editConfigNode = rpcChild.getFirstChild();
-        assertEquals(editConfigNode.getLocalName(), "edit-config");
-
-        final org.w3c.dom.Node targetNode = editConfigNode.getFirstChild();
-        assertEquals(targetNode.getLocalName(), "target");
-
-        final org.w3c.dom.Node runningNode = targetNode.getFirstChild();
-        assertEquals(runningNode.getLocalName(), "running");
-
-        final org.w3c.dom.Node configNode = targetNode.getNextSibling();
-        assertEquals(configNode.getLocalName(), "config");
-
-        final org.w3c.dom.Node topNode = configNode.getFirstChild();
-        assertEquals(topNode.getLocalName(), "top");
-
-        final org.w3c.dom.Node interfaceNode = topNode.getFirstChild();
-        assertEquals(interfaceNode.getLocalName(), "interface");
-
-        final org.w3c.dom.Node nameNode = interfaceNode.getFirstChild();
-        assertEquals(nameNode.getLocalName(), "name");
-
-        final org.w3c.dom.Node mtuNode = nameNode.getNextSibling();
-        assertEquals(mtuNode.getLocalName(), "mtu");
-    }
-
-    @Test
-    public void testIsGetOperation() throws Exception {
-        messageTransformer.onGlobalContextUpdated(cfgCtx);
-
-        final CompositeNodeBuilder rootBuilder = ImmutableCompositeNode.builder();
-        rootBuilder.setQName(GET_QNAME);
-
-        final CompositeNodeBuilder inputBuilder = ImmutableCompositeNode.builder();
-        inputBuilder.setQName(QName.create(CONFIG_TEST_NAMESPACE, CONFIG_TEST_REVISION, "input"));
-
-        rootBuilder.add(inputBuilder.toInstance());
-        final ImmutableCompositeNode root = rootBuilder.toInstance();
-
-        final NetconfMessage message = messageTransformer.toRpcRequest(GET_QNAME, root);
-        assertNotNull(message);
-
-        final Document xmlDoc = message.getDocument();
-        final org.w3c.dom.Node rpcChild = xmlDoc.getFirstChild();
-        assertEquals(rpcChild.getLocalName(), "rpc");
-
-        final org.w3c.dom.Node get = rpcChild.getFirstChild();
-        assertEquals(get.getLocalName(), "get");
+        messageTransformer = new NetconfMessageTransformer(cfgCtx);
     }
 
-    @Test
-    public void testIsGetConfigOperation() throws Exception {
-        messageTransformer.onGlobalContextUpdated(cfgCtx);
-
-        final CompositeNodeBuilder rootBuilder = ImmutableCompositeNode.builder();
-        rootBuilder.setQName(GET_CONFIG_QNAME);
-
-        final CompositeNodeBuilder inputBuilder = ImmutableCompositeNode.builder();
-        inputBuilder.setQName(QName.create(CONFIG_TEST_NAMESPACE, CONFIG_TEST_REVISION, "input"));
-
-        final CompositeNodeBuilder sourceBuilder = ImmutableCompositeNode.builder();
-        sourceBuilder.setQName(QName.create(CONFIG_TEST_NAMESPACE, CONFIG_TEST_REVISION, "source"));
-        sourceBuilder.addLeaf(QName.create(CONFIG_TEST_NAMESPACE, CONFIG_TEST_REVISION, "running"), null);
-
-        final CompositeNodeBuilder anyxmlFilterBuilder = ImmutableCompositeNode.builder();
-        anyxmlFilterBuilder.setQName(QName.create(CONFIG_TEST_NAMESPACE, CONFIG_TEST_REVISION, "filter"));
-
-        final CompositeNodeBuilder anyxmlTopBuilder = ImmutableCompositeNode.builder();
-        anyxmlTopBuilder.setQName(QName.create(CONFIG_TEST_NAMESPACE, CONFIG_TEST_REVISION, "top"));
-        anyxmlTopBuilder.addLeaf(QName.create(CONFIG_TEST_NAMESPACE, CONFIG_TEST_REVISION, "users"), null);
-
-        anyxmlFilterBuilder.add(anyxmlTopBuilder.toInstance());
-
-        inputBuilder.add(sourceBuilder.toInstance());
-        inputBuilder.add(anyxmlFilterBuilder.toInstance());
-        rootBuilder.add(inputBuilder.toInstance());
-        final ImmutableCompositeNode root = rootBuilder.toInstance();
-
-        final NetconfMessage message = messageTransformer.toRpcRequest(GET_CONFIG_QNAME, root);
-        assertNotNull(message);
-
-        final Document xmlDoc = message.getDocument();
-        final org.w3c.dom.Node rpcChild = xmlDoc.getFirstChild();
-        assertEquals(rpcChild.getLocalName(), "rpc");
-
-        final org.w3c.dom.Node getConfig = rpcChild.getFirstChild();
-        assertEquals(getConfig.getLocalName(), "get-config");
-
-        final org.w3c.dom.Node sourceNode = getConfig.getFirstChild();
-        assertEquals(sourceNode.getLocalName(), "source");
-
-        final org.w3c.dom.Node runningNode = sourceNode.getFirstChild();
-        assertEquals(runningNode.getLocalName(), "running");
-
-        final org.w3c.dom.Node filterNode = sourceNode.getNextSibling();
-        assertEquals(filterNode.getLocalName(), "filter");
-
-        final org.w3c.dom.Node topNode = filterNode.getFirstChild();
-        assertEquals(topNode.getLocalName(), "top");
-
-        final org.w3c.dom.Node usersNode = topNode.getFirstChild();
-        assertEquals(usersNode.getLocalName(), "users");
+    private LeafNode buildLeaf(final QName running, final Object value) {
+        return Builders.leafBuilder().withNodeIdentifier(toId(running)).withValue(value).build();
     }
 
     @Test
     public void testUserDefinedRpcCall() throws Exception {
-        messageTransformer.onGlobalContextUpdated(notifCtx);
-
-        final CompositeNodeBuilder rootBuilder = ImmutableCompositeNode.builder();
-        rootBuilder.setQName(SUBSCRIBE_RPC_NAME);
-
-        final CompositeNodeBuilder inputBuilder = ImmutableCompositeNode.builder();
-        inputBuilder.setQName(INPUT_QNAME);
-        inputBuilder.addLeaf(STREAM_NAME, "NETCONF");
+        final DataContainerNodeAttrBuilder rootBuilder = Builders.containerBuilder();
+        rootBuilder.withNodeIdentifier(toId(SUBSCRIBE_RPC_NAME));
 
-        rootBuilder.add(inputBuilder.toInstance());
-        final ImmutableCompositeNode root = rootBuilder.toInstance();
+        rootBuilder.withChild(buildLeaf(STREAM_NAME, "NETCONF"));
+        final ContainerNode root = rootBuilder.build();
 
-        final CompositeNode flattenedNode = NetconfMessageTransformUtil.flattenInput(root);
-        assertNotNull(flattenedNode);
-        assertEquals(1, flattenedNode.size());
-
-        final List inputNode = flattenedNode.getCompositesByName(INPUT_QNAME);
-        assertNotNull(inputNode);
-        assertTrue(inputNode.isEmpty());
-
-        final NetconfMessage message = messageTransformer.toRpcRequest(SUBSCRIBE_RPC_NAME, root);
+        final NetconfMessage message = messageTransformer.toRpcRequest(toPath(SUBSCRIBE_RPC_NAME), root);
         assertNotNull(message);
 
         final Document xmlDoc = message.getDocument();
@@ -256,7 +99,8 @@ public class NetconfToRpcRequestTest {
 
     }
 
-    @Test
+    // The edit config defined in yang has no output
+    @Test(expected = IllegalArgumentException.class)
     public void testRpcResponse() throws Exception {
         final NetconfMessage response = new NetconfMessage(XmlUtil.readXmlToDocument(
                 "\n" +
@@ -265,9 +109,8 @@ public class NetconfToRpcRequestTest {
                 "\n" +
                 "\n"
         ));
-        final RpcResult compositeNodeRpcResult = messageTransformer.toRpcResult(response, SUBSCRIBE_RPC_NAME);
-        final Node dataNode = compositeNodeRpcResult.getResult().getValue().get(0);
-        assertEquals("module schema", dataNode.getValue());
+
+        messageTransformer.toRpcResult(response, toPath(EDIT_CONFIG_QNAME));
     }
 
 }
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/NetconfDeviceWriteOnlyTxTest.java b/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/NetconfDeviceWriteOnlyTxTest.java
index a37fade915..4f6366ffd7 100644
--- a/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/NetconfDeviceWriteOnlyTxTest.java
+++ b/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/NetconfDeviceWriteOnlyTxTest.java
@@ -2,16 +2,18 @@ package org.opendaylight.controller.sal.connect.netconf.sal.tx;
 
 import static org.junit.Assert.fail;
 import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.same;
+import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
 import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.DISCARD_CHANGES_RPC_CONTENT;
 import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_CANDIDATE_QNAME;
 import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_RUNNING_QNAME;
+import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.toPath;
 
 import com.google.common.util.concurrent.CheckedFuture;
 import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
+import java.net.InetSocketAddress;
 import java.util.Collections;
 import org.junit.Before;
 import org.junit.Test;
@@ -20,46 +22,45 @@ import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 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.common.impl.util.compat.DataNormalizer;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
+import org.opendaylight.controller.md.sal.dom.spi.DefaultDOMRpcResult;
+import org.opendaylight.controller.sal.connect.netconf.NetconfDevice;
 import org.opendaylight.controller.sal.connect.netconf.listener.NetconfSessionPreferences;
 import org.opendaylight.controller.sal.connect.netconf.util.NetconfBaseOps;
 import org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil;
 import org.opendaylight.controller.sal.connect.util.RemoteDeviceId;
-import org.opendaylight.controller.sal.core.api.RpcImplementation;
-import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.common.RpcResult;
-import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
-import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.NetconfState;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
 
 public class NetconfDeviceWriteOnlyTxTest {
 
-    private final RemoteDeviceId id = new RemoteDeviceId("test-mount");
+    private final RemoteDeviceId id = new RemoteDeviceId("test-mount", new InetSocketAddress(99));
 
     @Mock
-    private RpcImplementation rpc;
-    @Mock
-    private DataNormalizer normalizer;
+    private DOMRpcService rpc;
     private YangInstanceIdentifier yangIId;
 
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
 
-        ListenableFuture> successFuture = Futures.immediateFuture(RpcResultBuilder.success().build());
+        final CheckedFuture successFuture =
+                Futures.immediateCheckedFuture(new DefaultDOMRpcResult(((NormalizedNode) null)));
 
         doReturn(successFuture)
-                .doReturn(Futures.>immediateFailedFuture(new IllegalStateException("Failed tx")))
+                .doReturn(Futures.immediateFailedCheckedFuture(new IllegalStateException("Failed tx")))
                 .doReturn(successFuture)
-                .when(rpc).invokeRpc(any(QName.class), any(CompositeNode.class));
+                .when(rpc).invokeRpc(any(SchemaPath.class), any(NormalizedNode.class));
 
-        yangIId = YangInstanceIdentifier.builder().node(QName.create("namespace", "2012-12-12", "name")).build();
-        doReturn(yangIId).when(normalizer).toLegacy(yangIId);
+        yangIId = YangInstanceIdentifier.builder().node(NetconfState.QNAME).build();
     }
 
     @Test
     public void testDiscardChanges() {
-        final WriteCandidateTx tx = new WriteCandidateTx(id, new NetconfBaseOps(rpc), normalizer,
+        final WriteCandidateTx tx = new WriteCandidateTx(id, new NetconfBaseOps(rpc, mock(SchemaContext.class)),
                 NetconfSessionPreferences.fromStrings(Collections.emptySet()));
         final CheckedFuture submitFuture = tx.submit();
         try {
@@ -67,10 +68,10 @@ public class NetconfDeviceWriteOnlyTxTest {
         } catch (final TransactionCommitFailedException e) {
             // verify discard changes was sent
             final InOrder inOrder = inOrder(rpc);
-            inOrder.verify(rpc).invokeRpc(NetconfMessageTransformUtil.NETCONF_LOCK_QNAME, NetconfBaseOps.getLockContent(NETCONF_CANDIDATE_QNAME));
-            inOrder.verify(rpc).invokeRpc(NetconfMessageTransformUtil.NETCONF_COMMIT_QNAME, NetconfMessageTransformUtil.COMMIT_RPC_CONTENT);
-            inOrder.verify(rpc).invokeRpc(NetconfMessageTransformUtil.NETCONF_DISCARD_CHANGES_QNAME, DISCARD_CHANGES_RPC_CONTENT);
-            inOrder.verify(rpc).invokeRpc(NetconfMessageTransformUtil.NETCONF_UNLOCK_QNAME, NetconfBaseOps.getUnLockContent(NETCONF_CANDIDATE_QNAME));
+            inOrder.verify(rpc).invokeRpc(toPath(NetconfMessageTransformUtil.NETCONF_LOCK_QNAME), NetconfBaseOps.getLockContent(NETCONF_CANDIDATE_QNAME));
+            inOrder.verify(rpc).invokeRpc(toPath(NetconfMessageTransformUtil.NETCONF_COMMIT_QNAME), NetconfMessageTransformUtil.COMMIT_RPC_CONTENT);
+            inOrder.verify(rpc).invokeRpc(toPath(NetconfMessageTransformUtil.NETCONF_DISCARD_CHANGES_QNAME), DISCARD_CHANGES_RPC_CONTENT);
+            inOrder.verify(rpc).invokeRpc(toPath(NetconfMessageTransformUtil.NETCONF_UNLOCK_QNAME), NetconfBaseOps.getUnLockContent(NETCONF_CANDIDATE_QNAME));
             return;
         }
 
@@ -79,20 +80,20 @@ public class NetconfDeviceWriteOnlyTxTest {
 
     @Test
     public void testDiscardChangesNotSentWithoutCandidate() {
-        doReturn(Futures.immediateFuture(RpcResultBuilder.success().build()))
-        .doReturn(Futures.>immediateFailedFuture(new IllegalStateException("Failed tx")))
-                .when(rpc).invokeRpc(any(QName.class), any(CompositeNode.class));
+        doReturn(Futures.immediateCheckedFuture(new DefaultDOMRpcResult(((NormalizedNode) null))))
+                .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), normalizer,
+        final WriteRunningTx tx = new WriteRunningTx(id, new NetconfBaseOps(rpc, NetconfDevice.INIT_SCHEMA_CTX),
                 NetconfSessionPreferences.fromStrings(Collections.emptySet()));
         try {
             tx.delete(LogicalDatastoreType.CONFIGURATION, yangIId);
         } catch (final Exception e) {
             // verify discard changes was sent
             final InOrder inOrder = inOrder(rpc);
-            inOrder.verify(rpc).invokeRpc(NetconfMessageTransformUtil.NETCONF_LOCK_QNAME, NetconfBaseOps.getLockContent(NETCONF_RUNNING_QNAME));
-            inOrder.verify(rpc).invokeRpc(same(NetconfMessageTransformUtil.NETCONF_EDIT_CONFIG_QNAME), any(CompositeNode.class));
-            inOrder.verify(rpc).invokeRpc(NetconfMessageTransformUtil.NETCONF_UNLOCK_QNAME, NetconfBaseOps.getUnLockContent(NETCONF_RUNNING_QNAME));
+            inOrder.verify(rpc).invokeRpc(toPath(NetconfMessageTransformUtil.NETCONF_LOCK_QNAME), NetconfBaseOps.getLockContent(NETCONF_RUNNING_QNAME));
+            inOrder.verify(rpc).invokeRpc(eq(toPath(NetconfMessageTransformUtil.NETCONF_EDIT_CONFIG_QNAME)), any(NormalizedNode.class));
+            inOrder.verify(rpc).invokeRpc(toPath(NetconfMessageTransformUtil.NETCONF_UNLOCK_QNAME), NetconfBaseOps.getUnLockContent(NETCONF_RUNNING_QNAME));
             return;
         }
 
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/ReadOnlyTxTest.java b/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/ReadOnlyTxTest.java
index 072cb185d0..e8587d609d 100644
--- a/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/ReadOnlyTxTest.java
+++ b/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/ReadOnlyTxTest.java
@@ -10,8 +10,10 @@ package org.opendaylight.controller.sal.connect.netconf.sal.tx;
 
 import static org.mockito.Matchers.any;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 
+import com.google.common.util.concurrent.Futures;
 import java.net.InetSocketAddress;
 import org.junit.Before;
 import org.junit.Test;
@@ -20,43 +22,42 @@ import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizationException;
-import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizer;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
+import org.opendaylight.controller.md.sal.dom.spi.DefaultDOMRpcResult;
 import org.opendaylight.controller.sal.connect.netconf.util.NetconfBaseOps;
 import org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil;
 import org.opendaylight.controller.sal.connect.util.RemoteDeviceId;
-import org.opendaylight.controller.sal.core.api.RpcImplementation;
-import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
-import org.opendaylight.yangtools.yang.data.api.CompositeNode;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
 
 public class ReadOnlyTxTest {
 
     private static final YangInstanceIdentifier path = YangInstanceIdentifier.create();
 
     @Mock
-    private RpcImplementation rpc;
+    private DOMRpcService rpc;
     @Mock
-    private DataNormalizer normalizer;
-    @Mock
-    private CompositeNode mockedNode;
+    private NormalizedNode mockedNode;
 
     @Before
     public void setUp() throws DataNormalizationException {
         MockitoAnnotations.initMocks(this);
-        doReturn(path).when(normalizer).toLegacy(any(YangInstanceIdentifier.class));
-        doReturn(com.google.common.util.concurrent.Futures.immediateFuture(RpcResultBuilder.success(mockedNode).build())).when(rpc).invokeRpc(any(org.opendaylight.yangtools.yang.common.QName.class), any(CompositeNode.class));
+        doReturn(Futures.immediateCheckedFuture(new DefaultDOMRpcResult(mockedNode))).when(rpc)
+                .invokeRpc(any(SchemaPath.class), any(NormalizedNode.class));
         doReturn("node").when(mockedNode).toString();
     }
 
     @Test
     public void testRead() throws Exception {
-        final NetconfBaseOps netconfOps = new NetconfBaseOps(rpc);
+        final NetconfBaseOps netconfOps = new NetconfBaseOps(rpc, mock(SchemaContext.class));
 
-        final ReadOnlyTx readOnlyTx = new ReadOnlyTx(netconfOps, normalizer, new RemoteDeviceId("a", new InetSocketAddress("localhost", 196)));
+        final ReadOnlyTx readOnlyTx = new ReadOnlyTx(netconfOps, new RemoteDeviceId("a", new InetSocketAddress("localhost", 196)));
 
         readOnlyTx.read(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.create());
-        verify(rpc).invokeRpc(Mockito.same(NetconfMessageTransformUtil.NETCONF_GET_CONFIG_QNAME), any(CompositeNode.class));
+        verify(rpc).invokeRpc(Mockito.eq(NetconfMessageTransformUtil.toPath(NetconfMessageTransformUtil.NETCONF_GET_CONFIG_QNAME)), any(NormalizedNode.class));
         readOnlyTx.read(LogicalDatastoreType.OPERATIONAL, path);
-        verify(rpc).invokeRpc(Mockito.same(NetconfMessageTransformUtil.NETCONF_GET_QNAME), any(CompositeNode.class));
+        verify(rpc).invokeRpc(Mockito.eq(NetconfMessageTransformUtil.toPath(NetconfMessageTransformUtil.NETCONF_GET_QNAME)), any(NormalizedNode.class));
     }
 }
\ No newline at end of file
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/schema/mapping/NetconfMessageTransformerTest.java b/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/schema/mapping/NetconfMessageTransformerTest.java
index 5a2c97c549..ea0941e8ee 100644
--- a/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/schema/mapping/NetconfMessageTransformerTest.java
+++ b/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/schema/mapping/NetconfMessageTransformerTest.java
@@ -10,63 +10,272 @@ package org.opendaylight.controller.sal.connect.netconf.schema.mapping;
 
 import static org.junit.Assert.assertEquals;
 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.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.GET_SCHEMA_QNAME;
+import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_CANDIDATE_QNAME;
+import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_COMMIT_QNAME;
+import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_DATA_QNAME;
+import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_DISCARD_CHANGES_QNAME;
+import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_EDIT_CONFIG_QNAME;
+import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_GET_CONFIG_QNAME;
+import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_GET_QNAME;
+import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_RUNNING_QNAME;
+import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.createEditConfigStructure;
+import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.toFilterStructure;
+import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.toId;
+import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.toPath;
 
-import java.io.InputStream;
+import com.google.common.base.Optional;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import java.io.IOException;
 import java.util.Collections;
 import java.util.List;
-import java.util.Set;
+import java.util.Map;
+import org.custommonkey.xmlunit.Diff;
+import org.custommonkey.xmlunit.XMLUnit;
 import org.hamcrest.CoreMatchers;
+import org.junit.Before;
 import org.junit.Test;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
 import org.opendaylight.controller.netconf.api.NetconfMessage;
 import org.opendaylight.controller.netconf.util.xml.XmlUtil;
-import org.opendaylight.controller.sal.connect.netconf.NetconfToRpcRequestTest;
+import org.opendaylight.controller.sal.connect.netconf.NetconfDevice;
+import org.opendaylight.controller.sal.connect.netconf.schema.NetconfRemoteSchemaYangSourceProvider;
+import org.opendaylight.controller.sal.connect.netconf.util.NetconfBaseOps;
+import org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.base._1._0.rev110601.$YangModuleInfoImpl;
+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.Schemas;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.schemas.Schema;
+import org.opendaylight.yangtools.sal.binding.generator.impl.ModuleInfoBackedContext;
 import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.common.RpcResult;
-import org.opendaylight.yangtools.yang.data.api.CompositeNode;
-import org.opendaylight.yangtools.yang.data.api.Node;
-import org.opendaylight.yangtools.yang.data.impl.NodeFactory;
-import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.data.api.ModifyAction;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.AnyXmlNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
+import org.xml.sax.SAXException;
 
 public class NetconfMessageTransformerTest {
 
-    private static final QName COMMIT_Q_NAME = QName.create("namespace", "2012-12-12", "commit");
+    private NetconfMessageTransformer netconfMessageTransformer;
+    private SchemaContext schema;
+
+    @Before
+    public void setUp() throws Exception {
+        XMLUnit.setIgnoreWhitespace(true);
+        XMLUnit.setIgnoreAttributeOrder(true);
+        XMLUnit.setIgnoreComments(true);
+
+        schema = getSchema();
+        netconfMessageTransformer = getTransformer(schema);
+
+    }
+
+    @Test
+    public void testDiscardChangesRequest() throws Exception {
+        final NetconfMessage netconfMessage = netconfMessageTransformer.toRpcRequest(toPath(NETCONF_DISCARD_CHANGES_QNAME),
+                NetconfMessageTransformUtil.DISCARD_CHANGES_RPC_CONTENT);
+        assertThat(XmlUtil.toString(netconfMessage.getDocument()), CoreMatchers.containsString("\n" +
+                "\n" +
+                "yang\n" +
+                "module\n" +
+                "2012-12-12\n" +
+                "\n" +
+                "");
+    }
+
+    @Test
+    public void tesGetSchemaResponse() throws Exception {
+        final NetconfMessageTransformer netconfMessageTransformer = getTransformer(getSchema());
+        final NetconfMessage response = new NetconfMessage(XmlUtil.readXmlToDocument(
+                "\n" +
+                        "\n" +
+                        "\n" +
+                        "Random YANG SCHEMA\n" +
+                        "\n" +
+                        "\n" +
+                        ""
+        ));
+        final DOMRpcResult compositeNodeRpcResult = netconfMessageTransformer.toRpcResult(response, toPath(GET_SCHEMA_QNAME));
+        assertTrue(compositeNodeRpcResult.getErrors().isEmpty());
+        assertNotNull(compositeNodeRpcResult.getResult());
+        final Object schemaContent = ((AnyXmlNode) ((ContainerNode) compositeNodeRpcResult.getResult()).getValue().iterator().next()).getValue().getValue();
+        assertThat(schemaContent.toString(), CoreMatchers.containsString("Random YANG SCHEMA"));
+    }
+
+    @Test
+    public void testGetConfigResponse() throws Exception {
+        final NetconfMessage response = new NetconfMessage(XmlUtil.readXmlToDocument("\n" +
+                "\n" +
+                "\n" +
+                "\n" +
+                "\n" +
+                "module\n" +
+                "2012-12-12\n" +
+                "yang\n" +
+                "\n" +
+                "\n" +
+                "\n" +
+                "\n" +
+                ""));
+
+        final NetconfMessageTransformer netconfMessageTransformer = getTransformer(getSchema());
+        final DOMRpcResult compositeNodeRpcResult = netconfMessageTransformer.toRpcResult(response, toPath(NETCONF_GET_CONFIG_QNAME));
+        assertTrue(compositeNodeRpcResult.getErrors().isEmpty());
+        assertNotNull(compositeNodeRpcResult.getResult());
+
+        final List> values = Lists.newArrayList(
+                NetconfRemoteSchemaYangSourceProvider.createGetSchemaRequest("module", Optional.of("2012-12-12")).getValue());
+
+        final Map keys = Maps.newHashMap();
+        for (final DataContainerChild value : values) {
+            keys.put(value.getNodeType(), value.getValue());
+        }
+
+        final YangInstanceIdentifier.NodeIdentifierWithPredicates identifierWithPredicates = new YangInstanceIdentifier.NodeIdentifierWithPredicates(Schema.QNAME, keys);
+        final MapEntryNode schemaNode = Builders.mapEntryBuilder().withNodeIdentifier(identifierWithPredicates).withValue(values).build();
+
+        final ContainerNode data = (ContainerNode) ((ContainerNode) compositeNodeRpcResult.getResult()).getChild(toId(NETCONF_DATA_QNAME)).get();
+        final ContainerNode state = (ContainerNode) data.getChild(toId(NetconfState.QNAME)).get();
+        final ContainerNode schemas = (ContainerNode) state.getChild(toId(Schemas.QNAME)).get();
+        final MapNode schemaParent = (MapNode) schemas.getChild(toId(Schema.QNAME)).get();
+        assertEquals(1, Iterables.size(schemaParent.getValue()));
+
+        assertEquals(schemaNode, schemaParent.getValue().iterator().next());
+    }
+
+    @Test
+    public void testGetConfigRequest() throws Exception {
+        final DataContainerChild filter = toFilterStructure(
+                YangInstanceIdentifier.create(toId(NetconfState.QNAME), toId(Schemas.QNAME)), schema);
+
+        final DataContainerChild source = NetconfBaseOps.getSourceNode(NETCONF_RUNNING_QNAME);
+
+        final NetconfMessage netconfMessage = netconfMessageTransformer.toRpcRequest(toPath(NETCONF_GET_CONFIG_QNAME),
+                NetconfMessageTransformUtil.wrap(NETCONF_GET_CONFIG_QNAME, source, filter));
+
+        assertSimilarXml(netconfMessage, "\n" +
+                "\n" +
+                "\n" +
+                "\n" +
+                "\n" +
+                "" +
+                "\n" +
+                "\n" +
+                "\n" +
+                "\n" +
+                "" +
+                "");
+    }
+
+    @Test
+    public void testEditConfigRequest() throws Exception {
+        final List> values = Lists.newArrayList(
+            NetconfRemoteSchemaYangSourceProvider.createGetSchemaRequest("module", Optional.of("2012-12-12")).getValue());
+
+        final Map keys = Maps.newHashMap();
+        for (final DataContainerChild value : values) {
+            keys.put(value.getNodeType(), value.getValue());
+        }
+
+        final YangInstanceIdentifier.NodeIdentifierWithPredicates identifierWithPredicates = new YangInstanceIdentifier.NodeIdentifierWithPredicates(Schema.QNAME, keys);
+        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.of(ModifyAction.REPLACE), Optional.>fromNullable(schemaNode));
+
+        final DataContainerChild target = NetconfBaseOps.getTargetNode(NETCONF_CANDIDATE_QNAME);
+
+        final ContainerNode wrap = NetconfMessageTransformUtil.wrap(NETCONF_EDIT_CONFIG_QNAME, editConfigStructure, target);
+        final NetconfMessage netconfMessage = netconfMessageTransformer.toRpcRequest(toPath(NETCONF_EDIT_CONFIG_QNAME), wrap);
+
+        assertSimilarXml(netconfMessage, "\n" +
+                "\n" +
+                "\n" +
+                "\n" +
+                "\n" +
+                "\n" +
+                "\n" +
+                "\n" +
+                "\n" +
+                "module\n" +
+                "2012-12-12\n" +
+                "yang\n" +
+                "\n" +
+                "\n" +
+                "\n" +
+                "\n" +
+                "\n" +
+                "");
+    }
+
+    private void assertSimilarXml(final NetconfMessage netconfMessage, final String xmlContent) throws SAXException, IOException {
+        final Diff diff = XMLUnit.compareXML(netconfMessage.getDocument(), XmlUtil.readXmlToDocument(xmlContent));
+        assertTrue(diff.toString(), diff.similar());
+    }
 
     @Test
-    public void testToRpcRequestNoSchemaForRequest() throws Exception {
-        final NetconfMessageTransformer netconfMessageTransformer = getTransformer();
-        final NetconfMessage netconfMessage = netconfMessageTransformer.toRpcRequest(COMMIT_Q_NAME,
-                NodeFactory.createImmutableCompositeNode(COMMIT_Q_NAME, null, Collections.>emptyList()));
-        assertThat(XmlUtil.toString(netconfMessage.getDocument()), CoreMatchers.containsString(" filter = toFilterStructure(
+                YangInstanceIdentifier.create(toId(NetconfState.QNAME), toId(Capabilities.QNAME), toId(capability), new YangInstanceIdentifier.NodeWithValue(capability, "a:b:c")), schema);
+
+        final NetconfMessage netconfMessage = netconfMessageTransformer.toRpcRequest(toPath(NETCONF_GET_QNAME),
+                NetconfMessageTransformUtil.wrap(NETCONF_GET_QNAME, filter));
+
+        assertSimilarXml(netconfMessage, "" +
+                "\n" +
+                "\n" +
+                "\n" +
+                "\n" +
+                "a:b:c\n" +
+                "\n" +
+                "" +
+                "\n" +
+                "" +
+                "");
     }
 
-    private NetconfMessageTransformer getTransformer() {
-        final NetconfMessageTransformer netconfMessageTransformer = new NetconfMessageTransformer();
-        netconfMessageTransformer.onGlobalContextUpdated(getSchema());
-        return netconfMessageTransformer;
+    private NetconfMessageTransformer getTransformer(final SchemaContext schema) {
+        return new NetconfMessageTransformer(schema);
     }
 
     @Test
-    public void testToRpcResultNoSchemaForResult() throws Exception {
-        final NetconfMessageTransformer netconfMessageTransformer = getTransformer();
+    public void testCommitResponse() throws Exception {
         final NetconfMessage response = new NetconfMessage(XmlUtil.readXmlToDocument(
-                ""
+                ""
         ));
-        final RpcResult compositeNodeRpcResult = netconfMessageTransformer.toRpcResult(response, COMMIT_Q_NAME);
-        assertTrue(compositeNodeRpcResult.isSuccessful());
-        assertEquals("ok", compositeNodeRpcResult.getResult().getValue().get(0).getKey().getLocalName());
+        final DOMRpcResult compositeNodeRpcResult = netconfMessageTransformer.toRpcResult(response, toPath(NETCONF_COMMIT_QNAME));
+        assertTrue(compositeNodeRpcResult.getErrors().isEmpty());
+        assertNull(compositeNodeRpcResult.getResult());
     }
 
     public SchemaContext getSchema() {
-        final List modelsToParse = Collections
-                .singletonList(NetconfToRpcRequestTest.class.getResourceAsStream("/schemas/rpc-notification-subscription.yang"));
-        final YangParserImpl parser = new YangParserImpl();
-        final Set configModules = parser.parseYangModelsFromStreams(modelsToParse);
-        final SchemaContext cfgCtx = parser.resolveSchemaContext(configModules);
-        assertNotNull(cfgCtx);
-        return cfgCtx;
+        final ModuleInfoBackedContext moduleInfoBackedContext = ModuleInfoBackedContext.create();
+        moduleInfoBackedContext.addModuleInfos(Collections.singleton($YangModuleInfoImpl.getInstance()));
+        moduleInfoBackedContext.addModuleInfos(Collections.singleton(org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.$YangModuleInfoImpl.getInstance()));
+        return moduleInfoBackedContext.tryToCreateSchemaContext().get();
     }
 }
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/util/InstanceIdToNodesTest.java b/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/util/InstanceIdToNodesTest.java
new file mode 100644
index 0000000000..39a8f42f49
--- /dev/null
+++ b/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/util/InstanceIdToNodesTest.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 2015 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.controller.sal.connect.netconf.util;
+
+import static org.junit.Assert.assertEquals;
+
+import com.google.common.base.Function;
+import com.google.common.collect.Collections2;
+import com.google.common.io.ByteSource;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Collections;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException;
+import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
+
+public class InstanceIdToNodesTest {
+
+    private static final String NS = "urn:opendaylight:params:xml:ns:yang:controller:md:sal:normalization:test";
+    private static final String REVISION = "2014-03-13";
+    private static final QName ID = QName.create(NS, REVISION, "id");
+    private SchemaContext ctx;
+
+    private final YangInstanceIdentifier.NodeIdentifier rootContainer = new YangInstanceIdentifier.NodeIdentifier(QName.create(NS, REVISION, "test"));
+    private final YangInstanceIdentifier.NodeIdentifier outerContainer = new YangInstanceIdentifier.NodeIdentifier(QName.create(NS, REVISION, "outer-container"));
+    private final YangInstanceIdentifier.NodeIdentifier augmentedLeaf = new YangInstanceIdentifier.NodeIdentifier(QName.create(NS, REVISION, "augmented-leaf"));
+    private final YangInstanceIdentifier.AugmentationIdentifier augmentation = new YangInstanceIdentifier.AugmentationIdentifier(Collections.singleton(augmentedLeaf.getNodeType()));
+
+    private final YangInstanceIdentifier.NodeIdentifier outerList = new YangInstanceIdentifier.NodeIdentifier(QName.create(NS, REVISION, "outer-list"));
+    private final YangInstanceIdentifier.NodeIdentifierWithPredicates outerListWithKey = new YangInstanceIdentifier.NodeIdentifierWithPredicates(QName.create(NS, REVISION, "outer-list"), ID, 1);
+    private final YangInstanceIdentifier.NodeIdentifier choice = new YangInstanceIdentifier.NodeIdentifier(QName.create(NS, REVISION, "outer-choice"));
+    private final YangInstanceIdentifier.NodeIdentifier leafFromCase = new YangInstanceIdentifier.NodeIdentifier(QName.create(NS, REVISION, "one"));
+
+    private final YangInstanceIdentifier.NodeIdentifier leafList = new YangInstanceIdentifier.NodeIdentifier(QName.create(NS, REVISION, "ordered-leaf-list"));
+    private final YangInstanceIdentifier.NodeWithValue leafListWithValue = new YangInstanceIdentifier.NodeWithValue(leafList.getNodeType(), "abcd");
+
+    static SchemaContext createTestContext() throws IOException, YangSyntaxErrorException {
+        final YangParserImpl parser = new YangParserImpl();
+        return parser.parseSources(Collections2.transform(Collections.singletonList("/schemas/filter-test.yang"), new Function() {
+            @Override
+            public ByteSource apply(final String input) {
+                return new ByteSource() {
+                    @Override
+                    public InputStream openStream() throws IOException {
+                        return InstanceIdToNodesTest.class.getResourceAsStream(input);
+                    }
+                };
+            }
+        }));
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        ctx = createTestContext();
+
+    }
+
+    @Test
+    public void testInAugment() throws Exception {
+        final ContainerNode expectedFilter = Builders.containerBuilder().withNodeIdentifier(rootContainer).withChild(
+                Builders.containerBuilder().withNodeIdentifier(outerContainer).withChild(
+                        Builders.augmentationBuilder().withNodeIdentifier(augmentation).withChild(
+                                Builders.leafBuilder().withNodeIdentifier(augmentedLeaf).build()
+                        ).build()
+                ).build()
+        ).build();
+
+        final NormalizedNode filter = InstanceIdToNodes.serialize(ctx, YangInstanceIdentifier.create(rootContainer, outerContainer, augmentation, augmentedLeaf));
+        assertEquals(expectedFilter, filter);
+    }
+
+    @Test
+    public void testInAugmentLeafOverride() throws Exception {
+        final LeafNode lastLeaf = Builders.leafBuilder().withNodeIdentifier(augmentedLeaf).withValue("randomValue").build();
+
+        final ContainerNode expectedFilter = Builders.containerBuilder().withNodeIdentifier(rootContainer).withChild(
+                Builders.containerBuilder().withNodeIdentifier(outerContainer).withChild(
+                        Builders.augmentationBuilder().withNodeIdentifier(augmentation).withChild(
+                                lastLeaf
+                        ).build()
+                ).build()
+        ).build();
+
+        final NormalizedNode filter = InstanceIdToNodes.serialize(ctx, YangInstanceIdentifier.create(rootContainer, outerContainer, augmentation, augmentedLeaf), lastLeaf);
+        assertEquals(expectedFilter, filter);
+    }
+
+    @Test
+    public void testListChoice() throws Exception {
+        final ContainerNode expectedFilter = Builders.containerBuilder().withNodeIdentifier(rootContainer).withChild(
+                Builders.mapBuilder().withNodeIdentifier(outerList).withChild(
+                        Builders.mapEntryBuilder().withNodeIdentifier(outerListWithKey).withChild(
+                                Builders.leafBuilder().withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(ID)).withValue(1).build()
+                        ).withChild(
+                                Builders.choiceBuilder().withNodeIdentifier(choice).withChild(
+                                        Builders.leafBuilder().withNodeIdentifier(leafFromCase).build()
+                                ).build()
+                        ).build()
+                ).build()
+        ).build();
+
+        final NormalizedNode filter = InstanceIdToNodes.serialize(ctx, YangInstanceIdentifier.create(rootContainer, outerList, outerListWithKey, choice, leafFromCase));
+        assertEquals(expectedFilter, filter);
+    }
+
+    @Test
+    public void testTopContainerLastChildOverride() throws Exception {
+        final ContainerNode expectedStructure = Builders.containerBuilder().withNodeIdentifier(rootContainer).withChild(
+                Builders.mapBuilder().withNodeIdentifier(outerList).withChild(
+                        Builders.mapEntryBuilder().withNodeIdentifier(outerListWithKey).withChild(
+                                Builders.leafBuilder().withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(ID)).withValue(1).build()
+                        ).withChild(
+                                Builders.choiceBuilder().withNodeIdentifier(choice).withChild(
+                                        Builders.leafBuilder().withNodeIdentifier(leafFromCase).build()
+                                ).build()
+                        ).build()
+                ).build()
+        ).build();
+
+        final NormalizedNode filter = InstanceIdToNodes.serialize(ctx, YangInstanceIdentifier.create(rootContainer), expectedStructure);
+        assertEquals(expectedStructure, filter);
+    }
+
+    @Test
+    public void testListLastChildOverride() throws Exception {
+        final MapEntryNode outerListEntry = Builders.mapEntryBuilder().withNodeIdentifier(outerListWithKey).withChild(
+                Builders.leafBuilder().withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(ID)).withValue(1).build()
+        ).build();
+        final MapNode lastChild = Builders.mapBuilder().withNodeIdentifier(this.outerList).withChild(
+                outerListEntry
+        ).build();
+        final ContainerNode expectedStructure = Builders.containerBuilder().withNodeIdentifier(rootContainer).withChild(
+                lastChild
+        ).build();
+
+        NormalizedNode filter = InstanceIdToNodes.serialize(ctx, YangInstanceIdentifier.create(rootContainer, outerList, outerListWithKey), outerListEntry);
+        assertEquals(expectedStructure, filter);
+        filter = InstanceIdToNodes.serialize(ctx, YangInstanceIdentifier.create(rootContainer, outerList, outerListWithKey));
+        assertEquals(expectedStructure, filter);
+    }
+
+    @Test
+    public void testLeafList() throws Exception {
+        final ContainerNode expectedFilter = Builders.containerBuilder().withNodeIdentifier(rootContainer).withChild(
+                Builders.orderedLeafSetBuilder().withNodeIdentifier(leafList).withChild(
+                        Builders.leafSetEntryBuilder().withNodeIdentifier(leafListWithValue).withValue(leafListWithValue.getValue()).build()
+                ).build()
+        ).build();
+
+        final NormalizedNode filter = InstanceIdToNodes.serialize(ctx, YangInstanceIdentifier.create(rootContainer, leafList, leafListWithValue));
+        assertEquals(expectedFilter, filter);
+    }
+}
\ No newline at end of file
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/test/resources/schemas/config-test-rpc.yang b/opendaylight/md-sal/sal-netconf-connector/src/test/resources/schemas/config-test-rpc.yang
index f67c2df256..18a4ead1b5 100644
--- a/opendaylight/md-sal/sal-netconf-connector/src/test/resources/schemas/config-test-rpc.yang
+++ b/opendaylight/md-sal/sal-netconf-connector/src/test/resources/schemas/config-test-rpc.yang
@@ -15,7 +15,7 @@ module config-test-rpc {
     revision 2014-07-21 {
         description "Initial revision.";
     }
-    
+
     extension get-filter-element-attributes {
           description
             "If this extension is present within an 'anyxml'
@@ -25,21 +25,21 @@ module config-test-rpc {
              following unqualified XML attribute is supported
              within the  element, within a  or
               protocol operation:
-    
+
                type : optional attribute with allowed
                       value strings 'subtree' and 'xpath'.
                       If missing, the default value is 'subtree'.
-    
+
              If the 'xpath' feature is supported, then the
              following unqualified XML attribute is
              also supported:
-    
+
                select: optional attribute containing a
                        string representing an XPath expression.
                        The 'type' attribute must be equal to 'xpath'
                        if this attribute is present.";
     }
-    
+
     rpc edit-config {
         description "The  operation loads all or part of a specified
              configuration to the specified target configuration.";
@@ -164,4 +164,13 @@ module config-test-rpc {
             }
         }
     }
+
+      rpc discard-changes {
+        if-feature candidate;
+
+        description
+          "Revert the candidate configuration to the current
+           running configuration.";
+        reference "RFC 6241, Section 8.3.4.2";
+      }
 }
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/test/resources/schemas/filter-test.yang b/opendaylight/md-sal/sal-netconf-connector/src/test/resources/schemas/filter-test.yang
new file mode 100644
index 0000000000..6df5306850
--- /dev/null
+++ b/opendaylight/md-sal/sal-netconf-connector/src/test/resources/schemas/filter-test.yang
@@ -0,0 +1,74 @@
+module normalization-test {
+    yang-version 1;
+    namespace "urn:opendaylight:params:xml:ns:yang:controller:md:sal:normalization:test";
+    prefix "norm-test";
+
+    revision "2014-03-13" {
+        description "Initial revision.";
+    }
+
+    grouping outer-grouping {
+    }
+
+    container test {
+        list outer-list {
+            key id;
+            leaf id {
+                type uint16;
+            }
+            choice outer-choice {
+                case one {
+                    leaf one {
+                        type string;
+                    }
+                }
+                case two-three {
+                    leaf two {
+                        type string;
+                    }
+                    leaf three {
+                        type string;
+                    }
+               }
+           }
+           list inner-list {
+                key name;
+                ordered-by user;
+
+                leaf name {
+                    type string;
+                }
+                leaf value {
+                    type string;
+                }
+            }
+        }
+
+        list unkeyed-list {
+            leaf name {
+                type string;
+            }
+        }
+
+        leaf-list unordered-leaf-list {
+            type string;
+        }
+
+        leaf-list ordered-leaf-list {
+            ordered-by user;
+            type string;
+        }
+
+        container outer-container {
+        }
+
+        anyxml any-xml-data;
+    }
+
+    augment /norm-test:test/norm-test:outer-container {
+
+        leaf augmented-leaf {
+           type string;
+        }
+    }
+}
\ No newline at end of file
diff --git a/opendaylight/netconf/pom.xml b/opendaylight/netconf/pom.xml
index a990b5c6cb..4899ebcb23 100644
--- a/opendaylight/netconf/pom.xml
+++ b/opendaylight/netconf/pom.xml
@@ -16,7 +16,7 @@
 
   
     netconf-api
-    netconf-cli
+    
     netconf-config
     netconf-impl
     config-netconf-connector