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 java.util.stream.Collectors.toList;
11 import static org.opendaylight.genius.infra.Datastore.CONFIGURATION;
12 import static org.opendaylight.genius.infra.Datastore.OPERATIONAL;
13 import static org.opendaylight.genius.mdsalutil.NWUtil.isIpv4Address;
15 import com.google.common.base.Optional;
16 import com.google.common.base.Preconditions;
17 import com.google.common.util.concurrent.ListenableFuture;
19 import java.util.ArrayList;
20 import java.util.Arrays;
21 import java.util.Collections;
22 import java.util.HashMap;
23 import java.util.List;
24 import java.util.Locale;
26 import java.util.Objects;
27 import java.util.concurrent.CopyOnWriteArrayList;
28 import java.util.concurrent.ExecutionException;
29 import java.util.concurrent.Future;
31 import javax.annotation.PreDestroy;
32 import javax.inject.Inject;
33 import javax.inject.Singleton;
35 import org.eclipse.jdt.annotation.Nullable;
36 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
37 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
38 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
39 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
40 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
41 import org.opendaylight.genius.interfacemanager.interfaces.IInterfaceManager;
42 import org.opendaylight.genius.itm.globals.ITMConstants;
43 import org.opendaylight.genius.mdsalutil.ActionInfo;
44 import org.opendaylight.genius.mdsalutil.BucketInfo;
45 import org.opendaylight.genius.mdsalutil.GroupEntity;
46 import org.opendaylight.genius.mdsalutil.MDSALUtil;
47 import org.opendaylight.genius.mdsalutil.NwConstants;
48 import org.opendaylight.genius.mdsalutil.actions.ActionGroup;
49 import org.opendaylight.genius.mdsalutil.actions.ActionNxResubmit;
50 import org.opendaylight.genius.mdsalutil.actions.ActionOutput;
51 import org.opendaylight.genius.mdsalutil.actions.ActionPushMpls;
52 import org.opendaylight.genius.mdsalutil.actions.ActionPushVlan;
53 import org.opendaylight.genius.mdsalutil.actions.ActionRegLoad;
54 import org.opendaylight.genius.mdsalutil.actions.ActionRegMove;
55 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldEthernetDestination;
56 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldEthernetSource;
57 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldTunnelId;
58 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldVlanVid;
59 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
60 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
61 import org.opendaylight.netvirt.elanmanager.api.IElanService;
62 import org.opendaylight.netvirt.fibmanager.api.L3VPNTransportTypes;
63 import org.opendaylight.netvirt.vpnmanager.api.VpnExtraRouteHelper;
64 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana._if.type.rev170119.L2vlan;
65 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana._if.type.rev170119.Tunnel;
66 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddressBuilder;
67 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.InterfaceType;
68 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.OutputActionCase;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.PushVlanActionCase;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetFieldCase;
72 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
73 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.AllocateIdInput;
74 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.AllocateIdInputBuilder;
75 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.AllocateIdOutput;
76 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.CreateIdPoolInput;
77 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.CreateIdPoolInputBuilder;
78 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.CreateIdPoolOutput;
79 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
80 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.ReleaseIdInput;
81 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.ReleaseIdInputBuilder;
82 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.ReleaseIdOutput;
83 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeBase;
84 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeGre;
85 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeMplsOverGre;
86 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeVxlan;
87 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetEgressActionsForInterfaceInputBuilder;
88 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetEgressActionsForInterfaceOutput;
89 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.OdlInterfaceRpcService;
90 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.TunnelOperStatus;
91 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.TunnelsState;
92 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.tunnels_state.StateTunnelList;
93 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.tunnels_state.StateTunnelListKey;
94 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.DcGatewayIpList;
95 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetEgressActionsForTunnelInputBuilder;
96 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetEgressActionsForTunnelOutput;
97 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetInternalOrExternalInterfaceNameInputBuilder;
98 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetInternalOrExternalInterfaceNameOutput;
99 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetTunnelInterfaceNameInputBuilder;
100 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetTunnelInterfaceNameOutput;
101 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.ItmRpcService;
102 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.lockmanager.rev160413.LockManagerService;
103 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.AddGroupInput;
104 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.AddGroupInputBuilder;
105 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.AddGroupOutput;
106 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.SalGroupService;
107 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupId;
108 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupRef;
109 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupTypes;
110 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.group.Buckets;
111 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.group.buckets.Bucket;
112 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.Group;
113 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
114 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.SegmentTypeBase;
115 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.SegmentTypeFlat;
116 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.SegmentTypeVlan;
117 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.instances.ElanInstance;
118 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentries.VrfEntry;
119 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.L3nexthop;
120 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.VpnNexthops;
121 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.VpnNexthopsKey;
122 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.vpnnexthops.VpnNexthop;
123 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.vpnnexthops.VpnNexthopBuilder;
124 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.vpnnexthops.VpnNexthopKey;
125 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.vpnnexthops.vpnnexthop.IpAdjacencies;
126 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.vpnnexthops.vpnnexthop.IpAdjacenciesBuilder;
127 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.vpnnexthops.vpnnexthop.IpAdjacenciesKey;
128 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.ConfTransportTypeL3vpn;
129 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.ConfTransportTypeL3vpnBuilder;
130 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.l3vpn.lb.nexthops.Nexthops;
131 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.prefix.to._interface.vpn.ids.Prefixes;
132 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.to.extraroutes.vpn.extra.routes.Routes;
133 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg6;
134 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.add.group.input.buckets.bucket.action.action.NxActionResubmitRpcAddGroupCase;
135 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;
136 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.nx.action.reg.load.grouping.NxRegLoad;
137 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
138 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.InstanceIdentifierBuilder;
139 import org.opendaylight.yangtools.yang.common.RpcResult;
140 import org.opendaylight.yangtools.yang.common.Uint32;
141 import org.opendaylight.yangtools.yang.common.Uint64;
142 import org.slf4j.Logger;
143 import org.slf4j.LoggerFactory;
146 public class NexthopManager implements AutoCloseable {
147 private static final Logger LOG = LoggerFactory.getLogger(NexthopManager.class);
148 private static final String NEXTHOP_ID_POOL_NAME = "nextHopPointerPool";
149 private static final long WAIT_TIME_FOR_SYNC_INSTALL = Long.getLong("wait.time.sync.install", 300L);
150 private static final long WAIT_TIME_TO_ACQUIRE_LOCK = 3000L;
151 private static final int SELECT_GROUP_WEIGHT = 1;
152 private static final int RETRY_COUNT = 6;
153 private static final String NEXTHOPMANAGER_JOB_KEY_PREFIX = "NextHopManager";
155 private final DataBroker dataBroker;
156 private final ManagedNewTransactionRunner txRunner;
157 private final IMdsalApiManager mdsalApiManager;
158 private final OdlInterfaceRpcService odlInterfaceRpcService;
159 private final ItmRpcService itmManager;
160 private final IdManagerService idManager;
161 private final IElanService elanService;
162 private final LockManagerService lockManager;
163 private final SalGroupService salGroupService;
164 private final JobCoordinator jobCoordinator;
165 private final FibUtil fibUtil;
166 private final IInterfaceManager interfaceManager;
167 private volatile L3VPNTransportTypes configuredTransportTypeL3VPN = L3VPNTransportTypes.Invalid;
170 * Provides nexthop functions.
171 * Creates group ID pool
173 * @param dataBroker - dataBroker reference
174 * @param mdsalApiManager - mdsalApiManager reference
175 * @param idManager - idManager reference
176 * @param odlInterfaceRpcService - odlInterfaceRpcService reference
177 * @param itmManager - itmManager reference
180 public NexthopManager(final DataBroker dataBroker,
181 final IMdsalApiManager mdsalApiManager,
182 final IdManagerService idManager,
183 final OdlInterfaceRpcService odlInterfaceRpcService,
184 final ItmRpcService itmManager,
185 final LockManagerService lockManager,
186 final IElanService elanService,
187 final SalGroupService salGroupService,
188 final JobCoordinator jobCoordinator,
189 final FibUtil fibUtil,
190 final IInterfaceManager interfaceManager) {
191 this.dataBroker = dataBroker;
192 this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
193 this.mdsalApiManager = mdsalApiManager;
194 this.idManager = idManager;
195 this.odlInterfaceRpcService = odlInterfaceRpcService;
196 this.itmManager = itmManager;
197 this.elanService = elanService;
198 this.salGroupService = salGroupService;
199 this.jobCoordinator = jobCoordinator;
200 this.fibUtil = fibUtil;
201 this.lockManager = lockManager;
202 this.interfaceManager = interfaceManager;
206 private void createIdPool() {
207 CreateIdPoolInput createPool = new CreateIdPoolInputBuilder()
208 .setPoolName(NEXTHOP_ID_POOL_NAME)
213 Future<RpcResult<CreateIdPoolOutput>> result = idManager.createIdPool(createPool);
214 if (result != null && result.get().isSuccessful()) {
215 LOG.info("Created IdPool for NextHopPointerPool");
217 } catch (InterruptedException | ExecutionException e) {
218 LOG.error("Failed to create idPool for NextHopPointerPool", e);
222 private String getNextHopKey(Uint32 vpnId, String ipAddress) {
223 return "nexthop." + vpnId + ipAddress;
226 String getRemoteSelectGroupKey(Uint32 vpnId, String ipAddress) {
227 return "remote.ecmp.nexthop." + vpnId + ipAddress;
230 String getLocalSelectGroupKey(Uint32 vpnId, String ipAddress) {
231 return "local.ecmp.nexthop." + vpnId + ipAddress;
234 public ItmRpcService getItmManager() {
238 protected long createNextHopPointer(String nexthopKey) {
239 AllocateIdInput getIdInput = new AllocateIdInputBuilder()
240 .setPoolName(NEXTHOP_ID_POOL_NAME).setIdKey(nexthopKey)
242 //TODO: Proper error handling once IdManager code is complete
244 Future<RpcResult<AllocateIdOutput>> result = idManager.allocateId(getIdInput);
245 RpcResult<AllocateIdOutput> rpcResult = result.get();
246 return rpcResult.getResult().getIdValue().toJava();
247 } catch (NullPointerException | InterruptedException | ExecutionException e) {
248 // FIXME: NPEs should not be caught but rather their root cause should be eliminated
249 LOG.trace("Failed to allocate {}", getIdInput, e);
254 protected void removeNextHopPointer(String nexthopKey) {
255 ReleaseIdInput idInput = new ReleaseIdInputBuilder()
256 .setPoolName(NEXTHOP_ID_POOL_NAME)
257 .setIdKey(nexthopKey).build();
259 RpcResult<ReleaseIdOutput> rpcResult = idManager.releaseId(idInput).get();
260 if (!rpcResult.isSuccessful()) {
261 LOG.error("RPC Call to Get Unique Id for nexthopKey {} returned with Errors {}",
262 nexthopKey, rpcResult.getErrors());
264 } catch (InterruptedException | ExecutionException e) {
265 LOG.warn("Exception when getting Unique Id for key {}", nexthopKey, e);
269 protected List<ActionInfo> getEgressActionsForInterface(final String ifName, int actionKey,
270 boolean isTunnelInterface,
271 Uint32 vpnId, String destIpPrefix) {
272 List<Action> actions;
274 if (isTunnelInterface && interfaceManager.isItmDirectTunnelsEnabled()) {
275 RpcResult<GetEgressActionsForTunnelOutput> rpcResult =
276 itmManager.getEgressActionsForTunnel(new GetEgressActionsForTunnelInputBuilder()
277 .setIntfName(ifName).build()).get();
278 if (!rpcResult.isSuccessful()) {
279 LOG.error("RPC Call to Get egress tunnel actions for interface {} returned with Errors {}",
280 ifName, rpcResult.getErrors());
281 return Collections.emptyList();
283 actions = rpcResult.getResult().nonnullAction();
286 RpcResult<GetEgressActionsForInterfaceOutput> rpcResult = odlInterfaceRpcService
287 .getEgressActionsForInterface(new GetEgressActionsForInterfaceInputBuilder()
288 .setIntfName(ifName).build()).get();
289 if (!rpcResult.isSuccessful()) {
290 LOG.error("RPC Call to Get egress vm actions for interface {} vpnId {} ipPrefix {} returned with "
291 + "Errors {}", ifName, vpnId, destIpPrefix, rpcResult.getErrors());
292 return Collections.emptyList();
294 actions = rpcResult.getResult().nonnullAction();
297 List<ActionInfo> listActionInfo = new ArrayList<>();
298 for (Action action : actions) {
299 actionKey = action.key().getOrder() + actionKey;
300 org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action
301 actionClass = action.getAction();
302 if (actionClass instanceof OutputActionCase) {
303 listActionInfo.add(new ActionOutput(actionKey,
304 ((OutputActionCase) actionClass).getOutputAction().getOutputNodeConnector()));
305 } else if (actionClass instanceof PushVlanActionCase) {
306 listActionInfo.add(new ActionPushVlan(actionKey));
307 } else if (actionClass instanceof SetFieldCase) {
308 if (((SetFieldCase) actionClass).getSetField().getVlanMatch() != null) {
309 int vlanVid = ((SetFieldCase) actionClass).getSetField().getVlanMatch()
310 .getVlanId().getVlanId().getValue().toJava();
311 listActionInfo.add(new ActionSetFieldVlanVid(actionKey, vlanVid));
313 } else if (actionClass instanceof NxActionResubmitRpcAddGroupCase) {
314 Short tableId = ((NxActionResubmitRpcAddGroupCase) actionClass).getNxResubmit().getTable().toJava();
315 listActionInfo.add(new ActionNxResubmit(actionKey, tableId));
316 } else if (actionClass instanceof NxActionRegLoadNodesNodeTableFlowApplyActionsCase) {
317 NxRegLoad nxRegLoad =
318 ((NxActionRegLoadNodesNodeTableFlowApplyActionsCase) actionClass).getNxRegLoad();
319 listActionInfo.add(new ActionRegLoad(actionKey, NxmNxReg6.class,
320 nxRegLoad.getDst().getStart().toJava(), nxRegLoad.getDst().getEnd().toJava(),
321 nxRegLoad.getValue().longValue()));
324 return listActionInfo;
325 } catch (InterruptedException | ExecutionException | NullPointerException e) {
326 LOG.error("Exception when egress actions for interface {} isTunnel {} vpnId {} ipPrefix {}", ifName,
327 isTunnelInterface, vpnId, destIpPrefix, e);
329 LOG.warn("Exception when egress actions for interface {}", ifName);
330 return Collections.emptyList();
334 protected String getTunnelInterfaceName(Uint64 srcDpId, Uint64 dstDpId) {
335 Class<? extends TunnelTypeBase> tunType = getReqTunType(getReqTransType().toUpperCase(Locale.getDefault()));
336 Future<RpcResult<GetTunnelInterfaceNameOutput>> result;
338 result = itmManager.getTunnelInterfaceName(new GetTunnelInterfaceNameInputBuilder()
339 .setSourceDpid(srcDpId)
340 .setDestinationDpid(dstDpId)
341 .setTunnelType(tunType)
343 RpcResult<GetTunnelInterfaceNameOutput> rpcResult = result.get();
344 if (!rpcResult.isSuccessful()) {
345 LOG.warn("RPC Call to getTunnelInterfaceId returned with Errors {}", rpcResult.getErrors());
347 return rpcResult.getResult().getInterfaceName();
349 } catch (InterruptedException | ExecutionException e) {
350 LOG.warn("Exception when getting tunnel interface Id for tunnel between {} and {}", srcDpId, dstDpId, e);
356 protected String getTunnelInterfaceName(Uint64 srcDpId, org.opendaylight.yang.gen.v1.urn.ietf.params
357 .xml.ns.yang.ietf.inet.types.rev130715.IpAddress dstIp, Class<? extends TunnelTypeBase> tunnelType) {
358 Future<RpcResult<GetInternalOrExternalInterfaceNameOutput>> result;
360 LOG.debug("Trying to fetch tunnel interface name for source dpn {} destIp {} tunType {}", srcDpId,
361 dstIp.stringValue(), tunnelType.getName());
362 result = itmManager.getInternalOrExternalInterfaceName(new GetInternalOrExternalInterfaceNameInputBuilder()
363 .setSourceDpid(srcDpId)
364 .setDestinationIp(dstIp)
365 .setTunnelType(tunnelType)
367 RpcResult<GetInternalOrExternalInterfaceNameOutput> rpcResult = result.get();
368 if (!rpcResult.isSuccessful()) {
369 LOG.warn("RPC Call to getTunnelInterfaceName returned with Errors {}", rpcResult.getErrors());
371 return rpcResult.getResult().getInterfaceName();
373 } catch (InterruptedException | ExecutionException e) {
374 LOG.error("Exception when getting tunnel interface Id for tunnel between {} and {}", srcDpId, dstIp, e);
380 public long getLocalNextHopGroup(Uint32 vpnId,
381 String ipNextHopAddress) {
382 long groupId = createNextHopPointer(getNextHopKey(vpnId, ipNextHopAddress));
383 if (groupId == FibConstants.INVALID_GROUP_ID) {
384 LOG.error("Unable to allocate groupId for vpnId {} , prefix {}", vpnId, ipNextHopAddress);
389 public long getLocalSelectGroup(Uint32 vpnId,
390 String ipNextHopAddress) {
391 long groupId = createNextHopPointer(getLocalSelectGroupKey(vpnId, ipNextHopAddress));
392 if (groupId == FibConstants.INVALID_GROUP_ID) {
393 LOG.error("Unable to allocate groupId for vpnId {} , prefix {}", vpnId, ipNextHopAddress);
398 public long createLocalNextHop(Uint32 vpnId, Uint64 dpnId, String ifName,
399 String primaryIpAddress, String currDestIpPrefix,
400 String gwMacAddress, Uint32 parentVpnId) {
401 //For VPN Imported routes, getting VPN Instance name using parentVpnId
402 String vpnName = parentVpnId != null ? fibUtil.getVpnNameFromId(parentVpnId) : fibUtil.getVpnNameFromId(vpnId);
403 if (vpnName == null) {
406 String macAddress = fibUtil.getMacAddressFromPrefix(ifName, vpnName, primaryIpAddress);
408 long groupId = createNextHopPointer(getNextHopKey(vpnId, primaryIpAddress));
410 LOG.error("Unable to allocate groupId for vpnId {} , IntfName {}, primaryIpAddress {} curIpPrefix {}",
411 vpnId, ifName, primaryIpAddress, currDestIpPrefix);
414 String nextHopLockStr = vpnId + primaryIpAddress;
415 String jobKey = FibUtil.getCreateLocalNextHopJobKey(vpnId, dpnId, currDestIpPrefix);
416 jobCoordinator.enqueueJob(jobKey, () -> {
418 if (FibUtil.lockCluster(lockManager, nextHopLockStr, WAIT_TIME_TO_ACQUIRE_LOCK)) {
419 VpnNexthop nexthop = getVpnNexthop(vpnId, primaryIpAddress);
420 LOG.trace("nexthop: {} retrieved for vpnId {}, prefix {}, ifName {} on dpn {}", nexthop, vpnId,
421 primaryIpAddress, ifName, dpnId);
422 if (nexthop == null) {
423 String encMacAddress = macAddress == null
424 ? fibUtil.getMacAddressFromPrefix(ifName, vpnName, primaryIpAddress) : macAddress;
425 List<ActionInfo> listActionInfo = new ArrayList<>();
428 if (encMacAddress != null) {
429 if (gwMacAddress != null) {
430 LOG.trace("The Local NextHop Group Source Mac {} for VpnInterface {} on VPN {}",
431 gwMacAddress, ifName, vpnId);
432 listActionInfo.add(new ActionSetFieldEthernetSource(actionKey++,
433 new MacAddress(gwMacAddress)));
435 listActionInfo.add(new ActionSetFieldEthernetDestination(actionKey++,
436 new MacAddress(encMacAddress)));
437 // listActionInfo.add(0, new ActionPopMpls());
439 LOG.error("mac address for new local nexthop group {} is null for vpnId {}, prefix {}, "
440 + "ifName {} on dpn {}", groupId, vpnId, primaryIpAddress, ifName, dpnId);
442 List<ActionInfo> nhActionInfoList = getEgressActionsForInterface(ifName, actionKey, false,
443 vpnId, currDestIpPrefix);
444 if (nhActionInfoList.isEmpty()) {
445 LOG.error("createLocalNextHop: Skipping, Empty list of egress actions received for "
446 + "interface {} on dpn {} for vpn {} prefix {}", ifName, dpnId, vpnId,
449 listActionInfo.addAll(nhActionInfoList);
450 BucketInfo bucket = new BucketInfo(listActionInfo);
451 List<BucketInfo> listBucketInfo = new ArrayList<>();
452 listBucketInfo.add(bucket);
453 GroupEntity groupEntity = MDSALUtil.buildGroupEntity(dpnId, groupId, primaryIpAddress,
454 GroupTypes.GroupAll, listBucketInfo);
455 LOG.trace("Install LNH Group: id {}, mac address {}, interface {} for prefix {}", groupId,
456 encMacAddress, ifName, primaryIpAddress);
457 //Try to install group directly on the DPN bypassing the FRM, in order to avoid waiting for the
458 // group to get installed before programming the flows
459 installGroupOnDpn(groupId, dpnId, primaryIpAddress, listBucketInfo,
460 getNextHopKey(vpnId, primaryIpAddress), GroupTypes.GroupAll);
462 mdsalApiManager.syncInstallGroup(groupEntity);
464 addVpnNexthopToDS(dpnId, vpnId, primaryIpAddress, currDestIpPrefix, groupId);
467 // Ignore adding new prefix , if it already exists
468 List<IpAdjacencies> prefixesList = nexthop.getIpAdjacencies();
469 IpAdjacencies prefix = new IpAdjacenciesBuilder().setIpAdjacency(currDestIpPrefix).build();
470 if (prefixesList != null && prefixesList.contains(prefix)) {
471 LOG.trace("Prefix {} is already present in l3nextHop {} ", currDestIpPrefix, nexthop);
473 IpAdjacenciesBuilder ipPrefixesBuilder =
474 new IpAdjacenciesBuilder().withKey(new IpAdjacenciesKey(currDestIpPrefix));
475 LOG.trace("Updating prefix {} to vpnNextHop {} Operational DS", currDestIpPrefix, nexthop);
476 MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL,
477 getVpnNextHopIpPrefixIdentifier(vpnId, primaryIpAddress, currDestIpPrefix),
478 ipPrefixesBuilder.build());
483 FibUtil.unlockCluster(lockManager, nextHopLockStr);
485 return Collections.emptyList();
490 private void installGroupOnDpn(long groupId, Uint64 dpnId, String groupName, List<BucketInfo> bucketsInfo,
491 String nextHopKey, GroupTypes groupType) {
492 NodeRef nodeRef = FibUtil.buildNodeRef(dpnId);
493 Buckets buckets = FibUtil.buildBuckets(bucketsInfo);
494 GroupRef groupRef = new GroupRef(FibUtil.buildGroupInstanceIdentifier(groupId, dpnId));
495 AddGroupInput input = new AddGroupInputBuilder().setNode(nodeRef).setGroupId(new GroupId(groupId))
496 .setBuckets(buckets).setGroupRef(groupRef).setGroupType(groupType)
497 .setGroupName(groupName).build();
498 Future<RpcResult<AddGroupOutput>> groupStats = salGroupService.addGroup(input);
499 RpcResult<AddGroupOutput> rpcResult = null;
501 rpcResult = groupStats.get();
502 if (rpcResult != null && rpcResult.isSuccessful()) {
503 LOG.info("Group {} with key {} has been successfully installed directly on dpn {}.", groupId,
506 LOG.error("Unable to install group {} with key {} directly on dpn {} due to {}.", groupId, nextHopKey,
507 dpnId, rpcResult != null ? rpcResult.getErrors() : null);
509 } catch (InterruptedException | ExecutionException e) {
510 LOG.error("Error while installing group {} directly on dpn {}", groupId, dpnId);
514 protected void addVpnNexthopToDS(Uint64 dpnId, Uint32 vpnId, String primaryIpAddr,
515 String currIpAddr, long egressPointer) {
516 InstanceIdentifierBuilder<VpnNexthops> idBuilder = InstanceIdentifier.builder(L3nexthop.class)
517 .child(VpnNexthops.class, new VpnNexthopsKey(vpnId));
519 List<IpAdjacencies> ipPrefixesList = new ArrayList<>();
520 IpAdjacencies prefix = new IpAdjacenciesBuilder().setIpAdjacency(currIpAddr).build();
521 ipPrefixesList.add(prefix);
522 // Add nexthop to vpn node
523 VpnNexthop nh = new VpnNexthopBuilder()
524 .withKey(new VpnNexthopKey(primaryIpAddr))
526 .setIpAdjacencies(ipPrefixesList)
527 .setEgressPointer(egressPointer).build();
529 InstanceIdentifier<VpnNexthop> id1 = idBuilder
530 .child(VpnNexthop.class, new VpnNexthopKey(primaryIpAddr)).build();
531 LOG.trace("Adding vpnnextHop {} to Operational DS", nh);
532 MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL, id1, nh);
536 protected InstanceIdentifier<IpAdjacencies> getVpnNextHopIpPrefixIdentifier(Uint32 vpnId, String primaryIpAddress,
538 InstanceIdentifier<IpAdjacencies> id = InstanceIdentifier.builder(L3nexthop.class)
539 .child(VpnNexthops.class, new VpnNexthopsKey(vpnId))
540 .child(VpnNexthop.class, new VpnNexthopKey(primaryIpAddress))
541 .child(IpAdjacencies.class, new IpAdjacenciesKey(ipPrefix)).build();
546 protected VpnNexthop getVpnNexthop(Uint32 vpnId, String ipAddress) {
548 // check if vpn node is there
549 InstanceIdentifierBuilder<VpnNexthops> idBuilder =
550 InstanceIdentifier.builder(L3nexthop.class).child(VpnNexthops.class,
551 new VpnNexthopsKey(vpnId));
552 InstanceIdentifier<VpnNexthops> id = idBuilder.build();
553 Optional<VpnNexthops> vpnNexthops = MDSALUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, id);
554 if (vpnNexthops.isPresent()) {
555 // get nexthops list for vpn
556 List<VpnNexthop> nexthops = vpnNexthops.get().nonnullVpnNexthop();
557 for (VpnNexthop nexthop : nexthops) {
558 if (Objects.equals(nexthop.getIpAddress(), ipAddress)) {
560 LOG.trace("VpnNextHop : {}", nexthop);
564 // return null if not found
570 public AdjacencyResult getRemoteNextHopPointer(Uint64 remoteDpnId, Uint32 vpnId, String prefixIp,
571 @Nullable String nextHopIp, Class<? extends TunnelTypeBase> tunnelType) {
572 String egressIfName = null;
573 LOG.trace("getRemoteNextHopPointer: input [remoteDpnId {}, vpnId {}, prefixIp {}, nextHopIp {} ]", remoteDpnId,
574 vpnId, prefixIp, nextHopIp);
576 Class<? extends InterfaceType> egressIfType;
577 ElanInstance elanInstance = getElanInstanceForPrefix(vpnId, prefixIp);
578 if (elanInstance != null) {
579 egressIfType = getInterfaceType(elanInstance);
581 LOG.warn("Failed to determine network type for prefixIp {} using tunnel", prefixIp);
582 egressIfType = Tunnel.class;
585 if (Tunnel.class.equals(egressIfType)) {
586 egressIfName = getTunnelRemoteNextHopPointer(remoteDpnId, nextHopIp, tunnelType);
588 egressIfName = getExtPortRemoteNextHopPointer(remoteDpnId, elanInstance);
591 LOG.trace("NextHop pointer for prefixIp {} vpnId {} dpnId {} is {}", prefixIp, vpnId, remoteDpnId,
593 return egressIfName != null ? new AdjacencyResult(egressIfName, egressIfType, nextHopIp,
597 private void removeVpnNexthopFromDS(Uint32 vpnId, String ipPrefix) {
599 InstanceIdentifierBuilder<VpnNexthop> idBuilder = InstanceIdentifier.builder(L3nexthop.class)
600 .child(VpnNexthops.class, new VpnNexthopsKey(vpnId))
601 .child(VpnNexthop.class, new VpnNexthopKey(ipPrefix));
602 InstanceIdentifier<VpnNexthop> id = idBuilder.build();
604 LOG.trace("Removing vpn next hop from datastore : {}", id);
605 MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.OPERATIONAL, id);
608 public void removeLocalNextHop(Uint64 dpnId, Uint32 vpnId, String primaryIpAddress, String currDestIpPrefix) {
609 String nextHopLockStr = vpnId + primaryIpAddress;
611 if (FibUtil.lockCluster(lockManager, nextHopLockStr, WAIT_TIME_TO_ACQUIRE_LOCK)) {
612 VpnNexthop nh = getVpnNexthop(vpnId, primaryIpAddress);
614 List<IpAdjacencies> prefixesList = new ArrayList<>(nh.nonnullIpAdjacencies());
615 IpAdjacencies prefix = new IpAdjacenciesBuilder().setIpAdjacency(currDestIpPrefix).build();
616 prefixesList.remove(prefix);
617 if (prefixesList.isEmpty()) { //remove the group only if there are no more flows using this group
618 GroupEntity groupEntity = MDSALUtil.buildGroupEntity(dpnId, nh.getEgressPointer().toJava(),
619 primaryIpAddress, GroupTypes.GroupAll, Collections.emptyList());
621 mdsalApiManager.removeGroup(groupEntity);
623 removeVpnNexthopFromDS(vpnId, primaryIpAddress);
625 removeNextHopPointer(getNextHopKey(vpnId, primaryIpAddress));
626 LOG.debug("Local Next hop {} for {} {} on dpn {} successfully deleted",
627 nh.getEgressPointer(), vpnId, primaryIpAddress, dpnId);
629 //remove the currIpPrefx from IpPrefixList of the vpnNexthop
630 LOG.trace("Removing the prefix {} from vpnNextHop {} Operational DS", currDestIpPrefix, nh);
631 MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.OPERATIONAL,
632 getVpnNextHopIpPrefixIdentifier(vpnId, primaryIpAddress, currDestIpPrefix));
636 LOG.error("Local NextHop for VpnId {} curIpPrefix {} on dpn {} primaryIpAddress {} not deleted",
637 vpnId, currDestIpPrefix, dpnId, primaryIpAddress);
641 FibUtil.unlockCluster(lockManager, nextHopLockStr);
645 public void setConfTransType(String service, String transportType) {
647 if (!service.equalsIgnoreCase("L3VPN")) {
648 LOG.error("Incorrect service {} provided for setting the transport type.", service);
652 L3VPNTransportTypes transType = L3VPNTransportTypes.validateTransportType(transportType
653 .toUpperCase(Locale.getDefault()));
655 if (transType != L3VPNTransportTypes.Invalid) {
656 configuredTransportTypeL3VPN = transType;
660 public void writeConfTransTypeConfigDS() {
661 MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, getConfTransportTypeIdentifier(),
662 createConfTransportType(configuredTransportTypeL3VPN.getTransportType()));
665 public L3VPNTransportTypes getConfiguredTransportTypeL3VPN() {
666 return this.configuredTransportTypeL3VPN;
669 public String getReqTransType() {
670 if (configuredTransportTypeL3VPN == L3VPNTransportTypes.Invalid) {
672 * Restart scenario, Read from the ConfigDS.
673 * if the value is Unset, cache value as VxLAN.
675 LOG.trace("configureTransportType is not yet set.");
676 Optional<ConfTransportTypeL3vpn> configuredTransTypeFromConfig =
677 MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, getConfTransportTypeIdentifier());
679 if (configuredTransTypeFromConfig.isPresent()) {
680 if (TunnelTypeGre.class.equals(configuredTransTypeFromConfig.get().getTransportType())) {
681 configuredTransportTypeL3VPN = L3VPNTransportTypes.GRE;
683 configuredTransportTypeL3VPN = L3VPNTransportTypes.VxLAN;
685 LOG.trace("configuredTransportType set from config DS to {}",
686 getConfiguredTransportTypeL3VPN().getTransportType());
688 setConfTransType("L3VPN", L3VPNTransportTypes.VxLAN.getTransportType());
689 LOG.trace("configuredTransportType is not set in the Config DS. VxLAN as default will be used.");
692 LOG.trace("configuredTransportType is set as {}", getConfiguredTransportTypeL3VPN().getTransportType());
694 return getConfiguredTransportTypeL3VPN().getTransportType();
697 public InstanceIdentifier<ConfTransportTypeL3vpn> getConfTransportTypeIdentifier() {
698 return InstanceIdentifier.builder(ConfTransportTypeL3vpn.class).build();
701 private ConfTransportTypeL3vpn createConfTransportType(String type) {
702 ConfTransportTypeL3vpn confTransType;
704 case ITMConstants.TUNNEL_TYPE_GRE:
705 confTransType = new ConfTransportTypeL3vpnBuilder().setTransportType(TunnelTypeGre.class).build();
706 LOG.trace("Setting the confTransportType to GRE.");
708 case ITMConstants.TUNNEL_TYPE_VXLAN:
709 confTransType = new ConfTransportTypeL3vpnBuilder().setTransportType(TunnelTypeVxlan.class).build();
710 LOG.trace("Setting the confTransportType to VxLAN.");
713 LOG.trace("Invalid transport type {} passed to Config DS ", type);
714 confTransType = null;
717 return confTransType;
720 public Class<? extends TunnelTypeBase> getReqTunType(String transportType) {
721 switch (transportType) {
723 return TunnelTypeVxlan.class;
725 return TunnelTypeGre.class;
727 return TunnelTypeMplsOverGre.class;
731 public String getTransportTypeStr(String tunType) {
732 if (tunType.equals(TunnelTypeVxlan.class.toString())) {
733 return ITMConstants.TUNNEL_TYPE_VXLAN;
734 } else if (tunType.equals(TunnelTypeGre.class.toString())) {
735 return ITMConstants.TUNNEL_TYPE_GRE;
736 } else if (tunType.equals(TunnelTypeMplsOverGre.class.toString())) {
737 return ITMConstants.TUNNEL_TYPE_MPLSoGRE;
739 return ITMConstants.TUNNEL_TYPE_INVALID;
745 public void close() {
746 LOG.info("{} close", getClass().getSimpleName());
749 // TODO Clean up the exception handling
750 @SuppressWarnings("checkstyle:IllegalCatch")
752 private String getTunnelRemoteNextHopPointer(Uint64 remoteDpnId, String nextHopIp,
753 Class<? extends TunnelTypeBase> tunnelType) {
754 if (nextHopIp != null && !nextHopIp.isEmpty()) {
756 // here use the config for tunnel type param
757 return getTunnelInterfaceName(remoteDpnId,
758 org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddressBuilder
759 .getDefaultInstance(nextHopIp), tunnelType);
760 } catch (Exception ex) {
761 LOG.error("Error while retrieving nexthop pointer for nexthop {} remoteDpn {}",
762 nextHopIp, remoteDpnId, ex);
769 private String getExtPortRemoteNextHopPointer(Uint64 remoteDpnId, ElanInstance elanInstance) {
770 return elanService.getExternalElanInterface(elanInstance.getElanInstanceName(), remoteDpnId);
774 * Get the interface type associated with the type of ELAN used for routing
775 * traffic to/from remote compute nodes.
777 * @param elanInstance The elan instance
778 * @return L2vlan for flat/VLAN network type and Tunnel otherwise
780 private Class<? extends InterfaceType> getInterfaceType(ElanInstance elanInstance) {
781 Class<? extends SegmentTypeBase> segmentType = elanInstance.getSegmentType();
782 if (SegmentTypeFlat.class.equals(segmentType) || SegmentTypeVlan.class.equals(segmentType)) {
789 private ElanInstance getElanInstanceForPrefix(Uint32 vpnId, String prefixIp) {
790 ElanInstance elanInstance = null;
791 Prefixes prefix = fibUtil.getPrefixToInterface(vpnId, prefixIp);
792 if (prefix != null) {
793 if (prefix.getNetworkId() != null) {
794 elanInstance = elanService.getElanInstance(prefix.getNetworkId().getValue());
801 static class AdjacencyResult {
802 private final String interfaceName;
803 private final Class<? extends InterfaceType> interfaceType;
804 private final String nextHopIp;
805 private final String prefix;
807 AdjacencyResult(String interfaceName, Class<? extends InterfaceType> interfaceType, String nextHopIp,
809 this.interfaceName = interfaceName;
810 this.interfaceType = interfaceType;
811 this.nextHopIp = nextHopIp;
812 this.prefix = prefix;
815 public String getInterfaceName() {
816 return interfaceName;
819 public Class<? extends InterfaceType> getInterfaceType() {
820 return interfaceType;
823 public String getNextHopIp() {
827 public String getPrefix() {
832 public int hashCode() {
833 final int prime = 31;
835 result = prime * result + (interfaceName == null ? 0 : interfaceName.hashCode());
840 public boolean equals(Object obj) {
845 if (getClass() != obj.getClass()) {
848 AdjacencyResult other = (AdjacencyResult) obj;
849 return interfaceName.equals(other.interfaceName);
854 protected long setupLoadBalancingNextHop(Uint32 parentVpnId, Uint64 dpnId,
855 String destPrefix, List<BucketInfo> localBucketInfo, List<BucketInfo> remoteBucketInfo) {
856 long remoteGroupId = createNextHopPointer(getRemoteSelectGroupKey(parentVpnId, destPrefix));
857 if (remoteGroupId == FibConstants.INVALID_GROUP_ID) {
858 LOG.error("Unable to allocate/retrieve remote groupId for vpnId {} , prefix {}", parentVpnId, destPrefix);
859 return remoteGroupId;
861 long localGroupId = createNextHopPointer(getLocalSelectGroupKey(parentVpnId, destPrefix));
862 if (localGroupId == FibConstants.INVALID_GROUP_ID) {
863 LOG.error("Unable to allocate/retrieve local groupId for vpnId {} , prefix {}",
864 parentVpnId, destPrefix);
865 return remoteGroupId;
867 List<BucketInfo> combinedBucketInfo = new ArrayList<>();
868 combinedBucketInfo.addAll(localBucketInfo);
869 combinedBucketInfo.addAll(remoteBucketInfo);
870 GroupEntity remoteGroupEntity = MDSALUtil.buildGroupEntity(
871 dpnId, remoteGroupId, destPrefix, GroupTypes.GroupSelect, combinedBucketInfo);
872 GroupEntity localGroupEntity = MDSALUtil.buildGroupEntity(
873 dpnId, localGroupId, destPrefix, GroupTypes.GroupSelect, localBucketInfo);
874 String jobKey = FibUtil.getCreateLocalNextHopJobKey(parentVpnId, dpnId, destPrefix);
875 jobCoordinator.enqueueJob(jobKey, () -> {
876 mdsalApiManager.syncInstallGroup(remoteGroupEntity);
877 if (!localBucketInfo.isEmpty()) {
878 mdsalApiManager.syncInstallGroup(localGroupEntity);
880 if (LOG.isDebugEnabled()) {
881 LOG.debug("Finished installing GroupEntity with jobCoordinator key {} remoteGroupEntity.groupId {}"
882 + "localGroupEntity.groupId {} groupEntity.groupType {}", jobKey,
883 remoteGroupEntity.getGroupId(), localGroupEntity.getGroupId(),
884 remoteGroupEntity.getGroupType());
886 // Delete local group(if exists) if there is no local info.
887 // Local group has to be deleted if all VMs in a compute is deleted.
888 if (localBucketInfo.isEmpty()) {
889 LOG.debug("Deleting local group {} since no local nhs present for "
890 + "prefix {}", localGroupEntity.getGroupId(), destPrefix);
891 mdsalApiManager.syncRemoveGroup(localGroupEntity);
893 return Collections.emptyList();
895 return remoteGroupId;
898 protected void deleteLoadBalancingNextHop(Uint32 parentVpnId, Uint64 dpnId, String destPrefix) {
899 long remoteGroupId = createNextHopPointer(getRemoteSelectGroupKey(parentVpnId, destPrefix));
900 if (remoteGroupId == FibConstants.INVALID_GROUP_ID) {
901 LOG.error("Unable to allocate/retrieve remote groupId for vpnId {} , prefix {}", parentVpnId, destPrefix);
903 long localGroupId = createNextHopPointer(getLocalSelectGroupKey(parentVpnId, destPrefix));
904 if (localGroupId == FibConstants.INVALID_GROUP_ID) {
905 LOG.error("Unable to allocate/retrieve local groupId for vpnId {} , prefix {}", parentVpnId, destPrefix);
907 GroupEntity remoteGroupEntity = MDSALUtil.buildGroupEntity(
908 dpnId, remoteGroupId, destPrefix, GroupTypes.GroupSelect, Collections.emptyList());
909 GroupEntity localGroupEntity = MDSALUtil.buildGroupEntity(
910 dpnId, localGroupId, destPrefix, GroupTypes.GroupSelect, Collections.emptyList());
911 String jobKey = FibUtil.getCreateLocalNextHopJobKey(parentVpnId, dpnId, destPrefix);
912 jobCoordinator.enqueueJob(jobKey, () -> {
913 mdsalApiManager.syncRemoveGroup(remoteGroupEntity);
914 mdsalApiManager.syncRemoveGroup(localGroupEntity);
915 if (LOG.isDebugEnabled()) {
916 LOG.debug("Finished removing GroupEntity with jobCoordinator key {} remoteGroupEntity.groupId {}"
917 + "localGroupEntity.groupId {}", jobKey, remoteGroupId, localGroupId);
919 return Collections.emptyList();
923 long createNextHopGroups(Uint32 vpnId, String rd, Uint64 dpnId, VrfEntry vrfEntry,
924 @Nullable Routes routes, List<Routes> vpnExtraRoutes) {
925 List<BucketInfo> localBucketInfo = new ArrayList<>();
926 List<Routes> clonedVpnExtraRoutes = new ArrayList<>(vpnExtraRoutes);
927 if (clonedVpnExtraRoutes.contains(routes)) {
928 localBucketInfo.addAll(getBucketsForLocalNexthop(vpnId, dpnId, vrfEntry, routes));
929 clonedVpnExtraRoutes.remove(routes);
931 List<BucketInfo> remoteBucketInfo =
932 new ArrayList<>(getBucketsForRemoteNexthop(vpnId, dpnId, vrfEntry, rd, clonedVpnExtraRoutes));
933 return setupLoadBalancingNextHop(vpnId, dpnId,
934 vrfEntry.getDestPrefix(), localBucketInfo, remoteBucketInfo);
937 private List<BucketInfo> getBucketsForLocalNexthop(Uint32 vpnId, Uint64 dpnId,
938 VrfEntry vrfEntry, Routes routes) {
939 @Nullable List<String> nexthopIpList = routes.getNexthopIpList();
940 if (LOG.isDebugEnabled()) {
941 LOG.debug("NexthopManager.getBucketsForLocalNexthop invoked with vpnId {} dpnId {} "
942 + " vrfEntry.routePaths {}, routes.nexthopList {}", vpnId, dpnId, vrfEntry.getRoutePaths(),
945 List<BucketInfo> listBucketInfo = new CopyOnWriteArrayList<>();
946 if (nexthopIpList != null) {
947 nexthopIpList.parallelStream().forEach(nextHopIp -> {
948 String localNextHopIP;
949 if (isIpv4Address(nextHopIp)) {
950 localNextHopIP = nextHopIp + NwConstants.IPV4PREFIX;
952 localNextHopIP = nextHopIp + NwConstants.IPV6PREFIX;
954 Prefixes localNextHopInfo = fibUtil.getPrefixToInterface(vpnId, localNextHopIP);
955 if (localNextHopInfo != null) {
956 long groupId = getLocalNextHopGroup(vpnId, localNextHopIP);
957 if (groupId == FibConstants.INVALID_GROUP_ID) {
958 LOG.error("Unable to allocate groupId for vpnId {} , prefix {} , interface {}", vpnId,
959 vrfEntry.getDestPrefix(), localNextHopInfo.getVpnInterfaceName());
962 List<ActionInfo> actionsInfos =
963 Collections.singletonList(new ActionGroup(groupId));
964 BucketInfo bucket = new BucketInfo(actionsInfos);
966 listBucketInfo.add(bucket);
970 LOG.trace("LOCAL: listbucket {}, vpnId {}, dpnId {}, routes {}", listBucketInfo, vpnId, dpnId, routes);
971 return listBucketInfo;
974 private List<BucketInfo> getBucketsForRemoteNexthop(Uint32 vpnId, Uint64 dpnId, VrfEntry vrfEntry, String rd,
975 List<Routes> vpnExtraRoutes) {
976 List<BucketInfo> listBucketInfo = new ArrayList<>();
977 Map<String, List<ActionInfo>> egressActionMap = new HashMap<>();
978 vpnExtraRoutes.stream().filter(vpnExtraRoute -> vpnExtraRoute.getNexthopIpList() != null).forEach(
979 vpnExtraRoute -> vpnExtraRoute.getNexthopIpList().forEach(nextHopIp -> {
980 String nextHopPrefixIp;
981 if (isIpv4Address(nextHopIp)) {
982 nextHopPrefixIp = nextHopIp + NwConstants.IPV4PREFIX;
984 nextHopPrefixIp = nextHopIp + NwConstants.IPV6PREFIX;
986 List<String> tepIpAddresses = fibUtil.getNextHopAddresses(rd, nextHopPrefixIp);
987 if (tepIpAddresses.isEmpty()) {
990 // There would be only one nexthop address for a VM ip which would be the tep Ip
991 String tepIp = tepIpAddresses.get(0);
992 AdjacencyResult adjacencyResult = getRemoteNextHopPointer(dpnId, vpnId,
993 vrfEntry.getDestPrefix(), tepIp, TunnelTypeVxlan.class);
994 if (adjacencyResult == null) {
997 String egressInterface = adjacencyResult.getInterfaceName();
998 if (!FibUtil.isTunnelInterface(adjacencyResult)) {
1001 Class<? extends TunnelTypeBase> tunnelType =
1002 VpnExtraRouteHelper.getTunnelType(itmManager, egressInterface);
1003 StateTunnelList ifState = null;
1005 ifState = fibUtil.getTunnelState(egressInterface);
1006 if (ifState == null || ifState.getOperState() != TunnelOperStatus.Up) {
1007 LOG.trace("Tunnel is not up for interface {}", egressInterface);
1010 } catch (ReadFailedException e) {
1011 LOG.error("getBucketsForRemoteNexthop: error in fetching tunnel state for interface {}",
1012 egressInterface, e);
1015 if (!TunnelTypeVxlan.class.equals(tunnelType)) {
1018 Uint32 label = FibUtil.getLabelFromRoutePaths(vrfEntry).get();
1019 Prefixes prefixInfo = fibUtil.getPrefixToInterface(vpnId, nextHopPrefixIp);
1020 if (prefixInfo == null) {
1021 LOG.error("No prefix info found for prefix {} in rd {} for VPN {}", nextHopPrefixIp, rd,
1026 if (FibUtil.isVxlanNetwork(prefixInfo.getNetworkType())) {
1027 tunnelId = prefixInfo.getSegmentationId();
1029 LOG.warn("Network is not of type VXLAN for prefix {}."
1030 + "Going with default Lport Tag.", prefixInfo.toString());
1033 List<ActionInfo> actionInfos = new ArrayList<>();
1034 actionInfos.add(new ActionSetFieldTunnelId(Uint64.valueOf(tunnelId.longValue())));
1035 String ifName = prefixInfo.getVpnInterfaceName();
1036 String vpnName = fibUtil.getVpnNameFromId(vpnId);
1037 if (vpnName == null) {
1040 String macAddress = fibUtil.getMacAddressFromPrefix(ifName, vpnName, nextHopPrefixIp);
1041 actionInfos.add(new ActionSetFieldEthernetDestination(actionInfos.size(),
1042 new MacAddress(macAddress)));
1043 List<ActionInfo> egressActions;
1044 if (egressActionMap.containsKey(egressInterface)) {
1045 egressActions = egressActionMap.get(egressInterface);
1047 egressActions = getEgressActionsForInterface(egressInterface, actionInfos.size(),
1048 true, vpnId, vrfEntry.getDestPrefix());
1049 if (egressActions.isEmpty()) {
1050 LOG.error("Skipping getBucketsForRemoteNexthop: Empty list of egress actions received for "
1051 + "interface {} on dpn {} for vpn {} prefix {} nextHop {}", ifName, dpnId,
1052 vpnId, vrfEntry.getDestPrefix(), nextHopPrefixIp);
1054 egressActionMap.put(egressInterface, egressActions);
1056 if (egressActions.isEmpty()) {
1057 LOG.error("Failed to retrieve egress action for prefix {} route-paths {}"
1058 + " interface {}." + " Aborting remote FIB entry creation.",
1059 vrfEntry.getDestPrefix(), vrfEntry.getRoutePaths(), egressInterface);
1061 actionInfos.addAll(egressActions);
1062 BucketInfo bucket = new BucketInfo(actionInfos);
1063 bucket.setWeight(1);
1064 listBucketInfo.add(bucket);
1066 LOG.trace("LOCAL: listbucket {}, rd {}, dpnId {}, routes {}", listBucketInfo, rd, dpnId, vpnExtraRoutes);
1067 return listBucketInfo;
1070 public void createDcGwLoadBalancingGroup(Uint64 dpnId, String destinationIp,
1071 Class<? extends TunnelTypeBase> tunnelType) {
1072 jobCoordinator.enqueueJob(getJobKey(dpnId), () -> {
1073 List<ListenableFuture<Void>> futures = new ArrayList<>();
1074 futures.add(txRunner.callWithNewReadWriteTransactionAndSubmit(OPERATIONAL, operationalTx -> {
1075 synchronized (getDcGateWaySyncKey(destinationIp)) {
1076 FibUtil.addL3vpnDcGateWay(destinationIp, operationalTx);
1078 futures.add(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, configTx -> {
1079 List<String> availableDcGws = getDcGwIps();
1080 Preconditions.checkNotNull(availableDcGws, "There are no dc-gws present");
1081 int noOfDcGws = availableDcGws.size();
1082 if (noOfDcGws == 1) {
1083 LOG.trace("There are no enough DC GateWays {} present to program LB group", availableDcGws);
1086 if (availableDcGws.contains(destinationIp)) {
1087 availableDcGws.remove(destinationIp);
1089 availableDcGws.forEach(dcGwIp -> {
1090 List<String> dcGws = Arrays.asList(dcGwIp, destinationIp);
1091 Collections.sort(dcGws);
1092 String groupIdKey = FibUtil.getGreLbGroupKey(dcGws);
1093 Long groupId = createNextHopPointer(groupIdKey);
1094 List<Bucket> listBucket = new ArrayList<>();
1095 for (int index = 0; index < dcGws.size(); index++) {
1096 if (isTunnelUp(dcGws.get(index), dpnId, tunnelType)) {
1097 listBucket.add(buildBucketForDcGwLbGroup(dcGws.get(index),
1098 dpnId, index, tunnelType, true));
1101 Group group = MDSALUtil.buildGroup(groupId, groupIdKey, GroupTypes.GroupSelect,
1102 MDSALUtil.buildBucketLists(listBucket));
1103 mdsalApiManager.addGroup(configTx, dpnId, group);
1104 FibUtil.updateLbGroupInfo(dpnId, groupIdKey, groupId.toString(), operationalTx);
1105 LOG.trace("LB group {} towards DC-GW installed on dpn {}. Group - {}",
1106 groupIdKey, dpnId, group);
1114 private String getJobKey(Uint64 dpnId) {
1115 return new StringBuilder().append(NEXTHOPMANAGER_JOB_KEY_PREFIX).append(dpnId).toString();
1118 private String getDcGateWaySyncKey(String destinationIp) {
1119 String mutex = new StringBuilder().append("L3vpncDcGateWay").append(destinationIp).toString();
1120 return mutex.intern();
1123 private List<String> getDcGwIps() {
1124 InstanceIdentifier<DcGatewayIpList> dcGatewayIpListid =
1125 InstanceIdentifier.builder(DcGatewayIpList.class).build();
1126 DcGatewayIpList dcGatewayIpListConfig =
1127 MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, dcGatewayIpListid).orNull();
1128 if (dcGatewayIpListConfig == null) {
1129 return Collections.emptyList();
1131 return dcGatewayIpListConfig.getDcGatewayIp()
1133 .filter(dcGwIp -> dcGwIp.getTunnnelType().equals(TunnelTypeMplsOverGre.class))
1134 .map(dcGwIp -> dcGwIp.getIpAddress().stringValue()).sorted()
1138 private boolean isTunnelUp(String dcGwIp, Uint64 dpnId, Class<? extends TunnelTypeBase> tunnelType) {
1139 String tunnelName = getTunnelRemoteNextHopPointer(dpnId, dcGwIp, tunnelType);
1140 if (tunnelName != null) {
1141 InstanceIdentifier<StateTunnelList> tunnelStateId =
1142 InstanceIdentifier.builder(TunnelsState.class).child(
1143 StateTunnelList.class, new StateTunnelListKey(tunnelName)).build();
1144 return MDSALUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, tunnelStateId)
1145 .toJavaUtil().map(StateTunnelList::getOperState)
1146 .orElse(TunnelOperStatus.Down) == TunnelOperStatus.Up;
1151 private List<Action> getEgressActions(String interfaceName, int actionKey) {
1152 List<Action> actions = Collections.emptyList();
1154 GetEgressActionsForInterfaceInputBuilder egressAction =
1155 new GetEgressActionsForInterfaceInputBuilder().setIntfName(interfaceName).setActionKey(actionKey);
1156 Future<RpcResult<GetEgressActionsForInterfaceOutput>> result =
1157 odlInterfaceRpcService.getEgressActionsForInterface(egressAction.build());
1158 RpcResult<GetEgressActionsForInterfaceOutput> rpcResult = result.get();
1159 if (!rpcResult.isSuccessful()) {
1160 LOG.warn("RPC Call to Get egress actions for interface {} returned with Errors {}",
1161 interfaceName, rpcResult.getErrors());
1163 actions = rpcResult.getResult().nonnullAction();
1165 } catch (InterruptedException | ExecutionException e) {
1166 LOG.warn("Exception when egress actions for interface {}", interfaceName, e);
1172 * This method is invoked when the neighbor is removed from DS.
1173 * All the LB groups which point to the given destination will be deleted.
1175 public void removeDcGwLoadBalancingGroup(Uint64 dpnId,
1176 String destinationIp) {
1177 jobCoordinator.enqueueJob(getJobKey(dpnId), () -> {
1178 List<String> availableDcGws = fibUtil.getL3VpnDcGateWays();
1179 if (availableDcGws.contains(destinationIp)) {
1180 availableDcGws.remove(destinationIp);
1182 List<ListenableFuture<Void>> futures = new ArrayList<>();
1183 futures.add(txRunner.callWithNewReadWriteTransactionAndSubmit(OPERATIONAL, operationalTx -> {
1184 futures.add(txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, configTx -> {
1185 availableDcGws.forEach(dcGwIp -> {
1186 List<String> dcGws = Arrays.asList(dcGwIp, destinationIp);
1187 Collections.sort(dcGws);
1188 String nextHopKey = FibUtil.getGreLbGroupKey(dcGws);
1189 Optional<Nexthops> optionalNextHops = fibUtil.getNexthops(nextHopKey);
1190 if (!optionalNextHops.isPresent()) {
1193 Nexthops nexthops = optionalNextHops.get();
1194 final String groupId = nexthops.getGroupId();
1195 final long groupIdValue = Long.parseLong(groupId);
1196 Group group = MDSALUtil.buildGroup(groupIdValue, nextHopKey, GroupTypes.GroupSelect,
1197 MDSALUtil.buildBucketLists(Collections.emptyList()));
1198 LOG.trace("Removed LB group {} on dpn {}", group, dpnId);
1200 mdsalApiManager.removeGroup(configTx, dpnId, group);
1201 } catch (ExecutionException | InterruptedException e) {
1202 LOG.error("Group removal failed for group {} with exception", groupId, e);
1204 removeNextHopPointer(nextHopKey);
1205 FibUtil.removeOrUpdateNextHopInfo(dpnId, nextHopKey, groupId, nexthops, operationalTx);
1207 synchronized (getDcGateWaySyncKey(destinationIp)) {
1208 FibUtil.removeL3vpnDcGateWay(destinationIp, operationalTx);
1217 * This method is invoked when the tunnel status is deleted.
1218 * All the buckets which point to given destination will be marked down.
1220 public void updateDcGwLoadBalancingGroup(Uint64 dpnId, String destinationIp,
1221 boolean isTunnelUp, Class<? extends TunnelTypeBase> tunnelType) {
1222 jobCoordinator.enqueueJob(getJobKey(dpnId), () -> {
1223 List<String> availableDcGws = fibUtil.getL3VpnDcGateWays();
1224 if (availableDcGws.contains(destinationIp)) {
1225 availableDcGws.remove(destinationIp);
1227 List<ListenableFuture<Void>> futures = new ArrayList<>();
1228 futures.add(txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, configTx -> {
1229 availableDcGws.forEach(dcGwIp -> {
1230 List<String> dcGws = Arrays.asList(dcGwIp, destinationIp);
1231 Collections.sort(dcGws);
1232 String nextHopKey = FibUtil.getGreLbGroupKey(dcGws);
1233 int bucketId = dcGws.indexOf(destinationIp);
1234 Optional<Nexthops> optionalNextHops = fibUtil.getNexthops(nextHopKey);
1235 if (!optionalNextHops.isPresent()) {
1238 Nexthops nexthops = optionalNextHops.get();
1239 final String groupId = nexthops.getGroupId();
1240 final long groupIdValue = Long.parseLong(groupId);
1241 Bucket bucket = buildBucketForDcGwLbGroup(destinationIp, dpnId, bucketId, tunnelType, isTunnelUp);
1242 LOG.trace("updated bucket {} to group {} on dpn {}.", bucket, groupId, dpnId);
1244 mdsalApiManager.addBucket(configTx, dpnId, groupIdValue, bucket);
1245 } catch (ExecutionException | InterruptedException e) {
1246 LOG.error("Bucket addition failed for bucket {} with exception", bucketId, e);
1254 private Bucket buildBucketForDcGwLbGroup(String ipAddress, Uint64 dpnId,
1255 int index, Class<? extends TunnelTypeBase> tunnelType, boolean isTunnelUp) {
1256 List<Action> listAction = new ArrayList<>();
1257 // ActionKey 0 goes to mpls label.
1259 listAction.add(new ActionPushMpls().buildAction());
1260 listAction.add(new ActionRegMove(actionKey++, FibConstants.NXM_REG_MAPPING
1261 .get(index), 0, 19).buildAction());
1262 String tunnelInterfaceName = getTunnelInterfaceName(dpnId, IpAddressBuilder.getDefaultInstance(ipAddress),
1264 List<Action> egressActions = getEgressActions(tunnelInterfaceName, actionKey++);
1265 if (!egressActions.isEmpty()) {
1266 listAction.addAll(getEgressActions(tunnelInterfaceName, actionKey++));
1268 // clear off actions if there is no egress actions.
1269 listAction = Collections.emptyList();
1271 long watchPort = MDSALUtil.WATCH_PORT;
1273 watchPort = 0xFFFFFFFEL;
1275 //OVS expects a non-zero weight value for load balancing to happen in select groups
1276 return MDSALUtil.buildBucket(listAction, SELECT_GROUP_WEIGHT, index,
1277 watchPort, MDSALUtil.WATCH_GROUP);
1280 public void programDcGwLoadBalancingGroup(Uint64 dpnId, String destinationIp,
1281 int addRemoveOrUpdate, boolean isTunnelUp,
1282 Class<? extends TunnelTypeBase> tunnelType) {
1283 if (NwConstants.ADD_FLOW == addRemoveOrUpdate) {
1284 createDcGwLoadBalancingGroup(dpnId, destinationIp, tunnelType);
1285 } else if (NwConstants.DEL_FLOW == addRemoveOrUpdate) {
1286 removeDcGwLoadBalancingGroup(dpnId, destinationIp);
1287 } else if (NwConstants.MOD_FLOW == addRemoveOrUpdate) {
1288 updateDcGwLoadBalancingGroup(dpnId, destinationIp, isTunnelUp, tunnelType);