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