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