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