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