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.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;
45 class MasterSalFacade implements RemoteDeviceHandler, AutoCloseable {
46 private static final Logger LOG = LoggerFactory.getLogger(MasterSalFacade.class);
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;
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;
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.
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
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) {
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;
91 datastoreAdapter = new NetconfDeviceTopologyAdapter(dataBroker, NetconfNodeUtils.DEFAULT_TOPOLOGY_IID, id,
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 "
106 LOG.info("Device {} connected - registering master mount point", id);
108 registerMasterMountPoint();
110 sendInitialDataToActor().onComplete(new OnComplete<>() {
112 public void onComplete(final Throwable failure, final Object success) {
113 if (failure == null) {
118 LOG.error("{}: CreateInitialMasterActorData to {} failed", id, masterActorRef, failure);
120 }, actorSystem.dispatcher());
124 public void onDeviceDisconnected() {
125 LOG.info("Device {} disconnected - unregistering master mount point", id);
126 datastoreAdapter.updateDeviceData(false, NetconfDeviceCapabilities.empty(), null);
127 mount.onDeviceDisconnected();
131 public void onDeviceFailed(final Throwable throwable) {
132 datastoreAdapter.setDeviceAsFailed(throwable);
133 mount.onDeviceDisconnected();
137 public void onNotification(final DOMNotification domNotification) {
138 mount.publish(domNotification);
142 public void close() {
143 final var future = datastoreAdapter.shutdown();
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);
156 private void registerMasterMountPoint() {
159 final var mountContext = requireNonNull(currentSchema,
160 "Device has no remote schema context yet. Probably not fully connected.")
162 final var preferences = requireNonNull(netconfSessionPreferences,
163 "Device has no capabilities yet. Probably not fully connected.");
165 deviceDataBroker = newDeviceDataBroker(mountContext, preferences);
166 netconfService = newNetconfDataTreeService(mountContext, preferences);
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);
177 protected DOMDataBroker newDeviceDataBroker(final MountPointContext mountContext,
178 final NetconfSessionPreferences preferences) {
179 return new NetconfDeviceDataBroker(id, mountContext, deviceServices.rpcs(), preferences, lockDatastore);
182 protected NetconfDataTreeService newNetconfDataTreeService(final MountPointContext mountContext,
183 final NetconfSessionPreferences preferences) {
184 return AbstractNetconfDataTreeService.of(id, mountContext, deviceServices.rpcs(), preferences, lockDatastore);
187 private Future<Object> sendInitialDataToActor() {
188 final List<SourceIdentifier> sourceIdentifiers = List.copyOf(SchemaContextUtil.getConstituentModuleIdentifiers(
189 currentSchema.mountContext().modelContext()));
191 LOG.debug("{}: Sending CreateInitialMasterActorData with sourceIdentifiers {} to {}", id, sourceIdentifiers,
194 // send initial data to master actor
195 return Patterns.ask(masterActorRef, new CreateInitialMasterActorData(deviceDataBroker, netconfService,
196 sourceIdentifiers, deviceServices), actorResponseWaitTime);
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());