2 * Copyright (c) 2016 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
9 package org.opendaylight.netconf.topology.singleton.impl;
11 import akka.actor.ActorSystem;
12 import akka.util.Timeout;
13 import com.google.common.base.Preconditions;
14 import com.google.common.util.concurrent.FutureCallback;
15 import com.google.common.util.concurrent.Futures;
16 import io.netty.util.concurrent.EventExecutor;
17 import java.util.Collection;
18 import java.util.HashMap;
20 import java.util.concurrent.TimeUnit;
21 import javax.annotation.Nonnull;
22 import org.opendaylight.controller.cluster.ActorSystemProvider;
23 import org.opendaylight.controller.config.threadpool.ScheduledThreadPool;
24 import org.opendaylight.controller.config.threadpool.ThreadPool;
25 import org.opendaylight.controller.md.sal.binding.api.ClusteredDataTreeChangeListener;
26 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
27 import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
28 import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
29 import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
30 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
31 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
32 import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
33 import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
34 import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonServiceProvider;
35 import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonServiceRegistration;
36 import org.opendaylight.mdsal.singleton.common.api.ServiceGroupIdentifier;
37 import org.opendaylight.netconf.client.NetconfClientDispatcher;
38 import org.opendaylight.netconf.topology.singleton.api.NetconfTopologySingletonService;
39 import org.opendaylight.netconf.topology.singleton.impl.utils.NetconfTopologySetup;
40 import org.opendaylight.netconf.topology.singleton.impl.utils.NetconfTopologySetup.NetconfTopologySetupBuilder;
41 import org.opendaylight.netconf.topology.singleton.impl.utils.NetconfTopologyUtils;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNode;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.topology.singleton.config.rev170419.Config;
44 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
45 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopologyBuilder;
46 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
47 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId;
48 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
49 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyBuilder;
50 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
51 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
52 import org.opendaylight.yangtools.concepts.ListenerRegistration;
53 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
54 import org.slf4j.Logger;
55 import org.slf4j.LoggerFactory;
56 import scala.concurrent.duration.Duration;
58 public class NetconfTopologyManager
59 implements ClusteredDataTreeChangeListener<Node>, NetconfTopologySingletonService, AutoCloseable {
61 private static final Logger LOG = LoggerFactory.getLogger(NetconfTopologyManager.class);
63 private final Map<InstanceIdentifier<Node>, NetconfTopologyContext> contexts = new HashMap<>();
64 private final Map<InstanceIdentifier<Node>, ClusterSingletonServiceRegistration>
65 clusterRegistrations = new HashMap<>();
67 private final DataBroker dataBroker;
68 private final RpcProviderRegistry rpcProviderRegistry;
69 private final ClusterSingletonServiceProvider clusterSingletonServiceProvider;
70 private final ScheduledThreadPool keepaliveExecutor;
71 private final ThreadPool processingExecutor;
72 private final ActorSystem actorSystem;
73 private final EventExecutor eventExecutor;
74 private final NetconfClientDispatcher clientDispatcher;
75 private final String topologyId;
76 private final Duration writeTxIdleTimeout;
77 private final DOMMountPointService mountPointService;
79 private ListenerRegistration<NetconfTopologyManager> dataChangeListenerRegistration;
80 private String privateKeyPath;
81 private String privateKeyPassphrase;
83 public NetconfTopologyManager(final DataBroker dataBroker, final RpcProviderRegistry rpcProviderRegistry,
84 final ClusterSingletonServiceProvider clusterSingletonServiceProvider,
85 final ScheduledThreadPool keepaliveExecutor, final ThreadPool processingExecutor,
86 final ActorSystemProvider actorSystemProvider, final EventExecutor eventExecutor,
87 final NetconfClientDispatcher clientDispatcher, final String topologyId,
88 final Config config, final DOMMountPointService mountPointService) {
89 this.dataBroker = Preconditions.checkNotNull(dataBroker);
90 this.rpcProviderRegistry = Preconditions.checkNotNull(rpcProviderRegistry);
91 this.clusterSingletonServiceProvider = Preconditions.checkNotNull(clusterSingletonServiceProvider);
92 this.keepaliveExecutor = Preconditions.checkNotNull(keepaliveExecutor);
93 this.processingExecutor = Preconditions.checkNotNull(processingExecutor);
94 this.actorSystem = Preconditions.checkNotNull(actorSystemProvider).getActorSystem();
95 this.eventExecutor = Preconditions.checkNotNull(eventExecutor);
96 this.clientDispatcher = Preconditions.checkNotNull(clientDispatcher);
97 this.topologyId = Preconditions.checkNotNull(topologyId);
98 this.writeTxIdleTimeout = Duration.apply(config.getWriteTransactionIdleTimeout(), TimeUnit.SECONDS);
99 this.mountPointService = mountPointService;
102 // Blueprint init method
104 dataChangeListenerRegistration = registerDataTreeChangeListener(topologyId);
108 public void onDataTreeChanged(@Nonnull final Collection<DataTreeModification<Node>> changes) {
109 for (final DataTreeModification<Node> change : changes) {
110 final DataObjectModification<Node> rootNode = change.getRootNode();
111 final InstanceIdentifier<Node> dataModifIdent = change.getRootPath().getRootIdentifier();
112 final NodeId nodeId = NetconfTopologyUtils.getNodeId(rootNode.getIdentifier());
113 switch (rootNode.getModificationType()) {
114 case SUBTREE_MODIFIED:
115 LOG.debug("Config for node {} updated", nodeId);
116 refreshNetconfDeviceContext(dataModifIdent, rootNode.getDataAfter());
119 if (contexts.containsKey(dataModifIdent)) {
120 LOG.debug("RemoteDevice{{}} was already configured, reconfiguring node...", nodeId);
121 refreshNetconfDeviceContext(dataModifIdent, rootNode.getDataAfter());
123 LOG.debug("Config for node {} created", nodeId);
124 startNetconfDeviceContext(dataModifIdent, rootNode.getDataAfter());
128 LOG.debug("Config for node {} deleted", nodeId);
129 stopNetconfDeviceContext(dataModifIdent);
132 LOG.warn("Unknown operation for {}.", nodeId);
137 private void refreshNetconfDeviceContext(final InstanceIdentifier<Node> instanceIdentifier, final Node node) {
138 final NetconfTopologyContext context = contexts.get(instanceIdentifier);
139 context.refresh(createSetup(instanceIdentifier, node));
142 // ClusterSingletonServiceRegistration registerClusterSingletonService method throws a Runtime exception if there
143 // are problems with registration and client has to deal with it. Only thing we can do if this error occurs is to
144 // retry registration several times and log the error.
145 // TODO change to a specific documented Exception when changed in ClusterSingletonServiceProvider
146 @SuppressWarnings("checkstyle:IllegalCatch")
147 private void startNetconfDeviceContext(final InstanceIdentifier<Node> instanceIdentifier, final Node node) {
148 final NetconfNode netconfNode = node.getAugmentation(NetconfNode.class);
149 Preconditions.checkNotNull(netconfNode);
150 Preconditions.checkNotNull(netconfNode.getHost());
151 Preconditions.checkNotNull(netconfNode.getHost().getIpAddress());
153 final Timeout actorResponseWaitTime = new Timeout(Duration.create(netconfNode.getActorResponseWaitTime(),
156 final ServiceGroupIdentifier serviceGroupIdent =
157 ServiceGroupIdentifier.create(instanceIdentifier.toString());
159 final NetconfTopologyContext newNetconfTopologyContext =
160 new NetconfTopologyContext(createSetup(instanceIdentifier, node), serviceGroupIdent,
161 actorResponseWaitTime, mountPointService);
166 final ClusterSingletonServiceRegistration clusterSingletonServiceRegistration =
167 clusterSingletonServiceProvider.registerClusterSingletonService(newNetconfTopologyContext);
168 clusterRegistrations.put(instanceIdentifier, clusterSingletonServiceRegistration);
169 contexts.put(instanceIdentifier, newNetconfTopologyContext);
171 } catch (final RuntimeException e) {
172 LOG.warn("Unable to register cluster singleton service {}, trying again", newNetconfTopologyContext, e);
175 LOG.error("Unable to register cluster singleton service {} - done trying, closing topology context",
176 newNetconfTopologyContext, e);
185 @SuppressWarnings("checkstyle:IllegalCatch")
186 private void stopNetconfDeviceContext(final InstanceIdentifier<Node> instanceIdentifier) {
187 if (contexts.containsKey(instanceIdentifier)) {
189 clusterRegistrations.get(instanceIdentifier).close();
190 contexts.get(instanceIdentifier).closeFinal();
191 } catch (final Exception e) {
192 LOG.warn("Error at closing topology context. InstanceIdentifier: " + instanceIdentifier);
194 contexts.remove(instanceIdentifier);
195 clusterRegistrations.remove(instanceIdentifier);
199 @SuppressWarnings("checkstyle:IllegalCatch")
201 public void close() {
202 if (dataChangeListenerRegistration != null) {
203 dataChangeListenerRegistration.close();
204 dataChangeListenerRegistration = null;
206 contexts.forEach((instanceIdentifier, netconfTopologyContext) -> {
208 netconfTopologyContext.closeFinal();
209 } catch (final Exception e) {
210 LOG.error("Error at closing topology context. InstanceIdentifier: " + instanceIdentifier, e);
213 clusterRegistrations.forEach((instanceIdentifier, clusterSingletonServiceRegistration) -> {
215 clusterSingletonServiceRegistration.close();
216 } catch (final Exception e) {
217 LOG.error("Error at unregistering from cluster. InstanceIdentifier: " + instanceIdentifier, e);
221 clusterRegistrations.clear();
225 * Sets the private key path from location specified in configuration file using blueprint.
227 public void setPrivateKeyPath(String privateKeyPath) {
228 this.privateKeyPath = privateKeyPath;
232 * Sets the private key passphrase from location specified in configuration file using blueprint.
234 public void setPrivateKeyPassphrase(String privateKeyPassphrase) {
235 this.privateKeyPassphrase = privateKeyPassphrase;
238 private ListenerRegistration<NetconfTopologyManager> registerDataTreeChangeListener(final String topologyId) {
239 final WriteTransaction wtx = dataBroker.newWriteOnlyTransaction();
240 initTopology(wtx, LogicalDatastoreType.CONFIGURATION, topologyId);
241 initTopology(wtx, LogicalDatastoreType.OPERATIONAL, topologyId);
242 Futures.addCallback(wtx.submit(), new FutureCallback<Void>() {
244 public void onSuccess(final Void result) {
245 LOG.debug("topology initialization successful");
249 public void onFailure(@Nonnull final Throwable throwable) {
250 LOG.error("Unable to initialize netconf-topology, {}", throwable);
254 LOG.debug("Registering datastore listener");
255 return dataBroker.registerDataTreeChangeListener(
256 new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION,
257 NetconfTopologyUtils.createTopologyListPath(topologyId).child(Node.class)), this);
260 private void initTopology(final WriteTransaction wtx, final LogicalDatastoreType datastoreType,
261 final String topologyId) {
262 final NetworkTopology networkTopology = new NetworkTopologyBuilder().build();
263 final InstanceIdentifier<NetworkTopology> networkTopologyId =
264 InstanceIdentifier.builder(NetworkTopology.class).build();
265 wtx.merge(datastoreType, networkTopologyId, networkTopology);
266 final Topology topology = new TopologyBuilder().setTopologyId(new TopologyId(topologyId)).build();
267 wtx.merge(datastoreType, networkTopologyId.child(Topology.class,
268 new TopologyKey(new TopologyId(topologyId))), topology);
271 private NetconfTopologySetup createSetup(final InstanceIdentifier<Node> instanceIdentifier, final Node node) {
272 final NetconfTopologySetupBuilder builder = NetconfTopologySetupBuilder.create()
273 .setClusterSingletonServiceProvider(clusterSingletonServiceProvider)
274 .setDataBroker(dataBroker)
275 .setInstanceIdentifier(instanceIdentifier)
276 .setRpcProviderRegistry(rpcProviderRegistry)
278 .setActorSystem(actorSystem)
279 .setEventExecutor(eventExecutor)
280 .setKeepaliveExecutor(keepaliveExecutor)
281 .setProcessingExecutor(processingExecutor)
282 .setTopologyId(topologyId)
283 .setNetconfClientDispatcher(clientDispatcher)
284 .setSchemaResourceDTO(NetconfTopologyUtils.setupSchemaCacheDTO(node))
285 .setIdleTimeout(writeTxIdleTimeout)
286 .setPrivateKeyPath(privateKeyPath)
287 .setPrivateKeyPassphrase(privateKeyPassphrase);
289 return builder.build();