Split out NetconfClientConfigurationBuilderFactory
[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 com.google.common.util.concurrent.ListeningExecutorService;
14 import com.google.common.util.concurrent.MoreExecutors;
15 import io.netty.util.concurrent.EventExecutor;
16 import java.util.HashMap;
17 import java.util.concurrent.ExecutionException;
18 import org.checkerframework.checker.lock.qual.Holding;
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;
42
43 public abstract class AbstractNetconfTopology {
44     private static final Logger LOG = LoggerFactory.getLogger(AbstractNetconfTopology.class);
45
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;
53
54     protected final ScheduledThreadPool keepaliveExecutor;
55     protected final ListeningExecutorService processingExecutor;
56     protected final DataBroker dataBroker;
57     protected final DOMMountPointService mountPointService;
58     protected final String topologyId;
59
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;
69         this.processingExecutor = MoreExecutors.listeningDecorator(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);
76
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();
84         try {
85             future.get();
86         } catch (InterruptedException | ExecutionException e) {
87             LOG.error("Unable to initialize topology {}", topologyId, e);
88             throw new IllegalStateException(e);
89         }
90
91         LOG.debug("Topology {} initialized", topologyId);
92     }
93
94     // Non-final for testing
95     protected void ensureNode(final Node node) {
96         lockedEnsureNode(node);
97     }
98
99     private synchronized void lockedEnsureNode(final Node node) {
100         final var nodeId = node.requireNodeId();
101         final var prev = activeConnectors.remove(nodeId);
102         if (prev != null) {
103             LOG.info("RemoteDevice{{}} was already configured, disconnecting", nodeId);
104             prev.close();
105         }
106
107         LOG.info("Connecting RemoteDevice{{}}, with config {}", nodeId, hideCredentials(node));
108         setupConnection(nodeId, node);
109     }
110
111     // Non-final for testing
112     protected void deleteNode(final NodeId nodeId) {
113         lockedDeleteNode(nodeId);
114     }
115
116     private synchronized void lockedDeleteNode(final NodeId nodeId) {
117         final var nodeName = nodeId.getValue();
118         LOG.debug("Disconnecting RemoteDevice{{}}", nodeName);
119
120         final var connectorDTO = activeConnectors.remove(nodeId);
121         if (connectorDTO != null) {
122             connectorDTO.close();
123         }
124     }
125
126     protected final synchronized void deleteAllNodes() {
127         activeConnectors.values().forEach(NetconfNodeHandler::close);
128         activeConnectors.clear();
129     }
130
131     @Holding("this")
132     protected final void setupConnection(final NodeId nodeId, final Node configNode) {
133         final var netconfNode = configNode.augmentation(NetconfNode.class);
134         final var nodeOptional = configNode.augmentation(NetconfNodeAugmentedOptional.class);
135
136         requireNonNull(netconfNode.getHost());
137         requireNonNull(netconfNode.getPort());
138
139         // Instantiate the handler ...
140         final var deviceId = NetconfNodeUtils.toRemoteDeviceId(nodeId, netconfNode);
141         final var deviceSalFacade = createSalFacade(deviceId, netconfNode.requireLockDatastore());
142         final var nodeHandler = new NetconfNodeHandler(clientDispatcher, eventExecutor, keepaliveExecutor.getExecutor(),
143             baseSchemas, schemaManager, processingExecutor, deviceActionFactory,
144             deviceSalFacade, deviceId, nodeId, netconfNode, nodeOptional,
145             builderFactory.createClientConfigurationBuilder(nodeId, netconfNode));
146
147         // ... record it ...
148         activeConnectors.put(nodeId, nodeHandler);
149
150         // ... and start it
151         nodeHandler.connect();
152     }
153
154     protected RemoteDeviceHandler createSalFacade(final RemoteDeviceId deviceId, final boolean lockDatastore) {
155         return new NetconfTopologyDeviceSalFacade(deviceId, mountPointService, lockDatastore, dataBroker);
156     }
157
158     /**
159      * Hiding of private credentials from node configuration (credentials data is replaced by asterisks).
160      *
161      * @param nodeConfiguration Node configuration container.
162      * @return String representation of node configuration with credentials replaced by asterisks.
163      */
164     @VisibleForTesting
165     static final String hideCredentials(final Node nodeConfiguration) {
166         final var netconfNodeAugmentation = nodeConfiguration.augmentation(NetconfNode.class);
167         final var nodeCredentials = netconfNodeAugmentation.getCredentials().toString();
168         final var nodeConfigurationString = nodeConfiguration.toString();
169         return nodeConfigurationString.replace(nodeCredentials, "***");
170     }
171 }