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