178d7331dab490e75c3f2b0b54db1d76481b4ea8
[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.util.concurrent.FutureCallback;
13 import com.google.common.util.concurrent.MoreExecutors;
14 import java.util.ArrayList;
15 import java.util.List;
16 import java.util.Map;
17 import java.util.concurrent.ExecutionException;
18 import java.util.stream.Collectors;
19 import org.opendaylight.mdsal.binding.api.TransactionChain;
20 import org.opendaylight.mdsal.binding.api.WriteTransaction;
21 import org.opendaylight.mdsal.common.api.CommitInfo;
22 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
23 import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCapabilities;
24 import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId;
25 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.PortNumber;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNode;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNodeBuilder;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNodeConnectionStatus.ConnectionStatus;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.status.AvailableCapabilitiesBuilder;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.status.ClusteredConnectionStatusBuilder;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.status.UnavailableCapabilities;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.status.UnavailableCapabilitiesBuilder;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.status.available.capabilities.AvailableCapability;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.status.unavailable.capabilities.UnavailableCapability.FailureReason;
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.QName;
40 import org.opendaylight.yangtools.yang.common.Uint16;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
43
44 // Non-final for testing
45 public class NetconfDeviceTopologyAdapter implements AutoCloseable {
46     private static final Logger LOG = LoggerFactory.getLogger(NetconfDeviceTopologyAdapter.class);
47
48     private final RemoteDeviceId id;
49
50     private TransactionChain txChain;
51
52     NetconfDeviceTopologyAdapter(final RemoteDeviceId id, final TransactionChain txChain) {
53         this.id = requireNonNull(id);
54         this.txChain = requireNonNull(txChain);
55
56         final WriteTransaction writeTx = txChain.newWriteOnlyTransaction();
57         LOG.trace("{}: Init device state transaction {} putting if absent operational data started.", id,
58             writeTx.getIdentifier());
59         writeTx.put(LogicalDatastoreType.OPERATIONAL, id.getTopologyBindingPath(), getNodeIdBuilder(id)
60             .addAugmentation(new NetconfNodeBuilder()
61                 .setConnectionStatus(ConnectionStatus.Connecting)
62                 .setHost(id.getHost())
63                 .setPort(new PortNumber(Uint16.valueOf(id.getAddress().getPort()))).build())
64             .build());
65         LOG.trace("{}: Init device state transaction {} putting operational data ended.", id, writeTx.getIdentifier());
66
67         commitTransaction(writeTx, "init");
68     }
69
70     public void updateDeviceData(final ConnectionStatus connectionStatus,
71             final NetconfDeviceCapabilities capabilities, final LogicalDatastoreType dsType, final NetconfNode node) {
72         NetconfNode data;
73         if (node != null && dsType == LogicalDatastoreType.CONFIGURATION) {
74             data = node;
75         } else {
76             data = buildDataForNetconfNode(connectionStatus, capabilities, dsType, node);
77         }
78
79         final WriteTransaction writeTx = txChain.newWriteOnlyTransaction();
80         LOG.trace("{}: Update device state transaction {} merging operational data started.",
81                 id, writeTx.getIdentifier());
82         writeTx.mergeParentStructurePut(dsType, id.getTopologyBindingPath().augmentation(NetconfNode.class), data);
83         LOG.trace("{}: Update device state transaction {} merging operational data ended.",
84                 id, writeTx.getIdentifier());
85
86         commitTransaction(writeTx, "update");
87     }
88
89     public void updateDeviceData(final boolean up, final NetconfDeviceCapabilities capabilities) {
90         updateDeviceData(up ? ConnectionStatus.Connected : ConnectionStatus.Connecting, capabilities,
91                 LogicalDatastoreType.OPERATIONAL, null);
92     }
93
94     public void updateClusteredDeviceData(final boolean up, final String masterAddress,
95                                           final NetconfDeviceCapabilities capabilities) {
96         final NetconfNode data = buildDataForNetconfClusteredNode(up, masterAddress, capabilities);
97
98         final WriteTransaction writeTx = txChain.newWriteOnlyTransaction();
99         LOG.trace("{}: Update device state transaction {} merging operational data started.",
100                 id, writeTx.getIdentifier());
101         writeTx.mergeParentStructurePut(LogicalDatastoreType.OPERATIONAL,
102                 id.getTopologyBindingPath().augmentation(NetconfNode.class), data);
103         LOG.trace("{}: Update device state transaction {} merging operational data ended.",
104                 id, writeTx.getIdentifier());
105
106         commitTransaction(writeTx, "update");
107     }
108
109     public void setDeviceAsFailed(final Throwable throwable) {
110         String reason = throwable != null && throwable.getMessage() != null ? throwable.getMessage() : "Unknown reason";
111
112         final NetconfNode data = new NetconfNodeBuilder()
113                 .setHost(id.getHost())
114                 .setPort(new PortNumber(Uint16.valueOf(id.getAddress().getPort())))
115                 .setConnectionStatus(ConnectionStatus.UnableToConnect).setConnectedMessage(reason).build();
116
117         final WriteTransaction writeTx = txChain.newWriteOnlyTransaction();
118         LOG.trace(
119                 "{}: Setting device state as failed {} putting operational data started.",
120                 id, writeTx.getIdentifier());
121         writeTx.mergeParentStructurePut(LogicalDatastoreType.OPERATIONAL,
122                 id.getTopologyBindingPath().augmentation(NetconfNode.class), data);
123         LOG.trace(
124                 "{}: Setting device state as failed {} putting operational data ended.",
125                 id, writeTx.getIdentifier());
126
127         commitTransaction(writeTx, "update-failed-device");
128     }
129
130     private NetconfNode buildDataForNetconfNode(final ConnectionStatus connectionStatus,
131             final NetconfDeviceCapabilities capabilities, final LogicalDatastoreType dsType, final NetconfNode node) {
132         List<AvailableCapability> capabilityList = new ArrayList<>();
133         capabilityList.addAll(capabilities.getNonModuleBasedCapabilities());
134         capabilityList.addAll(capabilities.getResolvedCapabilities());
135
136         final AvailableCapabilitiesBuilder avCapabalitiesBuilder = new AvailableCapabilitiesBuilder();
137         avCapabalitiesBuilder.setAvailableCapability(capabilityList);
138
139         return new NetconfNodeBuilder()
140             .setHost(id.getHost())
141             .setPort(new PortNumber(Uint16.valueOf(id.getAddress().getPort())))
142             .setConnectionStatus(connectionStatus)
143             .setAvailableCapabilities(avCapabalitiesBuilder.build())
144             .setUnavailableCapabilities(unavailableCapabilities(capabilities.getUnresolvedCapabilites()))
145             .build();
146     }
147
148     private NetconfNode buildDataForNetconfClusteredNode(final boolean up, final String masterNodeAddress,
149                                                          final NetconfDeviceCapabilities capabilities) {
150         List<AvailableCapability> capabilityList = new ArrayList<>();
151         capabilityList.addAll(capabilities.getNonModuleBasedCapabilities());
152         capabilityList.addAll(capabilities.getResolvedCapabilities());
153         final AvailableCapabilitiesBuilder avCapabalitiesBuilder = new AvailableCapabilitiesBuilder();
154         avCapabalitiesBuilder.setAvailableCapability(capabilityList);
155
156         final NetconfNodeBuilder netconfNodeBuilder = new NetconfNodeBuilder()
157                 .setHost(id.getHost())
158                 .setPort(new PortNumber(Uint16.valueOf(id.getAddress().getPort())))
159                 .setConnectionStatus(up ? ConnectionStatus.Connected : ConnectionStatus.Connecting)
160                 .setAvailableCapabilities(avCapabalitiesBuilder.build())
161                 .setUnavailableCapabilities(unavailableCapabilities(capabilities.getUnresolvedCapabilites()))
162                 .setClusteredConnectionStatus(
163                         new ClusteredConnectionStatusBuilder().setNetconfMasterNode(masterNodeAddress).build());
164
165         return netconfNodeBuilder.build();
166     }
167
168     private static UnavailableCapabilities unavailableCapabilities(final Map<QName, FailureReason> input) {
169         return new UnavailableCapabilitiesBuilder().setUnavailableCapability(input.entrySet().stream().map(
170             e -> new UnavailableCapabilityBuilder().setCapability(e.getKey().toString()).setFailureReason(
171                 e.getValue()).build()).collect(Collectors.toList())).build();
172     }
173
174     public void removeDeviceConfiguration() {
175         final WriteTransaction writeTx = txChain.newWriteOnlyTransaction();
176
177         LOG.trace("{}: Close device state transaction {} removing all data started.", id, writeTx.getIdentifier());
178         writeTx.delete(LogicalDatastoreType.OPERATIONAL, id.getTopologyBindingPath());
179         LOG.trace("{}: Close device state transaction {} removing all data ended.", id, writeTx.getIdentifier());
180
181         final var future = writeTx.commit();
182         try {
183             future.get();
184         } catch (InterruptedException | ExecutionException e) {
185             LOG.error("{}: Transaction(close) {} FAILED!", id, writeTx.getIdentifier(), e);
186             throw new IllegalStateException(id + "  Transaction(close) not committed correctly", e);
187         }
188     }
189
190     private void commitTransaction(final WriteTransaction transaction, final String txType) {
191         LOG.trace("{}: Committing Transaction {}:{}", id, txType, transaction.getIdentifier());
192
193         transaction.commit().addCallback(new FutureCallback<CommitInfo>() {
194             @Override
195             public void onSuccess(final CommitInfo result) {
196                 LOG.trace("{}: Transaction({}) {} SUCCESSFUL", id, txType, transaction.getIdentifier());
197             }
198
199             @Override
200             public void onFailure(final Throwable throwable) {
201                 LOG.error("{}: Transaction({}) {} FAILED!", id, txType, transaction.getIdentifier(), throwable);
202             }
203         }, MoreExecutors.directExecutor());
204     }
205
206     private static NodeBuilder getNodeIdBuilder(final RemoteDeviceId id) {
207         final NodeBuilder nodeBuilder = new NodeBuilder();
208         nodeBuilder.withKey(new NodeKey(new NodeId(id.getName())));
209         return nodeBuilder;
210     }
211
212     @Override
213     public void close() {
214         removeDeviceConfiguration();
215     }
216
217     public void setTxChain(final TransactionChain txChain) {
218         this.txChain = requireNonNull(txChain);
219     }
220 }