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