MRI version bump for Aluminium
[genius.git] / itm / itm-impl / src / main / java / org / opendaylight / genius / itm / monitoring / ItmTunnelEventListener.java
1 /*
2  * Copyright (c) 2016 Ericsson India Global Services Pvt Ltd. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.genius.itm.monitoring;
9
10 import com.google.common.util.concurrent.ListenableFuture;
11 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
12 import java.util.Collections;
13 import java.util.List;
14 import java.util.Objects;
15 import java.util.concurrent.Callable;
16 import javax.annotation.PreDestroy;
17 import javax.inject.Inject;
18 import javax.inject.Singleton;
19 import javax.management.JMException;
20 import org.eclipse.jdt.annotation.NonNull;
21 import org.opendaylight.genius.infra.Datastore;
22 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
23 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
24 import org.opendaylight.genius.itm.cache.UnprocessedTunnelsStateCache;
25 import org.opendaylight.genius.itm.globals.ITMConstants;
26 import org.opendaylight.genius.itm.impl.ItmUtils;
27 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
28 import org.opendaylight.mdsal.binding.api.DataBroker;
29 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
30 import org.opendaylight.serviceutils.tools.listener.AbstractSyncDataTreeChangeListener;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.TunnelOperStatus;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.TunnelsState;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.external.tunnel.list.ExternalTunnel;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.tunnel.list.InternalTunnel;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.tunnels_state.StateTunnelList;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.tunnels_state.StateTunnelListBuilder;
37 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
38 import org.opendaylight.yangtools.yang.common.Uint64;
39 import org.slf4j.Logger;
40 import org.slf4j.LoggerFactory;
41
42 @Singleton
43 public class ItmTunnelEventListener extends AbstractSyncDataTreeChangeListener<StateTunnelList> {
44
45     private static final Logger LOG = LoggerFactory.getLogger(ItmTunnelEventListener.class);
46
47     private final DataBroker broker;
48     private final JobCoordinator jobCoordinator;
49     private final ManagedNewTransactionRunner txRunner;
50     private JMXAlarmAgent alarmAgent;
51     private final UnprocessedTunnelsStateCache unprocessedTunnelsStateCache;
52
53     @Inject
54     public ItmTunnelEventListener(DataBroker dataBroker, JobCoordinator jobCoordinator,
55                                   UnprocessedTunnelsStateCache unprocessedTunnelsStateCache) {
56         super(dataBroker, LogicalDatastoreType.OPERATIONAL,
57               InstanceIdentifier.create(TunnelsState.class).child(StateTunnelList.class));
58         this.broker = dataBroker;
59         this.jobCoordinator = jobCoordinator;
60         this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
61         this.unprocessedTunnelsStateCache = unprocessedTunnelsStateCache;
62         try {
63             alarmAgent = new JMXAlarmAgent();
64             alarmAgent.registerMbean();
65         } catch (JMException e) {
66             LOG.error("Can not initialize the Alarm agent", e);
67         }
68     }
69
70     @SuppressWarnings("checkstyle:IllegalCatch")
71     @Override
72     @PreDestroy
73     public void close() {
74         try {
75             if (alarmAgent != null) {
76                 alarmAgent.unregisterMbean();
77             }
78         } catch (final Exception e) {
79             LOG.error("Error when cleaning up DataChangeListener.", e);
80         }
81     }
82
83     @Override
84     public void remove(@NonNull InstanceIdentifier<StateTunnelList> instanceIdentifier,
85                        @NonNull StateTunnelList stateTunnelList) {
86         LOG.trace("Tunnel Interface remove: {}", stateTunnelList.getTunnelInterfaceName());
87         ItmTunnelRemoveAlarmWorker itmTunnelRemoveAlarmWorker = new ItmTunnelRemoveAlarmWorker(stateTunnelList);
88         // For now, its all queued in one queue. If any delay in alarm being raised, queue based on interface Name
89         jobCoordinator.enqueueJob(ITMConstants.ITM_ALARM, itmTunnelRemoveAlarmWorker);
90         unprocessedTunnelsStateCache.remove(stateTunnelList.getTunnelInterfaceName());
91     }
92
93     @Override
94     public void update(@NonNull InstanceIdentifier<StateTunnelList> instanceIdentifier,
95                        @NonNull StateTunnelList originalTunnelList, @NonNull StateTunnelList updatedTunnelList) {
96         LOG.trace("Tunnel Interface updated. Old: {} New: {}", originalTunnelList, updatedTunnelList);
97         TunnelOperStatus operStatus = updatedTunnelList.getOperState();
98         if (!Objects.equals(originalTunnelList.getOperState(), updatedTunnelList.getOperState())) {
99             LOG.debug("Tunnel Interface {} changed state to {}", originalTunnelList.getTunnelInterfaceName(),
100                       operStatus);
101             ItmTunnelUpdateAlarmWorker itmTunnelUpdateAlarmWorker = new ItmTunnelUpdateAlarmWorker(originalTunnelList,
102                                                                                                    updatedTunnelList);
103             jobCoordinator.enqueueJob(ITMConstants.ITM_ALARM, itmTunnelUpdateAlarmWorker);
104         }
105     }
106
107     @Override
108     public void add(@NonNull InstanceIdentifier<StateTunnelList> instanceIdentifier,
109                     @NonNull StateTunnelList stateTunnelList) {
110         LOG.debug("Tunnel Interface of type Tunnel added: {}", stateTunnelList.getTunnelInterfaceName());
111         ItmTunnelAddAlarmWorker itmTunnelAddAlarmWorker = new ItmTunnelAddAlarmWorker(stateTunnelList);
112         // For now, its all queued in one queue. If any delay in alarm being raised, queue based on interface Name
113         jobCoordinator.enqueueJob(ITMConstants.ITM_ALARM, itmTunnelAddAlarmWorker);
114         TunnelOperStatus operStatus = unprocessedTunnelsStateCache.remove(stateTunnelList.getTunnelInterfaceName());
115         if (operStatus != null) {
116             if (operStatus != stateTunnelList.getOperState()) {
117                 jobCoordinator.enqueueJob(stateTunnelList.getTunnelInterfaceName(),
118                         new ItmTunnelStatusOutOfOrderEventWorker(instanceIdentifier, stateTunnelList, operStatus,
119                                 txRunner));
120             } else {
121                 LOG.debug("BFD status in unprocessed cache is the same as in DTCN for {} "
122                     + "hence no operations ",stateTunnelList.getTunnelInterfaceName());
123             }
124         } else {
125             LOG.debug("No Unprocessed tunnel state for {} ", stateTunnelList.getTunnelInterfaceName());
126         }
127     }
128
129     @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD",
130             justification = "https://github.com/spotbugs/spotbugs/issues/811")
131     private void raiseInternalDataPathAlarm(String srcDpnId, String dstDpnId, String tunnelType, String alarmText) {
132         StringBuilder source = new StringBuilder();
133         source.append("srcDpn=openflow:").append(srcDpnId).append("-dstDpn=openflow:").append(dstDpnId)
134                 .append("-tunnelType").append(tunnelType);
135
136         LOG.trace("Raising DataPathConnectionFailure alarm... alarmText {} source {} ", alarmText, source);
137         // Invokes JMX raiseAlarm method
138         if (alarmAgent != null) {
139             alarmAgent.invokeFMraisemethod("DataPathConnectionFailure", alarmText, source.toString());
140         }
141     }
142
143     @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD",
144             justification = "https://github.com/spotbugs/spotbugs/issues/811")
145     private void clearInternalDataPathAlarm(String srcDpnId, String dstDpnId, String tunnelType, String alarmText) {
146         StringBuilder source = new StringBuilder();
147
148         source.append("srcDpn=openflow:").append(srcDpnId).append("-dstDpn=openflow:").append(dstDpnId)
149                 .append("-tunnelType").append(tunnelType);
150         LOG.trace("Clearing DataPathConnectionFailure alarm of source {} alarmText {} ", source, alarmText);
151         // Invokes JMX clearAlarm method
152         if (alarmAgent != null) {
153             alarmAgent.invokeFMclearmethod("DataPathConnectionFailure", alarmText, source.toString());
154         }
155     }
156
157     @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD",
158             justification = "https://github.com/spotbugs/spotbugs/issues/811")
159     private void raiseExternalDataPathAlarm(String srcDevice, String dstDevice, String tunnelType, String alarmText) {
160
161         StringBuilder source = new StringBuilder();
162         source.append("srcDevice=").append(srcDevice).append("-dstDevice=").append(dstDevice).append("-tunnelType")
163                 .append(tunnelType);
164
165         LOG.trace("Raising DataPathConnectionFailure alarm... alarmText {} source {} ", alarmText, source);
166         // Invokes JMX raiseAlarm method
167         if (alarmAgent != null) {
168             alarmAgent.invokeFMraisemethod("DataPathConnectionFailure", alarmText, source.toString());
169         }
170     }
171
172     @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD",
173             justification = "https://github.com/spotbugs/spotbugs/issues/811")
174     private void clearExternalDataPathAlarm(String srcDevice, String dstDevice, String tunnelType, String alarmText) {
175         StringBuilder source = new StringBuilder();
176         source.append("srcDevice=").append(srcDevice).append("-dstDevice=").append(dstDevice).append("-tunnelType")
177                 .append(tunnelType);
178         LOG.trace("Clearing DataPathConnectionFailure alarm of source {} alarmText {} ", source, alarmText);
179         // Invokes JMX clearAlarm method
180         if (alarmAgent != null) {
181             alarmAgent.invokeFMclearmethod("DataPathConnectionFailure", alarmText, source.toString());
182         }
183     }
184
185     @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD",
186             justification = "https://github.com/spotbugs/spotbugs/issues/811")
187     private static boolean isTunnelInterfaceUp(StateTunnelList intf) {
188         return intf.getOperState() == TunnelOperStatus.Up;
189     }
190
191     @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD",
192             justification = "https://github.com/spotbugs/spotbugs/issues/811")
193     private static String getInternalAlarmText(String srcDpId, String dstDpId, String tunnelType) {
194         StringBuilder alarmText = new StringBuilder();
195         alarmText.append("Data Path Connectivity is lost between ").append("openflow:").append(srcDpId)
196                 .append(" and openflow:").append(dstDpId).append(" for tunnelType:").append(tunnelType);
197         return alarmText.toString();
198     }
199
200     @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD",
201             justification = "https://github.com/spotbugs/spotbugs/issues/811")
202     private static String getExternalAlarmText(String srcNode, String dstNode, String tunnelType) {
203         StringBuilder alarmText = new StringBuilder();
204         alarmText.append("Data Path Connectivity is lost between ").append(srcNode).append(" and ").append(dstNode)
205                 .append(" for tunnelType:").append(tunnelType);
206         return alarmText.toString();
207     }
208
209     private class ItmTunnelAddAlarmWorker implements Callable<List<? extends ListenableFuture<?>>> {
210         private final StateTunnelList add;
211
212         ItmTunnelAddAlarmWorker(StateTunnelList tnlIface) {
213             this.add = tnlIface;
214         }
215
216         @Override
217         public List<ListenableFuture<Void>> call() {
218             String ifName = add.getTunnelInterfaceName();
219             InternalTunnel internalTunnel = ItmUtils.getInternalTunnel(ifName, broker);
220             if (internalTunnel != null) {
221                 Uint64 srcDpId = internalTunnel.getSourceDPN();
222                 Uint64 dstDpId = internalTunnel.getDestinationDPN();
223                 String tunnelType = ItmUtils.convertTunnelTypetoString(internalTunnel.getTransportType());
224                 if (!isTunnelInterfaceUp(add)) {
225                     LOG.trace("ITM Tunnel State during tep add is DOWN b/w srcDpn: {} and dstDpn: {} for tunnelType: "
226                                       + "{}", srcDpId, dstDpId, tunnelType);
227                     String alarmText = getInternalAlarmText(srcDpId.toString(), dstDpId.toString(), tunnelType);
228                     raiseInternalDataPathAlarm(srcDpId.toString(), dstDpId.toString(), tunnelType, alarmText);
229                 }
230             } else {
231                 ExternalTunnel externalTunnel = ItmUtils.getExternalTunnel(ifName, broker);
232                 if (externalTunnel != null) {
233                     String srcNode = externalTunnel.getSourceDevice();
234                     String dstNode = externalTunnel.getDestinationDevice();
235                     if (!srcNode.contains("hwvtep")) {
236                         srcNode = "openflow:" + externalTunnel.getSourceDevice();
237                     }
238                     if (!dstNode.contains("hwvtep")) {
239                         dstNode = "openflow:" + externalTunnel.getDestinationDevice();
240                     }
241                     String tunnelType = ItmUtils.convertTunnelTypetoString(externalTunnel.getTransportType());
242                     if (!isTunnelInterfaceUp(add)) {
243                         LOG.trace("ITM Tunnel State during tep add is DOWN b/w srcNode: {} and dstNode: {} for "
244                                           + "tunnelType: {}", srcNode, dstNode, tunnelType);
245                         String alarmText = getExternalAlarmText(srcNode, dstNode, tunnelType);
246                         raiseExternalDataPathAlarm(srcNode, dstNode, tunnelType, alarmText);
247                     }
248                 }
249             }
250             return null;
251         }
252     }
253
254     private class ItmTunnelRemoveAlarmWorker implements Callable<List<? extends ListenableFuture<?>>> {
255         private final StateTunnelList del;
256
257         ItmTunnelRemoveAlarmWorker(StateTunnelList tnlIface) {
258             this.del = tnlIface;
259         }
260
261         @Override
262         public List<ListenableFuture<Void>> call() {
263             String ifName = del.getTunnelInterfaceName();
264             InternalTunnel internalTunnel = ItmUtils.getInternalTunnel(ifName, broker);
265             if (internalTunnel != null) {
266                 Uint64 srcDpId = internalTunnel.getSourceDPN();
267                 Uint64 dstDpId = internalTunnel.getDestinationDPN();
268                 String tunnelType = ItmUtils.convertTunnelTypetoString(internalTunnel.getTransportType());
269                 LOG.trace("ITM Tunnel removed b/w srcDpn: {} and dstDpn: {} for tunnelType: {}", srcDpId, dstDpId,
270                           tunnelType);
271                 String alarmText = getInternalAlarmText(srcDpId.toString(), dstDpId.toString(), tunnelType);
272                 clearInternalDataPathAlarm(srcDpId.toString(), dstDpId.toString(), tunnelType, alarmText);
273             } else {
274                 ExternalTunnel externalTunnel = ItmUtils.getExternalTunnel(ifName, broker);
275                 if (externalTunnel != null) {
276                     String srcNode = externalTunnel.getSourceDevice();
277                     String dstNode = externalTunnel.getDestinationDevice();
278                     String tunnelType = ItmUtils.convertTunnelTypetoString(externalTunnel.getTransportType());
279                     LOG.trace("ITM Tunnel removed b/w srcNode: {} and dstNode: {} for tunnelType: {}", srcNode, dstNode,
280                               tunnelType);
281                     String alarmText = getExternalAlarmText(srcNode, dstNode, tunnelType);
282                     clearExternalDataPathAlarm(srcNode, dstNode, tunnelType, alarmText);
283                 }
284             }
285             return null;
286         }
287     }
288
289     private class ItmTunnelUpdateAlarmWorker implements Callable<List<? extends ListenableFuture<?>>> {
290         private final StateTunnelList update;
291         private final StateTunnelList original;
292
293         ItmTunnelUpdateAlarmWorker(StateTunnelList original, StateTunnelList update) {
294             this.update = update;
295             this.original = original;
296         }
297
298         @Override
299         public List<ListenableFuture<Void>> call() {
300             String ifName = update.getTunnelInterfaceName();
301             InternalTunnel internalTunnel = ItmUtils.getInternalTunnel(ifName, broker);
302             if (internalTunnel != null) {
303                 Uint64 srcDpId = internalTunnel.getSourceDPN();
304                 Uint64 dstDpId = internalTunnel.getDestinationDPN();
305                 String tunnelType = ItmUtils.convertTunnelTypetoString(internalTunnel.getTransportType());
306                 if (LOG.isTraceEnabled()) {
307                     LOG.trace("ITM Tunnel state event changed from :{} to :{} for Tunnel Interface - {}",
308                               isTunnelInterfaceUp(original), isTunnelInterfaceUp(update), ifName);
309                 }
310                 switch (update.getOperState()) {
311                     case Up: {
312                         LOG.trace("ITM Tunnel State is UP b/w srcDpn: {} and dstDpn: {} for tunnelType {} ", srcDpId,
313                                 dstDpId, tunnelType);
314                         String alarmText = getInternalAlarmText(srcDpId.toString(), dstDpId.toString(), tunnelType);
315                         clearInternalDataPathAlarm(srcDpId.toString(), dstDpId.toString(), tunnelType, alarmText);
316                         break;
317                     }
318                     case Down: {
319                         LOG.trace("ITM Tunnel State is DOWN b/w srcDpn: {} and dstDpn: {}", srcDpId, dstDpId);
320                         String alarmText = getInternalAlarmText(srcDpId.toString(), dstDpId.toString(), tunnelType);
321                         raiseInternalDataPathAlarm(srcDpId.toString(), dstDpId.toString(), tunnelType, alarmText);
322                         break;
323                     }
324                     case Unknown:
325                     default:
326                         return null;
327                 }
328             }
329              /*else{
330                     // TODO: Uncomment this when tunnel towards DC gateway or HwVtep is supported
331                     ExternalTunnel externalTunnel = ItmUtils.getExternalTunnel(ifName,broker);
332                     if (externalTunnel != null) {
333                         String srcNode = externalTunnel.getSourceDevice();
334                         String dstNode = externalTunnel.getDestinationDevice();
335                         if (!srcNode.contains("hwvtep")){
336                             srcNode = "openflow:" + externalTunnel.getSourceDevice();
337                         }
338                         if (!dstNode.contains("hwvtep")){
339                             dstNode = "openflow:" + externalTunnel.getDestinationDevice();
340                         }
341                         String tunnelType = ItmUtils.convertTunnelTypetoString(externalTunnel.getTransportType());
342                         logger.trace("ITM Tunnel state event changed from :{} to :{} for Tunnel Interface - {}",
343                          isTunnelInterfaceUp(original), isTunnelInterfaceUp(update), ifName);
344                         if (isTunnelInterfaceUp(update)) {
345                             logger.trace("ITM Tunnel State is UP b/w srcNode: {} and dstNode: {} for tunnelType: {}",
346                              srcNode, dstNode, tunnelType);
347                             String alarmText = getExternalAlarmText(srcNode, dstNode, tunnelType);
348                             clearExternalDataPathAlarm(srcNode, dstNode, tunnelType, alarmText);
349                         }else {
350                             logger.trace("ITM Tunnel State is DOWN b/w srcNode: {} and dstNode: {}", srcNode, dstNode);
351                             String alarmText = getExternalAlarmText(srcNode, dstNode, tunnelType);
352                             raiseExternalDataPathAlarm(srcNode, dstNode, tunnelType, alarmText);
353                         }
354                     }
355                 }*/
356             return null;
357         }
358     }
359
360     private static class ItmTunnelStatusOutOfOrderEventWorker implements Callable<List<? extends ListenableFuture<?>>> {
361         private final InstanceIdentifier<StateTunnelList> identifier;
362         private final StateTunnelList add;
363         private final TunnelOperStatus operStatus;
364         private final ManagedNewTransactionRunner txRunner;
365
366         ItmTunnelStatusOutOfOrderEventWorker(InstanceIdentifier<StateTunnelList> identifier, StateTunnelList add,
367                                              TunnelOperStatus operStatus,
368                                              ManagedNewTransactionRunner tx) {
369             this.identifier = identifier;
370             this.add = add;
371             this.operStatus = operStatus;
372             this.txRunner = tx;
373         }
374
375         @Override
376         public List<ListenableFuture<Void>> call() throws Exception {
377             // Process any unprocessed interface bfd updates
378             LOG.debug(" Tunnel events are processed out order for {} hence updating it from cache",
379                     add.getTunnelInterfaceName());
380             return Collections.singletonList(txRunner
381                 .callWithNewWriteOnlyTransactionAndSubmit(Datastore.OPERATIONAL,
382                     tx -> tx.mergeParentStructureMerge(identifier,
383                         new StateTunnelListBuilder(add).setOperState(operStatus).build())));
384         }
385     }
386 }