adced6b33992df17c7fce9f4c2a3e3effb499c76
[ccsdk/features.git] /
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, created=false;
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                                     created = true;
474                                 }
475
476                                 LOG.info(
477                                         "L1 NETCONF Node change with id:{} ConnectedBefore:{} connectedAfter {}:cluster status:{} akkaIsCluster:{}",
478                                         nodeId, connectedBefore, connectedAfter,
479                                         getClusteredConnectionStatus(nNodeAfter), isCluster);
480
481                                 if(created) {
482                                          netconfNodeStateListenerList.forEach(item -> {
483                                          try {
484                                              item.onCreated(nodeId, nNodeAfter);
485                                          } catch (Exception e) {
486                                              LOG.info("Exception during onCreated listener call", e);
487                                          }
488                                      });
489                                 }
490                                 if (!connectedBefore && connectedAfter) {
491                                     enterConnectedState(nodeId, nNodeAfter);
492                                 } else {
493                                     LOG.debug("State change {} {}", connectedBefore, connectedAfter);
494                                     if (connectedBefore && !connectedAfter) {
495                                         leaveConnectedState(nodeId, Optional.of(nNodeAfter));
496                                     }
497                                     netconfNodeStateListenerList.forEach(item -> {
498                                         try {
499                                             item.onStateChange(nodeId, nNodeAfter);
500                                         } catch (Exception e) {
501                                             LOG.info("Exception during onStateChange listener call", e);
502                                         }
503                                     });
504                                 }
505                                 // doProcessing(update ? Action.UPDATE : Action.CREATE, nodeId, root);
506                                 break;
507                             case DELETE:
508                                 // Node removed
509                                 // leaveconnected state.before = connected;
510                                 leaveConnectedState(nodeId, Optional.empty());
511                                 netconfNodeStateListenerList.forEach(item -> {
512                                     try {
513                                         item.onRemoved(nodeId);
514                                     } catch (Exception e) {
515                                         LOG.info("Exception during onRemoved listener call", e);
516                                     }
517                                 });
518                                 // doProcessing(Action.REMOVE, nodeId, root);
519                                 break;
520                             }
521                         }
522                     }
523                 }
524             } catch (NullPointerException e) {
525                 LOG.info("Data not available at ", e);
526             }
527         } //for
528     }
529
530     // ---- subclasses for listeners
531
532     /**
533      * Clustered listener function to select the right node from
534      * DataObjectModification. Called at all nodes.
535      */
536     private class L1 implements ClusteredDataTreeChangeListener<Node> {
537         @Override
538         public void onDataTreeChanged(@NonNull Collection<DataTreeModification<Node>> changes) {
539             LOG.info("L1 TreeChange enter changes:{}", changes.size());
540             new Thread( () -> onDataTreeChangedHandler(changes)).start();
541             LOG.info("L1 TreeChange leave");
542         }
543     }
544
545     /**
546      * Data change, called at leader/master
547      */
548     private class L2 implements DataTreeChangeListener<Node> {
549
550         @Override
551         public void onDataTreeChanged(@NonNull Collection<DataTreeModification<Node>> changes) {
552             LOG.info("L2 TreeChange enter changes:{}", changes.size());
553             // Do nothing
554             LOG.info("L2 TreeChange leave");
555         }
556     }
557
558     /* --- private helpers --- */
559     private static @Nullable NetconfNode getNetconfNode(Node node) {
560           return node != null ? node.augmentation(NetconfNode.class) : null;
561     }
562
563     private static boolean isConnected(NetconfNode nNode) {
564         return nNode != null ? ConnectionStatus.Connected.equals(nNode.getConnectionStatus()) : false;
565     }
566
567     private static @Nullable ClusteredConnectionStatus getClusteredConnectionStatus(NetconfNode node) {
568         return node != null ? node.getClusteredConnectionStatus() : null;
569     }
570
571     /* -- LOG related functions -- */
572
573     /** Analyze configuration **/
574     private static @Nullable AkkaConfig getAkkaConfig() {
575         AkkaConfig akkaConfig;
576         try {
577             akkaConfig = AkkaConfig.load();
578             LOG.debug("akka.conf loaded: " + akkaConfig.toString());
579         } catch (Exception e1) {
580             akkaConfig = null;
581             LOG.warn("problem loading akka.conf: " + e1.getMessage());
582         }
583         if (akkaConfig != null && akkaConfig.isCluster()) {
584             LOG.info("cluster mode detected");
585             if (GeoConfig.fileExists()) {
586                 try {
587                     LOG.debug("try to load geoconfig");
588                     GeoConfig.load();
589                 } catch (Exception err) {
590                     LOG.warn("problem loading geoconfig: " + err.getMessage());
591                 }
592             } else {
593                 LOG.debug("no geoconfig file found");
594             }
595         } else {
596             LOG.info("single node mode detected");
597         }
598         return akkaConfig;
599     }
600
601     private boolean isNetconfNodeMaster(NetconfNode nNode) {
602         if (this.isCluster) {
603             LOG.debug("check if me is responsible for node");
604             ClusteredConnectionStatus ccs = nNode.getClusteredConnectionStatus();
605             @SuppressWarnings("null")
606             @NonNull String masterNodeName = ccs == null || ccs.getNetconfMasterNode() == null ? "null" : ccs.getNetconfMasterNode();
607             LOG.debug("sdnMasterNode=" + masterNodeName + " and sdnMyNode=" + this.clusterName);
608             if (!masterNodeName.equals(this.clusterName)) {
609                 LOG.debug("netconf change but me is not master for this node");
610                 return false;
611             }
612         }
613         return true;
614     }
615
616
617     private void sleepMs(int milliseconds) {
618         try {
619             Thread.sleep(milliseconds);
620         } catch (InterruptedException e) {
621             LOG.debug("Interrupted sleep");
622             // Restore interrupted state...
623             Thread.currentThread().interrupt();
624         }
625     }
626
627
628 }