Remove NetconfTopologySingletonService
[netconf.git] / apps / netconf-topology-singleton / src / main / java / org / opendaylight / netconf / topology / singleton / impl / NetconfNodeManager.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 akka.actor.ActorRef;
11 import akka.actor.ActorSelection;
12 import akka.actor.PoisonPill;
13 import akka.dispatch.OnComplete;
14 import akka.pattern.AskTimeoutException;
15 import akka.pattern.Patterns;
16 import akka.util.Timeout;
17 import java.util.Collection;
18 import org.checkerframework.checker.lock.qual.GuardedBy;
19 import org.checkerframework.checker.lock.qual.Holding;
20 import org.opendaylight.mdsal.binding.api.ClusteredDataTreeChangeListener;
21 import org.opendaylight.mdsal.binding.api.DataObjectModification;
22 import org.opendaylight.mdsal.binding.api.DataTreeIdentifier;
23 import org.opendaylight.mdsal.binding.api.DataTreeModification;
24 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
25 import org.opendaylight.mdsal.dom.api.DOMMountPointService;
26 import org.opendaylight.netconf.client.mdsal.api.RemoteDeviceId;
27 import org.opendaylight.netconf.topology.singleton.impl.actors.NetconfNodeActor;
28 import org.opendaylight.netconf.topology.singleton.impl.utils.NetconfTopologySetup;
29 import org.opendaylight.netconf.topology.singleton.impl.utils.NetconfTopologyUtils;
30 import org.opendaylight.netconf.topology.singleton.messages.AskForMasterMountPoint;
31 import org.opendaylight.netconf.topology.singleton.messages.RefreshSlaveActor;
32 import org.opendaylight.netconf.topology.singleton.messages.UnregisterSlaveMountPoint;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev230430.ConnectionOper.ConnectionStatus;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev221225.NetconfNode;
35 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
36 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
37 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey;
38 import org.opendaylight.yangtools.concepts.ListenerRegistration;
39 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
40 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory;
42
43 /**
44  * Managing and reacting on data tree changes in specific netconf node when master writes status to the operational
45  * data store (e.g. handling lifecycle of slave mount point).
46  */
47 class NetconfNodeManager implements ClusteredDataTreeChangeListener<Node>, AutoCloseable {
48     private static final Logger LOG = LoggerFactory.getLogger(NetconfNodeManager.class);
49
50     private final Timeout actorResponseWaitTime;
51     private final DOMMountPointService mountPointService;
52
53     private volatile NetconfTopologySetup setup;
54     private volatile ListenerRegistration<NetconfNodeManager> dataChangeListenerRegistration;
55     private volatile RemoteDeviceId id;
56
57     @GuardedBy("this")
58     private ActorRef slaveActorRef;
59
60     @GuardedBy("this")
61     private boolean closed;
62
63     @GuardedBy("this")
64     private int lastUpdateCount;
65
66     NetconfNodeManager(final NetconfTopologySetup setup, final RemoteDeviceId id, final Timeout actorResponseWaitTime,
67                        final DOMMountPointService mountPointService) {
68         this.setup = setup;
69         this.id = id;
70         this.actorResponseWaitTime = actorResponseWaitTime;
71         this.mountPointService = mountPointService;
72     }
73
74     @Override
75     public void onDataTreeChanged(final Collection<DataTreeModification<Node>> changes) {
76         for (final DataTreeModification<Node> change : changes) {
77             final DataObjectModification<Node> rootNode = change.getRootNode();
78             final NodeId nodeId = NetconfTopologyUtils.getNodeId(rootNode.getIdentifier());
79             switch (rootNode.getModificationType()) {
80                 case SUBTREE_MODIFIED:
81                     LOG.debug("{}: Operational state for node {} - subtree modified from {} to {}",
82                             id, nodeId, rootNode.getDataBefore(), rootNode.getDataAfter());
83                     handleSlaveMountPoint(rootNode);
84                     break;
85                 case WRITE:
86                     if (rootNode.getDataBefore() != null) {
87                         LOG.debug("{}: Operational state for node {} updated from {} to {}",
88                                 id, nodeId, rootNode.getDataBefore(), rootNode.getDataAfter());
89                     } else {
90                         LOG.debug("{}: Operational state for node {} created: {}",
91                                 id, nodeId, rootNode.getDataAfter());
92                     }
93                     handleSlaveMountPoint(rootNode);
94                     break;
95                 case DELETE:
96                     LOG.debug("{}: Operational state for node {} deleted.", id, nodeId);
97                     unregisterSlaveMountpoint();
98                     break;
99                 default:
100                     LOG.debug("{}: Uknown operation for node: {}", id, nodeId);
101             }
102         }
103     }
104
105     @Override
106     public synchronized void close() {
107         if (closed) {
108             return;
109         }
110
111         closed = true;
112         closeActor();
113         if (dataChangeListenerRegistration != null) {
114             dataChangeListenerRegistration.close();
115             dataChangeListenerRegistration = null;
116         }
117     }
118
119     @Holding("this")
120     private void closeActor() {
121         if (slaveActorRef != null) {
122             LOG.debug("{}: Sending poison pill to {}", id, slaveActorRef);
123             slaveActorRef.tell(PoisonPill.getInstance(), ActorRef.noSender());
124             slaveActorRef = null;
125         }
126     }
127
128     private synchronized void unregisterSlaveMountpoint() {
129         lastUpdateCount++;
130         if (slaveActorRef != null) {
131             LOG.debug("{}: Sending message to unregister slave mountpoint to {}", id, slaveActorRef);
132             slaveActorRef.tell(new UnregisterSlaveMountPoint(), ActorRef.noSender());
133         }
134     }
135
136     void registerDataTreeChangeListener(final String topologyId, final NodeKey key) {
137         final InstanceIdentifier<Node> path = NetconfTopologyUtils.createTopologyNodeListPath(key, topologyId);
138         LOG.debug("{}: Registering data tree change listener on path {}", id, path);
139         dataChangeListenerRegistration = setup.getDataBroker().registerDataTreeChangeListener(
140                 DataTreeIdentifier.create(LogicalDatastoreType.OPERATIONAL, path), this);
141     }
142
143     private synchronized void handleSlaveMountPoint(final DataObjectModification<Node> rootNode) {
144         if (closed) {
145             return;
146         }
147
148         @SuppressWarnings("ConstantConditions")
149         final NetconfNode netconfNodeAfter = rootNode.getDataAfter().augmentation(NetconfNode.class);
150
151         if (ConnectionStatus.Connected == netconfNodeAfter.getConnectionStatus()) {
152             lastUpdateCount++;
153             createOrUpdateActorRef();
154
155             final String masterAddress = netconfNodeAfter.getClusteredConnectionStatus().getNetconfMasterNode();
156             final String masterActorPath = NetconfTopologyUtils.createActorPath(masterAddress,
157                     NetconfTopologyUtils.createMasterActorName(id.name(),
158                             netconfNodeAfter.getClusteredConnectionStatus().getNetconfMasterNode()));
159
160             final AskForMasterMountPoint askForMasterMountPoint = new AskForMasterMountPoint(slaveActorRef);
161             final ActorSelection masterActor = setup.getActorSystem().actorSelection(masterActorPath);
162
163             LOG.debug("{}: Sending {} message to master {}", id, askForMasterMountPoint, masterActor);
164
165             sendAskForMasterMountPointWithRetries(askForMasterMountPoint, masterActor, 1, lastUpdateCount);
166         } else {
167             unregisterSlaveMountpoint();
168         }
169     }
170
171     @Holding("this")
172     private void sendAskForMasterMountPointWithRetries(final AskForMasterMountPoint askForMasterMountPoint,
173             final ActorSelection masterActor, final int tries, final int updateCount) {
174         Patterns.ask(masterActor, askForMasterMountPoint, actorResponseWaitTime).onComplete(new OnComplete<>() {
175             @Override
176             public void onComplete(final Throwable failure, final Object response) {
177                 synchronized (this) {
178                     // Ignore the response if we were since closed or another notification update occurred.
179                     if (closed || updateCount != lastUpdateCount) {
180                         return;
181                     }
182
183                     if (failure instanceof AskTimeoutException) {
184                         if (tries <= 5 || tries % 10 == 0) {
185                             LOG.warn("{}: Failed to send message to {} - retrying...", id, masterActor, failure);
186                         }
187                         sendAskForMasterMountPointWithRetries(askForMasterMountPoint, masterActor, tries + 1,
188                             updateCount);
189                     } else if (failure != null) {
190                         LOG.error("{}: Failed to send message {} to {}. Slave mount point could not be created",
191                             id, askForMasterMountPoint, masterActor, failure);
192                     } else {
193                         LOG.debug("{}: {} message to {} succeeded", id, askForMasterMountPoint, masterActor);
194                     }
195                 }
196             }
197         }, setup.getActorSystem().dispatcher());
198     }
199
200     @Holding("this")
201     private void createOrUpdateActorRef() {
202         if (slaveActorRef == null) {
203             slaveActorRef = setup.getActorSystem().actorOf(NetconfNodeActor.props(setup, id, actorResponseWaitTime,
204                     mountPointService));
205             LOG.debug("{}: Slave actor created with name {}", id, slaveActorRef);
206         } else {
207             slaveActorRef.tell(new RefreshSlaveActor(setup, id, actorResponseWaitTime), ActorRef.noSender());
208         }
209     }
210
211     void refreshDevice(final NetconfTopologySetup netconfTopologyDeviceSetup, final RemoteDeviceId remoteDeviceId) {
212         setup = netconfTopologyDeviceSetup;
213         id = remoteDeviceId;
214     }
215 }