Use simple Executor in NetconfDevice
[netconf.git] / apps / netconf-topology-singleton / src / main / java / org / opendaylight / netconf / topology / singleton / impl / NetconfNodeContext.java
1 /*
2  * Copyright (c) 2023 PANTHEON.tech, s.r.o. 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.singleton.impl;
9
10 import static java.util.Objects.requireNonNull;
11
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 io.netty.util.concurrent.EventExecutor;
19 import java.util.concurrent.Executor;
20 import org.opendaylight.controller.config.threadpool.ScheduledThreadPool;
21 import org.opendaylight.controller.config.threadpool.ThreadPool;
22 import org.opendaylight.mdsal.binding.api.DataBroker;
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.RemoteDeviceId;
28 import org.opendaylight.netconf.client.mdsal.api.SchemaResourceManager;
29 import org.opendaylight.netconf.topology.singleton.impl.actors.NetconfNodeActor;
30 import org.opendaylight.netconf.topology.singleton.impl.utils.NetconfTopologySetup;
31 import org.opendaylight.netconf.topology.singleton.impl.utils.NetconfTopologyUtils;
32 import org.opendaylight.netconf.topology.singleton.messages.RefreshSetupMasterActorData;
33 import org.opendaylight.netconf.topology.spi.NetconfClientConfigurationBuilderFactory;
34 import org.opendaylight.netconf.topology.spi.NetconfNodeHandler;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.optional.rev221225.NetconfNodeAugmentedOptional;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev221225.NetconfNode;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
39
40 final class NetconfNodeContext implements AutoCloseable {
41     private static final Logger LOG = LoggerFactory.getLogger(NetconfNodeContext.class);
42
43     private final NetconfClientDispatcher clientDispatcher;
44     private final EventExecutor eventExecutor;
45     private final DeviceActionFactory deviceActionFactory;
46     private final SchemaResourceManager schemaManager;
47     private final BaseNetconfSchemas baseSchemas;
48     private final NetconfClientConfigurationBuilderFactory builderFactory;
49     private final ScheduledThreadPool keepaliveExecutor;
50     private final Executor processingExecutor;
51     private final DataBroker dataBroker;
52     private final DOMMountPointService mountPointService;
53     private final RemoteDeviceId remoteDeviceId;
54     private final NetconfTopologySetup setup;
55     private final Timeout actorResponseWaitTime;
56
57     private ActorRef masterActorRef;
58     private MasterSalFacade masterSalFacade;
59     private NetconfNodeManager netconfNodeManager;
60     private NetconfNodeHandler nodeHandler;
61
62     NetconfNodeContext(final NetconfClientDispatcher clientDispatcher, final EventExecutor eventExecutor,
63             final ScheduledThreadPool keepaliveExecutor, final ThreadPool processingExecutor,
64             final SchemaResourceManager schemaManager, final DataBroker dataBroker,
65             final DOMMountPointService mountPointService, final NetconfClientConfigurationBuilderFactory builderFactory,
66             final DeviceActionFactory deviceActionFactory, final BaseNetconfSchemas baseSchemas,
67             final RemoteDeviceId remoteDeviceId, final NetconfTopologySetup setup,
68             final Timeout actorResponseWaitTime) {
69         this.clientDispatcher = requireNonNull(clientDispatcher);
70         this.eventExecutor = requireNonNull(eventExecutor);
71         this.keepaliveExecutor = requireNonNull(keepaliveExecutor);
72         this.processingExecutor = processingExecutor.getExecutor();
73         this.schemaManager = requireNonNull(schemaManager);
74         this.dataBroker = requireNonNull(dataBroker);
75         this.mountPointService = requireNonNull(mountPointService);
76         this.builderFactory = requireNonNull(builderFactory);
77         this.deviceActionFactory = deviceActionFactory;
78         this.baseSchemas = requireNonNull(baseSchemas);
79         this.remoteDeviceId = requireNonNull(remoteDeviceId);
80         this.setup = requireNonNull(setup);
81         this.actorResponseWaitTime = actorResponseWaitTime;
82         registerNodeManager();
83     }
84
85     void becomeTopologyLeader() {
86         // all nodes initially register listener
87         unregisterNodeManager();
88
89         // create master actor reference
90         final var masterAddress = Cluster.get(setup.getActorSystem()).selfAddress().toString();
91         masterActorRef = setup.getActorSystem().actorOf(NetconfNodeActor.props(setup, remoteDeviceId,
92                 actorResponseWaitTime, mountPointService), NetconfTopologyUtils.createMasterActorName(
93                 remoteDeviceId.name(), masterAddress));
94
95         connectNode();
96     }
97
98     void becomeTopologyFollower() {
99         registerNodeManager();
100
101         // disconnect device from this node and listen for changes from leader
102         dropNode();
103
104         if (masterActorRef != null) {
105             // was leader before
106             setup.getActorSystem().stop(masterActorRef);
107         }
108     }
109
110     void refreshSetupConnection(final NetconfTopologySetup netconfTopologyDeviceSetup, final RemoteDeviceId device) {
111         dropNode();
112
113         Patterns.ask(masterActorRef, new RefreshSetupMasterActorData(netconfTopologyDeviceSetup, device),
114             actorResponseWaitTime).onComplete(
115                 new OnComplete<>() {
116                     @Override
117                     public void onComplete(final Throwable failure, final Object success) {
118                         if (failure != null) {
119                             LOG.error("Failed to refresh master actor data", failure);
120                             return;
121                         }
122                         LOG.debug("Succeed to refresh Master Action data. Creating Connector...");
123                         connectNode();
124                     }
125                 }, netconfTopologyDeviceSetup.getActorSystem().dispatcher());
126     }
127
128     void refreshDevice(final NetconfTopologySetup netconfTopologyDeviceSetup, final RemoteDeviceId deviceId) {
129         netconfNodeManager.refreshDevice(netconfTopologyDeviceSetup, deviceId);
130     }
131
132     private void registerNodeManager() {
133         netconfNodeManager = new NetconfNodeManager(setup, remoteDeviceId, actorResponseWaitTime, mountPointService);
134         netconfNodeManager.registerDataTreeChangeListener(setup.getTopologyId(), setup.getNode().key());
135     }
136
137     private void unregisterNodeManager() {
138         netconfNodeManager.close();
139     }
140
141     @Override
142     public void close() {
143         unregisterNodeManager();
144
145         // we expect that even leader node is going to be follower when data are deleted
146         // thus we do not close connection and actor here
147         // anyway we need to close topology and transaction chain on all nodes that were leaders
148         if (masterSalFacade != null) {
149             // node was at least once leader
150             masterSalFacade.close();
151         }
152     }
153
154     private void connectNode() {
155         final var configNode = setup.getNode();
156
157         final var netconfNode = configNode.augmentation(NetconfNode.class);
158         final var nodeOptional = configNode.augmentation(NetconfNodeAugmentedOptional.class);
159
160         requireNonNull(netconfNode.getHost());
161         requireNonNull(netconfNode.getPort());
162
163         // Instantiate the handler ...
164         masterSalFacade = createSalFacade(netconfNode.requireLockDatastore());
165
166         nodeHandler = new NetconfNodeHandler(clientDispatcher, eventExecutor, keepaliveExecutor.getExecutor(),
167             baseSchemas, schemaManager, processingExecutor, builderFactory, deviceActionFactory, masterSalFacade,
168             remoteDeviceId, configNode.getNodeId(), netconfNode, nodeOptional);
169         nodeHandler.connect();
170     }
171
172     private void dropNode() {
173         if (nodeHandler != null) {
174             nodeHandler.close();
175             nodeHandler = null;
176         }
177     }
178
179     @VisibleForTesting
180     MasterSalFacade createSalFacade(final boolean lockDatastore) {
181         return new MasterSalFacade(remoteDeviceId, setup.getActorSystem(), masterActorRef, actorResponseWaitTime,
182             mountPointService, dataBroker, lockDatastore);
183     }
184 }