2 * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.netconf.topology.spi;
10 import static java.util.Objects.requireNonNull;
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.Transaction;
21 import org.opendaylight.mdsal.binding.api.TransactionChain;
22 import org.opendaylight.mdsal.binding.api.TransactionChainListener;
23 import org.opendaylight.mdsal.binding.api.WriteTransaction;
24 import org.opendaylight.mdsal.common.api.CommitInfo;
25 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
26 import org.opendaylight.netconf.client.mdsal.NetconfDeviceCapabilities;
27 import org.opendaylight.netconf.client.mdsal.api.RemoteDeviceId;
28 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.base._1._0.rev110601.SessionIdType;
29 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.PortNumber;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev231025.ConnectionOper.ConnectionStatus;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev231025.connection.oper.AvailableCapabilitiesBuilder;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev231025.connection.oper.ClusteredConnectionStatusBuilder;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev231025.connection.oper.UnavailableCapabilitiesBuilder;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev231025.connection.oper.available.capabilities.AvailableCapability;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev231025.connection.oper.unavailable.capabilities.UnavailableCapabilityBuilder;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev221225.NetconfNode;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev221225.NetconfNodeBuilder;
38 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
39 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
40 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
41 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
42 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeBuilder;
43 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey;
44 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
45 import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
46 import org.opendaylight.yangtools.yang.common.Empty;
47 import org.opendaylight.yangtools.yang.common.Uint16;
48 import org.slf4j.Logger;
49 import org.slf4j.LoggerFactory;
51 public final class NetconfDeviceTopologyAdapter implements TransactionChainListener {
52 private static final Logger LOG = LoggerFactory.getLogger(NetconfDeviceTopologyAdapter.class);
54 private final @NonNull KeyedInstanceIdentifier<Topology, TopologyKey> topologyPath;
55 private final DataBroker dataBroker;
56 private final RemoteDeviceId id;
59 private SettableFuture<Empty> closeFuture;
61 private TransactionChain txChain;
63 public NetconfDeviceTopologyAdapter(final DataBroker dataBroker,
64 final KeyedInstanceIdentifier<Topology, TopologyKey> topologyPath, final RemoteDeviceId id) {
65 this.dataBroker = requireNonNull(dataBroker);
66 this.topologyPath = requireNonNull(topologyPath);
67 this.id = requireNonNull(id);
68 txChain = dataBroker.createMergingTransactionChain(this);
70 final var tx = txChain.newWriteOnlyTransaction();
71 LOG.trace("{}: Init device state transaction {} putting if absent operational data started.", id,
73 final var nodePath = nodePath();
74 tx.put(LogicalDatastoreType.OPERATIONAL, nodePath, new NodeBuilder()
75 .withKey(nodePath.getKey())
76 .addAugmentation(new NetconfNodeBuilder()
77 .setConnectionStatus(ConnectionStatus.Connecting)
79 .setPort(new PortNumber(Uint16.valueOf(id.address().getPort()))).build())
81 LOG.trace("{}: Init device state transaction {} putting operational data ended.", id, tx.getIdentifier());
82 commitTransaction(tx, "init");
85 public synchronized void updateDeviceData(final boolean up, final NetconfDeviceCapabilities capabilities,
86 final SessionIdType sessionId) {
87 final var tx = txChain.newWriteOnlyTransaction();
88 LOG.trace("{}: Update device state transaction {} merging operational data started.", id, tx.getIdentifier());
89 tx.put(LogicalDatastoreType.OPERATIONAL, netconfNodePath(),
90 newNetconfNodeBuilder(up, capabilities, sessionId).build());
91 LOG.trace("{}: Update device state transaction {} merging operational data ended.", id, tx.getIdentifier());
92 commitTransaction(tx, "update");
95 public synchronized void updateClusteredDeviceData(final boolean up, final String masterAddress,
96 final NetconfDeviceCapabilities capabilities, final SessionIdType sessionId) {
97 final var tx = txChain.newWriteOnlyTransaction();
98 LOG.trace("{}: Update device state transaction {} merging operational data started.", id, tx.getIdentifier());
99 tx.put(LogicalDatastoreType.OPERATIONAL, netconfNodePath(), newNetconfNodeBuilder(up, capabilities, sessionId)
100 .setClusteredConnectionStatus(new ClusteredConnectionStatusBuilder()
101 .setNetconfMasterNode(masterAddress)
104 LOG.trace("{}: Update device state transaction {} merging operational data ended.", id, tx.getIdentifier());
105 commitTransaction(tx, "update");
108 public synchronized void setDeviceAsFailed(final Throwable throwable) {
109 final var data = new NetconfNodeBuilder()
111 .setPort(new PortNumber(Uint16.valueOf(id.address().getPort())))
112 .setConnectionStatus(ConnectionStatus.UnableToConnect)
113 .setConnectedMessage(extractReason(throwable))
116 final var tx = txChain.newWriteOnlyTransaction();
117 LOG.trace("{}: Setting device state as failed {} putting operational data started.", id, tx.getIdentifier());
118 tx.put(LogicalDatastoreType.OPERATIONAL, netconfNodePath(), data);
119 LOG.trace("{}: Setting device state as failed {} putting operational data ended.", id, tx.getIdentifier());
120 commitTransaction(tx, "update-failed-device");
123 public synchronized ListenableFuture<Empty> shutdown() {
124 if (closeFuture != null) {
128 closeFuture = SettableFuture.create();
130 final var tx = txChain.newWriteOnlyTransaction();
131 LOG.trace("{}: Close device state transaction {} removing all data started.", id, tx.getIdentifier());
132 tx.delete(LogicalDatastoreType.OPERATIONAL, nodePath());
133 LOG.trace("{}: Close device state transaction {} removing all data ended.", id, tx.getIdentifier());
134 commitTransaction(tx, "close");
140 private @NonNull KeyedInstanceIdentifier<Node, NodeKey> nodePath() {
141 return topologyPath.child(Node.class, new NodeKey(new NodeId(id.name())));
144 private @NonNull InstanceIdentifier<NetconfNode> netconfNodePath() {
145 return nodePath().augmentation(NetconfNode.class);
148 private NetconfNodeBuilder newNetconfNodeBuilder(final boolean up, final NetconfDeviceCapabilities capabilities,
149 final SessionIdType sessionId) {
150 return new NetconfNodeBuilder()
152 .setPort(new PortNumber(Uint16.valueOf(id.address().getPort())))
153 .setConnectionStatus(up ? ConnectionStatus.Connected : ConnectionStatus.Connecting)
154 .setAvailableCapabilities(new AvailableCapabilitiesBuilder()
155 .setAvailableCapability(ImmutableList.<AvailableCapability>builder()
156 .addAll(capabilities.nonModuleBasedCapabilities())
157 .addAll(capabilities.resolvedCapabilities())
160 .setUnavailableCapabilities(new UnavailableCapabilitiesBuilder()
161 .setUnavailableCapability(capabilities.unresolvedCapabilites().entrySet().stream()
162 .map(unresolved -> new UnavailableCapabilityBuilder()
163 // FIXME: better conversion than 'toString' ?
164 .setCapability(unresolved.getKey().toString())
165 .setFailureReason(unresolved.getValue())
169 .setSessionId(sessionId);
172 private void commitTransaction(final WriteTransaction transaction, final String txType) {
173 LOG.trace("{}: Committing Transaction {}:{}", id, txType, transaction.getIdentifier());
175 transaction.commit().addCallback(new FutureCallback<CommitInfo>() {
177 public void onSuccess(final CommitInfo result) {
178 LOG.trace("{}: Transaction({}) {} SUCCESSFUL", id, txType, transaction.getIdentifier());
182 public void onFailure(final Throwable throwable) {
183 LOG.error("{}: Transaction({}) {} FAILED!", id, txType, transaction.getIdentifier(), throwable);
185 }, MoreExecutors.directExecutor());
189 public synchronized void onTransactionChainFailed(final TransactionChain chain, final Transaction transaction,
190 final Throwable cause) {
191 LOG.warn("{}: TransactionChain({}) {} FAILED!", id, chain, transaction.getIdentifier(), cause);
192 if (closeFuture != null) {
193 closeFuture.setException(cause);
197 // FIXME: move this up once we have MDSAL-838 fixed
200 txChain = dataBroker.createMergingTransactionChain(this);
201 LOG.info("{}: TransactionChain reset to {}", id, txChain);
202 // FIXME: restart last update
206 public synchronized void onTransactionChainSuccessful(final TransactionChain chain) {
207 LOG.trace("{}: TransactionChain({}) SUCCESSFUL", id, chain);
208 closeFuture.set(Empty.value());
211 private static @NonNull String extractReason(final Throwable throwable) {
212 if (throwable != null) {
213 final var message = throwable.getMessage();
214 if (message != null) {
218 return "Unknown reason";