df5605d01842b1f6774faec6253d456dacd53d9a
[netconf.git] / apps / netconf-topology / src / main / java / org / opendaylight / netconf / topology / spi / AbstractNetconfTopology.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.annotations.VisibleForTesting;
13 import java.util.HashMap;
14 import java.util.NoSuchElementException;
15 import java.util.concurrent.ExecutionException;
16 import org.opendaylight.mdsal.binding.api.DataBroker;
17 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
18 import org.opendaylight.mdsal.dom.api.DOMMountPointService;
19 import org.opendaylight.netconf.client.NetconfClientFactory;
20 import org.opendaylight.netconf.client.mdsal.api.BaseNetconfSchemas;
21 import org.opendaylight.netconf.client.mdsal.api.DeviceActionFactory;
22 import org.opendaylight.netconf.client.mdsal.api.RemoteDeviceHandler;
23 import org.opendaylight.netconf.client.mdsal.api.RemoteDeviceId;
24 import org.opendaylight.netconf.client.mdsal.api.SchemaResourceManager;
25 import org.opendaylight.netconf.common.NetconfTimer;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.optional.rev221225.NetconfNodeAugmentedOptional;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev231121.NetconfNode;
28 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
29 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
30 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId;
31 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
32 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyBuilder;
33 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
34 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
35 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
38
39 public abstract class AbstractNetconfTopology {
40     private static final Logger LOG = LoggerFactory.getLogger(AbstractNetconfTopology.class);
41
42     private final HashMap<NodeId, NetconfNodeHandler> activeConnectors = new HashMap<>();
43     private final NetconfClientFactory clientFactory;
44     private final DeviceActionFactory deviceActionFactory;
45     private final SchemaResourceManager schemaManager;
46     private final BaseNetconfSchemas baseSchemas;
47     private final NetconfClientConfigurationBuilderFactory builderFactory;
48     private final NetconfTimer timer;
49
50     protected final NetconfTopologySchemaAssembler schemaAssembler;
51     protected final DataBroker dataBroker;
52     protected final DOMMountPointService mountPointService;
53     protected final String topologyId;
54
55     protected AbstractNetconfTopology(final String topologyId, final NetconfClientFactory clientFactory,
56             final NetconfTimer timer, final NetconfTopologySchemaAssembler schemaAssembler,
57             final SchemaResourceManager schemaManager, final DataBroker dataBroker,
58             final DOMMountPointService mountPointService, final NetconfClientConfigurationBuilderFactory builderFactory,
59             final DeviceActionFactory deviceActionFactory, final BaseNetconfSchemas baseSchemas) {
60         this.topologyId = requireNonNull(topologyId);
61         this.clientFactory = requireNonNull(clientFactory);
62         this.timer = requireNonNull(timer);
63         this.schemaAssembler = requireNonNull(schemaAssembler);
64         this.schemaManager = requireNonNull(schemaManager);
65         this.deviceActionFactory = deviceActionFactory;
66         this.dataBroker = requireNonNull(dataBroker);
67         this.mountPointService = mountPointService;
68         this.builderFactory = requireNonNull(builderFactory);
69         this.baseSchemas = requireNonNull(baseSchemas);
70
71         // FIXME: this should be a put(), as we are initializing and will be re-populating the datastore with all the
72         //        devices. Whatever has been there before should be nuked to properly re-align lifecycle.
73         final var wtx = dataBroker.newWriteOnlyTransaction();
74         wtx.merge(LogicalDatastoreType.OPERATIONAL, InstanceIdentifier.builder(NetworkTopology.class)
75             .child(Topology.class, new TopologyKey(new TopologyId(topologyId)))
76             .build(), new TopologyBuilder().setTopologyId(new TopologyId(topologyId)).build());
77         final var future = wtx.commit();
78         try {
79             future.get();
80         } catch (InterruptedException | ExecutionException e) {
81             LOG.error("Unable to initialize topology {}", topologyId, e);
82             throw new IllegalStateException(e);
83         }
84
85         LOG.debug("Topology {} initialized", topologyId);
86     }
87
88     // Non-final for testing
89     protected void ensureNode(final Node node) {
90         lockedEnsureNode(node);
91     }
92
93     private synchronized void lockedEnsureNode(final Node node) {
94         final var nodeId = node.requireNodeId();
95         final var prev = activeConnectors.remove(nodeId);
96         if (prev != null) {
97             LOG.info("RemoteDevice{{}} was already configured, disconnecting", nodeId);
98             prev.close();
99         }
100         final var netconfNode = node.augmentation(NetconfNode.class);
101         if (netconfNode == null) {
102             LOG.warn("RemoteDevice{{}} is missing NETCONF node configuration, not connecting it", nodeId);
103             return;
104         }
105         final RemoteDeviceId deviceId;
106         try {
107             deviceId = NetconfNodeUtils.toRemoteDeviceId(nodeId, netconfNode);
108         } catch (NoSuchElementException e) {
109             LOG.warn("RemoteDevice{{}} has invalid configuration, not connecting it", nodeId, e);
110             return;
111         }
112
113         if (LOG.isInfoEnabled()) {
114             LOG.info("Connecting RemoteDevice{{}}, with config {}", nodeId, hideCredentials(node));
115         }
116
117         // Instantiate the handler ...
118         final var nodeOptional = node.augmentation(NetconfNodeAugmentedOptional.class);
119         final var deviceSalFacade = createSalFacade(deviceId, netconfNode.requireLockDatastore());
120
121         final NetconfNodeHandler nodeHandler;
122         try {
123             nodeHandler = new NetconfNodeHandler(clientFactory, timer, baseSchemas, schemaManager, schemaAssembler,
124                 builderFactory, deviceActionFactory, deviceSalFacade, deviceId, nodeId, netconfNode, nodeOptional);
125         } catch (IllegalArgumentException e) {
126             // This is a workaround for NETCONF-1114 where the encrypted password's lexical structure is not enforced
127             // in the datastore and it ends up surfacing when we decrypt the password.
128             LOG.warn("RemoteDevice{{}} failed to connect, removing from operational datastore", nodeId, e);
129             deviceSalFacade.close();
130             return;
131         }
132
133         // ... record it ...
134         activeConnectors.put(nodeId, nodeHandler);
135
136         // ... and start it
137         nodeHandler.connect();
138     }
139
140     // Non-final for testing
141     protected void deleteNode(final NodeId nodeId) {
142         lockedDeleteNode(nodeId);
143     }
144
145     private synchronized void lockedDeleteNode(final NodeId nodeId) {
146         final var nodeName = nodeId.getValue();
147         LOG.debug("Disconnecting RemoteDevice{{}}", nodeName);
148
149         final var connectorDTO = activeConnectors.remove(nodeId);
150         if (connectorDTO != null) {
151             connectorDTO.close();
152         }
153     }
154
155     protected final synchronized void deleteAllNodes() {
156         activeConnectors.values().forEach(NetconfNodeHandler::close);
157         activeConnectors.clear();
158     }
159
160     protected RemoteDeviceHandler createSalFacade(final RemoteDeviceId deviceId, final boolean lockDatastore) {
161         return new NetconfTopologyDeviceSalFacade(deviceId, mountPointService, lockDatastore, dataBroker);
162     }
163
164     /**
165      * Hiding of private credentials from node configuration (credentials data is replaced by asterisks).
166      *
167      * @param nodeConfiguration Node configuration container.
168      * @return String representation of node configuration with credentials replaced by asterisks.
169      */
170     @VisibleForTesting
171     static final String hideCredentials(final Node nodeConfiguration) {
172         final var nodeConfigurationString = nodeConfiguration.toString();
173         final var netconfNodeAugmentation = nodeConfiguration.augmentation(NetconfNode.class);
174         if (netconfNodeAugmentation != null && netconfNodeAugmentation.getCredentials() != null) {
175             final var nodeCredentials = netconfNodeAugmentation.getCredentials().toString();
176             return nodeConfigurationString.replace(nodeCredentials, "***");
177         }
178         return nodeConfigurationString;
179     }
180 }