c360afec4b8af092c77f6a5e39b0eededb7ae3bb
[genius.git] / interfacemanager / interfacemanager-impl / src / main / java / org / opendaylight / genius / interfacemanager / commons / AlivenessMonitorUtils.java
1 /*
2  * Copyright (c) 2016, 2017 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.interfacemanager.commons;
9
10 import com.google.common.base.Optional;
11 import com.google.common.util.concurrent.ListenableFuture;
12 import java.util.ArrayList;
13 import java.util.List;
14 import java.util.concurrent.ExecutionException;
15 import java.util.concurrent.Future;
16 import javax.inject.Inject;
17 import javax.inject.Singleton;
18 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
19 import org.opendaylight.controller.md.sal.binding.api.ReadTransaction;
20 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
21 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
22 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
23 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
24 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
25 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
26 import org.opendaylight.infrautils.utils.concurrent.ListenableFutures;
27 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.alivenessmonitor.rev160411.AlivenessMonitorService;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.alivenessmonitor.rev160411.EtherTypes;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.alivenessmonitor.rev160411.MonitorProfileCreateInput;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.alivenessmonitor.rev160411.MonitorProfileCreateInputBuilder;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.alivenessmonitor.rev160411.MonitorProfileCreateOutput;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.alivenessmonitor.rev160411.MonitorProfileDeleteInput;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.alivenessmonitor.rev160411.MonitorProfileDeleteInputBuilder;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.alivenessmonitor.rev160411.MonitorProfileDeleteOutput;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.alivenessmonitor.rev160411.MonitorProfileGetInput;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.alivenessmonitor.rev160411.MonitorProfileGetInputBuilder;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.alivenessmonitor.rev160411.MonitorProfileGetOutput;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.alivenessmonitor.rev160411.MonitorStartInput;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.alivenessmonitor.rev160411.MonitorStartInputBuilder;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.alivenessmonitor.rev160411.MonitorStartOutput;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.alivenessmonitor.rev160411.MonitorStopInput;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.alivenessmonitor.rev160411.MonitorStopInputBuilder;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.alivenessmonitor.rev160411.MonitorStopOutput;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.alivenessmonitor.rev160411.MonitoringMode;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.alivenessmonitor.rev160411.monitor.params.SourceBuilder;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.alivenessmonitor.rev160411.monitor.profile.create.input.Profile;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.alivenessmonitor.rev160411.monitor.profile.create.input.ProfileBuilder;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.alivenessmonitor.rev160411.monitor.start.input.ConfigBuilder;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.meta.rev160406.InterfaceMonitorIdMap;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.meta.rev160406.MonitorIdInterfaceMap;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.meta.rev160406._interface.monitor.id.map.InterfaceMonitorId;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.meta.rev160406._interface.monitor.id.map.InterfaceMonitorIdBuilder;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.meta.rev160406._interface.monitor.id.map.InterfaceMonitorIdKey;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.meta.rev160406.monitor.id._interface.map.MonitorIdInterface;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.meta.rev160406.monitor.id._interface.map.MonitorIdInterfaceBuilder;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.meta.rev160406.monitor.id._interface.map.MonitorIdInterfaceKey;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.IfTunnel;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelMonitoringTypeLldp;
60 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
61 import org.opendaylight.yangtools.yang.common.RpcResult;
62 import org.slf4j.Logger;
63 import org.slf4j.LoggerFactory;
64
65 @Singleton
66 public final class AlivenessMonitorUtils {
67
68     private static final Logger LOG = LoggerFactory.getLogger(AlivenessMonitorUtils.class);
69     private static final long FAILURE_THRESHOLD = 4;
70     private static final long MONITORING_WINDOW = 4;
71
72     private final AlivenessMonitorService alivenessMonitorService;
73     private final ManagedNewTransactionRunner txRunner;
74
75     @Inject
76     public AlivenessMonitorUtils(AlivenessMonitorService alivenessMonitor, DataBroker dataBroker) {
77         this.alivenessMonitorService = alivenessMonitor;
78         this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
79     }
80
81     public void startLLDPMonitoring(IfTunnel ifTunnel, String trunkInterfaceName) {
82         // LLDP monitoring for the tunnel interface
83         if (lldpMonitoringEnabled(ifTunnel)) {
84             MonitorStartInput lldpMonitorInput = new MonitorStartInputBuilder()
85                     .setConfig(new ConfigBuilder()
86                             .setSource(new SourceBuilder()
87                                     .setEndpointType(
88                                             getInterfaceForMonitoring(trunkInterfaceName, ifTunnel.getTunnelSource()))
89                                     .build())
90                             .setMode(MonitoringMode.OneOne)
91                             .setProfileId(allocateProfile(FAILURE_THRESHOLD,
92                                     ifTunnel.getMonitorInterval(), MONITORING_WINDOW, EtherTypes.Lldp))
93                             .build())
94                     .build();
95             try {
96                 Future<RpcResult<MonitorStartOutput>> result = alivenessMonitorService.monitorStart(lldpMonitorInput);
97                 RpcResult<MonitorStartOutput> rpcResult = result.get();
98                 if (rpcResult.isSuccessful()) {
99                     long monitorId = rpcResult.getResult().getMonitorId();
100                     ListenableFutures.addErrorLogging(txRunner.callWithNewReadWriteTransactionAndSubmit(tx -> {
101                         createOrUpdateInterfaceMonitorIdMap(tx, trunkInterfaceName, monitorId);
102                         createOrUpdateMonitorIdInterfaceMap(tx, trunkInterfaceName, monitorId);
103                         LOG.trace("Started LLDP monitoring with id {}", monitorId);
104                     }), LOG, "Error starting monitoring");
105                 } else {
106                     LOG.warn("RPC Call to start monitoring returned with Errors {}", rpcResult.getErrors());
107                 }
108             } catch (InterruptedException | ExecutionException e) {
109                 LOG.warn("Exception when starting monitoring", e);
110             }
111         }
112     }
113
114     public void stopLLDPMonitoring(IfTunnel ifTunnel, String trunkInterface) {
115         if (!lldpMonitoringEnabled(ifTunnel)) {
116             return;
117         }
118         LOG.debug("stop LLDP monitoring for {}", trunkInterface);
119         ListenableFutures.addErrorLogging(txRunner.callWithNewReadWriteTransactionAndSubmit(tx -> {
120             List<Long> monitorIds = getMonitorIdForInterface(tx, trunkInterface);
121             if (monitorIds == null) {
122                 LOG.error("Monitor Id doesn't exist for Interface {}", trunkInterface);
123                 return;
124             }
125             for (Long monitorId : monitorIds) {
126                 String interfaceName = getInterfaceFromMonitorId(tx, monitorId);
127                 if (interfaceName != null) {
128                     MonitorStopInput input = new MonitorStopInputBuilder().setMonitorId(monitorId).build();
129
130                     ListenableFuture<RpcResult<MonitorStopOutput>> future = alivenessMonitorService.monitorStop(input);
131                     ListenableFutures.addErrorLogging(future, LOG, "Stop LLDP monitoring for {}", trunkInterface);
132
133                     removeMonitorIdInterfaceMap(tx, monitorId);
134                     removeMonitorIdFromInterfaceMonitorIdMap(tx, interfaceName, monitorId);
135                     return;
136                 }
137             }
138         }), LOG, "Error stopping LLDP monitoring for {}", trunkInterface);
139     }
140
141     public static String getInterfaceFromMonitorId(ReadTransaction tx, Long monitorId) throws ReadFailedException {
142         InstanceIdentifier<MonitorIdInterface> id = InstanceIdentifier.builder(MonitorIdInterfaceMap.class)
143                 .child(MonitorIdInterface.class, new MonitorIdInterfaceKey(monitorId)).build();
144         return tx.read(LogicalDatastoreType.OPERATIONAL, id).checkedGet()
145                 .toJavaUtil().map(MonitorIdInterface::getInterfaceName).orElse(null);
146     }
147
148     private void removeMonitorIdInterfaceMap(ReadWriteTransaction tx, long monitorId) throws ReadFailedException {
149         InstanceIdentifier<MonitorIdInterface> id = InstanceIdentifier.builder(MonitorIdInterfaceMap.class)
150                 .child(MonitorIdInterface.class, new MonitorIdInterfaceKey(monitorId)).build();
151         if (tx.read(LogicalDatastoreType.OPERATIONAL, id).checkedGet().isPresent()) {
152             tx.delete(LogicalDatastoreType.OPERATIONAL, id);
153         }
154     }
155
156     private void removeMonitorIdFromInterfaceMonitorIdMap(ReadWriteTransaction tx, String infName, long monitorId)
157             throws ReadFailedException {
158         InstanceIdentifier<InterfaceMonitorId> id = InstanceIdentifier.builder(InterfaceMonitorIdMap.class)
159                 .child(InterfaceMonitorId.class, new InterfaceMonitorIdKey(infName)).build();
160         Optional<InterfaceMonitorId> interfaceMonitorIdMap = tx.read(LogicalDatastoreType.OPERATIONAL, id).checkedGet();
161         if (interfaceMonitorIdMap.isPresent()) {
162             InterfaceMonitorId interfaceMonitorIdInstance = interfaceMonitorIdMap.get();
163             List<Long> existingMonitorIds = interfaceMonitorIdInstance.getMonitorId();
164             if (existingMonitorIds != null && existingMonitorIds.contains(monitorId)) {
165                 existingMonitorIds.remove(monitorId);
166                 InterfaceMonitorIdBuilder interfaceMonitorIdBuilder = new InterfaceMonitorIdBuilder();
167                 interfaceMonitorIdInstance = interfaceMonitorIdBuilder.withKey(new InterfaceMonitorIdKey(infName))
168                         .setMonitorId(existingMonitorIds).build();
169                 tx.merge(LogicalDatastoreType.OPERATIONAL, id, interfaceMonitorIdInstance,
170                         WriteTransaction.CREATE_MISSING_PARENTS);
171             }
172         }
173     }
174
175     private static org.opendaylight.yang.gen.v1
176         .urn.opendaylight.genius.alivenessmonitor
177         .rev160411.endpoint.endpoint.type.Interface getInterfaceForMonitoring(
178             String interfaceName,
179             org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress ipAddress) {
180         return new org.opendaylight.yang.gen.v1.urn.opendaylight
181                 .genius.alivenessmonitor.rev160411.endpoint.endpoint.type.InterfaceBuilder()
182                 .setInterfaceIp(ipAddress).setInterfaceName(interfaceName).build();
183     }
184
185     public void handleTunnelMonitorUpdates(Interface interfaceOld, Interface interfaceNew) {
186         String interfaceName = interfaceNew.getName();
187         IfTunnel ifTunnelNew = interfaceNew.augmentation(IfTunnel.class);
188         if (!lldpMonitoringEnabled(ifTunnelNew)) {
189             return;
190         }
191         LOG.debug("handling tunnel monitoring updates for interface {}", interfaceName);
192
193         stopLLDPMonitoring(ifTunnelNew, interfaceOld.getName());
194         if (ifTunnelNew.isMonitorEnabled()) {
195             startLLDPMonitoring(ifTunnelNew, interfaceName);
196
197             // Delete old profile from Aliveness Manager
198             IfTunnel ifTunnelOld = interfaceOld.augmentation(IfTunnel.class);
199             if (!ifTunnelNew.getMonitorInterval().equals(ifTunnelOld.getMonitorInterval())) {
200                 LOG.debug("deleting older monitor profile for interface {}", interfaceName);
201                 long profileId = allocateProfile(FAILURE_THRESHOLD, ifTunnelOld.getMonitorInterval(), MONITORING_WINDOW,
202                         EtherTypes.Lldp);
203                 MonitorProfileDeleteInput profileDeleteInput = new MonitorProfileDeleteInputBuilder()
204                         .setProfileId(profileId).build();
205
206                 ListenableFuture<RpcResult<MonitorProfileDeleteOutput>> future =
207                         alivenessMonitorService.monitorProfileDelete(profileDeleteInput);
208                 ListenableFutures.addErrorLogging(future, LOG, "Delete monitor profile {}", interfaceName);
209             }
210         }
211     }
212
213     private static void createOrUpdateInterfaceMonitorIdMap(ReadWriteTransaction tx, String infName, long monitorId)
214             throws ReadFailedException {
215         InterfaceMonitorId interfaceMonitorIdInstance;
216         List<Long> existingMonitorIds;
217         InterfaceMonitorIdBuilder interfaceMonitorIdBuilder = new InterfaceMonitorIdBuilder();
218         InstanceIdentifier<InterfaceMonitorId> id = InstanceIdentifier.builder(InterfaceMonitorIdMap.class)
219                 .child(InterfaceMonitorId.class, new InterfaceMonitorIdKey(infName)).build();
220         Optional<InterfaceMonitorId> interfaceMonitorIdMap =
221                 tx.read(LogicalDatastoreType.OPERATIONAL, id).checkedGet();
222         if (interfaceMonitorIdMap.isPresent()) {
223             interfaceMonitorIdInstance = interfaceMonitorIdMap.get();
224             existingMonitorIds = interfaceMonitorIdInstance.getMonitorId();
225             if (existingMonitorIds == null) {
226                 existingMonitorIds = new ArrayList<>();
227             }
228             if (!existingMonitorIds.contains(monitorId)) {
229                 existingMonitorIds.add(monitorId);
230                 interfaceMonitorIdInstance = interfaceMonitorIdBuilder.withKey(new InterfaceMonitorIdKey(infName))
231                         .setMonitorId(existingMonitorIds).build();
232                 tx.merge(LogicalDatastoreType.OPERATIONAL, id, interfaceMonitorIdInstance,
233                         WriteTransaction.CREATE_MISSING_PARENTS);
234             }
235         } else {
236             existingMonitorIds = new ArrayList<>();
237             existingMonitorIds.add(monitorId);
238             interfaceMonitorIdInstance = interfaceMonitorIdBuilder.setMonitorId(existingMonitorIds)
239                     .withKey(new InterfaceMonitorIdKey(infName)).setInterfaceName(infName).build();
240             tx.merge(LogicalDatastoreType.OPERATIONAL, id, interfaceMonitorIdInstance,
241                     WriteTransaction.CREATE_MISSING_PARENTS);
242         }
243     }
244
245     private static void createOrUpdateMonitorIdInterfaceMap(ReadWriteTransaction tx, String infName, long monitorId)
246             throws ReadFailedException {
247         MonitorIdInterface monitorIdInterfaceInstance;
248         String existinginterfaceName;
249         MonitorIdInterfaceBuilder monitorIdInterfaceBuilder = new MonitorIdInterfaceBuilder();
250         InstanceIdentifier<MonitorIdInterface> id = InstanceIdentifier.builder(MonitorIdInterfaceMap.class)
251                 .child(MonitorIdInterface.class, new MonitorIdInterfaceKey(monitorId)).build();
252         Optional<MonitorIdInterface> monitorIdInterfaceMap =
253                 tx.read(LogicalDatastoreType.OPERATIONAL, id).checkedGet();
254         if (monitorIdInterfaceMap.isPresent()) {
255             monitorIdInterfaceInstance = monitorIdInterfaceMap.get();
256             existinginterfaceName = monitorIdInterfaceInstance.getInterfaceName();
257             if (!existinginterfaceName.equals(infName)) {
258                 monitorIdInterfaceInstance = monitorIdInterfaceBuilder.withKey(new MonitorIdInterfaceKey(monitorId))
259                         .setInterfaceName(infName).build();
260                 tx.merge(LogicalDatastoreType.OPERATIONAL, id, monitorIdInterfaceInstance,
261                         WriteTransaction.CREATE_MISSING_PARENTS);
262             }
263         } else {
264             monitorIdInterfaceInstance = monitorIdInterfaceBuilder.setMonitorId(monitorId)
265                     .withKey(new MonitorIdInterfaceKey(monitorId)).setInterfaceName(infName).build();
266             tx.merge(LogicalDatastoreType.OPERATIONAL, id, monitorIdInterfaceInstance,
267                     WriteTransaction.CREATE_MISSING_PARENTS);
268         }
269     }
270
271     private static List<Long> getMonitorIdForInterface(ReadTransaction tx, String infName) throws ReadFailedException {
272         InstanceIdentifier<InterfaceMonitorId> id = InstanceIdentifier.builder(InterfaceMonitorIdMap.class)
273                 .child(InterfaceMonitorId.class, new InterfaceMonitorIdKey(infName)).build();
274         return tx.read(LogicalDatastoreType.OPERATIONAL, id).checkedGet().toJavaUtil().map(
275                 InterfaceMonitorId::getMonitorId).orElse(null);
276     }
277
278     public long createMonitorProfile(MonitorProfileCreateInput monitorProfileCreateInput) {
279         try {
280             Future<RpcResult<MonitorProfileCreateOutput>> result = alivenessMonitorService
281                     .monitorProfileCreate(monitorProfileCreateInput);
282             RpcResult<MonitorProfileCreateOutput> rpcResult = result.get();
283             if (rpcResult.isSuccessful()) {
284                 return rpcResult.getResult().getProfileId();
285             } else {
286                 LOG.warn("RPC Call to Get Profile Id Id returned with Errors {}.. Trying to fetch existing profile ID",
287                         rpcResult.getErrors());
288                 Profile createProfile = monitorProfileCreateInput.getProfile();
289                 Future<RpcResult<MonitorProfileGetOutput>> existingProfile = alivenessMonitorService.monitorProfileGet(
290                         buildMonitorGetProfile(createProfile.getMonitorInterval(), createProfile.getMonitorWindow(),
291                                 createProfile.getFailureThreshold(), createProfile.getProtocolType()));
292                 RpcResult<MonitorProfileGetOutput> rpcGetResult = existingProfile.get();
293                 if (rpcGetResult.isSuccessful()) {
294                     return rpcGetResult.getResult().getProfileId();
295                 } else {
296                     LOG.warn("RPC Call to Get Existing Profile Id returned with Errors {}", rpcGetResult.getErrors());
297                 }
298             }
299         } catch (InterruptedException | ExecutionException e) {
300             LOG.warn("Exception when allocating profile Id", e);
301         }
302         return 0;
303     }
304
305     private static MonitorProfileGetInput buildMonitorGetProfile(long monitorInterval, long monitorWindow,
306             long failureThreshold, EtherTypes protocolType) {
307         org.opendaylight.yang.gen.v1.urn.opendaylight.genius.alivenessmonitor
308             .rev160411.monitor.profile.get.input.ProfileBuilder profileBuilder =
309             new org.opendaylight.yang.gen.v1.urn.opendaylight
310             .genius.alivenessmonitor.rev160411.monitor.profile.get.input.ProfileBuilder();
311
312         profileBuilder.setFailureThreshold(failureThreshold);
313         profileBuilder.setMonitorInterval(monitorInterval);
314         profileBuilder.setMonitorWindow(monitorWindow);
315         profileBuilder.setProtocolType(protocolType);
316         MonitorProfileGetInputBuilder buildGetProfile = new MonitorProfileGetInputBuilder();
317         buildGetProfile.setProfile(profileBuilder.build());
318         return buildGetProfile.build();
319     }
320
321     public long allocateProfile(long failureThreshold, long monitoringInterval, long monitoringWindow,
322             EtherTypes etherTypes) {
323         MonitorProfileCreateInput input = new MonitorProfileCreateInputBuilder().setProfile(
324                 new ProfileBuilder().setFailureThreshold(failureThreshold).setMonitorInterval(monitoringInterval)
325                         .setMonitorWindow(monitoringWindow).setProtocolType(etherTypes).build())
326                 .build();
327         return createMonitorProfile(input);
328     }
329
330     public static boolean lldpMonitoringEnabled(IfTunnel ifTunnel) {
331         return ifTunnel.isInternal() && ifTunnel.isMonitorEnabled()
332                 && TunnelMonitoringTypeLldp.class.isAssignableFrom(ifTunnel.getMonitorProtocol());
333     }
334 }