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