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