Freeze upstream versions
[netvirt.git] / fibmanager / impl / src / main / java / org / opendaylight / netvirt / fibmanager / EvpnVrfEntryHandler.java
1 /*
2  * Copyright (c) 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 com.google.common.base.Preconditions.checkNotNull;
11 import static org.opendaylight.mdsal.binding.util.Datastore.CONFIGURATION;
12
13 import java.util.ArrayList;
14 import java.util.Collections;
15 import java.util.List;
16 import java.util.Map;
17 import java.util.Objects;
18 import java.util.Optional;
19 import org.opendaylight.genius.datastoreutils.listeners.DataTreeEventCallbackRegistrar;
20 import org.opendaylight.genius.mdsalutil.ActionInfo;
21 import org.opendaylight.genius.mdsalutil.InstructionInfo;
22 import org.opendaylight.genius.mdsalutil.NwConstants;
23 import org.opendaylight.genius.mdsalutil.actions.ActionGroup;
24 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldEthernetDestination;
25 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldTunnelId;
26 import org.opendaylight.genius.mdsalutil.instructions.InstructionApplyActions;
27 import org.opendaylight.genius.utils.batching.SubTransaction;
28 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
29 import org.opendaylight.mdsal.binding.api.DataBroker;
30 import org.opendaylight.mdsal.binding.api.WriteTransaction;
31 import org.opendaylight.mdsal.binding.util.Datastore;
32 import org.opendaylight.mdsal.binding.util.ManagedNewTransactionRunner;
33 import org.opendaylight.mdsal.binding.util.ManagedNewTransactionRunnerImpl;
34 import org.opendaylight.mdsal.binding.util.TransactionAdapter;
35 import org.opendaylight.mdsal.common.api.ReadFailedException;
36 import org.opendaylight.netvirt.fibmanager.api.RouteOrigin;
37 import org.opendaylight.serviceutils.upgrade.UpgradeState;
38 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.TunnelOperStatus;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.tunnels_state.StateTunnelList;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.SubnetRoute;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.fibentries.VrfTables;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.fibentries.VrfTablesKey;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentries.VrfEntry;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.vpnnexthops.VpnNexthop;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.prefix.to._interface.vpn.ids.Prefixes;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntry;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.VpnToDpnList;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.VpnToDpnListKey;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.to.extraroutes.vpn.extra.routes.Routes;
51 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
52 import org.opendaylight.yangtools.yang.common.Uint32;
53 import org.opendaylight.yangtools.yang.common.Uint64;
54 import org.slf4j.Logger;
55 import org.slf4j.LoggerFactory;
56
57 public class EvpnVrfEntryHandler extends BaseVrfEntryHandler {
58     private static final Logger LOG = LoggerFactory.getLogger(EvpnVrfEntryHandler.class);
59
60     private final ManagedNewTransactionRunner txRunner;
61     private final VrfEntryListener vrfEntryListener;
62     private final BgpRouteVrfEntryHandler bgpRouteVrfEntryHandler;
63     private final NexthopManager nexthopManager;
64     private final JobCoordinator jobCoordinator;
65
66     EvpnVrfEntryHandler(DataBroker broker, VrfEntryListener vrfEntryListener,
67             BgpRouteVrfEntryHandler bgpRouteVrfEntryHandler, NexthopManager nexthopManager,
68             JobCoordinator jobCoordinator, FibUtil fibUtil,
69             final UpgradeState upgradeState, final DataTreeEventCallbackRegistrar eventCallbacks) {
70         super(broker, nexthopManager, null, fibUtil, upgradeState, eventCallbacks);
71         this.txRunner = new ManagedNewTransactionRunnerImpl(broker);
72         this.vrfEntryListener = vrfEntryListener;
73         this.bgpRouteVrfEntryHandler = bgpRouteVrfEntryHandler;
74         this.nexthopManager = nexthopManager;
75         this.jobCoordinator = jobCoordinator;
76     }
77
78     void createFlows(InstanceIdentifier<VrfEntry> identifier, VrfEntry vrfEntry, String rd) {
79         LOG.info("Initiating creation of Evpn Flows");
80         final VrfTablesKey vrfTableKey = identifier.firstKeyOf(VrfTables.class);
81         final VpnInstanceOpDataEntry vpnInstance = getFibUtil().getVpnInstanceOpData(
82                 vrfTableKey.getRouteDistinguisher()).get();
83         Uint32 vpnId = vpnInstance.getVpnId();
84         checkNotNull(vpnInstance, "Vpn Instance not available %s", vrfTableKey.getRouteDistinguisher());
85         checkNotNull(vpnId, "Vpn Instance with rd %s has null vpnId!", vpnInstance.getVrfId());
86         if (RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.CONNECTED) {
87             SubnetRoute subnetRoute = vrfEntry.augmentation(SubnetRoute.class);
88             final Map<VpnToDpnListKey, VpnToDpnList> keyVpnToDpnListMap = vpnInstance.nonnullVpnToDpnList();
89             final long elanTag = subnetRoute.getElantag().toJava();
90             LOG.trace("SubnetRoute augmented vrfentry found for rd {} prefix {} with elantag {}",
91                     rd, vrfEntry.getDestPrefix(), elanTag);
92             if (keyVpnToDpnListMap != null) {
93                 jobCoordinator.enqueueJob("FIB-" + rd + "-" + vrfEntry.getDestPrefix(),
94                     () -> Collections.singletonList(
95                         txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
96                             for (final VpnToDpnList curDpn : keyVpnToDpnListMap.values()) {
97                                 if (curDpn.getDpnState() == VpnToDpnList.DpnState.Active) {
98                                     vrfEntryListener.installSubnetRouteInFib(curDpn.getDpnId(), elanTag, rd,
99                                         vpnId, vrfEntry, tx);
100                                 }
101                             }
102                         })));
103             }
104             return;
105         }
106         Prefixes localNextHopInfo = getFibUtil().getPrefixToInterface(vpnInstance.getVpnId(),
107                                                                             vrfEntry.getDestPrefix());
108         List<Uint64> localDpnId = new ArrayList<>();
109         boolean isNatPrefix = false;
110         if (Prefixes.PrefixCue.Nat.equals(localNextHopInfo.getPrefixCue())) {
111             LOG.info("NAT Prefix {} with vpnId {} rd {}. Skip local dpn {} FIB processing",
112                     vrfEntry.getDestPrefix(), vpnId, rd, localNextHopInfo.getDpnId());
113             localDpnId.add(localNextHopInfo.getDpnId());
114             isNatPrefix = true;
115         } else {
116             localDpnId = createLocalEvpnFlows(vpnInstance.getVpnId(), rd, vrfEntry,
117                     localNextHopInfo);
118         }
119         createRemoteEvpnFlows(rd, vrfEntry, vpnInstance, localDpnId, vrfTableKey, isNatPrefix);
120     }
121
122     void removeFlows(InstanceIdentifier<VrfEntry> identifier, VrfEntry vrfEntry, String rd) {
123         final VrfTablesKey vrfTableKey = identifier.firstKeyOf(VrfTables.class);
124         final VpnInstanceOpDataEntry vpnInstance = getFibUtil().getVpnInstanceOpData(
125                 vrfTableKey.getRouteDistinguisher()).get();
126         if (vpnInstance == null) {
127             LOG.error("VPN Instance for rd {} is not available from VPN Op Instance Datastore", rd);
128             return;
129         }
130         VpnNexthop localNextHopInfo = nexthopManager.getVpnNexthop(vpnInstance.getVpnId(),
131                 vrfEntry.getDestPrefix());
132         List<Uint64> localDpnId = checkDeleteLocalEvpnFLows(vpnInstance.getVpnId(),
133                                                                     rd, vrfEntry, localNextHopInfo);
134         deleteRemoteEvpnFlows(rd, vrfEntry, vpnInstance, vrfTableKey, localDpnId);
135         vrfEntryListener.cleanUpOpDataForFib(vpnInstance.getVpnId(), rd, vrfEntry);
136     }
137
138     private List<Uint64> createLocalEvpnFlows(Uint32 vpnId, String rd, VrfEntry vrfEntry,
139                                                   Prefixes localNextHopInfo) {
140         List<Uint64> returnLocalDpnId = new ArrayList<>();
141         String localNextHopIP = vrfEntry.getDestPrefix();
142         if (localNextHopInfo == null) {
143             //Handle extra routes and imported routes
144             Routes extraRoute = getVpnToExtraroute(vpnId, rd, vrfEntry.getDestPrefix());
145             if (extraRoute != null && extraRoute.getNexthopIpList() != null) {
146                 for (String nextHopIp : extraRoute.getNexthopIpList()) {
147                     LOG.info("NextHop IP for destination {} is {}", vrfEntry.getDestPrefix(), nextHopIp);
148                     if (nextHopIp != null) {
149                         localNextHopInfo = getFibUtil().getPrefixToInterface(vpnId, nextHopIp + "/32");
150                         if (localNextHopInfo != null) {
151                             localNextHopIP = nextHopIp + "/32";
152                             Uint64 dpnId = checkCreateLocalEvpnFlows(localNextHopInfo, localNextHopIP, vpnId,
153                                     rd, vrfEntry);
154                             returnLocalDpnId.add(dpnId);
155                         }
156                     }
157                 }
158             }
159         } else {
160             LOG.info("Creating local EVPN flows for prefix {} rd {} route-paths {} evi {}.",
161                     vrfEntry.getDestPrefix(), rd, vrfEntry.getRoutePaths(), vrfEntry.getL3vni());
162             Uint64 dpnId = checkCreateLocalEvpnFlows(localNextHopInfo, localNextHopIP, vpnId,
163                     rd, vrfEntry);
164             returnLocalDpnId.add(dpnId);
165         }
166         return returnLocalDpnId;
167     }
168
169     // Allow deprecated TransactionRunner calls for now
170     @SuppressWarnings("ForbidCertainMethod")
171     private Uint64 checkCreateLocalEvpnFlows(Prefixes localNextHopInfo, String localNextHopIP,
172                                                  final Uint32 vpnId, final String rd,
173                                                  final VrfEntry vrfEntry) {
174         final Uint64 dpnId = localNextHopInfo.getDpnId();
175         String jobKey = FibUtil.getCreateLocalNextHopJobKey(vpnId, dpnId, vrfEntry.getDestPrefix());
176         final long groupId = nexthopManager.createLocalNextHop(vpnId, dpnId,
177             localNextHopInfo.getVpnInterfaceName(), localNextHopIP, vrfEntry.getDestPrefix(),
178             vrfEntry.getGatewayMacAddress(), /*parentVpnId*/ null);
179         LOG.debug("LocalNextHopGroup {} created/reused for prefix {} rd {} evi {} route-paths {}", groupId,
180             vrfEntry.getDestPrefix(), rd, vrfEntry.getL3vni(), vrfEntry.getRoutePaths());
181
182         final List<InstructionInfo> instructions = Collections.singletonList(
183             new InstructionApplyActions(
184                 Collections.singletonList(new ActionGroup(groupId))));
185         jobCoordinator.enqueueJob(jobKey,
186             () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION,
187                 tx -> makeConnectedRoute(dpnId, vpnId, vrfEntry, rd, instructions, NwConstants.ADD_FLOW,
188                         TransactionAdapter.toWriteTransaction(tx), null))));
189         return dpnId;
190     }
191
192     // Allow deprecated TransactionRunner calls for now
193     @SuppressWarnings("ForbidCertainMethod")
194     private void createRemoteEvpnFlows(String rd, VrfEntry vrfEntry, VpnInstanceOpDataEntry vpnInstance,
195                                        List<Uint64> localDpnId, VrfTablesKey vrfTableKey, boolean isNatPrefix) {
196         LOG.info("Creating remote EVPN flows for prefix {} rd {} route-paths {} evi {}",
197             vrfEntry.getDestPrefix(), rd, vrfEntry.getRoutePaths(), vrfEntry.getL3vni());
198         Map<VpnToDpnListKey, VpnToDpnList> keyVpnToDpnListMap = vpnInstance.nonnullVpnToDpnList();
199         if (keyVpnToDpnListMap != null) {
200             jobCoordinator.enqueueJob("FIB" + rd + vrfEntry.getDestPrefix(),
201                 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(
202                         Datastore.CONFIGURATION, tx -> {
203                         for (VpnToDpnList vpnDpn : keyVpnToDpnListMap.values()) {
204                             if (!localDpnId.contains(vpnDpn.getDpnId())) {
205                                 if (vpnDpn.getDpnState() == VpnToDpnList.DpnState.Active) {
206                                     createRemoteFibEntry(vpnDpn.getDpnId(), vpnInstance.getVpnId(),
207                                             vrfTableKey, vrfEntry, isNatPrefix,
208                                             TransactionAdapter.toWriteTransaction(tx));
209                                 }
210                             }
211                         }
212                     })));
213         }
214     }
215
216     private void createRemoteFibEntry(final Uint64 remoteDpnId, final Uint32 vpnId, final VrfTablesKey vrfTableKey,
217                                       final VrfEntry vrfEntry, boolean isNatPrefix, WriteTransaction tx) {
218
219         String rd = vrfTableKey.getRouteDistinguisher();
220         List<SubTransaction> subTxns =  new ArrayList<>();
221         LOG.debug("createremotefibentry: adding route {} for rd {} with transaction {}",
222                 vrfEntry.getDestPrefix(), rd, tx);
223         List<NexthopManager.AdjacencyResult> tunnelInterfaceList = resolveAdjacency(remoteDpnId, vpnId, vrfEntry, rd);
224
225         if (tunnelInterfaceList.isEmpty()) {
226             LOG.error("Could not get interface for route-paths: {} in vpn {}",
227                     vrfEntry.getRoutePaths(), rd);
228             LOG.warn("Failed to add Route: {} in vpn: {}",
229                     vrfEntry.getDestPrefix(), rd);
230             return;
231         }
232
233         for (NexthopManager.AdjacencyResult adjacencyResult : tunnelInterfaceList) {
234             List<ActionInfo> actionInfos = new ArrayList<>();
235             Uint64 tunnelId = Uint64.ZERO;
236             String prefix = adjacencyResult.getPrefix();
237             Prefixes prefixInfo = getFibUtil().getPrefixToInterface(vpnId, prefix);
238             String interfaceName = prefixInfo.getVpnInterfaceName();
239             if (RouteOrigin.BGP.getValue().equals(vrfEntry.getOrigin()) || isNatPrefix) {
240                 tunnelId = Uint64.valueOf(vrfEntry.getL3vni().longValue());
241             } else if (FibUtil.isVxlanNetwork(prefixInfo.getNetworkType())) {
242                 tunnelId = Uint64.valueOf(prefixInfo.getSegmentationId().longValue());
243             } else {
244                 try {
245                     StateTunnelList stateTunnelList = getFibUtil().getTunnelState(interfaceName);
246                     if (stateTunnelList == null || stateTunnelList.getOperState() != TunnelOperStatus.Up) {
247                         LOG.trace("Tunnel is not up for interface {}", interfaceName);
248                         return;
249                     }
250                     tunnelId = Uint64.valueOf(stateTunnelList.getIfIndex().intValue());
251                 } catch (ReadFailedException e) {
252                     LOG.error("createRemoteFibEntry: error in fetching tunnel state for interface {}",
253                             interfaceName, e);
254                     continue;
255                 }
256             }
257             LOG.debug("adding set tunnel id action for label {}", tunnelId);
258             String macAddress = null;
259             String vpnName = getFibUtil().getVpnNameFromId(vpnId);
260             if (vpnName == null) {
261                 LOG.debug("Failed to get VPN name for vpnId {}", vpnId);
262                 return;
263             }
264             if (interfaceName != null) {
265                 macAddress = getFibUtil().getMacAddressFromPrefix(interfaceName, vpnName, prefix);
266                 actionInfos.add(new ActionSetFieldEthernetDestination(new MacAddress(macAddress)));
267             }
268             actionInfos.add(new ActionSetFieldTunnelId(tunnelId));
269             List<ActionInfo> egressActions =
270                     nexthopManager.getEgressActionsForInterface(adjacencyResult.getInterfaceName(), actionInfos.size(),
271                             true, vpnId, vrfEntry.getDestPrefix());
272             if (egressActions.isEmpty()) {
273                 LOG.error("Failed to retrieve egress action for prefix {} route-paths {} interface {}."
274                         + " Aborting remote FIB entry creation..", vrfEntry.getDestPrefix(),
275                         vrfEntry.getRoutePaths(), adjacencyResult.getInterfaceName());
276                 return;
277             }
278             actionInfos.addAll(egressActions);
279             List<InstructionInfo> instructions = new ArrayList<>();
280             instructions.add(new InstructionApplyActions(actionInfos));
281             makeConnectedRoute(remoteDpnId, vpnId, vrfEntry, rd, instructions, NwConstants.ADD_FLOW, tx, subTxns);
282         }
283         LOG.debug("Successfully added FIB entry for prefix {} in rd {}", vrfEntry.getDestPrefix(), rd);
284     }
285
286     // Allow deprecated TransactionRunner calls for now
287     @SuppressWarnings("ForbidCertainMethod")
288     private void deleteRemoteEvpnFlows(String rd, VrfEntry vrfEntry, VpnInstanceOpDataEntry vpnInstance,
289                                        VrfTablesKey vrfTableKey, List<Uint64> localDpnIdList) {
290         Map<VpnToDpnListKey, VpnToDpnList> keyVpnToDpnListMap = vpnInstance.nonnullVpnToDpnList();
291         List<SubTransaction> subTxns =  new ArrayList<>();
292         if (keyVpnToDpnListMap != null) {
293             jobCoordinator.enqueueJob("FIB" + rd + vrfEntry.getDestPrefix(),
294                 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(
295                         Datastore.CONFIGURATION, tx -> {
296                         final Optional<Routes> extraRouteOptional = Optional.empty();
297                         if (localDpnIdList.size() <= 0) {
298                             for (VpnToDpnList curDpn1 : keyVpnToDpnListMap.values()) {
299                                 if (RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.BGP) {
300                                     if (curDpn1.getDpnState() == VpnToDpnList.DpnState.Active) {
301                                         bgpRouteVrfEntryHandler.deleteRemoteRoute(Uint64.ZERO,
302                                                 curDpn1.getDpnId(),
303                                                 vpnInstance.getVpnId(), vrfTableKey, vrfEntry,
304                                                 extraRouteOptional, TransactionAdapter.toWriteTransaction(tx),
305                                                 subTxns);
306                                     }
307                                 } else {
308                                     deleteRemoteRoute(Uint64.ZERO, curDpn1.getDpnId(),
309                                             vpnInstance.getVpnId(), vrfTableKey, vrfEntry,
310                                             extraRouteOptional, TransactionAdapter.toWriteTransaction(tx));
311                                 }
312                             }
313                         } else {
314                             for (Uint64 localDpnId : localDpnIdList) {
315                                 for (VpnToDpnList curDpn2 : keyVpnToDpnListMap.values()) {
316                                     if (!Objects.equals(curDpn2.getDpnId(), localDpnId)) {
317                                         if (RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.BGP) {
318                                             if (curDpn2.getDpnState() == VpnToDpnList.DpnState.Active) {
319                                                 bgpRouteVrfEntryHandler.deleteRemoteRoute(localDpnId,
320                                                         curDpn2.getDpnId(),
321                                                         vpnInstance.getVpnId(), vrfTableKey, vrfEntry,
322                                                         extraRouteOptional,
323                                                         TransactionAdapter.toWriteTransaction(tx), subTxns);
324                                             }
325                                         } else {
326                                             deleteRemoteRoute(localDpnId, curDpn2.getDpnId(),
327                                                     vpnInstance.getVpnId(), vrfTableKey, vrfEntry,
328                                                     extraRouteOptional, TransactionAdapter.toWriteTransaction(tx));
329                                         }
330                                     }
331                                 }
332                             }
333                         }
334                     })));
335         }
336     }
337
338     // Allow deprecated TransactionRunner calls for now
339     @SuppressWarnings("ForbidCertainMethod")
340     private List<Uint64> checkDeleteLocalEvpnFLows(Uint32 vpnId, String rd, VrfEntry vrfEntry,
341                                                        VpnNexthop localNextHopInfo) {
342         List<Uint64> returnLocalDpnId = new ArrayList<>();
343         if (localNextHopInfo == null) {
344             //Handle extra routes and imported routes
345         } else {
346             final Uint64 dpnId = localNextHopInfo.getDpnId();
347             jobCoordinator.enqueueJob("FIB-" + rd + "-" + vrfEntry.getDestPrefix(),
348                 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION,
349                     tx -> makeConnectedRoute(dpnId, vpnId, vrfEntry, rd, null, NwConstants.DEL_FLOW,
350                             TransactionAdapter.toWriteTransaction(tx), null))));
351             //TODO: verify below adjacency call need to be optimized (?)
352             deleteLocalAdjacency(dpnId, vpnId, vrfEntry.getDestPrefix(), vrfEntry.getDestPrefix());
353             returnLocalDpnId.add(dpnId);
354         }
355         return returnLocalDpnId;
356     }
357 }