Use lock in topology node writer
[netconf.git] / opendaylight / netconf / netconf-topology / src / main / java / org / opendaylight / netconf / topology / impl / TopologyNodeWriter.java
1 /*
2  * Copyright (c) 2015 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
9 package org.opendaylight.netconf.topology.impl;
10
11 import com.google.common.base.Preconditions;
12 import com.google.common.util.concurrent.CheckedFuture;
13 import com.google.common.util.concurrent.FutureCallback;
14 import com.google.common.util.concurrent.Futures;
15 import java.util.concurrent.locks.ReentrantLock;
16 import javax.annotation.Nonnull;
17 import org.opendaylight.controller.md.sal.binding.api.BindingTransactionChain;
18 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
19 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
20 import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction;
21 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
22 import org.opendaylight.controller.md.sal.common.api.data.TransactionChain;
23 import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
24 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
25 import org.opendaylight.netconf.topology.util.NodeWriter;
26 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
27 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopologyBuilder;
28 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
29 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId;
30 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
31 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyBuilder;
32 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
33 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
34 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey;
35 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
36 import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
37 import org.opendaylight.yangtools.yang.common.QName;
38 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
39 import org.slf4j.Logger;
40 import org.slf4j.LoggerFactory;
41
42 public class TopologyNodeWriter implements NodeWriter{
43
44     private static final Logger LOG = LoggerFactory.getLogger(TopologyNodeWriter.class);
45
46     private final String topologyId;
47     private final BindingTransactionChain txChain;
48
49     private final InstanceIdentifier<NetworkTopology> networkTopologyPath;
50     private final KeyedInstanceIdentifier<Topology, TopologyKey> topologyListPath;
51
52     private final ReentrantLock lock = new ReentrantLock(true);
53
54     public TopologyNodeWriter(final String topologyId, final DataBroker dataBroker) {
55         this.topologyId = topologyId;
56         this.txChain = Preconditions.checkNotNull(dataBroker).createTransactionChain(new TransactionChainListener() {
57             @Override
58             public void onTransactionChainFailed(TransactionChain<?, ?> chain, AsyncTransaction<?, ?> transaction, Throwable cause) {
59                 LOG.error("{}: TransactionChain({}) {} FAILED!", chain,
60                         transaction.getIdentifier(), cause);
61                 throw new IllegalStateException("Clustered topology writer TransactionChain(" + chain + ") not committed correctly", cause);
62             }
63
64             @Override
65             public void onTransactionChainSuccessful(TransactionChain<?, ?> chain) {
66                 LOG.trace("Clustered topology writer TransactionChain({}) SUCCESSFUL", chain);
67             }
68         });
69
70         this.networkTopologyPath = InstanceIdentifier.builder(NetworkTopology.class).build();
71         this.topologyListPath = networkTopologyPath.child(Topology.class, new TopologyKey(new TopologyId(topologyId)));
72     }
73
74     @Override
75     public void init(@Nonnull NodeId id, @Nonnull Node operationalDataNode) {
76         lock.lock();
77         try {
78             final WriteTransaction writeTx = txChain.newWriteOnlyTransaction();
79
80             createNetworkTopologyIfNotPresent(writeTx);
81             final InstanceIdentifier<Node> path = createBindingPathForTopology(new NodeKey(id), topologyId);
82
83             LOG.trace("{}: Init device state transaction {} putting if absent operational data started. Putting data on path {}",
84                     id.getValue(), writeTx.getIdentifier(), path);
85             writeTx.put(LogicalDatastoreType.OPERATIONAL, path, operationalDataNode);
86             LOG.trace("{}: Init device state transaction {} putting operational data ended.",
87                     id.getValue(), writeTx.getIdentifier());
88
89             commitTransaction(writeTx, "init", id);
90         } finally {
91             lock.unlock();
92         }
93     }
94
95     @Override
96     public void update(@Nonnull NodeId id, @Nonnull Node operationalDataNode) {
97         lock.lock();
98         try {
99             final WriteTransaction writeTx = txChain.newWriteOnlyTransaction();
100
101             final InstanceIdentifier<Node> path = createBindingPathForTopology(new NodeKey(id), topologyId);
102             LOG.trace("{}: Update device state transaction {} merging operational data started. Putting data on path {}",
103                     id, writeTx.getIdentifier(), operationalDataNode);
104             writeTx.put(LogicalDatastoreType.OPERATIONAL, path, operationalDataNode);
105             LOG.trace("{}: Update device state transaction {} merging operational data ended.",
106                     id, writeTx.getIdentifier());
107
108             commitTransaction(writeTx, "update", id);
109         } finally {
110             lock.unlock();
111         }
112
113     }
114
115     @Override
116     public void delete(@Nonnull NodeId id) {
117         lock.lock();
118         try {
119             final WriteTransaction writeTx = txChain.newWriteOnlyTransaction();
120
121             final InstanceIdentifier<Node> path = createBindingPathForTopology(new NodeKey(id), topologyId);
122
123             LOG.trace(
124                     "{}: Close device state transaction {} removing all data started. Path: {}",
125                     id, writeTx.getIdentifier(), path);
126             writeTx.delete(LogicalDatastoreType.OPERATIONAL, path);
127             LOG.trace(
128                     "{}: Close device state transaction {} removing all data ended.",
129                     id, writeTx.getIdentifier());
130
131             commitTransaction(writeTx, "close", id);
132         } finally {
133             lock.unlock();
134         }
135     }
136
137     private void commitTransaction(final WriteTransaction transaction, final String txType, final NodeId id) {
138         LOG.trace("{}: Committing Transaction {}:{}", id.getValue(), txType,
139                 transaction.getIdentifier());
140         final CheckedFuture<Void, TransactionCommitFailedException> result = transaction.submit();
141
142         Futures.addCallback(result, new FutureCallback<Void>() {
143             @Override
144             public void onSuccess(final Void result) {
145                 LOG.trace("{}: Transaction({}) {} SUCCESSFUL", id.getValue(), txType,
146                         transaction.getIdentifier());
147             }
148
149             @Override
150             public void onFailure(final Throwable t) {
151                 LOG.error("{}: Transaction({}) {} FAILED!", id.getValue(), txType,
152                         transaction.getIdentifier(), t);
153                 throw new IllegalStateException(id.getValue() + "  Transaction(" + txType + ") not committed correctly", t);
154             }
155         });
156     }
157
158     private void createNetworkTopologyIfNotPresent(final WriteTransaction writeTx) {
159
160         final NetworkTopology networkTopology = new NetworkTopologyBuilder().build();
161         LOG.trace("{}: Merging {} container to ensure its presence", topologyId,
162                 NetworkTopology.QNAME, writeTx.getIdentifier());
163         writeTx.merge(LogicalDatastoreType.OPERATIONAL, networkTopologyPath, networkTopology);
164
165         final Topology topology = new TopologyBuilder().setTopologyId(new TopologyId(topologyId)).build();
166         LOG.trace("{}: Merging {} container to ensure its presence", topologyId,
167                 Topology.QNAME, writeTx.getIdentifier());
168         writeTx.merge(LogicalDatastoreType.OPERATIONAL, topologyListPath, topology);
169     }
170
171     private static InstanceIdentifier<Node> createBindingPathForTopology(final NodeKey key, final String topologyId) {
172         final InstanceIdentifier<NetworkTopology> networkTopology = InstanceIdentifier.builder(NetworkTopology.class).build();
173         final KeyedInstanceIdentifier<Topology, TopologyKey> topology = networkTopology.child(Topology.class, new TopologyKey(new TopologyId(topologyId)));
174         return topology
175                 .child(Node.class, new NodeKey(new NodeId(key.getNodeId().getValue())));
176     }
177
178     private static org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier createBIPathForTopology(final String name, final String topologyId) {
179         final YangInstanceIdentifier.InstanceIdentifierBuilder builder = YangInstanceIdentifier.builder();
180         builder
181                 .node(NetworkTopology.QNAME)
182                 .node(Topology.QNAME)
183                 .nodeWithKey(Topology.QNAME, QName.create(Topology.QNAME, "topology-id"), topologyId)
184                 .node(Node.QNAME)
185                 .nodeWithKey(Node.QNAME, QName.create(Node.QNAME, "node-id"), name);
186         return builder.build();
187     }
188 }