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.sal.connect.netconf.sal;
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.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.node.topology.rev150114.NetconfNode;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNodeBuilder;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNodeConnectionStatus.ConnectionStatus;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.status.AvailableCapabilitiesBuilder;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.status.ClusteredConnectionStatusBuilder;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.status.UnavailableCapabilitiesBuilder;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.status.available.capabilities.AvailableCapability;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.status.unavailable.capabilities.UnavailableCapabilityBuilder;
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;
44 public final class NetconfDeviceTopologyAdapter implements TransactionChainListener, AutoCloseable {
45 private static final Logger LOG = LoggerFactory.getLogger(NetconfDeviceTopologyAdapter.class);
47 private final SettableFuture<Empty> closeFuture = SettableFuture.create();
48 private final DataBroker dataBroker;
49 private final RemoteDeviceId id;
51 private TransactionChain txChain;
53 NetconfDeviceTopologyAdapter(final DataBroker dataBroker, final RemoteDeviceId id) {
54 this.dataBroker = requireNonNull(dataBroker);
55 this.id = requireNonNull(id);
56 txChain = dataBroker.createMergingTransactionChain(this);
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())
67 LOG.trace("{}: Init device state transaction {} putting operational data ended.", id, writeTx.getIdentifier());
69 commitTransaction(writeTx, "init");
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());
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());
84 commitTransaction(writeTx, "update");
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)
99 LOG.trace("{}: Update device state transaction {} merging operational data ended.",
100 id, writeTx.getIdentifier());
102 commitTransaction(writeTx, "update");
106 public void onTransactionChainFailed(final TransactionChain chain, final Transaction transaction,
107 final Throwable cause) {
108 LOG.warn("{}: TransactionChain({}) {} FAILED!", id, chain, transaction.getIdentifier(), cause);
111 txChain = dataBroker.createMergingTransactionChain(this);
112 LOG.info("{}: TransactionChain reset to {}", id, txChain);
113 // FIXME: restart last update
117 public void onTransactionChainSuccessful(final TransactionChain chain) {
118 LOG.trace("{}: TransactionChain({}) SUCCESSFUL", id, chain);
119 closeFuture.set(Empty.value());
122 public void setDeviceAsFailed(final Throwable throwable) {
123 String reason = throwable != null && throwable.getMessage() != null ? throwable.getMessage() : "Unknown reason";
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();
130 final WriteTransaction writeTx = txChain.newWriteOnlyTransaction();
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);
137 "{}: Setting device state as failed {} putting operational data ended.",
138 id, writeTx.getIdentifier());
140 commitTransaction(writeTx, "update-failed-device");
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.getNonModuleBasedCapabilities())
151 .addAll(capabilities.getResolvedCapabilities())
154 .setUnavailableCapabilities(new UnavailableCapabilitiesBuilder()
155 .setUnavailableCapability(capabilities.getUnresolvedCapabilites().entrySet().stream()
156 .map(unresolved -> new UnavailableCapabilityBuilder()
157 // FIXME: better conversion than 'toString' ?
158 .setCapability(unresolved.getKey().toString())
159 .setFailureReason(unresolved.getValue())
161 .collect(Collectors.toUnmodifiableList()))
165 private void commitTransaction(final WriteTransaction transaction, final String txType) {
166 LOG.trace("{}: Committing Transaction {}:{}", id, txType, transaction.getIdentifier());
168 transaction.commit().addCallback(new FutureCallback<CommitInfo>() {
170 public void onSuccess(final CommitInfo result) {
171 LOG.trace("{}: Transaction({}) {} SUCCESSFUL", id, txType, transaction.getIdentifier());
175 public void onFailure(final Throwable throwable) {
176 LOG.error("{}: Transaction({}) {} FAILED!", id, txType, transaction.getIdentifier(), throwable);
178 }, MoreExecutors.directExecutor());
181 private static NodeBuilder getNodeIdBuilder(final RemoteDeviceId id) {
182 return new NodeBuilder().withKey(new NodeKey(new NodeId(id.getName())));
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");
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);