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.concurrent.CopyOnWriteArrayList;
24 import java.util.concurrent.ExecutionException;
25 import java.util.concurrent.Future;
26 import javax.annotation.PreDestroy;
27 import javax.inject.Inject;
28 import javax.inject.Singleton;
29 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
30 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
31 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
32 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
33 import org.opendaylight.genius.interfacemanager.interfaces.IInterfaceManager;
34 import org.opendaylight.genius.itm.globals.ITMConstants;
35 import org.opendaylight.genius.mdsalutil.ActionInfo;
36 import org.opendaylight.genius.mdsalutil.BucketInfo;
37 import org.opendaylight.genius.mdsalutil.GroupEntity;
38 import org.opendaylight.genius.mdsalutil.MDSALUtil;
39 import org.opendaylight.genius.mdsalutil.NwConstants;
40 import org.opendaylight.genius.mdsalutil.actions.ActionGroup;
41 import org.opendaylight.genius.mdsalutil.actions.ActionNxResubmit;
42 import org.opendaylight.genius.mdsalutil.actions.ActionOutput;
43 import org.opendaylight.genius.mdsalutil.actions.ActionPushMpls;
44 import org.opendaylight.genius.mdsalutil.actions.ActionPushVlan;
45 import org.opendaylight.genius.mdsalutil.actions.ActionRegLoad;
46 import org.opendaylight.genius.mdsalutil.actions.ActionRegMove;
47 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldEthernetDestination;
48 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldEthernetSource;
49 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldTunnelId;
50 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldVlanVid;
51 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
52 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
53 import org.opendaylight.infrautils.utils.concurrent.ListenableFutures;
54 import org.opendaylight.netvirt.elanmanager.api.IElanService;
55 import org.opendaylight.netvirt.fibmanager.api.L3VPNTransportTypes;
56 import org.opendaylight.netvirt.vpnmanager.api.VpnExtraRouteHelper;
57 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana._if.type.rev140508.L2vlan;
58 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana._if.type.rev140508.Tunnel;
59 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddressBuilder;
60 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.InterfaceType;
61 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.OutputActionCase;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.PushVlanActionCase;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetFieldCase;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.AllocateIdInput;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.AllocateIdInputBuilder;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.AllocateIdOutput;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.CreateIdPoolInput;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.CreateIdPoolInputBuilder;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.CreateIdPoolOutput;
72 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
73 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.ReleaseIdInput;
74 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.ReleaseIdInputBuilder;
75 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.ReleaseIdOutput;
76 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeBase;
77 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeGre;
78 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeMplsOverGre;
79 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeVxlan;
80 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetEgressActionsForInterfaceInputBuilder;
81 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetEgressActionsForInterfaceOutput;
82 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.OdlInterfaceRpcService;
83 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.TunnelOperStatus;
84 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.TunnelsState;
85 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.tunnels_state.StateTunnelList;
86 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.tunnels_state.StateTunnelListKey;
87 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetEgressActionsForTunnelInputBuilder;
88 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetEgressActionsForTunnelOutput;
89 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetInternalOrExternalInterfaceNameInputBuilder;
90 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetInternalOrExternalInterfaceNameOutput;
91 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetTunnelInterfaceNameInputBuilder;
92 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetTunnelInterfaceNameOutput;
93 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.ItmRpcService;
94 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.lockmanager.rev160413.LockManagerService;
95 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.AddGroupInput;
96 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.AddGroupInputBuilder;
97 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.AddGroupOutput;
98 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.SalGroupService;
99 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupId;
100 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupRef;
101 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupTypes;
102 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.group.Buckets;
103 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.group.buckets.Bucket;
104 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.Group;
105 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
106 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.SegmentTypeBase;
107 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.SegmentTypeFlat;
108 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.SegmentTypeVlan;
109 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.instances.ElanInstance;
110 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentries.VrfEntry;
111 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.L3nexthop;
112 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.VpnNexthops;
113 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.VpnNexthopsKey;
114 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.vpnnexthops.VpnNexthop;
115 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.vpnnexthops.VpnNexthopBuilder;
116 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.vpnnexthops.VpnNexthopKey;
117 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.vpnnexthops.vpnnexthop.IpAdjacencies;
118 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.vpnnexthops.vpnnexthop.IpAdjacenciesBuilder;
119 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.vpnnexthops.vpnnexthop.IpAdjacenciesKey;
120 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.ConfTransportTypeL3vpn;
121 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.ConfTransportTypeL3vpnBuilder;
122 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.dpid.l3vpn.lb.nexthops.DpnLbNexthops;
123 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.l3vpn.lb.nexthops.Nexthops;
124 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.prefix.to._interface.vpn.ids.Prefixes;
125 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.to.extraroutes.vpn.extra.routes.Routes;
126 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg6;
127 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.add.group.input.buckets.bucket.action.action.NxActionResubmitRpcAddGroupCase;
128 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;
129 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.nx.action.reg.load.grouping.NxRegLoad;
130 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
131 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.InstanceIdentifierBuilder;
132 import org.opendaylight.yangtools.yang.common.RpcResult;
133 import org.slf4j.Logger;
134 import org.slf4j.LoggerFactory;
137 public class NexthopManager implements AutoCloseable {
138 private static final Logger LOG = LoggerFactory.getLogger(NexthopManager.class);
139 private static final String NEXTHOP_ID_POOL_NAME = "nextHopPointerPool";
140 private static final long WAIT_TIME_FOR_SYNC_INSTALL = Long.getLong("wait.time.sync.install", 300L);
141 private static final long WAIT_TIME_TO_ACQUIRE_LOCK = 3000L;
142 private static final int SELECT_GROUP_WEIGHT = 1;
144 private final DataBroker dataBroker;
145 private final ManagedNewTransactionRunner txRunner;
146 private final IMdsalApiManager mdsalApiManager;
147 private final OdlInterfaceRpcService odlInterfaceRpcService;
148 private final ItmRpcService itmManager;
149 private final IdManagerService idManager;
150 private final IElanService elanService;
151 private final LockManagerService lockManager;
152 private final SalGroupService salGroupService;
153 private final JobCoordinator jobCoordinator;
154 private final FibUtil fibUtil;
155 private final IInterfaceManager interfaceManager;
156 private volatile L3VPNTransportTypes configuredTransportTypeL3VPN = L3VPNTransportTypes.Invalid;
159 * Provides nexthop functions.
160 * Creates group ID pool
162 * @param dataBroker - dataBroker reference
163 * @param mdsalApiManager - mdsalApiManager reference
164 * @param idManager - idManager reference
165 * @param odlInterfaceRpcService - odlInterfaceRpcService reference
166 * @param itmManager - itmManager reference
169 public NexthopManager(final DataBroker dataBroker,
170 final IMdsalApiManager mdsalApiManager,
171 final IdManagerService idManager,
172 final OdlInterfaceRpcService odlInterfaceRpcService,
173 final ItmRpcService itmManager,
174 final LockManagerService lockManager,
175 final IElanService elanService,
176 final SalGroupService salGroupService,
177 final JobCoordinator jobCoordinator,
178 final FibUtil fibUtil,
179 final IInterfaceManager interfaceManager) {
180 this.dataBroker = dataBroker;
181 this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
182 this.mdsalApiManager = mdsalApiManager;
183 this.idManager = idManager;
184 this.odlInterfaceRpcService = odlInterfaceRpcService;
185 this.itmManager = itmManager;
186 this.elanService = elanService;
187 this.salGroupService = salGroupService;
188 this.jobCoordinator = jobCoordinator;
189 this.fibUtil = fibUtil;
190 this.lockManager = lockManager;
191 this.interfaceManager = interfaceManager;
195 private void createIdPool() {
196 CreateIdPoolInput createPool = new CreateIdPoolInputBuilder()
197 .setPoolName(NEXTHOP_ID_POOL_NAME)
202 Future<RpcResult<CreateIdPoolOutput>> result = idManager.createIdPool(createPool);
203 if (result != null && result.get().isSuccessful()) {
204 LOG.info("Created IdPool for NextHopPointerPool");
206 } catch (InterruptedException | ExecutionException e) {
207 LOG.error("Failed to create idPool for NextHopPointerPool", e);
211 private String getNextHopKey(long vpnId, String ipAddress) {
212 return "nexthop." + vpnId + ipAddress;
215 public ItmRpcService getItmManager() {
219 protected long createNextHopPointer(String nexthopKey) {
220 AllocateIdInput getIdInput = new AllocateIdInputBuilder()
221 .setPoolName(NEXTHOP_ID_POOL_NAME).setIdKey(nexthopKey)
223 //TODO: Proper error handling once IdManager code is complete
225 Future<RpcResult<AllocateIdOutput>> result = idManager.allocateId(getIdInput);
226 RpcResult<AllocateIdOutput> rpcResult = result.get();
227 return rpcResult.getResult().getIdValue();
228 } catch (NullPointerException | InterruptedException | ExecutionException e) {
234 protected void removeNextHopPointer(String nexthopKey) {
235 ReleaseIdInput idInput = new ReleaseIdInputBuilder()
236 .setPoolName(NEXTHOP_ID_POOL_NAME)
237 .setIdKey(nexthopKey).build();
239 RpcResult<ReleaseIdOutput> rpcResult = idManager.releaseId(idInput).get();
240 if (!rpcResult.isSuccessful()) {
241 LOG.error("RPC Call to Get Unique Id for nexthopKey {} returned with Errors {}",
242 nexthopKey, rpcResult.getErrors());
244 } catch (InterruptedException | ExecutionException e) {
245 LOG.warn("Exception when getting Unique Id for key {}", nexthopKey, e);
249 protected List<ActionInfo> getEgressActionsForInterface(final String ifName, int actionKey,
250 boolean isTunnelInterface) {
251 List<Action> actions;
253 if (isTunnelInterface && interfaceManager.isItmDirectTunnelsEnabled()) {
254 RpcResult<GetEgressActionsForTunnelOutput> rpcResult =
255 itmManager.getEgressActionsForTunnel(new GetEgressActionsForTunnelInputBuilder()
256 .setIntfName(ifName).build()).get();
257 if (!rpcResult.isSuccessful()) {
258 LOG.error("RPC Call to Get egress tunnel actions for interface {} returned with Errors {}",
259 ifName, rpcResult.getErrors());
260 return Collections.emptyList();
262 actions = rpcResult.getResult().getAction();
265 RpcResult<GetEgressActionsForInterfaceOutput> rpcResult = odlInterfaceRpcService
266 .getEgressActionsForInterface(new GetEgressActionsForInterfaceInputBuilder()
267 .setIntfName(ifName).build()).get();
268 if (!rpcResult.isSuccessful()) {
269 LOG.error("RPC Call to Get egress vm actions for interface {} returned with Errors {}",
270 ifName, rpcResult.getErrors());
271 return Collections.emptyList();
273 actions = rpcResult.getResult().getAction();
276 List<ActionInfo> listActionInfo = new ArrayList<>();
277 for (Action action : actions) {
278 actionKey = action.key().getOrder() + actionKey;
279 org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action
280 actionClass = action.getAction();
281 if (actionClass instanceof OutputActionCase) {
282 listActionInfo.add(new ActionOutput(actionKey,
283 ((OutputActionCase) actionClass).getOutputAction().getOutputNodeConnector()));
284 } else if (actionClass instanceof PushVlanActionCase) {
285 listActionInfo.add(new ActionPushVlan(actionKey));
286 } else if (actionClass instanceof SetFieldCase) {
287 if (((SetFieldCase) actionClass).getSetField().getVlanMatch() != null) {
288 int vlanVid = ((SetFieldCase) actionClass).getSetField().getVlanMatch()
289 .getVlanId().getVlanId().getValue();
290 listActionInfo.add(new ActionSetFieldVlanVid(actionKey, vlanVid));
292 } else if (actionClass instanceof NxActionResubmitRpcAddGroupCase) {
293 Short tableId = ((NxActionResubmitRpcAddGroupCase) actionClass).getNxResubmit().getTable();
294 listActionInfo.add(new ActionNxResubmit(actionKey, tableId));
295 } else if (actionClass instanceof NxActionRegLoadNodesNodeTableFlowApplyActionsCase) {
296 NxRegLoad nxRegLoad =
297 ((NxActionRegLoadNodesNodeTableFlowApplyActionsCase) actionClass).getNxRegLoad();
298 listActionInfo.add(new ActionRegLoad(actionKey, NxmNxReg6.class,
299 nxRegLoad.getDst().getStart(), nxRegLoad.getDst().getEnd(),
300 nxRegLoad.getValue().longValue()));
303 return listActionInfo;
304 } catch (InterruptedException | ExecutionException e) {
305 LOG.warn("Exception when egress actions for interface {}", ifName, e);
307 LOG.warn("Exception when egress actions for interface {}", ifName);
308 return Collections.emptyList();
311 protected String getTunnelInterfaceName(BigInteger srcDpId, BigInteger dstDpId) {
312 Class<? extends TunnelTypeBase> tunType = getReqTunType(getReqTransType().toUpperCase(Locale.getDefault()));
313 Future<RpcResult<GetTunnelInterfaceNameOutput>> result;
315 result = itmManager.getTunnelInterfaceName(new GetTunnelInterfaceNameInputBuilder()
316 .setSourceDpid(srcDpId)
317 .setDestinationDpid(dstDpId)
318 .setTunnelType(tunType)
320 RpcResult<GetTunnelInterfaceNameOutput> rpcResult = result.get();
321 if (!rpcResult.isSuccessful()) {
322 LOG.warn("RPC Call to getTunnelInterfaceId returned with Errors {}", rpcResult.getErrors());
324 return rpcResult.getResult().getInterfaceName();
326 } catch (InterruptedException | ExecutionException e) {
327 LOG.warn("Exception when getting tunnel interface Id for tunnel between {} and {}", srcDpId, dstDpId, e);
332 protected String getTunnelInterfaceName(BigInteger srcDpId, org.opendaylight.yang.gen.v1.urn.ietf.params
333 .xml.ns.yang.ietf.inet.types.rev130715.IpAddress dstIp, Class<? extends TunnelTypeBase> tunnelType) {
334 Future<RpcResult<GetInternalOrExternalInterfaceNameOutput>> result;
336 LOG.debug("Trying to fetch tunnel interface name for source dpn {} destIp {} tunType {}", srcDpId,
337 dstIp.stringValue(), tunnelType.getName());
338 result = itmManager.getInternalOrExternalInterfaceName(new GetInternalOrExternalInterfaceNameInputBuilder()
339 .setSourceDpid(srcDpId)
340 .setDestinationIp(dstIp)
341 .setTunnelType(tunnelType)
343 RpcResult<GetInternalOrExternalInterfaceNameOutput> rpcResult = result.get();
344 if (!rpcResult.isSuccessful()) {
345 LOG.warn("RPC Call to getTunnelInterfaceName 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, dstIp, e);
356 public long getLocalNextHopGroup(long vpnId,
357 String ipNextHopAddress) {
358 long groupId = createNextHopPointer(getNextHopKey(vpnId, ipNextHopAddress));
359 if (groupId == FibConstants.INVALID_GROUP_ID) {
360 LOG.error("Unable to allocate groupId for vpnId {} , prefix {}", vpnId, ipNextHopAddress);
365 public long createLocalNextHop(long vpnId, BigInteger dpnId, String ifName,
366 String primaryIpAddress, String currDestIpPrefix,
367 String gwMacAddress) {
368 String vpnName = fibUtil.getVpnNameFromId(vpnId);
369 if (vpnName == null) {
372 String macAddress = fibUtil.getMacAddressFromPrefix(ifName, vpnName, primaryIpAddress);
374 long groupId = createNextHopPointer(getNextHopKey(vpnId, primaryIpAddress));
376 LOG.error("Unable to allocate groupId for vpnId {} , IntfName {}, primaryIpAddress {} curIpPrefix {}",
377 vpnId, ifName, primaryIpAddress, currDestIpPrefix);
380 String nextHopLockStr = vpnId + primaryIpAddress;
381 String jobKey = FibUtil.getCreateLocalNextHopJobKey(vpnId, dpnId, currDestIpPrefix);
382 jobCoordinator.enqueueJob(jobKey, () -> {
384 if (FibUtil.lockCluster(lockManager, nextHopLockStr, WAIT_TIME_TO_ACQUIRE_LOCK)) {
385 VpnNexthop nexthop = getVpnNexthop(vpnId, primaryIpAddress);
386 LOG.trace("nexthop: {} retrieved for vpnId {}, prefix {}, ifName {} on dpn {}", nexthop, vpnId,
387 primaryIpAddress, ifName, dpnId);
388 if (nexthop == null) {
389 String encMacAddress = macAddress == null
390 ? fibUtil.getMacAddressFromPrefix(ifName, vpnName, primaryIpAddress) : macAddress;
391 List<BucketInfo> listBucketInfo = new ArrayList<>();
392 List<ActionInfo> listActionInfo = new ArrayList<>();
395 if (encMacAddress != null) {
396 if (gwMacAddress != null) {
397 LOG.trace("The Local NextHop Group Source Mac {} for VpnInterface {} on VPN {}",
398 gwMacAddress, ifName, vpnId);
399 listActionInfo.add(new ActionSetFieldEthernetSource(actionKey++,
400 new MacAddress(gwMacAddress)));
402 listActionInfo.add(new ActionSetFieldEthernetDestination(actionKey++,
403 new MacAddress(encMacAddress)));
404 // listActionInfo.add(0, new ActionPopMpls());
406 // FIXME: Log message here.
407 LOG.debug("mac address for new local nexthop is null");
409 listActionInfo.addAll(getEgressActionsForInterface(ifName, actionKey, false));
410 BucketInfo bucket = new BucketInfo(listActionInfo);
412 listBucketInfo.add(bucket);
413 GroupEntity groupEntity = MDSALUtil.buildGroupEntity(dpnId, groupId, primaryIpAddress,
414 GroupTypes.GroupAll, listBucketInfo);
415 LOG.trace("Install LNH Group: id {}, mac address {}, interface {} for prefix {}", groupId,
416 encMacAddress, ifName, primaryIpAddress);
417 //Try to install group directly on the DPN bypassing the FRM, in order to avoid waiting for the
418 // group to get installed before programming the flows
419 installGroupOnDpn(groupId, dpnId, primaryIpAddress, listBucketInfo,
420 getNextHopKey(vpnId, primaryIpAddress), GroupTypes.GroupAll);
422 mdsalApiManager.syncInstallGroup(groupEntity);
424 addVpnNexthopToDS(dpnId, vpnId, primaryIpAddress, currDestIpPrefix, groupId);
427 // Ignore adding new prefix , if it already exists
428 List<IpAdjacencies> prefixesList = nexthop.getIpAdjacencies();
429 IpAdjacencies prefix = new IpAdjacenciesBuilder().setIpAdjacency(currDestIpPrefix).build();
430 if (prefixesList != null && prefixesList.contains(prefix)) {
431 LOG.trace("Prefix {} is already present in l3nextHop {} ", currDestIpPrefix, nexthop);
433 IpAdjacenciesBuilder ipPrefixesBuilder =
434 new IpAdjacenciesBuilder().withKey(new IpAdjacenciesKey(currDestIpPrefix));
435 LOG.trace("Updating prefix {} to vpnNextHop {} Operational DS", currDestIpPrefix, nexthop);
436 MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL,
437 getVpnNextHopIpPrefixIdentifier(vpnId, primaryIpAddress, currDestIpPrefix),
438 ipPrefixesBuilder.build());
443 FibUtil.unlockCluster(lockManager, nextHopLockStr);
445 return Collections.emptyList();
450 private void installGroupOnDpn(long groupId, BigInteger dpnId, String groupName, List<BucketInfo> bucketsInfo,
451 String nextHopKey, GroupTypes groupType) {
452 NodeRef nodeRef = FibUtil.buildNodeRef(dpnId);
453 Buckets buckets = FibUtil.buildBuckets(bucketsInfo);
454 GroupRef groupRef = new GroupRef(FibUtil.buildGroupInstanceIdentifier(groupId, dpnId));
455 AddGroupInput input = new AddGroupInputBuilder().setNode(nodeRef).setGroupId(new GroupId(groupId))
456 .setBuckets(buckets).setGroupRef(groupRef).setGroupType(groupType)
457 .setGroupName(groupName).build();
458 Future<RpcResult<AddGroupOutput>> groupStats = salGroupService.addGroup(input);
459 RpcResult<AddGroupOutput> rpcResult = null;
461 rpcResult = groupStats.get();
462 if (rpcResult != null && rpcResult.isSuccessful()) {
463 LOG.info("Group {} with key {} has been successfully installed directly on dpn {}.", groupId,
466 LOG.error("Unable to install group {} with key {} directly on dpn {} due to {}.", groupId, nextHopKey,
467 dpnId, rpcResult != null ? rpcResult.getErrors() : null);
469 } catch (InterruptedException | ExecutionException e) {
470 LOG.error("Error while installing group {} directly on dpn {}", groupId, dpnId);
474 protected void addVpnNexthopToDS(BigInteger dpnId, long vpnId, String primaryIpAddr,
475 String currIpAddr, long egressPointer) {
476 InstanceIdentifierBuilder<VpnNexthops> idBuilder = InstanceIdentifier.builder(L3nexthop.class)
477 .child(VpnNexthops.class, new VpnNexthopsKey(vpnId));
479 List<IpAdjacencies> ipPrefixesList = new ArrayList<>();
480 IpAdjacencies prefix = new IpAdjacenciesBuilder().setIpAdjacency(currIpAddr).build();
481 ipPrefixesList.add(prefix);
482 // Add nexthop to vpn node
483 VpnNexthop nh = new VpnNexthopBuilder()
484 .withKey(new VpnNexthopKey(primaryIpAddr))
486 .setIpAdjacencies(ipPrefixesList)
487 .setEgressPointer(egressPointer).build();
489 InstanceIdentifier<VpnNexthop> id1 = idBuilder
490 .child(VpnNexthop.class, new VpnNexthopKey(primaryIpAddr)).build();
491 LOG.trace("Adding vpnnextHop {} to Operational DS", nh);
492 MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL, id1, nh);
496 protected InstanceIdentifier<IpAdjacencies> getVpnNextHopIpPrefixIdentifier(long vpnId, String primaryIpAddress,
498 InstanceIdentifier<IpAdjacencies> id = InstanceIdentifier.builder(L3nexthop.class)
499 .child(VpnNexthops.class, new VpnNexthopsKey(vpnId))
500 .child(VpnNexthop.class, new VpnNexthopKey(primaryIpAddress))
501 .child(IpAdjacencies.class, new IpAdjacenciesKey(ipPrefix)).build();
505 protected VpnNexthop getVpnNexthop(long vpnId, String ipAddress) {
507 // check if vpn node is there
508 InstanceIdentifierBuilder<VpnNexthops> idBuilder =
509 InstanceIdentifier.builder(L3nexthop.class).child(VpnNexthops.class,
510 new VpnNexthopsKey(vpnId));
511 InstanceIdentifier<VpnNexthops> id = idBuilder.build();
512 Optional<VpnNexthops> vpnNexthops = MDSALUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, id);
513 if (vpnNexthops.isPresent()) {
514 // get nexthops list for vpn
515 List<VpnNexthop> nexthops = vpnNexthops.get().getVpnNexthop();
516 for (VpnNexthop nexthop : nexthops) {
517 if (nexthop.getIpAddress().equals(ipAddress)) {
519 LOG.trace("VpnNextHop : {}", nexthop);
523 // return null if not found
528 public AdjacencyResult getRemoteNextHopPointer(BigInteger remoteDpnId, long vpnId, String prefixIp,
529 String nextHopIp, Class<? extends TunnelTypeBase> tunnelType) {
530 String egressIfName = null;
531 LOG.trace("getRemoteNextHopPointer: input [remoteDpnId {}, vpnId {}, prefixIp {}, nextHopIp {} ]", remoteDpnId,
532 vpnId, prefixIp, nextHopIp);
534 Class<? extends InterfaceType> egressIfType;
535 ElanInstance elanInstance = getElanInstanceForPrefix(vpnId, prefixIp);
536 if (elanInstance != null) {
537 egressIfType = getInterfaceType(elanInstance);
539 LOG.warn("Failed to determine network type for prefixIp {} using tunnel", prefixIp);
540 egressIfType = Tunnel.class;
543 if (Tunnel.class.equals(egressIfType)) {
544 egressIfName = getTunnelRemoteNextHopPointer(remoteDpnId, nextHopIp, tunnelType);
546 egressIfName = getExtPortRemoteNextHopPointer(remoteDpnId, elanInstance);
549 LOG.trace("NextHop pointer for prefixIp {} vpnId {} dpnId {} is {}", prefixIp, vpnId, remoteDpnId,
551 return egressIfName != null ? new AdjacencyResult(egressIfName, egressIfType, nextHopIp,
555 public BigInteger getDpnForPrefix(long vpnId, String prefixIp) {
556 VpnNexthop vpnNexthop = getVpnNexthop(vpnId, prefixIp);
557 BigInteger localDpnId = vpnNexthop == null ? null : vpnNexthop.getDpnId();
561 private void removeVpnNexthopFromDS(long vpnId, String ipPrefix) {
563 InstanceIdentifierBuilder<VpnNexthop> idBuilder = InstanceIdentifier.builder(L3nexthop.class)
564 .child(VpnNexthops.class, new VpnNexthopsKey(vpnId))
565 .child(VpnNexthop.class, new VpnNexthopKey(ipPrefix));
566 InstanceIdentifier<VpnNexthop> id = idBuilder.build();
568 LOG.trace("Removing vpn next hop from datastore : {}", id);
569 MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.OPERATIONAL, id);
572 public void removeLocalNextHop(BigInteger dpnId, Long vpnId, String primaryIpAddress, String currDestIpPrefix) {
573 String nextHopLockStr = vpnId + primaryIpAddress;
575 if (FibUtil.lockCluster(lockManager, nextHopLockStr, WAIT_TIME_TO_ACQUIRE_LOCK)) {
576 VpnNexthop nh = getVpnNexthop(vpnId, primaryIpAddress);
578 List<IpAdjacencies> prefixesList = nh.getIpAdjacencies();
579 IpAdjacencies prefix = new IpAdjacenciesBuilder().setIpAdjacency(currDestIpPrefix).build();
580 prefixesList.remove(prefix);
581 if (prefixesList.isEmpty()) { //remove the group only if there are no more flows using this group
582 GroupEntity groupEntity = MDSALUtil.buildGroupEntity(dpnId, nh.getEgressPointer(),
583 primaryIpAddress, GroupTypes.GroupAll, Collections.EMPTY_LIST);
585 mdsalApiManager.removeGroup(groupEntity);
587 removeVpnNexthopFromDS(vpnId, primaryIpAddress);
589 removeNextHopPointer(getNextHopKey(vpnId, primaryIpAddress));
590 LOG.debug("Local Next hop {} for {} {} on dpn {} successfully deleted",
591 nh.getEgressPointer(), vpnId, primaryIpAddress, dpnId);
593 //remove the currIpPrefx from IpPrefixList of the vpnNexthop
594 LOG.trace("Removing the prefix {} from vpnNextHop {} Operational DS", currDestIpPrefix, nh);
595 MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.OPERATIONAL,
596 getVpnNextHopIpPrefixIdentifier(vpnId, primaryIpAddress, currDestIpPrefix));
600 LOG.error("Local NextHop for VpnId {} curIpPrefix {} on dpn {} primaryIpAddress {} not deleted",
601 vpnId, currDestIpPrefix, dpnId, primaryIpAddress);
605 FibUtil.unlockCluster(lockManager, nextHopLockStr);
609 public void setConfTransType(String service, String transportType) {
611 if (!service.equalsIgnoreCase("L3VPN")) {
612 LOG.error("Incorrect service {} provided for setting the transport type.", service);
616 L3VPNTransportTypes transType = L3VPNTransportTypes.validateTransportType(transportType
617 .toUpperCase(Locale.getDefault()));
619 if (transType != L3VPNTransportTypes.Invalid) {
620 configuredTransportTypeL3VPN = transType;
624 public void writeConfTransTypeConfigDS() {
625 MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, getConfTransportTypeIdentifier(),
626 createConfTransportType(configuredTransportTypeL3VPN.getTransportType()));
629 public L3VPNTransportTypes getConfiguredTransportTypeL3VPN() {
630 return this.configuredTransportTypeL3VPN;
633 public String getReqTransType() {
634 if (configuredTransportTypeL3VPN == L3VPNTransportTypes.Invalid) {
636 * Restart scenario, Read from the ConfigDS.
637 * if the value is Unset, cache value as VxLAN.
639 LOG.trace("configureTransportType is not yet set.");
640 Optional<ConfTransportTypeL3vpn> configuredTransTypeFromConfig =
641 MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, getConfTransportTypeIdentifier());
643 if (configuredTransTypeFromConfig.isPresent()) {
644 if (configuredTransTypeFromConfig.get().getTransportType().equals(TunnelTypeGre.class)) {
645 configuredTransportTypeL3VPN = L3VPNTransportTypes.GRE;
647 configuredTransportTypeL3VPN = L3VPNTransportTypes.VxLAN;
649 LOG.trace("configuredTransportType set from config DS to {}",
650 getConfiguredTransportTypeL3VPN().getTransportType());
652 setConfTransType("L3VPN", L3VPNTransportTypes.VxLAN.getTransportType());
653 LOG.trace("configuredTransportType is not set in the Config DS. VxLAN as default will be used.");
656 LOG.trace("configuredTransportType is set as {}", getConfiguredTransportTypeL3VPN().getTransportType());
658 return getConfiguredTransportTypeL3VPN().getTransportType();
661 public InstanceIdentifier<ConfTransportTypeL3vpn> getConfTransportTypeIdentifier() {
662 return InstanceIdentifier.builder(ConfTransportTypeL3vpn.class).build();
665 private ConfTransportTypeL3vpn createConfTransportType(String type) {
666 ConfTransportTypeL3vpn confTransType;
668 case ITMConstants.TUNNEL_TYPE_GRE:
669 confTransType = new ConfTransportTypeL3vpnBuilder().setTransportType(TunnelTypeGre.class).build();
670 LOG.trace("Setting the confTransportType to GRE.");
672 case ITMConstants.TUNNEL_TYPE_VXLAN:
673 confTransType = new ConfTransportTypeL3vpnBuilder().setTransportType(TunnelTypeVxlan.class).build();
674 LOG.trace("Setting the confTransportType to VxLAN.");
677 LOG.trace("Invalid transport type {} passed to Config DS ", type);
678 confTransType = null;
681 return confTransType;
684 public Class<? extends TunnelTypeBase> getReqTunType(String transportType) {
685 switch (transportType) {
687 return TunnelTypeVxlan.class;
689 return TunnelTypeGre.class;
691 return TunnelTypeMplsOverGre.class;
695 public String getTransportTypeStr(String tunType) {
696 if (tunType.equals(TunnelTypeVxlan.class.toString())) {
697 return ITMConstants.TUNNEL_TYPE_VXLAN;
698 } else if (tunType.equals(TunnelTypeGre.class.toString())) {
699 return ITMConstants.TUNNEL_TYPE_GRE;
700 } else if (tunType.equals(TunnelTypeMplsOverGre.class.toString())) {
701 return ITMConstants.TUNNEL_TYPE_MPLSoGRE;
703 return ITMConstants.TUNNEL_TYPE_INVALID;
709 public void close() {
710 LOG.info("{} close", getClass().getSimpleName());
713 // TODO Clean up the exception handling
714 @SuppressWarnings("checkstyle:IllegalCatch")
715 private String getTunnelRemoteNextHopPointer(BigInteger remoteDpnId, String nextHopIp,
716 Class<? extends TunnelTypeBase> tunnelType) {
717 if (nextHopIp != null && !nextHopIp.isEmpty()) {
719 // here use the config for tunnel type param
720 return getTunnelInterfaceName(remoteDpnId,
721 org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddressBuilder
722 .getDefaultInstance(nextHopIp), tunnelType);
723 } catch (Exception ex) {
724 LOG.error("Error while retrieving nexthop pointer for nexthop {} remoteDpn {}",
725 nextHopIp, remoteDpnId, ex);
732 private String getExtPortRemoteNextHopPointer(BigInteger remoteDpnId, ElanInstance elanInstance) {
733 return elanService.getExternalElanInterface(elanInstance.getElanInstanceName(), remoteDpnId);
737 * Get the interface type associated with the type of ELAN used for routing
738 * traffic to/from remote compute nodes.
740 * @param elanInstance The elan instance
741 * @return L2vlan for flat/VLAN network type and Tunnel otherwise
743 private Class<? extends InterfaceType> getInterfaceType(ElanInstance elanInstance) {
744 Class<? extends SegmentTypeBase> segmentType = elanInstance.getSegmentType();
745 if (SegmentTypeFlat.class.equals(segmentType) || SegmentTypeVlan.class.equals(segmentType)) {
752 private ElanInstance getElanInstanceForPrefix(long vpnId, String prefixIp) {
753 ElanInstance elanInstance = null;
754 Prefixes prefix = fibUtil.getPrefixToInterface(vpnId, prefixIp);
755 if (prefix != null) {
756 if (prefix.getNetworkId() != null) {
757 elanInstance = elanService.getElanInstance(prefix.getNetworkId().getValue());
764 static class AdjacencyResult {
765 private final String interfaceName;
766 private final Class<? extends InterfaceType> interfaceType;
767 private final String nextHopIp;
768 private final String prefix;
770 AdjacencyResult(String interfaceName, Class<? extends InterfaceType> interfaceType, String nextHopIp,
772 this.interfaceName = interfaceName;
773 this.interfaceType = interfaceType;
774 this.nextHopIp = nextHopIp;
775 this.prefix = prefix;
778 public String getInterfaceName() {
779 return interfaceName;
782 public Class<? extends InterfaceType> getInterfaceType() {
783 return interfaceType;
786 public String getNextHopIp() {
790 public String getPrefix() {
795 public int hashCode() {
796 final int prime = 31;
798 result = prime * result + (interfaceName == null ? 0 : interfaceName.hashCode());
803 public boolean equals(Object obj) {
808 boolean result = false;
809 if (getClass() != obj.getClass()) {
812 AdjacencyResult other = (AdjacencyResult) obj;
813 result = interfaceName.equals(other.interfaceName);
819 protected long setupLoadBalancingNextHop(Long parentVpnId, BigInteger dpnId,
820 String destPrefix, List<BucketInfo> listBucketInfo, boolean addOrRemove) {
821 long groupId = createNextHopPointer(getNextHopKey(parentVpnId, destPrefix));
822 if (groupId == FibConstants.INVALID_GROUP_ID) {
823 LOG.error("Unable to allocate/retrieve groupId for vpnId {} , prefix {}", parentVpnId, destPrefix);
826 GroupEntity groupEntity = MDSALUtil.buildGroupEntity(
827 dpnId, groupId, destPrefix, GroupTypes.GroupSelect, listBucketInfo);
828 String jobKey = FibUtil.getCreateLocalNextHopJobKey(parentVpnId, dpnId, destPrefix);
829 jobCoordinator.enqueueJob(jobKey, () -> {
831 mdsalApiManager.syncInstallGroup(groupEntity);
832 if (LOG.isDebugEnabled()) {
833 LOG.debug("Finished installing GroupEntity with jobCoordinator key {} groupEntity.groupId {}"
834 + " groupEntity.groupType {}", jobKey, groupEntity.getGroupId(),
835 groupEntity.getGroupType());
838 mdsalApiManager.removeGroup(groupEntity);
839 if (LOG.isDebugEnabled()) {
840 LOG.debug("Finished removing GroupEntity with jobCoordinator key {} groupEntity.groupId {}"
841 + " groupEntity.groupType {}", jobKey, groupEntity.getGroupId(),
842 groupEntity.getGroupType());
845 return Collections.emptyList();
850 long createNextHopGroups(Long vpnId, String rd, BigInteger dpnId, VrfEntry vrfEntry,
851 Routes routes, List<Routes> vpnExtraRoutes) {
852 List<BucketInfo> listBucketInfo = new ArrayList<>();
853 List<Routes> clonedVpnExtraRoutes = new ArrayList<>(vpnExtraRoutes);
854 if (clonedVpnExtraRoutes.contains(routes)) {
855 listBucketInfo.addAll(getBucketsForLocalNexthop(vpnId, dpnId, vrfEntry, routes));
856 clonedVpnExtraRoutes.remove(routes);
858 listBucketInfo.addAll(getBucketsForRemoteNexthop(vpnId, dpnId, vrfEntry, rd, clonedVpnExtraRoutes));
859 return setupLoadBalancingNextHop(vpnId, dpnId, vrfEntry.getDestPrefix(), listBucketInfo,true);
862 private List<BucketInfo> getBucketsForLocalNexthop(Long vpnId, BigInteger dpnId,
863 VrfEntry vrfEntry, Routes routes) {
864 if (LOG.isDebugEnabled()) {
865 LOG.debug("NexthopManager.getBucketsForLocalNexthop invoked with vpnId {} dpnId {} "
866 + " vrfEntry.routePaths {}, routes.nexthopList {}", vpnId, dpnId, vrfEntry.getRoutePaths(),
867 routes.getNexthopIpList());
869 List<BucketInfo> listBucketInfo = new CopyOnWriteArrayList<>();
870 routes.getNexthopIpList().parallelStream().forEach(nextHopIp -> {
871 String localNextHopIP;
872 if (isIpv4Address(nextHopIp)) {
873 localNextHopIP = nextHopIp + NwConstants.IPV4PREFIX;
875 localNextHopIP = nextHopIp + NwConstants.IPV6PREFIX;
877 Prefixes localNextHopInfo = fibUtil.getPrefixToInterface(vpnId, localNextHopIP);
878 if (localNextHopInfo != null) {
879 long groupId = getLocalNextHopGroup(vpnId, localNextHopIP);
880 if (groupId == FibConstants.INVALID_GROUP_ID) {
881 LOG.error("Unable to allocate groupId for vpnId {} , prefix {} , interface {}", vpnId,
882 vrfEntry.getDestPrefix(), localNextHopInfo.getVpnInterfaceName());
885 List<ActionInfo> actionsInfos =
886 Collections.singletonList(new ActionGroup(groupId));
887 BucketInfo bucket = new BucketInfo(actionsInfos);
889 listBucketInfo.add(bucket);
892 LOG.trace("LOCAL: listbucket {}, vpnId {}, dpnId {}, routes {}", listBucketInfo, vpnId, dpnId, routes);
893 return listBucketInfo;
896 private List<BucketInfo> getBucketsForRemoteNexthop(Long vpnId, BigInteger dpnId, VrfEntry vrfEntry, String rd,
897 List<Routes> vpnExtraRoutes) {
898 List<BucketInfo> listBucketInfo = new ArrayList<>();
899 Map<String, List<ActionInfo>> egressActionMap = new HashMap<>();
900 vpnExtraRoutes.forEach(vpnExtraRoute -> vpnExtraRoute.getNexthopIpList().forEach(nextHopIp -> {
901 String nextHopPrefixIp;
902 if (isIpv4Address(nextHopIp)) {
903 nextHopPrefixIp = nextHopIp + NwConstants.IPV4PREFIX;
905 nextHopPrefixIp = nextHopIp + NwConstants.IPV6PREFIX;
907 List<String> tepIpAddresses = fibUtil.getNextHopAddresses(rd, nextHopPrefixIp);
908 if (tepIpAddresses.isEmpty()) {
911 // There would be only one nexthop address for a VM ip which would be the tep Ip
912 String tepIp = tepIpAddresses.get(0);
913 AdjacencyResult adjacencyResult = getRemoteNextHopPointer(dpnId, vpnId,
914 vrfEntry.getDestPrefix(), tepIp, TunnelTypeVxlan.class);
915 if (adjacencyResult == null) {
918 String egressInterface = adjacencyResult.getInterfaceName();
919 if (!FibUtil.isTunnelInterface(adjacencyResult)) {
922 Class<? extends TunnelTypeBase> tunnelType = VpnExtraRouteHelper.getTunnelType(itmManager, egressInterface);
923 StateTunnelList ifState = fibUtil.getTunnelState(egressInterface);
924 if (ifState == null || ifState.getOperState() != TunnelOperStatus.Up) {
925 LOG.trace("Tunnel not up {}", egressInterface);
928 if (!TunnelTypeVxlan.class.equals(tunnelType)) {
931 Long label = FibUtil.getLabelFromRoutePaths(vrfEntry).get();
932 Prefixes prefixInfo = fibUtil.getPrefixToInterface(vpnId, nextHopPrefixIp);
933 if (prefixInfo == null) {
934 LOG.error("No prefix info found for prefix {} in rd {} for VPN {}", nextHopPrefixIp, rd,
939 if (FibUtil.isVxlanNetwork(prefixInfo.getNetworkType())) {
940 tunnelId = BigInteger.valueOf(prefixInfo.getSegmentationId());
942 LOG.warn("Network is not of type VXLAN for prefix {}."
943 + "Going with default Lport Tag.", prefixInfo.toString());
944 tunnelId = BigInteger.valueOf(label);
946 List<ActionInfo> actionInfos = new ArrayList<>();
947 actionInfos.add(new ActionSetFieldTunnelId(tunnelId));
948 String ifName = prefixInfo.getVpnInterfaceName();
949 String vpnName = fibUtil.getVpnNameFromId(vpnId);
950 if (vpnName == null) {
953 String macAddress = fibUtil.getMacAddressFromPrefix(ifName, vpnName, nextHopPrefixIp);
954 actionInfos.add(new ActionSetFieldEthernetDestination(actionInfos.size(),
955 new MacAddress(macAddress)));
956 List<ActionInfo> egressActions;
957 if (egressActionMap.containsKey(egressInterface)) {
958 egressActions = egressActionMap.get(egressInterface);
960 egressActions = getEgressActionsForInterface(egressInterface, actionInfos.size(), true);
961 egressActionMap.put(egressInterface, egressActions);
963 if (egressActions.isEmpty()) {
964 LOG.error("Failed to retrieve egress action for prefix {} route-paths {}"
965 + " interface {}." + " Aborting remote FIB entry creation.",
966 vrfEntry.getDestPrefix(), vrfEntry.getRoutePaths(), egressInterface);
968 actionInfos.addAll(egressActions);
969 BucketInfo bucket = new BucketInfo(actionInfos);
971 listBucketInfo.add(bucket);
973 LOG.trace("LOCAL: listbucket {}, rd {}, dpnId {}, routes {}", listBucketInfo, rd, dpnId, vpnExtraRoutes);
974 return listBucketInfo;
977 public void createDcGwLoadBalancingGroup(List<String> availableDcGws, BigInteger dpnId, String destinationIp,
978 Class<? extends TunnelTypeBase> tunnelType) {
979 Preconditions.checkNotNull(availableDcGws, "There are no dc-gws present");
980 int noOfDcGws = availableDcGws.size();
981 if (noOfDcGws == 1) {
982 LOG.trace("There are no enough DC GateWays {} present to program LB group", availableDcGws);
985 // TODO : Place the logic to construct all possible DC-GW combination here.
986 String groupIdKey = FibUtil.getGreLbGroupKey(availableDcGws);
987 Long groupId = createNextHopPointer(groupIdKey);
988 List<Bucket> listBucket = new ArrayList<>();
989 for (int index = 0; index < noOfDcGws; index++) {
990 if (isTunnelUp(availableDcGws.get(index), dpnId, tunnelType)) {
991 listBucket.add(buildBucketForDcGwLbGroup(availableDcGws.get(index), dpnId, index, tunnelType));
994 Group group = MDSALUtil.buildGroup(groupId, groupIdKey, GroupTypes.GroupSelect,
995 MDSALUtil.buildBucketLists(listBucket));
996 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION,
997 confTx -> mdsalApiManager.addGroup(confTx, dpnId, group)), LOG, "Error adding load-balancing group");
998 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(OPERATIONAL,
999 operTx -> FibUtil.updateLbGroupInfo(dpnId, destinationIp, groupIdKey, groupId.toString(), operTx)), LOG,
1000 "Error updating load-balancing group info");
1001 LOG.trace("LB group {} towards DC-GW installed on dpn {}. Group - {}", groupIdKey, dpnId, group);
1004 private boolean isTunnelUp(String dcGwIp, BigInteger dpnId, Class<? extends TunnelTypeBase> tunnelType) {
1005 String tunnelName = getTunnelRemoteNextHopPointer(dpnId, dcGwIp, tunnelType);
1006 if (tunnelName != null) {
1007 InstanceIdentifier<StateTunnelList> tunnelStateId =
1008 InstanceIdentifier.builder(TunnelsState.class).child(
1009 StateTunnelList.class, new StateTunnelListKey(tunnelName)).build();
1010 return MDSALUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, tunnelStateId)
1011 .toJavaUtil().map(StateTunnelList::getOperState)
1012 .orElse(TunnelOperStatus.Down) == TunnelOperStatus.Up;
1017 private List<Action> getEgressActions(String interfaceName, int actionKey) {
1018 List<Action> actions = Collections.emptyList();
1020 GetEgressActionsForInterfaceInputBuilder egressAction =
1021 new GetEgressActionsForInterfaceInputBuilder().setIntfName(interfaceName).setActionKey(actionKey);
1022 Future<RpcResult<GetEgressActionsForInterfaceOutput>> result =
1023 odlInterfaceRpcService.getEgressActionsForInterface(egressAction.build());
1024 RpcResult<GetEgressActionsForInterfaceOutput> rpcResult = result.get();
1025 if (!rpcResult.isSuccessful()) {
1026 LOG.warn("RPC Call to Get egress actions for interface {} returned with Errors {}",
1027 interfaceName, rpcResult.getErrors());
1029 actions = rpcResult.getResult().getAction();
1031 } catch (InterruptedException | ExecutionException e) {
1032 LOG.warn("Exception when egress actions for interface {}", interfaceName, e);
1038 * This method is invoked when the tunnel state is removed from DS.
1039 * If the there is just one DC-GW left in configuration then the LB groups can be deleted.
1040 * Otherwise, the groups are just updated.
1042 public void removeOrUpdateDcGwLoadBalancingGroup(List<String> availableDcGws, BigInteger dpnId,
1043 String destinationIp) {
1044 Preconditions.checkNotNull(availableDcGws, "There are no dc-gws present");
1045 ListenableFutures.addErrorLogging(txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, confTx -> {
1046 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(OPERATIONAL, operTx -> {
1047 int noOfDcGws = availableDcGws.size();
1048 // If availableDcGws does not contain the destination Ip it means this is a configuration delete.
1049 if (!availableDcGws.contains(destinationIp)) {
1050 availableDcGws.add(destinationIp);
1051 Collections.sort(availableDcGws);
1053 // TODO : Place the logic to construct all possible DC-GW combination here.
1054 int bucketId = availableDcGws.indexOf(destinationIp);
1055 Optional<DpnLbNexthops> dpnLbNextHops = fibUtil.getDpnLbNexthops(dpnId, destinationIp);
1056 if (!dpnLbNextHops.isPresent()) {
1059 List<String> nextHopKeys = dpnLbNextHops.get().getNexthopKey();
1060 for (String nextHopKey : nextHopKeys) {
1061 Optional<Nexthops> optionalNextHops = fibUtil.getNexthops(nextHopKey);
1062 if (!optionalNextHops.isPresent()) {
1065 Nexthops nexthops = optionalNextHops.get();
1066 final String groupId = nexthops.getGroupId();
1067 final long groupIdValue = Long.parseLong(groupId);
1068 if (noOfDcGws > 1) {
1069 mdsalApiManager.removeBucket(confTx, dpnId, groupIdValue, bucketId);
1071 LOG.trace("Removed LB group {} on dpn {}", groupIdValue, dpnId);
1072 mdsalApiManager.removeGroup(confTx, dpnId, groupIdValue);
1073 removeNextHopPointer(nextHopKey);
1075 // When the DC-GW is removed from configuration.
1076 if (noOfDcGws != availableDcGws.size()) {
1077 FibUtil.removeOrUpdateNextHopInfo(dpnId, nextHopKey, groupId, nexthops, operTx);
1080 FibUtil.removeDpnIdToNextHopInfo(destinationIp, dpnId, operTx);
1081 }), LOG, "Error removing or updating load-balancing group");
1082 }), LOG, "Error removing or updating load-balancing group");
1086 * This method is invoked when the tunnel status is updated.
1087 * The bucket is directly removed/added based on the operational status of the tunnel.
1089 public void updateDcGwLoadBalancingGroup(List<String> availableDcGws,
1090 BigInteger dpnId, String destinationIp, boolean isTunnelUp, Class<? extends TunnelTypeBase> tunnelType) {
1091 Preconditions.checkNotNull(availableDcGws, "There are no dc-gws present");
1092 ListenableFutures.addErrorLogging(txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, confTx -> {
1093 // TODO : Place the logic to construct all possible DC-GW combination here.
1094 int bucketId = availableDcGws.indexOf(destinationIp);
1095 Optional<DpnLbNexthops> dpnLbNextHops = fibUtil.getDpnLbNexthops(dpnId, destinationIp);
1096 if (!dpnLbNextHops.isPresent()) {
1099 List<String> nextHopKeys = dpnLbNextHops.get().getNexthopKey();
1100 for (String nextHopKey : nextHopKeys) {
1101 Optional<Nexthops> optionalNextHops = fibUtil.getNexthops(nextHopKey);
1102 if (!optionalNextHops.isPresent()) {
1105 Nexthops nexthops = optionalNextHops.get();
1106 final String groupId = nexthops.getGroupId();
1107 final long groupIdValue = Long.parseLong(groupId);
1109 Bucket bucket = buildBucketForDcGwLbGroup(destinationIp, dpnId, bucketId, tunnelType);
1110 LOG.trace("Added bucket {} to group {} on dpn {}.", bucket, groupId, dpnId);
1111 mdsalApiManager.addBucket(confTx, dpnId, groupIdValue, bucket);
1113 LOG.trace("Removed bucketId {} from group {} on dpn {}.", bucketId, groupId, dpnId);
1114 mdsalApiManager.removeBucket(confTx, dpnId, groupIdValue, bucketId);
1117 }), LOG, "Error updating load-balancing group");
1120 private Bucket buildBucketForDcGwLbGroup(String ipAddress, BigInteger dpnId, int index,
1121 Class<? extends TunnelTypeBase> tunnelType) {
1122 List<Action> listAction = new ArrayList<>();
1123 // ActionKey 0 goes to mpls label.
1125 listAction.add(new ActionPushMpls().buildAction());
1126 listAction.add(new ActionRegMove(actionKey++, FibConstants.NXM_REG_MAPPING
1127 .get(index), 0, 19).buildAction());
1128 String tunnelInterfaceName = getTunnelInterfaceName(dpnId, IpAddressBuilder.getDefaultInstance(ipAddress),
1130 List<Action> egressActions = getEgressActions(tunnelInterfaceName, actionKey++);
1131 if (!egressActions.isEmpty()) {
1132 listAction.addAll(getEgressActions(tunnelInterfaceName, actionKey++));
1134 // clear off actions if there is no egress actions.
1135 listAction = Collections.emptyList();
1137 //OVS expects a non-zero weight value for load balancing to happen in select groups
1138 return MDSALUtil.buildBucket(listAction, SELECT_GROUP_WEIGHT, index,
1139 MDSALUtil.WATCH_PORT, MDSALUtil.WATCH_GROUP);
1142 public void programDcGwLoadBalancingGroup(List<String> availableDcGws, BigInteger dpnId, String destinationIp,
1143 int addRemoveOrUpdate, boolean isTunnelUp,
1144 Class<? extends TunnelTypeBase> tunnelType) {
1145 if (NwConstants.ADD_FLOW == addRemoveOrUpdate) {
1146 createDcGwLoadBalancingGroup(availableDcGws, dpnId, destinationIp, tunnelType);
1147 } else if (NwConstants.DEL_FLOW == addRemoveOrUpdate) {
1148 removeOrUpdateDcGwLoadBalancingGroup(availableDcGws, dpnId, destinationIp);
1149 } else if (NwConstants.MOD_FLOW == addRemoveOrUpdate) {
1150 updateDcGwLoadBalancingGroup(availableDcGws, dpnId, destinationIp, isTunnelUp, tunnelType);