Upgrade: wait for interface states for adjacencies
[netvirt.git] / fibmanager / impl / src / main / java / org / opendaylight / netvirt / fibmanager / BgpRouteVrfEntryHandler.java
1 /*
2  * Copyright © 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.fibmanager;
9
10 import static java.util.stream.Collectors.toList;
11
12 import com.google.common.base.Optional;
13 import com.google.common.base.Preconditions;
14 import java.math.BigInteger;
15 import java.util.ArrayList;
16 import java.util.Collection;
17 import java.util.Collections;
18 import java.util.List;
19 import java.util.concurrent.BlockingQueue;
20 import java.util.concurrent.LinkedBlockingQueue;
21 import java.util.function.Consumer;
22 import javax.annotation.PostConstruct;
23 import javax.inject.Inject;
24 import javax.inject.Singleton;
25 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
26 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
27 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
28 import org.opendaylight.genius.datastoreutils.listeners.DataTreeEventCallbackRegistrar;
29 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
30 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
31 import org.opendaylight.genius.mdsalutil.ActionInfo;
32 import org.opendaylight.genius.mdsalutil.InstructionInfo;
33 import org.opendaylight.genius.mdsalutil.NwConstants;
34 import org.opendaylight.genius.mdsalutil.UpgradeState;
35 import org.opendaylight.genius.mdsalutil.actions.ActionGroup;
36 import org.opendaylight.genius.mdsalutil.actions.ActionNxLoadInPort;
37 import org.opendaylight.genius.mdsalutil.actions.ActionPushMpls;
38 import org.opendaylight.genius.mdsalutil.actions.ActionRegLoad;
39 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldEthernetDestination;
40 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldMplsLabel;
41 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldTunnelId;
42 import org.opendaylight.genius.mdsalutil.instructions.InstructionApplyActions;
43 import org.opendaylight.genius.utils.batching.ActionableResource;
44 import org.opendaylight.genius.utils.batching.ActionableResourceImpl;
45 import org.opendaylight.genius.utils.batching.ResourceBatchingManager;
46 import org.opendaylight.genius.utils.batching.ResourceHandler;
47 import org.opendaylight.genius.utils.batching.SubTransaction;
48 import org.opendaylight.infrautils.utils.concurrent.ListenableFutures;
49 import org.opendaylight.netvirt.vpnmanager.api.VpnExtraRouteHelper;
50 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeBase;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeMplsOverGre;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeVxlan;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.fibentries.VrfTables;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.fibentries.VrfTablesKey;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentries.VrfEntry;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.vpnnexthops.VpnNexthop;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.prefix.to._interface.vpn.ids.Prefixes;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntry;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.VpnToDpnList;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.to.extraroutes.vpn.extra.routes.Routes;
62 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
63 import org.slf4j.Logger;
64 import org.slf4j.LoggerFactory;
65
66
67 @Singleton
68 public class BgpRouteVrfEntryHandler extends BaseVrfEntryHandler
69         implements ResourceHandler, IVrfEntryHandler {
70
71     private static final Logger LOG = LoggerFactory.getLogger(BgpRouteVrfEntryHandler.class);
72     private static final int BATCH_INTERVAL = 500;
73     private static final int BATCH_SIZE = 1000;
74
75     private final DataBroker dataBroker;
76     private final ManagedNewTransactionRunner txRunner;
77     private final BlockingQueue<ActionableResource> vrfEntryBufferQ = new LinkedBlockingQueue<>();
78     private final ResourceBatchingManager resourceBatchingManager;
79     private final NexthopManager nexthopManager;
80
81     @Inject
82     public BgpRouteVrfEntryHandler(final DataBroker dataBroker,
83                                    final NexthopManager nexthopManager,
84                                    final FibUtil fibUtil,
85                                    final UpgradeState upgradeState,
86                                    final DataTreeEventCallbackRegistrar eventCallbacks) {
87         super(dataBroker, nexthopManager, null, fibUtil, upgradeState, eventCallbacks);
88         this.dataBroker = dataBroker;
89         this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
90         this.nexthopManager = nexthopManager;
91
92         resourceBatchingManager = ResourceBatchingManager.getInstance();
93         resourceBatchingManager.registerBatchableResource("FIB-VRFENTRY", vrfEntryBufferQ, this);
94     }
95
96     @PostConstruct
97     public void init() {
98         LOG.info("{} start", getClass().getSimpleName());
99     }
100
101     @Override
102     public void close() {
103         LOG.info("{} close", getClass().getSimpleName());
104     }
105
106     @Override
107     public DataBroker getResourceBroker() {
108         return dataBroker;
109     }
110
111     @Override
112     public int getBatchSize() {
113         return BATCH_SIZE;
114     }
115
116     @Override
117     public int getBatchInterval() {
118         return BATCH_INTERVAL;
119     }
120
121     @Override
122     public LogicalDatastoreType getDatastoreType() {
123         return LogicalDatastoreType.CONFIGURATION;
124     }
125
126     @Override
127     public void update(WriteTransaction tx, LogicalDatastoreType datastoreType, InstanceIdentifier identifier,
128                        Object original, Object update, List<SubTransaction> subTxns) {
129         if (original instanceof VrfEntry && update instanceof VrfEntry) {
130             createFibEntries(tx, identifier, (VrfEntry) update, subTxns);
131         }
132     }
133
134     @Override
135     public void create(WriteTransaction tx, LogicalDatastoreType datastoreType, InstanceIdentifier identifier,
136                        Object vrfEntry, List<SubTransaction> subTxns) {
137         if (vrfEntry instanceof VrfEntry) {
138             createFibEntries(tx, identifier, (VrfEntry) vrfEntry, subTxns);
139         }
140     }
141
142     @Override
143     public void delete(WriteTransaction tx, LogicalDatastoreType datastoreType, InstanceIdentifier identifier,
144                        Object vrfEntry, List<SubTransaction> subTxns) {
145         if (vrfEntry instanceof VrfEntry) {
146             deleteFibEntries(tx, identifier, (VrfEntry) vrfEntry, subTxns);
147         }
148     }
149
150     @Override
151     public void createFlows(InstanceIdentifier<VrfEntry> identifier, VrfEntry vrfEntry, String rd) {
152         ActionableResource actResource = new ActionableResourceImpl(rd + vrfEntry.getDestPrefix());
153         actResource.setAction(ActionableResource.CREATE);
154         actResource.setInstanceIdentifier(identifier);
155         actResource.setInstance(vrfEntry);
156         vrfEntryBufferQ.add(actResource);
157     }
158
159     @Override
160     public void removeFlows(InstanceIdentifier<VrfEntry> identifier, VrfEntry vrfEntry, String rd) {
161         ActionableResource actResource = new ActionableResourceImpl(rd + vrfEntry.getDestPrefix());
162         actResource.setAction(ActionableResource.DELETE);
163         actResource.setInstanceIdentifier(identifier);
164         actResource.setInstance(vrfEntry);
165         vrfEntryBufferQ.add(actResource);
166     }
167
168     @Override
169     public void updateFlows(InstanceIdentifier<VrfEntry> identifier, VrfEntry original, VrfEntry update, String rd) {
170         ActionableResource actResource = new ActionableResourceImpl(rd + update.getDestPrefix());
171         actResource.setAction(ActionableResource.UPDATE);
172         actResource.setInstanceIdentifier(identifier);
173         actResource.setInstance(update);
174         actResource.setOldInstance(original);
175         vrfEntryBufferQ.add(actResource);
176     }
177
178     /*
179       Please note that the following createFibEntries will be invoked only for BGP Imported Routes.
180       The invocation of the following method is via create() callback from the MDSAL Batching Infrastructure
181       provided by ResourceBatchingManager
182      */
183     private void createFibEntries(WriteTransaction writeTx, final InstanceIdentifier<VrfEntry> vrfEntryIid,
184                                   final VrfEntry vrfEntry, List<SubTransaction> subTxns) {
185         final VrfTablesKey vrfTableKey = vrfEntryIid.firstKeyOf(VrfTables.class);
186
187         final VpnInstanceOpDataEntry vpnInstance =
188                 getFibUtil().getVpnInstance(vrfTableKey.getRouteDistinguisher());
189         Preconditions.checkNotNull(vpnInstance, "Vpn Instance not available " + vrfTableKey.getRouteDistinguisher());
190         Preconditions.checkNotNull(vpnInstance.getVpnId(), "Vpn Instance with rd " + vpnInstance.getVrfId()
191                 + " has null vpnId!");
192
193         final Collection<VpnToDpnList> vpnToDpnList = vpnInstance.getVpnToDpnList();
194         if (vpnToDpnList != null) {
195             for (VpnToDpnList vpnDpn : vpnToDpnList) {
196                 if (vpnDpn.getDpnState() == VpnToDpnList.DpnState.Active) {
197                     createRemoteFibEntry(vpnDpn.getDpnId(), vpnInstance.getVpnId(), vrfTableKey.getRouteDistinguisher(),
198                             vrfEntry, writeTx, subTxns);
199                 }
200             }
201         }
202     }
203
204     /*
205       Please note that the following deleteFibEntries will be invoked only for BGP Imported Routes.
206       The invocation of the following method is via delete() callback from the MDSAL Batching Infrastructure
207       provided by ResourceBatchingManager
208      */
209     private void deleteFibEntries(WriteTransaction writeTx, final InstanceIdentifier<VrfEntry> identifier,
210                                   final VrfEntry vrfEntry, List<SubTransaction> subTxns) {
211         final VrfTablesKey vrfTableKey = identifier.firstKeyOf(VrfTables.class);
212         String rd = vrfTableKey.getRouteDistinguisher();
213         final VpnInstanceOpDataEntry vpnInstance =
214                 getFibUtil().getVpnInstance(vrfTableKey.getRouteDistinguisher());
215         if (vpnInstance == null) {
216             LOG.debug("VPN Instance for rd {} is not available from VPN Op Instance Datastore", rd);
217             return;
218         }
219         String vpnName = getFibUtil().getVpnNameFromId(vpnInstance.getVpnId());
220         final Collection<VpnToDpnList> vpnToDpnList = vpnInstance.getVpnToDpnList();
221         if (vpnToDpnList != null) {
222             List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker,
223                     vpnInstance.getVpnId(), vrfEntry.getDestPrefix());
224             Optional<Routes> extraRouteOptional;
225             //Is this fib route an extra route? If yes, get the nexthop which would be an adjacency in the vpn
226             if (usedRds != null && !usedRds.isEmpty()) {
227                 if (usedRds.size() > 1) {
228                     LOG.error("The extra route prefix is still present in some DPNs");
229                     return ;
230                 } else {
231                     extraRouteOptional = VpnExtraRouteHelper.getVpnExtraroutes(dataBroker, vpnName,
232                             usedRds.get(0), vrfEntry.getDestPrefix());
233                 }
234             } else {
235                 extraRouteOptional = Optional.absent();
236             }
237             for (VpnToDpnList curDpn : vpnToDpnList) {
238                 if (curDpn.getDpnState() == VpnToDpnList.DpnState.Active) {
239                     deleteRemoteRoute(BigInteger.ZERO, curDpn.getDpnId(), vpnInstance.getVpnId(),
240                             vrfTableKey, vrfEntry, extraRouteOptional, writeTx, subTxns);
241                 }
242             }
243         }
244     }
245
246     public void programRemoteFibForBgpRoutes(final BigInteger remoteDpnId,
247                                              final long vpnId,
248                                              final VrfEntry vrfEntry,
249                                              WriteTransaction tx,
250                                              String rd,
251                                              List<NexthopManager.AdjacencyResult> adjacencyResults,
252                                              List<SubTransaction> subTxns) {
253         Preconditions.checkArgument(vrfEntry.getRoutePaths().size() <= 2);
254
255         if (adjacencyResults.size() == 1) {
256             programRemoteFib(remoteDpnId, vpnId, vrfEntry, tx, rd, adjacencyResults, subTxns);
257             return;
258         }
259         // ECMP Use case, point to LB group. Move the mpls label accordingly.
260         List<String> tunnelList =
261                 adjacencyResults.stream()
262                         .map(NexthopManager.AdjacencyResult::getNextHopIp)
263                         .sorted().collect(toList());
264         String lbGroupKey = FibUtil.getGreLbGroupKey(tunnelList);
265         long groupId = nexthopManager.createNextHopPointer(lbGroupKey);
266         int index = 0;
267         List<ActionInfo> actionInfos = new ArrayList<>();
268         for (NexthopManager.AdjacencyResult adjResult : adjacencyResults) {
269             String nextHopIp = adjResult.getNextHopIp();
270             java.util.Optional<Long> optionalLabel = FibUtil.getLabelForNextHop(vrfEntry, nextHopIp);
271             if (!optionalLabel.isPresent()) {
272                 LOG.warn("NextHopIp {} not found in vrfEntry {}", nextHopIp, vrfEntry);
273                 continue;
274             }
275             long label = optionalLabel.get();
276
277             actionInfos.add(new ActionRegLoad(index, FibConstants.NXM_REG_MAPPING.get(index++), 0,
278                     31, label));
279         }
280         List<InstructionInfo> instructions = new ArrayList<>();
281         actionInfos.add(new ActionGroup(index, groupId));
282         instructions.add(new InstructionApplyActions(actionInfos));
283         makeConnectedRoute(remoteDpnId, vpnId, vrfEntry, rd, instructions, NwConstants.ADD_FLOW, tx, subTxns);
284     }
285
286     public void createRemoteFibEntry(final BigInteger remoteDpnId,
287                                      final long vpnId,
288                                      final String rd,
289                                      final VrfEntry vrfEntry,
290                                      WriteTransaction tx,
291                                      List<SubTransaction> subTxns) {
292         if (tx == null) {
293             ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(
294                 newTx -> createRemoteFibEntry(remoteDpnId, vpnId, rd, vrfEntry, newTx, subTxns)), LOG,
295                 "Error creating remote FIB entry");
296             return;
297         }
298
299         LOG.debug("createRemoteFibEntry: adding route {} for rd {} on remoteDpnId {}",
300                 vrfEntry.getDestPrefix(), rd, remoteDpnId);
301
302         List<NexthopManager.AdjacencyResult> adjacencyResults =
303                 resolveAdjacency(remoteDpnId, vpnId, vrfEntry, rd);
304         if (adjacencyResults.isEmpty()) {
305             LOG.error("Could not get interface for route-paths: {} in vpn {}", vrfEntry.getRoutePaths(), rd);
306             LOG.warn("Failed to add Route: {} in vpn: {}", vrfEntry.getDestPrefix(), rd);
307             return;
308         }
309
310         programRemoteFibForBgpRoutes(remoteDpnId, vpnId, vrfEntry, tx, rd, adjacencyResults, subTxns);
311
312         LOG.debug("Successfully added FIB entry for prefix {} in vpnId {}", vrfEntry.getDestPrefix(), vpnId);
313     }
314
315     private void deleteFibEntryForBgpRoutes(BigInteger remoteDpnId, long vpnId, VrfEntry vrfEntry,
316                                              String rd, WriteTransaction tx, List<SubTransaction> subTxns) {
317         // When the tunnel is removed the fib entries should be reprogrammed/deleted depending on
318         // the adjacencyResults.
319         List<NexthopManager.AdjacencyResult> adjacencyResults = resolveAdjacency(remoteDpnId, vpnId, vrfEntry, rd);
320         if (!adjacencyResults.isEmpty()) {
321             programRemoteFibForBgpRoutes(remoteDpnId, vpnId, vrfEntry, tx, rd, adjacencyResults, subTxns);
322         }
323     }
324
325     public void deleteRemoteRoute(final BigInteger localDpnId, final BigInteger remoteDpnId,
326                                   final long vpnId, final VrfTablesKey vrfTableKey,
327                                   final VrfEntry vrfEntry, Optional<Routes> extraRouteOptional,
328                                   WriteTransaction tx, List<SubTransaction> subTxns) {
329         if (tx == null) {
330             ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(
331                 newTx -> deleteRemoteRoute(localDpnId, remoteDpnId, vpnId, vrfTableKey, vrfEntry,
332                         extraRouteOptional, newTx)), LOG, "Error deleting remote route");
333             return;
334         }
335
336         LOG.debug("deleting remote route: prefix={}, vpnId={} localDpnId {} remoteDpnId {}",
337                 vrfEntry.getDestPrefix(), vpnId, localDpnId, remoteDpnId);
338         String rd = vrfTableKey.getRouteDistinguisher();
339
340         if (localDpnId != null && localDpnId != BigInteger.ZERO) {
341             // localDpnId is not known when clean up happens for last vm for a vpn on a dpn
342             if (extraRouteOptional.isPresent()) {
343                 nexthopManager.setupLoadBalancingNextHop(vpnId, remoteDpnId, vrfEntry.getDestPrefix(),
344                         Collections.emptyList() /*listBucketInfo*/ , false);
345             }
346             deleteFibEntryForBgpRoutes(remoteDpnId, vpnId, vrfEntry, rd, tx, subTxns);
347             return;
348         }
349
350         // below two reads are kept as is, until best way is found to identify dpnID
351         VpnNexthop localNextHopInfo = nexthopManager.getVpnNexthop(vpnId, vrfEntry.getDestPrefix());
352         if (extraRouteOptional.isPresent()) {
353             nexthopManager.setupLoadBalancingNextHop(vpnId, remoteDpnId, vrfEntry.getDestPrefix(),
354                     Collections.emptyList()  /*listBucketInfo*/ , false);
355         } else {
356             checkDpnDeleteFibEntry(localNextHopInfo, remoteDpnId, vpnId, vrfEntry, rd, tx, subTxns);
357         }
358     }
359
360     public Consumer<? super VrfEntry> getConsumerForCreatingRemoteFib(
361             final BigInteger dpnId, final long vpnId, final String rd,
362             final String remoteNextHopIp, final Optional<VrfTables> vrfTable,
363             WriteTransaction writeCfgTxn, List<SubTransaction> subTxns) {
364         return vrfEntry -> vrfEntry.getRoutePaths().stream()
365                 .filter(routes -> !routes.getNexthopAddress().isEmpty()
366                         && remoteNextHopIp.trim().equals(routes.getNexthopAddress().trim()))
367                 .findFirst()
368                 .ifPresent(routes -> {
369                     LOG.trace("creating remote FIB entry for prefix {} rd {} on Dpn {}",
370                             vrfEntry.getDestPrefix(), rd, dpnId);
371                     createRemoteFibEntry(dpnId, vpnId, vrfTable.get().getRouteDistinguisher(),
372                             vrfEntry, writeCfgTxn, subTxns);
373                 });
374     }
375
376     public Consumer<? super VrfEntry> getConsumerForDeletingRemoteFib(
377             final BigInteger dpnId, final long vpnId,
378             final String remoteNextHopIp, final Optional<VrfTables> vrfTable,
379             WriteTransaction writeCfgTxn, List<SubTransaction> subTxns) {
380         return vrfEntry -> vrfEntry.getRoutePaths().stream()
381                 .filter(routes -> !routes.getNexthopAddress().isEmpty()
382                         && remoteNextHopIp.trim().equals(routes.getNexthopAddress().trim()))
383                 .findFirst()
384                 .ifPresent(routes -> {
385                     LOG.trace(" deleting remote FIB entry {}", vrfEntry);
386                     deleteRemoteRoute(null, dpnId, vpnId, vrfTable.get().key(), vrfEntry,
387                             Optional.absent(), writeCfgTxn, subTxns);
388                 });
389     }
390
391     @Override
392     protected void addTunnelInterfaceActions(NexthopManager.AdjacencyResult adjacencyResult, long vpnId,
393             VrfEntry vrfEntry, List<ActionInfo> actionInfos, String rd) {
394         Class<? extends TunnelTypeBase> tunnelType = VpnExtraRouteHelper
395                 .getTunnelType(getNextHopManager().getItmManager(), adjacencyResult.getInterfaceName());
396         if (tunnelType == null) {
397             LOG.debug("Tunnel type not found for vrfEntry {}", vrfEntry);
398             return;
399         }
400         String nextHopIp = adjacencyResult.getNextHopIp();
401         if (tunnelType.equals(TunnelTypeMplsOverGre.class)) {
402             java.util.Optional<Long> optionalLabel = FibUtil.getLabelForNextHop(vrfEntry, nextHopIp);
403             if (!optionalLabel.isPresent()) {
404                 LOG.warn("NextHopIp {} not found in vrfEntry {}", nextHopIp, vrfEntry);
405                 return;
406             }
407             long label = optionalLabel.get();
408             LOG.debug("addTunnelInterfaceActions: Push label action for prefix {} rd {} l3vni {} nextHop {}",
409                     vrfEntry.getDestPrefix(), rd, vrfEntry.getL3vni(), nextHopIp);
410             actionInfos.add(new ActionPushMpls());
411             actionInfos.add(new ActionSetFieldMplsLabel(label));
412             actionInfos.add(new ActionNxLoadInPort(BigInteger.ZERO));
413         } else if (tunnelType.equals(TunnelTypeVxlan.class)) {
414             actionInfos.add(new ActionSetFieldTunnelId(BigInteger.valueOf(vrfEntry.getL3vni())));
415             LOG.debug("addTunnelInterfaceActions: adding set tunnel id action for prefix {} rd {} l3vni {}"
416                     + " nextHop {} ", vrfEntry.getDestPrefix(), rd, vrfEntry.getL3vni(), nextHopIp);
417             addRewriteDstMacAction(vpnId, vrfEntry, null /*prefixInfo*/, actionInfos);
418         }
419     }
420
421     @Override
422     protected void addRewriteDstMacAction(long vpnId, VrfEntry vrfEntry, Prefixes prefixInfo,
423                                         List<ActionInfo> actionInfos) {
424         if (vrfEntry.getGatewayMacAddress() != null) {
425             actionInfos.add(new ActionSetFieldEthernetDestination(actionInfos.size(),
426                     new MacAddress(vrfEntry.getGatewayMacAddress())));
427         }
428     }
429
430 }