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