Bump upstreams
[netconf.git] / apps / netconf-topology / src / main / java / org / opendaylight / netconf / topology / spi / 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.topology.spi;
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.ListenableFuture;
15 import com.google.common.util.concurrent.MoreExecutors;
16 import com.google.common.util.concurrent.SettableFuture;
17 import org.checkerframework.checker.lock.qual.GuardedBy;
18 import org.eclipse.jdt.annotation.NonNull;
19 import org.opendaylight.mdsal.binding.api.DataBroker;
20 import org.opendaylight.mdsal.binding.api.TransactionChain;
21 import org.opendaylight.mdsal.binding.api.WriteTransaction;
22 import org.opendaylight.mdsal.common.api.CommitInfo;
23 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
24 import org.opendaylight.netconf.client.mdsal.NetconfDeviceCapabilities;
25 import org.opendaylight.netconf.client.mdsal.api.RemoteDeviceId;
26 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.base._1._0.rev110601.SessionIdType;
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.rev240120.ConnectionOper.ConnectionStatus;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev240120.connection.oper.AvailableCapabilitiesBuilder;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev240120.connection.oper.ClusteredConnectionStatusBuilder;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev240120.connection.oper.UnavailableCapabilitiesBuilder;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev240120.connection.oper.available.capabilities.AvailableCapability;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev240120.connection.oper.unavailable.capabilities.UnavailableCapabilityBuilder;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev231121.NetconfNode;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev231121.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;
38 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
39 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
40 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeBuilder;
41 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey;
42 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
43 import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
44 import org.opendaylight.yangtools.yang.common.Empty;
45 import org.opendaylight.yangtools.yang.common.Uint16;
46 import org.slf4j.Logger;
47 import org.slf4j.LoggerFactory;
48
49 public final class NetconfDeviceTopologyAdapter implements FutureCallback<Empty> {
50     private static final Logger LOG = LoggerFactory.getLogger(NetconfDeviceTopologyAdapter.class);
51
52     private final @NonNull KeyedInstanceIdentifier<Topology, TopologyKey> topologyPath;
53     private final DataBroker dataBroker;
54     private final RemoteDeviceId id;
55
56     @GuardedBy("this")
57     private SettableFuture<Empty> closeFuture;
58     @GuardedBy("this")
59     private TransactionChain txChain;
60
61     public NetconfDeviceTopologyAdapter(final DataBroker dataBroker,
62             final KeyedInstanceIdentifier<Topology, TopologyKey> topologyPath, final RemoteDeviceId id) {
63         this.dataBroker = requireNonNull(dataBroker);
64         this.topologyPath = requireNonNull(topologyPath);
65         this.id = requireNonNull(id);
66         txChain = dataBroker.createMergingTransactionChain();
67         txChain.addCallback(this);
68
69         final var tx = txChain.newWriteOnlyTransaction();
70         LOG.trace("{}: Init device state transaction {} putting if absent operational data started.", id,
71             tx.getIdentifier());
72         final var nodePath = nodePath();
73         tx.put(LogicalDatastoreType.OPERATIONAL, nodePath, new NodeBuilder()
74             .withKey(nodePath.getKey())
75             .addAugmentation(new NetconfNodeBuilder()
76                 .setConnectionStatus(ConnectionStatus.Connecting)
77                 .setHost(id.host())
78                 .setPort(new PortNumber(Uint16.valueOf(id.address().getPort()))).build())
79             .build());
80         LOG.trace("{}: Init device state transaction {} putting operational data ended.", id, tx.getIdentifier());
81         commitTransaction(tx, "init");
82     }
83
84     public synchronized void updateDeviceData(final boolean up, final NetconfDeviceCapabilities capabilities,
85             final SessionIdType sessionId) {
86         final var tx = txChain.newWriteOnlyTransaction();
87         LOG.trace("{}: Update device state transaction {} merging operational data started.", id, tx.getIdentifier());
88         tx.put(LogicalDatastoreType.OPERATIONAL, netconfNodePath(),
89             newNetconfNodeBuilder(up, capabilities, sessionId).build());
90         LOG.trace("{}: Update device state transaction {} merging operational data ended.", id, tx.getIdentifier());
91         commitTransaction(tx, "update");
92     }
93
94     public synchronized void updateClusteredDeviceData(final boolean up, final String masterAddress,
95             final NetconfDeviceCapabilities capabilities, final SessionIdType sessionId) {
96         final var tx = txChain.newWriteOnlyTransaction();
97         LOG.trace("{}: Update device state transaction {} merging operational data started.", id, tx.getIdentifier());
98         tx.put(LogicalDatastoreType.OPERATIONAL, netconfNodePath(), newNetconfNodeBuilder(up, capabilities, sessionId)
99             .setClusteredConnectionStatus(new ClusteredConnectionStatusBuilder()
100                 .setNetconfMasterNode(masterAddress)
101                 .build())
102             .build());
103         LOG.trace("{}: Update device state transaction {} merging operational data ended.", id, tx.getIdentifier());
104         commitTransaction(tx, "update");
105     }
106
107     public synchronized void setDeviceAsFailed(final Throwable throwable) {
108         final var data = new NetconfNodeBuilder()
109                 .setHost(id.host())
110                 .setPort(new PortNumber(Uint16.valueOf(id.address().getPort())))
111                 .setConnectionStatus(ConnectionStatus.UnableToConnect)
112                 .setConnectedMessage(extractReason(throwable))
113                 .build();
114
115         final var tx = txChain.newWriteOnlyTransaction();
116         LOG.trace("{}: Setting device state as failed {} putting operational data started.", id, tx.getIdentifier());
117         tx.put(LogicalDatastoreType.OPERATIONAL, netconfNodePath(), data);
118         LOG.trace("{}: Setting device state as failed {} putting operational data ended.", id, tx.getIdentifier());
119         commitTransaction(tx, "update-failed-device");
120     }
121
122     public synchronized ListenableFuture<Empty> shutdown() {
123         if (closeFuture != null) {
124             return closeFuture;
125         }
126
127         closeFuture = SettableFuture.create();
128
129         final var tx = txChain.newWriteOnlyTransaction();
130         LOG.trace("{}: Close device state transaction {} removing all data started.", id, tx.getIdentifier());
131         tx.delete(LogicalDatastoreType.OPERATIONAL, nodePath());
132         LOG.trace("{}: Close device state transaction {} removing all data ended.", id, tx.getIdentifier());
133         commitTransaction(tx, "close");
134
135         txChain.close();
136         return closeFuture;
137     }
138
139     private @NonNull KeyedInstanceIdentifier<Node, NodeKey> nodePath() {
140         return topologyPath.child(Node.class, new NodeKey(new NodeId(id.name())));
141     }
142
143     private @NonNull InstanceIdentifier<NetconfNode> netconfNodePath() {
144         return nodePath().augmentation(NetconfNode.class);
145     }
146
147     private NetconfNodeBuilder newNetconfNodeBuilder(final boolean up, final NetconfDeviceCapabilities capabilities,
148             final SessionIdType sessionId) {
149         return new NetconfNodeBuilder()
150             .setHost(id.host())
151             .setPort(new PortNumber(Uint16.valueOf(id.address().getPort())))
152             .setConnectionStatus(up ? ConnectionStatus.Connected : ConnectionStatus.Connecting)
153             .setAvailableCapabilities(new AvailableCapabilitiesBuilder()
154                 .setAvailableCapability(ImmutableList.<AvailableCapability>builder()
155                     .addAll(capabilities.nonModuleBasedCapabilities())
156                     .addAll(capabilities.resolvedCapabilities())
157                     .build())
158                 .build())
159             .setUnavailableCapabilities(new UnavailableCapabilitiesBuilder()
160                 .setUnavailableCapability(capabilities.unresolvedCapabilites().entrySet().stream()
161                     .map(unresolved -> new UnavailableCapabilityBuilder()
162                         // FIXME: better conversion than 'toString' ?
163                         .setCapability(unresolved.getKey().toString())
164                         .setFailureReason(unresolved.getValue())
165                         .build())
166                     .toList())
167                 .build())
168             .setSessionId(sessionId);
169     }
170
171     private void commitTransaction(final WriteTransaction transaction, final String txType) {
172         LOG.trace("{}: Committing Transaction {}:{}", id, txType, transaction.getIdentifier());
173
174         transaction.commit().addCallback(new FutureCallback<CommitInfo>() {
175             @Override
176             public void onSuccess(final CommitInfo result) {
177                 LOG.trace("{}: Transaction({}) {} SUCCESSFUL", id, txType, transaction.getIdentifier());
178             }
179
180             @Override
181             public void onFailure(final Throwable throwable) {
182                 LOG.error("{}: Transaction({}) {} FAILED!", id, txType, transaction.getIdentifier(), throwable);
183             }
184         }, MoreExecutors.directExecutor());
185     }
186
187     @Override
188     public synchronized void onFailure(final Throwable cause) {
189         LOG.warn("{}: transaction chain FAILED!", id, cause);
190         if (closeFuture != null) {
191             closeFuture.setException(cause);
192             return;
193         }
194
195         // FIXME: move this up once we have MDSAL-838 fixed
196         txChain.close();
197
198         txChain = dataBroker.createMergingTransactionChain();
199         LOG.info("{}: TransactionChain reset to {}", id, txChain);
200         txChain.addCallback(this);
201         // FIXME: restart last update
202     }
203
204     @Override
205     public synchronized void onSuccess(final Empty result) {
206         LOG.trace("{}: transaction chain SUCCESSFUL", id);
207         closeFuture.set(Empty.value());
208     }
209
210     private static @NonNull String extractReason(final Throwable throwable) {
211         if (throwable != null) {
212             final var message = throwable.getMessage();
213             if (message != null) {
214                 return message;
215             }
216         }
217         return "Unknown reason";
218     }
219 }