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