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.Nullable;
28 import javax.annotation.PreDestroy;
29 import javax.inject.Inject;
30 import javax.inject.Singleton;
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,
580 public BigInteger getDpnForPrefix(long vpnId, String prefixIp) {
581 VpnNexthop vpnNexthop = getVpnNexthop(vpnId, prefixIp);
582 return vpnNexthop == null ? null : vpnNexthop.getDpnId();
585 private void removeVpnNexthopFromDS(long vpnId, String ipPrefix) {
587 InstanceIdentifierBuilder<VpnNexthop> idBuilder = InstanceIdentifier.builder(L3nexthop.class)
588 .child(VpnNexthops.class, new VpnNexthopsKey(vpnId))
589 .child(VpnNexthop.class, new VpnNexthopKey(ipPrefix));
590 InstanceIdentifier<VpnNexthop> id = idBuilder.build();
592 LOG.trace("Removing vpn next hop from datastore : {}", id);
593 MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.OPERATIONAL, id);
596 public void removeLocalNextHop(BigInteger dpnId, Long vpnId, String primaryIpAddress, String currDestIpPrefix) {
597 String nextHopLockStr = vpnId + primaryIpAddress;
599 if (FibUtil.lockCluster(lockManager, nextHopLockStr, WAIT_TIME_TO_ACQUIRE_LOCK)) {
600 VpnNexthop nh = getVpnNexthop(vpnId, primaryIpAddress);
602 List<IpAdjacencies> prefixesList = new ArrayList<>(nh.nonnullIpAdjacencies());
603 IpAdjacencies prefix = new IpAdjacenciesBuilder().setIpAdjacency(currDestIpPrefix).build();
604 prefixesList.remove(prefix);
605 if (prefixesList.isEmpty()) { //remove the group only if there are no more flows using this group
606 GroupEntity groupEntity = MDSALUtil.buildGroupEntity(dpnId, nh.getEgressPointer(),
607 primaryIpAddress, GroupTypes.GroupAll, Collections.emptyList());
609 mdsalApiManager.removeGroup(groupEntity);
611 removeVpnNexthopFromDS(vpnId, primaryIpAddress);
613 removeNextHopPointer(getNextHopKey(vpnId, primaryIpAddress));
614 LOG.debug("Local Next hop {} for {} {} on dpn {} successfully deleted",
615 nh.getEgressPointer(), vpnId, primaryIpAddress, dpnId);
617 //remove the currIpPrefx from IpPrefixList of the vpnNexthop
618 LOG.trace("Removing the prefix {} from vpnNextHop {} Operational DS", currDestIpPrefix, nh);
619 MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.OPERATIONAL,
620 getVpnNextHopIpPrefixIdentifier(vpnId, primaryIpAddress, currDestIpPrefix));
624 LOG.error("Local NextHop for VpnId {} curIpPrefix {} on dpn {} primaryIpAddress {} not deleted",
625 vpnId, currDestIpPrefix, dpnId, primaryIpAddress);
629 FibUtil.unlockCluster(lockManager, nextHopLockStr);
633 public void setConfTransType(String service, String transportType) {
635 if (!service.equalsIgnoreCase("L3VPN")) {
636 LOG.error("Incorrect service {} provided for setting the transport type.", service);
640 L3VPNTransportTypes transType = L3VPNTransportTypes.validateTransportType(transportType
641 .toUpperCase(Locale.getDefault()));
643 if (transType != L3VPNTransportTypes.Invalid) {
644 configuredTransportTypeL3VPN = transType;
648 public void writeConfTransTypeConfigDS() {
649 MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, getConfTransportTypeIdentifier(),
650 createConfTransportType(configuredTransportTypeL3VPN.getTransportType()));
653 public L3VPNTransportTypes getConfiguredTransportTypeL3VPN() {
654 return this.configuredTransportTypeL3VPN;
657 public String getReqTransType() {
658 if (configuredTransportTypeL3VPN == L3VPNTransportTypes.Invalid) {
660 * Restart scenario, Read from the ConfigDS.
661 * if the value is Unset, cache value as VxLAN.
663 LOG.trace("configureTransportType is not yet set.");
664 Optional<ConfTransportTypeL3vpn> configuredTransTypeFromConfig =
665 MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, getConfTransportTypeIdentifier());
667 if (configuredTransTypeFromConfig.isPresent()) {
668 if (TunnelTypeGre.class.equals(configuredTransTypeFromConfig.get().getTransportType())) {
669 configuredTransportTypeL3VPN = L3VPNTransportTypes.GRE;
671 configuredTransportTypeL3VPN = L3VPNTransportTypes.VxLAN;
673 LOG.trace("configuredTransportType set from config DS to {}",
674 getConfiguredTransportTypeL3VPN().getTransportType());
676 setConfTransType("L3VPN", L3VPNTransportTypes.VxLAN.getTransportType());
677 LOG.trace("configuredTransportType is not set in the Config DS. VxLAN as default will be used.");
680 LOG.trace("configuredTransportType is set as {}", getConfiguredTransportTypeL3VPN().getTransportType());
682 return getConfiguredTransportTypeL3VPN().getTransportType();
685 public InstanceIdentifier<ConfTransportTypeL3vpn> getConfTransportTypeIdentifier() {
686 return InstanceIdentifier.builder(ConfTransportTypeL3vpn.class).build();
689 private ConfTransportTypeL3vpn createConfTransportType(String type) {
690 ConfTransportTypeL3vpn confTransType;
692 case ITMConstants.TUNNEL_TYPE_GRE:
693 confTransType = new ConfTransportTypeL3vpnBuilder().setTransportType(TunnelTypeGre.class).build();
694 LOG.trace("Setting the confTransportType to GRE.");
696 case ITMConstants.TUNNEL_TYPE_VXLAN:
697 confTransType = new ConfTransportTypeL3vpnBuilder().setTransportType(TunnelTypeVxlan.class).build();
698 LOG.trace("Setting the confTransportType to VxLAN.");
701 LOG.trace("Invalid transport type {} passed to Config DS ", type);
702 confTransType = null;
705 return confTransType;
708 public Class<? extends TunnelTypeBase> getReqTunType(String transportType) {
709 switch (transportType) {
711 return TunnelTypeVxlan.class;
713 return TunnelTypeGre.class;
715 return TunnelTypeMplsOverGre.class;
719 public String getTransportTypeStr(String tunType) {
720 if (tunType.equals(TunnelTypeVxlan.class.toString())) {
721 return ITMConstants.TUNNEL_TYPE_VXLAN;
722 } else if (tunType.equals(TunnelTypeGre.class.toString())) {
723 return ITMConstants.TUNNEL_TYPE_GRE;
724 } else if (tunType.equals(TunnelTypeMplsOverGre.class.toString())) {
725 return ITMConstants.TUNNEL_TYPE_MPLSoGRE;
727 return ITMConstants.TUNNEL_TYPE_INVALID;
733 public void close() {
734 LOG.info("{} close", getClass().getSimpleName());
737 // TODO Clean up the exception handling
738 @SuppressWarnings("checkstyle:IllegalCatch")
740 private String getTunnelRemoteNextHopPointer(BigInteger remoteDpnId, String nextHopIp,
741 Class<? extends TunnelTypeBase> tunnelType) {
742 if (nextHopIp != null && !nextHopIp.isEmpty()) {
744 // here use the config for tunnel type param
745 return getTunnelInterfaceName(remoteDpnId,
746 org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddressBuilder
747 .getDefaultInstance(nextHopIp), tunnelType);
748 } catch (Exception ex) {
749 LOG.error("Error while retrieving nexthop pointer for nexthop {} remoteDpn {}",
750 nextHopIp, remoteDpnId, ex);
757 private String getExtPortRemoteNextHopPointer(BigInteger remoteDpnId, ElanInstance elanInstance) {
758 return elanService.getExternalElanInterface(elanInstance.getElanInstanceName(), remoteDpnId);
762 * Get the interface type associated with the type of ELAN used for routing
763 * traffic to/from remote compute nodes.
765 * @param elanInstance The elan instance
766 * @return L2vlan for flat/VLAN network type and Tunnel otherwise
768 private Class<? extends InterfaceType> getInterfaceType(ElanInstance elanInstance) {
769 Class<? extends SegmentTypeBase> segmentType = elanInstance.getSegmentType();
770 if (SegmentTypeFlat.class.equals(segmentType) || SegmentTypeVlan.class.equals(segmentType)) {
777 private ElanInstance getElanInstanceForPrefix(long vpnId, String prefixIp) {
778 ElanInstance elanInstance = null;
779 Prefixes prefix = fibUtil.getPrefixToInterface(vpnId, prefixIp);
780 if (prefix != null) {
781 if (prefix.getNetworkId() != null) {
782 elanInstance = elanService.getElanInstance(prefix.getNetworkId().getValue());
789 static class AdjacencyResult {
790 private final String interfaceName;
791 private final Class<? extends InterfaceType> interfaceType;
792 private final String nextHopIp;
793 private final String prefix;
795 AdjacencyResult(String interfaceName, Class<? extends InterfaceType> interfaceType, String nextHopIp,
797 this.interfaceName = interfaceName;
798 this.interfaceType = interfaceType;
799 this.nextHopIp = nextHopIp;
800 this.prefix = prefix;
803 public String getInterfaceName() {
804 return interfaceName;
807 public Class<? extends InterfaceType> getInterfaceType() {
808 return interfaceType;
811 public String getNextHopIp() {
815 public String getPrefix() {
820 public int hashCode() {
821 final int prime = 31;
823 result = prime * result + (interfaceName == null ? 0 : interfaceName.hashCode());
828 public boolean equals(Object obj) {
833 if (getClass() != obj.getClass()) {
836 AdjacencyResult other = (AdjacencyResult) obj;
837 return interfaceName.equals(other.interfaceName);
842 protected long setupLoadBalancingNextHop(Long parentVpnId, BigInteger dpnId,
843 String destPrefix, List<BucketInfo> localBucketInfo, List<BucketInfo> remoteBucketInfo) {
844 long remoteGroupId = createNextHopPointer(getRemoteSelectGroupKey(parentVpnId, destPrefix));
845 if (remoteGroupId == FibConstants.INVALID_GROUP_ID) {
846 LOG.error("Unable to allocate/retrieve remote groupId for vpnId {} , prefix {}", parentVpnId, destPrefix);
847 return remoteGroupId;
849 long localGroupId = createNextHopPointer(getLocalSelectGroupKey(parentVpnId, destPrefix));
850 if (localGroupId == FibConstants.INVALID_GROUP_ID) {
851 LOG.error("Unable to allocate/retrieve local groupId for vpnId {} , prefix {}",
852 parentVpnId, destPrefix);
853 return remoteGroupId;
855 List<BucketInfo> combinedBucketInfo = new ArrayList<>();
856 combinedBucketInfo.addAll(localBucketInfo);
857 combinedBucketInfo.addAll(remoteBucketInfo);
858 GroupEntity remoteGroupEntity = MDSALUtil.buildGroupEntity(
859 dpnId, remoteGroupId, destPrefix, GroupTypes.GroupSelect, combinedBucketInfo);
860 GroupEntity localGroupEntity = MDSALUtil.buildGroupEntity(
861 dpnId, localGroupId, destPrefix, GroupTypes.GroupSelect, localBucketInfo);
862 String jobKey = FibUtil.getCreateLocalNextHopJobKey(parentVpnId, dpnId, destPrefix);
863 jobCoordinator.enqueueJob(jobKey, () -> {
864 mdsalApiManager.syncInstallGroup(remoteGroupEntity);
865 if (!localBucketInfo.isEmpty()) {
866 mdsalApiManager.syncInstallGroup(localGroupEntity);
868 if (LOG.isDebugEnabled()) {
869 LOG.debug("Finished installing GroupEntity with jobCoordinator key {} remoteGroupEntity.groupId {}"
870 + "localGroupEntity.groupId {} groupEntity.groupType {}", jobKey,
871 remoteGroupEntity.getGroupId(), localGroupEntity.getGroupId(),
872 remoteGroupEntity.getGroupType());
874 // Delete local group(if exists) if there is no local info.
875 // Local group has to be deleted if all VMs in a compute is deleted.
876 if (localBucketInfo.isEmpty()) {
877 LOG.debug("Deleting local group {} since no local nhs present for "
878 + "prefix {}", localGroupEntity.getGroupId(), destPrefix);
879 mdsalApiManager.syncRemoveGroup(localGroupEntity);
881 return Collections.emptyList();
883 return remoteGroupId;
886 protected void deleteLoadBalancingNextHop(Long parentVpnId, BigInteger dpnId, String destPrefix) {
887 long remoteGroupId = createNextHopPointer(getRemoteSelectGroupKey(parentVpnId, destPrefix));
888 if (remoteGroupId == FibConstants.INVALID_GROUP_ID) {
889 LOG.error("Unable to allocate/retrieve remote groupId for vpnId {} , prefix {}", parentVpnId, destPrefix);
891 long localGroupId = createNextHopPointer(getLocalSelectGroupKey(parentVpnId, destPrefix));
892 if (localGroupId == FibConstants.INVALID_GROUP_ID) {
893 LOG.error("Unable to allocate/retrieve local groupId for vpnId {} , prefix {}", parentVpnId, destPrefix);
895 GroupEntity remoteGroupEntity = MDSALUtil.buildGroupEntity(
896 dpnId, remoteGroupId, destPrefix, GroupTypes.GroupSelect, Collections.emptyList());
897 GroupEntity localGroupEntity = MDSALUtil.buildGroupEntity(
898 dpnId, localGroupId, destPrefix, GroupTypes.GroupSelect, Collections.emptyList());
899 String jobKey = FibUtil.getCreateLocalNextHopJobKey(parentVpnId, dpnId, destPrefix);
900 jobCoordinator.enqueueJob(jobKey, () -> {
901 mdsalApiManager.syncRemoveGroup(remoteGroupEntity);
902 mdsalApiManager.syncRemoveGroup(localGroupEntity);
903 if (LOG.isDebugEnabled()) {
904 LOG.debug("Finished removing GroupEntity with jobCoordinator key {} remoteGroupEntity.groupId {}"
905 + "localGroupEntity.groupId {}", jobKey, remoteGroupId, localGroupId);
907 return Collections.emptyList();
911 long createNextHopGroups(Long vpnId, String rd, BigInteger dpnId, VrfEntry vrfEntry,
912 @Nullable Routes routes, List<Routes> vpnExtraRoutes) {
913 List<BucketInfo> localBucketInfo = new ArrayList<>();
914 List<Routes> clonedVpnExtraRoutes = new ArrayList<>(vpnExtraRoutes);
915 if (clonedVpnExtraRoutes.contains(routes)) {
916 localBucketInfo.addAll(getBucketsForLocalNexthop(vpnId, dpnId, vrfEntry, routes));
917 clonedVpnExtraRoutes.remove(routes);
919 List<BucketInfo> remoteBucketInfo =
920 new ArrayList<>(getBucketsForRemoteNexthop(vpnId, dpnId, vrfEntry, rd, clonedVpnExtraRoutes));
921 return setupLoadBalancingNextHop(vpnId, dpnId,
922 vrfEntry.getDestPrefix(), localBucketInfo, remoteBucketInfo);
925 private List<BucketInfo> getBucketsForLocalNexthop(Long vpnId, BigInteger dpnId,
926 VrfEntry vrfEntry, Routes routes) {
927 @Nullable List<String> nexthopIpList = routes.getNexthopIpList();
928 if (LOG.isDebugEnabled()) {
929 LOG.debug("NexthopManager.getBucketsForLocalNexthop invoked with vpnId {} dpnId {} "
930 + " vrfEntry.routePaths {}, routes.nexthopList {}", vpnId, dpnId, vrfEntry.getRoutePaths(),
933 List<BucketInfo> listBucketInfo = new CopyOnWriteArrayList<>();
934 if (nexthopIpList != null) {
935 nexthopIpList.parallelStream().forEach(nextHopIp -> {
936 String localNextHopIP;
937 if (isIpv4Address(nextHopIp)) {
938 localNextHopIP = nextHopIp + NwConstants.IPV4PREFIX;
940 localNextHopIP = nextHopIp + NwConstants.IPV6PREFIX;
942 Prefixes localNextHopInfo = fibUtil.getPrefixToInterface(vpnId, localNextHopIP);
943 if (localNextHopInfo != null) {
944 long groupId = getLocalNextHopGroup(vpnId, localNextHopIP);
945 if (groupId == FibConstants.INVALID_GROUP_ID) {
946 LOG.error("Unable to allocate groupId for vpnId {} , prefix {} , interface {}", vpnId,
947 vrfEntry.getDestPrefix(), localNextHopInfo.getVpnInterfaceName());
950 List<ActionInfo> actionsInfos =
951 Collections.singletonList(new ActionGroup(groupId));
952 BucketInfo bucket = new BucketInfo(actionsInfos);
954 listBucketInfo.add(bucket);
958 LOG.trace("LOCAL: listbucket {}, vpnId {}, dpnId {}, routes {}", listBucketInfo, vpnId, dpnId, routes);
959 return listBucketInfo;
962 private List<BucketInfo> getBucketsForRemoteNexthop(Long vpnId, BigInteger dpnId, VrfEntry vrfEntry, String rd,
963 List<Routes> vpnExtraRoutes) {
964 List<BucketInfo> listBucketInfo = new ArrayList<>();
965 Map<String, List<ActionInfo>> egressActionMap = new HashMap<>();
966 vpnExtraRoutes.stream().filter(vpnExtraRoute -> vpnExtraRoute.getNexthopIpList() != null).forEach(
967 vpnExtraRoute -> vpnExtraRoute.getNexthopIpList().forEach(nextHopIp -> {
968 String nextHopPrefixIp;
969 if (isIpv4Address(nextHopIp)) {
970 nextHopPrefixIp = nextHopIp + NwConstants.IPV4PREFIX;
972 nextHopPrefixIp = nextHopIp + NwConstants.IPV6PREFIX;
974 List<String> tepIpAddresses = fibUtil.getNextHopAddresses(rd, nextHopPrefixIp);
975 if (tepIpAddresses.isEmpty()) {
978 // There would be only one nexthop address for a VM ip which would be the tep Ip
979 String tepIp = tepIpAddresses.get(0);
980 AdjacencyResult adjacencyResult = getRemoteNextHopPointer(dpnId, vpnId,
981 vrfEntry.getDestPrefix(), tepIp, TunnelTypeVxlan.class);
982 if (adjacencyResult == null) {
985 String egressInterface = adjacencyResult.getInterfaceName();
986 if (!FibUtil.isTunnelInterface(adjacencyResult)) {
989 Class<? extends TunnelTypeBase> tunnelType =
990 VpnExtraRouteHelper.getTunnelType(itmManager, egressInterface);
991 StateTunnelList ifState = null;
993 ifState = fibUtil.getTunnelState(egressInterface);
994 if (ifState == null || ifState.getOperState() != TunnelOperStatus.Up) {
995 LOG.trace("Tunnel is not up for interface {}", egressInterface);
998 } catch (ReadFailedException e) {
999 LOG.error("getBucketsForRemoteNexthop: error in fetching tunnel state for interface {}",
1000 egressInterface, e);
1003 if (!TunnelTypeVxlan.class.equals(tunnelType)) {
1006 Long label = FibUtil.getLabelFromRoutePaths(vrfEntry).get();
1007 Prefixes prefixInfo = fibUtil.getPrefixToInterface(vpnId, nextHopPrefixIp);
1008 if (prefixInfo == null) {
1009 LOG.error("No prefix info found for prefix {} in rd {} for VPN {}", nextHopPrefixIp, rd,
1013 BigInteger tunnelId;
1014 if (FibUtil.isVxlanNetwork(prefixInfo.getNetworkType())) {
1015 tunnelId = BigInteger.valueOf(prefixInfo.getSegmentationId());
1017 LOG.warn("Network is not of type VXLAN for prefix {}."
1018 + "Going with default Lport Tag.", prefixInfo.toString());
1019 tunnelId = BigInteger.valueOf(label);
1021 List<ActionInfo> actionInfos = new ArrayList<>();
1022 actionInfos.add(new ActionSetFieldTunnelId(tunnelId));
1023 String ifName = prefixInfo.getVpnInterfaceName();
1024 String vpnName = fibUtil.getVpnNameFromId(vpnId);
1025 if (vpnName == null) {
1028 String macAddress = fibUtil.getMacAddressFromPrefix(ifName, vpnName, nextHopPrefixIp);
1029 actionInfos.add(new ActionSetFieldEthernetDestination(actionInfos.size(),
1030 new MacAddress(macAddress)));
1031 List<ActionInfo> egressActions;
1032 if (egressActionMap.containsKey(egressInterface)) {
1033 egressActions = egressActionMap.get(egressInterface);
1035 egressActions = getEgressActionsForInterface(egressInterface, actionInfos.size(), true);
1036 egressActionMap.put(egressInterface, egressActions);
1038 if (egressActions.isEmpty()) {
1039 LOG.error("Failed to retrieve egress action for prefix {} route-paths {}"
1040 + " interface {}." + " Aborting remote FIB entry creation.",
1041 vrfEntry.getDestPrefix(), vrfEntry.getRoutePaths(), egressInterface);
1043 actionInfos.addAll(egressActions);
1044 BucketInfo bucket = new BucketInfo(actionInfos);
1045 bucket.setWeight(1);
1046 listBucketInfo.add(bucket);
1048 LOG.trace("LOCAL: listbucket {}, rd {}, dpnId {}, routes {}", listBucketInfo, rd, dpnId, vpnExtraRoutes);
1049 return listBucketInfo;
1052 public void createDcGwLoadBalancingGroup(List<String> availableDcGws, BigInteger dpnId, String destinationIp,
1053 Class<? extends TunnelTypeBase> tunnelType) {
1054 Preconditions.checkNotNull(availableDcGws, "There are no dc-gws present");
1055 int noOfDcGws = availableDcGws.size();
1056 if (noOfDcGws == 1) {
1057 LOG.trace("There are no enough DC GateWays {} present to program LB group", availableDcGws);
1060 // TODO : Place the logic to construct all possible DC-GW combination here.
1061 String groupIdKey = FibUtil.getGreLbGroupKey(availableDcGws);
1062 Long groupId = createNextHopPointer(groupIdKey);
1063 List<Bucket> listBucket = new ArrayList<>();
1064 for (int index = 0; index < noOfDcGws; index++) {
1065 if (isTunnelUp(availableDcGws.get(index), dpnId, tunnelType)) {
1066 listBucket.add(buildBucketForDcGwLbGroup(availableDcGws.get(index), dpnId, index, tunnelType));
1069 Group group = MDSALUtil.buildGroup(groupId, groupIdKey, GroupTypes.GroupSelect,
1070 MDSALUtil.buildBucketLists(listBucket));
1071 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION,
1072 confTx -> mdsalApiManager.addGroup(confTx, dpnId, group)), LOG, "Error adding load-balancing group");
1073 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(OPERATIONAL,
1074 operTx -> FibUtil.updateLbGroupInfo(dpnId, destinationIp, groupIdKey, groupId.toString(), operTx)), LOG,
1075 "Error updating load-balancing group info");
1076 LOG.trace("LB group {} towards DC-GW installed on dpn {}. Group - {}", groupIdKey, dpnId, group);
1079 private boolean isTunnelUp(String dcGwIp, BigInteger dpnId, Class<? extends TunnelTypeBase> tunnelType) {
1080 String tunnelName = getTunnelRemoteNextHopPointer(dpnId, dcGwIp, tunnelType);
1081 if (tunnelName != null) {
1082 InstanceIdentifier<StateTunnelList> tunnelStateId =
1083 InstanceIdentifier.builder(TunnelsState.class).child(
1084 StateTunnelList.class, new StateTunnelListKey(tunnelName)).build();
1085 return MDSALUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, tunnelStateId)
1086 .toJavaUtil().map(StateTunnelList::getOperState)
1087 .orElse(TunnelOperStatus.Down) == TunnelOperStatus.Up;
1092 private List<Action> getEgressActions(String interfaceName, int actionKey) {
1093 List<Action> actions = Collections.emptyList();
1095 GetEgressActionsForInterfaceInputBuilder egressAction =
1096 new GetEgressActionsForInterfaceInputBuilder().setIntfName(interfaceName).setActionKey(actionKey);
1097 Future<RpcResult<GetEgressActionsForInterfaceOutput>> result =
1098 odlInterfaceRpcService.getEgressActionsForInterface(egressAction.build());
1099 RpcResult<GetEgressActionsForInterfaceOutput> rpcResult = result.get();
1100 if (!rpcResult.isSuccessful()) {
1101 LOG.warn("RPC Call to Get egress actions for interface {} returned with Errors {}",
1102 interfaceName, rpcResult.getErrors());
1104 actions = rpcResult.getResult().nonnullAction();
1106 } catch (InterruptedException | ExecutionException e) {
1107 LOG.warn("Exception when egress actions for interface {}", interfaceName, e);
1113 * This method is invoked when the tunnel state is removed from DS.
1114 * If the there is just one DC-GW left in configuration then the LB groups can be deleted.
1115 * Otherwise, the groups are just updated.
1117 public void removeOrUpdateDcGwLoadBalancingGroup(List<String> availableDcGws, BigInteger dpnId,
1118 String destinationIp) {
1119 Preconditions.checkNotNull(availableDcGws, "There are no dc-gws present");
1120 ListenableFutures.addErrorLogging(txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, confTx -> {
1121 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(OPERATIONAL, operTx -> {
1122 int noOfDcGws = availableDcGws.size();
1123 // If availableDcGws does not contain the destination Ip it means this is a configuration delete.
1124 if (!availableDcGws.contains(destinationIp)) {
1125 availableDcGws.add(destinationIp);
1126 Collections.sort(availableDcGws);
1128 // TODO : Place the logic to construct all possible DC-GW combination here.
1129 int bucketId = availableDcGws.indexOf(destinationIp);
1130 Optional<DpnLbNexthops> dpnLbNextHops = fibUtil.getDpnLbNexthops(dpnId, destinationIp);
1131 if (!dpnLbNextHops.isPresent()) {
1134 List<String> nextHopKeys = dpnLbNextHops.get().getNexthopKey();
1135 if (nextHopKeys != null) {
1136 for (String nextHopKey : nextHopKeys) {
1137 Optional<Nexthops> optionalNextHops = fibUtil.getNexthops(nextHopKey);
1138 if (!optionalNextHops.isPresent()) {
1141 Nexthops nexthops = optionalNextHops.get();
1142 final String groupId = nexthops.getGroupId();
1143 final long groupIdValue = Long.parseLong(groupId);
1144 if (noOfDcGws > 1) {
1145 mdsalApiManager.removeBucket(confTx, dpnId, groupIdValue, bucketId);
1147 LOG.trace("Removed LB group {} on dpn {}", groupIdValue, dpnId);
1148 mdsalApiManager.removeGroup(confTx, dpnId, groupIdValue);
1149 removeNextHopPointer(nextHopKey);
1151 // When the DC-GW is removed from configuration.
1152 if (noOfDcGws != availableDcGws.size()) {
1153 FibUtil.removeOrUpdateNextHopInfo(dpnId, nextHopKey, groupId, nexthops, operTx);
1157 FibUtil.removeDpnIdToNextHopInfo(destinationIp, dpnId, operTx);
1158 }), LOG, "Error removing or updating load-balancing group");
1159 }), LOG, "Error removing or updating load-balancing group");
1163 * This method is invoked when the tunnel status is updated.
1164 * The bucket is directly removed/added based on the operational status of the tunnel.
1166 public void updateDcGwLoadBalancingGroup(List<String> availableDcGws,
1167 BigInteger dpnId, String destinationIp, boolean isTunnelUp, Class<? extends TunnelTypeBase> tunnelType) {
1168 Preconditions.checkNotNull(availableDcGws, "There are no dc-gws present");
1169 ListenableFutures.addErrorLogging(txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, confTx -> {
1170 // TODO : Place the logic to construct all possible DC-GW combination here.
1171 int bucketId = availableDcGws.indexOf(destinationIp);
1172 Optional<DpnLbNexthops> dpnLbNextHops = fibUtil.getDpnLbNexthops(dpnId, destinationIp);
1173 if (!dpnLbNextHops.isPresent()) {
1176 List<String> nextHopKeys = dpnLbNextHops.get().getNexthopKey();
1177 if (nextHopKeys != null) {
1178 for (String nextHopKey : nextHopKeys) {
1179 Optional<Nexthops> optionalNextHops = fibUtil.getNexthops(nextHopKey);
1180 if (!optionalNextHops.isPresent()) {
1183 Nexthops nexthops = optionalNextHops.get();
1184 final String groupId = nexthops.getGroupId();
1185 final long groupIdValue = Long.parseLong(groupId);
1187 Bucket bucket = buildBucketForDcGwLbGroup(destinationIp, dpnId, bucketId, tunnelType);
1188 LOG.trace("Added bucket {} to group {} on dpn {}.", bucket, groupId, dpnId);
1189 mdsalApiManager.addBucket(confTx, dpnId, groupIdValue, bucket);
1191 LOG.trace("Removed bucketId {} from group {} on dpn {}.", bucketId, groupId, dpnId);
1192 mdsalApiManager.removeBucket(confTx, dpnId, groupIdValue, bucketId);
1196 }), LOG, "Error updating load-balancing group");
1199 private Bucket buildBucketForDcGwLbGroup(String ipAddress, BigInteger dpnId, int index,
1200 Class<? extends TunnelTypeBase> tunnelType) {
1201 List<Action> listAction = new ArrayList<>();
1202 // ActionKey 0 goes to mpls label.
1204 listAction.add(new ActionPushMpls().buildAction());
1205 listAction.add(new ActionRegMove(actionKey++, FibConstants.NXM_REG_MAPPING
1206 .get(index), 0, 19).buildAction());
1207 String tunnelInterfaceName = getTunnelInterfaceName(dpnId, IpAddressBuilder.getDefaultInstance(ipAddress),
1209 List<Action> egressActions = getEgressActions(tunnelInterfaceName, actionKey++);
1210 if (!egressActions.isEmpty()) {
1211 listAction.addAll(getEgressActions(tunnelInterfaceName, actionKey++));
1213 // clear off actions if there is no egress actions.
1214 listAction = Collections.emptyList();
1216 //OVS expects a non-zero weight value for load balancing to happen in select groups
1217 return MDSALUtil.buildBucket(listAction, SELECT_GROUP_WEIGHT, index,
1218 MDSALUtil.WATCH_PORT, MDSALUtil.WATCH_GROUP);
1221 public void programDcGwLoadBalancingGroup(List<String> availableDcGws, BigInteger dpnId, String destinationIp,
1222 int addRemoveOrUpdate, boolean isTunnelUp,
1223 Class<? extends TunnelTypeBase> tunnelType) {
1224 if (NwConstants.ADD_FLOW == addRemoveOrUpdate) {
1225 createDcGwLoadBalancingGroup(availableDcGws, dpnId, destinationIp, tunnelType);
1226 } else if (NwConstants.DEL_FLOW == addRemoveOrUpdate) {
1227 removeOrUpdateDcGwLoadBalancingGroup(availableDcGws, dpnId, destinationIp);
1228 } else if (NwConstants.MOD_FLOW == addRemoveOrUpdate) {
1229 updateDcGwLoadBalancingGroup(availableDcGws, dpnId, destinationIp, isTunnelUp, tunnelType);