Prevent NPE for Credentials
[netconf.git] / apps / netconf-topology-singleton / src / main / java / org / opendaylight / netconf / topology / singleton / impl / MasterSalFacade.java
1 /*
2  * Copyright (c) 2016 Cisco Systems, Inc. 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.actor.ActorSystem;
14 import akka.cluster.Cluster;
15 import akka.dispatch.OnComplete;
16 import akka.pattern.Patterns;
17 import akka.util.Timeout;
18 import java.util.List;
19 import java.util.concurrent.ExecutionException;
20 import org.opendaylight.mdsal.binding.api.DataBroker;
21 import org.opendaylight.mdsal.dom.api.DOMDataBroker;
22 import org.opendaylight.mdsal.dom.api.DOMMountPointService;
23 import org.opendaylight.mdsal.dom.api.DOMNotification;
24 import org.opendaylight.netconf.client.mdsal.NetconfDeviceCapabilities;
25 import org.opendaylight.netconf.client.mdsal.NetconfDeviceSchema;
26 import org.opendaylight.netconf.client.mdsal.api.NetconfSessionPreferences;
27 import org.opendaylight.netconf.client.mdsal.api.RemoteDeviceHandler;
28 import org.opendaylight.netconf.client.mdsal.api.RemoteDeviceId;
29 import org.opendaylight.netconf.client.mdsal.api.RemoteDeviceServices;
30 import org.opendaylight.netconf.client.mdsal.spi.AbstractNetconfDataTreeService;
31 import org.opendaylight.netconf.client.mdsal.spi.NetconfDeviceDataBroker;
32 import org.opendaylight.netconf.client.mdsal.spi.NetconfDeviceMount;
33 import org.opendaylight.netconf.dom.api.NetconfDataTreeService;
34 import org.opendaylight.netconf.topology.singleton.messages.CreateInitialMasterActorData;
35 import org.opendaylight.netconf.topology.spi.NetconfDeviceTopologyAdapter;
36 import org.opendaylight.netconf.topology.spi.NetconfNodeUtils;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev240611.credentials.Credentials;
38 import org.opendaylight.yangtools.yang.data.api.schema.MountPointContext;
39 import org.opendaylight.yangtools.yang.model.api.source.SourceIdentifier;
40 import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
43 import scala.concurrent.Future;
44
45 class MasterSalFacade implements RemoteDeviceHandler, AutoCloseable {
46     private static final Logger LOG = LoggerFactory.getLogger(MasterSalFacade.class);
47
48     private final RemoteDeviceId id;
49     private final Timeout actorResponseWaitTime;
50     private final ActorRef masterActorRef;
51     private final ActorSystem actorSystem;
52     private final NetconfDeviceTopologyAdapter datastoreAdapter;
53     private final NetconfDeviceMount mount;
54     private final boolean lockDatastore;
55
56     private NetconfDeviceSchema currentSchema = null;
57     private NetconfSessionPreferences netconfSessionPreferences = null;
58     private RemoteDeviceServices deviceServices = null;
59     private DOMDataBroker deviceDataBroker = null;
60     private NetconfDataTreeService netconfService = null;
61
62     /**
63      * MasterSalFacade is responsible for handling the connection and disconnection
64      * of NETCONF devices. It manages the master mount point and coordinates with
65      * the master actor to update device data and manage session preferences.
66      *
67      * @param id                    the unique identifier for the remote device
68      * @param credentials           the credentials used to authenticate the remote device
69      * @param actorSystem           the Actor system for managing actors
70      * @param masterActorRef        the reference to the master actor responsible for this device
71      * @param actorResponseWaitTime the timeout duration to wait for responses from the actor
72      * @param mountService          the mount point service for managing mount points
73      * @param dataBroker            the data broker for accessing and modifying data in the data store
74      * @param lockDatastore         a flag indicating whether the datastore should be locked
75      */
76     MasterSalFacade(final RemoteDeviceId id,
77                     final Credentials credentials,
78                     final ActorSystem actorSystem,
79                     final ActorRef masterActorRef,
80                     final Timeout actorResponseWaitTime,
81                     final DOMMountPointService mountService,
82                     final DataBroker dataBroker,
83                     final boolean lockDatastore) {
84         this.id = id;
85         mount = new NetconfDeviceMount(id, mountService, NetconfNodeUtils.defaultTopologyMountPath(id));
86         this.actorSystem = actorSystem;
87         this.masterActorRef = masterActorRef;
88         this.actorResponseWaitTime = actorResponseWaitTime;
89         this.lockDatastore = lockDatastore;
90
91         datastoreAdapter = new NetconfDeviceTopologyAdapter(dataBroker, NetconfNodeUtils.DEFAULT_TOPOLOGY_IID, id,
92             credentials);
93     }
94
95     @Override
96     public void onDeviceConnected(final NetconfDeviceSchema deviceSchema,
97             final NetconfSessionPreferences sessionPreferences, final RemoteDeviceServices services) {
98         currentSchema = requireNonNull(deviceSchema);
99         netconfSessionPreferences = requireNonNull(sessionPreferences);
100         deviceServices = requireNonNull(services);
101         if (services.actions() != null) {
102             LOG.debug("{}: YANG 1.1 actions are supported in clustered netconf topology, DOMActionService exposed for "
103                 + "the device", id);
104         }
105
106         LOG.info("Device {} connected - registering master mount point", id);
107
108         registerMasterMountPoint();
109
110         sendInitialDataToActor().onComplete(new OnComplete<>() {
111             @Override
112             public void onComplete(final Throwable failure, final Object success) {
113                 if (failure == null) {
114                     updateDeviceData();
115                     return;
116                 }
117
118                 LOG.error("{}: CreateInitialMasterActorData to {} failed", id, masterActorRef, failure);
119             }
120         }, actorSystem.dispatcher());
121     }
122
123     @Override
124     public void onDeviceDisconnected() {
125         LOG.info("Device {} disconnected - unregistering master mount point", id);
126         datastoreAdapter.updateDeviceData(false, NetconfDeviceCapabilities.empty(), null);
127         mount.onDeviceDisconnected();
128     }
129
130     @Override
131     public void onDeviceFailed(final Throwable throwable) {
132         datastoreAdapter.setDeviceAsFailed(throwable);
133         mount.onDeviceDisconnected();
134     }
135
136     @Override
137     public void onNotification(final DOMNotification domNotification) {
138         mount.publish(domNotification);
139     }
140
141     @Override
142     public void close() {
143         final var future = datastoreAdapter.shutdown();
144         mount.close();
145
146         try {
147             future.get();
148         } catch (InterruptedException e) {
149             Thread.currentThread().interrupt();
150             throw new IllegalStateException("Interrupted while waiting for datastore adapter shutdown", e);
151         } catch (ExecutionException e) {
152             throw new IllegalStateException("Datastore adapter shutdown failed", e);
153         }
154     }
155
156     private void registerMasterMountPoint() {
157         requireNonNull(id);
158
159         final var mountContext = requireNonNull(currentSchema,
160             "Device has no remote schema context yet. Probably not fully connected.")
161             .mountContext();
162         final var preferences = requireNonNull(netconfSessionPreferences,
163             "Device has no capabilities yet. Probably not fully connected.");
164
165         deviceDataBroker = newDeviceDataBroker(mountContext, preferences);
166         netconfService = newNetconfDataTreeService(mountContext, preferences);
167
168         // We need to create ProxyDOMDataBroker so accessing mountpoint
169         // on leader node would be same as on follower node
170         final ProxyDOMDataBroker proxyDataBroker = new ProxyDOMDataBroker(id, masterActorRef, actorSystem.dispatcher(),
171             actorResponseWaitTime);
172         final NetconfDataTreeService proxyNetconfService = new ProxyNetconfDataTreeService(id, masterActorRef,
173             actorSystem.dispatcher(), actorResponseWaitTime);
174         mount.onDeviceConnected(mountContext.modelContext(), deviceServices, proxyDataBroker, proxyNetconfService);
175     }
176
177     protected DOMDataBroker newDeviceDataBroker(final MountPointContext mountContext,
178             final NetconfSessionPreferences preferences) {
179         return new NetconfDeviceDataBroker(id, mountContext, deviceServices.rpcs(), preferences, lockDatastore);
180     }
181
182     protected NetconfDataTreeService newNetconfDataTreeService(final MountPointContext mountContext,
183             final NetconfSessionPreferences preferences) {
184         return AbstractNetconfDataTreeService.of(id, mountContext, deviceServices.rpcs(), preferences, lockDatastore);
185     }
186
187     private Future<Object> sendInitialDataToActor() {
188         final List<SourceIdentifier> sourceIdentifiers = List.copyOf(SchemaContextUtil.getConstituentModuleIdentifiers(
189             currentSchema.mountContext().modelContext()));
190
191         LOG.debug("{}: Sending CreateInitialMasterActorData with sourceIdentifiers {} to {}", id, sourceIdentifiers,
192             masterActorRef);
193
194         // send initial data to master actor
195         return Patterns.ask(masterActorRef, new CreateInitialMasterActorData(deviceDataBroker, netconfService,
196             sourceIdentifiers, deviceServices), actorResponseWaitTime);
197     }
198
199     private void updateDeviceData() {
200         final String masterAddress = Cluster.get(actorSystem).selfAddress().toString();
201         LOG.debug("{}: updateDeviceData with master address {}", id, masterAddress);
202         datastoreAdapter.updateClusteredDeviceData(true, masterAddress, currentSchema.capabilities(),
203                 netconfSessionPreferences.sessionId());
204     }
205 }