9ec3aa3bb00bff78762770203ae79f7b80a94f3f
[controller.git] / opendaylight / md-sal / sal-netconf-connector / src / main / java / org / opendaylight / controller / sal / connect / netconf / NetconfDeviceTwoPhaseCommitTransaction.java
1 /*
2  * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.controller.sal.connect.netconf;
9
10 import static org.opendaylight.controller.sal.connect.netconf.NetconfMapping.NETCONF_CANDIDATE_QNAME;
11 import static org.opendaylight.controller.sal.connect.netconf.NetconfMapping.NETCONF_COMMIT_QNAME;
12 import static org.opendaylight.controller.sal.connect.netconf.NetconfMapping.NETCONF_CONFIG_QNAME;
13 import static org.opendaylight.controller.sal.connect.netconf.NetconfMapping.NETCONF_EDIT_CONFIG_QNAME;
14 import static org.opendaylight.controller.sal.connect.netconf.NetconfMapping.NETCONF_OPERATION_QNAME;
15 import static org.opendaylight.controller.sal.connect.netconf.NetconfMapping.NETCONF_RUNNING_QNAME;
16 import static org.opendaylight.controller.sal.connect.netconf.NetconfMapping.NETCONF_TARGET_QNAME;
17
18 import java.util.Collection;
19 import java.util.Collections;
20 import java.util.List;
21 import java.util.Map;
22 import java.util.Map.Entry;
23 import java.util.concurrent.ExecutionException;
24
25 import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler.DataCommitTransaction;
26 import org.opendaylight.controller.md.sal.common.api.data.DataModification;
27 import org.opendaylight.yangtools.yang.common.QName;
28 import org.opendaylight.yangtools.yang.common.RpcError;
29 import org.opendaylight.yangtools.yang.common.RpcResult;
30 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
31 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
32 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifierWithPredicates;
33 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
34 import org.opendaylight.yangtools.yang.data.api.Node;
35 import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
36 import org.opendaylight.yangtools.yang.data.impl.util.CompositeNodeBuilder;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
39
40 import com.google.common.base.Optional;
41 import com.google.common.base.Preconditions;
42 import com.google.common.collect.ImmutableList;
43 import com.google.common.collect.Lists;
44
45 public class NetconfDeviceTwoPhaseCommitTransaction implements DataCommitTransaction<InstanceIdentifier, CompositeNode> {
46     private static final Logger LOG = LoggerFactory.getLogger(NetconfDeviceTwoPhaseCommitTransaction.class);
47     private final NetconfDevice device;
48     private final DataModification<InstanceIdentifier, CompositeNode> modification;
49     private final boolean candidateSupported = true;
50
51     public NetconfDeviceTwoPhaseCommitTransaction(NetconfDevice device,
52             DataModification<InstanceIdentifier, CompositeNode> modification) {
53         super();
54         this.device = device;
55         this.modification = modification;
56     }
57
58     void prepare() throws InterruptedException, ExecutionException {
59         for (InstanceIdentifier toRemove : modification.getRemovedConfigurationData()) {
60             sendDelete(toRemove);
61         }
62         for(Entry<InstanceIdentifier, CompositeNode> toUpdate : modification.getUpdatedConfigurationData().entrySet()) {
63             sendMerge(toUpdate.getKey(),toUpdate.getValue());
64         }
65
66     }
67
68     private void sendMerge(InstanceIdentifier key, CompositeNode value) throws InterruptedException, ExecutionException {
69         sendEditRpc(createEditStructure(key, Optional.<String>absent(), Optional.of(value)));
70     }
71
72     private void sendDelete(InstanceIdentifier toDelete) throws InterruptedException, ExecutionException {
73         sendEditRpc(createEditStructure(toDelete, Optional.of("delete"), Optional.<CompositeNode> absent()));
74     }
75
76     private void sendEditRpc(CompositeNode editStructure) throws InterruptedException, ExecutionException {
77         CompositeNodeBuilder<ImmutableCompositeNode> builder = configurationRpcBuilder();
78         builder.setQName(NETCONF_EDIT_CONFIG_QNAME);
79         builder.add(editStructure);
80
81         RpcResult<CompositeNode> rpcResult = device.invokeRpc(NETCONF_EDIT_CONFIG_QNAME, builder.toInstance()).get();
82         Preconditions.checkState(rpcResult.isSuccessful(),"Rpc Result was unsuccessful");
83
84     }
85
86     private CompositeNodeBuilder<ImmutableCompositeNode> configurationRpcBuilder() {
87         CompositeNodeBuilder<ImmutableCompositeNode> ret = ImmutableCompositeNode.builder();
88
89         Node<?> targetNode;
90         if(candidateSupported) {
91             targetNode = ImmutableCompositeNode.create(NETCONF_CANDIDATE_QNAME, ImmutableList.<Node<?>>of());
92         } else {
93             targetNode = ImmutableCompositeNode.create(NETCONF_RUNNING_QNAME, ImmutableList.<Node<?>>of());
94         }
95         Node<?> targetWrapperNode = ImmutableCompositeNode.create(NETCONF_TARGET_QNAME, ImmutableList.<Node<?>>of(targetNode));
96         ret.add(targetWrapperNode);
97         return ret;
98     }
99
100     private CompositeNode createEditStructure(InstanceIdentifier dataPath, Optional<String> operation,
101             Optional<CompositeNode> lastChildOverride) {
102         List<PathArgument> path = dataPath.getPath();
103         List<PathArgument> reversed = Lists.reverse(path);
104         CompositeNode previous = null;
105         boolean isLast = true;
106         for (PathArgument arg : reversed) {
107             CompositeNodeBuilder<ImmutableCompositeNode> builder = ImmutableCompositeNode.builder();
108             builder.setQName(arg.getNodeType());
109             Map<QName, Object> predicates = Collections.emptyMap();
110             if (arg instanceof NodeIdentifierWithPredicates) {
111                 predicates = ((NodeIdentifierWithPredicates) arg).getKeyValues();
112             }
113             for (Entry<QName, Object> entry : predicates.entrySet()) {
114                 builder.addLeaf(entry.getKey(), entry.getValue());
115             }
116
117             if (isLast) {
118                 if (operation.isPresent()) {
119                     builder.setAttribute(NETCONF_OPERATION_QNAME, operation.get());
120                 }
121                 if (lastChildOverride.isPresent()) {
122                     List<Node<?>> children = lastChildOverride.get().getChildren();
123                     for(Node<?> child : children) {
124                         if(!predicates.containsKey(child.getKey())) {
125                             builder.add(child);
126                         }
127                     }
128
129                 }
130             } else {
131                 builder.add(previous);
132             }
133             previous = builder.toInstance();
134             isLast = false;
135         }
136         return ImmutableCompositeNode.create(NETCONF_CONFIG_QNAME, ImmutableList.<Node<?>>of(previous));
137     }
138
139     @Override
140     public RpcResult<Void> finish() {
141         CompositeNodeBuilder<ImmutableCompositeNode> commitInput = ImmutableCompositeNode.builder();
142         commitInput.setQName(NETCONF_COMMIT_QNAME);
143         try {
144             final RpcResult<?> rpcResult = device.invokeRpc(NetconfMapping.NETCONF_COMMIT_QNAME, commitInput.toInstance()).get();
145             return new RpcResult<Void>() {
146
147                 @Override
148                 public boolean isSuccessful() {
149                     return rpcResult.isSuccessful();
150                 }
151
152                 @Override
153                 public Void getResult() {
154                     return null;
155                 }
156
157                 @Override
158                 public Collection<RpcError> getErrors() {
159                     return rpcResult.getErrors();
160                 }
161             };
162         } catch (final InterruptedException | ExecutionException e) {
163             LOG.warn("Failed to finish operation", e);
164             return new RpcResult<Void>() {
165                 @Override
166                 public boolean isSuccessful() {
167                     return false;
168                 }
169
170                 @Override
171                 public Void getResult() {
172                     return null;
173                 }
174
175                 @Override
176                 public Collection<RpcError> getErrors() {
177                     // FIXME: wrap the exception
178                     return Collections.emptySet();
179                 }
180             };
181         }
182     }
183
184     @Override
185     public DataModification<InstanceIdentifier, CompositeNode> getModification() {
186         return this.modification;
187     }
188
189     @Override
190     public RpcResult<Void> rollback() throws IllegalStateException {
191         // TODO Auto-generated method stub
192         return null;
193     }
194 }