2 * Copyright (c) 2015 - 2016 Ericsson India Global Services Pvt Ltd. and others. All rights reserved.
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
8 package org.opendaylight.netvirt.vpnmanager;
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;
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();
46 /*public VpnInterfaceOpListener(final DataBroker dataBroker) {
47 super(VpnInterface.class);
48 this.dataBroker = dataBroker;
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;
63 LOG.info("{} start", getClass().getSimpleName());
64 registerListener(LogicalDatastoreType.OPERATIONAL, dataBroker);
68 protected InstanceIdentifier<VpnInterface> getWildCardPath() {
69 return InstanceIdentifier.create(VpnInterfaces.class).child(VpnInterface.class);
73 protected VpnInterfaceOpListener getDataTreeChangeListener() {
74 return VpnInterfaceOpListener.this;
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,
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());
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();
100 LOG.info("postProcessVpnInterfaceRemoval: interface name {} vpnName {} dpn {}", interfaceName, vpnName,
102 //decrement the vpn interface count in Vpn Instance Op Data
103 Optional<VpnInstance> vpnInstance = VpnUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION,
104 VpnOperDsUtils.getVpnInstanceToVpnIdIdentifier(vpnName));
106 if (vpnInstance.isPresent()) {
108 rd = vpnInstance.get().getVrfId();
109 //String rd = getRouteDistinguisher(del.getVpnInstanceName());
111 VpnInstanceOpDataEntry vpnInstOp = VpnUtil.getVpnInstanceOpData(dataBroker, rd);
113 Adjacencies adjs = del.getAugmentation(Adjacencies.class);
114 List<Adjacency> adjList = adjs != null ? adjs.getAdjacency() : null;
116 if (vpnInstOp != null && adjList != null && adjList.size() > 0) {
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.
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.
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) {
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());
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());
153 if (!prefixToInterfaceLocal.isEmpty()) {
154 prefixToInterface.addAll(prefixToInterfaceLocal);
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.
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.
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()));
175 VpnUtil.delete(dataBroker, LogicalDatastoreType.OPERATIONAL,
176 VpnUtil.getPrefixToInterfaceIdentifier(vpnInstOp.getVpnId(), pref.getIpAddress()),
177 VpnUtil.DEFAULT_CALLBACK);
182 if (del.getDpnId() != null) {
183 vpnFootprintService.updateVpnToDpnMapping(del.getDpnId(), del.getVpnInstanceName(), rd,
184 interfaceName, null /*ipAddressSourceValuePair*/,
185 false /* do delete */);
187 LOG.info("postProcessVpnInterfaceRemoval: Removed vpn operational data and updated vpn footprint"
188 + " for interface {} on dpn {} vpn {}", interfaceName, del.getDpnId(), vpnName);
190 LOG.error("postProcessVpnInterfaceRemoval: rd not retrievable as vpninstancetovpnid for vpn {} is absent,"
191 + " trying rd as {}. interface {} dpn {}", vpnName, vpnName, interfaceName,
194 notifyTaskIfRequired(interfaceName);
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());
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);
215 executorService.execute(notifyTask);
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();
224 LOG.trace("update: VpnInterfaceOpListener updated: original {} updated {}", original, update);
225 if (original.getVpnInstanceName().equals(update.getVpnInstanceName())) {
229 jobCoordinator.enqueueJob("VPNINTERFACE-" + interfaceName,
231 postProcessVpnInterfaceUpdate(identifier, original, update);
232 LOG.info("update: vpn interface {} on dpn {} vpn {} processed successfully", update.getName(),
233 update.getDpnId(), update.getVpnInstanceName());
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();
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()));
252 if (origVpnInstance.isPresent()) {
253 String rd = origVpnInstance.get().getVrfId();
255 vpnInstOp = VpnUtil.getVpnInstanceOpData(dataBroker, rd);
257 Adjacencies adjs = original.getAugmentation(Adjacencies.class);
258 List<Adjacency> adjList = adjs != null ? adjs.getAdjacency() : null;
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) {
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());
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());
281 for (Prefixes prefix : prefixToInterfaceListLocal) {
282 vpnFootprintService.updateVpnToDpnMapping(prefix.getDpnId(),
283 original.getVpnInstanceName(), rd,
284 interfaceName, null /*ipAddressSourceValuePair*/, false /* delete */);
288 LOG.info("postProcessVpnInterfaceUpdate: Updated vpn operational data and vpn footprint"
289 + " for interface {} on dpn {} vpn {}", interfaceName, update.getDpnId(),
290 update.getVpnInstanceName());
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());
296 notifyTaskIfRequired(interfaceName);
300 protected void add(InstanceIdentifier<VpnInterface> identifier, VpnInterface add) {