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 <mmarsale@cisco.com>
<packaging>bundle</packaging>
<dependencies>
- <dependency>
- <groupId>${project.groupId}</groupId>
- <artifactId>netconf-client</artifactId>
- <version>${netconf.version}</version>
- </dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>netconf-config-dispatcher</artifactId>
<groupId>org.opendaylight.controller</groupId>
<artifactId>sal-binding-api</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>binding-generator-impl</artifactId>
+ </dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>sal-binding-config</artifactId>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
- <dependency>
+ <dependency>
+ <groupId>xmlunit</groupId>
+ <artifactId>xmlunit</artifactId>
+ </dependency>
+ <dependency>
<groupId>${project.groupId}</groupId>
<artifactId>config-api</artifactId>
<version>${netconf.version}</version>
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;
final BindingAwareBroker bindingBroker = getBindingRegistryDependency();
final RemoteDeviceHandler<NetconfSessionPreferences> 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);
*/
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<M> extends SchemaContextListener {
+public interface MessageTransformer<M> {
- CompositeNode toNotification(M message);
+ ContainerNode toNotification(M message);
- M toRpcRequest(QName rpc, CompositeNode node);
+ M toRpcRequest(SchemaPath rpc, ContainerNode node);
- RpcResult<CompositeNode> toRpcResult(M message, QName rpc);
+ DOMRpcResult toRpcResult(M message, SchemaPath rpc);
}
*/
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<PREF> 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();
}
*/
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;
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;
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;
private static final Logger logger = LoggerFactory.getLogger(NetconfDevice.class);
+ /**
+ * Initial schema context contains schemas for netconf monitoring and netconf notifications
+ */
+ public static final SchemaContext INIT_SCHEMA_CTX;
+
+ static {
+ try {
+ final ModuleInfoBackedContext moduleInfoBackedContext = ModuleInfoBackedContext.create();
+ moduleInfoBackedContext.addModuleInfos(
+ Lists.newArrayList(
+ $YangModuleInfoImpl.getInstance(),
+ org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.notification._1._0.rev080714.$YangModuleInfoImpl.getInstance(),
+ org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.base._1._0.rev110601.$YangModuleInfoImpl.getInstance()));
+ INIT_SCHEMA_CTX = moduleInfoBackedContext.tryToCreateSchemaContext().get();
+ } catch (final RuntimeException e) {
+ logger.error("Unable to prepare schema context for netconf initialization", e);
+ throw new ExceptionInInitializerError(e);
+ }
+ }
+
public static final Function<QName, SourceIdentifier> QNAME_TO_SOURCE_ID_FUNCTION = new Function<QName, SourceIdentifier>() {
@Override
public SourceIdentifier apply(final QName input) {
private final RemoteDeviceHandler<NetconfSessionPreferences> salFacade;
private final ListeningExecutorService processingExecutor;
private final SchemaSourceRegistry schemaRegistry;
- private final MessageTransformer<NetconfMessage> messageTransformer;
private final NetconfStateSchemas.NetconfStateSchemasResolver stateSchemasResolver;
private final NotificationHandler notificationHandler;
private final List<SchemaSourceRegistration<? extends SchemaSourceRepresentation>> sourceRegistrations = Lists.newArrayList();
+ // Message transformer is constructed once the schemas are available
+ private MessageTransformer<NetconfMessage> messageTransformer;
+
public NetconfDevice(final SchemaResourcesDTO schemaResourcesDTO, final RemoteDeviceId id, final RemoteDeviceHandler<NetconfSessionPreferences> salFacade,
- final ExecutorService globalProcessingExecutor, final MessageTransformer<NetconfMessage> 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<NetconfSessionPreferences> salFacade,
- final ExecutorService globalProcessingExecutor, final MessageTransformer<NetconfMessage> 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
// http://netty.io/wiki/thread-model.html
logger.debug("{}: Session to remote device established with {}", id, remoteSessionCapabilities);
- final NetconfDeviceRpc deviceRpc = setUpDeviceRpc(listener);
-
- final DeviceSourcesResolver task = new DeviceSourcesResolver(deviceRpc, remoteSessionCapabilities, id, stateSchemasResolver);
+ final NetconfDeviceRpc initRpc = getRpcForInitialization(listener);
+ final DeviceSourcesResolver task = new DeviceSourcesResolver(remoteSessionCapabilities, id, stateSchemasResolver, initRpc);
final ListenableFuture<DeviceSources> sourceResolverFuture = processingExecutor.submit(task);
if(shouldListenOnSchemaChange(remoteSessionCapabilities)) {
- registerToBaseNetconfStream(deviceRpc, listener);
+ registerToBaseNetconfStream(initRpc, listener);
}
final FutureCallback<DeviceSources> resolvedSourceCallback = new FutureCallback<DeviceSources>() {
@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
};
Futures.addCallback(sourceResolverFuture, resolvedSourceCallback);
-
}
private void registerToBaseNetconfStream(final NetconfDeviceRpc deviceRpc, final NetconfDeviceCommunicator listener) {
- final ListenableFuture<RpcResult<CompositeNode>> rpcResultListenableFuture =
- deviceRpc.invokeRpc(NetconfMessageTransformUtil.CREATE_SUBSCRIPTION_RPC_QNAME, NetconfMessageTransformUtil.CREATE_SUBSCRIPTION_RPC_CONTENT);
+ final CheckedFuture<DOMRpcResult, DOMRpcException> 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<CompositeNode> filterNotification(final CompositeNode notification) {
+ public Optional<NormalizedNode<?, ?>> 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.<NormalizedNode<?, ?>>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<RpcResult<CompositeNode>>() {
+ Futures.addCallback(rpcResultListenableFuture, new FutureCallback<DOMRpcResult>() {
@Override
- public void onSuccess(final RpcResult<CompositeNode> result) {
+ public void onSuccess(final DOMRpcResult domRpcResult) {
notificationHandler.addNotificationFilter(filter);
}
return remoteSessionCapabilities.isNotificationsSupported() && reconnectOnSchemasChange;
}
- private void handleSalInitializationSuccess(final SchemaContext result, final NetconfSessionPreferences remoteSessionCapabilities, final NetconfDeviceRpc deviceRpc) {
- updateMessageTransformer(result);
+ @VisibleForTesting
+ void handleSalInitializationSuccess(final SchemaContext result, final NetconfSessionPreferences remoteSessionCapabilities, final DOMRpcService deviceRpc) {
+ messageTransformer = new NetconfMessageTransformer(result);
+
+ updateTransformer(messageTransformer);
+ notificationHandler.onRemoteSchemaUp(messageTransformer);
salFacade.onDeviceConnected(result, remoteSessionCapabilities, deviceRpc);
- notificationHandler.onRemoteSchemaUp();
logger.info("{}: Netconf connector initialized successfully", id);
}
}
/**
- * Set the schema context inside transformer to null as is in initial state
+ * Set the transformer to null as is in initial state
*/
private void resetMessageTransformer() {
- updateMessageTransformer(null);
+ updateTransformer(null);
}
- /**
- * Update initial message transformer to use retrieved schema
- */
- private void updateMessageTransformer(final SchemaContext currentSchemaContext) {
- messageTransformer.onGlobalContextUpdated(currentSchemaContext);
+ private void updateTransformer(final MessageTransformer<NetconfMessage> transformer) {
+ messageTransformer = transformer;
}
private void addProvidedSourcesToSchemaRegistry(final NetconfDeviceRpc deviceRpc, final DeviceSources deviceSources) {
}
}
- private NetconfDeviceRpc setUpDeviceRpc(final RemoteDeviceCommunicator<NetconfMessage> listener) {
- return new NetconfDeviceRpc(listener, messageTransformer);
- }
-
@Override
public void onRemoteSessionDown() {
notificationHandler.onRemoteSchemaDown();
}
@Override
- public void onRemoteSessionFailed(Throwable throwable) {
+ public void onRemoteSessionFailed(final Throwable throwable) {
salFacade.onDeviceFailed(throwable);
}
* Schema building callable.
*/
private static class DeviceSourcesResolver implements Callable<DeviceSources> {
+
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 {
}
- // TODO should we perform this ? We have a mechanism to fix initialization of devices not reporting or required modules in hello
- // That is overriding capabilities in configuration using attribute yang-module-capabilities
- // This is more user friendly even though it clashes with attribute yang-module-capabilities
- // Some devices do not report all required models in hello message, but provide them
+ // Here all the sources reported in netconf monitoring are merged with those reported in hello.
+ // It is necessary to perform this since submodules are not mentioned in hello but still required.
+ // This clashes with the option of a user to specify supported yang models manually in configuration for netconf-connector
+ // and as a result one is not able to fully override yang models of a device. It is only possible to add additional models.
final Set<SourceIdentifier> providedSourcesNotRequired = Sets.difference(providedSources, requiredSources);
if (!providedSourcesNotRequired.isEmpty()) {
logger.warn("{}: Netconf device provides additional yang models not reported in hello message capabilities: {}",
/**
* Contains RequiredSources - sources from capabilities.
- *
*/
private static final class DeviceSources {
private final Collection<SourceIdentifier> requiredSources;
private final class RecursiveSchemaSetup implements Runnable {
private final DeviceSources deviceSources;
private final NetconfSessionPreferences remoteSessionCapabilities;
- private final NetconfDeviceRpc deviceRpc;
private final RemoteDeviceCommunicator<NetconfMessage> listener;
- private NetconfDeviceCapabilities capabilities;
+ private final NetconfDeviceCapabilities capabilities;
- public RecursiveSchemaSetup(final DeviceSources deviceSources, final NetconfSessionPreferences remoteSessionCapabilities, final NetconfDeviceRpc deviceRpc, final RemoteDeviceCommunicator<NetconfMessage> listener) {
+ public RecursiveSchemaSetup(final DeviceSources deviceSources, final NetconfSessionPreferences remoteSessionCapabilities, final RemoteDeviceCommunicator<NetconfMessage> listener) {
this.deviceSources = deviceSources;
this.remoteSessionCapabilities = remoteSessionCapabilities;
- this.deviceRpc = deviceRpc;
this.listener = listener;
this.capabilities = remoteSessionCapabilities.getNetconfDeviceCapabilities();
}
@Override
public void onSuccess(final SchemaContext result) {
logger.debug("{}: Schema context built successfully from {}", id, requiredSources);
- Collection<QName> filteredQNames = Sets.difference(remoteSessionCapabilities.getModuleBasedCaps(), capabilities.getUnresolvedCapabilites().keySet());
+ final Collection<QName> 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
Futures.addCallback(schemaBuilderFuture, RecursiveSchemaBuilderCallback);
}
+ private NetconfDeviceRpc getDeviceSpecificRpc(final SchemaContext result) {
+ return new NetconfDeviceRpc(result, listener, new NetconfMessageTransformer(result));
+ }
+
private Collection<SourceIdentifier> stripMissingSource(final Collection<SourceIdentifier> requiredSources, final SourceIdentifier sIdToRemove) {
final LinkedList<SourceIdentifier> sourceIdentifiers = Lists.newLinkedList(requiredSources);
final boolean removed = sourceIdentifiers.remove(sIdToRemove);
return sourceIdentifiers;
}
- private Collection<QName> getQNameFromSourceIdentifiers(Collection<SourceIdentifier> identifiers) {
- Collection<QName> qNames = new HashSet<>();
- for (SourceIdentifier source : identifiers) {
- Optional<QName> qname = getQNameFromSourceIdentifier(source);
+ private Collection<QName> getQNameFromSourceIdentifiers(final Collection<SourceIdentifier> identifiers) {
+ final Collection<QName> qNames = new HashSet<>();
+ for (final SourceIdentifier source : identifiers) {
+ final Optional<QName> qname = getQNameFromSourceIdentifier(source);
if (qname.isPresent()) {
qNames.add(qname.get());
}
return qNames;
}
- private Optional<QName> getQNameFromSourceIdentifier(SourceIdentifier identifier) {
- for (QName qname : remoteSessionCapabilities.getModuleBasedCaps()) {
+ private Optional<QName> 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);
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;
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;
*/
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
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.<Node<?>>newArrayList(filter));
+ = Builders.containerBuilder().withNodeIdentifier(toId(NETCONF_GET_QNAME)).withChild(filter).build();
}
private final Set<RemoteYangSchema> availableYangSchemas;
*/
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<CompositeNode> 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<? extends NormalizedNode<?, ?>> 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<? extends NormalizedNode<?, ?>> findSchemasNode(final NormalizedNode<?, ?> result) {
+ if(result == null) {
+ return Optional.absent();
+ }
+ final Optional<DataContainerChild<?, ?>> dataNode = ((DataContainerNode<?>) result).getChild(toId(NETCONF_DATA_QNAME));
+ if(dataNode.isPresent() == false) {
+ return Optional.absent();
+ }
+
+ final Optional<DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?>> 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<RemoteYangSchema> availableYangSchemas = Sets.newHashSet();
- for (final CompositeNode schemaNode : schemasNode.getCompositesByName(Schema.QNAME.withoutRevision())) {
- final Optional<RemoteYangSchema> fromCompositeNode = RemoteYangSchema.createFromCompositeNode(id, schemaNode);
+ final Optional<DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?>> 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<RemoteYangSchema> fromCompositeNode = RemoteYangSchema.createFromNormalizedNode(id, schemaNode);
if(fromCompositeNode.isPresent()) {
availableYangSchemas.add(fromCompositeNode.get());
}
return qname;
}
- static Optional<RemoteYangSchema> createFromCompositeNode(final RemoteDeviceId id, final CompositeNode schemaNode) {
- Preconditions.checkArgument(schemaNode.getKey().equals(Schema.QNAME.withoutRevision()), "Wrong QName %s", schemaNode.getKey());
+ static Optional<RemoteYangSchema> 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<String> 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<String> 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<String> getAllChildNodeValues(final CompositeNode schemaNode, final QName childNodeQName) {
+ /**
+ * Extracts all values of a leaf-list node as a set of strings
+ */
+ private static Set<String> getAllChildNodeValues(final DataContainerNode<?> schemaNode, final QName childNodeQName) {
final Set<String> extractedValues = Sets.newHashSet();
- for (final SimpleNode<?> childNode : schemaNode.getSimpleNodesByName(childNodeQName)) {
- extractedValues.add(getValueOfSimpleNode(childNodeQName, childNode).get());
+ final Optional<DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?>> 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<String> getSingleChildNodeValue(final CompositeNode schemaNode, final QName childNode) {
- final SimpleNode<?> node = schemaNode.getFirstSimpleByName(childNode);
- return getValueOfSimpleNode(childNode, node);
+ private static Optional<String> getSingleChildNodeValue(final DataContainerNode<?> schemaNode, final QName childNode) {
+ final Optional<DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?>> node = schemaNode.getChild(toId(childNode));
+ Preconditions.checkArgument(node.isPresent(), "Child node %s not present", childNode);
+ return getValueOfSimpleNode(node.get());
}
- private static Optional<String> getValueOfSimpleNode(final QName childNode, final SimpleNode<?> node) {
- Preconditions.checkNotNull(node, "Child node %s not present", childNode);
+ private static Optional<String> getValueOfSimpleNode(final NormalizedNode<? extends YangInstanceIdentifier.PathArgument, ?> node) {
final Object value = node.getValue();
- return value == null ? Optional.<String>absent() : Optional.of(value.toString().trim());
+ return value == null || Strings.isNullOrEmpty(value.toString()) ? Optional.<String>absent() : Optional.of(value.toString().trim());
}
@Override
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;
private final RemoteDeviceHandler<?> salFacade;
private final List<NetconfMessage> queue = new LinkedList<>();
- private final MessageTransformer<NetconfMessage> messageTransformer;
private final RemoteDeviceId id;
private boolean passNotifications = false;
+
private NotificationFilter filter;
+ private MessageTransformer<NetconfMessage> messageTransformer;
- NotificationHandler(final RemoteDeviceHandler<?> salFacade, final MessageTransformer<NetconfMessage> 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);
}
/**
* 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<NetconfMessage> messageTransformer) {
+ this.messageTransformer = Preconditions.checkNotNull(messageTransformer);
+
passNotifications = true;
for (final NetconfMessage cachedNotification : queue) {
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;
}
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()) {
synchronized void onRemoteSchemaDown() {
queue.clear();
passNotifications = false;
+ messageTransformer = null;
}
static interface NotificationFilter {
- Optional<CompositeNode> filterNotification(CompositeNode notification);
+ Optional<NormalizedNode<?, ?>> filterNotification(NormalizedNode<?, ?> notification);
}
}
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;
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
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);
}
}
* 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 {
--- /dev/null
+/*
+ * 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<SchemaPath, DOMNotificationListener> 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 <T extends DOMNotificationListener> ListenerRegistration<T> registerNotificationListener(@Nonnull final T listener, @Nonnull final Collection<SchemaPath> 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<T>() {
+ @Override
+ public void close() {
+ for (final SchemaPath type : types) {
+ listeners.remove(type, listener);
+ }
+ }
+
+ @Override
+ public T getInstance() {
+ return listener;
+ }
+ };
+ }
+
+ @Override
+ public synchronized <T extends DOMNotificationListener> ListenerRegistration<T> registerNotificationListener(@Nonnull final T listener, final SchemaPath... types) {
+ return registerNotificationListener(listener, Lists.newArrayList(types));
+ }
+}
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<RpcDefinition, DOMRpcIdentifier> RPC_TO_RPC_IDENTIFIER = new Function<RpcDefinition, DOMRpcIdentifier>() {
+ @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<NetconfMessage> listener;
private final MessageTransformer<NetconfMessage> transformer;
+ private final Collection<DOMRpcIdentifier> availableRpcs;
- public NetconfDeviceRpc(final RemoteDeviceCommunicator<NetconfMessage> listener, final MessageTransformer<NetconfMessage> transformer) {
+ public NetconfDeviceRpc(final SchemaContext schemaContext, final RemoteDeviceCommunicator<NetconfMessage> listener, final MessageTransformer<NetconfMessage> transformer) {
this.listener = listener;
this.transformer = transformer;
- }
- @Override
- public Set<QName> 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<RpcResult<CompositeNode>> invokeRpc(final QName rpc, final CompositeNode input) {
- final NetconfMessage message = transformRequest(rpc, input);
- final ListenableFuture<RpcResult<NetconfMessage>> delegateFutureWithPureResult = listener.sendRequest(
- message, rpc);
+ public CheckedFuture<DOMRpcResult, DOMRpcException> 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<RpcResult<NetconfMessage>> delegateFutureWithPureResult = listener.sendRequest(message, type.getLastComponent());
- return Futures.transform(delegateFutureWithPureResult, new Function<RpcResult<NetconfMessage>, RpcResult<CompositeNode>>() {
+ final ListenableFuture<DOMRpcResult> transformed = Futures.transform(delegateFutureWithPureResult, new Function<RpcResult<NetconfMessage>, DOMRpcResult>() {
@Override
- public RpcResult<CompositeNode> apply(@Nullable final RpcResult<NetconfMessage> input) {
- return transformResult(input, rpc);
+ public DOMRpcResult apply(final RpcResult<NetconfMessage> 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<Exception, DOMRpcException>() {
+ @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<CompositeNode> transformResult(final RpcResult<NetconfMessage> netconfMessageRpcResult,
- final QName rpc) {
- if (netconfMessageRpcResult.isSuccessful()) {
- return transformer.toRpcResult(netconfMessageRpcResult.getResult(), rpc);
- } else {
- return RpcResultBuilder.<CompositeNode> failed()
- .withRpcErrors(netconfMessageRpcResult.getErrors()).build();
- }
- }
+ @Nonnull
+ @Override
+ public <T extends DOMRpcAvailabilityListener> ListenerRegistration<T> registerRpcListener(@Nonnull final T listener) {
+
+ listener.onRpcAvailable(availableRpcs);
+ return new ListenerRegistration<T>() {
+ @Override
+ public void close() {
+ // NOOP, no rpcs appear and disappear in this implementation
+ }
+
+ @Override
+ public T getInstance() {
+ return listener;
+ }
+ };
+ }
}
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;
private final List<AutoCloseable> 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);
}
}
@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<NotificationListener> 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());
}
}
@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<QName, String> 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)) {
}
}
}
+
}
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;
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() {
private DOMMountPointService mountService;
private final RemoteDeviceId id;
private ObjectRegistration<DOMMountPoint> registration;
- private NotificationPublishService notificationSerivce;
+ private NetconfDeviceNotificationService notificationService;
private ObjectRegistration<DOMMountPoint> topologyRegistration;
@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");
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();
}
}
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");
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();
}
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);
}
}
.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() {
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;
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());
}
return finished;
}
- protected void invokeBlocking(final String msg, final Function<NetconfBaseOps, ListenableFuture<RpcResult<CompositeNode>>> op) throws NetconfDocumentedException {
+ protected void invokeBlocking(final String msg, final Function<NetconfBaseOps, ListenableFuture<DOMRpcResult>> op) throws NetconfDocumentedException {
try {
- final RpcResult<CompositeNode> 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);
}
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.<NormalizedNode<?, ?>>fromNullable(data), Optional.of(ModifyAction.REPLACE), path), Optional.of(ModifyAction.NONE));
} catch (final NetconfDocumentedException e) {
handleEditException(path, data, e, "putting");
}
checkEditable(store);
try {
- final YangInstanceIdentifier legacyPath = ReadOnlyTx.toLegacyPath(normalizer, path, id);
- final CompositeNode legacyData = normalizer.toLegacy(path, data);
editConfig(
- createEditConfigStructure(legacyPath, Optional.<ModifyAction>absent(), Optional.fromNullable(legacyData)), Optional.<ModifyAction>absent());
+ netOps.createEditConfigStrcture(Optional.<NormalizedNode<?, ?>>fromNullable(data), Optional.<ModifyAction>absent(), path), Optional.<ModifyAction>absent());
} catch (final NetconfDocumentedException e) {
handleEditException(path, data, e, "merge");
}
checkEditable(store);
try {
- editConfig(createEditConfigStructure(
- ReadOnlyTx.toLegacyPath(normalizer, path, id), Optional.of(ModifyAction.DELETE),
- Optional.<CompositeNode>absent()), Optional.of(ModifyAction.NONE));
+ editConfig(
+ netOps.createEditConfigStrcture(Optional.<NormalizedNode<?, ?>>absent(), Optional.of(ModifyAction.DELETE), path), Optional.of(ModifyAction.NONE));
} catch (final NetconfDocumentedException e) {
handleDeleteException(path, e);
}
Preconditions.checkArgument(store == LogicalDatastoreType.CONFIGURATION, "Can edit only configuration data, not %s", store);
}
- protected abstract void editConfig(CompositeNode editStructure, Optional<ModifyAction> defaultOperation) throws NetconfDocumentedException;
+ protected abstract void editConfig(DataContainerChild<?, ?> editStructure, Optional<ModifyAction> defaultOperation) throws NetconfDocumentedException;
}
*/
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;
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;
private static final Logger LOG = LoggerFactory.getLogger(ReadOnlyTx.class);
private final NetconfBaseOps netconfOps;
- private final DataNormalizer normalizer;
private final RemoteDeviceId id;
- private final FutureCallback<RpcResult<CompositeNode>> loggingCallback;
+ private final FutureCallback<DOMRpcResult> 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<RpcResult<CompositeNode>>() {
+ loggingCallback = new FutureCallback<DOMRpcResult>() {
@Override
- public void onSuccess(final RpcResult<CompositeNode> 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());
private CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> readConfigurationData(
final YangInstanceIdentifier path) {
- final ListenableFuture<RpcResult<CompositeNode>> configRunning = netconfOps.getConfigRunning(loggingCallback, Optional.fromNullable(path));
- // Find data node and normalize its content
- final ListenableFuture<Optional<NormalizedNode<?, ?>>> transformedFuture = Futures.transform(configRunning, new Function<RpcResult<CompositeNode>, Optional<NormalizedNode<?, ?>>>() {
+ final ListenableFuture<DOMRpcResult> configRunning = netconfOps.getConfigRunning(loggingCallback, Optional.fromNullable(path));
+
+ final ListenableFuture<Optional<NormalizedNode<?, ?>>> transformedFuture = Futures.transform(configRunning, new Function<DOMRpcResult, Optional<NormalizedNode<?, ?>>>() {
@Override
- public Optional<NormalizedNode<?, ?>> apply(final RpcResult<CompositeNode> result) {
+ public Optional<NormalizedNode<?, ?>> 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.<NormalizedNode<?, ?>>absent() :
- transform(path, node);
+ final DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?> dataNode = findDataNode(result);
+ return NormalizedNodes.findNode(dataNode, path.getPathArguments());
}
});
return MappingCheckedFuture.create(transformedFuture, ReadFailedException.MAPPER);
}
- private void checkReadSuccess(final RpcResult<CompositeNode> 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<? extends YangInstanceIdentifier.PathArgument, ?> findDataNode(final DOMRpcResult result) {
+ return ((ContainerNode) result.getResult()).getChild(NetconfMessageTransformUtil.toId(NetconfMessageTransformUtil.NETCONF_DATA_QNAME)).get();
}
- private Optional<NormalizedNode<?, ?>> 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.<NormalizedNode<?, ?>>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<Optional<NormalizedNode<?, ?>>, ReadFailedException> readOperationalData(
final YangInstanceIdentifier path) {
- final ListenableFuture<RpcResult<CompositeNode>> configCandidate = netconfOps.get(loggingCallback, Optional.fromNullable(path));
+ final ListenableFuture<DOMRpcResult> configCandidate = netconfOps.get(loggingCallback, Optional.fromNullable(path));
// Find data node and normalize its content
- final ListenableFuture<Optional<NormalizedNode<?, ?>>> transformedFuture = Futures.transform(configCandidate, new Function<RpcResult<CompositeNode>, Optional<NormalizedNode<?, ?>>>() {
+ final ListenableFuture<Optional<NormalizedNode<?, ?>>> transformedFuture = Futures.transform(configCandidate, new Function<DOMRpcResult, Optional<NormalizedNode<?, ?>>>() {
@Override
- public Optional<NormalizedNode<?, ?>> apply(final RpcResult<CompositeNode> result) {
+ public Optional<NormalizedNode<?, ?>> 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.<NormalizedNode<?, ?>>absent() :
- transform(path, node);
+ final DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?> dataNode = findDataNode(result);
+ return NormalizedNodes.findNode(dataNode, path.getPathArguments());
}
});
@Override
public CheckedFuture<Optional<NormalizedNode<?, ?>>, 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);
}
}
@Override
public CheckedFuture<Boolean, ReadFailedException> exists(final LogicalDatastoreType store, final YangInstanceIdentifier path) {
- final CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException>
- data = read(store, path);
+ final CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> data = read(store, path);
try {
return Futures.immediateCheckedFuture(data.get().isPresent());
}
}
- 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;
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;
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
private void lockRunning() {
try {
- invokeBlocking("Lock running", new Function<NetconfBaseOps, ListenableFuture<RpcResult<CompositeNode>>>() {
+ invokeBlocking("Lock running", new Function<NetconfBaseOps, ListenableFuture<DOMRpcResult>>() {
@Override
- public ListenableFuture<RpcResult<CompositeNode>> apply(final NetconfBaseOps input) {
+ public ListenableFuture<DOMRpcResult> apply(final NetconfBaseOps input) {
return input.lockRunning(new NetconfRpcFutureCallback("Lock running", id));
}
});
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;
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;
private static final Logger LOG = LoggerFactory.getLogger(WriteCandidateTx.class);
- private static final Function<RpcResult<CompositeNode>, RpcResult<TransactionStatus>> RPC_RESULT_TO_TX_STATUS = new Function<RpcResult<CompositeNode>, RpcResult<TransactionStatus>>() {
+ private static final Function<DOMRpcResult, RpcResult<TransactionStatus>> RPC_RESULT_TO_TX_STATUS = new Function<DOMRpcResult, RpcResult<TransactionStatus>>() {
@Override
- public RpcResult<TransactionStatus> apply(final RpcResult<CompositeNode> input) {
- if (input.isSuccessful()) {
+ public RpcResult<TransactionStatus> apply(final DOMRpcResult input) {
+ if (isSuccess(input)) {
return RpcResultBuilder.success(TransactionStatus.COMMITED).build();
} else {
final RpcResultBuilder<TransactionStatus> failed = RpcResultBuilder.failed();
}
};
- 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
private void lock() throws NetconfDocumentedException {
try {
- invokeBlocking("Lock candidate", new Function<NetconfBaseOps, ListenableFuture<RpcResult<CompositeNode>>>() {
+ invokeBlocking("Lock candidate", new Function<NetconfBaseOps, ListenableFuture<DOMRpcResult>>() {
@Override
- public ListenableFuture<RpcResult<CompositeNode>> apply(final NetconfBaseOps input) {
+ public ListenableFuture<DOMRpcResult> apply(final NetconfBaseOps input) {
return input.lockCandidate(new NetconfRpcFutureCallback("Lock candidate", id));
}
});
@Override
public synchronized ListenableFuture<RpcResult<TransactionStatus>> performCommit() {
- final ListenableFuture<RpcResult<CompositeNode>> rpcResult = netOps.commit(new NetconfRpcFutureCallback("Commit", id) {
+ final ListenableFuture<DOMRpcResult> rpcResult = netOps.commit(new NetconfRpcFutureCallback("Commit", id) {
@Override
- public void onSuccess(final RpcResult<CompositeNode> 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<CompositeNode> result) {
+ protected void onUnsuccess(final DOMRpcResult result) {
LOG.error("{}: Write failed, transaction {}, discarding changes, unlocking: {}", id, getIdentifier(), result.getErrors());
cleanup();
}
}
@Override
- protected void editConfig(final CompositeNode editStructure, final Optional<ModifyAction> defaultOperation) throws NetconfDocumentedException {
- invokeBlocking("Edit candidate", new Function<NetconfBaseOps, ListenableFuture<RpcResult<CompositeNode>>>() {
+ protected void editConfig(final DataContainerChild<?, ?> editStructure, final Optional<ModifyAction> defaultOperation) throws NetconfDocumentedException {
+ invokeBlocking("Edit candidate", new Function<NetconfBaseOps, ListenableFuture<DOMRpcResult>>() {
@Override
- public ListenableFuture<RpcResult<CompositeNode>> apply(final NetconfBaseOps input) {
+ public ListenableFuture<DOMRpcResult> apply(final NetconfBaseOps input) {
return defaultOperation.isPresent()
? input.editConfigCandidate(new NetconfRpcFutureCallback("Edit candidate", id), editStructure, defaultOperation.get(),
netconfSessionPreferences.isRollbackSupported())
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;
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;
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
private void lock() {
try {
- invokeBlocking("Lock running", new Function<NetconfBaseOps, ListenableFuture<RpcResult<CompositeNode>>>() {
+ invokeBlocking("Lock running", new Function<NetconfBaseOps, ListenableFuture<DOMRpcResult>>() {
@Override
- public ListenableFuture<RpcResult<CompositeNode>> apply(final NetconfBaseOps input) {
+ public ListenableFuture<DOMRpcResult> apply(final NetconfBaseOps input) {
return input.lockRunning(new NetconfRpcFutureCallback("Lock running", id));
}
});
}
@Override
- protected void editConfig(final CompositeNode editStructure, final Optional<ModifyAction> defaultOperation) throws NetconfDocumentedException {
- invokeBlocking("Edit running", new Function<NetconfBaseOps, ListenableFuture<RpcResult<CompositeNode>>>() {
+ protected void editConfig(final DataContainerChild<?, ?> editStructure, final Optional<ModifyAction> defaultOperation) throws NetconfDocumentedException {
+ invokeBlocking("Edit running", new Function<NetconfBaseOps, ListenableFuture<DOMRpcResult>>() {
@Override
- public ListenableFuture<RpcResult<CompositeNode>> apply(final NetconfBaseOps input) {
+ public ListenableFuture<DOMRpcResult> apply(final NetconfBaseOps input) {
return defaultOperation.isPresent()
? input.editConfigRunning(new NetconfRpcFutureCallback("Edit running", id), editStructure, defaultOperation.get(),
netconfSessionPreferences.isRollbackSupported())
private void unlock() {
try {
- invokeBlocking("Unlocking running", new Function<NetconfBaseOps, ListenableFuture<RpcResult<CompositeNode>>>() {
+ invokeBlocking("Unlocking running", new Function<NetconfBaseOps, ListenableFuture<DOMRpcResult>>() {
@Override
- public ListenableFuture<RpcResult<CompositeNode>> apply(final NetconfBaseOps input) {
+ public ListenableFuture<DOMRpcResult> apply(final NetconfBaseOps input) {
return input.unlockRunning(new NetconfRpcFutureCallback("Unlock running", id));
}
});
*/
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;
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;
public final class NetconfRemoteSchemaYangSourceProvider implements SchemaSourceProvider<YangTextSchemaSource> {
- 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<SchemaSourceException> MAPPER = new ExceptionMapper<SchemaSourceException>(
}
};
- 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<String> revision) {
- final CompositeNodeBuilder<ImmutableCompositeNode> 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<String> revision) {
+ final QName identifierQName = QName.cachedReference(QName.create(NetconfMessageTransformUtil.GET_SCHEMA_QNAME, "identifier"));
+ final YangInstanceIdentifier.NodeIdentifier identifierId = new YangInstanceIdentifier.NodeIdentifier(identifierQName);
+ final LeafNode<String> identifier = Builders.<String>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<String> format = Builders.<String>leafBuilder().withNodeIdentifier(formatId).withValue("yang").build();
+
+ final DataContainerNodeAttrBuilder<YangInstanceIdentifier.NodeIdentifier, ContainerNode> 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<String> revisionNode = Builders.<String>leafBuilder().withNodeIdentifier(revisionId).withValue(revision.get()).build();
+
+ builder.withChild(revisionNode);
}
- request.addLeaf("format", "yang");
- return request.toInstance();
+
+ return builder.build();
}
- private static Optional<String> getSchemaFromRpc(final RemoteDeviceId id, final CompositeNode result) {
+ private static Optional<String> 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<DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?>> 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.<String> absent();
}
// 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<String> 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<YangTextSchemaSource> 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<YangTextSchemaSource, SchemaSourceException> 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();
* Transform composite node to string schema representation and then to ASTSchemaSource
*/
private static final class ResultToYangSourceTransformer implements
- Function<RpcResult<CompositeNode>, YangTextSchemaSource> {
+ Function<DOMRpcResult, YangTextSchemaSource> {
private final RemoteDeviceId id;
private final SourceIdentifier sourceIdentifier;
}
@Override
- public YangTextSchemaSource apply(final RpcResult<CompositeNode> input) {
+ public YangTextSchemaSource apply(final DOMRpcResult input) {
- if (input.isSuccessful()) {
+ if (input.getErrors().isEmpty()) {
final Optional<String> schemaString = getSchemaFromRpc(id, input.getResult());
"%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);
}
throw new IllegalStateException(String.format(
"%s: YANG schema was not successfully retrieved for %s. Errors: %s", id, sourceIdentifier,
input.getErrors()));
-
}
}
*/
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<NetconfMessage> {
public static final String MESSAGE_ID_PREFIX = "m";
- private Optional<SchemaContext> 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<SchemaNode, QName> QNAME_FUNCTION = new Function<SchemaNode, QName>() {
+ @Override
+ public QName apply(final SchemaNode rpcDefinition) {
+ return rpcDefinition.getQName();
+ }
+ };
+
+ private static final Function<SchemaNode, QName> QNAME_NOREV_FUNCTION = new Function<SchemaNode, QName>() {
+ @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<QName, RpcDefinition> mappedRpcs;
+ private final Multimap<QName, NotificationDefinition> 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.<Set<NotificationDefinition>>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<NotificationDefinition> 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<NotificationDefinition> 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<XmlElement> childElements = xmlElement.getChildElements();
+ Preconditions.checkArgument(childElements.size() == 2, "Unable to parse notification %s, unexpected format", message);
+ try {
+ return Iterables.find(childElements, new Predicate<XmlElement>() {
+ @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<Iterable<Element>> editDataElements = Optional.absent();
+ for (final DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?> 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<DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?>> 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<RpcDefinition> 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<? extends YangInstanceIdentifier.PathArgument, ?> 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<CompositeNode> 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<Iterable<Element>> filterElements = Optional.absent();
+
+ for (final DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?> 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<Element> 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<CompositeNode> 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<Element> 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<Element> serializeAnyXmlAccordingToSchema(final Iterable<DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?>> values) throws IOException, XMLStreamException {
+ return Iterables.transform(values, new Function<DataContainerChild<? extends YangInstanceIdentifier.PathArgument,?>, Element>() {
+ @Override
+ public Element apply(final DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?> 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<org.opendaylight.yangtools.yang.data.api.Node<?>> 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<ImmutableCompositeNode> 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<Element> 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);
- }
}
--- /dev/null
+/*
+ * 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<T extends PathArgument> implements Identifiable<T> {
+
+ 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<NormalizedNode<?, ?>> deepestChild, Optional<ModifyAction> operation);
+
+ private static abstract class SimpleTypeNormalization<T extends PathArgument> extends InstanceIdToNodes<T> {
+
+ protected SimpleTypeNormalization(final T identifier) {
+ super(identifier);
+ }
+
+ @Override
+ public NormalizedNode<?, ?> create(final YangInstanceIdentifier id, final Optional<NormalizedNode<?, ?>> deepestChild, final Optional<ModifyAction> operation) {
+ checkNotNull(id);
+ final PathArgument pathArgument = Iterables.get(id.getPathArguments(), 0);
+ final NormalizedNodeAttrBuilder<? extends PathArgument, Object, ? extends NormalizedNode<? extends PathArgument, Object>> builder = getBuilder(pathArgument);
+
+ if(deepestChild.isPresent()) {
+ builder.withValue(deepestChild.get().getValue());
+ }
+
+ addModifyOpIfPresent(operation, builder);
+ return builder.build();
+ }
+
+ protected abstract NormalizedNodeAttrBuilder<? extends PathArgument, Object, ? extends NormalizedNode<? extends PathArgument, Object>> getBuilder(PathArgument node);
+
+ @Override
+ public InstanceIdToNodes<?> getChild(final PathArgument child) {
+ return null;
+ }
+ }
+
+
+ public void addModifyOpIfPresent(final Optional<ModifyAction> 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<NodeIdentifier> {
+
+ protected LeafNormalization(final LeafSchemaNode potential) {
+ super(new NodeIdentifier(potential.getQName()));
+ }
+
+ @Override
+ protected NormalizedNodeAttrBuilder<NodeIdentifier, Object, LeafNode<Object>> getBuilder(final PathArgument node) {
+ return Builders.leafBuilder().withNodeIdentifier(getIdentifier());
+ }
+ }
+
+ private static final class LeafListEntryNormalization extends SimpleTypeNormalization<NodeWithValue> {
+
+ public LeafListEntryNormalization(final LeafListSchemaNode potential) {
+ super(new NodeWithValue(potential.getQName(), null));
+ }
+
+ @Override
+ protected NormalizedNodeAttrBuilder<NodeWithValue, Object, LeafSetEntryNode<Object>> getBuilder(final PathArgument node) {
+ Preconditions.checkArgument(node instanceof NodeWithValue);
+ return Builders.leafSetEntryBuilder().withNodeIdentifier((NodeWithValue) node).withValue(((NodeWithValue) node).getValue());
+ }
+
+ }
+
+ private static abstract class CompositeNodeNormalizationOperation<T extends PathArgument> extends
+ InstanceIdToNodes<T> {
+
+ protected CompositeNodeNormalizationOperation(final T identifier) {
+ super(identifier);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public final NormalizedNode<?, ?> create(final YangInstanceIdentifier id, final Optional<NormalizedNode<?, ?>> lastChild, final Optional<ModifyAction> operation) {
+ checkNotNull(id);
+ final Iterator<PathArgument> 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<T extends PathArgument> extends
+ CompositeNodeNormalizationOperation<T> {
+
+ private final DataNodeContainer schema;
+ private final Map<PathArgument, InstanceIdToNodes<?>> 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<NodeIdentifierWithPredicates> {
+
+ protected ListItemNormalization(final NodeIdentifierWithPredicates identifier, final ListSchemaNode schema) {
+ super(identifier, schema);
+ }
+
+ @Override
+ protected NormalizedNodeContainerBuilder<?, ?, ?, ?> createBuilder(final PathArgument currentArg) {
+ final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder = Builders
+ .mapEntryBuilder().withNodeIdentifier((NodeIdentifierWithPredicates) currentArg);
+ for (final Entry<QName, Object> 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<NodeIdentifier> {
+
+ 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<NodeIdentifier> {
+
+ 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<NodeIdentifier> 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<AugmentationIdentifier> 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<NodeIdentifier> 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.<QName, Object>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<NodeIdentifier> 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<NodeIdentifier> implements MixinNormalizationOp {
+
+ private final ImmutableMap<PathArgument, InstanceIdToNodes<?>> byArg;
+
+ protected ChoiceNodeNormalization(final org.opendaylight.yangtools.yang.model.api.ChoiceNode schema) {
+ super(new NodeIdentifier(schema.getQName()));
+ final ImmutableMap.Builder<PathArgument, InstanceIdToNodes<?>> 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<NodeIdentifier> {
+
+ 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<NormalizedNode<?, ?>> deepestChild, final Optional<ModifyAction> operation) {
+ if(deepestChild.isPresent()) {
+ Preconditions.checkState(deepestChild instanceof AnyXmlNode);
+ final NormalizedNodeAttrBuilder<NodeIdentifier, Node<?>, AnyXmlNode> anyXmlBuilder =
+ Builders.anyXmlBuilder().withNodeIdentifier(getIdentifier()).withValue(((AnyXmlNode) deepestChild).getValue());
+ addModifyOpIfPresent(operation, anyXmlBuilder);
+ return anyXmlBuilder.build();
+ }
+
+ final NormalizedNodeAttrBuilder<NodeIdentifier, Node<?>, AnyXmlNode> builder =
+ Builders.anyXmlBuilder().withNodeIdentifier(getIdentifier());
+ return builder.build();
+ }
+
+ }
+
+ private static Optional<DataSchemaNode> findChildSchemaNode(final DataNodeContainer parent, final QName child) {
+ DataSchemaNode potential = parent.getDataChildByName(child);
+ if (potential == null) {
+ final Iterable<org.opendaylight.yangtools.yang.model.api.ChoiceNode> 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<DataSchemaNode> 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<org.opendaylight.yangtools.yang.model.api.ChoiceNode> 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<QName> 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<DataSchemaNode> 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
+ * <p/>
+ * 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<QName> 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.<NormalizedNode<?, ?>>absent(), Optional.<ModifyAction>absent());
+ }
+
+ public static NormalizedNode<?, ?> serialize(final SchemaContext ctx, final YangInstanceIdentifier id, final NormalizedNode<?, ?> deepestElement) {
+ return serialize(ctx, id, Optional.<NormalizedNode<?, ?>>of(deepestElement), Optional.<ModifyAction>absent());
+ }
+
+ public static NormalizedNode<?, ?> serialize(final SchemaContext ctx, final YangInstanceIdentifier id, final Optional<NormalizedNode<?, ?>> deepestElement, final Optional<ModifyAction> 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);
+ }
+ }
+}
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;
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.
*/
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<RpcResult<CompositeNode>> lock(final FutureCallback<RpcResult<CompositeNode>> callback, final QName datastore) {
+ public ListenableFuture<DOMRpcResult> lock(final FutureCallback<DOMRpcResult> callback, final QName datastore) {
Preconditions.checkNotNull(callback);
Preconditions.checkNotNull(datastore);
- final ListenableFuture<RpcResult<CompositeNode>> future = rpc.invokeRpc(NETCONF_LOCK_QNAME, getLockContent(datastore));
+ final ListenableFuture<DOMRpcResult> future = rpc.invokeRpc(toPath(NETCONF_LOCK_QNAME), getLockContent(datastore));
Futures.addCallback(future, callback);
return future;
}
- public ListenableFuture<RpcResult<CompositeNode>> lockCandidate(final FutureCallback<RpcResult<CompositeNode>> callback) {
- final ListenableFuture<RpcResult<CompositeNode>> future = rpc.invokeRpc(NETCONF_LOCK_QNAME, getLockContent(NETCONF_CANDIDATE_QNAME));
+ public ListenableFuture<DOMRpcResult> lockCandidate(final FutureCallback<DOMRpcResult> callback) {
+ final ListenableFuture<DOMRpcResult> future = rpc.invokeRpc(toPath(NETCONF_LOCK_QNAME), getLockContent(NETCONF_CANDIDATE_QNAME));
Futures.addCallback(future, callback);
return future;
}
- public ListenableFuture<RpcResult<CompositeNode>> lockRunning(final FutureCallback<RpcResult<CompositeNode>> callback) {
- final ListenableFuture<RpcResult<CompositeNode>> future = rpc.invokeRpc(NETCONF_LOCK_QNAME, getLockContent(NETCONF_RUNNING_QNAME));
+ public ListenableFuture<DOMRpcResult> lockRunning(final FutureCallback<DOMRpcResult> callback) {
+ final ListenableFuture<DOMRpcResult> future = rpc.invokeRpc(toPath(NETCONF_LOCK_QNAME), getLockContent(NETCONF_RUNNING_QNAME));
Futures.addCallback(future, callback);
return future;
}
- public ListenableFuture<RpcResult<CompositeNode>> unlock(final FutureCallback<RpcResult<CompositeNode>> callback, final QName datastore) {
+ public ListenableFuture<DOMRpcResult> unlock(final FutureCallback<DOMRpcResult> callback, final QName datastore) {
Preconditions.checkNotNull(callback);
Preconditions.checkNotNull(datastore);
- final ListenableFuture<RpcResult<CompositeNode>> future = rpc.invokeRpc(NETCONF_UNLOCK_QNAME, getUnLockContent(datastore));
+ final ListenableFuture<DOMRpcResult> future = rpc.invokeRpc(toPath(NETCONF_UNLOCK_QNAME), getUnLockContent(datastore));
Futures.addCallback(future, callback);
return future;
}
- public ListenableFuture<RpcResult<CompositeNode>> unlockRunning(final FutureCallback<RpcResult<CompositeNode>> callback) {
- final ListenableFuture<RpcResult<CompositeNode>> future = rpc.invokeRpc(NETCONF_UNLOCK_QNAME, getUnLockContent(NETCONF_RUNNING_QNAME));
+ public ListenableFuture<DOMRpcResult> unlockRunning(final FutureCallback<DOMRpcResult> callback) {
+ final ListenableFuture<DOMRpcResult> future = rpc.invokeRpc(toPath(NETCONF_UNLOCK_QNAME), getUnLockContent(NETCONF_RUNNING_QNAME));
Futures.addCallback(future, callback);
return future;
}
- public ListenableFuture<RpcResult<CompositeNode>> unlockCandidate(final FutureCallback<RpcResult<CompositeNode>> callback) {
- final ListenableFuture<RpcResult<CompositeNode>> future = rpc.invokeRpc(NETCONF_UNLOCK_QNAME, getUnLockContent(NETCONF_CANDIDATE_QNAME));
+ public ListenableFuture<DOMRpcResult> unlockCandidate(final FutureCallback<DOMRpcResult> callback) {
+ final ListenableFuture<DOMRpcResult> future = rpc.invokeRpc(toPath(NETCONF_UNLOCK_QNAME), getUnLockContent(NETCONF_CANDIDATE_QNAME));
Futures.addCallback(future, callback);
return future;
}
- public ListenableFuture<RpcResult<CompositeNode>> discardChanges(final FutureCallback<RpcResult<CompositeNode>> callback) {
+ public ListenableFuture<DOMRpcResult> discardChanges(final FutureCallback<DOMRpcResult> callback) {
Preconditions.checkNotNull(callback);
- final ListenableFuture<RpcResult<CompositeNode>> future = rpc.invokeRpc(NETCONF_DISCARD_CHANGES_QNAME, DISCARD_CHANGES_RPC_CONTENT);
+ final ListenableFuture<DOMRpcResult> future = rpc.invokeRpc(toPath(NETCONF_DISCARD_CHANGES_QNAME), DISCARD_CHANGES_RPC_CONTENT);
Futures.addCallback(future, callback);
return future;
}
- public ListenableFuture<RpcResult<CompositeNode>> commit(final FutureCallback<RpcResult<CompositeNode>> callback) {
+ public ListenableFuture<DOMRpcResult> commit(final FutureCallback<DOMRpcResult> callback) {
Preconditions.checkNotNull(callback);
- final ListenableFuture<RpcResult<CompositeNode>> future = rpc.invokeRpc(NetconfMessageTransformUtil.NETCONF_COMMIT_QNAME, NetconfMessageTransformUtil.COMMIT_RPC_CONTENT);
+ final ListenableFuture<DOMRpcResult> future = rpc.invokeRpc(toPath(NetconfMessageTransformUtil.NETCONF_COMMIT_QNAME), NetconfMessageTransformUtil.COMMIT_RPC_CONTENT);
Futures.addCallback(future, callback);
return future;
}
- public ListenableFuture<RpcResult<CompositeNode>> validate(final FutureCallback<RpcResult<CompositeNode>> callback, final QName datastore) {
+ public ListenableFuture<DOMRpcResult> validate(final FutureCallback<DOMRpcResult> callback, final QName datastore) {
Preconditions.checkNotNull(callback);
Preconditions.checkNotNull(datastore);
- final ListenableFuture<RpcResult<CompositeNode>> future = rpc.invokeRpc(NetconfMessageTransformUtil.NETCONF_VALIDATE_QNAME, getValidateContent(datastore));
+ final ListenableFuture<DOMRpcResult> future = rpc.invokeRpc(toPath(NetconfMessageTransformUtil.NETCONF_VALIDATE_QNAME), getValidateContent(datastore));
Futures.addCallback(future, callback);
return future;
}
- public ListenableFuture<RpcResult<CompositeNode>> validateCandidate(final FutureCallback<RpcResult<CompositeNode>> callback) {
+ public ListenableFuture<DOMRpcResult> validateCandidate(final FutureCallback<DOMRpcResult> callback) {
return validate(callback, NETCONF_CANDIDATE_QNAME);
}
- public ListenableFuture<RpcResult<CompositeNode>> validateRunning(final FutureCallback<RpcResult<CompositeNode>> callback) {
+ public ListenableFuture<DOMRpcResult> validateRunning(final FutureCallback<DOMRpcResult> callback) {
return validate(callback, NETCONF_RUNNING_QNAME);
}
- public ListenableFuture<RpcResult<CompositeNode>> copyConfig(final FutureCallback<RpcResult<CompositeNode>> callback, final QName source, final QName target) {
+ public ListenableFuture<DOMRpcResult> copyConfig(final FutureCallback<DOMRpcResult> callback, final QName source, final QName target) {
Preconditions.checkNotNull(callback);
Preconditions.checkNotNull(source);
Preconditions.checkNotNull(target);
- final ListenableFuture<RpcResult<CompositeNode>> future = rpc.invokeRpc(NetconfMessageTransformUtil.NETCONF_COPY_CONFIG_QNAME, getCopyConfigContent(source, target));
+ final ListenableFuture<DOMRpcResult> future = rpc.invokeRpc(toPath(NetconfMessageTransformUtil.NETCONF_COPY_CONFIG_QNAME), getCopyConfigContent(source, target));
Futures.addCallback(future, callback);
return future;
}
- public ListenableFuture<RpcResult<CompositeNode>> copyRunningToCandidate(final FutureCallback<RpcResult<CompositeNode>> callback) {
+ public ListenableFuture<DOMRpcResult> copyRunningToCandidate(final FutureCallback<DOMRpcResult> callback) {
return copyConfig(callback, NETCONF_RUNNING_QNAME, NETCONF_CANDIDATE_QNAME);
}
- public ListenableFuture<RpcResult<CompositeNode>> getConfig(final FutureCallback<RpcResult<CompositeNode>> callback, final QName datastore, final Optional<YangInstanceIdentifier> filterPath) {
+ public ListenableFuture<DOMRpcResult> getConfig(final FutureCallback<DOMRpcResult> callback, final QName datastore, final Optional<YangInstanceIdentifier> filterPath) {
Preconditions.checkNotNull(callback);
Preconditions.checkNotNull(datastore);
- final ListenableFuture<RpcResult<CompositeNode>> future;
- if (filterPath.isPresent()) {
- final Node<?> node = toFilterStructure(filterPath.get());
- future = rpc.invokeRpc(NETCONF_GET_CONFIG_QNAME,
+ final ListenableFuture<DOMRpcResult> 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)));
}
return future;
}
- public ListenableFuture<RpcResult<CompositeNode>> getConfigRunning(final FutureCallback<RpcResult<CompositeNode>> callback, final Optional<YangInstanceIdentifier> filterPath) {
+ public ListenableFuture<DOMRpcResult> getConfigRunning(final FutureCallback<DOMRpcResult> callback, final Optional<YangInstanceIdentifier> filterPath) {
return getConfig(callback, NETCONF_RUNNING_QNAME, filterPath);
}
- public ListenableFuture<RpcResult<CompositeNode>> getConfigCandidate(final FutureCallback<RpcResult<CompositeNode>> callback, final Optional<YangInstanceIdentifier> filterPath) {
+ public ListenableFuture<DOMRpcResult> getConfigCandidate(final FutureCallback<DOMRpcResult> callback, final Optional<YangInstanceIdentifier> filterPath) {
return getConfig(callback, NETCONF_CANDIDATE_QNAME, filterPath);
}
- public ListenableFuture<RpcResult<CompositeNode>> get(final FutureCallback<RpcResult<CompositeNode>> callback, final Optional<YangInstanceIdentifier> filterPath) {
+ public ListenableFuture<DOMRpcResult> get(final FutureCallback<DOMRpcResult> callback, final Optional<YangInstanceIdentifier> filterPath) {
Preconditions.checkNotNull(callback);
- final ListenableFuture<RpcResult<CompositeNode>> 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<DOMRpcResult> 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<RpcResult<CompositeNode>> editConfigCandidate(final FutureCallback<? super RpcResult<CompositeNode>> callback, final CompositeNode editStructure, final ModifyAction modifyAction, final boolean rollback) {
+ private boolean isFilterPresent(final Optional<YangInstanceIdentifier> filterPath) {
+ return filterPath.isPresent() && Iterables.isEmpty(filterPath.get().getPathArguments()) == false;
+ }
+
+ public ListenableFuture<DOMRpcResult> editConfigCandidate(final FutureCallback<? super DOMRpcResult> callback, final DataContainerChild<?, ?> editStructure, final ModifyAction modifyAction, final boolean rollback) {
return editConfig(callback, NETCONF_CANDIDATE_QNAME, editStructure, Optional.of(modifyAction), rollback);
}
- public ListenableFuture<RpcResult<CompositeNode>> editConfigCandidate(final FutureCallback<? super RpcResult<CompositeNode>> callback, final CompositeNode editStructure, final boolean rollback) {
+ public ListenableFuture<DOMRpcResult> editConfigCandidate(final FutureCallback<? super DOMRpcResult> callback, final DataContainerChild<?, ?> editStructure, final boolean rollback) {
return editConfig(callback, NETCONF_CANDIDATE_QNAME, editStructure, Optional.<ModifyAction>absent(), rollback);
}
- public ListenableFuture<RpcResult<CompositeNode>> editConfigRunning(final FutureCallback<? super RpcResult<CompositeNode>> callback, final CompositeNode editStructure, final ModifyAction modifyAction, final boolean rollback) {
+ public ListenableFuture<DOMRpcResult> editConfigRunning(final FutureCallback<? super DOMRpcResult> callback, final DataContainerChild<?, ?> editStructure, final ModifyAction modifyAction, final boolean rollback) {
return editConfig(callback, NETCONF_RUNNING_QNAME, editStructure, Optional.of(modifyAction), rollback);
}
- public ListenableFuture<RpcResult<CompositeNode>> editConfigRunning(final FutureCallback<? super RpcResult<CompositeNode>> callback, final CompositeNode editStructure, final boolean rollback) {
+ public ListenableFuture<DOMRpcResult> editConfigRunning(final FutureCallback<? super DOMRpcResult> callback, final DataContainerChild<?, ?> editStructure, final boolean rollback) {
return editConfig(callback, NETCONF_RUNNING_QNAME, editStructure, Optional.<ModifyAction>absent(), rollback);
}
- public ListenableFuture<RpcResult<CompositeNode>> editConfig(final FutureCallback<? super RpcResult<CompositeNode>> callback, final QName datastore, final CompositeNode editStructure, final Optional<ModifyAction> modifyAction, final boolean rollback) {
+ public ListenableFuture<DOMRpcResult> editConfig(final FutureCallback<? super DOMRpcResult> callback, final QName datastore, final DataContainerChild<?, ?> editStructure, final Optional<ModifyAction> modifyAction, final boolean rollback) {
Preconditions.checkNotNull(editStructure);
Preconditions.checkNotNull(callback);
Preconditions.checkNotNull(datastore);
- final ListenableFuture<RpcResult<CompositeNode>> future = rpc.invokeRpc(NETCONF_EDIT_CONFIG_QNAME, getEditConfigContent(datastore, editStructure, modifyAction, rollback));
+ final ListenableFuture<DOMRpcResult> 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<ModifyAction> defaultOperation, final boolean rollback) {
- final CompositeNodeBuilder<ImmutableCompositeNode> ret = ImmutableCompositeNode.builder();
+ public DataContainerChild<?, ?> createEditConfigStrcture(final Optional<NormalizedNode<?, ?>> lastChild, final Optional<ModifyAction> operation, final YangInstanceIdentifier dataPath) {
+ return NetconfMessageTransformUtil.createEditConfigStructure(schemaContext, dataPath, operation, lastChild);
+ }
+
+ private ContainerNode getEditConfigContent(final QName datastore, final DataContainerChild<?, ?> editStructure, final Optional<ModifyAction> defaultOperation, final boolean rollback) {
+ final DataContainerNodeAttrBuilder<YangInstanceIdentifier.NodeIdentifier, ContainerNode> 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<String> 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.<Node<?>> 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.<Node<?>>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.<Node<?>>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.<Node<?>> 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.<Node<?>> 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.<Node<?>>singletonList(
- getTargetNode(preferedDatastore)));
+ public static NormalizedNode<?, ?> getUnLockContent(final QName datastore) {
+ return Builders.containerBuilder().withNodeIdentifier(toId(NETCONF_UNLOCK_QNAME))
+ .withChild(getTargetNode(datastore)).build();
}
}
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;
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;
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");
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");
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.<Node<?>>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.<Node<?>>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.<Node<?>>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.<Node<?>>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<YangInstanceIdentifier.NodeIdentifier, ContainerNode> 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<Node<?>> list = new ArrayList<>();
- for (final Map.Entry<QName, Object> 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)
final String outputMsgId = output.getDocument().getDocumentElement().getAttribute(MESSAGE_ID_ATTR);
if(inputMsgId.equals(outputMsgId) == false) {
- Map<String,String> errorInfo = ImmutableMap.<String,String>builder()
+ final Map<String,String> errorInfo = ImmutableMap.<String,String>builder()
.put( "actual-message-id", outputMsgId )
.put( "expected-message-id", inputMsgId )
.build();
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<String, String> errorInfo = ex.getErrorInfo();
- if( errorInfo != null )
- {
- for( Entry<String,String> e: errorInfo.entrySet() ) {
+ public static RpcError toRpcError( final NetconfDocumentedException ex ) {
+ final StringBuilder infoBuilder = new StringBuilder();
+ final Map<String, String> errorInfo = ex.getErrorInfo();
+ if(errorInfo != null) {
+ for( final Entry<String,String> e: errorInfo.entrySet() ) {
infoBuilder.append( '<' ).append( e.getKey() ).append( '>' ).append( e.getValue() )
.append( "</" ).append( e.getKey() ).append( '>' );
}
}
- ErrorSeverity severity = toRpcErrorSeverity( ex.getErrorSeverity() );
+ final ErrorSeverity severity = toRpcErrorSeverity( ex.getErrorSeverity() );
return severity == ErrorSeverity.ERROR ?
RpcResultBuilder.newError(
toRpcErrorType( ex.getErrorType() ), ex.getErrorTag().getTagValue(),
}
}
- 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;
}
}
- 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<Node<?>> nodes = ImmutableList.<Node<?>> builder() //
- .addAll(input.getValue()) //
- .addAll(Collections2.filter(node.getValue(), new Predicate<Node<?>>() {
- @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.<Node<?>> singletonList(node));
- } else {
- return new SimpleNodeTOImpl<Void>(argument.getNodeType(), null, null);
- }
+ public static YangInstanceIdentifier.NodeIdentifier toId(final QName nodeType) {
+ return new YangInstanceIdentifier.NodeIdentifier(nodeType);
}
public static Element getDataSubtree(final Document doc) {
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());
return new NodeContainerProxy(NETCONF_RPC_QNAME, Sets.<DataSchemaNode>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:
* <pre>
return new NodeContainerProxy(NETCONF_RPC_QNAME, Sets.<DataSchemaNode>newHashSet(editConfigProxy));
}
-
public static Optional<RpcDefinition> findSchemaForRpc(final QName rpcName, final SchemaContext schemaContext) {
Preconditions.checkNotNull(rpcName);
Preconditions.checkNotNull(schemaContext);
return new NodeContainerProxy(NETCONF_RPC_QNAME, Sets.<DataSchemaNode>newHashSet(rpcBodyProxy));
}
- public static CompositeNodeTOImpl wrap(final QName name, final Node<?> node) {
- if (node != null) {
- return new CompositeNodeTOImpl(name, null, Collections.<Node<?>> singletonList(node));
- } else {
- return new CompositeNodeTOImpl(name, null, Collections.<Node<?>> 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<ModifyAction> operation, final Optional<NormalizedNode<?, ?>> 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.<Node<?>> 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<ImmutableCompositeNode> 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<ModifyAction> operation,
- final Optional<CompositeNode> lastChildOverride) {
- Preconditions.checkArgument(Iterables.isEmpty(dataPath.getPathArguments()) == false, "Instance identifier with empty path %s", dataPath);
-
- List<YangInstanceIdentifier.PathArgument> 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<ImmutableCompositeNode> builder = ImmutableCompositeNode.builder();
- builder.setQName(arg.getNodeType());
-
- addPredicatesToCompositeNodeBuilder(getPredicates(arg), builder);
-
- builder.add(previous);
- previous = builder.toInstance();
- }
- return ImmutableCompositeNode.create(NETCONF_CONFIG_QNAME, ImmutableList.<Node<?>>of(previous));
- }
-
- public static void addPredicatesToCompositeNodeBuilder(final Map<QName, Object> predicates, final CompositeNodeBuilder<ImmutableCompositeNode> builder) {
+ public static void addPredicatesToCompositeNodeBuilder(final Map<QName, Object> predicates,
+ final DataContainerNodeAttrBuilder<YangInstanceIdentifier.NodeIdentifier, ContainerNode> builder) {
for (final Map.Entry<QName, Object> entry : predicates.entrySet()) {
- builder.addLeaf(entry.getKey(), entry.getValue());
+ builder.withChild(Builders.leafBuilder().withNodeIdentifier(toId(entry.getKey())).withValue(entry.getValue()).build());
}
}
return predicates;
}
- public static CompositeNode getDeepestEditElement(final YangInstanceIdentifier.PathArgument arg, final Optional<ModifyAction> operation, final Optional<CompositeNode> lastChildOverride) {
- final CompositeNodeBuilder<ImmutableCompositeNode> builder = ImmutableCompositeNode.builder();
- builder.setQName(arg.getNodeType());
-
- final Map<QName, Object> predicates = getPredicates(arg);
- addPredicatesToCompositeNodeBuilder(predicates, builder);
-
- if (operation.isPresent()) {
- builder.setAttribute(NETCONF_OPERATION_QNAME, modifyOperationToXmlString(operation.get()));
- }
- if (lastChildOverride.isPresent()) {
- final List<Node<?>> 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();
}
}
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<RpcResult<CompositeNode>> {
+public class NetconfRpcFutureCallback implements FutureCallback<DOMRpcResult> {
private static final Logger LOG = LoggerFactory.getLogger(WriteRunningTx.class);
private final String type;
}
@Override
- public void onSuccess(final RpcResult<CompositeNode> 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<CompositeNode> result) {
+ protected void onUnsuccess(final DOMRpcResult result) {
LOG.warn("{}: " + type + " invoked unsuccessfully: {}", id, result.getErrors());
}
*/
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;
private final Map<QName, DataSchemaNode> childNodes;
private final QName qName;
+ private final Set<AugmentationSchema> availableAugmentations;
- public NodeContainerProxy(final QName qName, final Map<QName, DataSchemaNode> childNodes) {
+ public NodeContainerProxy(final QName qName, final Map<QName, DataSchemaNode> childNodes, final Set<AugmentationSchema> 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<DataSchemaNode> childNodes) {
- this(qName, asMap(childNodes));
+ this(qName, asMap(childNodes), Collections.<AugmentationSchema>emptySet());
+ }
+
+ public NodeContainerProxy(final QName qName, final Collection<DataSchemaNode> childNodes, final Set<AugmentationSchema> availableAugmentations) {
+ this(qName, asMap(childNodes), availableAugmentations);
}
private static Map<QName, DataSchemaNode> asMap(final Collection<DataSchemaNode> childNodes) {
- final Map<QName, DataSchemaNode> mapped = Maps.newHashMap();
- for (final DataSchemaNode childNode : childNodes) {
- mapped.put(childNode.getQName(), childNode);
- }
- return mapped;
+ return Maps.uniqueIndex(childNodes, new Function<DataSchemaNode, QName>() {
+ @Override
+ public QName apply(final DataSchemaNode input) {
+ return input.getQName();
+ }
+ });
}
@Override
@Override
public Set<AugmentationSchema> getAvailableAugmentations() {
- throw new UnsupportedOperationException();
+ return availableAugmentations;
}
@Override
/*
- * 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;
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;
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;
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<NetconfMessage> rpcResult = RpcResultBuilder.success(netconfMessage).build();
- private static final RpcResult<CompositeNode> 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";
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);
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));
}
final RemoteDeviceHandler<NetconfSessionPreferences> facade = getFacade();
final NetconfDeviceCommunicator listener = getListener();
- final MessageTransformer<NetconfMessage> 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
final NetconfDeviceCommunicator listener = getListener();
final SchemaContextFactory schemaContextProviderFactory = getSchemaFactory();
- final MessageTransformer<NetconfMessage> 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();
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() {
private RemoteDeviceHandler<NetconfSessionPreferences> getFacade() throws Exception {
final RemoteDeviceHandler<NetconfSessionPreferences> 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;
}
public MessageTransformer<NetconfMessage> getMessageTransformer() throws Exception {
final MessageTransformer<NetconfMessage> 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;
}
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;
}
}
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<Element, ContainerNode, ContainerSchemaNode> 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<QName> availableYangSchemasQNames = schemas.getAvailableYangSchemasQNames();
assertEquals(73, availableYangSchemasQNames.size());
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;
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;
@SuppressWarnings("deprecation")
@Before
public void setup() throws Exception {
- final List<InputStream> modelsToParse = Collections.singletonList(getClass().getResourceAsStream("/schemas/user-notification.yang"));
- final YangContextParser parser = new YangParserImpl();
- final Set<Module> 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);
userNotification = new NetconfMessage(doc);
}
+ static SchemaContext getNotificationSchemaContext(Class<?> loadClass) {
+ final List<InputStream> modelsToParse = Collections.singletonList(loadClass.getResourceAsStream("/schemas/user-notification.yang"));
+ final YangContextParser parser = new YangParserImpl();
+ final Set<Module> 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());
}
}
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;
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;
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;
final Set<Module> 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<Module> 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<ImmutableCompositeNode> rootBuilder = ImmutableCompositeNode.builder();
- rootBuilder.setQName(EDIT_CONFIG_QNAME);
-
- final CompositeNodeBuilder<ImmutableCompositeNode> inputBuilder = ImmutableCompositeNode.builder();
- inputBuilder.setQName(QName.create(CONFIG_TEST_NAMESPACE, CONFIG_TEST_REVISION, "input"));
-
- final CompositeNodeBuilder<ImmutableCompositeNode> 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<ImmutableCompositeNode> configBuilder = ImmutableCompositeNode.builder();
- configBuilder.setQName(QName.create(CONFIG_TEST_NAMESPACE, CONFIG_TEST_REVISION, "config"));
-
- final CompositeNodeBuilder<ImmutableCompositeNode> anyxmlTopBuilder = ImmutableCompositeNode.builder();
- anyxmlTopBuilder.setQName(QName.create(CONFIG_TEST_NAMESPACE, CONFIG_TEST_REVISION, "top"));
-
- final CompositeNodeBuilder<ImmutableCompositeNode> 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<ImmutableCompositeNode> rootBuilder = ImmutableCompositeNode.builder();
- rootBuilder.setQName(GET_QNAME);
-
- final CompositeNodeBuilder<ImmutableCompositeNode> 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<ImmutableCompositeNode> rootBuilder = ImmutableCompositeNode.builder();
- rootBuilder.setQName(GET_CONFIG_QNAME);
-
- final CompositeNodeBuilder<ImmutableCompositeNode> inputBuilder = ImmutableCompositeNode.builder();
- inputBuilder.setQName(QName.create(CONFIG_TEST_NAMESPACE, CONFIG_TEST_REVISION, "input"));
-
- final CompositeNodeBuilder<ImmutableCompositeNode> 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<ImmutableCompositeNode> anyxmlFilterBuilder = ImmutableCompositeNode.builder();
- anyxmlFilterBuilder.setQName(QName.create(CONFIG_TEST_NAMESPACE, CONFIG_TEST_REVISION, "filter"));
-
- final CompositeNodeBuilder<ImmutableCompositeNode> 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<Object> 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<ImmutableCompositeNode> rootBuilder = ImmutableCompositeNode.builder();
- rootBuilder.setQName(SUBSCRIBE_RPC_NAME);
-
- final CompositeNodeBuilder<ImmutableCompositeNode> inputBuilder = ImmutableCompositeNode.builder();
- inputBuilder.setQName(INPUT_QNAME);
- inputBuilder.addLeaf(STREAM_NAME, "NETCONF");
+ final DataContainerNodeAttrBuilder<YangInstanceIdentifier.NodeIdentifier, ContainerNode> 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<CompositeNode> 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();
}
- @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(
"<rpc-reply xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\" message-id=\"m-5\">\n" +
"</data>\n" +
"</rpc-reply>\n"
));
- final RpcResult<CompositeNode> 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));
}
}
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;
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<RpcResult<CompositeNode>> successFuture = Futures.immediateFuture(RpcResultBuilder.<CompositeNode>success().build());
+ final CheckedFuture<DefaultDOMRpcResult, Exception> successFuture =
+ Futures.immediateCheckedFuture(new DefaultDOMRpcResult(((NormalizedNode<?, ?>) null)));
doReturn(successFuture)
- .doReturn(Futures.<RpcResult<CompositeNode>>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.<String>emptySet()));
final CheckedFuture<Void, TransactionCommitFailedException> submitFuture = tx.submit();
try {
} 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;
}
@Test
public void testDiscardChangesNotSentWithoutCandidate() {
- doReturn(Futures.immediateFuture(RpcResultBuilder.<CompositeNode>success().build()))
- .doReturn(Futures.<RpcResult<CompositeNode>>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.<String>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;
}
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;
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
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("<discard"));
+ }
+
+ @Test
+ public void tesGetSchemaRequest() throws Exception {
+ final NetconfMessage netconfMessage = netconfMessageTransformer.toRpcRequest(toPath(GET_SCHEMA_QNAME),
+ NetconfRemoteSchemaYangSourceProvider.createGetSchemaRequest("module", Optional.of("2012-12-12")));
+ assertSimilarXml(netconfMessage, "<rpc message-id=\"m-0\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n" +
+ "<get-schema xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring\">\n" +
+ "<format>yang</format>\n" +
+ "<identifier>module</identifier>\n" +
+ "<version>2012-12-12</version>\n" +
+ "</get-schema>\n" +
+ "</rpc>");
+ }
+
+ @Test
+ public void tesGetSchemaResponse() throws Exception {
+ final NetconfMessageTransformer netconfMessageTransformer = getTransformer(getSchema());
+ final NetconfMessage response = new NetconfMessage(XmlUtil.readXmlToDocument(
+ "<rpc-reply message-id=\"101\"\n" +
+ "xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n" +
+ "<data\n" +
+ "xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring\">\n" +
+ "<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\">\n" +
+ "Random YANG SCHEMA\n" +
+ "</xs:schema>\n" +
+ "</data>\n" +
+ "</rpc-reply>"
+ ));
+ 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("<rpc-reply message-id=\"101\"\n" +
+ "xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n" +
+ "<data>\n" +
+ "<netconf-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring\">\n" +
+ "<schemas>\n" +
+ "<schema>\n" +
+ "<identifier>module</identifier>\n" +
+ "<version>2012-12-12</version>\n" +
+ "<format>yang</format>\n" +
+ "</schema>\n" +
+ "</schemas>\n" +
+ "</netconf-state>\n" +
+ "</data>\n" +
+ "</rpc-reply>"));
+
+ 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<DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?>> values = Lists.newArrayList(
+ NetconfRemoteSchemaYangSourceProvider.createGetSchemaRequest("module", Optional.of("2012-12-12")).getValue());
+
+ final Map<QName, Object> keys = Maps.newHashMap();
+ for (final DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?> 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, "<rpc message-id=\"m-0\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n" +
+ "<get-config xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n" +
+ "<filter xmlns:ns0=\"urn:ietf:params:xml:ns:netconf:base:1.0\" ns0:type=\"subtree\">\n" +
+ "<netconf-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring\">\n" +
+ "<schemas/>\n" +
+ "</netconf-state>" +
+ "</filter>\n" +
+ "<source>\n" +
+ "<running/>\n" +
+ "</source>\n" +
+ "</get-config>" +
+ "</rpc>");
+ }
+
+ @Test
+ public void testEditConfigRequest() throws Exception {
+ final List<DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?>> values = Lists.newArrayList(
+ NetconfRemoteSchemaYangSourceProvider.createGetSchemaRequest("module", Optional.of("2012-12-12")).getValue());
+
+ final Map<QName, Object> keys = Maps.newHashMap();
+ for (final DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?> 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.<NormalizedNode<?, ?>>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, "<rpc message-id=\"m-0\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n" +
+ "<edit-config>\n" +
+ "<target>\n" +
+ "<candidate/>\n" +
+ "</target>\n" +
+ "<config>\n" +
+ "<netconf-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring\">\n" +
+ "<schemas>\n" +
+ "<schema>\n" +
+ "<identifier>module</identifier>\n" +
+ "<version>2012-12-12</version>\n" +
+ "<format>yang</format>\n" +
+ "</schema>\n" +
+ "</schemas>\n" +
+ "</netconf-state>\n" +
+ "</config>\n" +
+ "</edit-config>\n" +
+ "</rpc>");
+ }
+
+ 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.<Node<?>>emptyList()));
- assertThat(XmlUtil.toString(netconfMessage.getDocument()), CoreMatchers.containsString("<commit"));
+ public void testGetRequest() throws Exception {
+
+ final QName capability = QName.create(Capabilities.QNAME, "capability");
+ final DataContainerChild<?, ?> 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, "<rpc message-id=\"m-0\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">" +
+ "<get xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n" +
+ "<filter xmlns:ns0=\"urn:ietf:params:xml:ns:netconf:base:1.0\" ns0:type=\"subtree\">\n" +
+ "<netconf-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring\">\n" +
+ "<capabilities>\n" +
+ "<capability>a:b:c</capability>\n" +
+ "</capabilities>\n" +
+ "</netconf-state>" +
+ "</filter>\n" +
+ "</get>" +
+ "</rpc>");
}
- 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(
- "<rpc-reply><ok/></rpc-reply>"
+ "<rpc-reply xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\"><ok/></rpc-reply>"
));
- final RpcResult<CompositeNode> 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<InputStream> modelsToParse = Collections
- .singletonList(NetconfToRpcRequestTest.class.getResourceAsStream("/schemas/rpc-notification-subscription.yang"));
- final YangParserImpl parser = new YangParserImpl();
- final Set<Module> 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();
}
}
--- /dev/null
+/*
+ * 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<String, ByteSource>() {
+ @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<Object> 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
revision 2014-07-21 {
description "Initial revision.";
}
-
+
extension get-filter-element-attributes {
description
"If this extension is present within an 'anyxml'
following unqualified XML attribute is supported
within the <filter> element, within a <get> or
<get-config> 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 <edit-config> operation loads all or part of a specified
configuration to the specified target configuration.";
}
}
}
+
+ rpc discard-changes {
+ if-feature candidate;
+
+ description
+ "Revert the candidate configuration to the current
+ running configuration.";
+ reference "RFC 6241, Section 8.3.4.2";
+ }
}
--- /dev/null
+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
<modules>
<module>netconf-api</module>
- <module>netconf-cli</module>
+ <!--<module>netconf-cli</module>-->
<module>netconf-config</module>
<module>netconf-impl</module>
<module>config-netconf-connector</module>