/* * 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.netconf.sal.connect.netconf.util; import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.EDIT_CONTENT_NODEID; import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_CANDIDATE_QNAME; import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_COPY_CONFIG_NODEID; import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_COPY_CONFIG_PATH; import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_DEFAULT_OPERATION_NODEID; import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_DISCARD_CHANGES_PATH; import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_EDIT_CONFIG_NODEID; import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_EDIT_CONFIG_PATH; import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_ERROR_OPTION_NODEID; import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_GET_CONFIG_NODEID; import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_GET_CONFIG_PATH; import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_GET_NODEID; import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_GET_PATH; import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_LOCK_NODEID; import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_LOCK_PATH; import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_RUNNING_QNAME; import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_SOURCE_NODEID; import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_TARGET_NODEID; import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_UNLOCK_NODEID; import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_UNLOCK_PATH; import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_VALIDATE_NODEID; import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.ROLLBACK_ON_ERROR_OPTION; import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.toFilterStructure; import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.toId; import com.google.common.base.Preconditions; import com.google.common.util.concurrent.FluentFuture; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.MoreExecutors; import java.util.Locale; import java.util.Optional; import org.opendaylight.mdsal.dom.api.DOMRpcResult; import org.opendaylight.mdsal.dom.api.DOMRpcService; import org.opendaylight.netconf.sal.connect.netconf.sal.KeepaliveSalFacade.KeepaliveDOMRpcService; import org.opendaylight.netconf.sal.connect.netconf.sal.SchemalessNetconfDeviceRpc; 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.Empty; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.data.api.ModifyAction; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; 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.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. * According to RFC-6241 */ public final class NetconfBaseOps { private static final NodeIdentifier CONFIG_SOURCE_NODEID = NodeIdentifier.create(ConfigSource.QNAME); private static final NodeIdentifier CONFIG_TARGET_NODEID = NodeIdentifier.create(ConfigTarget.QNAME); private final DOMRpcService rpc; private final SchemaContext schemaContext; private final RpcStructureTransformer transformer; public NetconfBaseOps(final DOMRpcService rpc, final SchemaContext schemaContext) { this.rpc = rpc; this.schemaContext = schemaContext; if (rpc instanceof KeepaliveDOMRpcService && ((KeepaliveDOMRpcService) rpc).getDeviceRpc() instanceof SchemalessNetconfDeviceRpc) { this.transformer = new SchemalessRpcStructureTransformer(); } else { this.transformer = new NetconfRpcStructureTransformer(schemaContext); } } public FluentFuture lock(final FutureCallback callback, final QName datastore) { Preconditions.checkNotNull(callback); Preconditions.checkNotNull(datastore); final FluentFuture future = rpc.invokeRpc(NETCONF_LOCK_PATH, getLockContent(datastore)); future.addCallback(callback, MoreExecutors.directExecutor()); return future; } public FluentFuture lockCandidate(final FutureCallback callback) { final FluentFuture future = rpc.invokeRpc(NETCONF_LOCK_PATH, getLockContent(NETCONF_CANDIDATE_QNAME)); future.addCallback(callback, MoreExecutors.directExecutor()); return future; } public FluentFuture lockRunning(final FutureCallback callback) { final FluentFuture future = rpc.invokeRpc(NETCONF_LOCK_PATH, getLockContent(NETCONF_RUNNING_QNAME)); future.addCallback(callback, MoreExecutors.directExecutor()); return future; } public FluentFuture unlock(final FutureCallback callback, final QName datastore) { Preconditions.checkNotNull(callback); Preconditions.checkNotNull(datastore); final FluentFuture future = rpc.invokeRpc(NETCONF_UNLOCK_PATH, getUnLockContent(datastore)); future.addCallback(callback, MoreExecutors.directExecutor()); return future; } public FluentFuture unlockRunning(final FutureCallback callback) { final FluentFuture future = rpc.invokeRpc(NETCONF_UNLOCK_PATH, getUnLockContent(NETCONF_RUNNING_QNAME)); future.addCallback(callback, MoreExecutors.directExecutor()); return future; } public FluentFuture unlockCandidate(final FutureCallback callback) { final FluentFuture future = rpc.invokeRpc(NETCONF_UNLOCK_PATH, getUnLockContent(NETCONF_CANDIDATE_QNAME)); future.addCallback(callback, MoreExecutors.directExecutor()); return future; } public FluentFuture discardChanges(final FutureCallback callback) { Preconditions.checkNotNull(callback); final FluentFuture future = rpc.invokeRpc(NETCONF_DISCARD_CHANGES_PATH, null); future.addCallback(callback, MoreExecutors.directExecutor()); return future; } public FluentFuture commit(final FutureCallback callback) { Preconditions.checkNotNull(callback); final FluentFuture future = rpc.invokeRpc(NetconfMessageTransformUtil.NETCONF_COMMIT_PATH, NetconfMessageTransformUtil.COMMIT_RPC_CONTENT); future.addCallback(callback, MoreExecutors.directExecutor()); return future; } public FluentFuture validate(final FutureCallback callback, final QName datastore) { Preconditions.checkNotNull(callback); Preconditions.checkNotNull(datastore); final FluentFuture future = rpc.invokeRpc(NetconfMessageTransformUtil.NETCONF_VALIDATE_PATH, getValidateContent(datastore)); future.addCallback(callback, MoreExecutors.directExecutor()); return future; } public FluentFuture validateCandidate(final FutureCallback callback) { return validate(callback, NETCONF_CANDIDATE_QNAME); } public FluentFuture validateRunning(final FutureCallback callback) { return validate(callback, NETCONF_RUNNING_QNAME); } public FluentFuture copyConfig(final FutureCallback callback, final QName source, final QName target) { Preconditions.checkNotNull(callback); Preconditions.checkNotNull(source); Preconditions.checkNotNull(target); final FluentFuture future = rpc.invokeRpc(NETCONF_COPY_CONFIG_PATH, getCopyConfigContent(source, target)); future.addCallback(callback, MoreExecutors.directExecutor()); return future; } public FluentFuture copyRunningToCandidate(final FutureCallback callback) { return copyConfig(callback, NETCONF_RUNNING_QNAME, NETCONF_CANDIDATE_QNAME); } public FluentFuture getConfig(final FutureCallback callback, final QName datastore, final Optional filterPath) { Preconditions.checkNotNull(callback); Preconditions.checkNotNull(datastore); final FluentFuture future; if (isFilterPresent(filterPath)) { final DataContainerChild node = transformer.toFilterStructure(filterPath.get()); future = rpc.invokeRpc(NETCONF_GET_CONFIG_PATH, NetconfMessageTransformUtil.wrap(NETCONF_GET_CONFIG_NODEID, getSourceNode(datastore), node)); } else { future = rpc.invokeRpc(NETCONF_GET_CONFIG_PATH, NetconfMessageTransformUtil.wrap(NETCONF_GET_CONFIG_NODEID, getSourceNode(datastore))); } future.addCallback(callback, MoreExecutors.directExecutor()); return future; } public FluentFuture>> getConfigRunningData( final FutureCallback callback, final Optional filterPath) { final FluentFuture configRunning = getConfigRunning(callback, filterPath); return extractData(filterPath, configRunning); } public FluentFuture>> getData(final FutureCallback callback, final Optional filterPath) { final FluentFuture configRunning = get(callback, filterPath); return extractData(filterPath, configRunning); } private FluentFuture>> extractData( final Optional path, final FluentFuture configRunning) { return configRunning.transform(result -> { Preconditions.checkArgument(result.getErrors().isEmpty(), "Unable to read data: %s, errors: %s", path, result.getErrors()); final DataContainerChild dataNode = ((ContainerNode) result.getResult()).getChild(NetconfMessageTransformUtil.NETCONF_DATA_NODEID) .get(); return transformer.selectFromDataStructure(dataNode, path.get()); }, MoreExecutors.directExecutor()); } public FluentFuture getConfigRunning(final FutureCallback callback, final Optional filterPath) { return getConfig(callback, NETCONF_RUNNING_QNAME, filterPath); } public FluentFuture getConfigCandidate(final FutureCallback callback, final Optional filterPath) { return getConfig(callback, NETCONF_CANDIDATE_QNAME, filterPath); } public FluentFuture get(final FutureCallback callback, final Optional filterPath) { Preconditions.checkNotNull(callback); final FluentFuture future = rpc.invokeRpc(NETCONF_GET_PATH, isFilterPresent(filterPath) ? NetconfMessageTransformUtil.wrap(NETCONF_GET_NODEID, toFilterStructure(filterPath.get(), schemaContext)) : NetconfMessageTransformUtil.GET_RPC_CONTENT); future.addCallback(callback, MoreExecutors.directExecutor()); return future; } private static boolean isFilterPresent(final Optional filterPath) { return filterPath.isPresent() && !filterPath.get().isEmpty(); } public FluentFuture editConfigCandidate(final FutureCallback callback, final DataContainerChild editStructure, final ModifyAction modifyAction, final boolean rollback) { return editConfig(callback, NETCONF_CANDIDATE_QNAME, editStructure, Optional.of(modifyAction), rollback); } public FluentFuture editConfigCandidate(final FutureCallback callback, final DataContainerChild editStructure, final boolean rollback) { return editConfig(callback, NETCONF_CANDIDATE_QNAME, editStructure, Optional.empty(), rollback); } public FluentFuture editConfigRunning(final FutureCallback callback, final DataContainerChild editStructure, final ModifyAction modifyAction, final boolean rollback) { return editConfig(callback, NETCONF_RUNNING_QNAME, editStructure, Optional.of(modifyAction), rollback); } public FluentFuture editConfigRunning(final FutureCallback callback, final DataContainerChild editStructure, final boolean rollback) { return editConfig(callback, NETCONF_RUNNING_QNAME, editStructure, Optional.empty(), rollback); } public FluentFuture editConfig( final FutureCallback callback, final QName datastore, final DataContainerChild editStructure, final Optional modifyAction, final boolean rollback) { Preconditions.checkNotNull(editStructure); Preconditions.checkNotNull(callback); Preconditions.checkNotNull(datastore); final FluentFuture future = rpc.invokeRpc(NETCONF_EDIT_CONFIG_PATH, getEditConfigContent(datastore, editStructure, modifyAction, rollback)); future.addCallback(callback, MoreExecutors.directExecutor()); return future; } public DataContainerChild createEditConfigStrcture(final Optional> lastChild, final Optional operation, final YangInstanceIdentifier dataPath) { final AnyXmlNode configContent = transformer.createEditConfigStructure(lastChild, dataPath, operation); return Builders.choiceBuilder().withNodeIdentifier(EDIT_CONTENT_NODEID).withChild(configContent).build(); } private static ContainerNode getEditConfigContent( final QName datastore, final DataContainerChild editStructure, final Optional defaultOperation, final boolean rollback) { final DataContainerNodeAttrBuilder editBuilder = Builders.containerBuilder().withNodeIdentifier(NETCONF_EDIT_CONFIG_NODEID); // Target editBuilder.withChild(getTargetNode(datastore)); // Default operation if (defaultOperation.isPresent()) { final String opString = defaultOperation.get().name().toLowerCase(Locale.ROOT); editBuilder.withChild(Builders.leafBuilder().withNodeIdentifier(NETCONF_DEFAULT_OPERATION_NODEID) .withValue(opString).build()); } // Error option if (rollback) { editBuilder.withChild(Builders.leafBuilder().withNodeIdentifier(NETCONF_ERROR_OPTION_NODEID) .withValue(ROLLBACK_ON_ERROR_OPTION).build()); } // Edit content editBuilder.withChild(editStructure); return editBuilder.build(); } public static DataContainerChild getSourceNode(final QName datastore) { return Builders.containerBuilder().withNodeIdentifier(NETCONF_SOURCE_NODEID) .withChild(Builders.choiceBuilder().withNodeIdentifier(CONFIG_SOURCE_NODEID).withChild( Builders.leafBuilder().withNodeIdentifier(toId(datastore)).withValue(Empty.getInstance()).build()) .build()).build(); } public static ContainerNode getLockContent(final QName datastore) { return Builders.containerBuilder().withNodeIdentifier(NETCONF_LOCK_NODEID) .withChild(getTargetNode(datastore)).build(); } public static DataContainerChild getTargetNode(final QName datastore) { return Builders.containerBuilder().withNodeIdentifier(NETCONF_TARGET_NODEID) .withChild(Builders.choiceBuilder().withNodeIdentifier(CONFIG_TARGET_NODEID).withChild( Builders.leafBuilder().withNodeIdentifier(toId(datastore)).withValue(Empty.getInstance()).build()) .build()).build(); } public static NormalizedNode getCopyConfigContent(final QName source, final QName target) { return Builders.containerBuilder().withNodeIdentifier(NETCONF_COPY_CONFIG_NODEID) .withChild(getTargetNode(target)).withChild(getSourceNode(source)).build(); } public static NormalizedNode getValidateContent(final QName source) { return Builders.containerBuilder().withNodeIdentifier(NETCONF_VALIDATE_NODEID) .withChild(getSourceNode(source)).build(); } public static NormalizedNode getUnLockContent(final QName datastore) { return Builders.containerBuilder().withNodeIdentifier(NETCONF_UNLOCK_NODEID) .withChild(getTargetNode(datastore)).build(); } }