0fee1dd838fd40723a9464bde15ee28fdba5e966
[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.Optional;
13 import com.google.common.base.Preconditions;
14
15 import java.math.BigInteger;
16 import java.util.ArrayList;
17 import java.util.Collections;
18 import java.util.List;
19
20 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
21 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
22 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
23 import org.opendaylight.genius.datastoreutils.listeners.DataTreeEventCallbackRegistrar;
24 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
25 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
26 import org.opendaylight.genius.mdsalutil.ActionInfo;
27 import org.opendaylight.genius.mdsalutil.InstructionInfo;
28 import org.opendaylight.genius.mdsalutil.NwConstants;
29 import org.opendaylight.genius.mdsalutil.actions.ActionGroup;
30 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldEthernetDestination;
31 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldTunnelId;
32 import org.opendaylight.genius.mdsalutil.instructions.InstructionApplyActions;
33 import org.opendaylight.genius.utils.batching.SubTransaction;
34 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
35 import org.opendaylight.netvirt.elanmanager.api.IElanService;
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.to.extraroutes.vpn.extra.routes.Routes;
50 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
51 import org.slf4j.Logger;
52 import org.slf4j.LoggerFactory;
53
54
55 public class EvpnVrfEntryHandler extends BaseVrfEntryHandler implements IVrfEntryHandler {
56     private static final Logger LOG = LoggerFactory.getLogger(EvpnVrfEntryHandler.class);
57     private final ManagedNewTransactionRunner txRunner;
58     private final VrfEntryListener vrfEntryListener;
59     private final BgpRouteVrfEntryHandler bgpRouteVrfEntryHandler;
60     private final NexthopManager nexthopManager;
61     private final JobCoordinator jobCoordinator;
62     private final IElanService elanManager;
63
64     EvpnVrfEntryHandler(DataBroker broker, VrfEntryListener vrfEntryListener,
65             BgpRouteVrfEntryHandler bgpRouteVrfEntryHandler, NexthopManager nexthopManager,
66             JobCoordinator jobCoordinator, IElanService elanManager, 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         this.elanManager = elanManager;
75     }
76
77     @Override
78     public 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         Long vpnId = vpnInstance.getVpnId();
84         Preconditions.checkNotNull(vpnInstance, "Vpn Instance not available " + vrfTableKey.getRouteDistinguisher());
85         Preconditions.checkNotNull(vpnId, "Vpn Instance with rd " + vpnInstance.getVrfId()
86                 + " has null vpnId!");
87         if (RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.CONNECTED) {
88             SubnetRoute subnetRoute = vrfEntry.augmentation(SubnetRoute.class);
89             final List<VpnToDpnList> vpnToDpnList = vpnInstance.getVpnToDpnList();
90             final long elanTag = subnetRoute.getElantag();
91             LOG.trace("SubnetRoute augmented vrfentry found for rd {} prefix {} with elantag {}",
92                     rd, vrfEntry.getDestPrefix(), elanTag);
93             if (vpnToDpnList != null) {
94                 jobCoordinator.enqueueJob("FIB-" + rd + "-" + vrfEntry.getDestPrefix(),
95                     () -> Collections.singletonList(
96                         txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
97                             for (final VpnToDpnList curDpn : vpnToDpnList) {
98                                 if (curDpn.getDpnState() == VpnToDpnList.DpnState.Active) {
99                                     vrfEntryListener.installSubnetRouteInFib(curDpn.getDpnId(), elanTag, rd,
100                                         vpnId, vrfEntry, tx);
101                                 }
102                             }
103                         })));
104             }
105             return;
106         }
107         Prefixes localNextHopInfo = getFibUtil().getPrefixToInterface(vpnInstance.getVpnId(), vrfEntry.getDestPrefix());
108         List<BigInteger> 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     @Override
123     public void removeFlows(InstanceIdentifier<VrfEntry> identifier, VrfEntry vrfEntry, String rd) {
124         final VrfTablesKey vrfTableKey = identifier.firstKeyOf(VrfTables.class);
125         final VpnInstanceOpDataEntry vpnInstance = getFibUtil().getVpnInstanceOpData(
126                 vrfTableKey.getRouteDistinguisher()).get();
127         if (vpnInstance == null) {
128             LOG.error("VPN Instance for rd {} is not available from VPN Op Instance Datastore", rd);
129             return;
130         }
131         VpnNexthop localNextHopInfo = nexthopManager.getVpnNexthop(vpnInstance.getVpnId(),
132                 vrfEntry.getDestPrefix());
133         List<BigInteger> localDpnId = checkDeleteLocalEvpnFLows(vpnInstance.getVpnId(), rd, vrfEntry, localNextHopInfo);
134         deleteRemoteEvpnFlows(rd, vrfEntry, vpnInstance, vrfTableKey, localDpnId);
135         vrfEntryListener.cleanUpOpDataForFib(vpnInstance.getVpnId(), rd, vrfEntry);
136     }
137
138     @Override
139     public void updateFlows(InstanceIdentifier<VrfEntry> identifier, VrfEntry original, VrfEntry update, String rd) {
140         //Not used
141     }
142
143     private List<BigInteger> createLocalEvpnFlows(long vpnId, String rd, VrfEntry vrfEntry,
144                                                   Prefixes localNextHopInfo) {
145         List<BigInteger> returnLocalDpnId = new ArrayList<>();
146         String localNextHopIP = vrfEntry.getDestPrefix();
147         if (localNextHopInfo == null) {
148             //Handle extra routes and imported routes
149             Routes extraRoute = getVpnToExtraroute(vpnId, rd, vrfEntry.getDestPrefix());
150             if (extraRoute != null) {
151                 for (String nextHopIp : extraRoute.getNexthopIpList()) {
152                     LOG.info("NextHop IP for destination {} is {}", vrfEntry.getDestPrefix(), nextHopIp);
153                     if (nextHopIp != null) {
154                         localNextHopInfo = getFibUtil().getPrefixToInterface(vpnId, nextHopIp + "/32");
155                         if (localNextHopInfo != null) {
156                             localNextHopIP = nextHopIp + "/32";
157                             BigInteger dpnId = checkCreateLocalEvpnFlows(localNextHopInfo, localNextHopIP, vpnId,
158                                     rd, vrfEntry);
159                             returnLocalDpnId.add(dpnId);
160                         }
161                     }
162                 }
163             }
164         } else {
165             LOG.info("Creating local EVPN flows for prefix {} rd {} route-paths {} evi {}.",
166                     vrfEntry.getDestPrefix(), rd, vrfEntry.getRoutePaths(), vrfEntry.getL3vni());
167             BigInteger dpnId = checkCreateLocalEvpnFlows(localNextHopInfo, localNextHopIP, vpnId,
168                     rd, vrfEntry);
169             returnLocalDpnId.add(dpnId);
170         }
171         return returnLocalDpnId;
172     }
173
174     private BigInteger checkCreateLocalEvpnFlows(Prefixes localNextHopInfo, String localNextHopIP,
175                                                  final Long vpnId, final String rd,
176                                                  final VrfEntry vrfEntry) {
177         final BigInteger dpnId = localNextHopInfo.getDpnId();
178         String jobKey = FibUtil.getCreateLocalNextHopJobKey(vpnId, dpnId, vrfEntry.getDestPrefix());
179         final long groupId = nexthopManager.createLocalNextHop(vpnId, dpnId,
180             localNextHopInfo.getVpnInterfaceName(), localNextHopIP, vrfEntry.getDestPrefix(),
181             vrfEntry.getGatewayMacAddress());
182         LOG.debug("LocalNextHopGroup {} created/reused for prefix {} rd {} evi {} route-paths {}", groupId,
183             vrfEntry.getDestPrefix(), rd, vrfEntry.getL3vni(), vrfEntry.getRoutePaths());
184
185         final List<InstructionInfo> instructions = Collections.singletonList(
186             new InstructionApplyActions(
187                 Collections.singletonList(new ActionGroup(groupId))));
188         jobCoordinator.enqueueJob(jobKey,
189             () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(
190                 tx -> makeConnectedRoute(dpnId, vpnId, vrfEntry, rd, instructions, NwConstants.ADD_FLOW, tx,
191                         null))));
192         return dpnId;
193     }
194
195     private void createRemoteEvpnFlows(String rd, VrfEntry vrfEntry, VpnInstanceOpDataEntry vpnInstance,
196                                        List<BigInteger> localDpnId, VrfTablesKey vrfTableKey, boolean isNatPrefix) {
197         LOG.info("Creating remote EVPN flows for prefix {} rd {} route-paths {} evi {}",
198             vrfEntry.getDestPrefix(), rd, vrfEntry.getRoutePaths(), vrfEntry.getL3vni());
199         List<VpnToDpnList> vpnToDpnList = vpnInstance.getVpnToDpnList();
200         if (vpnToDpnList != null) {
201             jobCoordinator.enqueueJob("FIB" + rd + vrfEntry.getDestPrefix(),
202                 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
203                     for (VpnToDpnList vpnDpn : vpnToDpnList) {
204                         if (!localDpnId.contains(vpnDpn.getDpnId())) {
205                             if (vpnDpn.getDpnState() == VpnToDpnList.DpnState.Active) {
206                                 createRemoteFibEntry(vpnDpn.getDpnId(), vpnInstance.getVpnId(),
207                                         vrfTableKey, vrfEntry, isNatPrefix, tx);
208                             }
209                         }
210                     }
211                 })));
212         }
213     }
214
215     private void createRemoteFibEntry(final BigInteger remoteDpnId, final long vpnId, final VrfTablesKey vrfTableKey,
216                                       final VrfEntry vrfEntry, boolean isNatPrefix, WriteTransaction tx) {
217
218         String rd = vrfTableKey.getRouteDistinguisher();
219         List<SubTransaction> subTxns =  new ArrayList<>();
220         LOG.debug("createremotefibentry: adding route {} for rd {} with transaction {}",
221                 vrfEntry.getDestPrefix(), rd, tx);
222         List<NexthopManager.AdjacencyResult> tunnelInterfaceList = resolveAdjacency(remoteDpnId, vpnId, vrfEntry, rd);
223
224         if (tunnelInterfaceList.isEmpty()) {
225             LOG.error("Could not get interface for route-paths: {} in vpn {}",
226                     vrfEntry.getRoutePaths(), rd);
227             LOG.warn("Failed to add Route: {} in vpn: {}",
228                     vrfEntry.getDestPrefix(), rd);
229             return;
230         }
231
232         for (NexthopManager.AdjacencyResult adjacencyResult : tunnelInterfaceList) {
233             List<ActionInfo> actionInfos = new ArrayList<>();
234             BigInteger tunnelId = BigInteger.ZERO;
235             String prefix = adjacencyResult.getPrefix();
236             Prefixes prefixInfo = getFibUtil().getPrefixToInterface(vpnId, prefix);
237             String interfaceName = prefixInfo.getVpnInterfaceName();
238             if (vrfEntry.getOrigin().equals(RouteOrigin.BGP.getValue()) || isNatPrefix) {
239                 tunnelId = BigInteger.valueOf(vrfEntry.getL3vni());
240             } else if (FibUtil.isVxlanNetwork(prefixInfo.getNetworkType())) {
241                 tunnelId = BigInteger.valueOf(prefixInfo.getSegmentationId());
242             } else {
243                 try {
244                     StateTunnelList stateTunnelList = getFibUtil().getTunnelState(interfaceName);
245                     if (stateTunnelList == null || stateTunnelList.getOperState() != TunnelOperStatus.Up) {
246                         LOG.trace("Tunnel is not up for interface {}", interfaceName);
247                         return;
248                     }
249                     tunnelId = BigInteger.valueOf(stateTunnelList.getIfIndex());
250                 } catch (ReadFailedException e) {
251                     LOG.error("error in fetching tunnel state for interface {}", interfaceName, e);
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);
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     private void deleteRemoteEvpnFlows(String rd, VrfEntry vrfEntry, VpnInstanceOpDataEntry vpnInstance,
284                                        VrfTablesKey vrfTableKey, List<BigInteger> localDpnIdList) {
285         List<VpnToDpnList> vpnToDpnList = vpnInstance.getVpnToDpnList();
286         List<SubTransaction> subTxns =  new ArrayList<>();
287         if (vpnToDpnList != null) {
288             jobCoordinator.enqueueJob("FIB" + rd + vrfEntry.getDestPrefix(),
289                 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
290                     final Optional<Routes> extraRouteOptional = Optional.absent();
291                     if (localDpnIdList.size() <= 0) {
292                         for (VpnToDpnList curDpn1 : vpnToDpnList) {
293                             if (RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.BGP) {
294                                 if (curDpn1.getDpnState() == VpnToDpnList.DpnState.Active) {
295                                     bgpRouteVrfEntryHandler.deleteRemoteRoute(BigInteger.ZERO, curDpn1.getDpnId(),
296                                             vpnInstance.getVpnId(), vrfTableKey, vrfEntry,
297                                             extraRouteOptional, tx, subTxns);
298                                 }
299                             } else {
300                                 deleteRemoteRoute(BigInteger.ZERO, curDpn1.getDpnId(),
301                                         vpnInstance.getVpnId(), vrfTableKey, vrfEntry,
302                                         extraRouteOptional, tx);
303                             }
304                         }
305                     } else {
306                         for (BigInteger localDpnId : localDpnIdList) {
307                             for (VpnToDpnList curDpn2 : vpnToDpnList) {
308                                 if (!curDpn2.getDpnId().equals(localDpnId)) {
309                                     if (RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.BGP) {
310                                         if (curDpn2.getDpnState() == VpnToDpnList.DpnState.Active) {
311                                             bgpRouteVrfEntryHandler.deleteRemoteRoute(localDpnId, curDpn2.getDpnId(),
312                                                     vpnInstance.getVpnId(), vrfTableKey, vrfEntry,
313                                                     extraRouteOptional, tx, subTxns);
314                                         }
315                                     } else {
316                                         deleteRemoteRoute(localDpnId, curDpn2.getDpnId(),
317                                                 vpnInstance.getVpnId(), vrfTableKey, vrfEntry,
318                                                 extraRouteOptional, tx);
319                                     }
320                                 }
321                             }
322                         }
323                     }
324                 })));
325         }
326     }
327
328     private List<BigInteger> checkDeleteLocalEvpnFLows(long vpnId, String rd, VrfEntry vrfEntry,
329                                                        VpnNexthop localNextHopInfo) {
330         List<BigInteger> returnLocalDpnId = new ArrayList<>();
331         if (localNextHopInfo == null) {
332             //Handle extra routes and imported routes
333         } else {
334             final BigInteger dpnId = localNextHopInfo.getDpnId();
335             jobCoordinator.enqueueJob("FIB-" + rd + "-" + vrfEntry.getDestPrefix(),
336                 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(
337                     tx -> makeConnectedRoute(dpnId, vpnId, vrfEntry, rd, null, NwConstants.DEL_FLOW, tx,
338                             null))));
339             //TODO: verify below adjacency call need to be optimized (?)
340             deleteLocalAdjacency(dpnId, vpnId, vrfEntry.getDestPrefix(), vrfEntry.getDestPrefix());
341             returnLocalDpnId.add(dpnId);
342         }
343         return returnLocalDpnId;
344     }
345 }