5647c4a9cd15b46038a13ced943c099d681c9238
[netvirt.git] / vpnmanager / impl / src / main / java / org / opendaylight / netvirt / vpnmanager / VpnInterfaceOpListener.java
1 /*
2  * Copyright (c) 2015 - 2016 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 org.opendaylight.genius.infra.Datastore.CONFIGURATION;
11 import static org.opendaylight.genius.infra.Datastore.OPERATIONAL;
12
13 import com.google.common.base.Optional;
14 import java.util.ArrayList;
15 import java.util.Collections;
16 import java.util.List;
17 import java.util.concurrent.ExecutionException;
18 import java.util.concurrent.ExecutorService;
19 import java.util.concurrent.Executors;
20 import javax.annotation.PostConstruct;
21 import javax.inject.Inject;
22 import javax.inject.Singleton;
23 import org.eclipse.jdt.annotation.Nullable;
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.genius.datastoreutils.AsyncDataTreeChangeListenerBase;
27 import org.opendaylight.genius.infra.Datastore.Configuration;
28 import org.opendaylight.genius.infra.Datastore.Operational;
29 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
30 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
31 import org.opendaylight.genius.infra.TypedReadTransaction;
32 import org.opendaylight.genius.infra.TypedReadWriteTransaction;
33 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
34 import org.opendaylight.infrautils.utils.concurrent.LoggingFutures;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.AdjacenciesOp;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.VpnInterfaceOpData;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.prefix.to._interface.vpn.ids.Prefixes;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn._interface.op.data.VpnInterfaceOpDataEntry;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn._interface.op.data.VpnInterfaceOpDataEntryKey;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntry;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.to.vpn.id.VpnInstance;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.l3vpn.rev200204.adjacency.list.Adjacency;
43 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
46
47 @Singleton
48 public class VpnInterfaceOpListener extends AsyncDataTreeChangeListenerBase<VpnInterfaceOpDataEntry,
49                                                                      VpnInterfaceOpListener> {
50     private static final Logger LOG = LoggerFactory.getLogger(VpnInterfaceOpListener.class);
51     private final DataBroker dataBroker;
52     private final ManagedNewTransactionRunner txRunner;
53     private final VpnInterfaceManager vpnInterfaceManager;
54     private final VpnFootprintService vpnFootprintService;
55     private final JobCoordinator jobCoordinator;
56     private final ExecutorService executorService = Executors.newSingleThreadExecutor();
57     private final VpnUtil vpnUtil;
58
59     /*public VpnInterfaceOpListener(final DataBroker dataBroker) {
60         super(VpnInterface.class);
61         this.dataBroker = dataBroker;
62     }*/
63
64     @Inject
65     public VpnInterfaceOpListener(final DataBroker dataBroker, final VpnInterfaceManager vpnInterfaceManager,
66         final VpnFootprintService vpnFootprintService, final JobCoordinator jobCoordinator,
67                                   final VpnUtil vpnUtil) {
68         super(VpnInterfaceOpDataEntry.class, VpnInterfaceOpListener.class);
69         this.dataBroker = dataBroker;
70         this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
71         this.vpnInterfaceManager = vpnInterfaceManager;
72         this.vpnFootprintService = vpnFootprintService;
73         this.jobCoordinator = jobCoordinator;
74         this.vpnUtil = vpnUtil;
75     }
76
77     @PostConstruct
78     public void start() {
79         LOG.info("{} start", getClass().getSimpleName());
80         registerListener(LogicalDatastoreType.OPERATIONAL, dataBroker);
81     }
82
83     @Override
84     protected InstanceIdentifier<VpnInterfaceOpDataEntry> getWildCardPath() {
85         InstanceIdentifier<VpnInterfaceOpDataEntry> id = InstanceIdentifier.create(VpnInterfaceOpData.class
86                 ).child(VpnInterfaceOpDataEntry.class);
87         return id;
88     }
89
90     @Override
91     protected VpnInterfaceOpListener getDataTreeChangeListener() {
92         return VpnInterfaceOpListener.this;
93     }
94
95
96     @Override
97     protected void remove(final InstanceIdentifier<VpnInterfaceOpDataEntry> identifier,
98             final VpnInterfaceOpDataEntry del) {
99         final VpnInterfaceOpDataEntryKey key = identifier.firstKeyOf(VpnInterfaceOpDataEntry.class);
100         final String interfaceName = key.getName();
101         jobCoordinator.enqueueJob("VPNINTERFACE-" + interfaceName,
102             () -> Collections.singletonList(txRunner.callWithNewReadWriteTransactionAndSubmit(OPERATIONAL, tx -> {
103                 postProcessVpnInterfaceRemoval(identifier, del, tx, null);
104                 LOG.info("remove: Removed vpn operational data for interface {} on dpn {} vpn {}", del.getName(),
105                         del.getDpnId(), del.getVpnInstanceName());
106             })));
107     }
108
109     private void postProcessVpnInterfaceRemoval(InstanceIdentifier<VpnInterfaceOpDataEntry> identifier,
110             VpnInterfaceOpDataEntry del, @Nullable TypedReadWriteTransaction<Operational> operTx,
111             @Nullable TypedReadTransaction<Configuration> confTx) throws InterruptedException {
112         if (confTx == null) {
113             txRunner.callWithNewReadOnlyTransactionAndClose(CONFIGURATION,
114                 tx -> postProcessVpnInterfaceRemoval(identifier, del, operTx, tx));
115             return;
116         }
117         if (operTx == null) {
118             LoggingFutures.addErrorLogging(txRunner.callWithNewReadWriteTransactionAndSubmit(OPERATIONAL,
119                 tx -> postProcessVpnInterfaceRemoval(identifier, del, tx, confTx)), LOG,
120                 "Error post-processing VPN interface removal");
121             return;
122         }
123         final VpnInterfaceOpDataEntryKey key = identifier.firstKeyOf(VpnInterfaceOpDataEntry.class);
124         String interfaceName = key.getName();
125         String vpnName = del.getVpnInstanceName();
126         try {
127             LOG.info("postProcessVpnInterfaceRemoval: interface name {} vpnName {} dpn {}", interfaceName, vpnName,
128                     del.getDpnId());
129             //decrement the vpn interface count in Vpn Instance Op Data
130             Optional<VpnInstance> vpnInstance =
131                 confTx.read(VpnOperDsUtils.getVpnInstanceToVpnIdIdentifier(vpnName)).get();
132
133             if (vpnInstance.isPresent()) {
134                 String rd = vpnInstance.get().getVrfId();
135
136                 VpnInstanceOpDataEntry vpnInstOp = vpnUtil.getVpnInstanceOpData(rd);
137
138                 AdjacenciesOp adjs = del.augmentation(AdjacenciesOp.class);
139                 List<Adjacency> adjList = adjs != null ? adjs.getAdjacency() : null;
140
141                 if (vpnInstOp != null && adjList != null && adjList.size() > 0) {
142                 /*
143                  * When a VPN Interface is removed by FibManager (aka VrfEntryListener and its cohorts),
144                  * one adjacency or two adjacency (in case of dual-stack)
145                  * for that VPN Interface will be hanging around along with that
146                  * VPN Interface.   That adjacency could be primary (or) non-primary.
147                  * If its a primary adjacency, then a prefix-to-interface entry will be available for the
148                  * same.  If its a non-primary adjacency, then a prefix-to-interface entry will not be
149                  * available for the same, instead we will have vpn-to-extraroutes filled in for them.
150                  *
151                  * Here we try to remove prefix-to-interface entry for pending adjacency in the deleted
152                  * vpnInterface.   More importantly, we also update the vpnInstanceOpData by removing this
153                  * vpnInterface from it.
154                  */
155                     List<Prefixes> prefixToInterface = new ArrayList<>();
156                     for (Adjacency adjacency : adjs.getAdjacency()) {
157                         List<Prefixes> prefixToInterfaceLocal = new ArrayList<>();
158                         Optional<Prefixes> prefix = operTx.read(
159                             VpnUtil.getPrefixToInterfaceIdentifier(vpnInstOp.getVpnId(),
160                                 VpnUtil.getIpPrefix(adjacency.getIpAddress()))).get();
161                         if (prefix.isPresent()) {
162                             prefixToInterfaceLocal.add(prefix.get());
163                         }
164                         if (prefixToInterfaceLocal.isEmpty() && adjacency.getNextHopIpList() != null) {
165                             for (String nh : adjacency.getNextHopIpList()) {
166                                 prefix = operTx.read(VpnUtil.getPrefixToInterfaceIdentifier(vpnInstOp.getVpnId(),
167                                     VpnUtil.getIpPrefix(nh))).get();
168                                 if (prefix.isPresent()) {
169                                     prefixToInterfaceLocal.add(prefix.get());
170                                 }
171                             }
172                         }
173                         if (!prefixToInterfaceLocal.isEmpty()) {
174                             prefixToInterface.addAll(prefixToInterfaceLocal);
175                         }
176                     }
177                 /*
178                  * In VPN Migration scenarios, there is a race condition where we use the new DPNID
179                  * for the migrated VM instead of old DPNID because when we read prefix-to-interface to cleanup
180                  * old DPNID, we actually get the new DPNID.
181                  *
182                  * More dangerously, we tend to alter the new prefix-to-interface which should be retained intac
183                  * for the migration to succeed in L3VPN.  As a workaround, here we are going to use the dpnId in
184                  * the deleted vpnInterface itself instead of tinkering with the prefix-to-interface.  Further we
185                  * will tinker prefix-to-interface only when are damn sure if its value matches our
186                  * deleted vpnInterface.
187                  *
188                  */
189                     for (Prefixes pref : prefixToInterface) {
190                         if (VpnUtil.isMatchedPrefixToInterface(pref, del)) {
191                             operTx.delete(
192                                 VpnUtil.getPrefixToInterfaceIdentifier(vpnInstOp.getVpnId(), pref.getIpAddress()));
193                         }
194                     }
195                 }
196                 if (del.getDpnId() != null) {
197                     vpnFootprintService.updateVpnToDpnMapping(del.getDpnId(), del.getVpnInstanceName(), rd,
198                             interfaceName, null /*ipAddressSourceValuePair*/,
199                             false /* do delete */);
200                 }
201                 LOG.info("postProcessVpnInterfaceRemoval: Removed vpn operational data and updated vpn footprint"
202                         + " for interface {} on dpn {} vpn {}", interfaceName, del.getDpnId(), vpnName);
203             } else {
204                 LOG.error("postProcessVpnInterfaceRemoval: rd not retrievable as vpninstancetovpnid for vpn {}"
205                         + " is absent, trying rd as {}. interface {} dpn {}", vpnName, vpnName, interfaceName,
206                         del.getDpnId());
207             }
208             notifyTaskIfRequired(interfaceName);
209         } catch (InterruptedException | ExecutionException e) {
210             LOG.error("postProcessVpnInterfaceRemoval: Failed to read data store for interface {} vpn {}",
211                     interfaceName, vpnName);
212         }
213     }
214
215     private void notifyTaskIfRequired(String intfName) {
216         Runnable notifyTask = vpnInterfaceManager.isNotifyTaskQueued(intfName);
217         if (notifyTask == null) {
218             LOG.debug("notifyTaskIfRequired: No tasks queued to wait for deletion of vpnInterface {}", intfName);
219             return;
220         }
221         executorService.execute(notifyTask);
222     }
223
224     @Override
225     protected void update(final InstanceIdentifier<VpnInterfaceOpDataEntry> identifier,
226             final VpnInterfaceOpDataEntry original, final VpnInterfaceOpDataEntry update) {
227         LOG.info("update: interface {} vpn {}", original.getName(), original.getVpnInstanceName());
228     }
229
230     @Override
231     protected void add(InstanceIdentifier<VpnInterfaceOpDataEntry> identifier, VpnInterfaceOpDataEntry add) {
232         LOG.info("add: interface {} vpn {}. Ignoring", add.getName(), add.getVpnInstanceName());
233     }
234 }