Bug 7787 - missing flows in T21
[netvirt.git] / vpnservice / vpnmanager / vpnmanager-impl / src / main / java / org / opendaylight / netvirt / vpnmanager / TunnelInterfaceStateListener.java
1 /*
2  * Copyright © 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.netvirt.vpnmanager;
9
10 import com.google.common.base.Optional;
11 import com.google.common.util.concurrent.ListenableFuture;
12 import java.math.BigInteger;
13 import java.util.ArrayList;
14 import java.util.Collections;
15 import java.util.HashMap;
16 import java.util.Iterator;
17 import java.util.List;
18 import java.util.Map;
19 import java.util.concurrent.Callable;
20 import java.util.concurrent.Future;
21 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
22 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
23 import org.opendaylight.genius.datastoreutils.AsyncDataTreeChangeListenerBase;
24 import org.opendaylight.genius.datastoreutils.DataStoreJobCoordinator;
25 import org.opendaylight.netvirt.bgpmanager.api.IBgpManager;
26 import org.opendaylight.netvirt.fibmanager.api.IFibManager;
27 import org.opendaylight.netvirt.vpnmanager.utilities.InterfaceUtils;
28 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterface;
29 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetDpnInterfaceListInputBuilder;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetDpnInterfaceListOutput;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.OdlInterfaceRpcService;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.TepTypeExternal;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.TepTypeHwvtep;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.TepTypeInternal;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.TunnelOperStatus;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.TunnelsState;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.tunnels_state.StateTunnelList;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.ItmRpcService;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.Adjacencies;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.PortOpData;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.adjacency.list.Adjacency;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.port.op.data.PortOpDataEntry;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.port.op.data.PortOpDataEntryKey;
45 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
46 import org.opendaylight.yangtools.yang.common.RpcResult;
47 import org.slf4j.Logger;
48 import org.slf4j.LoggerFactory;
49
50 public class TunnelInterfaceStateListener extends AsyncDataTreeChangeListenerBase<StateTunnelList,
51     TunnelInterfaceStateListener> implements AutoCloseable {
52     private static final Logger LOG = LoggerFactory.getLogger(TunnelInterfaceStateListener.class);
53     private final DataBroker dataBroker;
54     private final IBgpManager bgpManager;
55     private final IFibManager fibManager;
56     private final ItmRpcService itmRpcService;
57     private OdlInterfaceRpcService intfRpcService;
58     private VpnInterfaceManager vpnInterfaceManager;
59     private VpnSubnetRouteHandler vpnSubnetRouteHandler;
60
61     protected enum UpdateRouteAction {
62         ADVERTISE_ROUTE, WITHDRAW_ROUTE
63     }
64
65     protected enum TunnelAction {
66         TUNNEL_EP_ADD,
67         TUNNEL_EP_DELETE,
68         TUNNEL_EP_UPDATE
69     }
70
71     /**
72      * Responsible for listening to tunnel interface state change.
73      *
74      * @param dataBroker dataBroker
75      * @param bgpManager bgpManager
76      * @param fibManager fibManager
77      * @param itmRpcService itmRpcService
78      */
79     public TunnelInterfaceStateListener(final DataBroker dataBroker,
80         final IBgpManager bgpManager,
81         final IFibManager fibManager,
82         final ItmRpcService itmRpcService,
83         final OdlInterfaceRpcService ifaceMgrRpcService,
84         final VpnInterfaceManager vpnInterfaceManager,
85         final VpnSubnetRouteHandler vpnSubnetRouteHandler) {
86         super(StateTunnelList.class, TunnelInterfaceStateListener.class);
87         this.dataBroker = dataBroker;
88         this.bgpManager = bgpManager;
89         this.fibManager = fibManager;
90         this.itmRpcService = itmRpcService;
91         this.intfRpcService = ifaceMgrRpcService;
92         this.vpnInterfaceManager = vpnInterfaceManager;
93         this.vpnSubnetRouteHandler = vpnSubnetRouteHandler;
94     }
95
96     public void start() {
97         LOG.info("{} start", getClass().getSimpleName());
98         registerListener(LogicalDatastoreType.OPERATIONAL, dataBroker);
99     }
100
101     @Override
102     protected InstanceIdentifier<StateTunnelList> getWildCardPath() {
103         return InstanceIdentifier.create(TunnelsState.class).child(StateTunnelList.class);
104     }
105
106     @Override
107     protected TunnelInterfaceStateListener getDataTreeChangeListener() {
108         return TunnelInterfaceStateListener.this;
109     }
110
111     @Override
112     protected void remove(InstanceIdentifier<StateTunnelList> identifier, StateTunnelList del) {
113         LOG.trace("Tunnel deletion---- {}", del);
114         handleTunnelEventForDPN(del, UpdateRouteAction.WITHDRAW_ROUTE, TunnelAction.TUNNEL_EP_DELETE);
115     }
116
117     @Override
118     protected void update(InstanceIdentifier<StateTunnelList> identifier, StateTunnelList original,
119         StateTunnelList update) {
120     }
121
122     @Override
123     protected void add(InstanceIdentifier<StateTunnelList> identifier, StateTunnelList add) {
124         LOG.trace("Tunnel addition---- {}", add);
125         TunnelOperStatus tunOpStatus = add.getOperState();
126         if ((tunOpStatus != TunnelOperStatus.Down) && (tunOpStatus != TunnelOperStatus.Up)) {
127             LOG.trace("Returning from unsupported tunnelOperStatus {}", tunOpStatus);
128             return;
129         }
130         if (tunOpStatus != TunnelOperStatus.Up) {
131             LOG.trace("Tunnel {} is not yet UP.", add.getTunnelInterfaceName());
132         }
133         LOG.trace("ITM Tunnel ,type {} ,added between src: {} and dest: {}",
134             fibManager.getTransportTypeStr(add.getTransportType().toString()),
135             add.getSrcInfo().getTepDeviceId(), add.getDstInfo().getTepDeviceId());
136         handleTunnelEventForDPN(add, UpdateRouteAction.ADVERTISE_ROUTE, TunnelAction.TUNNEL_EP_ADD);
137     }
138
139     // TODO Clean up the exception handling
140     @SuppressWarnings("checkstyle:IllegalCatch")
141     private void handleTunnelEventForDPN(StateTunnelList stateTunnelList, UpdateRouteAction action,
142         TunnelAction tunnelAction) {
143         final BigInteger srcDpnId = new BigInteger(stateTunnelList.getSrcInfo().getTepDeviceId());
144         final String srcTepIp = String.valueOf(stateTunnelList.getSrcInfo().getTepIp().getValue());
145         String destTepIp = String.valueOf(stateTunnelList.getDstInfo().getTepIp().getValue());
146         String rd;
147         BigInteger remoteDpnId = null;
148         boolean isTepDeletedOnDpn = false;
149
150         LOG.trace("Handle tunnel event for srcDpn {} SrcTepIp {} DestTepIp {} ", srcDpnId, srcTepIp, destTepIp);
151         int tunTypeVal = getTunnelType(stateTunnelList);
152
153         LOG.trace("tunTypeVal is {}", tunTypeVal);
154
155         try {
156             if (tunnelAction == TunnelAction.TUNNEL_EP_ADD) {
157                 LOG.trace(" Tunnel ADD event received for Dpn {} VTEP Ip {} ", srcDpnId, srcTepIp);
158             } else if (tunnelAction == TunnelAction.TUNNEL_EP_DELETE) {
159                 LOG.trace(" Tunnel DELETE event received for Dpn {} VTEP Ip {} ", srcDpnId, srcTepIp);
160                 // When tunnel EP is deleted on a DPN , VPN gets two deletion event.
161                 // One for a DPN on which tunnel EP was deleted and another for other-end DPN.
162                 // Update the adj for the vpninterfaces for a DPN on which TEP is deleted.
163                 // Update the adj & VRF for the vpninterfaces for a DPN on which TEP is deleted.
164                 // Dont update the adj & VRF for vpninterfaces for a DPN on which TEP is not deleted.
165                 String endpointIpForDPN = null;
166                 try {
167                     endpointIpForDPN = InterfaceUtils.getEndpointIpAddressForDPN(dataBroker, srcDpnId);
168                 } catch (Exception e) {
169                     /* this dpn does not have the VTEP */
170                     endpointIpForDPN = null;
171                 }
172
173                 if (endpointIpForDPN == null) {
174                     LOG.trace("Tunnel TEP is deleted on Dpn {} VTEP Ip {}", srcDpnId, srcTepIp);
175                     isTepDeletedOnDpn = true;
176                 }
177             }
178
179             // Get the list of VpnInterfaces from Intf Mgr for a SrcDPN on which TEP is added/deleted
180             Future<RpcResult<GetDpnInterfaceListOutput>> result;
181             List<String> srcDpninterfacelist = new ArrayList<>();
182             List<String> destDpninterfacelist = new ArrayList<>();
183             try {
184                 result =
185                     intfRpcService.getDpnInterfaceList(new GetDpnInterfaceListInputBuilder().setDpid(srcDpnId).build());
186                 RpcResult<GetDpnInterfaceListOutput> rpcResult = result.get();
187                 if (!rpcResult.isSuccessful()) {
188                     LOG.warn("RPC Call to GetDpnInterfaceList for dpnid {} returned with Errors {}", srcDpnId,
189                         rpcResult.getErrors());
190                 } else {
191                     srcDpninterfacelist = rpcResult.getResult().getInterfacesList();
192                 }
193             } catch (Exception e) {
194                 LOG.warn("Exception when querying for GetDpnInterfaceList for dpnid {}", srcDpnId, e);
195             }
196
197             // Get the list of VpnInterfaces from Intf Mgr for a destDPN only for internal tunnel.
198             if (tunTypeVal == VpnConstants.ITMTunnelLocType.Internal.getValue()) {
199                 remoteDpnId = new BigInteger(stateTunnelList.getDstInfo().getTepDeviceId());
200                 try {
201                     result = intfRpcService.getDpnInterfaceList(
202                         new GetDpnInterfaceListInputBuilder().setDpid(remoteDpnId).build());
203                     RpcResult<GetDpnInterfaceListOutput> rpcResult = result.get();
204                     if (!rpcResult.isSuccessful()) {
205                         LOG.warn("RPC Call to GetDpnInterfaceList for dpnid {} returned with Errors {}", srcDpnId,
206                             rpcResult.getErrors());
207                     } else {
208                         destDpninterfacelist = rpcResult.getResult().getInterfacesList();
209                     }
210                 } catch (Exception e) {
211                     LOG.warn("Exception when querying for GetDpnInterfaceList for dpnid {}", srcDpnId, e);
212                 }
213             }
214
215             /*
216              * Iterate over the list of VpnInterface for a SrcDpn on which TEP is added or deleted and read the adj.
217              * Update the adjacencies with the updated nexthop.
218              */
219             Iterator<String> interfacelistIter = srcDpninterfacelist.iterator();
220             String intfName = null;
221             List<Uuid> subnetList = new ArrayList<>();
222             Map<Long, String> vpnIdRdMap = new HashMap<>();
223
224             while (interfacelistIter.hasNext()) {
225                 intfName = interfacelistIter.next();
226                 final VpnInterface vpnInterface = VpnUtil.getOperationalVpnInterface(dataBroker, intfName);
227                 if (vpnInterface != null) {
228
229                     DataStoreJobCoordinator dataStoreCoordinator = DataStoreJobCoordinator.getInstance();
230                     dataStoreCoordinator.enqueueJob("VPNINTERFACE-" + intfName,
231                             new UpdateVpnInterfaceOnTunnelEvent(dataBroker,
232                                     vpnInterfaceManager,
233                                     tunnelAction,
234                                     vpnInterface,
235                                     stateTunnelList,
236                                     isTepDeletedOnDpn));
237
238                     // Populate the List of subnets
239                     InstanceIdentifier<PortOpDataEntry> portOpIdentifier =
240                         InstanceIdentifier.builder(PortOpData.class).child(PortOpDataEntry.class,
241                             new PortOpDataEntryKey(intfName)).build();
242                     Optional<PortOpDataEntry> optionalPortOp =
243                         VpnUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, portOpIdentifier);
244                     if (optionalPortOp.isPresent()) {
245                         Uuid subnetId = optionalPortOp.get().getSubnetId();
246                         if (!subnetList.contains(subnetId)) {
247                             subnetList.add(subnetId);
248                         }
249                     }
250                     //Populate the map for VpnId-to-Rd
251                     long vpnId = VpnUtil.getVpnId(dataBroker, vpnInterface.getVpnInstanceName());
252                     rd = VpnUtil.getVpnRd(dataBroker, vpnInterface.getVpnInstanceName());
253                     vpnIdRdMap.put(vpnId, rd);
254                 }
255             }
256
257             /*
258              * Iterate over the list of VpnInterface for destDPN and get the prefix .
259              * Create remote rule for each of those prefix on srcDPN.
260              */
261             interfacelistIter = destDpninterfacelist.iterator();
262             while (interfacelistIter.hasNext()) {
263                 intfName = interfacelistIter.next();
264                 final VpnInterface vpnInterface = VpnUtil.getOperationalVpnInterface(dataBroker, intfName);
265                 if (vpnInterface != null) {
266                     Adjacencies adjacencies = vpnInterface.getAugmentation(Adjacencies.class);
267                     List<Adjacency> adjList = adjacencies != null ? adjacencies.getAdjacency()
268                             : Collections.emptyList();
269                     String prefix = null;
270                     long vpnId = VpnUtil.getVpnId(dataBroker, vpnInterface.getVpnInstanceName());
271                     if (vpnIdRdMap.containsKey(vpnId)) {
272                         rd = vpnIdRdMap.get(vpnId);
273                         LOG.trace(" Remote DpnId {} VpnId {} rd {} VpnInterface {}", remoteDpnId, vpnId, rd,
274                             vpnInterface);
275                         for (Adjacency adj : adjList) {
276                             prefix = adj.getIpAddress();
277                             long label = adj.getLabel();
278                             if ((tunnelAction == TunnelAction.TUNNEL_EP_ADD)
279                                 && (tunTypeVal == VpnConstants.ITMTunnelLocType.Internal.getValue())) {
280                                 fibManager.manageRemoteRouteOnDPN(true, srcDpnId, vpnId, rd, prefix, destTepIp, label);
281                             }
282
283                             if ((tunnelAction == TunnelAction.TUNNEL_EP_DELETE)
284                                 && (tunTypeVal == VpnConstants.ITMTunnelLocType.Internal.getValue())) {
285                                 fibManager.manageRemoteRouteOnDPN(false, srcDpnId, vpnId, rd, prefix, destTepIp, label);
286                             }
287                         }
288                     }
289                 }
290             }
291
292             //Iterate over the VpnId-to-Rd map.
293             for (Map.Entry<Long, String> entry : vpnIdRdMap.entrySet()) {
294                 Long vpnId = entry.getKey();
295                 rd = entry.getValue();
296                 if ((tunnelAction == TunnelAction.TUNNEL_EP_ADD)
297                     && (tunTypeVal == VpnConstants.ITMTunnelLocType.External.getValue())) {
298                     fibManager.populateExternalRoutesOnDpn(srcDpnId, vpnId, rd, srcTepIp, destTepIp);
299                 } else if ((tunnelAction == TunnelAction.TUNNEL_EP_DELETE)
300                     && (tunTypeVal == VpnConstants.ITMTunnelLocType.External.getValue())) {
301                     fibManager.cleanUpExternalRoutesOnDpn(srcDpnId, vpnId, rd, srcTepIp, destTepIp);
302                 }
303             }
304
305             if (tunnelAction == TunnelAction.TUNNEL_EP_ADD) {
306                 for (Uuid subnetId : subnetList) {
307                     // Populate the List of subnets
308                     vpnSubnetRouteHandler.updateSubnetRouteOnTunnelUpEvent(subnetId, srcDpnId);
309                 }
310             }
311
312             if ((tunnelAction == TunnelAction.TUNNEL_EP_DELETE) && isTepDeletedOnDpn) {
313                 for (Uuid subnetId : subnetList) {
314                     // Populate the List of subnets
315                     vpnSubnetRouteHandler.updateSubnetRouteOnTunnelDownEvent(subnetId, srcDpnId);
316                 }
317             }
318         } catch (Exception e) {
319             LOG.error("Unable to handle the tunnel event.", e);
320             return;
321         }
322     }
323
324
325     private class UpdateVpnInterfaceOnTunnelEvent implements Callable {
326         private VpnInterface vpnInterface;
327         private StateTunnelList stateTunnelList;
328         private VpnInterfaceManager vpnInterfaceManager;
329         private DataBroker broker;
330         private TunnelAction tunnelAction;
331         private boolean isTepDeletedOnDpn;
332
333         UpdateVpnInterfaceOnTunnelEvent(DataBroker broker,
334             VpnInterfaceManager vpnInterfaceManager,
335             TunnelAction tunnelAction,
336             VpnInterface vpnInterface,
337             StateTunnelList stateTunnelList,
338             boolean isTepDeletedOnDpn) {
339             this.broker = broker;
340             this.vpnInterfaceManager = vpnInterfaceManager;
341             this.stateTunnelList = stateTunnelList;
342             this.vpnInterface = vpnInterface;
343             this.tunnelAction = tunnelAction;
344             this.isTepDeletedOnDpn = isTepDeletedOnDpn;
345         }
346
347         @Override
348         public List<ListenableFuture<Void>> call() throws Exception {
349
350             if (tunnelAction == TunnelAction.TUNNEL_EP_ADD) {
351                 vpnInterfaceManager.updateVpnInterfaceOnTepAdd(vpnInterface, stateTunnelList);
352             }
353
354             if ((tunnelAction == TunnelAction.TUNNEL_EP_DELETE) && isTepDeletedOnDpn) {
355                 vpnInterfaceManager.updateVpnInterfaceOnTepDelete(vpnInterface, stateTunnelList);
356             }
357
358             return null;
359         }
360     }
361
362     private int getTunnelType(StateTunnelList stateTunnelList) {
363         int tunTypeVal = 0;
364         if (stateTunnelList.getDstInfo().getTepDeviceType() == TepTypeInternal.class) {
365             tunTypeVal = VpnConstants.ITMTunnelLocType.Internal.getValue();
366         } else if (stateTunnelList.getDstInfo().getTepDeviceType() == TepTypeExternal.class) {
367             tunTypeVal = VpnConstants.ITMTunnelLocType.External.getValue();
368         } else if (stateTunnelList.getDstInfo().getTepDeviceType() == TepTypeHwvtep.class) {
369             tunTypeVal = VpnConstants.ITMTunnelLocType.Hwvtep.getValue();
370         } else {
371             tunTypeVal = VpnConstants.ITMTunnelLocType.Invalid.getValue();
372         }
373         return tunTypeVal;
374     }
375 }