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.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.BaseNetconfSchemaProvider;
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;
39 public abstract class AbstractNetconfTopology {
40 private static final Logger LOG = LoggerFactory.getLogger(AbstractNetconfTopology.class);
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 BaseNetconfSchemaProvider baseSchemaProvider;
47 private final NetconfClientConfigurationBuilderFactory builderFactory;
48 private final NetconfTimer timer;
50 protected final NetconfTopologySchemaAssembler schemaAssembler;
51 protected final DataBroker dataBroker;
52 protected final DOMMountPointService mountPointService;
53 protected final String topologyId;
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 BaseNetconfSchemaProvider baseSchemaProvider) {
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.baseSchemaProvider = requireNonNull(baseSchemaProvider);
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();
80 } catch (InterruptedException | ExecutionException e) {
81 LOG.error("Unable to initialize topology {}", topologyId, e);
82 throw new IllegalStateException(e);
85 LOG.debug("Topology {} initialized", topologyId);
88 // Non-final for testing
89 protected void ensureNode(final Node node) {
90 lockedEnsureNode(node);
93 private synchronized void lockedEnsureNode(final Node node) {
94 final var nodeId = node.requireNodeId();
95 final var prev = activeConnectors.remove(nodeId);
97 LOG.info("RemoteDevice{{}} was already configured, disconnecting", nodeId);
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);
105 final RemoteDeviceId deviceId;
107 deviceId = NetconfNodeUtils.toRemoteDeviceId(nodeId, netconfNode);
108 } catch (NoSuchElementException e) {
109 LOG.warn("RemoteDevice{{}} has invalid configuration, not connecting it", nodeId, e);
113 if (LOG.isInfoEnabled()) {
114 LOG.info("Connecting RemoteDevice{{}}, with config {}", nodeId, hideCredentials(node));
117 // Instantiate the handler ...
118 final var nodeOptional = node.augmentation(NetconfNodeAugmentedOptional.class);
119 final var deviceSalFacade = createSalFacade(deviceId, netconfNode.requireLockDatastore());
121 final NetconfNodeHandler nodeHandler;
123 nodeHandler = new NetconfNodeHandler(clientFactory, timer, baseSchemaProvider, schemaManager,
124 schemaAssembler, builderFactory, deviceActionFactory, deviceSalFacade, deviceId, nodeId, netconfNode,
126 } catch (IllegalArgumentException e) {
127 // This is a workaround for NETCONF-1114 where the encrypted password's lexical structure is not enforced
128 // in the datastore and it ends up surfacing when we decrypt the password.
129 LOG.warn("RemoteDevice{{}} failed to connect, removing from operational datastore", nodeId, e);
130 deviceSalFacade.close();
135 activeConnectors.put(nodeId, nodeHandler);
138 nodeHandler.connect();
141 // Non-final for testing
142 protected void deleteNode(final NodeId nodeId) {
143 lockedDeleteNode(nodeId);
146 private synchronized void lockedDeleteNode(final NodeId nodeId) {
147 final var nodeName = nodeId.getValue();
148 LOG.debug("Disconnecting RemoteDevice{{}}", nodeName);
150 final var connectorDTO = activeConnectors.remove(nodeId);
151 if (connectorDTO != null) {
152 connectorDTO.close();
156 protected final synchronized void deleteAllNodes() {
157 activeConnectors.values().forEach(NetconfNodeHandler::close);
158 activeConnectors.clear();
161 protected RemoteDeviceHandler createSalFacade(final RemoteDeviceId deviceId, final boolean lockDatastore) {
162 return new NetconfTopologyDeviceSalFacade(deviceId, mountPointService, lockDatastore, dataBroker);
166 * Hiding of private credentials from node configuration (credentials data is replaced by asterisks).
168 * @param nodeConfiguration Node configuration container.
169 * @return String representation of node configuration with credentials replaced by asterisks.
172 static final String hideCredentials(final Node nodeConfiguration) {
173 final var nodeConfigurationString = nodeConfiguration.toString();
174 final var netconfNodeAugmentation = nodeConfiguration.augmentation(NetconfNode.class);
175 if (netconfNodeAugmentation != null && netconfNodeAugmentation.getCredentials() != null) {
176 final var nodeCredentials = netconfNodeAugmentation.getCredentials().toString();
177 return nodeConfigurationString.replace(nodeCredentials, "***");
179 return nodeConfigurationString;