Update features for Argon release
[ccsdk/features.git] / sdnr / wt / devicemanager-core / provider / src / main / java / org / onap / ccsdk / features / sdnr / wt / devicemanager / eventdatahandler / ODLEventListenerHandler.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.devicemanager.eventdatahandler;
19
20 import java.util.Optional;
21 import java.util.concurrent.ExecutionException;
22 import java.util.concurrent.ExecutorService;
23 import java.util.concurrent.Executors;
24 import java.util.concurrent.TimeUnit;
25 import org.eclipse.jdt.annotation.NonNull;
26 import org.onap.ccsdk.features.sdnr.wt.dataprovider.model.DataProvider;
27 import org.onap.ccsdk.features.sdnr.wt.dataprovider.model.NetconfTimeStamp;
28 import org.onap.ccsdk.features.sdnr.wt.dataprovider.model.types.NetconfTimeStampImpl;
29 import org.onap.ccsdk.features.sdnr.wt.devicemanager.dcaeconnector.impl.DcaeForwarderInternal;
30 import org.onap.ccsdk.features.sdnr.wt.devicemanager.impl.util.InternalDateAndTime;
31 import org.onap.ccsdk.features.sdnr.wt.devicemanager.impl.util.InternalSeverity;
32 import org.onap.ccsdk.features.sdnr.wt.devicemanager.impl.util.NetworkElementConnectionEntitiyUtil;
33 import org.onap.ccsdk.features.sdnr.wt.devicemanager.impl.xml.ProblemNotificationXml;
34 import org.onap.ccsdk.features.sdnr.wt.devicemanager.impl.xml.WebSocketServiceClientInternal;
35 import org.onap.ccsdk.features.sdnr.wt.devicemanager.service.EventHandlingService;
36 import org.opendaylight.mdsal.binding.api.DataBroker;
37 import org.opendaylight.mdsal.binding.api.ReadTransaction;
38 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
39 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.DateAndTime;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev221225.ConnectionOper.ConnectionStatus;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev221225.NetconfNode;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev221225.network.topology.topology.topology.types.TopologyNetconf;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.data.provider.rev201110.ConnectionLogStatus;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.data.provider.rev201110.Connectionlog;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.data.provider.rev201110.ConnectionlogBuilder;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.data.provider.rev201110.EventlogBuilder;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.data.provider.rev201110.NetworkElementConnectionEntity;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.data.provider.rev201110.NetworkElementDeviceType;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.data.provider.rev201110.SourceType;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.devicemanager.rev190109.AttributeValueChangedNotification;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.devicemanager.rev190109.AttributeValueChangedNotificationBuilder;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.devicemanager.rev190109.ObjectCreationNotification;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.devicemanager.rev190109.ObjectCreationNotificationBuilder;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.devicemanager.rev190109.ObjectDeletionNotification;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.devicemanager.rev190109.ObjectDeletionNotificationBuilder;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.devicemanager.rev190109.ProblemNotification;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.devicemanager.rev190109.ProblemNotificationBuilder;
58 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
59 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
60 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId;
61 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
62 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
63 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
64 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey;
65 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
66 import org.slf4j.Logger;
67 import org.slf4j.LoggerFactory;
68
69 /**
70  * Responsible class for documenting changes in the ODL itself. The occurence of such an event is documented in the
71  * database and to clients. Specific example here is the registration or deregistration of a netconf device. This
72  * service has an own eventcounter to apply to the ONF Coremodel netconf behaviour.
73  *
74  * Important: Websocket notification must be the last action.
75  *
76  * @author herbert
77  */
78
79 public class ODLEventListenerHandler implements EventHandlingService, AutoCloseable {
80
81     private static final Logger LOG = LoggerFactory.getLogger(ODLEventListenerHandler.class);
82
83     private static final NetconfTimeStamp NETCONFTIME_CONVERTER = NetconfTimeStampImpl.getConverter();
84
85     /**
86      * if update NE failed delay before retrying to write data into database
87      */
88     private static final long DBWRITE_RETRY_DELAY_MS = 3000;
89
90     private final String ownKeyName;
91     private final WebSocketServiceClientInternal webSocketService;
92     private final DataProvider databaseService;
93     private final DcaeForwarderInternal aotsDcaeForwarder;
94     private final DataBroker dataBroker;
95     private final ExecutorService executor = Executors.newFixedThreadPool(5);
96     private int eventNumber;
97
98
99     /*---------------------------------------------------------------
100      * Construct
101      */
102
103     /**
104      * Create a Service to document events to clients and within a database
105      *
106      * @param ownKeyName The name of this service, that is used in the database as identification key.
107      * @param webSocketService service to direct messages to clients
108      * @param databaseService service to write to the database
109      * @param dcaeForwarder to deliver problems to external service
110      */
111     public ODLEventListenerHandler(String ownKeyName, WebSocketServiceClientInternal webSocketService,
112             DataProvider databaseService, DcaeForwarderInternal dcaeForwarder, DataBroker dataBroker) {
113         super();
114
115         this.ownKeyName = ownKeyName;
116         this.webSocketService = webSocketService;
117
118         this.databaseService = databaseService;
119         this.aotsDcaeForwarder = dcaeForwarder;
120         this.dataBroker = dataBroker;
121
122         this.eventNumber = 0;
123     }
124
125     /*---------------------------------------------------------------
126      * Handling of ODL Controller events
127      */
128
129     /**
130      * (NonConnected) A registration after creation of a mountpoint occured
131      *
132      * @param registrationName of device (mountpoint name)
133      * @param nNode with mountpoint data
134      */
135     @Override
136     public void registration(NodeId nodeId, NetconfNode nNode) {
137
138         DateAndTime ts = NETCONFTIME_CONVERTER.getTimeStamp();
139         ObjectCreationNotification notification = new ObjectCreationNotificationBuilder()
140                 .setObjectIdRef(nodeId.getValue()).setCounter(popEvntNumber()).setTimeStamp(ts).build();
141         Connectionlog log = new ConnectionlogBuilder().setNodeId(nodeId.getValue())
142                 .setStatus(ConnectionLogStatus.Mounted).setTimestamp(ts).build();
143
144         NetworkElementConnectionEntity e = NetworkElementConnectionEntitiyUtil.getNetworkConnection(nodeId.getValue(),
145                 nNode, getNnodeConfig(nodeId));
146         LOG.debug("registration networkelement-connection for {} with status {}", nodeId.getValue(), e.getStatus());
147
148         // Write first to prevent missing entries
149         databaseService.updateNetworkConnection22(e, nodeId.getValue());
150         databaseService.writeConnectionLog(log);
151         webSocketService.sendViaWebsockets(new NodeId(ownKeyName), notification, ObjectCreationNotification.QNAME,
152                 NetconfTimeStampImpl.getConverter().getTimeStamp());
153     }
154
155     private Optional<NetconfNode> getNnodeConfig(NodeId nodeId) {
156         if (this.dataBroker != null) {
157
158             InstanceIdentifier<NetconfNode> iif = InstanceIdentifier.create(NetworkTopology.class)
159                     .child(Topology.class, new TopologyKey(new TopologyId(TopologyNetconf.QNAME.getLocalName())))
160                     .child(Node.class, new NodeKey(nodeId)).augmentation(NetconfNode.class);
161
162             //Implicit close of try with resource is not handled correctly by underlying opendaylight NETCONF service
163             @NonNull
164             ReadTransaction readTransaction = this.dataBroker.newReadOnlyTransaction();
165             try {
166                 return readTransaction.read(LogicalDatastoreType.CONFIGURATION, iif).get();
167             } catch (InterruptedException e) {
168                 LOG.warn("InterruptedException occurred - problem requesting netconfnode again:", e);
169                 Thread.currentThread().interrupt();
170             } catch (ExecutionException e) {
171                 LOG.warn("ExecutionException occurred - problem requesting netconfnode again:", e);
172             }
173         }
174         return Optional.empty();
175     }
176
177     /**
178      * (Connected) mountpoint state moves to connected
179      *
180      * @param mountpointNodeName uuid that is nodeId or mountpointId
181      * @param deviceType according to assessement
182      */
183     @Override
184     public void connectIndication(NodeId nNodeId, NetworkElementDeviceType deviceType) {
185
186         // Write first to prevent missing entries
187         LOG.debug("updating networkelement-connection devicetype for {} with {}", nNodeId.getValue(), deviceType);
188         NetworkElementConnectionEntity e =
189                 NetworkElementConnectionEntitiyUtil.getNetworkConnectionDeviceTpe(deviceType);
190         //if updating db entry for ne connection fails retry later on (due elasticsearch max script executions error)
191         if (!databaseService.updateNetworkConnectionDeviceType(e, nNodeId.getValue())) {
192             this.updateNeConnectionRetryWithDelay(e, nNodeId.getValue());
193         }
194         DateAndTime ts = NETCONFTIME_CONVERTER.getTimeStamp();
195         AttributeValueChangedNotification notification = new AttributeValueChangedNotificationBuilder()
196                 .setCounter(popEvntNumber()).setTimeStamp(ts).setObjectIdRef(nNodeId.getValue())
197                 .setAttributeName("deviceType").setNewValue(deviceType.getName()).build();
198         webSocketService.sendViaWebsockets(new NodeId(ownKeyName), notification,
199                 AttributeValueChangedNotification.QNAME, ts);
200     }
201
202     /**
203      * (NonConnected) mountpoint state changed.
204      *
205      * @param mountpointNodeName nodeid
206      * @param netconfNode node
207      */
208     public void onStateChangeIndication(NodeId nodeId, NetconfNode netconfNode) {
209         LOG.debug("mountpoint state changed indication for {}", nodeId.getValue());
210         ConnectionStatus csts = netconfNode.getConnectionStatus();
211         this.updateRegistration(nodeId, ConnectionStatus.class.getSimpleName(), csts != null ? csts.getName() : "null",
212                 netconfNode);
213
214     }
215
216     /**
217      * (NonConnected) A deregistration after removal of a mountpoint occured.
218      *
219      * @param registrationName Name of the event that is used as key in the database.
220      */
221     @SuppressWarnings("null")
222     @Override
223     public void deRegistration(NodeId nodeId) {
224
225         DateAndTime ts = NETCONFTIME_CONVERTER.getTimeStamp();
226         ObjectDeletionNotification notification = new ObjectDeletionNotificationBuilder().setCounter(popEvntNumber())
227                 .setTimeStamp(ts).setObjectIdRef(nodeId.getValue()).build();
228         Connectionlog log = new ConnectionlogBuilder().setNodeId(nodeId.getValue())
229                 .setStatus(ConnectionLogStatus.Unmounted).setTimestamp(ts).build();
230         // Write first to prevent missing entries
231         databaseService.removeNetworkConnection(nodeId.getValue());
232         databaseService.writeConnectionLog(log);
233         webSocketService.sendViaWebsockets(new NodeId(ownKeyName), notification,
234                 ObjectDeletionNotification.QNAME, ts);
235
236     }
237
238     /**
239      * Mountpoint state changed .. from connected -> connecting or unable-to-connect or vis-e-versa.
240      *
241      * @param registrationName Name of the event that is used as key in the database.
242      */
243     @Override
244     public void updateRegistration(NodeId nodeId, String attribute, String attributeNewValue, NetconfNode nNode) {
245         DateAndTime ts = NETCONFTIME_CONVERTER.getTimeStamp();
246         AttributeValueChangedNotification notification = new AttributeValueChangedNotificationBuilder()
247                 .setCounter(popEvntNumber()).setTimeStamp(ts).setObjectIdRef(nodeId.getValue())
248                 .setAttributeName(attribute).setNewValue(attributeNewValue).build();
249         Connectionlog log = new ConnectionlogBuilder().setNodeId(nodeId.getValue())
250                 .setStatus(getStatus(attributeNewValue)).setTimestamp(ts).build();
251         NetworkElementConnectionEntity e = NetworkElementConnectionEntitiyUtil.getNetworkConnection(nodeId.getValue(),
252                 nNode, getNnodeConfig(nodeId));
253         LOG.debug("updating networkelement-connection for {} with status {}", nodeId.getValue(), e.getStatus());
254
255         //if updating db entry for ne connection fails retry later on (due elasticsearch max script executions error)
256         if (!databaseService.updateNetworkConnection22(e, nodeId.getValue())) {
257             this.updateNeConnectionRetryWithDelay(nNode, nodeId.getValue());
258         }
259         databaseService.writeConnectionLog(log);
260         webSocketService.sendViaWebsockets(new NodeId(ownKeyName), notification,
261                 AttributeValueChangedNotification.QNAME, ts);
262     }
263
264
265     private void updateNeConnectionRetryWithDelay(NetconfNode nNode, String registrationName) {
266         LOG.debug("try to rewrite networkelement-connection in {} for node {}", DBWRITE_RETRY_DELAY_MS,
267                 registrationName);
268         executor.execute(new DelayedThread(DBWRITE_RETRY_DELAY_MS) {
269             @Override
270             public void run() {
271                 super.run();
272                 databaseService.updateNetworkConnection22(
273                         NetworkElementConnectionEntitiyUtil.getNetworkConnection(registrationName, nNode),
274                         registrationName);
275             }
276         });
277     }
278
279     private void updateNeConnectionRetryWithDelay(NetworkElementConnectionEntity e, String registrationName) {
280         LOG.debug("try to rewrite networkelement-connection in {} for node {}", DBWRITE_RETRY_DELAY_MS,
281                 registrationName);
282         executor.execute(new DelayedThread(DBWRITE_RETRY_DELAY_MS) {
283             @Override
284             public void run() {
285                 super.run();
286                 databaseService.updateNetworkConnection22(e, registrationName);
287             }
288         });
289     }
290
291     /**
292      * At a mountpoint a problem situation is indicated
293      *
294      * @param registrationName indicating object within SDN controller, normally the mountpointName
295      * @param problemName that changed
296      * @param problemSeverity of the problem according to NETCONF/YANG
297      */
298
299     public void onProblemNotification(String registrationName, String problemName, InternalSeverity problemSeverity) {
300         LOG.debug("Got event of {} {} {}", registrationName, problemName, problemSeverity);
301         // notification
302
303         ProblemNotificationXml notificationXml =
304                 new ProblemNotificationXml(ownKeyName, registrationName, problemName, problemSeverity,
305                         // popEvntNumberAsString(), InternalDateAndTime.TESTPATTERN );
306                         popEvntNumber(), InternalDateAndTime.valueOf(NETCONFTIME_CONVERTER.getTimeStamp()));
307         DateAndTime ts = NETCONFTIME_CONVERTER.getTimeStamp();
308         ProblemNotification notification =
309                 new ProblemNotificationBuilder().setObjectIdRef(registrationName).setCounter(popEvntNumber())
310                         .setProblem(problemName).setSeverity(InternalSeverity.toYang(problemSeverity)).build();
311         databaseService.writeFaultLog(notificationXml.getFaultlog(SourceType.Controller));
312         databaseService.updateFaultCurrent(notificationXml.getFaultcurrent());
313
314         aotsDcaeForwarder.sendProblemNotificationUsingMaintenanceFilter(new NodeId(ownKeyName), notificationXml);
315
316         webSocketService.sendViaWebsockets(new NodeId(ownKeyName), notification, ProblemNotification.QNAME, ts);
317     }
318
319     @Override
320     public void writeEventLog(String objectId, String msg, String value) {
321
322         LOG.debug("Got startComplete");
323         EventlogBuilder eventlogBuilder = new EventlogBuilder();
324         eventlogBuilder.setNodeId(ownKeyName).setTimestamp(new DateAndTime(NETCONFTIME_CONVERTER.getTimeStamp()))
325                 .setObjectId(objectId).setAttributeName(msg).setNewValue(value).setCounter(popEvntNumber())
326                 .setSourceType(SourceType.Controller);
327         databaseService.writeEventLog(eventlogBuilder.build());
328
329     }
330
331     /*---------------------------------------------
332      * Handling of ODL Controller events
333      */
334
335     /**
336      * Called on exit to remove everything for a node from the current list.
337      *
338      * @param nodeName to remove all problems for
339      * @return Number of deleted objects
340      */
341     public int removeAllCurrentProblemsOfNode(String nodeName) {
342         return databaseService.clearFaultsCurrentOfNodeWithObjectId(ownKeyName, nodeName);
343     }
344
345     /*---------------------------------------------------------------
346      * Get/Set
347      */
348
349     /**
350      * @return the ownKeyName
351      */
352     public String getOwnKeyName() {
353         return ownKeyName;
354     }
355
356     @Override
357     public void close() throws Exception {
358         executor.shutdown();
359         executor.awaitTermination(DBWRITE_RETRY_DELAY_MS * 3, TimeUnit.SECONDS);
360     }
361
362     /*---------------------------------------------------------------
363      * Private
364      */
365     private Integer popEvntNumber() {
366         return eventNumber++;
367     }
368
369     private static ConnectionLogStatus getStatus(String newValue) {
370
371         if (newValue.equals(ConnectionStatus.Connected.getName())) {
372             return ConnectionLogStatus.Connected;
373
374         } else if (newValue.equals(ConnectionStatus.Connecting.getName())) {
375             return ConnectionLogStatus.Connecting;
376
377         } else if (newValue.equals(ConnectionStatus.UnableToConnect.getName())) {
378             return ConnectionLogStatus.UnableToConnect;
379
380         }
381         return ConnectionLogStatus.Undefined;
382     }
383
384     private class DelayedThread extends Thread {
385         private final long delay;
386
387         public DelayedThread(long delayms) {
388             this.delay = delayms;
389         }
390
391         @Override
392         public void run() {
393             try {
394                 Thread.sleep(this.delay);
395             } catch (InterruptedException e) {
396                 Thread.currentThread().interrupt();
397             }
398         }
399     }
400 }