49c678ebcceba624220305ab6a4312c4f1b4a1b4
[ccsdk/features.git] / sdnr / wt / devicemanager-core / provider / src / main / java / org / onap / ccsdk / features / sdnr / wt / devicemanager / housekeeping / ConnectionStatusHousekeepingService.java
1 /*
2  * ============LICENSE_START=======================================================
3  * ONAP : ccsdk feature sdnr wt
4  *  ================================================================================
5  * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property.
6  * All rights reserved.
7  * ================================================================================
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  * ============LICENSE_END=========================================================
20  */
21 package org.onap.ccsdk.features.sdnr.wt.devicemanager.housekeeping;
22
23 import com.google.common.util.concurrent.FluentFuture;
24 import com.google.common.util.concurrent.Futures;
25 import com.google.common.util.concurrent.ListenableFuture;
26 import java.util.List;
27 import java.util.NoSuchElementException;
28 import java.util.Optional;
29 import java.util.concurrent.ExecutionException;
30 import java.util.concurrent.Executors;
31 import java.util.concurrent.Future;
32 import java.util.concurrent.ScheduledExecutorService;
33 import java.util.concurrent.TimeUnit;
34 import org.eclipse.jdt.annotation.NonNull;
35 import org.onap.ccsdk.features.sdnr.wt.common.configuration.ConfigurationFileRepresentation;
36 import org.onap.ccsdk.features.sdnr.wt.common.configuration.filechange.IConfigChangedListener;
37 import org.onap.ccsdk.features.sdnr.wt.dataprovider.model.DataProvider;
38 import org.onap.ccsdk.features.sdnr.wt.dataprovider.model.NetconfTimeStamp;
39 import org.onap.ccsdk.features.sdnr.wt.dataprovider.model.types.NetconfTimeStampImpl;
40 import org.onap.ccsdk.features.sdnr.wt.devicemanager.types.InternalConnectionStatus;
41 import org.opendaylight.mdsal.binding.api.DataBroker;
42 import org.opendaylight.mdsal.binding.api.ReadTransaction;
43 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
44 import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonService;
45 import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonServiceProvider;
46 import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonServiceRegistration;
47 import org.opendaylight.mdsal.singleton.common.api.ServiceGroupIdentifier;
48 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.DateAndTime;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNode;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.network.topology.topology.topology.types.TopologyNetconf;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.data.provider.rev201110.ConnectionLogStatus;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.data.provider.rev201110.EventlogBuilder;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.data.provider.rev201110.NetworkElementConnectionBuilder;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.data.provider.rev201110.NetworkElementConnectionEntity;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.data.provider.rev201110.SourceType;
56 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
57 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
58 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId;
59 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
60 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
61 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
62 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey;
63 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
64 import org.slf4j.Logger;
65 import org.slf4j.LoggerFactory;
66
67 public class ConnectionStatusHousekeepingService
68         implements ClusterSingletonService, AutoCloseable, IConfigChangedListener {
69
70     private static final Logger LOG = LoggerFactory.getLogger(ConnectionStatusHousekeepingService.class);
71
72     private static final long INTERVAL_SECONDS = 30;
73     private static final InstanceIdentifier<Topology> NETCONF_TOPO_IID =
74             InstanceIdentifier.create(NetworkTopology.class).child(Topology.class,
75                     new TopologyKey(new TopologyId(TopologyNetconf.QNAME.getLocalName())));
76     private static final ServiceGroupIdentifier IDENT =
77             ServiceGroupIdentifier.create("ConnectionStatusHousekeepingService");
78
79     private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(3);
80     private final DataBroker dataBroker;
81     private final DataProvider dataProvider;
82     private final Runnable runner = () -> doClean();
83     private final HouseKeepingConfig config;
84     private final ConfigurationFileRepresentation cfg;
85
86     private final ClusterSingletonServiceRegistration cssRegistration2;
87     private boolean isMaster;
88     private Future<?> taskReference;
89     private int eventNumber;
90     private volatile boolean enabled;
91
92     public ConnectionStatusHousekeepingService(ConfigurationFileRepresentation cfg,
93             ClusterSingletonServiceProvider clusterSingletonServiceProvider, DataBroker dataBroker,
94             DataProvider dataProvider) {
95         this.config = new HouseKeepingConfig(cfg);
96         this.cfg = cfg;
97         cfg.registerConfigChangedListener(this);
98         this.dataBroker = dataBroker;
99         this.dataProvider = dataProvider;
100         this.eventNumber = 0;
101
102         setEnabled(this.config.isEnabled());
103         this.start();
104
105         this.cssRegistration2 = clusterSingletonServiceProvider.registerClusterSingletonService(this);
106     }
107
108     private void setEnabled(boolean pEnabled) {
109         LOG.info("ConnectionStatusHousekeepingService status change from {} to {}", enabled, pEnabled);
110         this.enabled = pEnabled;
111     }
112
113     private boolean isEnabled() {
114         return this.enabled;
115     }
116
117     private void start() {
118         if (taskReference != null) {
119             taskReference.cancel(false);
120         }
121         if (!isMaster) {
122             LOG.info("Do not start. not the master node");
123         } else {
124             LOG.info("Starting scheduler with interval {}", INTERVAL_SECONDS);
125             this.taskReference =
126                     this.scheduler.scheduleAtFixedRate(runner, INTERVAL_SECONDS, INTERVAL_SECONDS, TimeUnit.SECONDS);
127         }
128     }
129
130     private void doClean() {
131         if (!isEnabled()) {
132             LOG.debug("service is disabled by config");
133             return;
134         }
135         LOG.debug("start housekeeping");
136         // get all devices from networkelement-connection index
137         try {
138             List<NetworkElementConnectionEntity> list = this.dataProvider.getNetworkElementConnections();
139
140             ConnectionLogStatus dbStatus;
141             ConnectionLogStatus mdsalStatus;
142             String nodeId;
143             if (list == null || list.size() <= 0) {
144                 LOG.trace("no items in list.");
145             } else {
146                 NetconfTimeStamp ts = NetconfTimeStampImpl.getConverter();
147                 //check all db entries and sync connection status
148                 for (NetworkElementConnectionEntity item : list) {
149
150                     // compare with MD-SAL
151                     nodeId = item.getNodeId();
152                     LOG.trace("check status of {}", nodeId);
153                     dbStatus = item.getStatus();
154                     mdsalStatus = this.getMDSalConnectionStatus(nodeId);
155                     if (mdsalStatus == null) {
156                         LOG.trace("unable to get connection status. jump over");
157                         continue;
158                     }
159                     // if different then update db
160                     if (dbStatus != mdsalStatus) {
161                         LOG.trace("status is inconsistent db={}, mdsal={}. updating db", dbStatus, mdsalStatus);
162                         this.dataProvider.writeEventLog(new EventlogBuilder().setNodeId("SDN-Controller")
163                                 .setTimestamp(new DateAndTime(ts.getTimeStamp())).setObjectId(item.getNodeId())
164                                 .setAttributeName("status").setNewValue(String.valueOf(mdsalStatus))
165                                 .setCounter(popEvntNumber()).setSourceType(SourceType.Controller).build());
166                         if ((item.isIsRequired() == null || item.isIsRequired() == false)
167                                 && mdsalStatus == ConnectionLogStatus.Disconnected) {
168                             LOG.info("removing entry for node {} ({}) from database due missing MD-SAL entry",
169                                     item.getNodeId(), mdsalStatus);
170                             this.dataProvider.removeNetworkConnection(nodeId);
171                         } else {
172                             NetworkElementConnectionBuilder ne =
173                                     new NetworkElementConnectionBuilder().setStatus(mdsalStatus);
174
175                             this.dataProvider.updateNetworkConnection22(ne.build(), nodeId);
176                         }
177                     } else {
178                         LOG.trace("no difference");
179                     }
180                 }
181             }
182
183         } catch (Exception e) {
184             LOG.warn("problem executing housekeeping task: {}", e);
185         }
186         LOG.debug("finish housekeeping");
187     }
188
189     private Integer popEvntNumber() {
190         return eventNumber++;
191     }
192
193     private ConnectionLogStatus getMDSalConnectionStatus(String nodeId) {
194
195         @SuppressWarnings("null")
196         @NonNull
197         InstanceIdentifier<Node> instanceIdentifier =
198                 NETCONF_TOPO_IID.child(Node.class, new NodeKey(new NodeId(nodeId)));
199         ReadTransaction trans = this.dataBroker.newReadOnlyTransaction();
200         FluentFuture<Optional<Node>> optionalNode = trans.read(LogicalDatastoreType.OPERATIONAL, instanceIdentifier);
201         try {
202             //Node node = optionalNode.get(5, TimeUnit.SECONDS).get();
203             Node node = optionalNode.get().get();
204             LOG.debug("node is {}", node);
205             NetconfNode nNode = node.augmentation(NetconfNode.class);
206             LOG.debug("nnode is {}", nNode);
207             if (nNode != null) {
208                 return InternalConnectionStatus.statusFromNodeStatus(nNode.getConnectionStatus());
209             }
210         } catch (NoSuchElementException e) {
211             return ConnectionLogStatus.Disconnected;
212         } catch (ExecutionException | InterruptedException e) {// | TimeoutException e) {
213             LOG.warn("unable to get node info: {}", e);
214         } finally {
215             trans.close();
216         }
217
218         return null;
219     }
220
221     @Override
222     public void close() throws Exception {
223         if (taskReference != null) {
224             taskReference.cancel(false);
225         }
226         if (this.cfg != null) {
227             this.cfg.unregisterConfigChangedListener(this);
228         }
229         this.scheduler.shutdown();
230         if (this.cssRegistration2 != null)
231             this.cssRegistration2.close();
232     }
233
234     @Override
235     public @NonNull ServiceGroupIdentifier getIdentifier() {
236         return IDENT;
237     }
238
239     @Override
240     public void instantiateServiceInstance() {
241         LOG.info("We take Leadership");
242         this.isMaster = true;
243         this.start();
244     }
245
246     @Override
247     public ListenableFuture<? extends Object> closeServiceInstance() {
248         LOG.info("We lost Leadership");
249         this.isMaster = false;
250         this.start();
251         return Futures.immediateFuture(null);
252     }
253
254     @Override
255     public void onConfigChanged() {
256
257         setEnabled(this.config.isEnabled());
258     }
259 }