2 * Copyright (c) 2023 PANTHEON.tech, s.r.o. 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.ActorRef;
13 import akka.cluster.Cluster;
14 import akka.dispatch.OnComplete;
15 import akka.pattern.Patterns;
16 import akka.util.Timeout;
17 import com.google.common.annotations.VisibleForTesting;
18 import com.google.common.util.concurrent.ListeningExecutorService;
19 import com.google.common.util.concurrent.MoreExecutors;
20 import io.netty.util.concurrent.EventExecutor;
21 import org.opendaylight.controller.config.threadpool.ScheduledThreadPool;
22 import org.opendaylight.controller.config.threadpool.ThreadPool;
23 import org.opendaylight.mdsal.binding.api.DataBroker;
24 import org.opendaylight.mdsal.dom.api.DOMMountPointService;
25 import org.opendaylight.netconf.client.NetconfClientDispatcher;
26 import org.opendaylight.netconf.client.mdsal.api.BaseNetconfSchemas;
27 import org.opendaylight.netconf.client.mdsal.api.DeviceActionFactory;
28 import org.opendaylight.netconf.client.mdsal.api.RemoteDeviceId;
29 import org.opendaylight.netconf.client.mdsal.api.SchemaResourceManager;
30 import org.opendaylight.netconf.topology.singleton.impl.actors.NetconfNodeActor;
31 import org.opendaylight.netconf.topology.singleton.impl.utils.NetconfTopologySetup;
32 import org.opendaylight.netconf.topology.singleton.impl.utils.NetconfTopologyUtils;
33 import org.opendaylight.netconf.topology.singleton.messages.RefreshSetupMasterActorData;
34 import org.opendaylight.netconf.topology.spi.NetconfClientConfigurationBuilderFactory;
35 import org.opendaylight.netconf.topology.spi.NetconfNodeHandler;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.optional.rev221225.NetconfNodeAugmentedOptional;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev221225.NetconfNode;
38 import org.slf4j.Logger;
39 import org.slf4j.LoggerFactory;
41 final class NetconfNodeContext implements AutoCloseable {
42 private static final Logger LOG = LoggerFactory.getLogger(NetconfNodeContext.class);
44 private final NetconfClientDispatcher clientDispatcher;
45 private final EventExecutor eventExecutor;
46 private final DeviceActionFactory deviceActionFactory;
47 private final SchemaResourceManager schemaManager;
48 private final BaseNetconfSchemas baseSchemas;
49 private final NetconfClientConfigurationBuilderFactory builderFactory;
50 private final ScheduledThreadPool keepaliveExecutor;
51 private final ListeningExecutorService processingExecutor;
52 private final DataBroker dataBroker;
53 private final DOMMountPointService mountPointService;
54 private final RemoteDeviceId remoteDeviceId;
55 private final NetconfTopologySetup setup;
56 private final Timeout actorResponseWaitTime;
58 private ActorRef masterActorRef;
59 private MasterSalFacade masterSalFacade;
60 private NetconfNodeManager netconfNodeManager;
61 private NetconfNodeHandler nodeHandler;
63 NetconfNodeContext(final NetconfClientDispatcher clientDispatcher, final EventExecutor eventExecutor,
64 final ScheduledThreadPool keepaliveExecutor, final ThreadPool processingExecutor,
65 final SchemaResourceManager schemaManager, final DataBroker dataBroker,
66 final DOMMountPointService mountPointService, final NetconfClientConfigurationBuilderFactory builderFactory,
67 final DeviceActionFactory deviceActionFactory, final BaseNetconfSchemas baseSchemas,
68 final RemoteDeviceId remoteDeviceId, final NetconfTopologySetup setup,
69 final Timeout actorResponseWaitTime) {
70 this.clientDispatcher = requireNonNull(clientDispatcher);
71 this.eventExecutor = requireNonNull(eventExecutor);
72 this.keepaliveExecutor = requireNonNull(keepaliveExecutor);
73 // FIXME: share a single instance!
74 this.processingExecutor = MoreExecutors.listeningDecorator(processingExecutor.getExecutor());
75 this.schemaManager = requireNonNull(schemaManager);
76 this.dataBroker = requireNonNull(dataBroker);
77 this.mountPointService = requireNonNull(mountPointService);
78 this.builderFactory = requireNonNull(builderFactory);
79 this.deviceActionFactory = deviceActionFactory;
80 this.baseSchemas = requireNonNull(baseSchemas);
81 this.remoteDeviceId = requireNonNull(remoteDeviceId);
82 this.setup = requireNonNull(setup);
83 this.actorResponseWaitTime = actorResponseWaitTime;
84 registerNodeManager();
87 void becomeTopologyLeader() {
88 // all nodes initially register listener
89 unregisterNodeManager();
91 // create master actor reference
92 final var masterAddress = Cluster.get(setup.getActorSystem()).selfAddress().toString();
93 masterActorRef = setup.getActorSystem().actorOf(NetconfNodeActor.props(setup, remoteDeviceId,
94 actorResponseWaitTime, mountPointService), NetconfTopologyUtils.createMasterActorName(
95 remoteDeviceId.name(), masterAddress));
100 void becomeTopologyFollower() {
101 registerNodeManager();
103 // disconnect device from this node and listen for changes from leader
106 if (masterActorRef != null) {
108 setup.getActorSystem().stop(masterActorRef);
112 void refreshSetupConnection(final NetconfTopologySetup netconfTopologyDeviceSetup, final RemoteDeviceId device) {
115 Patterns.ask(masterActorRef, new RefreshSetupMasterActorData(netconfTopologyDeviceSetup, device),
116 actorResponseWaitTime).onComplete(
119 public void onComplete(final Throwable failure, final Object success) {
120 if (failure != null) {
121 LOG.error("Failed to refresh master actor data", failure);
124 LOG.debug("Succeed to refresh Master Action data. Creating Connector...");
127 }, netconfTopologyDeviceSetup.getActorSystem().dispatcher());
130 void refreshDevice(final NetconfTopologySetup netconfTopologyDeviceSetup, final RemoteDeviceId deviceId) {
131 netconfNodeManager.refreshDevice(netconfTopologyDeviceSetup, deviceId);
134 private void registerNodeManager() {
135 netconfNodeManager = new NetconfNodeManager(setup, remoteDeviceId, actorResponseWaitTime, mountPointService);
136 netconfNodeManager.registerDataTreeChangeListener(setup.getTopologyId(), setup.getNode().key());
139 private void unregisterNodeManager() {
140 netconfNodeManager.close();
144 public void close() {
145 unregisterNodeManager();
147 // we expect that even leader node is going to be follower when data are deleted
148 // thus we do not close connection and actor here
149 // anyway we need to close topology and transaction chain on all nodes that were leaders
150 if (masterSalFacade != null) {
151 // node was at least once leader
152 masterSalFacade.close();
156 private void connectNode() {
157 final var configNode = setup.getNode();
159 final var netconfNode = configNode.augmentation(NetconfNode.class);
160 final var nodeOptional = configNode.augmentation(NetconfNodeAugmentedOptional.class);
162 requireNonNull(netconfNode.getHost());
163 requireNonNull(netconfNode.getPort());
165 // Instantiate the handler ...
166 masterSalFacade = createSalFacade(netconfNode.requireLockDatastore());
168 nodeHandler = new NetconfNodeHandler(clientDispatcher, eventExecutor, keepaliveExecutor.getExecutor(),
169 baseSchemas, schemaManager, processingExecutor, builderFactory, deviceActionFactory, masterSalFacade,
170 remoteDeviceId, configNode.getNodeId(), netconfNode, nodeOptional);
171 nodeHandler.connect();
174 private void dropNode() {
175 if (nodeHandler != null) {
182 MasterSalFacade createSalFacade(final boolean lockDatastore) {
183 return new MasterSalFacade(remoteDeviceId, setup.getActorSystem(), masterActorRef, actorResponseWaitTime,
184 mountPointService, dataBroker, lockDatastore);