2 * ============LICENSE_START=======================================================
3 * ONAP : ccsdk feature sdnr wt
4 * ================================================================================
5 * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property.
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
12 * http://www.apache.org/licenses/LICENSE-2.0
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=========================================================
21 package org.onap.ccsdk.features.sdnr.wt.devicemanager.housekeeping;
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;
67 public class ConnectionStatusHousekeepingService
68 implements ClusterSingletonService, AutoCloseable, IConfigChangedListener {
70 private static final Logger LOG = LoggerFactory.getLogger(ConnectionStatusHousekeepingService.class);
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");
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;
86 private final ClusterSingletonServiceRegistration cssRegistration2;
87 private boolean isMaster;
88 private Future<?> taskReference;
89 private int eventNumber;
90 private volatile boolean enabled;
92 public ConnectionStatusHousekeepingService(ConfigurationFileRepresentation cfg,
93 ClusterSingletonServiceProvider clusterSingletonServiceProvider, DataBroker dataBroker,
94 DataProvider dataProvider) {
95 this.config = new HouseKeepingConfig(cfg);
97 cfg.registerConfigChangedListener(this);
98 this.dataBroker = dataBroker;
99 this.dataProvider = dataProvider;
100 this.eventNumber = 0;
102 setEnabled(this.config.isEnabled());
105 this.cssRegistration2 = clusterSingletonServiceProvider.registerClusterSingletonService(this);
108 private void setEnabled(boolean pEnabled) {
109 LOG.info("ConnectionStatusHousekeepingService status change from {} to {}", enabled, pEnabled);
110 this.enabled = pEnabled;
113 private boolean isEnabled() {
117 private void start() {
118 if (taskReference != null) {
119 taskReference.cancel(false);
122 LOG.info("Do not start. not the master node");
124 LOG.info("Starting scheduler with interval {}", INTERVAL_SECONDS);
126 this.scheduler.scheduleAtFixedRate(runner, INTERVAL_SECONDS, INTERVAL_SECONDS, TimeUnit.SECONDS);
130 private void doClean() {
132 LOG.debug("service is disabled by config");
135 LOG.debug("start housekeeping");
136 // get all devices from networkelement-connection index
138 List<NetworkElementConnectionEntity> list = this.dataProvider.getNetworkElementConnections();
140 ConnectionLogStatus dbStatus;
141 ConnectionLogStatus mdsalStatus;
143 if (list == null || list.size() <= 0) {
144 LOG.trace("no items in list.");
146 NetconfTimeStamp ts = NetconfTimeStampImpl.getConverter();
147 //check all db entries and sync connection status
148 for (NetworkElementConnectionEntity item : list) {
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");
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.requireIsRequired() == null || item.requireIsRequired() == 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);
172 NetworkElementConnectionBuilder ne =
173 new NetworkElementConnectionBuilder().setStatus(mdsalStatus);
175 this.dataProvider.updateNetworkConnection22(ne.build(), nodeId);
178 LOG.trace("no difference");
183 } catch (Exception e) {
184 LOG.warn("problem executing housekeeping task: {}", e);
186 LOG.debug("finish housekeeping");
189 private Integer popEvntNumber() {
190 return eventNumber++;
193 private ConnectionLogStatus getMDSalConnectionStatus(String nodeId) {
196 InstanceIdentifier<Node> instanceIdentifier =
197 NETCONF_TOPO_IID.child(Node.class, new NodeKey(new NodeId(nodeId)));
198 //Implicit close of try with resource is not handled correctly by underlying opendaylight NETCONF service
199 ReadTransaction trans = this.dataBroker.newReadOnlyTransaction();
201 FluentFuture<Optional<Node>> optionalNode =
202 trans.read(LogicalDatastoreType.OPERATIONAL, instanceIdentifier);
203 Optional<Node> nodeOpt = optionalNode.get();
204 if (nodeOpt.isEmpty()) {
205 LOG.warn("unable to get node info");
208 Node node = nodeOpt.get();
209 LOG.debug("node is {}", node);
210 NetconfNode nNode = node.augmentation(NetconfNode.class);
211 LOG.debug("nnode is {}", nNode);
213 return InternalConnectionStatus.statusFromNodeStatus(nNode.getConnectionStatus());
215 } catch (NoSuchElementException e) {
216 return ConnectionLogStatus.Disconnected;
217 } catch (InterruptedException e) {
218 LOG.warn("InterruptedException unable to get node info: ", e);
219 Thread.currentThread().interrupt();
220 } catch (ExecutionException e) {// | TimeoutException e) {
221 LOG.warn("ExecutionException unable to get node info: ", e);
230 public void close() throws Exception {
231 if (taskReference != null) {
232 taskReference.cancel(false);
234 if (this.cfg != null) {
235 this.cfg.unregisterConfigChangedListener(this);
237 this.scheduler.shutdown();
238 if (this.cssRegistration2 != null)
239 this.cssRegistration2.close();
243 public @NonNull ServiceGroupIdentifier getIdentifier() {
248 public void instantiateServiceInstance() {
249 LOG.info("We take Leadership");
250 this.isMaster = true;
255 public ListenableFuture<? extends Object> closeServiceInstance() {
256 LOG.info("We lost Leadership");
257 this.isMaster = false;
259 return Futures.immediateFuture(null);
263 public void onConfigChanged() {
265 setEnabled(this.config.isEnabled());