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