Convert vpnmanager-impl to use blueprint annotations
[netvirt.git] / vpnservice / vpnmanager / 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 com.google.common.base.Optional;
11 import com.google.common.util.concurrent.ListenableFuture;
12 import java.util.ArrayList;
13 import java.util.List;
14 import java.util.concurrent.ExecutorService;
15 import java.util.concurrent.Executors;
16 import javax.annotation.PostConstruct;
17 import javax.inject.Inject;
18 import javax.inject.Singleton;
19 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
20 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
21 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
22 import org.opendaylight.genius.datastoreutils.AsyncDataTreeChangeListenerBase;
23 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
24 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnInterfaces;
25 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterface;
26 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterfaceKey;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.Adjacencies;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.adjacency.list.Adjacency;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.adjacency.list.Adjacency.AdjacencyType;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.prefix.to._interface.vpn.ids.Prefixes;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntry;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.to.vpn.id.VpnInstance;
33 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
36
37 @Singleton
38 public class VpnInterfaceOpListener extends AsyncDataTreeChangeListenerBase<VpnInterface, VpnInterfaceOpListener> {
39     private static final Logger LOG = LoggerFactory.getLogger(VpnInterfaceOpListener.class);
40     private final DataBroker dataBroker;
41     private final VpnInterfaceManager vpnInterfaceManager;
42     private final VpnFootprintService vpnFootprintService;
43     private final JobCoordinator jobCoordinator;
44     private final ExecutorService executorService = Executors.newSingleThreadExecutor();
45
46     /*public VpnInterfaceOpListener(final DataBroker dataBroker) {
47         super(VpnInterface.class);
48         this.dataBroker = dataBroker;
49     }*/
50
51     @Inject
52     public VpnInterfaceOpListener(final DataBroker dataBroker, final VpnInterfaceManager vpnInterfaceManager,
53         final VpnFootprintService vpnFootprintService, final JobCoordinator jobCoordinator) {
54         super(VpnInterface.class, VpnInterfaceOpListener.class);
55         this.dataBroker = dataBroker;
56         this.vpnInterfaceManager = vpnInterfaceManager;
57         this.vpnFootprintService = vpnFootprintService;
58         this.jobCoordinator = jobCoordinator;
59     }
60
61     @PostConstruct
62     public void start() {
63         LOG.info("{} start", getClass().getSimpleName());
64         registerListener(LogicalDatastoreType.OPERATIONAL, dataBroker);
65     }
66
67     @Override
68     protected InstanceIdentifier<VpnInterface> getWildCardPath() {
69         return InstanceIdentifier.create(VpnInterfaces.class).child(VpnInterface.class);
70     }
71
72     @Override
73     protected VpnInterfaceOpListener getDataTreeChangeListener() {
74         return VpnInterfaceOpListener.this;
75     }
76
77
78     @Override
79     protected void remove(final InstanceIdentifier<VpnInterface> identifier, final VpnInterface del) {
80         final VpnInterfaceKey key = identifier.firstKeyOf(VpnInterface.class, VpnInterfaceKey.class);
81         final String interfaceName = key.getName();
82         jobCoordinator.enqueueJob("VPNINTERFACE-" + interfaceName,
83             () -> {
84                 WriteTransaction writeOperTxn = dataBroker.newWriteOnlyTransaction();
85                 postProcessVpnInterfaceRemoval(identifier, del, writeOperTxn);
86                 List<ListenableFuture<Void>> futures = new ArrayList<>();
87                 futures.add(writeOperTxn.submit());
88                 LOG.info("remove: Removed vpn operational data for interface {} on dpn {} vpn {}", del.getName(),
89                         del.getDpnId(), del.getVpnInstanceName());
90                 return futures;
91             });
92     }
93
94     private void postProcessVpnInterfaceRemoval(InstanceIdentifier<VpnInterface> identifier, VpnInterface del,
95         WriteTransaction writeOperTxn) {
96         final VpnInterfaceKey key = identifier.firstKeyOf(VpnInterface.class, VpnInterfaceKey.class);
97         String interfaceName = key.getName();
98         String vpnName = del.getVpnInstanceName();
99
100         LOG.info("postProcessVpnInterfaceRemoval: interface name {} vpnName {} dpn {}", interfaceName, vpnName,
101                 del.getDpnId());
102         //decrement the vpn interface count in Vpn Instance Op Data
103         Optional<VpnInstance> vpnInstance = VpnUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION,
104                                                          VpnOperDsUtils.getVpnInstanceToVpnIdIdentifier(vpnName));
105
106         if (vpnInstance.isPresent()) {
107             String rd = null;
108             rd = vpnInstance.get().getVrfId();
109             //String rd = getRouteDistinguisher(del.getVpnInstanceName());
110
111             VpnInstanceOpDataEntry vpnInstOp = VpnUtil.getVpnInstanceOpData(dataBroker, rd);
112
113             Adjacencies adjs = del.getAugmentation(Adjacencies.class);
114             List<Adjacency> adjList = adjs != null ? adjs.getAdjacency() : null;
115
116             if (vpnInstOp != null && adjList != null && adjList.size() > 0) {
117                 /*
118                  * When a VPN Interface is removed by FibManager (aka VrfEntryListener and its cohorts),
119                  * one adjacency for that VPN Interface will be hanging around along with that
120                  * VPN Interface.   That adjacency could be primary (or) non-primary.
121                  * If its a primary adjacency, then a prefix-to-interface entry will be available for the
122                  * same.  If its a non-primary adjacency, then a prefix-to-interface entry will not be
123                  * available for the same, instead we will have vpn-to-extraroutes filled in for them.
124                  *
125                  * Here we try to remove prefix-to-interface entry for pending adjacency in the deleted
126                  * vpnInterface.   More importantly, we also update the vpnInstanceOpData by removing this
127                  * vpnInterface from it.
128                  */
129                 // TODO(vivek) # It is not yet clear, where we are cleaning up the prefix-to-interface
130                 // TODO(vivek) # for primary adjacencies and that has to be fixed.
131                 List<Prefixes> prefixToInterface = new ArrayList<>();
132                 for (Adjacency adjacency : adjs.getAdjacency()) {
133                     List<Prefixes> prefixToInterfaceLocal = new ArrayList<>();
134                     if (adjacency.getAdjacencyType() != AdjacencyType.PrimaryAdjacency) {
135                         continue;
136                     }
137                     Optional<Prefixes> prefix = VpnUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL,
138                         VpnUtil.getPrefixToInterfaceIdentifier(vpnInstOp.getVpnId(),
139                             VpnUtil.getIpPrefix(adjacency.getIpAddress())));
140                     if (prefix.isPresent()) {
141                         prefixToInterfaceLocal.add(prefix.get());
142                     }
143                     if (prefixToInterfaceLocal.isEmpty()) {
144                         for (String nh : adjacency.getNextHopIpList()) {
145                             prefix = VpnUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL,
146                                 VpnUtil.getPrefixToInterfaceIdentifier(vpnInstOp.getVpnId(),
147                                     VpnUtil.getIpPrefix(nh)));
148                             if (prefix.isPresent()) {
149                                 prefixToInterfaceLocal.add(prefix.get());
150                             }
151                         }
152                     }
153                     if (!prefixToInterfaceLocal.isEmpty()) {
154                         prefixToInterface.addAll(prefixToInterfaceLocal);
155                     }
156                 }
157                 /*
158                  * In VPN Migration scenarios, there is a race condition where we use the new DPNID
159                  * for the migrated VM instead of old DPNID because when we read prefix-to-interface to cleanup
160                  * old DPNID, we actually get the new DPNID.
161                  *
162                  * More dangerously, we tend to alter the new prefix-to-interface which should be retained intac
163                  * for the migration to succeed in L3VPN.  As a workaround, here we are going to use the dpnId in
164                  * the deleted vpnInterface itself instead of tinkering with the prefix-to-interface.  Further we
165                  * will tinker prefix-to-interface only when are damn sure if its value matches our
166                  * deleted vpnInterface.
167                  *
168                  */
169                 for (Prefixes pref : prefixToInterface) {
170                     if (isMatchedPrefixToInterface(pref, del)) {
171                         if (writeOperTxn != null) {
172                             writeOperTxn.delete(LogicalDatastoreType.OPERATIONAL,
173                                     VpnUtil.getPrefixToInterfaceIdentifier(vpnInstOp.getVpnId(), pref.getIpAddress()));
174                         } else {
175                             VpnUtil.delete(dataBroker, LogicalDatastoreType.OPERATIONAL,
176                                     VpnUtil.getPrefixToInterfaceIdentifier(vpnInstOp.getVpnId(), pref.getIpAddress()),
177                                     VpnUtil.DEFAULT_CALLBACK);
178                         }
179                     }
180                 }
181             }
182             if (del.getDpnId() != null) {
183                 vpnFootprintService.updateVpnToDpnMapping(del.getDpnId(), del.getVpnInstanceName(), rd,
184                         interfaceName, null /*ipAddressSourceValuePair*/,
185                         false /* do delete */);
186             }
187             LOG.info("postProcessVpnInterfaceRemoval: Removed vpn operational data and updated vpn footprint"
188                     + " for interface {} on dpn {} vpn {}", interfaceName, del.getDpnId(), vpnName);
189         } else {
190             LOG.error("postProcessVpnInterfaceRemoval: rd not retrievable as vpninstancetovpnid for vpn {} is absent,"
191                     + " trying rd as {}. interface {} dpn {}", vpnName, vpnName, interfaceName,
192                     del.getDpnId());
193         }
194         notifyTaskIfRequired(interfaceName);
195     }
196
197     private boolean isMatchedPrefixToInterface(Prefixes prefix, VpnInterface vpnInterface) {
198         if (prefix != null && vpnInterface != null) {
199             if (prefix.getDpnId() != null && vpnInterface.getDpnId() != null) {
200                 if (prefix.getVpnInterfaceName() != null && vpnInterface.getName() != null) {
201                     return prefix.getDpnId().equals(vpnInterface.getDpnId())
202                             && prefix.getVpnInterfaceName().equalsIgnoreCase(vpnInterface.getName());
203                 }
204             }
205         }
206         return false;
207     }
208
209     private void notifyTaskIfRequired(String intfName) {
210         Runnable notifyTask = vpnInterfaceManager.isNotifyTaskQueued(intfName);
211         if (notifyTask == null) {
212             LOG.debug("notifyTaskIfRequired: No tasks queued to wait for deletion of vpnInterface {}", intfName);
213             return;
214         }
215         executorService.execute(notifyTask);
216     }
217
218     @Override
219     protected void update(final InstanceIdentifier<VpnInterface> identifier, final VpnInterface original,
220         final VpnInterface update) {
221         final VpnInterfaceKey key = identifier.firstKeyOf(VpnInterface.class, VpnInterfaceKey.class);
222         final String interfaceName = key.getName();
223
224         LOG.trace("update: VpnInterfaceOpListener updated: original {} updated {}", original, update);
225         if (original.getVpnInstanceName().equals(update.getVpnInstanceName())) {
226             return;
227         }
228
229         jobCoordinator.enqueueJob("VPNINTERFACE-" + interfaceName,
230             () -> {
231                 postProcessVpnInterfaceUpdate(identifier, original, update);
232                 LOG.info("update: vpn interface {} on dpn {} vpn {} processed successfully", update.getName(),
233                         update.getDpnId(), update.getVpnInstanceName());
234                 return null;
235             });
236     }
237
238     private void postProcessVpnInterfaceUpdate(InstanceIdentifier<VpnInterface> identifier, VpnInterface original,
239         VpnInterface update) {
240         final VpnInterfaceKey key = identifier.firstKeyOf(VpnInterface.class, VpnInterfaceKey.class);
241         String interfaceName = key.getName();
242
243         LOG.info("postProcessVpnInterfaceUpdate: interface name {} vpnName {} dpn {}", interfaceName,
244                 update.getVpnInstanceName(), update.getDpnId());
245         //increment the vpn interface count in Vpn Instance Op Data
246         VpnInstanceOpDataEntry vpnInstOp = null;
247         Optional<org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.to.vpn.id
248                 .VpnInstance> origVpnInstance =
249             VpnUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION,
250                          VpnOperDsUtils.getVpnInstanceToVpnIdIdentifier(original.getVpnInstanceName()));
251
252         if (origVpnInstance.isPresent()) {
253             String rd = origVpnInstance.get().getVrfId();
254
255             vpnInstOp = VpnUtil.getVpnInstanceOpData(dataBroker, rd);
256
257             Adjacencies adjs = original.getAugmentation(Adjacencies.class);
258             List<Adjacency> adjList = adjs != null ? adjs.getAdjacency() : null;
259
260             if (vpnInstOp != null && adjList != null && adjList.size() > 0) {
261                 for (Adjacency adjacency : adjs.getAdjacency()) {
262                     List<Prefixes> prefixToInterfaceListLocal = new ArrayList<>();
263                     if (adjacency.getAdjacencyType() != AdjacencyType.PrimaryAdjacency) {
264                         continue;
265                     }
266                     Optional<Prefixes> prefixToInterface = VpnUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL,
267                         VpnUtil.getPrefixToInterfaceIdentifier(vpnInstOp.getVpnId(),
268                         VpnUtil.getIpPrefix(adjacency.getIpAddress())));
269                     if (prefixToInterface.isPresent()) {
270                         prefixToInterfaceListLocal.add(prefixToInterface.get());
271                     } else {
272                         for (String adj : adjacency.getNextHopIpList()) {
273                             prefixToInterface = VpnUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL,
274                                 VpnUtil.getPrefixToInterfaceIdentifier(vpnInstOp.getVpnId(),
275                                     VpnUtil.getIpPrefix(adj)));
276                             if (prefixToInterface.isPresent()) {
277                                 prefixToInterfaceListLocal.add(prefixToInterface.get());
278                             }
279                         }
280                     }
281                     for (Prefixes prefix : prefixToInterfaceListLocal) {
282                         vpnFootprintService.updateVpnToDpnMapping(prefix.getDpnId(),
283                             original.getVpnInstanceName(), rd,
284                             interfaceName, null /*ipAddressSourceValuePair*/, false /* delete */);
285                     }
286                 }
287             }
288             LOG.info("postProcessVpnInterfaceUpdate: Updated vpn operational data and vpn footprint"
289                     + " for interface {} on dpn {} vpn {}", interfaceName, update.getDpnId(),
290                     update.getVpnInstanceName());
291         } else {
292             LOG.error("postProcessVpnInterfaceUpdate: rd not retrievable as vpninstancetovpnid for vpn {} is absent,"
293                     + " trying rd as {}. interface {} dpn {}", update.getVpnInstanceName(),
294                     update.getVpnInstanceName(), interfaceName, update.getDpnId());
295         }
296         notifyTaskIfRequired(interfaceName);
297     }
298
299     @Override
300     protected void add(InstanceIdentifier<VpnInterface> identifier, VpnInterface add) {
301     }
302 }