Merge "update helpserver"
[ccsdk/features.git] / sdnr / wt / netconfnode-state-service / provider / src / main / java / org / onap / ccsdk / features / sdnr / wt / netconfnodestateservice / impl / NetconfNodeStateServiceImpl.java
1 /*******************************************************************************
2  * ============LICENSE_START========================================================================
3  * ONAP : ccsdk feature sdnr wt
4  * =================================================================================================
5  * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved.
6  * =================================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
8  * in compliance with the License. You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software distributed under the License
13  * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
14  * or implied. See the License for the specific language governing permissions and limitations under
15  * the License.
16  * ============LICENSE_END==========================================================================
17  ******************************************************************************/
18 package org.onap.ccsdk.features.sdnr.wt.netconfnodestateservice.impl;
19
20 import java.util.Collection;
21 import java.util.List;
22 import java.util.Optional;
23 import java.util.concurrent.CopyOnWriteArrayList;
24
25 import javax.annotation.Nullable;
26
27 import org.eclipse.jdt.annotation.NonNull;
28 import org.onap.ccsdk.features.sdnr.wt.dataprovider.model.IEntityDataProvider;
29 import org.onap.ccsdk.features.sdnr.wt.dataprovider.model.StatusChangedHandler.StatusKey;
30 import org.onap.ccsdk.features.sdnr.wt.netconfnodestateservice.NetconfAccessor;
31 import org.onap.ccsdk.features.sdnr.wt.netconfnodestateservice.NetconfNodeConnectListener;
32 import org.onap.ccsdk.features.sdnr.wt.netconfnodestateservice.NetconfNodeStateListener;
33 import org.onap.ccsdk.features.sdnr.wt.netconfnodestateservice.NetconfNodeStateService;
34 import org.onap.ccsdk.features.sdnr.wt.netconfnodestateservice.TransactionUtils;
35 import org.onap.ccsdk.features.sdnr.wt.netconfnodestateservice.VesNotificationListener;
36 import org.onap.ccsdk.features.sdnr.wt.netconfnodestateservice.impl.conf.odlAkka.AkkaConfig;
37 import org.onap.ccsdk.features.sdnr.wt.netconfnodestateservice.impl.conf.odlAkka.ClusterConfig;
38 import org.onap.ccsdk.features.sdnr.wt.netconfnodestateservice.impl.conf.odlGeo.GeoConfig;
39 import org.onap.ccsdk.features.sdnr.wt.netconfnodestateservice.impl.rpc.NetconfnodeStateServiceRpcApiImpl;
40 import org.onap.ccsdk.features.sdnr.wt.netconfnodestateservice.impl.rpc.RpcApigetStateCallback;
41 import org.opendaylight.mdsal.binding.api.ClusteredDataTreeChangeListener;
42 import org.opendaylight.mdsal.binding.api.DataBroker;
43 import org.opendaylight.mdsal.binding.api.DataObjectModification;
44 import org.opendaylight.mdsal.binding.api.DataObjectModification.ModificationType;
45 import org.opendaylight.mdsal.binding.api.DataTreeChangeListener;
46 import org.opendaylight.mdsal.binding.api.DataTreeIdentifier;
47 import org.opendaylight.mdsal.binding.api.DataTreeModification;
48 import org.opendaylight.mdsal.binding.api.MountPoint;
49 import org.opendaylight.mdsal.binding.api.MountPointService;
50 import org.opendaylight.mdsal.binding.api.NotificationPublishService;
51 import org.opendaylight.mdsal.binding.api.RpcProviderService;
52 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
53 import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonServiceProvider;
54 import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonServiceRegistration;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNode;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNodeConnectionStatus.ConnectionStatus;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.status.ClusteredConnectionStatus;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.network.topology.topology.topology.types.TopologyNetconf;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netconfnode.state.rev191011.GetStatusInput;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netconfnode.state.rev191011.GetStatusOutputBuilder;
61 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
62 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
63 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId;
64 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
65 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
66 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
67 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey;
68 import org.opendaylight.yangtools.concepts.ListenerRegistration;
69 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
70 import org.slf4j.Logger;
71 import org.slf4j.LoggerFactory;
72
73 public class NetconfNodeStateServiceImpl implements NetconfNodeStateService, RpcApigetStateCallback, AutoCloseable {
74
75     private static final Logger LOG = LoggerFactory.getLogger(NetconfNodeStateServiceImpl.class);
76     private static final String APPLICATION_NAME = "NetconfNodeStateService";
77     @SuppressWarnings("unused")
78     private static final String CONFIGURATIONFILE = "etc/netconfnode-status-service.properties";
79
80
81     @SuppressWarnings("null")
82     private static final @NonNull InstanceIdentifier<Topology> NETCONF_TOPO_IID =
83             InstanceIdentifier.create(NetworkTopology.class).child(Topology.class,
84                     new TopologyKey(new TopologyId(TopologyNetconf.QNAME.getLocalName())));
85
86     @SuppressWarnings("null")
87     private static final @NonNull InstanceIdentifier<Node> NETCONF_NODE_TOPO_IID =
88             InstanceIdentifier.create(NetworkTopology.class)
89                     .child(Topology.class, new TopologyKey(new TopologyId(TopologyNetconf.QNAME.getLocalName())))
90                     .child(Node.class);
91
92     private static final DataTreeIdentifier<Node> NETCONF_NODE_TOPO_TREE_ID =
93             DataTreeIdentifier.create(LogicalDatastoreType.OPERATIONAL, NETCONF_NODE_TOPO_IID);
94
95     // Name of ODL controller NETCONF instance
96     private static final NodeId CONTROLLER = new NodeId("controller-config");
97     private static final TransactionUtils TRANSACTIONUTILS = new GenericTransactionUtils();
98
99     // -- OSGi services, provided
100     private DataBroker dataBroker;
101     private MountPointService mountPointService;
102     private RpcProviderService rpcProviderRegistry;
103     private IEntityDataProvider iEntityDataProvider;
104     @SuppressWarnings("unused")
105     private NotificationPublishService notificationPublishService;
106     @SuppressWarnings("unused")
107     private ClusterSingletonServiceProvider clusterSingletonServiceProvider;
108
109     // -- Parameter
110     private ListenerRegistration<L1> listenerL1;
111     private ListenerRegistration<L2> listenerL2;
112     @SuppressWarnings("unused")
113     private ClusterSingletonServiceRegistration cssRegistration;
114
115     private NetconfnodeStateServiceRpcApiImpl rpcApiService;
116
117     /** Indication if init() function called and fully executed **/
118     private Boolean initializationSuccessful;
119
120     /** List of all registered listeners **/
121     private final List<NetconfNodeConnectListener> netconfNodeConnectListenerList;
122
123     /** List of all registered listeners **/
124     private final List<NetconfNodeStateListener> netconfNodeStateListenerList;
125
126     /** List of all registered listeners **/
127     private final List<VesNotificationListener> vesNotificationListenerList;
128
129     /** Indicates if running in cluster configuration **/
130     private boolean isCluster;
131
132     /** Indicates the name of the cluster **/
133     private String clusterName;
134
135     /** Blueprint **/
136     public NetconfNodeStateServiceImpl() {
137         LOG.info("Creating provider for {}", APPLICATION_NAME);
138
139         this.dataBroker = null;
140         this.mountPointService = null;
141         this.rpcProviderRegistry = null;
142         this.notificationPublishService = null;
143         this.clusterSingletonServiceProvider = null;
144
145         this.listenerL1 = null;
146         this.listenerL2 = null;
147         this.initializationSuccessful= false;
148         this.netconfNodeConnectListenerList = new CopyOnWriteArrayList<>();
149         this.netconfNodeStateListenerList = new CopyOnWriteArrayList<>();
150         this.vesNotificationListenerList = new CopyOnWriteArrayList<>();
151     }
152
153     public void setDataBroker(DataBroker dataBroker) {
154         this.dataBroker = dataBroker;
155     }
156
157     public void setRpcProviderRegistry(RpcProviderService rpcProviderRegistry) {
158         this.rpcProviderRegistry = rpcProviderRegistry;
159     }
160
161     public void setNotificationPublishService(NotificationPublishService notificationPublishService) {
162         this.notificationPublishService = notificationPublishService;
163     }
164
165     public void setMountPointService(MountPointService mountPointService) {
166         this.mountPointService = mountPointService;
167     }
168     public void setClusterSingletonService(ClusterSingletonServiceProvider clusterSingletonService) {
169         this.clusterSingletonServiceProvider = clusterSingletonService;
170     }
171     public void setEntityDataProvider(IEntityDataProvider iEntityDataProvider) {
172         this.iEntityDataProvider = iEntityDataProvider;
173     }
174
175     /** Blueprint initialization **/
176     public void init() {
177
178         LOG.info("Session Initiated start {}", APPLICATION_NAME);
179
180         // Start RPC Service
181         this.rpcApiService = new NetconfnodeStateServiceRpcApiImpl(rpcProviderRegistry, vesNotificationListenerList);
182         // Get configuration
183         // ConfigurationFileRepresentation config = new ConfigurationFileRepresentation(CONFIGURATIONFILE);
184         // Akka setup
185         AkkaConfig akkaConfig = getAkkaConfig();
186         this.isCluster = akkaConfig == null ? false : akkaConfig.isCluster();
187         this.clusterName = akkaConfig == null ? "" : akkaConfig.getClusterConfig().getClusterSeedNodeName("abc");
188
189         // Provide status information
190         ClusterConfig cc = akkaConfig==null?null:akkaConfig.getClusterConfig();
191         this.iEntityDataProvider.setStatus(StatusKey.CLUSTER_SIZE,cc==null?"1":String.format("%d",cc.getClusterSize()));
192
193         // RPC Service for specific services
194         this.rpcApiService.setStatusCallback(this);
195
196         LOG.debug("start NetconfSubscriptionManager Service");
197         //this.netconfChangeListener = new NetconfChangeListener(this, dataBroker);
198         //this.netconfChangeListener.register();
199         //DataTreeIdentifier<Node> treeId = new DataTreeIdentifier<>(LogicalDatastoreType.OPERATIONAL, NETCONF_NODE_TOPO_IID);
200
201         listenerL1 = dataBroker.registerDataTreeChangeListener(NETCONF_NODE_TOPO_TREE_ID, new L1());
202         listenerL2 = dataBroker.registerDataTreeChangeListener(NETCONF_NODE_TOPO_TREE_ID, new L2());
203
204         this.initializationSuccessful = true;
205
206         LOG.info("Session Initiated end. Initialization done {}", initializationSuccessful);
207
208     }
209     /** Blueprint destroy-method method */
210     public void destroy() {
211         close();
212     }
213
214     /**
215      * Getter
216      * @return NetconfnodeStateServiceRpcApiImpl
217      */
218     public NetconfnodeStateServiceRpcApiImpl getNetconfnodeStateServiceRpcApiImpl() {
219         return rpcApiService;
220     }
221
222     @Override
223     public GetStatusOutputBuilder getStatus(GetStatusInput input) {
224         return new GetStatusOutputBuilder();
225     }
226
227     @Override
228     public <L extends NetconfNodeConnectListener> @NonNull ListenerRegistration<L> registerNetconfNodeConnectListener(
229             final @NonNull L netconfNodeConnectListener) {
230         LOG.info("Register connect listener {}",netconfNodeConnectListener.getClass().getName());
231         netconfNodeConnectListenerList.add(netconfNodeConnectListener);
232
233         return new ListenerRegistration<L>() {
234             @Override
235             public @NonNull L getInstance() {
236                 return netconfNodeConnectListener;
237             }
238
239             @Override
240             public void close() {
241                 LOG.info("Remove connect listener {}",netconfNodeConnectListener);
242                 netconfNodeConnectListenerList.remove(netconfNodeConnectListener);
243             }
244         };
245     }
246
247     @Override
248     public <L extends NetconfNodeStateListener> @NonNull ListenerRegistration<L> registerNetconfNodeStateListener(
249             @NonNull L netconfNodeStateListener) {
250         LOG.info("Register state listener {}",netconfNodeStateListener.getClass().getName());
251         netconfNodeStateListenerList.add(netconfNodeStateListener);
252
253         return new ListenerRegistration<L>() {
254             @Override
255             public @NonNull L getInstance() {
256                 return netconfNodeStateListener;
257             }
258
259             @Override
260             public void close() {
261                 LOG.info("Remove state listener {}",netconfNodeStateListener);
262                 netconfNodeStateListenerList.remove(netconfNodeStateListener);
263             }
264         };
265     }
266
267     @Override
268     public <L extends VesNotificationListener> @NonNull ListenerRegistration<L> registerVesNotifications(
269             @NonNull L vesNotificationListener) {
270         LOG.info("Register Ves notification listener {}",vesNotificationListener.getClass().getName());
271         vesNotificationListenerList.add(vesNotificationListener);
272
273         return new ListenerRegistration<L>() {
274             @Override
275             public @NonNull L getInstance() {
276                 return vesNotificationListener;
277             }
278
279             @Override
280             public void close() {
281                 LOG.info("Remove Ves notification listener {}",vesNotificationListener);
282                 vesNotificationListenerList.remove(vesNotificationListener);
283             }
284         };
285     }
286
287     @Override
288     public void close() {
289         LOG.info("Closing start ...");
290         try {
291             close(rpcApiService, listenerL1, listenerL2);
292         } catch (Exception e) {
293             LOG.debug("Closing", e);
294         }
295         LOG.info("Closing done");
296     }
297
298     /**
299      * Used to close all Services, that should support AutoCloseable Pattern
300      *
301      * @param toClose
302      * @throws Exception
303      */
304     private void close(AutoCloseable... toCloseList) throws Exception {
305         for (AutoCloseable element : toCloseList) {
306             if (element != null) {
307                 element.close();
308             }
309         }
310     }
311
312     /**
313      * Indication if init() of this bundle successfully done.
314      * @return true if init() was successful. False if not done or not successful.
315      */
316     public boolean isInitializationSuccessful() {
317         return this.initializationSuccessful;
318     }
319
320     /*-------------------------------------------------------------------------------------------
321      * Functions for interface DeviceManagerService
322      */
323
324     /**
325      * For each mounted device a mountpoint is created and this listener is called.
326      * Mountpoint was created or existing. Managed device is now fully connected to node/mountpoint.
327      * @param nNodeId id of the mountpoint
328      * @param netconfNode mountpoint contents
329      */
330     private void enterConnectedState(NodeId nNodeId, NetconfNode netconfNode) {
331
332         String mountPointNodeName = nNodeId.getValue();
333         LOG.info("Access connected state for mountpoint {}", mountPointNodeName);
334
335         boolean preConditionMissing = false;
336         if (mountPointService == null) {
337             preConditionMissing = true;
338             LOG.warn("No mountservice available.");
339         }
340         if (!initializationSuccessful) {
341             preConditionMissing = true;
342             LOG.warn("Devicemanager initialization still pending.");
343         }
344         if (preConditionMissing) {
345             return;
346         }
347
348         boolean isNetconfNodeMaster = isNetconfNodeMaster(netconfNode);
349         LOG.info("isNetconfNodeMaster indication {} for mountpoint {}", isNetconfNodeMaster, mountPointNodeName);
350         if (isNetconfNodeMaster) {
351
352             InstanceIdentifier<Node> instanceIdentifier = NETCONF_TOPO_IID.child(Node.class,
353                     new NodeKey(new NodeId(mountPointNodeName)));
354
355             Optional<MountPoint> optionalMountPoint = null;
356             int timeout = 10000;
357             while (!(optionalMountPoint = mountPointService.getMountPoint(instanceIdentifier)).isPresent()
358                     && timeout > 0) {
359                 LOG.info("Event listener waiting for mount point for Netconf device :: Name : {}", mountPointNodeName);
360                 sleepMs(1000);
361                 timeout -= 1000;
362             }
363
364             if (!optionalMountPoint.isPresent()) {
365                 LOG.warn("Event listener timeout while waiting for mount point for Netconf device :: Name : {} ",
366                         mountPointNodeName);
367             } else {
368                 // Mountpoint is present for sure
369                 MountPoint mountPoint = optionalMountPoint.get();
370                 // BindingDOMDataBrokerAdapter.BUILDER_FACTORY;
371                 LOG.info("Mountpoint with id: {}", mountPoint.getIdentifier());
372
373                 Optional<DataBroker> optionalNetconfNodeDatabroker = mountPoint.getService(DataBroker.class);
374
375                 if (!optionalNetconfNodeDatabroker.isPresent()) {
376                     LOG.info("Slave mountpoint {} without databroker", mountPointNodeName);
377                 } else {
378                     LOG.info("Master mountpoint {}", mountPointNodeName);
379                     DataBroker netconfNodeDataBroker = optionalNetconfNodeDatabroker.get();
380                     NetconfAccessor acessor = new NetconfAccessorImpl(nNodeId, netconfNode, netconfNodeDataBroker,
381                             mountPoint, TRANSACTIONUTILS);
382                     /*
383                      * --> Call Listers for onConnect() Indication
384                        for (all)
385                      */
386                     netconfNodeConnectListenerList.forEach(item -> {
387                         try {
388                             item.onEnterConnected(acessor);
389                         } catch (Exception e) {
390                             LOG.info("Exception during onEnterConnected listener call", e);
391                         }
392                     });
393
394                     LOG.info("Connect indication forwarded for {}", mountPointNodeName);
395                 }
396             }
397         }
398     }
399
400     /**
401      * Leave the connected status to a non connected or removed status for master mountpoint
402      * @param action that occurred
403      * @param nNodeId id of the mountpoint
404      * @param netconfNode mountpoint contents or not available on remove
405      */
406     private void leaveConnectedState(NodeId nNodeId, Optional<NetconfNode> optionalNetconfNode) {
407         String mountPointNodeName = nNodeId.getValue();
408         LOG.info("netconfNode id {}", mountPointNodeName);
409
410         InstanceIdentifier<Node> instanceIdentifier = NETCONF_TOPO_IID.child(Node.class,
411                 new NodeKey(new NodeId(mountPointNodeName)));
412         Optional<MountPoint> optionalMountPoint = mountPointService.getMountPoint(instanceIdentifier);
413         if (optionalMountPoint.isPresent()) {
414             Optional<DataBroker> optionalNetconfNodeDatabroker = optionalMountPoint.get().getService(DataBroker.class);
415             if (optionalNetconfNodeDatabroker.isPresent()) {
416                 LOG.info("Master mountpoint {}", mountPointNodeName);
417                 netconfNodeConnectListenerList.forEach(item -> {
418                     try {
419                         if (item != null) {
420                             item.onLeaveConnected(nNodeId, optionalNetconfNode);
421                         } else {
422                             LOG.warn("Unexpeced null item during onleave");
423                         }
424                     } catch (Exception e) {
425                         LOG.info("Exception during onLeaveConnected listener call", e);
426                     }
427                 });
428             }
429         }
430     }
431
432     // ---- onDataTreeChangedHandler
433
434     private void onDataTreeChangedHandler(@NonNull Collection<DataTreeModification<Node>> changes) {
435         for (final DataTreeModification<Node> change : changes) {
436
437             final DataObjectModification<Node> root = change.getRootNode();
438             //if (LOG.isTraceEnabled()) {
439                 LOG.info /*trace*/("Handle this modificationType:{} path:{} root:{}", root.getModificationType(),
440                         change.getRootPath(), root);
441             //}
442
443             // Catch potential nullpointer exceptions ..
444             try {
445                 ModificationType modificationTyp = root.getModificationType();
446                 Node node = modificationTyp == ModificationType.DELETE ? root.getDataBefore()
447                         : root.getDataAfter();
448                 NodeId nodeId = node != null ? node.getNodeId() : null;
449                 if (nodeId != null) {
450                     if (nodeId.equals(CONTROLLER)) {
451                         // Do not forward any controller related events to devicemanager
452                         LOG.debug("Stop processing for [{}]", nodeId);
453                     } else {
454                         if (modificationTyp != null) {
455                             switch (modificationTyp) {
456                             case SUBTREE_MODIFIED: // Create or modify sub level node
457                             case WRITE: // Create or modify top level node
458                                 // Treat an overwrite as an update
459                                 // leaveconnected state.before = connected; state.after != connected
460                                 // enterConnected state.after == connected
461                                 // => Here create or update by checking root.getDataBefore() != null
462
463                                 boolean connectedBefore, connectedAfter;
464                                 NetconfNode nNodeAfter = getNetconfNode(root.getDataAfter());
465                                 connectedAfter = isConnected(nNodeAfter);
466                                 if (root.getDataBefore() != null) {
467                                     // It is an update
468                                     NetconfNode nodeBefore = getNetconfNode(root.getDataBefore());
469                                     connectedBefore = isConnected(nodeBefore);
470                                 } else {
471                                     // It is a create
472                                     connectedBefore = false;
473                                 }
474
475                                 LOG.info(
476                                         "L1 NETCONF Node change with id:{} ConnectedBefore:{} connectedAfter {}:cluster status:{} akkaIsCluster:{}",
477                                         nodeId, connectedBefore, connectedAfter,
478                                         getClusteredConnectionStatus(nNodeAfter), isCluster);
479
480                                 if (!connectedBefore && connectedAfter) {
481                                     netconfNodeStateListenerList.forEach(item -> {
482                                         try {
483                                             item.onCreated(nodeId, nNodeAfter);
484                                         } catch (Exception e) {
485                                             LOG.info("Exception during onCreated listener call", e);
486                                         }
487                                     });
488                                     enterConnectedState(nodeId, nNodeAfter);
489                                 } else {
490                                     LOG.debug("State change {} {}", connectedBefore, connectedAfter);
491                                     if (connectedBefore && !connectedAfter) {
492                                         leaveConnectedState(nodeId, Optional.of(nNodeAfter));
493                                     }
494                                     netconfNodeStateListenerList.forEach(item -> {
495                                         try {
496                                             item.onStateChange(nodeId, nNodeAfter);
497                                         } catch (Exception e) {
498                                             LOG.info("Exception during onStateChange listener call", e);
499                                         }
500                                     });
501                                 }
502                                 // doProcessing(update ? Action.UPDATE : Action.CREATE, nodeId, root);
503                                 break;
504                             case DELETE:
505                                 // Node removed
506                                 // leaveconnected state.before = connected;
507                                 leaveConnectedState(nodeId, Optional.empty());
508                                 netconfNodeStateListenerList.forEach(item -> {
509                                     try {
510                                         item.onRemoved(nodeId);
511                                     } catch (Exception e) {
512                                         LOG.info("Exception during onRemoved listener call", e);
513                                     }
514                                 });
515                                 // doProcessing(Action.REMOVE, nodeId, root);
516                                 break;
517                             }
518                         }
519                     }
520                 }
521             } catch (NullPointerException e) {
522                 LOG.info("Data not available at ", e);
523             }
524         } //for
525     }
526
527     // ---- subclasses for listeners
528
529     /**
530      * Clustered listener function to select the right node from
531      * DataObjectModification. Called at all nodes.
532      */
533     private class L1 implements ClusteredDataTreeChangeListener<Node> {
534         @Override
535         public void onDataTreeChanged(@NonNull Collection<DataTreeModification<Node>> changes) {
536             LOG.info("L1 TreeChange enter changes:{}", changes.size());
537             onDataTreeChangedHandler(changes);
538             LOG.info("L1 TreeChange leave");
539         }
540     }
541
542     /**
543      * Data change, called at leader/master
544      */
545     private class L2 implements DataTreeChangeListener<Node> {
546
547         @Override
548         public void onDataTreeChanged(@NonNull Collection<DataTreeModification<Node>> changes) {
549             LOG.info("L2 TreeChange enter changes:{}", changes.size());
550             // Do nothing
551             LOG.info("L2 TreeChange leave");
552         }
553     }
554
555     /* --- private helpers --- */
556     private static @Nullable NetconfNode getNetconfNode(Node node) {
557           return node != null ? node.augmentation(NetconfNode.class) : null;
558     }
559
560     private static boolean isConnected(NetconfNode nNode) {
561         return nNode != null ? ConnectionStatus.Connected.equals(nNode.getConnectionStatus()) : false;
562     }
563
564     private static @Nullable ClusteredConnectionStatus getClusteredConnectionStatus(NetconfNode node) {
565         return node != null ? node.getClusteredConnectionStatus() : null;
566     }
567
568     /* -- LOG related functions -- */
569
570     /** Analyze configuration **/
571     private static @Nullable AkkaConfig getAkkaConfig() {
572         AkkaConfig akkaConfig;
573         try {
574             akkaConfig = AkkaConfig.load();
575             LOG.debug("akka.conf loaded: " + akkaConfig.toString());
576         } catch (Exception e1) {
577             akkaConfig = null;
578             LOG.warn("problem loading akka.conf: " + e1.getMessage());
579         }
580         if (akkaConfig != null && akkaConfig.isCluster()) {
581             LOG.info("cluster mode detected");
582             if (GeoConfig.fileExists()) {
583                 try {
584                     LOG.debug("try to load geoconfig");
585                     GeoConfig.load();
586                 } catch (Exception err) {
587                     LOG.warn("problem loading geoconfig: " + err.getMessage());
588                 }
589             } else {
590                 LOG.debug("no geoconfig file found");
591             }
592         } else {
593             LOG.info("single node mode detected");
594         }
595         return akkaConfig;
596     }
597
598     private boolean isNetconfNodeMaster(NetconfNode nNode) {
599         if (this.isCluster) {
600             LOG.debug("check if me is responsible for node");
601             ClusteredConnectionStatus ccs = nNode.getClusteredConnectionStatus();
602             @SuppressWarnings("null")
603             @NonNull String masterNodeName = ccs == null || ccs.getNetconfMasterNode() == null ? "null" : ccs.getNetconfMasterNode();
604             LOG.debug("sdnMasterNode=" + masterNodeName + " and sdnMyNode=" + this.clusterName);
605             if (!masterNodeName.equals(this.clusterName)) {
606                 LOG.debug("netconf change but me is not master for this node");
607                 return false;
608             }
609         }
610         return true;
611     }
612
613
614     private void sleepMs(int milliseconds) {
615         try {
616             Thread.sleep(milliseconds);
617         } catch (InterruptedException e) {
618             LOG.debug("Interrupted sleep");
619             // Restore interrupted state...
620             Thread.currentThread().interrupt();
621         }
622     }
623
624
625 }