Freeze upstream versions
[netvirt.git] / vpnmanager / impl / src / main / java / org / opendaylight / netvirt / vpnmanager / VpnOpStatusListener.java
1 /*
2  * Copyright (c) 2015 - 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 static java.util.Collections.emptyList;
11
12 import com.google.common.util.concurrent.FutureCallback;
13 import com.google.common.util.concurrent.Futures;
14 import com.google.common.util.concurrent.ListenableFuture;
15 import com.google.common.util.concurrent.MoreExecutors;
16 import java.util.ArrayList;
17 import java.util.Collections;
18 import java.util.List;
19 import java.util.Map;
20 import java.util.Optional;
21 import java.util.concurrent.ExecutionException;
22 import java.util.concurrent.locks.ReentrantLock;
23 import javax.annotation.PreDestroy;
24 import javax.inject.Inject;
25 import javax.inject.Singleton;
26 import org.opendaylight.genius.datastoreutils.SingleTransactionDataBroker;
27 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
28 import org.opendaylight.genius.utils.JvmGlobalLocks;
29 import org.opendaylight.genius.utils.SystemPropertyReader;
30 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
31 import org.opendaylight.infrautils.utils.concurrent.Executors;
32 import org.opendaylight.mdsal.binding.api.DataBroker;
33 import org.opendaylight.mdsal.binding.util.Datastore;
34 import org.opendaylight.mdsal.binding.util.ManagedNewTransactionRunner;
35 import org.opendaylight.mdsal.binding.util.ManagedNewTransactionRunnerImpl;
36 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
37 import org.opendaylight.netvirt.bgpmanager.api.IBgpManager;
38 import org.opendaylight.netvirt.fibmanager.api.IFibManager;
39 import org.opendaylight.netvirt.vpnmanager.api.VpnExtraRouteHelper;
40 import org.opendaylight.serviceutils.tools.listener.AbstractAsyncDataTreeChangeListener;
41 import org.opendaylight.yang.gen.v1.urn.ericsson.params.xml.ns.yang.ebgp.rev150901.AddressFamily;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.L3nexthop;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.VpnNexthops;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.VpnNexthopsKey;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.VpnInstanceOpData;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.prefix.to._interface.VpnIds;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntry;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.vpntargets.VpnTarget;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.vpntargets.VpnTargetKey;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.to.extraroutes.Vpn;
52 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
53 import org.opendaylight.yangtools.yang.common.Uint32;
54 import org.slf4j.Logger;
55 import org.slf4j.LoggerFactory;
56
57 @Singleton
58 public class VpnOpStatusListener extends AbstractAsyncDataTreeChangeListener<VpnInstanceOpDataEntry> {
59     private static final Logger LOG = LoggerFactory.getLogger(VpnOpStatusListener.class);
60     private final DataBroker dataBroker;
61     private final ManagedNewTransactionRunner txRunner;
62     private final IBgpManager bgpManager;
63     private final IdManagerService idManager;
64     private final IFibManager fibManager;
65     private final IMdsalApiManager mdsalManager;
66     private final VpnFootprintService vpnFootprintService;
67     private final JobCoordinator jobCoordinator;
68     private final VpnUtil vpnUtil;
69
70     @Inject
71     public VpnOpStatusListener(final DataBroker dataBroker, final IBgpManager bgpManager,
72                                final IdManagerService idManager, final IFibManager fibManager,
73                                final IMdsalApiManager mdsalManager, final VpnFootprintService vpnFootprintService,
74                                final JobCoordinator jobCoordinator, VpnUtil vpnUtil) {
75         super(dataBroker, LogicalDatastoreType.OPERATIONAL, InstanceIdentifier.create(VpnInstanceOpData.class)
76                 .child(VpnInstanceOpDataEntry.class), Executors
77                 .newListeningSingleThreadExecutor("VpnOpStatusListener", LOG));
78         this.dataBroker = dataBroker;
79         this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
80         this.bgpManager = bgpManager;
81         this.idManager = idManager;
82         this.fibManager = fibManager;
83         this.mdsalManager = mdsalManager;
84         this.vpnFootprintService = vpnFootprintService;
85         this.jobCoordinator = jobCoordinator;
86         this.vpnUtil = vpnUtil;
87         start();
88     }
89
90     public void start() {
91         LOG.info("{} start", getClass().getSimpleName());
92     }
93
94     @Override
95     @PreDestroy
96     public void close() {
97         super.close();
98         Executors.shutdownAndAwaitTermination(getExecutorService());
99     }
100
101
102     @Override
103     public void remove(InstanceIdentifier<VpnInstanceOpDataEntry> identifier, VpnInstanceOpDataEntry value) {
104         LOG.info("remove: Ignoring vpn Op {} with rd {}", value.getVpnInstanceName(), value.getVrfId());
105     }
106
107     @Override
108     @SuppressWarnings("checkstyle:IllegalCatch")
109     public void update(InstanceIdentifier<VpnInstanceOpDataEntry> identifier,
110                        VpnInstanceOpDataEntry original, VpnInstanceOpDataEntry update) {
111         LOG.info("update: Processing update for vpn {} with rd {}", update.getVpnInstanceName(), update.getVrfId());
112         if (update.getVpnState() == VpnInstanceOpDataEntry.VpnState.PendingDelete
113                 && vpnFootprintService.isVpnFootPrintCleared(update)) {
114             //Cleanup VPN data
115             final String vpnName = update.getVpnInstanceName();
116             final List<String> rds = update.getRd();
117             String primaryRd = update.getVrfId();
118             final Uint32 vpnId = vpnUtil.getVpnId(vpnName);
119             jobCoordinator.enqueueJob("VPN-" + update.getVpnInstanceName(), () -> {
120                 // Two transactions are used, one for operational, one for config; we only submit the config
121                 // transaction if the operational transaction succeeds
122                 ListenableFuture<?> operationalFuture = txRunner.callWithNewWriteOnlyTransactionAndSubmit(
123                         Datastore.OPERATIONAL, operTx -> {
124                             // Clean up VPNExtraRoutes Operational DS
125                         if (rds != null && VpnUtil.isBgpVpn(vpnName, primaryRd)) {
126                             if (update.getType() == VpnInstanceOpDataEntry.Type.L2) {
127                                 rds.parallelStream().forEach(rd -> bgpManager.deleteVrf(
128                                             rd, false, AddressFamily.L2VPN));
129                             }
130                             if (update.getIpAddressFamilyConfigured()
131                                         == VpnInstanceOpDataEntry.IpAddressFamilyConfigured.Ipv4) {
132                                 rds.parallelStream().forEach(rd -> bgpManager.deleteVrf(
133                                             rd, false, AddressFamily.IPV4));
134                             }
135                             if (update.getIpAddressFamilyConfigured()
136                                         == VpnInstanceOpDataEntry.IpAddressFamilyConfigured.Ipv6) {
137                                 rds.parallelStream().forEach(rd -> bgpManager.deleteVrf(
138                                             rd, false, AddressFamily.IPV6));
139                             }
140                             if (update.getIpAddressFamilyConfigured()
141                                         == VpnInstanceOpDataEntry.IpAddressFamilyConfigured.Ipv4AndIpv6) {
142                                 rds.parallelStream()
143                                             .forEach(rd -> bgpManager.deleteVrf(
144                                                     rd, false, AddressFamily.IPV4));
145                                 rds.parallelStream()
146                                             .forEach(rd -> bgpManager.deleteVrf(
147                                                     rd, false, AddressFamily.IPV6));
148                             }
149                         }
150                         InstanceIdentifier<Vpn> vpnToExtraroute =
151                                 VpnExtraRouteHelper.getVpnToExtrarouteVpnIdentifier(vpnName);
152                         Optional<Vpn> optVpnToExtraroute = Optional.empty();
153                         try {
154                             optVpnToExtraroute = SingleTransactionDataBroker.syncReadOptional(dataBroker,
155                                         LogicalDatastoreType.OPERATIONAL, vpnToExtraroute);
156                         } catch (InterruptedException | ExecutionException e) {
157                             LOG.error("update: Failed to read VpnToExtraRoute for vpn {}", vpnName);
158                         }
159                         if (optVpnToExtraroute.isPresent()) {
160                             VpnUtil.removeVpnExtraRouteForVpn(vpnName, operTx);
161                         }
162                         if (VpnUtil.isL3VpnOverVxLan(update.getL3vni())) {
163                             vpnUtil.removeExternalTunnelDemuxFlows(vpnName);
164                         }
165                             // Clean up PrefixToInterface Operational DS
166                         Optional<VpnIds> optPrefixToIntf = Optional.empty();
167                         try {
168                             optPrefixToIntf = SingleTransactionDataBroker.syncReadOptional(dataBroker,
169                                     LogicalDatastoreType.OPERATIONAL, VpnUtil.getPrefixToInterfaceIdentifier(vpnId));
170                         } catch (InterruptedException | ExecutionException e) {
171                             LOG.error("update: Failed to read PrefixToInterface for vpn {}", vpnName);
172                         }
173                         if (optPrefixToIntf.isPresent()) {
174                             VpnUtil.removePrefixToInterfaceForVpnId(vpnId, operTx);
175                         }
176                         // Clean up L3NextHop Operational DS
177                         InstanceIdentifier<VpnNexthops> vpnNextHops = InstanceIdentifier.builder(L3nexthop.class).child(
178                                 VpnNexthops.class, new VpnNexthopsKey(vpnId)).build();
179                         Optional<VpnNexthops> optL3nexthopForVpnId = Optional.empty();
180                         try {
181                             optL3nexthopForVpnId = SingleTransactionDataBroker.syncReadOptional(dataBroker,
182                                     LogicalDatastoreType.OPERATIONAL, vpnNextHops);
183                         } catch (InterruptedException | ExecutionException e) {
184                             LOG.error("update: Failed to read VpnNextHops for vpn {}", vpnName);
185                         }
186                         if (optL3nexthopForVpnId.isPresent()) {
187                             VpnUtil.removeL3nexthopForVpnId(vpnId, operTx);
188                         }
189
190                         // Clean up VPNInstanceOpDataEntry
191                         VpnUtil.removeVpnOpInstance(primaryRd, operTx);
192                     });
193
194                 Futures.addCallback(operationalFuture, new FutureCallback<Object>() {
195                     @Override
196                     public void onSuccess(Object result) {
197                         Futures.addCallback(txRunner.callWithNewWriteOnlyTransactionAndSubmit(
198                                 Datastore.CONFIGURATION, confTx -> {
199                                     // Clean up VpnInstanceToVpnId from Config DS
200                                 VpnUtil.removeVpnIdToVpnInstance(vpnId, confTx);
201                                 VpnUtil.removeVpnInstanceToVpnId(vpnName, confTx);
202                                 LOG.trace("Removed vpnIdentifier for  rd{} vpnname {}", primaryRd, vpnName);
203
204                                 // Clean up FIB Entries Config DS
205                                 // FIXME: separate out to somehow?
206                                 final ReentrantLock lock = JvmGlobalLocks.getLockForString(vpnName);
207                                 lock.lock();
208                                 try {
209                                     fibManager.removeVrfTable(primaryRd, confTx);
210                                 } finally {
211                                     lock.unlock();
212                                 }
213                             }), new VpnOpStatusListener.PostDeleteVpnInstanceWorker(vpnName),
214                                 MoreExecutors.directExecutor());
215                         // Note: Release the of VpnId will happen in PostDeleteVpnInstancWorker only if
216                         // operationalTxn/Config succeeds.
217                     }
218
219                     @Override
220                     public void onFailure(Throwable throwable) {
221                         LOG.error("Error deleting VPN {}", vpnName, throwable);
222                     }
223                 }, MoreExecutors.directExecutor());
224
225                 LOG.info("Removed vpn data for vpnname {}", vpnName);
226                 return Collections.singletonList(operationalFuture);
227             }, SystemPropertyReader.getDataStoreJobCoordinatorMaxRetries());
228         } else if (update.getVpnState() == VpnInstanceOpDataEntry.VpnState.Created) {
229             final String vpnName = update.getVpnInstanceName();
230             String primaryRd = update.getVrfId();
231             if (!VpnUtil.isBgpVpn(vpnName, primaryRd)) {
232                 return;
233             }
234             if (original == null) {
235                 LOG.error("VpnOpStatusListener.update: vpn {} with RD {}. add() handler already called",
236                         vpnName, primaryRd);
237                 return;
238             }
239             if (update.getVpnTargets() == null) {
240                 LOG.error("VpnOpStatusListener.update: vpn {} with RD {} vpnTargets not ready",
241                         vpnName, primaryRd);
242                 return;
243             }
244             Map<VpnTargetKey, VpnTarget> vpnTargetMap = update.getVpnTargets().getVpnTarget();
245             List<String> ertList = new ArrayList<>();
246             List<String> irtList = new ArrayList<>();
247             if (vpnTargetMap != null) {
248                 for (VpnTarget vpnTarget : vpnTargetMap.values()) {
249                     if (vpnTarget.getVrfRTType() == VpnTarget.VrfRTType.ExportExtcommunity) {
250                         ertList.add(vpnTarget.getVrfRTValue());
251                     }
252                     if (vpnTarget.getVrfRTType() == VpnTarget.VrfRTType.ImportExtcommunity) {
253                         irtList.add(vpnTarget.getVrfRTValue());
254                     }
255                     if (vpnTarget.getVrfRTType() == VpnTarget.VrfRTType.Both) {
256                         ertList.add(vpnTarget.getVrfRTValue());
257                         irtList.add(vpnTarget.getVrfRTValue());
258                     }
259                 }
260             } else {
261                 LOG.error("VpnOpStatusListener.update: vpn target list is empty, cannot add BGP"
262                         + " VPN {} RD {}", vpnName, primaryRd);
263                 return;
264             }
265             jobCoordinator.enqueueJob("VPN-" + update.getVpnInstanceName(), () -> {
266                 //RD update case get only updated RD list
267                 List<String> rds = update.getRd() != null ? new ArrayList<>(update.getRd()) : new ArrayList<>();
268                 if (original.getRd() != null && original.getRd().size() != rds.size()) {
269                     rds.removeAll(original.getRd());
270                 }
271                 rds.parallelStream().forEach(rd -> {
272                     try {
273                         List<String> importRTList = rd.equals(primaryRd) ? irtList : emptyList();
274                         LOG.info("VpnOpStatusListener.update: updating BGPVPN for vpn {} with RD {}"
275                                         + " Type is {}, IPtype is {}, iRT {}", vpnName, primaryRd, update.getType(),
276                                 update.getIpAddressFamilyConfigured(), importRTList);
277                         int ipValue = VpnUtil.getIpFamilyValueToRemove(original,update);
278                         switch (ipValue) {
279                             case 4:
280                                 bgpManager.deleteVrf(rd, false, AddressFamily.IPV4);
281                                 break;
282                             case 6:
283                                 bgpManager.deleteVrf(rd, false, AddressFamily.IPV6);
284                                 break;
285                             case 10:
286                                 bgpManager.deleteVrf(rd, false, AddressFamily.IPV4);
287                                 bgpManager.deleteVrf(rd, false, AddressFamily.IPV6);
288                                 break;
289                             default:
290                                 break;
291                         }
292                         /* Update vrf entry with newly added RD list. VPN does not support for
293                          * deleting existing RDs
294                          */
295                         if (original.getRd().size() != update.getRd().size()) {
296                             ipValue = VpnUtil.getIpFamilyValueToAdd(original,update);
297                             switch (ipValue) {
298                                 case 4:
299                                     bgpManager.addVrf(rd, importRTList, ertList, AddressFamily.IPV4);
300                                     break;
301                                 case 6:
302                                     bgpManager.addVrf(rd, importRTList, ertList, AddressFamily.IPV6);
303                                     break;
304                                 case 10:
305                                     bgpManager.addVrf(rd, importRTList, ertList, AddressFamily.IPV4);
306                                     bgpManager.addVrf(rd, importRTList, ertList, AddressFamily.IPV6);
307                                     break;
308                                 default:
309                                     break;
310                             }
311                         }
312                     } catch (RuntimeException e) {
313                         LOG.error("VpnOpStatusListener.update: Exception when updating VRF to BGP for vpn {} rd {}",
314                                 vpnName, rd, e);
315                     }
316                 });
317                 return emptyList();
318             });
319         }
320     }
321
322     @Override
323     public void add(final InstanceIdentifier<VpnInstanceOpDataEntry> identifier,
324                     final VpnInstanceOpDataEntry value) {
325         LOG.debug("add: Ignoring vpn Op {} with rd {}", value.getVpnInstanceName(), value.getVrfId());
326     }
327
328     private class PostDeleteVpnInstanceWorker implements FutureCallback<Object> {
329         private final Logger log = LoggerFactory.getLogger(VpnOpStatusListener.PostDeleteVpnInstanceWorker.class);
330         String vpnName;
331
332         PostDeleteVpnInstanceWorker(String vpnName)  {
333             this.vpnName = vpnName;
334         }
335
336         /**
337          * This implies that all the future instances have returned success.
338          * Release the ID used for VPN back to IdManager
339          */
340         @Override
341         public void onSuccess(Object ignored) {
342             Integer releaseId = vpnUtil.releaseId(VpnConstants.VPN_IDPOOL_NAME, vpnName);
343             log.info("onSuccess: VpnId for VpnName {} is released to IdManager successfully.", vpnName);
344             if (releaseId == VpnConstants.INVALID_IDMAN_ID) {
345                 LOG.error("VpnOpStatusListener: onSuccess: Failed to release ID for vpn {}, vpn id pool {}", vpnName,
346                         VpnConstants.VPN_IDPOOL_NAME);
347             }
348         }
349
350         /**
351          * This method is used to handle failure callbacks.
352          */
353         @Override
354         public void onFailure(Throwable throwable) {
355             log.error("onFailure: Job for vpnInstance: {} failed with exception:",
356                     vpnName , throwable);
357         }
358     }
359 }