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