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