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.mdsalutil.NWUtil.isIpv4Address;
12 import com.google.common.base.Optional;
13 import com.google.common.base.Preconditions;
14 import java.math.BigInteger;
15 import java.util.ArrayList;
16 import java.util.Collections;
17 import java.util.HashMap;
18 import java.util.List;
19 import java.util.Locale;
21 import java.util.concurrent.CopyOnWriteArrayList;
22 import java.util.concurrent.ExecutionException;
23 import java.util.concurrent.Future;
24 import javax.annotation.PreDestroy;
25 import javax.inject.Inject;
26 import javax.inject.Singleton;
27 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
28 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
29 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
30 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
31 import org.opendaylight.genius.interfacemanager.interfaces.IInterfaceManager;
32 import org.opendaylight.genius.itm.globals.ITMConstants;
33 import org.opendaylight.genius.mdsalutil.ActionInfo;
34 import org.opendaylight.genius.mdsalutil.BucketInfo;
35 import org.opendaylight.genius.mdsalutil.GroupEntity;
36 import org.opendaylight.genius.mdsalutil.MDSALUtil;
37 import org.opendaylight.genius.mdsalutil.NwConstants;
38 import org.opendaylight.genius.mdsalutil.actions.ActionGroup;
39 import org.opendaylight.genius.mdsalutil.actions.ActionNxResubmit;
40 import org.opendaylight.genius.mdsalutil.actions.ActionOutput;
41 import org.opendaylight.genius.mdsalutil.actions.ActionPushMpls;
42 import org.opendaylight.genius.mdsalutil.actions.ActionPushVlan;
43 import org.opendaylight.genius.mdsalutil.actions.ActionRegLoad;
44 import org.opendaylight.genius.mdsalutil.actions.ActionRegMove;
45 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldEthernetDestination;
46 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldEthernetSource;
47 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldTunnelId;
48 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldVlanVid;
49 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
50 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
51 import org.opendaylight.infrautils.utils.concurrent.ListenableFutures;
52 import org.opendaylight.netvirt.elanmanager.api.IElanService;
53 import org.opendaylight.netvirt.fibmanager.api.L3VPNTransportTypes;
54 import org.opendaylight.netvirt.vpnmanager.api.VpnExtraRouteHelper;
55 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana._if.type.rev140508.L2vlan;
56 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana._if.type.rev140508.Tunnel;
57 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
58 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.InterfaceType;
59 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface;
60 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface.OperStatus;
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.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.OutputActionCase;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.PushVlanActionCase;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetFieldCase;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.AllocateIdInput;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.AllocateIdInputBuilder;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.AllocateIdOutput;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.CreateIdPoolInput;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.CreateIdPoolInputBuilder;
72 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.CreateIdPoolOutput;
73 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
74 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.ReleaseIdInput;
75 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.ReleaseIdInputBuilder;
76 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.ReleaseIdOutput;
77 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeBase;
78 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeGre;
79 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeMplsOverGre;
80 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeVxlan;
81 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetEgressActionsForInterfaceInputBuilder;
82 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetEgressActionsForInterfaceOutput;
83 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.OdlInterfaceRpcService;
84 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.TunnelOperStatus;
85 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.TunnelsState;
86 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.tunnels_state.StateTunnelList;
87 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.tunnels_state.StateTunnelListKey;
88 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetEgressActionsForTunnelInputBuilder;
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.netvirt.neutronvpn.rev150602.subnetmaps.Subnetmap;
127 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg6;
128 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.add.group.input.buckets.bucket.action.action.NxActionResubmitRpcAddGroupCase;
129 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;
130 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.nx.action.reg.load.grouping.NxRegLoad;
131 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
132 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.InstanceIdentifierBuilder;
133 import org.opendaylight.yangtools.yang.common.RpcResult;
134 import org.slf4j.Logger;
135 import org.slf4j.LoggerFactory;
138 public class NexthopManager implements AutoCloseable {
139 private static final Logger LOG = LoggerFactory.getLogger(NexthopManager.class);
140 private static final String NEXTHOP_ID_POOL_NAME = "nextHopPointerPool";
141 private static final long WAIT_TIME_FOR_SYNC_INSTALL = Long.getLong("wait.time.sync.install", 300L);
142 private static final long WAIT_TIME_TO_ACQUIRE_LOCK = 3000L;
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) {
252 List<Action> actions;
254 if (isTunnelInterface && interfaceManager.isItmDirectTunnelsEnabled()) {
255 rpcResult = itmManager.getEgressActionsForTunnel(
256 new GetEgressActionsForTunnelInputBuilder().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 GetEgressActionsForInterfaceOutput output =
263 (GetEgressActionsForInterfaceOutput)rpcResult.getResult();
264 actions = output.getAction();
267 rpcResult = odlInterfaceRpcService.getEgressActionsForInterface(
268 new GetEgressActionsForInterfaceInputBuilder().setIntfName(ifName).build()).get();
269 if (!rpcResult.isSuccessful()) {
270 LOG.error("RPC Call to Get egress vm actions for interface {} returned with Errors {}",
271 ifName, rpcResult.getErrors());
272 return Collections.emptyList();
274 GetEgressActionsForInterfaceOutput output =
275 (GetEgressActionsForInterfaceOutput)rpcResult.getResult();
276 actions = output.getAction();
279 List<ActionInfo> listActionInfo = new ArrayList<>();
280 for (Action action : actions) {
281 actionKey = action.getKey().getOrder() + actionKey;
282 org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action
283 actionClass = action.getAction();
284 if (actionClass instanceof OutputActionCase) {
285 listActionInfo.add(new ActionOutput(actionKey,
286 ((OutputActionCase) actionClass).getOutputAction().getOutputNodeConnector()));
287 } else if (actionClass instanceof PushVlanActionCase) {
288 listActionInfo.add(new ActionPushVlan(actionKey));
289 } else if (actionClass instanceof SetFieldCase) {
290 if (((SetFieldCase) actionClass).getSetField().getVlanMatch() != null) {
291 int vlanVid = ((SetFieldCase) actionClass).getSetField().getVlanMatch()
292 .getVlanId().getVlanId().getValue();
293 listActionInfo.add(new ActionSetFieldVlanVid(actionKey, vlanVid));
295 } else if (actionClass instanceof NxActionResubmitRpcAddGroupCase) {
296 Short tableId = ((NxActionResubmitRpcAddGroupCase) actionClass).getNxResubmit().getTable();
297 listActionInfo.add(new ActionNxResubmit(actionKey, tableId));
298 } else if (actionClass instanceof NxActionRegLoadNodesNodeTableFlowApplyActionsCase) {
299 NxRegLoad nxRegLoad =
300 ((NxActionRegLoadNodesNodeTableFlowApplyActionsCase) actionClass).getNxRegLoad();
301 listActionInfo.add(new ActionRegLoad(actionKey, NxmNxReg6.class,
302 nxRegLoad.getDst().getStart(), nxRegLoad.getDst().getEnd(),
303 nxRegLoad.getValue().longValue()));
306 return listActionInfo;
307 } catch (InterruptedException | ExecutionException e) {
308 LOG.warn("Exception when egress actions for interface {}", ifName, e);
310 LOG.warn("Exception when egress actions for interface {}", ifName);
311 return Collections.emptyList();
314 protected String getTunnelInterfaceName(BigInteger srcDpId, BigInteger dstDpId) {
315 Class<? extends TunnelTypeBase> tunType = getReqTunType(getReqTransType().toUpperCase(Locale.getDefault()));
316 Future<RpcResult<GetTunnelInterfaceNameOutput>> result;
318 result = itmManager.getTunnelInterfaceName(new GetTunnelInterfaceNameInputBuilder()
319 .setSourceDpid(srcDpId)
320 .setDestinationDpid(dstDpId)
321 .setTunnelType(tunType)
323 RpcResult<GetTunnelInterfaceNameOutput> rpcResult = result.get();
324 if (!rpcResult.isSuccessful()) {
325 LOG.warn("RPC Call to getTunnelInterfaceId returned with Errors {}", rpcResult.getErrors());
327 return rpcResult.getResult().getInterfaceName();
329 } catch (InterruptedException | ExecutionException e) {
330 LOG.warn("Exception when getting tunnel interface Id for tunnel between {} and {}", srcDpId, dstDpId, e);
335 protected String getTunnelInterfaceName(BigInteger srcDpId, org.opendaylight.yang.gen.v1.urn.ietf.params
336 .xml.ns.yang.ietf.inet.types.rev130715.IpAddress dstIp) {
337 Class<? extends TunnelTypeBase> tunType = getReqTunType(getReqTransType().toUpperCase(Locale.getDefault()));
338 Future<RpcResult<GetInternalOrExternalInterfaceNameOutput>> result;
340 result = itmManager.getInternalOrExternalInterfaceName(new GetInternalOrExternalInterfaceNameInputBuilder()
341 .setSourceDpid(srcDpId)
342 .setDestinationIp(dstIp)
343 .setTunnelType(tunType)
345 RpcResult<GetInternalOrExternalInterfaceNameOutput> rpcResult = result.get();
346 if (!rpcResult.isSuccessful()) {
347 LOG.warn("RPC Call to getTunnelInterfaceName returned with Errors {}", rpcResult.getErrors());
349 return rpcResult.getResult().getInterfaceName();
351 } catch (InterruptedException | ExecutionException e) {
352 LOG.warn("Exception when getting tunnel interface Id for tunnel between {} and {}", srcDpId, dstIp, e);
358 public long getLocalNextHopGroup(long vpnId,
359 String ipNextHopAddress) {
360 long groupId = createNextHopPointer(getNextHopKey(vpnId, ipNextHopAddress));
361 if (groupId == FibConstants.INVALID_GROUP_ID) {
362 LOG.error("Unable to allocate groupId for vpnId {} , prefix {}", vpnId, ipNextHopAddress);
367 public long createLocalNextHop(long vpnId, BigInteger dpnId, String ifName,
368 String primaryIpAddress, String currDestIpPrefix,
369 String gwMacAddress, String jobKey) {
370 String vpnName = fibUtil.getVpnNameFromId(vpnId);
371 if (vpnName == null) {
374 String macAddress = fibUtil.getMacAddressFromPrefix(ifName, vpnName, primaryIpAddress);
376 long groupId = createNextHopPointer(getNextHopKey(vpnId, primaryIpAddress));
378 LOG.error("Unable to allocate groupId for vpnId {} , IntfName {}, primaryIpAddress {} curIpPrefix {}",
379 vpnId, ifName, primaryIpAddress, currDestIpPrefix);
382 String nextHopLockStr = vpnId + primaryIpAddress;
383 jobCoordinator.enqueueJob(jobKey, () -> {
385 if (FibUtil.lockCluster(lockManager, nextHopLockStr, WAIT_TIME_TO_ACQUIRE_LOCK)) {
386 VpnNexthop nexthop = getVpnNexthop(vpnId, primaryIpAddress);
387 LOG.trace("nexthop: {} retrieved for vpnId {}, prefix {}, ifName {} on dpn {}", nexthop, vpnId,
388 primaryIpAddress, ifName, dpnId);
389 if (nexthop == null) {
390 String encMacAddress = macAddress == null
391 ? fibUtil.getMacAddressFromPrefix(ifName, vpnName, primaryIpAddress) : macAddress;
392 List<BucketInfo> listBucketInfo = new ArrayList<>();
393 List<ActionInfo> listActionInfo = new ArrayList<>();
396 if (encMacAddress != null) {
397 if (gwMacAddress != null) {
398 LOG.trace("The Local NextHop Group Source Mac {} for VpnInterface {} on VPN {}",
399 gwMacAddress, ifName, vpnId);
400 listActionInfo.add(new ActionSetFieldEthernetSource(actionKey++,
401 new MacAddress(gwMacAddress)));
403 listActionInfo.add(new ActionSetFieldEthernetDestination(actionKey++,
404 new MacAddress(encMacAddress)));
405 // listActionInfo.add(0, new ActionPopMpls());
407 // FIXME: Log message here.
408 LOG.debug("mac address for new local nexthop is null");
410 listActionInfo.addAll(getEgressActionsForInterface(ifName, actionKey, false));
411 BucketInfo bucket = new BucketInfo(listActionInfo);
413 listBucketInfo.add(bucket);
414 GroupEntity groupEntity = MDSALUtil.buildGroupEntity(dpnId, groupId, primaryIpAddress,
415 GroupTypes.GroupAll, listBucketInfo);
416 LOG.trace("Install LNH Group: id {}, mac address {}, interface {} for prefix {}", groupId,
417 encMacAddress, ifName, primaryIpAddress);
418 //Try to install group directly on the DPN bypassing the FRM, in order to avoid waiting for the
419 // group to get installed before programming the flows
420 installGroupOnDpn(groupId, dpnId, primaryIpAddress, listBucketInfo,
421 getNextHopKey(vpnId, primaryIpAddress), GroupTypes.GroupAll);
423 mdsalApiManager.syncInstallGroup(groupEntity);
425 addVpnNexthopToDS(dpnId, vpnId, primaryIpAddress, currDestIpPrefix, groupId);
428 // Ignore adding new prefix , if it already exists
429 List<IpAdjacencies> prefixesList = nexthop.getIpAdjacencies();
430 IpAdjacencies prefix = new IpAdjacenciesBuilder().setIpAdjacency(currDestIpPrefix).build();
431 if (prefixesList != null && prefixesList.contains(prefix)) {
432 LOG.trace("Prefix {} is already present in l3nextHop {} ", currDestIpPrefix, nexthop);
434 IpAdjacenciesBuilder ipPrefixesBuilder =
435 new IpAdjacenciesBuilder().setKey(new IpAdjacenciesKey(currDestIpPrefix));
436 LOG.trace("Updating prefix {} to vpnNextHop {} Operational DS", currDestIpPrefix, nexthop);
437 MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL,
438 getVpnNextHopIpPrefixIdentifier(vpnId, primaryIpAddress, currDestIpPrefix),
439 ipPrefixesBuilder.build());
444 FibUtil.unlockCluster(lockManager, nextHopLockStr);
446 return Collections.emptyList();
451 private void installGroupOnDpn(long groupId, BigInteger dpnId, String groupName, List<BucketInfo> bucketsInfo,
452 String nextHopKey, GroupTypes groupType) {
453 NodeRef nodeRef = FibUtil.buildNodeRef(dpnId);
454 Buckets buckets = FibUtil.buildBuckets(bucketsInfo);
455 GroupRef groupRef = new GroupRef(FibUtil.buildGroupInstanceIdentifier(groupId, dpnId));
456 AddGroupInput input = new AddGroupInputBuilder().setNode(nodeRef).setGroupId(new GroupId(groupId))
457 .setBuckets(buckets).setGroupRef(groupRef).setGroupType(groupType)
458 .setGroupName(groupName).build();
459 Future<RpcResult<AddGroupOutput>> groupStats = salGroupService.addGroup(input);
460 RpcResult<AddGroupOutput> rpcResult = null;
462 rpcResult = groupStats.get();
463 if (rpcResult != null && rpcResult.isSuccessful()) {
464 LOG.info("Group {} with key {} has been successfully installed directly on dpn {}.", groupId,
467 LOG.error("Unable to install group {} with key {} directly on dpn {} due to {}.", groupId, nextHopKey,
468 dpnId, rpcResult != null ? rpcResult.getErrors() : null);
470 } catch (InterruptedException | ExecutionException e) {
471 LOG.error("Error while installing group {} directly on dpn {}", groupId, dpnId);
475 protected void addVpnNexthopToDS(BigInteger dpnId, long vpnId, String primaryIpAddr,
476 String currIpAddr, long egressPointer) {
477 InstanceIdentifierBuilder<VpnNexthops> idBuilder = InstanceIdentifier.builder(L3nexthop.class)
478 .child(VpnNexthops.class, new VpnNexthopsKey(vpnId));
480 List<IpAdjacencies> ipPrefixesList = new ArrayList<>();
481 IpAdjacencies prefix = new IpAdjacenciesBuilder().setIpAdjacency(currIpAddr).build();
482 ipPrefixesList.add(prefix);
483 // Add nexthop to vpn node
484 VpnNexthop nh = new VpnNexthopBuilder()
485 .setKey(new VpnNexthopKey(primaryIpAddr))
487 .setIpAdjacencies(ipPrefixesList)
488 .setEgressPointer(egressPointer).build();
490 InstanceIdentifier<VpnNexthop> id1 = idBuilder
491 .child(VpnNexthop.class, new VpnNexthopKey(primaryIpAddr)).build();
492 LOG.trace("Adding vpnnextHop {} to Operational DS", nh);
493 MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL, id1, nh);
497 protected InstanceIdentifier<IpAdjacencies> getVpnNextHopIpPrefixIdentifier(long vpnId, String primaryIpAddress,
499 InstanceIdentifier<IpAdjacencies> id = InstanceIdentifier.builder(L3nexthop.class)
500 .child(VpnNexthops.class, new VpnNexthopsKey(vpnId))
501 .child(VpnNexthop.class, new VpnNexthopKey(primaryIpAddress))
502 .child(IpAdjacencies.class, new IpAdjacenciesKey(ipPrefix)).build();
506 protected VpnNexthop getVpnNexthop(long vpnId, String ipAddress) {
508 // check if vpn node is there
509 InstanceIdentifierBuilder<VpnNexthops> idBuilder =
510 InstanceIdentifier.builder(L3nexthop.class).child(VpnNexthops.class,
511 new VpnNexthopsKey(vpnId));
512 InstanceIdentifier<VpnNexthops> id = idBuilder.build();
513 Optional<VpnNexthops> vpnNexthops = MDSALUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, id);
514 if (vpnNexthops.isPresent()) {
515 // get nexthops list for vpn
516 List<VpnNexthop> nexthops = vpnNexthops.get().getVpnNexthop();
517 for (VpnNexthop nexthop : nexthops) {
518 if (nexthop.getIpAddress().equals(ipAddress)) {
520 LOG.trace("VpnNextHop : {}", nexthop);
524 // return null if not found
529 public AdjacencyResult getRemoteNextHopPointer(BigInteger remoteDpnId, long vpnId, String prefixIp,
531 String egressIfName = null;
532 LOG.trace("getRemoteNextHopPointer: input [remoteDpnId {}, vpnId {}, prefixIp {}, nextHopIp {} ]", remoteDpnId,
533 vpnId, prefixIp, nextHopIp);
535 Class<? extends InterfaceType> egressIfType;
536 ElanInstance elanInstance = getElanInstanceForPrefix(vpnId, prefixIp);
537 if (elanInstance != null) {
538 egressIfType = getInterfaceType(elanInstance);
540 LOG.warn("Failed to determine network type for prefixIp {} using tunnel", prefixIp);
541 egressIfType = Tunnel.class;
544 if (Tunnel.class.equals(egressIfType)) {
545 egressIfName = getTunnelRemoteNextHopPointer(remoteDpnId, nextHopIp);
547 egressIfName = getExtPortRemoteNextHopPointer(remoteDpnId, elanInstance);
550 LOG.trace("NextHop pointer for prefixIp {} vpnId {} dpnId {} is {}", prefixIp, vpnId, remoteDpnId,
552 return egressIfName != null ? new AdjacencyResult(egressIfName, egressIfType, nextHopIp,
556 public BigInteger getDpnForPrefix(long vpnId, String prefixIp) {
557 VpnNexthop vpnNexthop = getVpnNexthop(vpnId, prefixIp);
558 BigInteger localDpnId = vpnNexthop == null ? null : vpnNexthop.getDpnId();
562 private void removeVpnNexthopFromDS(long vpnId, String ipPrefix) {
564 InstanceIdentifierBuilder<VpnNexthop> idBuilder = InstanceIdentifier.builder(L3nexthop.class)
565 .child(VpnNexthops.class, new VpnNexthopsKey(vpnId))
566 .child(VpnNexthop.class, new VpnNexthopKey(ipPrefix));
567 InstanceIdentifier<VpnNexthop> id = idBuilder.build();
569 LOG.trace("Removing vpn next hop from datastore : {}", id);
570 MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.OPERATIONAL, id);
573 public void removeLocalNextHop(BigInteger dpnId, Long vpnId, String primaryIpAddress, String currDestIpPrefix) {
574 String nextHopLockStr = vpnId + primaryIpAddress;
576 if (FibUtil.lockCluster(lockManager, nextHopLockStr, WAIT_TIME_TO_ACQUIRE_LOCK)) {
577 VpnNexthop nh = getVpnNexthop(vpnId, primaryIpAddress);
579 List<IpAdjacencies> prefixesList = nh.getIpAdjacencies();
580 IpAdjacencies prefix = new IpAdjacenciesBuilder().setIpAdjacency(currDestIpPrefix).build();
581 prefixesList.remove(prefix);
582 if (prefixesList.isEmpty()) { //remove the group only if there are no more flows using this group
583 GroupEntity groupEntity = MDSALUtil.buildGroupEntity(dpnId, nh.getEgressPointer(),
584 primaryIpAddress, GroupTypes.GroupAll, Collections.EMPTY_LIST);
586 mdsalApiManager.removeGroup(groupEntity);
588 removeVpnNexthopFromDS(vpnId, primaryIpAddress);
590 removeNextHopPointer(getNextHopKey(vpnId, primaryIpAddress));
591 LOG.debug("Local Next hop {} for {} {} on dpn {} successfully deleted",
592 nh.getEgressPointer(), vpnId, primaryIpAddress, dpnId);
594 //remove the currIpPrefx from IpPrefixList of the vpnNexthop
595 LOG.trace("Removing the prefix {} from vpnNextHop {} Operational DS", currDestIpPrefix, nh);
596 MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.OPERATIONAL,
597 getVpnNextHopIpPrefixIdentifier(vpnId, primaryIpAddress, currDestIpPrefix));
601 LOG.error("Local NextHop for VpnId {} curIpPrefix {} on dpn {} primaryIpAddress {} not deleted",
602 vpnId, currDestIpPrefix, dpnId, primaryIpAddress);
606 FibUtil.unlockCluster(lockManager, nextHopLockStr);
610 public void setConfTransType(String service, String transportType) {
612 if (!service.equalsIgnoreCase("L3VPN")) {
613 LOG.error("Incorrect service {} provided for setting the transport type.", service);
617 L3VPNTransportTypes transType = L3VPNTransportTypes.validateTransportType(transportType
618 .toUpperCase(Locale.getDefault()));
620 if (transType != L3VPNTransportTypes.Invalid) {
621 configuredTransportTypeL3VPN = transType;
625 public void writeConfTransTypeConfigDS() {
626 MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, getConfTransportTypeIdentifier(),
627 createConfTransportType(configuredTransportTypeL3VPN.getTransportType()));
630 public L3VPNTransportTypes getConfiguredTransportTypeL3VPN() {
631 return this.configuredTransportTypeL3VPN;
634 public String getReqTransType() {
635 if (configuredTransportTypeL3VPN == L3VPNTransportTypes.Invalid) {
637 * Restart scenario, Read from the ConfigDS.
638 * if the value is Unset, cache value as VxLAN.
640 LOG.trace("configureTransportType is not yet set.");
641 Optional<ConfTransportTypeL3vpn> configuredTransTypeFromConfig =
642 MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, getConfTransportTypeIdentifier());
644 if (configuredTransTypeFromConfig.isPresent()) {
645 if (configuredTransTypeFromConfig.get().getTransportType().equals(TunnelTypeGre.class)) {
646 configuredTransportTypeL3VPN = L3VPNTransportTypes.GRE;
648 configuredTransportTypeL3VPN = L3VPNTransportTypes.VxLAN;
650 LOG.trace("configuredTransportType set from config DS to {}",
651 getConfiguredTransportTypeL3VPN().getTransportType());
653 setConfTransType("L3VPN", L3VPNTransportTypes.VxLAN.getTransportType());
654 LOG.trace("configuredTransportType is not set in the Config DS. VxLAN as default will be used.");
657 LOG.trace("configuredTransportType is set as {}", getConfiguredTransportTypeL3VPN().getTransportType());
659 return getConfiguredTransportTypeL3VPN().getTransportType();
662 public InstanceIdentifier<ConfTransportTypeL3vpn> getConfTransportTypeIdentifier() {
663 return InstanceIdentifier.builder(ConfTransportTypeL3vpn.class).build();
666 private ConfTransportTypeL3vpn createConfTransportType(String type) {
667 ConfTransportTypeL3vpn confTransType;
669 case ITMConstants.TUNNEL_TYPE_GRE:
670 confTransType = new ConfTransportTypeL3vpnBuilder().setTransportType(TunnelTypeGre.class).build();
671 LOG.trace("Setting the confTransportType to GRE.");
673 case ITMConstants.TUNNEL_TYPE_VXLAN:
674 confTransType = new ConfTransportTypeL3vpnBuilder().setTransportType(TunnelTypeVxlan.class).build();
675 LOG.trace("Setting the confTransportType to VxLAN.");
678 LOG.trace("Invalid transport type {} passed to Config DS ", type);
679 confTransType = null;
682 return confTransType;
685 public Class<? extends TunnelTypeBase> getReqTunType(String transportType) {
686 switch (transportType) {
688 return TunnelTypeVxlan.class;
690 return TunnelTypeGre.class;
692 return TunnelTypeMplsOverGre.class;
696 public String getTransportTypeStr(String tunType) {
697 if (tunType.equals(TunnelTypeVxlan.class.toString())) {
698 return ITMConstants.TUNNEL_TYPE_VXLAN;
699 } else if (tunType.equals(TunnelTypeGre.class.toString())) {
700 return ITMConstants.TUNNEL_TYPE_GRE;
701 } else if (tunType.equals(TunnelTypeMplsOverGre.class.toString())) {
702 return ITMConstants.TUNNEL_TYPE_MPLSoGRE;
704 return ITMConstants.TUNNEL_TYPE_INVALID;
710 public void close() {
711 LOG.info("{} close", getClass().getSimpleName());
714 // TODO Clean up the exception handling
715 @SuppressWarnings("checkstyle:IllegalCatch")
716 private String getTunnelRemoteNextHopPointer(BigInteger remoteDpnId, String nextHopIp) {
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));
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 Uuid subnetId = prefix.getSubnetId();
757 if (subnetId != null) {
758 Subnetmap subnetMap = fibUtil.getSubnetMap(subnetId);
759 if (subnetMap != null && subnetMap.getNetworkId() != null) {
760 elanInstance = elanService.getElanInstance(subnetMap.getNetworkId().getValue());
768 static class AdjacencyResult {
769 private final String interfaceName;
770 private final Class<? extends InterfaceType> interfaceType;
771 private final String nextHopIp;
772 private final String prefix;
774 AdjacencyResult(String interfaceName, Class<? extends InterfaceType> interfaceType, String nextHopIp,
776 this.interfaceName = interfaceName;
777 this.interfaceType = interfaceType;
778 this.nextHopIp = nextHopIp;
779 this.prefix = prefix;
782 public String getInterfaceName() {
783 return interfaceName;
786 public Class<? extends InterfaceType> getInterfaceType() {
787 return interfaceType;
790 public String getNextHopIp() {
794 public String getPrefix() {
799 public int hashCode() {
800 final int prime = 31;
802 result = prime * result + (interfaceName == null ? 0 : interfaceName.hashCode());
807 public boolean equals(Object obj) {
812 boolean result = false;
813 if (getClass() != obj.getClass()) {
816 AdjacencyResult other = (AdjacencyResult) obj;
817 result = interfaceName.equals(other.interfaceName);
823 protected long setupLoadBalancingNextHop(Long parentVpnId, BigInteger dpnId,
824 String destPrefix, List<BucketInfo> listBucketInfo, boolean addOrRemove) {
825 long groupId = createNextHopPointer(getNextHopKey(parentVpnId, destPrefix));
826 if (groupId == FibConstants.INVALID_GROUP_ID) {
827 LOG.error("Unable to allocate/retrieve groupId for vpnId {} , prefix {}", parentVpnId, destPrefix);
830 GroupEntity groupEntity = MDSALUtil.buildGroupEntity(
831 dpnId, groupId, destPrefix, GroupTypes.GroupSelect, listBucketInfo);
833 mdsalApiManager.syncInstallGroup(groupEntity);
835 Thread.sleep(WAIT_TIME_FOR_SYNC_INSTALL);
836 } catch (InterruptedException e1) {
837 LOG.warn("Thread got interrupted while programming LB group {}", groupEntity);
838 Thread.currentThread().interrupt();
841 mdsalApiManager.removeGroup(groupEntity);
846 long createNextHopGroups(Long vpnId, String rd, BigInteger dpnId, VrfEntry vrfEntry,
847 Routes routes, List<Routes> vpnExtraRoutes) {
848 List<BucketInfo> listBucketInfo = new ArrayList<>();
849 List<Routes> clonedVpnExtraRoutes = new ArrayList<>(vpnExtraRoutes);
850 if (clonedVpnExtraRoutes.contains(routes)) {
851 listBucketInfo.addAll(getBucketsForLocalNexthop(vpnId, dpnId, vrfEntry, routes));
852 clonedVpnExtraRoutes.remove(routes);
854 listBucketInfo.addAll(getBucketsForRemoteNexthop(vpnId, dpnId, vrfEntry, rd, clonedVpnExtraRoutes));
855 return setupLoadBalancingNextHop(vpnId, dpnId, vrfEntry.getDestPrefix(), listBucketInfo, true);
858 private List<BucketInfo> getBucketsForLocalNexthop(Long vpnId, BigInteger dpnId,
859 VrfEntry vrfEntry, Routes routes) {
860 List<BucketInfo> listBucketInfo = new CopyOnWriteArrayList<>();
861 routes.getNexthopIpList().parallelStream().forEach(nextHopIp -> {
862 String localNextHopIP;
863 if (isIpv4Address(nextHopIp)) {
864 localNextHopIP = nextHopIp + NwConstants.IPV4PREFIX;
866 localNextHopIP = nextHopIp + NwConstants.IPV6PREFIX;
868 Prefixes localNextHopInfo = fibUtil.getPrefixToInterface(vpnId, localNextHopIP);
869 if (localNextHopInfo != null) {
870 long groupId = getLocalNextHopGroup(vpnId, localNextHopIP);
871 if (groupId == FibConstants.INVALID_GROUP_ID) {
872 LOG.error("Unable to allocate groupId for vpnId {} , prefix {} , interface {}", vpnId,
873 vrfEntry.getDestPrefix(), localNextHopInfo.getVpnInterfaceName());
876 List<ActionInfo> actionsInfos =
877 Collections.singletonList(new ActionGroup(groupId));
878 BucketInfo bucket = new BucketInfo(actionsInfos);
880 listBucketInfo.add(bucket);
883 LOG.trace("LOCAL: listbucket {}, vpnId {}, dpnId {}, routes {}", listBucketInfo, vpnId, dpnId, routes);
884 return listBucketInfo;
887 private List<BucketInfo> getBucketsForRemoteNexthop(Long vpnId, BigInteger dpnId, VrfEntry vrfEntry, String rd,
888 List<Routes> vpnExtraRoutes) {
889 List<BucketInfo> listBucketInfo = new ArrayList<>();
890 Map<String, List<ActionInfo>> egressActionMap = new HashMap<>();
891 vpnExtraRoutes.forEach(vpnExtraRoute -> vpnExtraRoute.getNexthopIpList().forEach(nextHopIp -> {
892 String nextHopPrefixIp;
893 if (isIpv4Address(nextHopIp)) {
894 nextHopPrefixIp = nextHopIp + NwConstants.IPV4PREFIX;
896 nextHopPrefixIp = nextHopIp + NwConstants.IPV6PREFIX;
898 List<String> tepIpAddresses = fibUtil.getNextHopAddresses(rd, nextHopPrefixIp);
899 if (tepIpAddresses.isEmpty()) {
902 // There would be only one nexthop address for a VM ip which would be the tep Ip
903 String tepIp = tepIpAddresses.get(0);
904 AdjacencyResult adjacencyResult = getRemoteNextHopPointer(dpnId, vpnId,
905 vrfEntry.getDestPrefix(), tepIp);
906 if (adjacencyResult == null) {
909 String egressInterface = adjacencyResult.getInterfaceName();
910 if (!FibUtil.isTunnelInterface(adjacencyResult)) {
913 Class<? extends TunnelTypeBase> tunnelType = VpnExtraRouteHelper.getTunnelType(itmManager, egressInterface);
914 Interface ifState = fibUtil.getInterfaceStateFromOperDS(egressInterface);
915 if (ifState == null || ifState.getOperStatus() != OperStatus.Up) {
916 LOG.trace("Tunnel not up {}", egressInterface);
919 if (!TunnelTypeVxlan.class.equals(tunnelType)) {
922 Long label = FibUtil.getLabelFromRoutePaths(vrfEntry).get();
923 Prefixes prefixInfo = fibUtil.getPrefixToInterface(vpnId, nextHopPrefixIp);
924 if (prefixInfo == null) {
925 LOG.error("No prefix info found for prefix {} in rd {} for VPN {}", nextHopPrefixIp, rd,
930 if (fibUtil.enforceVxlanDatapathSemanticsforInternalRouterVpn(prefixInfo.getSubnetId(), vpnId,
932 java.util.Optional<Long> optionalVni = fibUtil.getVniForVxlanNetwork(prefixInfo.getSubnetId());
933 if (!optionalVni.isPresent()) {
934 LOG.error("VNI not found for nexthop {} vrfEntry {} with subnetId {}", nextHopIp,
935 vrfEntry, prefixInfo.getSubnetId());
938 tunnelId = BigInteger.valueOf(optionalVni.get());
940 tunnelId = BigInteger.valueOf(label);
942 List<ActionInfo> actionInfos = new ArrayList<>();
943 actionInfos.add(new ActionSetFieldTunnelId(tunnelId));
944 String ifName = prefixInfo.getVpnInterfaceName();
945 String vpnName = fibUtil.getVpnNameFromId(vpnId);
946 if (vpnName == null) {
949 String macAddress = fibUtil.getMacAddressFromPrefix(ifName, vpnName, nextHopPrefixIp);
950 actionInfos.add(new ActionSetFieldEthernetDestination(actionInfos.size(),
951 new MacAddress(macAddress)));
952 List<ActionInfo> egressActions;
953 if (egressActionMap.containsKey(egressInterface)) {
954 egressActions = egressActionMap.get(egressInterface);
956 egressActions = getEgressActionsForInterface(egressInterface, actionInfos.size(), true);
957 egressActionMap.put(egressInterface, egressActions);
959 if (egressActions.isEmpty()) {
960 LOG.error("Failed to retrieve egress action for prefix {} route-paths {}"
961 + " interface {}." + " Aborting remote FIB entry creation.",
962 vrfEntry.getDestPrefix(), vrfEntry.getRoutePaths(), egressInterface);
964 actionInfos.addAll(egressActions);
965 BucketInfo bucket = new BucketInfo(actionInfos);
967 listBucketInfo.add(bucket);
969 LOG.trace("LOCAL: listbucket {}, rd {}, dpnId {}, routes {}", listBucketInfo, rd, dpnId, vpnExtraRoutes);
970 return listBucketInfo;
973 public void createDcGwLoadBalancingGroup(List<String> availableDcGws, BigInteger dpnId, String destinationIp) {
974 Preconditions.checkNotNull(availableDcGws, "There are no dc-gws present");
975 int noOfDcGws = availableDcGws.size();
976 if (noOfDcGws == 1) {
977 LOG.trace("There are no enough DC GateWays {} present to program LB group", availableDcGws);
980 // TODO : Place the logic to construct all possible DC-GW combination here.
981 String groupIdKey = FibUtil.getGreLbGroupKey(availableDcGws);
982 Long groupId = createNextHopPointer(groupIdKey);
983 List<Bucket> listBucket = new ArrayList<>();
984 for (int index = 0; index < noOfDcGws; index++) {
985 if (isTunnelUp(availableDcGws.get(index), dpnId)) {
986 listBucket.add(buildBucketForDcGwLbGroup(availableDcGws.get(index), dpnId, index));
989 Group group = MDSALUtil.buildGroup(groupId, groupIdKey, GroupTypes.GroupSelect,
990 MDSALUtil.buildBucketLists(listBucket));
991 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(confTx -> {
992 mdsalApiManager.addGroupToTx(dpnId, group, confTx);
993 }), LOG, "Error adding load-balancing group");
994 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(operTx -> {
995 FibUtil.updateLbGroupInfo(dpnId, destinationIp, groupIdKey, groupId.toString(), operTx);
996 }), LOG, "Error updating load-balancing group info");
997 LOG.trace("LB group {} towards DC-GW installed on dpn {}. Group - {}", groupIdKey, dpnId, group);
1000 private boolean isTunnelUp(String dcGwIp, BigInteger dpnId) {
1001 String tunnelName = getTunnelRemoteNextHopPointer(dpnId, dcGwIp);
1002 if (tunnelName != null) {
1003 InstanceIdentifier<StateTunnelList> tunnelStateId =
1004 InstanceIdentifier.builder(TunnelsState.class).child(
1005 StateTunnelList.class, new StateTunnelListKey(tunnelName)).build();
1006 return MDSALUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, tunnelStateId)
1007 .toJavaUtil().map(StateTunnelList::getOperState)
1008 .orElse(TunnelOperStatus.Down) == TunnelOperStatus.Up;
1013 private List<Action> getEgressActions(String interfaceName, int actionKey) {
1014 List<Action> actions = Collections.emptyList();
1016 GetEgressActionsForInterfaceInputBuilder egressAction =
1017 new GetEgressActionsForInterfaceInputBuilder().setIntfName(interfaceName).setActionKey(actionKey);
1018 Future<RpcResult<GetEgressActionsForInterfaceOutput>> result =
1019 odlInterfaceRpcService.getEgressActionsForInterface(egressAction.build());
1020 RpcResult<GetEgressActionsForInterfaceOutput> rpcResult = result.get();
1021 if (!rpcResult.isSuccessful()) {
1022 LOG.warn("RPC Call to Get egress actions for interface {} returned with Errors {}",
1023 interfaceName, rpcResult.getErrors());
1025 actions = rpcResult.getResult().getAction();
1027 } catch (InterruptedException | ExecutionException e) {
1028 LOG.warn("Exception when egress actions for interface {}", interfaceName, e);
1034 * This method is invoked when the tunnel state is removed from DS.
1035 * If the there is just one DC-GW left in configuration then the LB groups can be deleted.
1036 * Otherwise, the groups are just updated.
1038 public void removeOrUpdateDcGwLoadBalancingGroup(List<String> availableDcGws, BigInteger dpnId,
1039 String destinationIp) {
1040 Preconditions.checkNotNull(availableDcGws, "There are no dc-gws present");
1041 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(confTx -> {
1042 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(operTx -> {
1043 int noOfDcGws = availableDcGws.size();
1044 // If availableDcGws does not contain the destination Ip it means this is a configuration delete.
1045 if (!availableDcGws.contains(destinationIp)) {
1046 availableDcGws.add(destinationIp);
1047 Collections.sort(availableDcGws);
1049 // TODO : Place the logic to construct all possible DC-GW combination here.
1050 int bucketId = availableDcGws.indexOf(destinationIp);
1051 Optional<DpnLbNexthops> dpnLbNextHops = fibUtil.getDpnLbNexthops(dpnId, destinationIp);
1052 if (!dpnLbNextHops.isPresent()) {
1055 List<String> nextHopKeys = dpnLbNextHops.get().getNexthopKey();
1056 nextHopKeys.forEach(nextHopKey -> {
1057 Optional<Nexthops> optionalNextHops = fibUtil.getNexthops(nextHopKey);
1058 if (!optionalNextHops.isPresent()) {
1061 Nexthops nexthops = optionalNextHops.get();
1062 final String groupId = nexthops.getGroupId();
1063 final long groupIdValue = Long.parseLong(groupId);
1064 if (noOfDcGws > 1) {
1065 mdsalApiManager.removeBucketToTx(dpnId, groupIdValue, bucketId, confTx);
1067 Group group = MDSALUtil.buildGroup(groupIdValue, nextHopKey, GroupTypes.GroupSelect,
1068 MDSALUtil.buildBucketLists(Collections.emptyList()));
1069 LOG.trace("Removed LB group {} on dpn {}", group, dpnId);
1070 mdsalApiManager.removeGroupToTx(dpnId, group, confTx);
1071 removeNextHopPointer(nextHopKey);
1073 // When the DC-GW is removed from configuration.
1074 if (noOfDcGws != availableDcGws.size()) {
1075 FibUtil.removeOrUpdateNextHopInfo(dpnId, nextHopKey, groupId, nexthops, operTx);
1078 FibUtil.removeDpnIdToNextHopInfo(destinationIp, dpnId, operTx);
1079 }), LOG, "Error removing or updating load-balancing group");
1080 }), LOG, "Error removing or updating load-balancing group");
1084 * This method is invoked when the tunnel status is updated.
1085 * The bucket is directly removed/added based on the operational status of the tunnel.
1087 public void updateDcGwLoadBalancingGroup(List<String> availableDcGws,
1088 BigInteger dpnId, String destinationIp, boolean isTunnelUp) {
1089 Preconditions.checkNotNull(availableDcGws, "There are no dc-gws present");
1090 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(confTx -> {
1091 // TODO : Place the logic to construct all possible DC-GW combination here.
1092 int bucketId = availableDcGws.indexOf(destinationIp);
1093 Optional<DpnLbNexthops> dpnLbNextHops = fibUtil.getDpnLbNexthops(dpnId, destinationIp);
1094 if (!dpnLbNextHops.isPresent()) {
1097 List<String> nextHopKeys = dpnLbNextHops.get().getNexthopKey();
1098 nextHopKeys.forEach(nextHopKey -> {
1099 Optional<Nexthops> optionalNextHops = fibUtil.getNexthops(nextHopKey);
1100 if (!optionalNextHops.isPresent()) {
1103 Nexthops nexthops = optionalNextHops.get();
1104 final String groupId = nexthops.getGroupId();
1105 final long groupIdValue = Long.parseLong(groupId);
1107 Bucket bucket = buildBucketForDcGwLbGroup(destinationIp, dpnId, bucketId);
1108 LOG.trace("Added bucket {} to group {} on dpn {}.", bucket, groupId, dpnId);
1109 mdsalApiManager.addBucketToTx(dpnId, groupIdValue, bucket , confTx);
1111 LOG.trace("Removed bucketId {} from group {} on dpn {}.", bucketId, groupId, dpnId);
1112 mdsalApiManager.removeBucketToTx(dpnId, groupIdValue, bucketId, confTx);
1115 }), LOG, "Error updating load-balancing group");
1118 private Bucket buildBucketForDcGwLbGroup(String ipAddress, BigInteger dpnId,
1120 List<Action> listAction = new ArrayList<>();
1121 // ActionKey 0 goes to mpls label.
1123 listAction.add(new ActionPushMpls().buildAction());
1124 listAction.add(new ActionRegMove(actionKey++, FibConstants.NXM_REG_MAPPING
1125 .get(index), 0, 19).buildAction());
1126 String tunnelInterfaceName = getTunnelInterfaceName(dpnId, new IpAddress(ipAddress.toCharArray()));
1127 List<Action> egressActions = getEgressActions(tunnelInterfaceName, actionKey++);
1128 if (!egressActions.isEmpty()) {
1129 listAction.addAll(getEgressActions(tunnelInterfaceName, actionKey++));
1131 // clear off actions if there is no egress actions.
1132 listAction = Collections.emptyList();
1134 return MDSALUtil.buildBucket(listAction, MDSALUtil.GROUP_WEIGHT, index,
1135 MDSALUtil.WATCH_PORT, MDSALUtil.WATCH_GROUP);
1138 public void programDcGwLoadBalancingGroup(List<String> availableDcGws, BigInteger dpnId,
1139 String destinationIp, int addRemoveOrUpdate, boolean isTunnelUp) {
1140 if (NwConstants.ADD_FLOW == addRemoveOrUpdate) {
1141 createDcGwLoadBalancingGroup(availableDcGws, dpnId, destinationIp);
1142 } else if (NwConstants.DEL_FLOW == addRemoveOrUpdate) {
1143 removeOrUpdateDcGwLoadBalancingGroup(availableDcGws, dpnId, destinationIp);
1144 } else if (NwConstants.MOD_FLOW == addRemoveOrUpdate) {
1145 updateDcGwLoadBalancingGroup(availableDcGws, dpnId, destinationIp, isTunnelUp);