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.NetconfClientDispatcher;
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 NetconfClientDispatcher clientDispatcher;
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 keepaliveExecutor;
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 NetconfClientDispatcher clientDispatcher,
61 final EventExecutor eventExecutor, final ScheduledThreadPool keepaliveExecutor,
62 final ThreadPool processingExecutor, final SchemaResourceManager schemaManager, final DataBroker dataBroker,
63 final DOMMountPointService mountPointService, final NetconfClientConfigurationBuilderFactory builderFactory,
64 final DeviceActionFactory deviceActionFactory, final BaseNetconfSchemas baseSchemas) {
65 this.topologyId = requireNonNull(topologyId);
66 this.clientDispatcher = clientDispatcher;
67 this.eventExecutor = eventExecutor;
68 this.keepaliveExecutor = keepaliveExecutor.getExecutor();
69 this.processingExecutor = processingExecutor.getExecutor();
70 this.schemaManager = requireNonNull(schemaManager);
71 this.deviceActionFactory = deviceActionFactory;
72 this.dataBroker = requireNonNull(dataBroker);
73 this.mountPointService = mountPointService;
74 this.builderFactory = requireNonNull(builderFactory);
75 this.baseSchemas = requireNonNull(baseSchemas);
77 // FIXME: this should be a put(), as we are initializing and will be re-populating the datastore with all the
78 // devices. Whatever has been there before should be nuked to properly re-align lifecycle.
79 final var wtx = dataBroker.newWriteOnlyTransaction();
80 wtx.merge(LogicalDatastoreType.OPERATIONAL, InstanceIdentifier.builder(NetworkTopology.class)
81 .child(Topology.class, new TopologyKey(new TopologyId(topologyId)))
82 .build(), new TopologyBuilder().setTopologyId(new TopologyId(topologyId)).build());
83 final var future = wtx.commit();
86 } catch (InterruptedException | ExecutionException e) {
87 LOG.error("Unable to initialize topology {}", topologyId, e);
88 throw new IllegalStateException(e);
91 LOG.debug("Topology {} initialized", topologyId);
94 // Non-final for testing
95 protected void ensureNode(final Node node) {
96 lockedEnsureNode(node);
99 private synchronized void lockedEnsureNode(final Node node) {
100 final var nodeId = node.requireNodeId();
101 final var prev = activeConnectors.remove(nodeId);
103 LOG.info("RemoteDevice{{}} was already configured, disconnecting", nodeId);
106 final var netconfNode = node.augmentation(NetconfNode.class);
107 if (netconfNode == null) {
108 LOG.warn("RemoteDevice{{}} is missing NETCONF node configuration, not connecting it", nodeId);
111 final RemoteDeviceId deviceId;
113 deviceId = NetconfNodeUtils.toRemoteDeviceId(nodeId, netconfNode);
114 } catch (NoSuchElementException e) {
115 LOG.warn("RemoteDevice{{}} has invalid configuration, not connecting it", nodeId, e);
119 LOG.info("Connecting RemoteDevice{{}}, with config {}", nodeId, hideCredentials(node));
121 // Instantiate the handler ...
122 final var nodeOptional = node.augmentation(NetconfNodeAugmentedOptional.class);
123 final var deviceSalFacade = createSalFacade(deviceId, netconfNode.requireLockDatastore());
124 final var nodeHandler = new NetconfNodeHandler(clientDispatcher, eventExecutor, keepaliveExecutor,
125 baseSchemas, schemaManager, processingExecutor, builderFactory, deviceActionFactory, deviceSalFacade,
126 deviceId, nodeId, netconfNode, nodeOptional);
129 activeConnectors.put(nodeId, nodeHandler);
132 nodeHandler.connect();
135 // Non-final for testing
136 protected void deleteNode(final NodeId nodeId) {
137 lockedDeleteNode(nodeId);
140 private synchronized void lockedDeleteNode(final NodeId nodeId) {
141 final var nodeName = nodeId.getValue();
142 LOG.debug("Disconnecting RemoteDevice{{}}", nodeName);
144 final var connectorDTO = activeConnectors.remove(nodeId);
145 if (connectorDTO != null) {
146 connectorDTO.close();
150 protected final synchronized void deleteAllNodes() {
151 activeConnectors.values().forEach(NetconfNodeHandler::close);
152 activeConnectors.clear();
155 protected RemoteDeviceHandler createSalFacade(final RemoteDeviceId deviceId, final boolean lockDatastore) {
156 return new NetconfTopologyDeviceSalFacade(deviceId, mountPointService, lockDatastore, dataBroker);
160 * Hiding of private credentials from node configuration (credentials data is replaced by asterisks).
162 * @param nodeConfiguration Node configuration container.
163 * @return String representation of node configuration with credentials replaced by asterisks.
166 static final String hideCredentials(final Node nodeConfiguration) {
167 final var netconfNodeAugmentation = nodeConfiguration.augmentation(NetconfNode.class);
168 final var nodeCredentials = netconfNodeAugmentation.getCredentials().toString();
169 final var nodeConfigurationString = nodeConfiguration.toString();
170 return nodeConfigurationString.replace(nodeCredentials, "***");