/* * 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.opendaylight.controller.sal.connect.netconf.NetconfMapping.NETCONF_CANDIDATE_QNAME; import static org.opendaylight.controller.sal.connect.netconf.NetconfMapping.NETCONF_COMMIT_QNAME; import static org.opendaylight.controller.sal.connect.netconf.NetconfMapping.NETCONF_CONFIG_QNAME; import static org.opendaylight.controller.sal.connect.netconf.NetconfMapping.NETCONF_EDIT_CONFIG_QNAME; import static org.opendaylight.controller.sal.connect.netconf.NetconfMapping.NETCONF_ERROR_OPTION_QNAME; import static org.opendaylight.controller.sal.connect.netconf.NetconfMapping.NETCONF_OPERATION_QNAME; import static org.opendaylight.controller.sal.connect.netconf.NetconfMapping.NETCONF_RUNNING_QNAME; import static org.opendaylight.controller.sal.connect.netconf.NetconfMapping.NETCONF_TARGET_QNAME; import static org.opendaylight.controller.sal.connect.netconf.NetconfMapping.ROLLBACK_ON_ERROR_OPTION; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.ExecutionException; import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler.DataCommitTransaction; import org.opendaylight.controller.md.sal.common.api.data.DataModification; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.common.RpcError; import org.opendaylight.yangtools.yang.common.RpcResult; import org.opendaylight.yangtools.yang.data.api.CompositeNode; import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifierWithPredicates; import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument; import org.opendaylight.yangtools.yang.data.api.Node; import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode; import org.opendaylight.yangtools.yang.data.impl.SimpleNodeTOImpl; import org.opendaylight.yangtools.yang.data.impl.util.CompositeNodeBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.base.Optional; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; class NetconfDeviceTwoPhaseCommitTransaction implements DataCommitTransaction { private static final Logger LOG = LoggerFactory.getLogger(NetconfDeviceTwoPhaseCommitTransaction.class); private final DataModification modification; private final NetconfDevice device; private final boolean candidateSupported; private final boolean rollbackSupported; public NetconfDeviceTwoPhaseCommitTransaction(NetconfDevice device, DataModification modification, boolean candidateSupported, boolean rollbackOnErrorSupported) { this.device = Preconditions.checkNotNull(device); this.modification = Preconditions.checkNotNull(modification); this.candidateSupported = candidateSupported; this.rollbackSupported = rollbackOnErrorSupported; } void prepare() throws InterruptedException, ExecutionException { for (InstanceIdentifier toRemove : modification.getRemovedConfigurationData()) { sendDelete(toRemove); } for(Entry toUpdate : modification.getUpdatedConfigurationData().entrySet()) { sendMerge(toUpdate.getKey(),toUpdate.getValue()); } } private void sendMerge(InstanceIdentifier key, CompositeNode value) throws InterruptedException, ExecutionException { sendEditRpc(createEditStructure(key, Optional.absent(), Optional.of(value))); } private void sendDelete(InstanceIdentifier toDelete) throws InterruptedException, ExecutionException { sendEditRpc(createEditStructure(toDelete, Optional.of("delete"), Optional. absent())); } private void sendEditRpc(CompositeNode editStructure) throws InterruptedException, ExecutionException { CompositeNodeBuilder builder = configurationRpcBuilder(); builder.setQName(NETCONF_EDIT_CONFIG_QNAME); builder.add(editStructure); RpcResult rpcResult = device.invokeRpc(NETCONF_EDIT_CONFIG_QNAME, builder.toInstance()).get(); Preconditions.checkState(rpcResult.isSuccessful(),"Rpc Result was unsuccessful"); } private CompositeNodeBuilder configurationRpcBuilder() { CompositeNodeBuilder ret = ImmutableCompositeNode.builder(); Node targetNode; if(candidateSupported) { targetNode = ImmutableCompositeNode.create(NETCONF_CANDIDATE_QNAME, ImmutableList.>of()); } else { targetNode = ImmutableCompositeNode.create(NETCONF_RUNNING_QNAME, ImmutableList.>of()); } Node targetWrapperNode = ImmutableCompositeNode.create(NETCONF_TARGET_QNAME, ImmutableList.>of(targetNode)); if(rollbackSupported) { LOG.debug("Rollback-on-error supported, setting {} to {}", NETCONF_ERROR_OPTION_QNAME, ROLLBACK_ON_ERROR_OPTION); ret.addLeaf(NETCONF_ERROR_OPTION_QNAME, ROLLBACK_ON_ERROR_OPTION); } ret.add(targetWrapperNode); return ret; } private CompositeNode createEditStructure(InstanceIdentifier dataPath, Optional operation, Optional lastChildOverride) { List path = dataPath.getPath(); List reversed = Lists.reverse(path); CompositeNode previous = null; boolean isLast = true; for (PathArgument arg : reversed) { CompositeNodeBuilder builder = ImmutableCompositeNode.builder(); builder.setQName(arg.getNodeType()); Map predicates = Collections.emptyMap(); if (arg instanceof NodeIdentifierWithPredicates) { predicates = ((NodeIdentifierWithPredicates) arg).getKeyValues(); } for (Entry entry : predicates.entrySet()) { builder.addLeaf(entry.getKey(), entry.getValue()); } if (isLast) { if (operation.isPresent()) { builder.setAttribute(NETCONF_OPERATION_QNAME, operation.get()); } if (lastChildOverride.isPresent()) { List> children = lastChildOverride.get().getChildren(); for(Node child : children) { if(!predicates.containsKey(child.getKey())) { builder.add(child); } } } } else { builder.add(previous); } previous = builder.toInstance(); isLast = false; } return ImmutableCompositeNode.create(NETCONF_CONFIG_QNAME, ImmutableList.>of(previous)); } @Override public RpcResult finish() { CompositeNodeBuilder commitInput = ImmutableCompositeNode.builder(); commitInput.setQName(NETCONF_COMMIT_QNAME); try { final RpcResult rpcResult = device.invokeRpc(NetconfMapping.NETCONF_COMMIT_QNAME, commitInput.toInstance()).get(); return new RpcResult() { @Override public boolean isSuccessful() { return rpcResult.isSuccessful(); } @Override public Void getResult() { return null; } @Override public Collection getErrors() { return rpcResult.getErrors(); } }; } catch (final InterruptedException | ExecutionException e) { LOG.warn("Failed to finish operation", e); return new RpcResult() { @Override public boolean isSuccessful() { return false; } @Override public Void getResult() { return null; } @Override public Collection getErrors() { // FIXME: wrap the exception return Collections.emptySet(); } }; } } @Override public DataModification getModification() { return this.modification; } @Override public RpcResult rollback() throws IllegalStateException { // TODO Auto-generated method stub return null; } }