Do not use AbstractNetconfTopology in netconf-topology-singleton
[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 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;
40
41 final class NetconfNodeContext implements AutoCloseable {
42     private static final Logger LOG = LoggerFactory.getLogger(NetconfNodeContext.class);
43
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;
57
58     private ActorRef masterActorRef;
59     private MasterSalFacade masterSalFacade;
60     private NetconfNodeManager netconfNodeManager;
61     private NetconfNodeHandler nodeHandler;
62
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();
85     }
86
87     void becomeTopologyLeader() {
88         // all nodes initially register listener
89         unregisterNodeManager();
90
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));
96
97         connectNode();
98     }
99
100     void becomeTopologyFollower() {
101         registerNodeManager();
102
103         // disconnect device from this node and listen for changes from leader
104         dropNode();
105
106         if (masterActorRef != null) {
107             // was leader before
108             setup.getActorSystem().stop(masterActorRef);
109         }
110     }
111
112     void refreshSetupConnection(final NetconfTopologySetup netconfTopologyDeviceSetup, final RemoteDeviceId device) {
113         dropNode();
114
115         Patterns.ask(masterActorRef, new RefreshSetupMasterActorData(netconfTopologyDeviceSetup, device),
116             actorResponseWaitTime).onComplete(
117                 new OnComplete<>() {
118                     @Override
119                     public void onComplete(final Throwable failure, final Object success) {
120                         if (failure != null) {
121                             LOG.error("Failed to refresh master actor data", failure);
122                             return;
123                         }
124                         LOG.debug("Succeed to refresh Master Action data. Creating Connector...");
125                         connectNode();
126                     }
127                 }, netconfTopologyDeviceSetup.getActorSystem().dispatcher());
128     }
129
130     void refreshDevice(final NetconfTopologySetup netconfTopologyDeviceSetup, final RemoteDeviceId deviceId) {
131         netconfNodeManager.refreshDevice(netconfTopologyDeviceSetup, deviceId);
132     }
133
134     private void registerNodeManager() {
135         netconfNodeManager = new NetconfNodeManager(setup, remoteDeviceId, actorResponseWaitTime, mountPointService);
136         netconfNodeManager.registerDataTreeChangeListener(setup.getTopologyId(), setup.getNode().key());
137     }
138
139     private void unregisterNodeManager() {
140         netconfNodeManager.close();
141     }
142
143     @Override
144     public void close() {
145         unregisterNodeManager();
146
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();
153         }
154     }
155
156     private void connectNode() {
157         final var configNode = setup.getNode();
158
159         final var netconfNode = configNode.augmentation(NetconfNode.class);
160         final var nodeOptional = configNode.augmentation(NetconfNodeAugmentedOptional.class);
161
162         requireNonNull(netconfNode.getHost());
163         requireNonNull(netconfNode.getPort());
164
165         // Instantiate the handler ...
166         masterSalFacade = createSalFacade(netconfNode.requireLockDatastore());
167
168         nodeHandler = new NetconfNodeHandler(clientDispatcher, eventExecutor, keepaliveExecutor.getExecutor(),
169             baseSchemas, schemaManager, processingExecutor, builderFactory, deviceActionFactory, masterSalFacade,
170             remoteDeviceId, configNode.getNodeId(), netconfNode, nodeOptional);
171         nodeHandler.connect();
172     }
173
174     private void dropNode() {
175         if (nodeHandler != null) {
176             nodeHandler.close();
177             nodeHandler = null;
178         }
179     }
180
181     @VisibleForTesting
182     MasterSalFacade createSalFacade(final boolean lockDatastore) {
183         return new MasterSalFacade(remoteDeviceId, setup.getActorSystem(), masterActorRef, actorResponseWaitTime,
184             mountPointService, dataBroker, lockDatastore);
185     }
186 }