Use Files.readString()
[netconf.git] / netconf / sal-netconf-connector / src / main / java / org / opendaylight / netconf / sal / connect / netconf / sal / NetconfDeviceTopologyAdapter.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 package org.opendaylight.netconf.sal.connect.netconf.sal;
9
10 import static java.util.Objects.requireNonNull;
11
12 import com.google.common.collect.ImmutableList;
13 import com.google.common.util.concurrent.FutureCallback;
14 import com.google.common.util.concurrent.MoreExecutors;
15 import com.google.common.util.concurrent.SettableFuture;
16 import java.util.concurrent.ExecutionException;
17 import java.util.stream.Collectors;
18 import org.opendaylight.mdsal.binding.api.DataBroker;
19 import org.opendaylight.mdsal.binding.api.Transaction;
20 import org.opendaylight.mdsal.binding.api.TransactionChain;
21 import org.opendaylight.mdsal.binding.api.TransactionChainListener;
22 import org.opendaylight.mdsal.binding.api.WriteTransaction;
23 import org.opendaylight.mdsal.common.api.CommitInfo;
24 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
25 import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCapabilities;
26 import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId;
27 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.PortNumber;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev221225.ConnectionOper.ConnectionStatus;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev221225.connection.oper.AvailableCapabilitiesBuilder;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev221225.connection.oper.ClusteredConnectionStatusBuilder;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev221225.connection.oper.UnavailableCapabilitiesBuilder;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev221225.connection.oper.available.capabilities.AvailableCapability;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev221225.connection.oper.unavailable.capabilities.UnavailableCapabilityBuilder;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev221225.NetconfNode;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev221225.NetconfNodeBuilder;
36 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
37 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeBuilder;
38 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey;
39 import org.opendaylight.yangtools.yang.common.Empty;
40 import org.opendaylight.yangtools.yang.common.Uint16;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
43
44 public final class NetconfDeviceTopologyAdapter implements TransactionChainListener, AutoCloseable {
45     private static final Logger LOG = LoggerFactory.getLogger(NetconfDeviceTopologyAdapter.class);
46
47     private final SettableFuture<Empty> closeFuture = SettableFuture.create();
48     private final DataBroker dataBroker;
49     private final RemoteDeviceId id;
50
51     private TransactionChain txChain;
52
53     NetconfDeviceTopologyAdapter(final DataBroker dataBroker, final RemoteDeviceId id) {
54         this.dataBroker = requireNonNull(dataBroker);
55         this.id = requireNonNull(id);
56         txChain = dataBroker.createMergingTransactionChain(this);
57
58         final WriteTransaction writeTx = txChain.newWriteOnlyTransaction();
59         LOG.trace("{}: Init device state transaction {} putting if absent operational data started.", id,
60             writeTx.getIdentifier());
61         writeTx.put(LogicalDatastoreType.OPERATIONAL, id.getTopologyBindingPath(), getNodeIdBuilder(id)
62             .addAugmentation(new NetconfNodeBuilder()
63                 .setConnectionStatus(ConnectionStatus.Connecting)
64                 .setHost(id.getHost())
65                 .setPort(new PortNumber(Uint16.valueOf(id.getAddress().getPort()))).build())
66             .build());
67         LOG.trace("{}: Init device state transaction {} putting operational data ended.", id, writeTx.getIdentifier());
68
69         commitTransaction(writeTx, "init");
70     }
71
72     public void updateDeviceData(final boolean up, final NetconfDeviceCapabilities capabilities) {
73         final WriteTransaction writeTx = txChain.newWriteOnlyTransaction();
74         LOG.trace("{}: Update device state transaction {} merging operational data started.",
75                 id, writeTx.getIdentifier());
76
77         // FIXME: this needs to be tied together with node's operational existence
78         writeTx.mergeParentStructurePut(LogicalDatastoreType.OPERATIONAL,
79             id.getTopologyBindingPath().augmentation(NetconfNode.class),
80             newNetconfNodeBuilder(up, capabilities).build());
81         LOG.trace("{}: Update device state transaction {} merging operational data ended.",
82                 id, writeTx.getIdentifier());
83
84         commitTransaction(writeTx, "update");
85     }
86
87     public void updateClusteredDeviceData(final boolean up, final String masterAddress,
88                                           final NetconfDeviceCapabilities capabilities) {
89         final WriteTransaction writeTx = txChain.newWriteOnlyTransaction();
90         LOG.trace("{}: Update device state transaction {} merging operational data started.",
91                 id, writeTx.getIdentifier());
92         writeTx.mergeParentStructurePut(LogicalDatastoreType.OPERATIONAL,
93             id.getTopologyBindingPath().augmentation(NetconfNode.class),
94             newNetconfNodeBuilder(up, capabilities)
95                 .setClusteredConnectionStatus(new ClusteredConnectionStatusBuilder()
96                     .setNetconfMasterNode(masterAddress)
97                     .build())
98                 .build());
99         LOG.trace("{}: Update device state transaction {} merging operational data ended.",
100                 id, writeTx.getIdentifier());
101
102         commitTransaction(writeTx, "update");
103     }
104
105     @Override
106     public void onTransactionChainFailed(final TransactionChain chain, final Transaction transaction,
107             final Throwable cause) {
108         LOG.warn("{}: TransactionChain({}) {} FAILED!", id, chain, transaction.getIdentifier(), cause);
109         chain.close();
110
111         txChain = dataBroker.createMergingTransactionChain(this);
112         LOG.info("{}: TransactionChain reset to {}", id, txChain);
113         // FIXME: restart last update
114     }
115
116     @Override
117     public void onTransactionChainSuccessful(final TransactionChain chain) {
118         LOG.trace("{}: TransactionChain({}) SUCCESSFUL", id, chain);
119         closeFuture.set(Empty.value());
120     }
121
122     public void setDeviceAsFailed(final Throwable throwable) {
123         String reason = throwable != null && throwable.getMessage() != null ? throwable.getMessage() : "Unknown reason";
124
125         final NetconfNode data = new NetconfNodeBuilder()
126                 .setHost(id.getHost())
127                 .setPort(new PortNumber(Uint16.valueOf(id.getAddress().getPort())))
128                 .setConnectionStatus(ConnectionStatus.UnableToConnect).setConnectedMessage(reason).build();
129
130         final WriteTransaction writeTx = txChain.newWriteOnlyTransaction();
131         LOG.trace(
132                 "{}: Setting device state as failed {} putting operational data started.",
133                 id, writeTx.getIdentifier());
134         writeTx.mergeParentStructurePut(LogicalDatastoreType.OPERATIONAL,
135                 id.getTopologyBindingPath().augmentation(NetconfNode.class), data);
136         LOG.trace(
137                 "{}: Setting device state as failed {} putting operational data ended.",
138                 id, writeTx.getIdentifier());
139
140         commitTransaction(writeTx, "update-failed-device");
141     }
142
143     private NetconfNodeBuilder newNetconfNodeBuilder(final boolean up, final NetconfDeviceCapabilities capabilities) {
144         return new NetconfNodeBuilder()
145             .setHost(id.getHost())
146             .setPort(new PortNumber(Uint16.valueOf(id.getAddress().getPort())))
147             .setConnectionStatus(up ? ConnectionStatus.Connected : ConnectionStatus.Connecting)
148             .setAvailableCapabilities(new AvailableCapabilitiesBuilder()
149                 .setAvailableCapability(ImmutableList.<AvailableCapability>builder()
150                     .addAll(capabilities.nonModuleBasedCapabilities())
151                     .addAll(capabilities.resolvedCapabilities())
152                     .build())
153                 .build())
154             .setUnavailableCapabilities(new UnavailableCapabilitiesBuilder()
155                 .setUnavailableCapability(capabilities.unresolvedCapabilites().entrySet().stream()
156                     .map(unresolved -> new UnavailableCapabilityBuilder()
157                         // FIXME: better conversion than 'toString' ?
158                         .setCapability(unresolved.getKey().toString())
159                         .setFailureReason(unresolved.getValue())
160                         .build())
161                     .collect(Collectors.toUnmodifiableList()))
162                 .build());
163     }
164
165     private void commitTransaction(final WriteTransaction transaction, final String txType) {
166         LOG.trace("{}: Committing Transaction {}:{}", id, txType, transaction.getIdentifier());
167
168         transaction.commit().addCallback(new FutureCallback<CommitInfo>() {
169             @Override
170             public void onSuccess(final CommitInfo result) {
171                 LOG.trace("{}: Transaction({}) {} SUCCESSFUL", id, txType, transaction.getIdentifier());
172             }
173
174             @Override
175             public void onFailure(final Throwable throwable) {
176                 LOG.error("{}: Transaction({}) {} FAILED!", id, txType, transaction.getIdentifier(), throwable);
177             }
178         }, MoreExecutors.directExecutor());
179     }
180
181     private static NodeBuilder getNodeIdBuilder(final RemoteDeviceId id) {
182         return new NodeBuilder().withKey(new NodeKey(new NodeId(id.getName())));
183     }
184
185     @Override
186     public void close() {
187         final WriteTransaction writeTx = txChain.newWriteOnlyTransaction();
188         LOG.trace("{}: Close device state transaction {} removing all data started.", id, writeTx.getIdentifier());
189         writeTx.delete(LogicalDatastoreType.OPERATIONAL, id.getTopologyBindingPath());
190         LOG.trace("{}: Close device state transaction {} removing all data ended.", id, writeTx.getIdentifier());
191         commitTransaction(writeTx, "close");
192
193         txChain.close();
194
195         try {
196             closeFuture.get();
197         } catch (InterruptedException | ExecutionException e) {
198             LOG.error("{}: Transaction(close) {} FAILED!", id, writeTx.getIdentifier(), e);
199             throw new IllegalStateException(id + "  Transaction(close) not committed correctly", e);
200         }
201     }
202 }