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
8 package org.opendaylight.netconf.topology.singleton.impl;
10 import static java.util.Objects.requireNonNull;
12 import akka.actor.ActorSystem;
13 import akka.util.Timeout;
14 import com.google.common.annotations.VisibleForTesting;
15 import com.google.common.util.concurrent.FutureCallback;
16 import com.google.common.util.concurrent.MoreExecutors;
17 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
18 import java.time.Duration;
19 import java.util.List;
21 import java.util.concurrent.ConcurrentHashMap;
22 import javax.annotation.PreDestroy;
23 import javax.inject.Inject;
24 import javax.inject.Singleton;
25 import org.opendaylight.aaa.encrypt.AAAEncryptionService;
26 import org.opendaylight.controller.cluster.ActorSystemProvider;
27 import org.opendaylight.mdsal.binding.api.DataBroker;
28 import org.opendaylight.mdsal.binding.api.DataTreeChangeListener;
29 import org.opendaylight.mdsal.binding.api.DataTreeIdentifier;
30 import org.opendaylight.mdsal.binding.api.DataTreeModification;
31 import org.opendaylight.mdsal.binding.api.RpcProviderService;
32 import org.opendaylight.mdsal.binding.api.WriteTransaction;
33 import org.opendaylight.mdsal.common.api.CommitInfo;
34 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
35 import org.opendaylight.mdsal.dom.api.DOMMountPointService;
36 import org.opendaylight.mdsal.singleton.api.ClusterSingletonServiceProvider;
37 import org.opendaylight.mdsal.singleton.api.ServiceGroupIdentifier;
38 import org.opendaylight.netconf.client.NetconfClientFactory;
39 import org.opendaylight.netconf.client.mdsal.api.BaseNetconfSchemaProvider;
40 import org.opendaylight.netconf.client.mdsal.api.DeviceActionFactory;
41 import org.opendaylight.netconf.client.mdsal.api.RemoteDeviceId;
42 import org.opendaylight.netconf.client.mdsal.api.SchemaResourceManager;
43 import org.opendaylight.netconf.common.NetconfTimer;
44 import org.opendaylight.netconf.topology.singleton.impl.utils.NetconfTopologySetup;
45 import org.opendaylight.netconf.topology.singleton.impl.utils.NetconfTopologyUtils;
46 import org.opendaylight.netconf.topology.spi.NetconfClientConfigurationBuilderFactory;
47 import org.opendaylight.netconf.topology.spi.NetconfNodeUtils;
48 import org.opendaylight.netconf.topology.spi.NetconfTopologyRPCProvider;
49 import org.opendaylight.netconf.topology.spi.NetconfTopologySchemaAssembler;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev231121.NetconfNode;
51 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
52 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
53 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId;
54 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
55 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyBuilder;
56 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
57 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
58 import org.opendaylight.yangtools.concepts.Registration;
59 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
60 import org.opendaylight.yangtools.yang.common.Uint16;
61 import org.osgi.service.component.annotations.Activate;
62 import org.osgi.service.component.annotations.Component;
63 import org.osgi.service.component.annotations.Deactivate;
64 import org.osgi.service.component.annotations.Reference;
65 import org.osgi.service.metatype.annotations.AttributeDefinition;
66 import org.osgi.service.metatype.annotations.Designate;
67 import org.osgi.service.metatype.annotations.ObjectClassDefinition;
68 import org.slf4j.Logger;
69 import org.slf4j.LoggerFactory;
72 @Component(service = { }, configurationPid = "org.opendaylight.netconf.topology.singleton")
73 @Designate(ocd = NetconfTopologyManager.Configuration.class)
74 // Non-final for testing
75 public class NetconfTopologyManager implements DataTreeChangeListener<Node>, AutoCloseable {
76 @ObjectClassDefinition
77 public @interface Configuration {
78 @AttributeDefinition(min = "1", description = "Name of the Network Topology instance to manage")
79 String topology$_$id() default "topology-netconf";
81 @AttributeDefinition(min = "0", max = "65535",
82 description = "Idle time in seconds after which write transaction is cancelled automatically. If 0, "
83 + "automatic cancellation is turned off.")
84 int write$_$transaction$_$idle$_$timeout() default 0;
87 private static final Logger LOG = LoggerFactory.getLogger(NetconfTopologyManager.class);
89 private final Map<InstanceIdentifier<Node>, NetconfTopologyContext> contexts = new ConcurrentHashMap<>();
90 private final Map<InstanceIdentifier<Node>, Registration> clusterRegistrations = new ConcurrentHashMap<>();
92 private final BaseNetconfSchemaProvider baseSchemaProvider;
93 private final DataBroker dataBroker;
94 private final ClusterSingletonServiceProvider clusterSingletonServiceProvider;
95 private final NetconfTimer timer;
96 private final NetconfTopologySchemaAssembler schemaAssembler;
97 private final ActorSystem actorSystem;
98 private final NetconfClientFactory clientFactory;
99 private final String topologyId;
100 private final Duration writeTxIdleTimeout;
101 private final DOMMountPointService mountPointService;
102 private final DeviceActionFactory deviceActionFactory;
103 private final NetconfClientConfigurationBuilderFactory builderFactory;
104 private final SchemaResourceManager resourceManager;
106 private Registration dataChangeListenerRegistration;
107 private NetconfTopologyRPCProvider rpcProvider;
110 public NetconfTopologyManager(@Reference final BaseNetconfSchemaProvider baseSchemaProvider,
111 @Reference final DataBroker dataBroker,
112 @Reference final ClusterSingletonServiceProvider clusterSingletonServiceProvider,
113 @Reference final NetconfTimer timer,
114 @Reference final NetconfTopologySchemaAssembler schemaAssembler,
115 @Reference final ActorSystemProvider actorSystemProvider,
116 @Reference final NetconfClientFactory clientFactory,
117 @Reference final DOMMountPointService mountPointService,
118 @Reference final AAAEncryptionService encryptionService,
119 @Reference final RpcProviderService rpcProviderService,
120 @Reference final DeviceActionFactory deviceActionFactory,
121 @Reference final SchemaResourceManager resourceManager,
122 @Reference final NetconfClientConfigurationBuilderFactory builderFactory,
123 final Configuration configuration) {
124 this(baseSchemaProvider, dataBroker, clusterSingletonServiceProvider, timer, schemaAssembler,
125 actorSystemProvider.getActorSystem(), clientFactory, mountPointService, encryptionService,
126 rpcProviderService, deviceActionFactory, resourceManager, builderFactory, configuration.topology$_$id(),
127 Uint16.valueOf(configuration.write$_$transaction$_$idle$_$timeout()));
131 public NetconfTopologyManager(final BaseNetconfSchemaProvider baseSchemaProvider, final DataBroker dataBroker,
132 final ClusterSingletonServiceProvider clusterSingletonServiceProvider, final NetconfTimer timer,
133 final NetconfTopologySchemaAssembler schemaAssembler, final ActorSystemProvider actorSystemProvider,
134 final NetconfClientFactory clientFactory, final DOMMountPointService mountPointService,
135 final AAAEncryptionService encryptionService, final RpcProviderService rpcProviderService,
136 final DeviceActionFactory deviceActionFactory, final SchemaResourceManager resourceManager,
137 final NetconfClientConfigurationBuilderFactory builderFactory) {
138 this(baseSchemaProvider, dataBroker, clusterSingletonServiceProvider, timer, schemaAssembler,
139 actorSystemProvider.getActorSystem(), clientFactory, mountPointService, encryptionService,
140 rpcProviderService, deviceActionFactory, resourceManager, builderFactory,
141 NetconfNodeUtils.DEFAULT_TOPOLOGY_NAME, Uint16.ZERO);
144 @SuppressFBWarnings(value = "MC_OVERRIDABLE_METHOD_CALL_IN_CONSTRUCTOR",
145 justification = "Non-final for mocking, but we register for DTCL and that leaks 'this'")
146 public NetconfTopologyManager(final BaseNetconfSchemaProvider baseSchemaProvider, final DataBroker dataBroker,
147 final ClusterSingletonServiceProvider clusterSingletonServiceProvider, final NetconfTimer timer,
148 final NetconfTopologySchemaAssembler schemaAssembler, final ActorSystem actorSystem,
149 final NetconfClientFactory clientFactory, final DOMMountPointService mountPointService,
150 final AAAEncryptionService encryptionService, final RpcProviderService rpcProviderService,
151 final DeviceActionFactory deviceActionFactory, final SchemaResourceManager resourceManager,
152 final NetconfClientConfigurationBuilderFactory builderFactory, final String topologyId,
153 final Uint16 writeTransactionIdleTimeout) {
154 this.baseSchemaProvider = requireNonNull(baseSchemaProvider);
155 this.dataBroker = requireNonNull(dataBroker);
156 this.clusterSingletonServiceProvider = requireNonNull(clusterSingletonServiceProvider);
157 this.timer = requireNonNull(timer);
158 this.schemaAssembler = requireNonNull(schemaAssembler);
159 this.actorSystem = requireNonNull(actorSystem);
160 this.clientFactory = requireNonNull(clientFactory);
161 this.topologyId = requireNonNull(topologyId);
162 writeTxIdleTimeout = Duration.ofSeconds(writeTransactionIdleTimeout.toJava());
163 this.mountPointService = mountPointService;
164 this.deviceActionFactory = requireNonNull(deviceActionFactory);
165 this.resourceManager = requireNonNull(resourceManager);
166 this.builderFactory = requireNonNull(builderFactory);
168 dataChangeListenerRegistration = registerDataTreeChangeListener();
169 rpcProvider = new NetconfTopologyRPCProvider(rpcProviderService, dataBroker, encryptionService, topologyId);
173 public void onDataTreeChanged(final List<DataTreeModification<Node>> changes) {
174 for (var change : changes) {
175 final var rootNode = change.getRootNode();
176 final var dataModifIdent = change.getRootPath().path();
177 final NodeId nodeId = NetconfTopologyUtils.getNodeId(rootNode.step());
178 switch (rootNode.modificationType()) {
179 case SUBTREE_MODIFIED:
180 LOG.debug("Config for node {} updated", nodeId);
181 refreshNetconfDeviceContext(dataModifIdent, rootNode.dataAfter());
184 if (contexts.containsKey(dataModifIdent)) {
185 LOG.debug("RemoteDevice{{}} was already configured, reconfiguring node...", nodeId);
186 refreshNetconfDeviceContext(dataModifIdent, rootNode.dataAfter());
188 LOG.debug("Config for node {} created", nodeId);
189 startNetconfDeviceContext(dataModifIdent, rootNode.dataAfter());
193 LOG.debug("Config for node {} deleted", nodeId);
194 stopNetconfDeviceContext(dataModifIdent);
197 LOG.warn("Unknown operation for {}.", nodeId);
202 private void refreshNetconfDeviceContext(final InstanceIdentifier<Node> instanceIdentifier, final Node node) {
203 final NetconfTopologyContext context = contexts.get(instanceIdentifier);
204 context.refresh(createSetup(instanceIdentifier, node));
207 // ClusterSingletonServiceRegistration registerClusterSingletonService method throws a Runtime exception if there
208 // are problems with registration and client has to deal with it. Only thing we can do if this error occurs is to
209 // retry registration several times and log the error.
210 // TODO change to a specific documented Exception when changed in ClusterSingletonServiceProvider
211 @SuppressWarnings("checkstyle:IllegalCatch")
212 private void startNetconfDeviceContext(final InstanceIdentifier<Node> instanceIdentifier, final Node node) {
213 final NetconfNode netconfNode = requireNonNull(node.augmentation(NetconfNode.class));
215 final Timeout actorResponseWaitTime = Timeout.create(
216 Duration.ofSeconds(netconfNode.getActorResponseWaitTime().toJava()));
218 final ServiceGroupIdentifier serviceGroupIdent = new ServiceGroupIdentifier(instanceIdentifier.toString());
220 final NetconfTopologyContext newNetconfTopologyContext = newNetconfTopologyContext(
221 createSetup(instanceIdentifier, node), serviceGroupIdent, actorResponseWaitTime, deviceActionFactory);
226 final var clusterSingletonServiceRegistration =
227 clusterSingletonServiceProvider.registerClusterSingletonService(newNetconfTopologyContext);
228 clusterRegistrations.put(instanceIdentifier, clusterSingletonServiceRegistration);
229 contexts.put(instanceIdentifier, newNetconfTopologyContext);
231 } catch (final RuntimeException e) {
232 LOG.warn("Unable to register cluster singleton service {}, trying again", newNetconfTopologyContext, e);
235 LOG.error("Unable to register cluster singleton service {} - done trying, closing topology context",
236 newNetconfTopologyContext, e);
237 close(newNetconfTopologyContext);
244 private void stopNetconfDeviceContext(final InstanceIdentifier<Node> instanceIdentifier) {
245 final NetconfTopologyContext netconfTopologyContext = contexts.remove(instanceIdentifier);
246 if (netconfTopologyContext != null) {
247 close(clusterRegistrations.remove(instanceIdentifier));
248 close(netconfTopologyContext);
253 protected NetconfTopologyContext newNetconfTopologyContext(final NetconfTopologySetup setup,
254 final ServiceGroupIdentifier serviceGroupIdent, final Timeout actorResponseWaitTime,
255 final DeviceActionFactory deviceActionFact) {
256 return new NetconfTopologyContext(resourceManager, mountPointService, builderFactory, deviceActionFactory,
257 actorResponseWaitTime, serviceGroupIdent, setup);
263 public void close() {
264 if (rpcProvider != null) {
268 if (dataChangeListenerRegistration != null) {
269 dataChangeListenerRegistration.close();
270 dataChangeListenerRegistration = null;
273 contexts.values().forEach(NetconfTopologyManager::close);
274 clusterRegistrations.values().forEach(NetconfTopologyManager::close);
277 clusterRegistrations.clear();
280 @SuppressWarnings("checkstyle:IllegalCatch")
281 private static void close(final AutoCloseable closeable) {
284 } catch (Exception e) {
285 LOG.warn("Error closing {}", closeable, e);
289 private Registration registerDataTreeChangeListener() {
290 final WriteTransaction wtx = dataBroker.newWriteOnlyTransaction();
291 // FIXME: how does this play out with lifecycle? In a cluster, someone needs to ensure this call happens, but
292 // also we need to to make sure config -> oper is properly synchronized. Non-clustered case relies on
293 // oper being transient and perhaps on a put() instead, how do we handle that in the clustered case?
294 wtx.merge(LogicalDatastoreType.OPERATIONAL, InstanceIdentifier.builder(NetworkTopology.class)
295 .child(Topology.class, new TopologyKey(new TopologyId(topologyId)))
296 .build(), new TopologyBuilder().setTopologyId(new TopologyId(topologyId)).build());
297 wtx.commit().addCallback(new FutureCallback<CommitInfo>() {
299 public void onSuccess(final CommitInfo result) {
300 LOG.debug("topology initialization successful");
304 public void onFailure(final Throwable throwable) {
305 LOG.error("Unable to initialize netconf-topology", throwable);
307 }, MoreExecutors.directExecutor());
309 LOG.debug("Registering datastore listener");
310 return dataBroker.registerTreeChangeListener(DataTreeIdentifier.of(LogicalDatastoreType.CONFIGURATION,
311 NetconfTopologyUtils.createTopologyListPath(topologyId).child(Node.class)), this);
314 private NetconfTopologySetup createSetup(final InstanceIdentifier<Node> instanceIdentifier, final Node node) {
315 final NetconfNode netconfNode = node.augmentation(NetconfNode.class);
316 final RemoteDeviceId deviceId = NetconfNodeUtils.toRemoteDeviceId(node.getNodeId(), netconfNode);
318 return NetconfTopologySetup.builder()
319 .setClusterSingletonServiceProvider(clusterSingletonServiceProvider)
320 .setBaseSchemaProvider(baseSchemaProvider)
321 .setDataBroker(dataBroker)
322 .setInstanceIdentifier(instanceIdentifier)
324 .setActorSystem(actorSystem)
326 .setSchemaAssembler(schemaAssembler)
327 .setTopologyId(topologyId)
328 .setNetconfClientFactory(clientFactory)
329 .setSchemaResourceDTO(resourceManager.getSchemaResources(netconfNode.getSchemaCacheDirectory(), deviceId))
330 .setIdleTimeout(writeTxIdleTimeout)