2 * Copyright © 2015, 2017 Ericsson India Global Services Pvt Ltd. and others. All rights reserved.
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
8 package org.opendaylight.netvirt.fibmanager;
10 import static org.opendaylight.genius.infra.Datastore.CONFIGURATION;
11 import static org.opendaylight.genius.infra.Datastore.OPERATIONAL;
12 import static org.opendaylight.genius.mdsalutil.NWUtil.isIpv4Address;
14 import com.google.common.base.Optional;
15 import com.google.common.base.Preconditions;
16 import java.math.BigInteger;
17 import java.util.ArrayList;
18 import java.util.Collections;
19 import java.util.HashMap;
20 import java.util.List;
21 import java.util.Locale;
23 import java.util.Objects;
24 import java.util.concurrent.CopyOnWriteArrayList;
25 import java.util.concurrent.ExecutionException;
26 import java.util.concurrent.Future;
27 import javax.annotation.PreDestroy;
28 import javax.inject.Inject;
29 import javax.inject.Singleton;
30 import org.eclipse.jdt.annotation.Nullable;
31 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
32 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
33 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
34 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
35 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
36 import org.opendaylight.genius.interfacemanager.interfaces.IInterfaceManager;
37 import org.opendaylight.genius.itm.globals.ITMConstants;
38 import org.opendaylight.genius.mdsalutil.ActionInfo;
39 import org.opendaylight.genius.mdsalutil.BucketInfo;
40 import org.opendaylight.genius.mdsalutil.GroupEntity;
41 import org.opendaylight.genius.mdsalutil.MDSALUtil;
42 import org.opendaylight.genius.mdsalutil.NwConstants;
43 import org.opendaylight.genius.mdsalutil.actions.ActionGroup;
44 import org.opendaylight.genius.mdsalutil.actions.ActionNxResubmit;
45 import org.opendaylight.genius.mdsalutil.actions.ActionOutput;
46 import org.opendaylight.genius.mdsalutil.actions.ActionPushMpls;
47 import org.opendaylight.genius.mdsalutil.actions.ActionPushVlan;
48 import org.opendaylight.genius.mdsalutil.actions.ActionRegLoad;
49 import org.opendaylight.genius.mdsalutil.actions.ActionRegMove;
50 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldEthernetDestination;
51 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldEthernetSource;
52 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldTunnelId;
53 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldVlanVid;
54 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
55 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
56 import org.opendaylight.infrautils.utils.concurrent.ListenableFutures;
57 import org.opendaylight.netvirt.elanmanager.api.IElanService;
58 import org.opendaylight.netvirt.fibmanager.api.L3VPNTransportTypes;
59 import org.opendaylight.netvirt.vpnmanager.api.VpnExtraRouteHelper;
60 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana._if.type.rev170119.L2vlan;
61 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana._if.type.rev170119.Tunnel;
62 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddressBuilder;
63 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.InterfaceType;
64 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.OutputActionCase;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.PushVlanActionCase;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetFieldCase;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.AllocateIdInput;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.AllocateIdInputBuilder;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.AllocateIdOutput;
72 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.CreateIdPoolInput;
73 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.CreateIdPoolInputBuilder;
74 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.CreateIdPoolOutput;
75 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
76 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.ReleaseIdInput;
77 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.ReleaseIdInputBuilder;
78 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.ReleaseIdOutput;
79 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeBase;
80 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeGre;
81 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeMplsOverGre;
82 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeVxlan;
83 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetEgressActionsForInterfaceInputBuilder;
84 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetEgressActionsForInterfaceOutput;
85 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.OdlInterfaceRpcService;
86 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.TunnelOperStatus;
87 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.TunnelsState;
88 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.tunnels_state.StateTunnelList;
89 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.tunnels_state.StateTunnelListKey;
90 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetEgressActionsForTunnelInputBuilder;
91 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetEgressActionsForTunnelOutput;
92 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetInternalOrExternalInterfaceNameInputBuilder;
93 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetInternalOrExternalInterfaceNameOutput;
94 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetTunnelInterfaceNameInputBuilder;
95 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetTunnelInterfaceNameOutput;
96 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.ItmRpcService;
97 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.lockmanager.rev160413.LockManagerService;
98 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.AddGroupInput;
99 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.AddGroupInputBuilder;
100 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.AddGroupOutput;
101 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.SalGroupService;
102 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupId;
103 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupRef;
104 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupTypes;
105 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.group.Buckets;
106 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.group.buckets.Bucket;
107 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.Group;
108 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
109 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.SegmentTypeBase;
110 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.SegmentTypeFlat;
111 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.SegmentTypeVlan;
112 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.instances.ElanInstance;
113 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentries.VrfEntry;
114 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.L3nexthop;
115 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.VpnNexthops;
116 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.VpnNexthopsKey;
117 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.vpnnexthops.VpnNexthop;
118 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.vpnnexthops.VpnNexthopBuilder;
119 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.vpnnexthops.VpnNexthopKey;
120 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.vpnnexthops.vpnnexthop.IpAdjacencies;
121 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.vpnnexthops.vpnnexthop.IpAdjacenciesBuilder;
122 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.vpnnexthops.vpnnexthop.IpAdjacenciesKey;
123 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.ConfTransportTypeL3vpn;
124 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.ConfTransportTypeL3vpnBuilder;
125 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.dpid.l3vpn.lb.nexthops.DpnLbNexthops;
126 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.l3vpn.lb.nexthops.Nexthops;
127 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.prefix.to._interface.vpn.ids.Prefixes;
128 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.to.extraroutes.vpn.extra.routes.Routes;
129 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg6;
130 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.add.group.input.buckets.bucket.action.action.NxActionResubmitRpcAddGroupCase;
131 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.nodes.node.table.flow.instructions.instruction.instruction.apply.actions._case.apply.actions.action.action.NxActionRegLoadNodesNodeTableFlowApplyActionsCase;
132 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.nx.action.reg.load.grouping.NxRegLoad;
133 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
134 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.InstanceIdentifierBuilder;
135 import org.opendaylight.yangtools.yang.common.RpcResult;
136 import org.slf4j.Logger;
137 import org.slf4j.LoggerFactory;
140 public class NexthopManager implements AutoCloseable {
141 private static final Logger LOG = LoggerFactory.getLogger(NexthopManager.class);
142 private static final String NEXTHOP_ID_POOL_NAME = "nextHopPointerPool";
143 private static final long WAIT_TIME_FOR_SYNC_INSTALL = Long.getLong("wait.time.sync.install", 300L);
144 private static final long WAIT_TIME_TO_ACQUIRE_LOCK = 3000L;
145 private static final int SELECT_GROUP_WEIGHT = 1;
147 private final DataBroker dataBroker;
148 private final ManagedNewTransactionRunner txRunner;
149 private final IMdsalApiManager mdsalApiManager;
150 private final OdlInterfaceRpcService odlInterfaceRpcService;
151 private final ItmRpcService itmManager;
152 private final IdManagerService idManager;
153 private final IElanService elanService;
154 private final LockManagerService lockManager;
155 private final SalGroupService salGroupService;
156 private final JobCoordinator jobCoordinator;
157 private final FibUtil fibUtil;
158 private final IInterfaceManager interfaceManager;
159 private volatile L3VPNTransportTypes configuredTransportTypeL3VPN = L3VPNTransportTypes.Invalid;
162 * Provides nexthop functions.
163 * Creates group ID pool
165 * @param dataBroker - dataBroker reference
166 * @param mdsalApiManager - mdsalApiManager reference
167 * @param idManager - idManager reference
168 * @param odlInterfaceRpcService - odlInterfaceRpcService reference
169 * @param itmManager - itmManager reference
172 public NexthopManager(final DataBroker dataBroker,
173 final IMdsalApiManager mdsalApiManager,
174 final IdManagerService idManager,
175 final OdlInterfaceRpcService odlInterfaceRpcService,
176 final ItmRpcService itmManager,
177 final LockManagerService lockManager,
178 final IElanService elanService,
179 final SalGroupService salGroupService,
180 final JobCoordinator jobCoordinator,
181 final FibUtil fibUtil,
182 final IInterfaceManager interfaceManager) {
183 this.dataBroker = dataBroker;
184 this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
185 this.mdsalApiManager = mdsalApiManager;
186 this.idManager = idManager;
187 this.odlInterfaceRpcService = odlInterfaceRpcService;
188 this.itmManager = itmManager;
189 this.elanService = elanService;
190 this.salGroupService = salGroupService;
191 this.jobCoordinator = jobCoordinator;
192 this.fibUtil = fibUtil;
193 this.lockManager = lockManager;
194 this.interfaceManager = interfaceManager;
198 private void createIdPool() {
199 CreateIdPoolInput createPool = new CreateIdPoolInputBuilder()
200 .setPoolName(NEXTHOP_ID_POOL_NAME)
205 Future<RpcResult<CreateIdPoolOutput>> result = idManager.createIdPool(createPool);
206 if (result != null && result.get().isSuccessful()) {
207 LOG.info("Created IdPool for NextHopPointerPool");
209 } catch (InterruptedException | ExecutionException e) {
210 LOG.error("Failed to create idPool for NextHopPointerPool", e);
214 private String getNextHopKey(long vpnId, String ipAddress) {
215 return "nexthop." + vpnId + ipAddress;
218 String getRemoteSelectGroupKey(long vpnId, String ipAddress) {
219 return "remote.ecmp.nexthop." + vpnId + ipAddress;
222 String getLocalSelectGroupKey(long vpnId, String ipAddress) {
223 return "local.ecmp.nexthop." + vpnId + ipAddress;
226 public ItmRpcService getItmManager() {
230 protected long createNextHopPointer(String nexthopKey) {
231 AllocateIdInput getIdInput = new AllocateIdInputBuilder()
232 .setPoolName(NEXTHOP_ID_POOL_NAME).setIdKey(nexthopKey)
234 //TODO: Proper error handling once IdManager code is complete
236 Future<RpcResult<AllocateIdOutput>> result = idManager.allocateId(getIdInput);
237 RpcResult<AllocateIdOutput> rpcResult = result.get();
238 return rpcResult.getResult().getIdValue();
239 } catch (NullPointerException | InterruptedException | ExecutionException e) {
245 protected void removeNextHopPointer(String nexthopKey) {
246 ReleaseIdInput idInput = new ReleaseIdInputBuilder()
247 .setPoolName(NEXTHOP_ID_POOL_NAME)
248 .setIdKey(nexthopKey).build();
250 RpcResult<ReleaseIdOutput> rpcResult = idManager.releaseId(idInput).get();
251 if (!rpcResult.isSuccessful()) {
252 LOG.error("RPC Call to Get Unique Id for nexthopKey {} returned with Errors {}",
253 nexthopKey, rpcResult.getErrors());
255 } catch (InterruptedException | ExecutionException e) {
256 LOG.warn("Exception when getting Unique Id for key {}", nexthopKey, e);
260 protected List<ActionInfo> getEgressActionsForInterface(final String ifName, int actionKey,
261 boolean isTunnelInterface) {
262 List<Action> actions;
264 if (isTunnelInterface && interfaceManager.isItmDirectTunnelsEnabled()) {
265 RpcResult<GetEgressActionsForTunnelOutput> rpcResult =
266 itmManager.getEgressActionsForTunnel(new GetEgressActionsForTunnelInputBuilder()
267 .setIntfName(ifName).build()).get();
268 if (!rpcResult.isSuccessful()) {
269 LOG.error("RPC Call to Get egress tunnel actions for interface {} returned with Errors {}",
270 ifName, rpcResult.getErrors());
271 return Collections.emptyList();
273 actions = rpcResult.getResult().nonnullAction();
276 RpcResult<GetEgressActionsForInterfaceOutput> rpcResult = odlInterfaceRpcService
277 .getEgressActionsForInterface(new GetEgressActionsForInterfaceInputBuilder()
278 .setIntfName(ifName).build()).get();
279 if (!rpcResult.isSuccessful()) {
280 LOG.error("RPC Call to Get egress vm actions for interface {} returned with Errors {}",
281 ifName, rpcResult.getErrors());
282 return Collections.emptyList();
284 actions = rpcResult.getResult().nonnullAction();
287 List<ActionInfo> listActionInfo = new ArrayList<>();
288 for (Action action : actions) {
289 actionKey = action.key().getOrder() + actionKey;
290 org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action
291 actionClass = action.getAction();
292 if (actionClass instanceof OutputActionCase) {
293 listActionInfo.add(new ActionOutput(actionKey,
294 ((OutputActionCase) actionClass).getOutputAction().getOutputNodeConnector()));
295 } else if (actionClass instanceof PushVlanActionCase) {
296 listActionInfo.add(new ActionPushVlan(actionKey));
297 } else if (actionClass instanceof SetFieldCase) {
298 if (((SetFieldCase) actionClass).getSetField().getVlanMatch() != null) {
299 int vlanVid = ((SetFieldCase) actionClass).getSetField().getVlanMatch()
300 .getVlanId().getVlanId().getValue();
301 listActionInfo.add(new ActionSetFieldVlanVid(actionKey, vlanVid));
303 } else if (actionClass instanceof NxActionResubmitRpcAddGroupCase) {
304 Short tableId = ((NxActionResubmitRpcAddGroupCase) actionClass).getNxResubmit().getTable();
305 listActionInfo.add(new ActionNxResubmit(actionKey, tableId));
306 } else if (actionClass instanceof NxActionRegLoadNodesNodeTableFlowApplyActionsCase) {
307 NxRegLoad nxRegLoad =
308 ((NxActionRegLoadNodesNodeTableFlowApplyActionsCase) actionClass).getNxRegLoad();
309 listActionInfo.add(new ActionRegLoad(actionKey, NxmNxReg6.class,
310 nxRegLoad.getDst().getStart(), nxRegLoad.getDst().getEnd(),
311 nxRegLoad.getValue().longValue()));
314 return listActionInfo;
315 } catch (InterruptedException | ExecutionException e) {
316 LOG.warn("Exception when egress actions for interface {}", ifName, e);
318 LOG.warn("Exception when egress actions for interface {}", ifName);
319 return Collections.emptyList();
323 protected String getTunnelInterfaceName(BigInteger srcDpId, BigInteger dstDpId) {
324 Class<? extends TunnelTypeBase> tunType = getReqTunType(getReqTransType().toUpperCase(Locale.getDefault()));
325 Future<RpcResult<GetTunnelInterfaceNameOutput>> result;
327 result = itmManager.getTunnelInterfaceName(new GetTunnelInterfaceNameInputBuilder()
328 .setSourceDpid(srcDpId)
329 .setDestinationDpid(dstDpId)
330 .setTunnelType(tunType)
332 RpcResult<GetTunnelInterfaceNameOutput> rpcResult = result.get();
333 if (!rpcResult.isSuccessful()) {
334 LOG.warn("RPC Call to getTunnelInterfaceId returned with Errors {}", rpcResult.getErrors());
336 return rpcResult.getResult().getInterfaceName();
338 } catch (InterruptedException | ExecutionException e) {
339 LOG.warn("Exception when getting tunnel interface Id for tunnel between {} and {}", srcDpId, dstDpId, e);
345 protected String getTunnelInterfaceName(BigInteger srcDpId, org.opendaylight.yang.gen.v1.urn.ietf.params
346 .xml.ns.yang.ietf.inet.types.rev130715.IpAddress dstIp, Class<? extends TunnelTypeBase> tunnelType) {
347 Future<RpcResult<GetInternalOrExternalInterfaceNameOutput>> result;
349 LOG.debug("Trying to fetch tunnel interface name for source dpn {} destIp {} tunType {}", srcDpId,
350 dstIp.stringValue(), tunnelType.getName());
351 result = itmManager.getInternalOrExternalInterfaceName(new GetInternalOrExternalInterfaceNameInputBuilder()
352 .setSourceDpid(srcDpId)
353 .setDestinationIp(dstIp)
354 .setTunnelType(tunnelType)
356 RpcResult<GetInternalOrExternalInterfaceNameOutput> rpcResult = result.get();
357 if (!rpcResult.isSuccessful()) {
358 LOG.warn("RPC Call to getTunnelInterfaceName returned with Errors {}", rpcResult.getErrors());
360 return rpcResult.getResult().getInterfaceName();
362 } catch (InterruptedException | ExecutionException e) {
363 LOG.warn("Exception when getting tunnel interface Id for tunnel between {} and {}", srcDpId, dstIp, e);
369 public long getLocalNextHopGroup(long vpnId,
370 String ipNextHopAddress) {
371 long groupId = createNextHopPointer(getNextHopKey(vpnId, ipNextHopAddress));
372 if (groupId == FibConstants.INVALID_GROUP_ID) {
373 LOG.error("Unable to allocate groupId for vpnId {} , prefix {}", vpnId, ipNextHopAddress);
378 public long getLocalSelectGroup(long vpnId,
379 String ipNextHopAddress) {
380 long groupId = createNextHopPointer(getLocalSelectGroupKey(vpnId, ipNextHopAddress));
381 if (groupId == FibConstants.INVALID_GROUP_ID) {
382 LOG.error("Unable to allocate groupId for vpnId {} , prefix {}", vpnId, ipNextHopAddress);
387 public long createLocalNextHop(long vpnId, BigInteger dpnId, String ifName,
388 String primaryIpAddress, String currDestIpPrefix,
389 String gwMacAddress) {
390 String vpnName = fibUtil.getVpnNameFromId(vpnId);
391 if (vpnName == null) {
394 String macAddress = fibUtil.getMacAddressFromPrefix(ifName, vpnName, primaryIpAddress);
396 long groupId = createNextHopPointer(getNextHopKey(vpnId, primaryIpAddress));
398 LOG.error("Unable to allocate groupId for vpnId {} , IntfName {}, primaryIpAddress {} curIpPrefix {}",
399 vpnId, ifName, primaryIpAddress, currDestIpPrefix);
402 String nextHopLockStr = vpnId + primaryIpAddress;
403 String jobKey = FibUtil.getCreateLocalNextHopJobKey(vpnId, dpnId, currDestIpPrefix);
404 jobCoordinator.enqueueJob(jobKey, () -> {
406 if (FibUtil.lockCluster(lockManager, nextHopLockStr, WAIT_TIME_TO_ACQUIRE_LOCK)) {
407 VpnNexthop nexthop = getVpnNexthop(vpnId, primaryIpAddress);
408 LOG.trace("nexthop: {} retrieved for vpnId {}, prefix {}, ifName {} on dpn {}", nexthop, vpnId,
409 primaryIpAddress, ifName, dpnId);
410 if (nexthop == null) {
411 String encMacAddress = macAddress == null
412 ? fibUtil.getMacAddressFromPrefix(ifName, vpnName, primaryIpAddress) : macAddress;
413 List<BucketInfo> listBucketInfo = new ArrayList<>();
414 List<ActionInfo> listActionInfo = new ArrayList<>();
417 if (encMacAddress != null) {
418 if (gwMacAddress != null) {
419 LOG.trace("The Local NextHop Group Source Mac {} for VpnInterface {} on VPN {}",
420 gwMacAddress, ifName, vpnId);
421 listActionInfo.add(new ActionSetFieldEthernetSource(actionKey++,
422 new MacAddress(gwMacAddress)));
424 listActionInfo.add(new ActionSetFieldEthernetDestination(actionKey++,
425 new MacAddress(encMacAddress)));
426 // listActionInfo.add(0, new ActionPopMpls());
428 // FIXME: Log message here.
429 LOG.debug("mac address for new local nexthop is null");
431 listActionInfo.addAll(getEgressActionsForInterface(ifName, actionKey, false));
432 BucketInfo bucket = new BucketInfo(listActionInfo);
434 listBucketInfo.add(bucket);
435 GroupEntity groupEntity = MDSALUtil.buildGroupEntity(dpnId, groupId, primaryIpAddress,
436 GroupTypes.GroupAll, listBucketInfo);
437 LOG.trace("Install LNH Group: id {}, mac address {}, interface {} for prefix {}", groupId,
438 encMacAddress, ifName, primaryIpAddress);
439 //Try to install group directly on the DPN bypassing the FRM, in order to avoid waiting for the
440 // group to get installed before programming the flows
441 installGroupOnDpn(groupId, dpnId, primaryIpAddress, listBucketInfo,
442 getNextHopKey(vpnId, primaryIpAddress), GroupTypes.GroupAll);
444 mdsalApiManager.syncInstallGroup(groupEntity);
446 addVpnNexthopToDS(dpnId, vpnId, primaryIpAddress, currDestIpPrefix, groupId);
449 // Ignore adding new prefix , if it already exists
450 List<IpAdjacencies> prefixesList = nexthop.getIpAdjacencies();
451 IpAdjacencies prefix = new IpAdjacenciesBuilder().setIpAdjacency(currDestIpPrefix).build();
452 if (prefixesList != null && prefixesList.contains(prefix)) {
453 LOG.trace("Prefix {} is already present in l3nextHop {} ", currDestIpPrefix, nexthop);
455 IpAdjacenciesBuilder ipPrefixesBuilder =
456 new IpAdjacenciesBuilder().withKey(new IpAdjacenciesKey(currDestIpPrefix));
457 LOG.trace("Updating prefix {} to vpnNextHop {} Operational DS", currDestIpPrefix, nexthop);
458 MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL,
459 getVpnNextHopIpPrefixIdentifier(vpnId, primaryIpAddress, currDestIpPrefix),
460 ipPrefixesBuilder.build());
465 FibUtil.unlockCluster(lockManager, nextHopLockStr);
467 return Collections.emptyList();
472 private void installGroupOnDpn(long groupId, BigInteger dpnId, String groupName, List<BucketInfo> bucketsInfo,
473 String nextHopKey, GroupTypes groupType) {
474 NodeRef nodeRef = FibUtil.buildNodeRef(dpnId);
475 Buckets buckets = FibUtil.buildBuckets(bucketsInfo);
476 GroupRef groupRef = new GroupRef(FibUtil.buildGroupInstanceIdentifier(groupId, dpnId));
477 AddGroupInput input = new AddGroupInputBuilder().setNode(nodeRef).setGroupId(new GroupId(groupId))
478 .setBuckets(buckets).setGroupRef(groupRef).setGroupType(groupType)
479 .setGroupName(groupName).build();
480 Future<RpcResult<AddGroupOutput>> groupStats = salGroupService.addGroup(input);
481 RpcResult<AddGroupOutput> rpcResult = null;
483 rpcResult = groupStats.get();
484 if (rpcResult != null && rpcResult.isSuccessful()) {
485 LOG.info("Group {} with key {} has been successfully installed directly on dpn {}.", groupId,
488 LOG.error("Unable to install group {} with key {} directly on dpn {} due to {}.", groupId, nextHopKey,
489 dpnId, rpcResult != null ? rpcResult.getErrors() : null);
491 } catch (InterruptedException | ExecutionException e) {
492 LOG.error("Error while installing group {} directly on dpn {}", groupId, dpnId);
496 protected void addVpnNexthopToDS(BigInteger dpnId, long vpnId, String primaryIpAddr,
497 String currIpAddr, long egressPointer) {
498 InstanceIdentifierBuilder<VpnNexthops> idBuilder = InstanceIdentifier.builder(L3nexthop.class)
499 .child(VpnNexthops.class, new VpnNexthopsKey(vpnId));
501 List<IpAdjacencies> ipPrefixesList = new ArrayList<>();
502 IpAdjacencies prefix = new IpAdjacenciesBuilder().setIpAdjacency(currIpAddr).build();
503 ipPrefixesList.add(prefix);
504 // Add nexthop to vpn node
505 VpnNexthop nh = new VpnNexthopBuilder()
506 .withKey(new VpnNexthopKey(primaryIpAddr))
508 .setIpAdjacencies(ipPrefixesList)
509 .setEgressPointer(egressPointer).build();
511 InstanceIdentifier<VpnNexthop> id1 = idBuilder
512 .child(VpnNexthop.class, new VpnNexthopKey(primaryIpAddr)).build();
513 LOG.trace("Adding vpnnextHop {} to Operational DS", nh);
514 MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL, id1, nh);
518 protected InstanceIdentifier<IpAdjacencies> getVpnNextHopIpPrefixIdentifier(long vpnId, String primaryIpAddress,
520 InstanceIdentifier<IpAdjacencies> id = InstanceIdentifier.builder(L3nexthop.class)
521 .child(VpnNexthops.class, new VpnNexthopsKey(vpnId))
522 .child(VpnNexthop.class, new VpnNexthopKey(primaryIpAddress))
523 .child(IpAdjacencies.class, new IpAdjacenciesKey(ipPrefix)).build();
528 protected VpnNexthop getVpnNexthop(long vpnId, String ipAddress) {
530 // check if vpn node is there
531 InstanceIdentifierBuilder<VpnNexthops> idBuilder =
532 InstanceIdentifier.builder(L3nexthop.class).child(VpnNexthops.class,
533 new VpnNexthopsKey(vpnId));
534 InstanceIdentifier<VpnNexthops> id = idBuilder.build();
535 Optional<VpnNexthops> vpnNexthops = MDSALUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, id);
536 if (vpnNexthops.isPresent()) {
537 // get nexthops list for vpn
538 List<VpnNexthop> nexthops = vpnNexthops.get().nonnullVpnNexthop();
539 for (VpnNexthop nexthop : nexthops) {
540 if (Objects.equals(nexthop.getIpAddress(), ipAddress)) {
542 LOG.trace("VpnNextHop : {}", nexthop);
546 // return null if not found
552 public AdjacencyResult getRemoteNextHopPointer(BigInteger remoteDpnId, long vpnId, String prefixIp,
553 @Nullable String nextHopIp, Class<? extends TunnelTypeBase> tunnelType) {
554 String egressIfName = null;
555 LOG.trace("getRemoteNextHopPointer: input [remoteDpnId {}, vpnId {}, prefixIp {}, nextHopIp {} ]", remoteDpnId,
556 vpnId, prefixIp, nextHopIp);
558 Class<? extends InterfaceType> egressIfType;
559 ElanInstance elanInstance = getElanInstanceForPrefix(vpnId, prefixIp);
560 if (elanInstance != null) {
561 egressIfType = getInterfaceType(elanInstance);
563 LOG.warn("Failed to determine network type for prefixIp {} using tunnel", prefixIp);
564 egressIfType = Tunnel.class;
567 if (Tunnel.class.equals(egressIfType)) {
568 egressIfName = getTunnelRemoteNextHopPointer(remoteDpnId, nextHopIp, tunnelType);
570 egressIfName = getExtPortRemoteNextHopPointer(remoteDpnId, elanInstance);
573 LOG.trace("NextHop pointer for prefixIp {} vpnId {} dpnId {} is {}", prefixIp, vpnId, remoteDpnId,
575 return egressIfName != null ? new AdjacencyResult(egressIfName, egressIfType, nextHopIp,
579 private void removeVpnNexthopFromDS(long vpnId, String ipPrefix) {
581 InstanceIdentifierBuilder<VpnNexthop> idBuilder = InstanceIdentifier.builder(L3nexthop.class)
582 .child(VpnNexthops.class, new VpnNexthopsKey(vpnId))
583 .child(VpnNexthop.class, new VpnNexthopKey(ipPrefix));
584 InstanceIdentifier<VpnNexthop> id = idBuilder.build();
586 LOG.trace("Removing vpn next hop from datastore : {}", id);
587 MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.OPERATIONAL, id);
590 public void removeLocalNextHop(BigInteger dpnId, Long vpnId, String primaryIpAddress, String currDestIpPrefix) {
591 String nextHopLockStr = vpnId + primaryIpAddress;
593 if (FibUtil.lockCluster(lockManager, nextHopLockStr, WAIT_TIME_TO_ACQUIRE_LOCK)) {
594 VpnNexthop nh = getVpnNexthop(vpnId, primaryIpAddress);
596 List<IpAdjacencies> prefixesList = new ArrayList<>(nh.nonnullIpAdjacencies());
597 IpAdjacencies prefix = new IpAdjacenciesBuilder().setIpAdjacency(currDestIpPrefix).build();
598 prefixesList.remove(prefix);
599 if (prefixesList.isEmpty()) { //remove the group only if there are no more flows using this group
600 GroupEntity groupEntity = MDSALUtil.buildGroupEntity(dpnId, nh.getEgressPointer(),
601 primaryIpAddress, GroupTypes.GroupAll, Collections.emptyList());
603 mdsalApiManager.removeGroup(groupEntity);
605 removeVpnNexthopFromDS(vpnId, primaryIpAddress);
607 removeNextHopPointer(getNextHopKey(vpnId, primaryIpAddress));
608 LOG.debug("Local Next hop {} for {} {} on dpn {} successfully deleted",
609 nh.getEgressPointer(), vpnId, primaryIpAddress, dpnId);
611 //remove the currIpPrefx from IpPrefixList of the vpnNexthop
612 LOG.trace("Removing the prefix {} from vpnNextHop {} Operational DS", currDestIpPrefix, nh);
613 MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.OPERATIONAL,
614 getVpnNextHopIpPrefixIdentifier(vpnId, primaryIpAddress, currDestIpPrefix));
618 LOG.error("Local NextHop for VpnId {} curIpPrefix {} on dpn {} primaryIpAddress {} not deleted",
619 vpnId, currDestIpPrefix, dpnId, primaryIpAddress);
623 FibUtil.unlockCluster(lockManager, nextHopLockStr);
627 public void setConfTransType(String service, String transportType) {
629 if (!service.equalsIgnoreCase("L3VPN")) {
630 LOG.error("Incorrect service {} provided for setting the transport type.", service);
634 L3VPNTransportTypes transType = L3VPNTransportTypes.validateTransportType(transportType
635 .toUpperCase(Locale.getDefault()));
637 if (transType != L3VPNTransportTypes.Invalid) {
638 configuredTransportTypeL3VPN = transType;
642 public void writeConfTransTypeConfigDS() {
643 MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, getConfTransportTypeIdentifier(),
644 createConfTransportType(configuredTransportTypeL3VPN.getTransportType()));
647 public L3VPNTransportTypes getConfiguredTransportTypeL3VPN() {
648 return this.configuredTransportTypeL3VPN;
651 public String getReqTransType() {
652 if (configuredTransportTypeL3VPN == L3VPNTransportTypes.Invalid) {
654 * Restart scenario, Read from the ConfigDS.
655 * if the value is Unset, cache value as VxLAN.
657 LOG.trace("configureTransportType is not yet set.");
658 Optional<ConfTransportTypeL3vpn> configuredTransTypeFromConfig =
659 MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, getConfTransportTypeIdentifier());
661 if (configuredTransTypeFromConfig.isPresent()) {
662 if (TunnelTypeGre.class.equals(configuredTransTypeFromConfig.get().getTransportType())) {
663 configuredTransportTypeL3VPN = L3VPNTransportTypes.GRE;
665 configuredTransportTypeL3VPN = L3VPNTransportTypes.VxLAN;
667 LOG.trace("configuredTransportType set from config DS to {}",
668 getConfiguredTransportTypeL3VPN().getTransportType());
670 setConfTransType("L3VPN", L3VPNTransportTypes.VxLAN.getTransportType());
671 LOG.trace("configuredTransportType is not set in the Config DS. VxLAN as default will be used.");
674 LOG.trace("configuredTransportType is set as {}", getConfiguredTransportTypeL3VPN().getTransportType());
676 return getConfiguredTransportTypeL3VPN().getTransportType();
679 public InstanceIdentifier<ConfTransportTypeL3vpn> getConfTransportTypeIdentifier() {
680 return InstanceIdentifier.builder(ConfTransportTypeL3vpn.class).build();
683 private ConfTransportTypeL3vpn createConfTransportType(String type) {
684 ConfTransportTypeL3vpn confTransType;
686 case ITMConstants.TUNNEL_TYPE_GRE:
687 confTransType = new ConfTransportTypeL3vpnBuilder().setTransportType(TunnelTypeGre.class).build();
688 LOG.trace("Setting the confTransportType to GRE.");
690 case ITMConstants.TUNNEL_TYPE_VXLAN:
691 confTransType = new ConfTransportTypeL3vpnBuilder().setTransportType(TunnelTypeVxlan.class).build();
692 LOG.trace("Setting the confTransportType to VxLAN.");
695 LOG.trace("Invalid transport type {} passed to Config DS ", type);
696 confTransType = null;
699 return confTransType;
702 public Class<? extends TunnelTypeBase> getReqTunType(String transportType) {
703 switch (transportType) {
705 return TunnelTypeVxlan.class;
707 return TunnelTypeGre.class;
709 return TunnelTypeMplsOverGre.class;
713 public String getTransportTypeStr(String tunType) {
714 if (tunType.equals(TunnelTypeVxlan.class.toString())) {
715 return ITMConstants.TUNNEL_TYPE_VXLAN;
716 } else if (tunType.equals(TunnelTypeGre.class.toString())) {
717 return ITMConstants.TUNNEL_TYPE_GRE;
718 } else if (tunType.equals(TunnelTypeMplsOverGre.class.toString())) {
719 return ITMConstants.TUNNEL_TYPE_MPLSoGRE;
721 return ITMConstants.TUNNEL_TYPE_INVALID;
727 public void close() {
728 LOG.info("{} close", getClass().getSimpleName());
731 // TODO Clean up the exception handling
732 @SuppressWarnings("checkstyle:IllegalCatch")
734 private String getTunnelRemoteNextHopPointer(BigInteger remoteDpnId, String nextHopIp,
735 Class<? extends TunnelTypeBase> tunnelType) {
736 if (nextHopIp != null && !nextHopIp.isEmpty()) {
738 // here use the config for tunnel type param
739 return getTunnelInterfaceName(remoteDpnId,
740 org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddressBuilder
741 .getDefaultInstance(nextHopIp), tunnelType);
742 } catch (Exception ex) {
743 LOG.error("Error while retrieving nexthop pointer for nexthop {} remoteDpn {}",
744 nextHopIp, remoteDpnId, ex);
751 private String getExtPortRemoteNextHopPointer(BigInteger remoteDpnId, ElanInstance elanInstance) {
752 return elanService.getExternalElanInterface(elanInstance.getElanInstanceName(), remoteDpnId);
756 * Get the interface type associated with the type of ELAN used for routing
757 * traffic to/from remote compute nodes.
759 * @param elanInstance The elan instance
760 * @return L2vlan for flat/VLAN network type and Tunnel otherwise
762 private Class<? extends InterfaceType> getInterfaceType(ElanInstance elanInstance) {
763 Class<? extends SegmentTypeBase> segmentType = elanInstance.getSegmentType();
764 if (SegmentTypeFlat.class.equals(segmentType) || SegmentTypeVlan.class.equals(segmentType)) {
771 private ElanInstance getElanInstanceForPrefix(long vpnId, String prefixIp) {
772 ElanInstance elanInstance = null;
773 Prefixes prefix = fibUtil.getPrefixToInterface(vpnId, prefixIp);
774 if (prefix != null) {
775 if (prefix.getNetworkId() != null) {
776 elanInstance = elanService.getElanInstance(prefix.getNetworkId().getValue());
783 static class AdjacencyResult {
784 private final String interfaceName;
785 private final Class<? extends InterfaceType> interfaceType;
786 private final String nextHopIp;
787 private final String prefix;
789 AdjacencyResult(String interfaceName, Class<? extends InterfaceType> interfaceType, String nextHopIp,
791 this.interfaceName = interfaceName;
792 this.interfaceType = interfaceType;
793 this.nextHopIp = nextHopIp;
794 this.prefix = prefix;
797 public String getInterfaceName() {
798 return interfaceName;
801 public Class<? extends InterfaceType> getInterfaceType() {
802 return interfaceType;
805 public String getNextHopIp() {
809 public String getPrefix() {
814 public int hashCode() {
815 final int prime = 31;
817 result = prime * result + (interfaceName == null ? 0 : interfaceName.hashCode());
822 public boolean equals(Object obj) {
827 if (getClass() != obj.getClass()) {
830 AdjacencyResult other = (AdjacencyResult) obj;
831 return interfaceName.equals(other.interfaceName);
836 protected long setupLoadBalancingNextHop(Long parentVpnId, BigInteger dpnId,
837 String destPrefix, List<BucketInfo> localBucketInfo, List<BucketInfo> remoteBucketInfo) {
838 long remoteGroupId = createNextHopPointer(getRemoteSelectGroupKey(parentVpnId, destPrefix));
839 if (remoteGroupId == FibConstants.INVALID_GROUP_ID) {
840 LOG.error("Unable to allocate/retrieve remote groupId for vpnId {} , prefix {}", parentVpnId, destPrefix);
841 return remoteGroupId;
843 long localGroupId = createNextHopPointer(getLocalSelectGroupKey(parentVpnId, destPrefix));
844 if (localGroupId == FibConstants.INVALID_GROUP_ID) {
845 LOG.error("Unable to allocate/retrieve local groupId for vpnId {} , prefix {}",
846 parentVpnId, destPrefix);
847 return remoteGroupId;
849 List<BucketInfo> combinedBucketInfo = new ArrayList<>();
850 combinedBucketInfo.addAll(localBucketInfo);
851 combinedBucketInfo.addAll(remoteBucketInfo);
852 GroupEntity remoteGroupEntity = MDSALUtil.buildGroupEntity(
853 dpnId, remoteGroupId, destPrefix, GroupTypes.GroupSelect, combinedBucketInfo);
854 GroupEntity localGroupEntity = MDSALUtil.buildGroupEntity(
855 dpnId, localGroupId, destPrefix, GroupTypes.GroupSelect, localBucketInfo);
856 String jobKey = FibUtil.getCreateLocalNextHopJobKey(parentVpnId, dpnId, destPrefix);
857 jobCoordinator.enqueueJob(jobKey, () -> {
858 mdsalApiManager.syncInstallGroup(remoteGroupEntity);
859 if (!localBucketInfo.isEmpty()) {
860 mdsalApiManager.syncInstallGroup(localGroupEntity);
862 if (LOG.isDebugEnabled()) {
863 LOG.debug("Finished installing GroupEntity with jobCoordinator key {} remoteGroupEntity.groupId {}"
864 + "localGroupEntity.groupId {} groupEntity.groupType {}", jobKey,
865 remoteGroupEntity.getGroupId(), localGroupEntity.getGroupId(),
866 remoteGroupEntity.getGroupType());
868 // Delete local group(if exists) if there is no local info.
869 // Local group has to be deleted if all VMs in a compute is deleted.
870 if (localBucketInfo.isEmpty()) {
871 LOG.debug("Deleting local group {} since no local nhs present for "
872 + "prefix {}", localGroupEntity.getGroupId(), destPrefix);
873 mdsalApiManager.syncRemoveGroup(localGroupEntity);
875 return Collections.emptyList();
877 return remoteGroupId;
880 protected void deleteLoadBalancingNextHop(Long parentVpnId, BigInteger dpnId, String destPrefix) {
881 long remoteGroupId = createNextHopPointer(getRemoteSelectGroupKey(parentVpnId, destPrefix));
882 if (remoteGroupId == FibConstants.INVALID_GROUP_ID) {
883 LOG.error("Unable to allocate/retrieve remote groupId for vpnId {} , prefix {}", parentVpnId, destPrefix);
885 long localGroupId = createNextHopPointer(getLocalSelectGroupKey(parentVpnId, destPrefix));
886 if (localGroupId == FibConstants.INVALID_GROUP_ID) {
887 LOG.error("Unable to allocate/retrieve local groupId for vpnId {} , prefix {}", parentVpnId, destPrefix);
889 GroupEntity remoteGroupEntity = MDSALUtil.buildGroupEntity(
890 dpnId, remoteGroupId, destPrefix, GroupTypes.GroupSelect, Collections.emptyList());
891 GroupEntity localGroupEntity = MDSALUtil.buildGroupEntity(
892 dpnId, localGroupId, destPrefix, GroupTypes.GroupSelect, Collections.emptyList());
893 String jobKey = FibUtil.getCreateLocalNextHopJobKey(parentVpnId, dpnId, destPrefix);
894 jobCoordinator.enqueueJob(jobKey, () -> {
895 mdsalApiManager.syncRemoveGroup(remoteGroupEntity);
896 mdsalApiManager.syncRemoveGroup(localGroupEntity);
897 if (LOG.isDebugEnabled()) {
898 LOG.debug("Finished removing GroupEntity with jobCoordinator key {} remoteGroupEntity.groupId {}"
899 + "localGroupEntity.groupId {}", jobKey, remoteGroupId, localGroupId);
901 return Collections.emptyList();
905 long createNextHopGroups(Long vpnId, String rd, BigInteger dpnId, VrfEntry vrfEntry,
906 @Nullable Routes routes, List<Routes> vpnExtraRoutes) {
907 List<BucketInfo> localBucketInfo = new ArrayList<>();
908 List<Routes> clonedVpnExtraRoutes = new ArrayList<>(vpnExtraRoutes);
909 if (clonedVpnExtraRoutes.contains(routes)) {
910 localBucketInfo.addAll(getBucketsForLocalNexthop(vpnId, dpnId, vrfEntry, routes));
911 clonedVpnExtraRoutes.remove(routes);
913 List<BucketInfo> remoteBucketInfo =
914 new ArrayList<>(getBucketsForRemoteNexthop(vpnId, dpnId, vrfEntry, rd, clonedVpnExtraRoutes));
915 return setupLoadBalancingNextHop(vpnId, dpnId,
916 vrfEntry.getDestPrefix(), localBucketInfo, remoteBucketInfo);
919 private List<BucketInfo> getBucketsForLocalNexthop(Long vpnId, BigInteger dpnId,
920 VrfEntry vrfEntry, Routes routes) {
921 @Nullable List<String> nexthopIpList = routes.getNexthopIpList();
922 if (LOG.isDebugEnabled()) {
923 LOG.debug("NexthopManager.getBucketsForLocalNexthop invoked with vpnId {} dpnId {} "
924 + " vrfEntry.routePaths {}, routes.nexthopList {}", vpnId, dpnId, vrfEntry.getRoutePaths(),
927 List<BucketInfo> listBucketInfo = new CopyOnWriteArrayList<>();
928 if (nexthopIpList != null) {
929 nexthopIpList.parallelStream().forEach(nextHopIp -> {
930 String localNextHopIP;
931 if (isIpv4Address(nextHopIp)) {
932 localNextHopIP = nextHopIp + NwConstants.IPV4PREFIX;
934 localNextHopIP = nextHopIp + NwConstants.IPV6PREFIX;
936 Prefixes localNextHopInfo = fibUtil.getPrefixToInterface(vpnId, localNextHopIP);
937 if (localNextHopInfo != null) {
938 long groupId = getLocalNextHopGroup(vpnId, localNextHopIP);
939 if (groupId == FibConstants.INVALID_GROUP_ID) {
940 LOG.error("Unable to allocate groupId for vpnId {} , prefix {} , interface {}", vpnId,
941 vrfEntry.getDestPrefix(), localNextHopInfo.getVpnInterfaceName());
944 List<ActionInfo> actionsInfos =
945 Collections.singletonList(new ActionGroup(groupId));
946 BucketInfo bucket = new BucketInfo(actionsInfos);
948 listBucketInfo.add(bucket);
952 LOG.trace("LOCAL: listbucket {}, vpnId {}, dpnId {}, routes {}", listBucketInfo, vpnId, dpnId, routes);
953 return listBucketInfo;
956 private List<BucketInfo> getBucketsForRemoteNexthop(Long vpnId, BigInteger dpnId, VrfEntry vrfEntry, String rd,
957 List<Routes> vpnExtraRoutes) {
958 List<BucketInfo> listBucketInfo = new ArrayList<>();
959 Map<String, List<ActionInfo>> egressActionMap = new HashMap<>();
960 vpnExtraRoutes.stream().filter(vpnExtraRoute -> vpnExtraRoute.getNexthopIpList() != null).forEach(
961 vpnExtraRoute -> vpnExtraRoute.getNexthopIpList().forEach(nextHopIp -> {
962 String nextHopPrefixIp;
963 if (isIpv4Address(nextHopIp)) {
964 nextHopPrefixIp = nextHopIp + NwConstants.IPV4PREFIX;
966 nextHopPrefixIp = nextHopIp + NwConstants.IPV6PREFIX;
968 List<String> tepIpAddresses = fibUtil.getNextHopAddresses(rd, nextHopPrefixIp);
969 if (tepIpAddresses.isEmpty()) {
972 // There would be only one nexthop address for a VM ip which would be the tep Ip
973 String tepIp = tepIpAddresses.get(0);
974 AdjacencyResult adjacencyResult = getRemoteNextHopPointer(dpnId, vpnId,
975 vrfEntry.getDestPrefix(), tepIp, TunnelTypeVxlan.class);
976 if (adjacencyResult == null) {
979 String egressInterface = adjacencyResult.getInterfaceName();
980 if (!FibUtil.isTunnelInterface(adjacencyResult)) {
983 Class<? extends TunnelTypeBase> tunnelType =
984 VpnExtraRouteHelper.getTunnelType(itmManager, egressInterface);
985 StateTunnelList ifState = null;
987 ifState = fibUtil.getTunnelState(egressInterface);
988 if (ifState == null || ifState.getOperState() != TunnelOperStatus.Up) {
989 LOG.trace("Tunnel is not up for interface {}", egressInterface);
992 } catch (ReadFailedException e) {
993 LOG.error("getBucketsForRemoteNexthop: error in fetching tunnel state for interface {}",
997 if (!TunnelTypeVxlan.class.equals(tunnelType)) {
1000 Long label = FibUtil.getLabelFromRoutePaths(vrfEntry).get();
1001 Prefixes prefixInfo = fibUtil.getPrefixToInterface(vpnId, nextHopPrefixIp);
1002 if (prefixInfo == null) {
1003 LOG.error("No prefix info found for prefix {} in rd {} for VPN {}", nextHopPrefixIp, rd,
1007 BigInteger tunnelId;
1008 if (FibUtil.isVxlanNetwork(prefixInfo.getNetworkType())) {
1009 tunnelId = BigInteger.valueOf(prefixInfo.getSegmentationId());
1011 LOG.warn("Network is not of type VXLAN for prefix {}."
1012 + "Going with default Lport Tag.", prefixInfo.toString());
1013 tunnelId = BigInteger.valueOf(label);
1015 List<ActionInfo> actionInfos = new ArrayList<>();
1016 actionInfos.add(new ActionSetFieldTunnelId(tunnelId));
1017 String ifName = prefixInfo.getVpnInterfaceName();
1018 String vpnName = fibUtil.getVpnNameFromId(vpnId);
1019 if (vpnName == null) {
1022 String macAddress = fibUtil.getMacAddressFromPrefix(ifName, vpnName, nextHopPrefixIp);
1023 actionInfos.add(new ActionSetFieldEthernetDestination(actionInfos.size(),
1024 new MacAddress(macAddress)));
1025 List<ActionInfo> egressActions;
1026 if (egressActionMap.containsKey(egressInterface)) {
1027 egressActions = egressActionMap.get(egressInterface);
1029 egressActions = getEgressActionsForInterface(egressInterface, actionInfos.size(), true);
1030 egressActionMap.put(egressInterface, egressActions);
1032 if (egressActions.isEmpty()) {
1033 LOG.error("Failed to retrieve egress action for prefix {} route-paths {}"
1034 + " interface {}." + " Aborting remote FIB entry creation.",
1035 vrfEntry.getDestPrefix(), vrfEntry.getRoutePaths(), egressInterface);
1037 actionInfos.addAll(egressActions);
1038 BucketInfo bucket = new BucketInfo(actionInfos);
1039 bucket.setWeight(1);
1040 listBucketInfo.add(bucket);
1042 LOG.trace("LOCAL: listbucket {}, rd {}, dpnId {}, routes {}", listBucketInfo, rd, dpnId, vpnExtraRoutes);
1043 return listBucketInfo;
1046 public void createDcGwLoadBalancingGroup(List<String> availableDcGws, BigInteger dpnId, String destinationIp,
1047 Class<? extends TunnelTypeBase> tunnelType) {
1048 Preconditions.checkNotNull(availableDcGws, "There are no dc-gws present");
1049 int noOfDcGws = availableDcGws.size();
1050 if (noOfDcGws == 1) {
1051 LOG.trace("There are no enough DC GateWays {} present to program LB group", availableDcGws);
1054 // TODO : Place the logic to construct all possible DC-GW combination here.
1055 String groupIdKey = FibUtil.getGreLbGroupKey(availableDcGws);
1056 Long groupId = createNextHopPointer(groupIdKey);
1057 List<Bucket> listBucket = new ArrayList<>();
1058 for (int index = 0; index < noOfDcGws; index++) {
1059 if (isTunnelUp(availableDcGws.get(index), dpnId, tunnelType)) {
1060 listBucket.add(buildBucketForDcGwLbGroup(availableDcGws.get(index), dpnId, index, tunnelType));
1063 Group group = MDSALUtil.buildGroup(groupId, groupIdKey, GroupTypes.GroupSelect,
1064 MDSALUtil.buildBucketLists(listBucket));
1065 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION,
1066 confTx -> mdsalApiManager.addGroup(confTx, dpnId, group)), LOG, "Error adding load-balancing group");
1067 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(OPERATIONAL,
1068 operTx -> FibUtil.updateLbGroupInfo(dpnId, destinationIp, groupIdKey, groupId.toString(), operTx)), LOG,
1069 "Error updating load-balancing group info");
1070 LOG.trace("LB group {} towards DC-GW installed on dpn {}. Group - {}", groupIdKey, dpnId, group);
1073 private boolean isTunnelUp(String dcGwIp, BigInteger dpnId, Class<? extends TunnelTypeBase> tunnelType) {
1074 String tunnelName = getTunnelRemoteNextHopPointer(dpnId, dcGwIp, tunnelType);
1075 if (tunnelName != null) {
1076 InstanceIdentifier<StateTunnelList> tunnelStateId =
1077 InstanceIdentifier.builder(TunnelsState.class).child(
1078 StateTunnelList.class, new StateTunnelListKey(tunnelName)).build();
1079 return MDSALUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, tunnelStateId)
1080 .toJavaUtil().map(StateTunnelList::getOperState)
1081 .orElse(TunnelOperStatus.Down) == TunnelOperStatus.Up;
1086 private List<Action> getEgressActions(String interfaceName, int actionKey) {
1087 List<Action> actions = Collections.emptyList();
1089 GetEgressActionsForInterfaceInputBuilder egressAction =
1090 new GetEgressActionsForInterfaceInputBuilder().setIntfName(interfaceName).setActionKey(actionKey);
1091 Future<RpcResult<GetEgressActionsForInterfaceOutput>> result =
1092 odlInterfaceRpcService.getEgressActionsForInterface(egressAction.build());
1093 RpcResult<GetEgressActionsForInterfaceOutput> rpcResult = result.get();
1094 if (!rpcResult.isSuccessful()) {
1095 LOG.warn("RPC Call to Get egress actions for interface {} returned with Errors {}",
1096 interfaceName, rpcResult.getErrors());
1098 actions = rpcResult.getResult().nonnullAction();
1100 } catch (InterruptedException | ExecutionException e) {
1101 LOG.warn("Exception when egress actions for interface {}", interfaceName, e);
1107 * This method is invoked when the tunnel state is removed from DS.
1108 * If the there is just one DC-GW left in configuration then the LB groups can be deleted.
1109 * Otherwise, the groups are just updated.
1111 public void removeOrUpdateDcGwLoadBalancingGroup(List<String> availableDcGws, BigInteger dpnId,
1112 String destinationIp) {
1113 Preconditions.checkNotNull(availableDcGws, "There are no dc-gws present");
1114 ListenableFutures.addErrorLogging(txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, confTx -> {
1115 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(OPERATIONAL, operTx -> {
1116 int noOfDcGws = availableDcGws.size();
1117 // If availableDcGws does not contain the destination Ip it means this is a configuration delete.
1118 if (!availableDcGws.contains(destinationIp)) {
1119 availableDcGws.add(destinationIp);
1120 Collections.sort(availableDcGws);
1122 // TODO : Place the logic to construct all possible DC-GW combination here.
1123 int bucketId = availableDcGws.indexOf(destinationIp);
1124 Optional<DpnLbNexthops> dpnLbNextHops = fibUtil.getDpnLbNexthops(dpnId, destinationIp);
1125 if (!dpnLbNextHops.isPresent()) {
1128 List<String> nextHopKeys = dpnLbNextHops.get().getNexthopKey();
1129 if (nextHopKeys != null) {
1130 for (String nextHopKey : nextHopKeys) {
1131 Optional<Nexthops> optionalNextHops = fibUtil.getNexthops(nextHopKey);
1132 if (!optionalNextHops.isPresent()) {
1135 Nexthops nexthops = optionalNextHops.get();
1136 final String groupId = nexthops.getGroupId();
1137 final long groupIdValue = Long.parseLong(groupId);
1138 if (noOfDcGws > 1) {
1139 mdsalApiManager.removeBucket(confTx, dpnId, groupIdValue, bucketId);
1141 LOG.trace("Removed LB group {} on dpn {}", groupIdValue, dpnId);
1142 mdsalApiManager.removeGroup(confTx, dpnId, groupIdValue);
1143 removeNextHopPointer(nextHopKey);
1145 // When the DC-GW is removed from configuration.
1146 if (noOfDcGws != availableDcGws.size()) {
1147 FibUtil.removeOrUpdateNextHopInfo(dpnId, nextHopKey, groupId, nexthops, operTx);
1151 FibUtil.removeDpnIdToNextHopInfo(destinationIp, dpnId, operTx);
1152 }), LOG, "Error removing or updating load-balancing group");
1153 }), LOG, "Error removing or updating load-balancing group");
1157 * This method is invoked when the tunnel status is updated.
1158 * The bucket is directly removed/added based on the operational status of the tunnel.
1160 public void updateDcGwLoadBalancingGroup(List<String> availableDcGws,
1161 BigInteger dpnId, String destinationIp, boolean isTunnelUp, Class<? extends TunnelTypeBase> tunnelType) {
1162 Preconditions.checkNotNull(availableDcGws, "There are no dc-gws present");
1163 ListenableFutures.addErrorLogging(txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, confTx -> {
1164 // TODO : Place the logic to construct all possible DC-GW combination here.
1165 int bucketId = availableDcGws.indexOf(destinationIp);
1166 Optional<DpnLbNexthops> dpnLbNextHops = fibUtil.getDpnLbNexthops(dpnId, destinationIp);
1167 if (!dpnLbNextHops.isPresent()) {
1170 List<String> nextHopKeys = dpnLbNextHops.get().getNexthopKey();
1171 if (nextHopKeys != null) {
1172 for (String nextHopKey : nextHopKeys) {
1173 Optional<Nexthops> optionalNextHops = fibUtil.getNexthops(nextHopKey);
1174 if (!optionalNextHops.isPresent()) {
1177 Nexthops nexthops = optionalNextHops.get();
1178 final String groupId = nexthops.getGroupId();
1179 final long groupIdValue = Long.parseLong(groupId);
1181 Bucket bucket = buildBucketForDcGwLbGroup(destinationIp, dpnId, bucketId, tunnelType);
1182 LOG.trace("Added bucket {} to group {} on dpn {}.", bucket, groupId, dpnId);
1183 mdsalApiManager.addBucket(confTx, dpnId, groupIdValue, bucket);
1185 LOG.trace("Removed bucketId {} from group {} on dpn {}.", bucketId, groupId, dpnId);
1186 mdsalApiManager.removeBucket(confTx, dpnId, groupIdValue, bucketId);
1190 }), LOG, "Error updating load-balancing group");
1193 private Bucket buildBucketForDcGwLbGroup(String ipAddress, BigInteger dpnId, int index,
1194 Class<? extends TunnelTypeBase> tunnelType) {
1195 List<Action> listAction = new ArrayList<>();
1196 // ActionKey 0 goes to mpls label.
1198 listAction.add(new ActionPushMpls().buildAction());
1199 listAction.add(new ActionRegMove(actionKey++, FibConstants.NXM_REG_MAPPING
1200 .get(index), 0, 19).buildAction());
1201 String tunnelInterfaceName = getTunnelInterfaceName(dpnId, IpAddressBuilder.getDefaultInstance(ipAddress),
1203 List<Action> egressActions = getEgressActions(tunnelInterfaceName, actionKey++);
1204 if (!egressActions.isEmpty()) {
1205 listAction.addAll(getEgressActions(tunnelInterfaceName, actionKey++));
1207 // clear off actions if there is no egress actions.
1208 listAction = Collections.emptyList();
1210 //OVS expects a non-zero weight value for load balancing to happen in select groups
1211 return MDSALUtil.buildBucket(listAction, SELECT_GROUP_WEIGHT, index,
1212 MDSALUtil.WATCH_PORT, MDSALUtil.WATCH_GROUP);
1215 public void programDcGwLoadBalancingGroup(List<String> availableDcGws, BigInteger dpnId, String destinationIp,
1216 int addRemoveOrUpdate, boolean isTunnelUp,
1217 Class<? extends TunnelTypeBase> tunnelType) {
1218 if (NwConstants.ADD_FLOW == addRemoveOrUpdate) {
1219 createDcGwLoadBalancingGroup(availableDcGws, dpnId, destinationIp, tunnelType);
1220 } else if (NwConstants.DEL_FLOW == addRemoveOrUpdate) {
1221 removeOrUpdateDcGwLoadBalancingGroup(availableDcGws, dpnId, destinationIp);
1222 } else if (NwConstants.MOD_FLOW == addRemoveOrUpdate) {
1223 updateDcGwLoadBalancingGroup(availableDcGws, dpnId, destinationIp, isTunnelUp, tunnelType);