2 * Copyright © 2015, 2017 Ericsson India Global Services Pvt Ltd. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.netvirt.fibmanager;
10 import static java.util.stream.Collectors.toList;
11 import static org.opendaylight.genius.infra.Datastore.CONFIGURATION;
12 import static org.opendaylight.genius.infra.Datastore.OPERATIONAL;
13 import static org.opendaylight.genius.mdsalutil.NWUtil.isIpv4Address;
15 import com.google.common.base.Optional;
16 import com.google.common.base.Preconditions;
17 import com.google.common.util.concurrent.ListenableFuture;
19 import java.util.ArrayList;
20 import java.util.Arrays;
21 import java.util.Collections;
22 import java.util.HashMap;
23 import java.util.List;
24 import java.util.Locale;
26 import java.util.Objects;
27 import java.util.concurrent.CopyOnWriteArrayList;
28 import java.util.concurrent.ExecutionException;
29 import java.util.concurrent.Future;
30 import java.util.concurrent.TimeUnit;
31 import java.util.concurrent.TimeoutException;
32 import javax.annotation.PreDestroy;
33 import javax.inject.Inject;
34 import javax.inject.Singleton;
36 import org.eclipse.jdt.annotation.Nullable;
37 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
38 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
39 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
40 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
41 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
42 import org.opendaylight.genius.interfacemanager.interfaces.IInterfaceManager;
43 import org.opendaylight.genius.itm.globals.ITMConstants;
44 import org.opendaylight.genius.mdsalutil.ActionInfo;
45 import org.opendaylight.genius.mdsalutil.BucketInfo;
46 import org.opendaylight.genius.mdsalutil.GroupEntity;
47 import org.opendaylight.genius.mdsalutil.MDSALUtil;
48 import org.opendaylight.genius.mdsalutil.NwConstants;
49 import org.opendaylight.genius.mdsalutil.actions.ActionGroup;
50 import org.opendaylight.genius.mdsalutil.actions.ActionNxResubmit;
51 import org.opendaylight.genius.mdsalutil.actions.ActionOutput;
52 import org.opendaylight.genius.mdsalutil.actions.ActionPushMpls;
53 import org.opendaylight.genius.mdsalutil.actions.ActionPushVlan;
54 import org.opendaylight.genius.mdsalutil.actions.ActionRegLoad;
55 import org.opendaylight.genius.mdsalutil.actions.ActionRegMove;
56 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldEthernetDestination;
57 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldEthernetSource;
58 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldTunnelId;
59 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldVlanVid;
60 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
61 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
62 import org.opendaylight.netvirt.elanmanager.api.IElanService;
63 import org.opendaylight.netvirt.fibmanager.api.L3VPNTransportTypes;
64 import org.opendaylight.netvirt.vpnmanager.api.VpnExtraRouteHelper;
65 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana._if.type.rev170119.L2vlan;
66 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana._if.type.rev170119.Tunnel;
67 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddressBuilder;
68 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.InterfaceType;
69 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.OutputActionCase;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.PushVlanActionCase;
72 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetFieldCase;
73 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
74 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.AllocateIdInput;
75 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.AllocateIdInputBuilder;
76 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.AllocateIdOutput;
77 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.CreateIdPoolInput;
78 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.CreateIdPoolInputBuilder;
79 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.CreateIdPoolOutput;
80 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
81 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.ReleaseIdInput;
82 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.ReleaseIdInputBuilder;
83 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.ReleaseIdOutput;
84 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeBase;
85 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeGre;
86 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeMplsOverGre;
87 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeVxlan;
88 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetEgressActionsForInterfaceInputBuilder;
89 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetEgressActionsForInterfaceOutput;
90 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.OdlInterfaceRpcService;
91 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.TunnelOperStatus;
92 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.TunnelsState;
93 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.tunnels_state.StateTunnelList;
94 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.tunnels_state.StateTunnelListKey;
95 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.DcGatewayIpList;
96 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetEgressActionsForTunnelInputBuilder;
97 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetEgressActionsForTunnelOutput;
98 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetInternalOrExternalInterfaceNameInputBuilder;
99 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetInternalOrExternalInterfaceNameOutput;
100 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetTunnelInterfaceNameInputBuilder;
101 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetTunnelInterfaceNameOutput;
102 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.ItmRpcService;
103 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.lockmanager.rev160413.LockManagerService;
104 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.AddGroupInput;
105 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.AddGroupInputBuilder;
106 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.AddGroupOutput;
107 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.SalGroupService;
108 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupId;
109 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupRef;
110 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupTypes;
111 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.group.Buckets;
112 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.group.buckets.Bucket;
113 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.Group;
114 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
115 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.SegmentTypeBase;
116 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.SegmentTypeFlat;
117 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.SegmentTypeVlan;
118 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.instances.ElanInstance;
119 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentries.VrfEntry;
120 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.L3nexthop;
121 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.VpnNexthops;
122 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.VpnNexthopsKey;
123 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.vpnnexthops.VpnNexthop;
124 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.vpnnexthops.VpnNexthopBuilder;
125 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.vpnnexthops.VpnNexthopKey;
126 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.vpnnexthops.vpnnexthop.IpAdjacencies;
127 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.vpnnexthops.vpnnexthop.IpAdjacenciesBuilder;
128 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.vpnnexthops.vpnnexthop.IpAdjacenciesKey;
129 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.ConfTransportTypeL3vpn;
130 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.ConfTransportTypeL3vpnBuilder;
131 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.l3vpn.lb.nexthops.Nexthops;
132 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.prefix.to._interface.vpn.ids.Prefixes;
133 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.to.extraroutes.vpn.extra.routes.Routes;
134 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg6;
135 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.add.group.input.buckets.bucket.action.action.NxActionResubmitRpcAddGroupCase;
136 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;
137 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.nx.action.reg.load.grouping.NxRegLoad;
138 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
139 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.InstanceIdentifierBuilder;
140 import org.opendaylight.yangtools.yang.common.RpcResult;
141 import org.opendaylight.yangtools.yang.common.Uint32;
142 import org.opendaylight.yangtools.yang.common.Uint64;
143 import org.slf4j.Logger;
144 import org.slf4j.LoggerFactory;
147 public class NexthopManager implements AutoCloseable {
148 private static final Logger LOG = LoggerFactory.getLogger(NexthopManager.class);
149 private static final String NEXTHOP_ID_POOL_NAME = "nextHopPointerPool";
150 private static final long WAIT_TIME_FOR_SYNC_INSTALL = Long.getLong("wait.time.sync.install", 300L);
151 private static final long WAIT_TIME_TO_ACQUIRE_LOCK = 3000L;
152 private static final int SELECT_GROUP_WEIGHT = 1;
153 private static final int RETRY_COUNT = 6;
154 private static final String NEXTHOPMANAGER_JOB_KEY_PREFIX = "NextHopManager";
156 private final DataBroker dataBroker;
157 private final ManagedNewTransactionRunner txRunner;
158 private final IMdsalApiManager mdsalApiManager;
159 private final OdlInterfaceRpcService odlInterfaceRpcService;
160 private final ItmRpcService itmManager;
161 private final IdManagerService idManager;
162 private final IElanService elanService;
163 private final LockManagerService lockManager;
164 private final SalGroupService salGroupService;
165 private final JobCoordinator jobCoordinator;
166 private final FibUtil fibUtil;
167 private final IInterfaceManager interfaceManager;
168 private volatile L3VPNTransportTypes configuredTransportTypeL3VPN = L3VPNTransportTypes.Invalid;
171 * Provides nexthop functions.
172 * Creates group ID pool
174 * @param dataBroker - dataBroker reference
175 * @param mdsalApiManager - mdsalApiManager reference
176 * @param idManager - idManager reference
177 * @param odlInterfaceRpcService - odlInterfaceRpcService reference
178 * @param itmManager - itmManager reference
181 public NexthopManager(final DataBroker dataBroker,
182 final IMdsalApiManager mdsalApiManager,
183 final IdManagerService idManager,
184 final OdlInterfaceRpcService odlInterfaceRpcService,
185 final ItmRpcService itmManager,
186 final LockManagerService lockManager,
187 final IElanService elanService,
188 final SalGroupService salGroupService,
189 final JobCoordinator jobCoordinator,
190 final FibUtil fibUtil,
191 final IInterfaceManager interfaceManager) {
192 this.dataBroker = dataBroker;
193 this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
194 this.mdsalApiManager = mdsalApiManager;
195 this.idManager = idManager;
196 this.odlInterfaceRpcService = odlInterfaceRpcService;
197 this.itmManager = itmManager;
198 this.elanService = elanService;
199 this.salGroupService = salGroupService;
200 this.jobCoordinator = jobCoordinator;
201 this.fibUtil = fibUtil;
202 this.lockManager = lockManager;
203 this.interfaceManager = interfaceManager;
207 private void createIdPool() {
208 CreateIdPoolInput createPool = new CreateIdPoolInputBuilder()
209 .setPoolName(NEXTHOP_ID_POOL_NAME)
214 Future<RpcResult<CreateIdPoolOutput>> result = idManager.createIdPool(createPool);
215 if (result != null && result.get().isSuccessful()) {
216 LOG.info("Created IdPool for NextHopPointerPool");
218 } catch (InterruptedException | ExecutionException e) {
219 LOG.error("Failed to create idPool for NextHopPointerPool", e);
223 private String getNextHopKey(Uint32 vpnId, String ipAddress) {
224 return "nexthop." + vpnId + ipAddress;
227 String getRemoteSelectGroupKey(Uint32 vpnId, String ipAddress) {
228 return "remote.ecmp.nexthop." + vpnId + ipAddress;
231 String getLocalSelectGroupKey(Uint32 vpnId, String ipAddress) {
232 return "local.ecmp.nexthop." + vpnId + ipAddress;
235 public ItmRpcService getItmManager() {
239 protected long createNextHopPointer(String nexthopKey) {
240 AllocateIdInput getIdInput = new AllocateIdInputBuilder()
241 .setPoolName(NEXTHOP_ID_POOL_NAME).setIdKey(nexthopKey)
243 //TODO: Proper error handling once IdManager code is complete
245 Future<RpcResult<AllocateIdOutput>> result = idManager.allocateId(getIdInput);
246 RpcResult<AllocateIdOutput> rpcResult = result.get();
247 return rpcResult.getResult().getIdValue().toJava();
248 } catch (NullPointerException | InterruptedException | ExecutionException e) {
249 // FIXME: NPEs should not be caught but rather their root cause should be eliminated
250 LOG.trace("Failed to allocate {}", getIdInput, e);
255 protected void removeNextHopPointer(String nexthopKey) {
256 ReleaseIdInput idInput = new ReleaseIdInputBuilder()
257 .setPoolName(NEXTHOP_ID_POOL_NAME)
258 .setIdKey(nexthopKey).build();
260 RpcResult<ReleaseIdOutput> rpcResult = idManager.releaseId(idInput).get();
261 if (!rpcResult.isSuccessful()) {
262 LOG.error("RPC Call to Get Unique Id for nexthopKey {} returned with Errors {}",
263 nexthopKey, rpcResult.getErrors());
265 } catch (InterruptedException | ExecutionException e) {
266 LOG.warn("Exception when getting Unique Id for key {}", nexthopKey, e);
270 protected List<ActionInfo> getEgressActionsForInterface(final String ifName, int actionKey,
271 boolean isTunnelInterface,
272 Uint32 vpnId, String destIpPrefix) {
273 List<Action> actions;
275 if (isTunnelInterface && interfaceManager.isItmDirectTunnelsEnabled()) {
276 RpcResult<GetEgressActionsForTunnelOutput> rpcResult =
277 itmManager.getEgressActionsForTunnel(new GetEgressActionsForTunnelInputBuilder()
278 .setIntfName(ifName).build()).get();
279 if (!rpcResult.isSuccessful()) {
280 LOG.error("RPC Call to Get egress tunnel actions for interface {} returned with Errors {}",
281 ifName, rpcResult.getErrors());
282 return Collections.emptyList();
284 actions = rpcResult.getResult().nonnullAction();
287 RpcResult<GetEgressActionsForInterfaceOutput> rpcResult = odlInterfaceRpcService
288 .getEgressActionsForInterface(new GetEgressActionsForInterfaceInputBuilder()
289 .setIntfName(ifName).build()).get();
290 if (!rpcResult.isSuccessful()) {
291 LOG.error("RPC Call to Get egress vm actions for interface {} vpnId {} ipPrefix {} returned with "
292 + "Errors {}", ifName, vpnId, destIpPrefix, rpcResult.getErrors());
293 return Collections.emptyList();
295 actions = rpcResult.getResult().nonnullAction();
298 List<ActionInfo> listActionInfo = new ArrayList<>();
299 for (Action action : actions) {
300 actionKey = action.key().getOrder() + actionKey;
301 org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action
302 actionClass = action.getAction();
303 if (actionClass instanceof OutputActionCase) {
304 listActionInfo.add(new ActionOutput(actionKey,
305 ((OutputActionCase) actionClass).getOutputAction().getOutputNodeConnector()));
306 } else if (actionClass instanceof PushVlanActionCase) {
307 listActionInfo.add(new ActionPushVlan(actionKey));
308 } else if (actionClass instanceof SetFieldCase) {
309 if (((SetFieldCase) actionClass).getSetField().getVlanMatch() != null) {
310 int vlanVid = ((SetFieldCase) actionClass).getSetField().getVlanMatch()
311 .getVlanId().getVlanId().getValue().toJava();
312 listActionInfo.add(new ActionSetFieldVlanVid(actionKey, vlanVid));
314 } else if (actionClass instanceof NxActionResubmitRpcAddGroupCase) {
315 Short tableId = ((NxActionResubmitRpcAddGroupCase) actionClass).getNxResubmit().getTable().toJava();
316 listActionInfo.add(new ActionNxResubmit(actionKey, tableId));
317 } else if (actionClass instanceof NxActionRegLoadNodesNodeTableFlowApplyActionsCase) {
318 NxRegLoad nxRegLoad =
319 ((NxActionRegLoadNodesNodeTableFlowApplyActionsCase) actionClass).getNxRegLoad();
320 listActionInfo.add(new ActionRegLoad(actionKey, NxmNxReg6.class,
321 nxRegLoad.getDst().getStart().toJava(), nxRegLoad.getDst().getEnd().toJava(),
322 nxRegLoad.getValue().longValue()));
325 return listActionInfo;
326 } catch (InterruptedException | ExecutionException | NullPointerException e) {
327 LOG.error("Exception when egress actions for interface {} isTunnel {} vpnId {} ipPrefix {}", ifName,
328 isTunnelInterface, vpnId, destIpPrefix, e);
330 LOG.warn("Exception when egress actions for interface {}", ifName);
331 return Collections.emptyList();
335 protected String getTunnelInterfaceName(Uint64 srcDpId, Uint64 dstDpId) {
336 Class<? extends TunnelTypeBase> tunType = getReqTunType(getReqTransType().toUpperCase(Locale.getDefault()));
337 Future<RpcResult<GetTunnelInterfaceNameOutput>> result;
339 result = itmManager.getTunnelInterfaceName(new GetTunnelInterfaceNameInputBuilder()
340 .setSourceDpid(srcDpId)
341 .setDestinationDpid(dstDpId)
342 .setTunnelType(tunType)
344 RpcResult<GetTunnelInterfaceNameOutput> rpcResult = result.get();
345 if (!rpcResult.isSuccessful()) {
346 LOG.warn("RPC Call to getTunnelInterfaceId returned with Errors {}", rpcResult.getErrors());
348 return rpcResult.getResult().getInterfaceName();
350 } catch (InterruptedException | ExecutionException e) {
351 LOG.warn("Exception when getting tunnel interface Id for tunnel between {} and {}", srcDpId, dstDpId, e);
357 protected String getTunnelInterfaceName(Uint64 srcDpId, org.opendaylight.yang.gen.v1.urn.ietf.params
358 .xml.ns.yang.ietf.inet.types.rev130715.IpAddress dstIp, Class<? extends TunnelTypeBase> tunnelType) {
359 Future<RpcResult<GetInternalOrExternalInterfaceNameOutput>> result;
361 LOG.debug("Trying to fetch tunnel interface name for source dpn {} destIp {} tunType {}", srcDpId,
362 dstIp.stringValue(), tunnelType.getName());
363 result = itmManager.getInternalOrExternalInterfaceName(new GetInternalOrExternalInterfaceNameInputBuilder()
364 .setSourceDpid(srcDpId)
365 .setDestinationIp(dstIp)
366 .setTunnelType(tunnelType)
368 RpcResult<GetInternalOrExternalInterfaceNameOutput> rpcResult = result.get();
369 if (!rpcResult.isSuccessful()) {
370 LOG.warn("RPC Call to getTunnelInterfaceName returned with Errors {}", rpcResult.getErrors());
372 return rpcResult.getResult().getInterfaceName();
374 } catch (InterruptedException | ExecutionException e) {
375 LOG.error("Exception when getting tunnel interface Id for tunnel between {} and {}", srcDpId, dstIp, e);
381 public long getLocalNextHopGroup(Uint32 vpnId,
382 String ipNextHopAddress) {
383 long groupId = createNextHopPointer(getNextHopKey(vpnId, ipNextHopAddress));
384 if (groupId == FibConstants.INVALID_GROUP_ID) {
385 LOG.error("Unable to allocate groupId for vpnId {} , prefix {}", vpnId, ipNextHopAddress);
390 public long getLocalSelectGroup(Uint32 vpnId,
391 String ipNextHopAddress) {
392 long groupId = createNextHopPointer(getLocalSelectGroupKey(vpnId, ipNextHopAddress));
393 if (groupId == FibConstants.INVALID_GROUP_ID) {
394 LOG.error("Unable to allocate groupId for vpnId {} , prefix {}", vpnId, ipNextHopAddress);
399 public long createLocalNextHop(Uint32 vpnId, Uint64 dpnId, String ifName,
400 String primaryIpAddress, String currDestIpPrefix,
401 String gwMacAddress, Uint32 parentVpnId) {
402 //For VPN Imported routes, getting VPN Instance name using parentVpnId
403 String vpnName = parentVpnId != null ? fibUtil.getVpnNameFromId(parentVpnId) : fibUtil.getVpnNameFromId(vpnId);
404 if (vpnName == null) {
407 String macAddress = fibUtil.getMacAddressFromPrefix(ifName, vpnName, primaryIpAddress);
409 long groupId = createNextHopPointer(getNextHopKey(vpnId, primaryIpAddress));
411 LOG.error("Unable to allocate groupId for vpnId {} , IntfName {}, primaryIpAddress {} curIpPrefix {}",
412 vpnId, ifName, primaryIpAddress, currDestIpPrefix);
415 String nextHopLockStr = vpnId + primaryIpAddress;
416 String jobKey = FibUtil.getCreateLocalNextHopJobKey(vpnId, dpnId, currDestIpPrefix);
417 jobCoordinator.enqueueJob(jobKey, () -> {
419 if (FibUtil.lockCluster(lockManager, nextHopLockStr, WAIT_TIME_TO_ACQUIRE_LOCK)) {
420 VpnNexthop nexthop = getVpnNexthop(vpnId, primaryIpAddress);
421 LOG.trace("nexthop: {} retrieved for vpnId {}, prefix {}, ifName {} on dpn {}", nexthop, vpnId,
422 primaryIpAddress, ifName, dpnId);
423 if (nexthop == null) {
424 String encMacAddress = macAddress == null
425 ? fibUtil.getMacAddressFromPrefix(ifName, vpnName, primaryIpAddress) : macAddress;
426 List<ActionInfo> listActionInfo = new ArrayList<>();
429 if (encMacAddress != null) {
430 if (gwMacAddress != null) {
431 LOG.trace("The Local NextHop Group Source Mac {} for VpnInterface {} on VPN {}",
432 gwMacAddress, ifName, vpnId);
433 listActionInfo.add(new ActionSetFieldEthernetSource(actionKey++,
434 new MacAddress(gwMacAddress)));
436 listActionInfo.add(new ActionSetFieldEthernetDestination(actionKey++,
437 new MacAddress(encMacAddress)));
438 // listActionInfo.add(0, new ActionPopMpls());
440 LOG.error("mac address for new local nexthop group {} is null for vpnId {}, prefix {}, "
441 + "ifName {} on dpn {}", groupId, vpnId, primaryIpAddress, ifName, dpnId);
443 List<ActionInfo> nhActionInfoList = getEgressActionsForInterface(ifName, actionKey, false,
444 vpnId, currDestIpPrefix);
445 if (nhActionInfoList.isEmpty()) {
446 LOG.error("createLocalNextHop: Skipping, Empty list of egress actions received for "
447 + "interface {} on dpn {} for vpn {} prefix {}", ifName, dpnId, vpnId,
450 listActionInfo.addAll(nhActionInfoList);
451 BucketInfo bucket = new BucketInfo(listActionInfo);
452 List<BucketInfo> listBucketInfo = new ArrayList<>();
453 listBucketInfo.add(bucket);
454 GroupEntity groupEntity = MDSALUtil.buildGroupEntity(dpnId, groupId, primaryIpAddress,
455 GroupTypes.GroupAll, listBucketInfo);
456 LOG.trace("Install LNH Group: id {}, mac address {}, interface {} for prefix {}", groupId,
457 encMacAddress, ifName, primaryIpAddress);
458 //Try to install group directly on the DPN bypassing the FRM, in order to avoid waiting for the
459 // group to get installed before programming the flows
460 installGroupOnDpn(groupId, dpnId, primaryIpAddress, listBucketInfo,
461 getNextHopKey(vpnId, primaryIpAddress), GroupTypes.GroupAll);
463 mdsalApiManager.syncInstallGroup(groupEntity);
465 addVpnNexthopToDS(dpnId, vpnId, primaryIpAddress, currDestIpPrefix, groupId);
468 // Ignore adding new prefix , if it already exists
469 List<IpAdjacencies> prefixesList = nexthop.getIpAdjacencies();
470 IpAdjacencies prefix = new IpAdjacenciesBuilder().setIpAdjacency(currDestIpPrefix).build();
471 if (prefixesList != null && prefixesList.contains(prefix)) {
472 LOG.trace("Prefix {} is already present in l3nextHop {} ", currDestIpPrefix, nexthop);
474 IpAdjacenciesBuilder ipPrefixesBuilder =
475 new IpAdjacenciesBuilder().withKey(new IpAdjacenciesKey(currDestIpPrefix));
476 LOG.trace("Updating prefix {} to vpnNextHop {} Operational DS", currDestIpPrefix, nexthop);
477 MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL,
478 getVpnNextHopIpPrefixIdentifier(vpnId, primaryIpAddress, currDestIpPrefix),
479 ipPrefixesBuilder.build());
484 FibUtil.unlockCluster(lockManager, nextHopLockStr);
486 return Collections.emptyList();
491 private void installGroupOnDpn(long groupId, Uint64 dpnId, String groupName, List<BucketInfo> bucketsInfo,
492 String nextHopKey, GroupTypes groupType) {
493 NodeRef nodeRef = FibUtil.buildNodeRef(dpnId);
494 Buckets buckets = FibUtil.buildBuckets(bucketsInfo);
495 GroupRef groupRef = new GroupRef(FibUtil.buildGroupInstanceIdentifier(groupId, dpnId));
496 AddGroupInput input = new AddGroupInputBuilder().setNode(nodeRef).setGroupId(new GroupId(groupId))
497 .setBuckets(buckets).setGroupRef(groupRef).setGroupType(groupType)
498 .setGroupName(groupName).build();
499 Future<RpcResult<AddGroupOutput>> groupStats = salGroupService.addGroup(input);
500 RpcResult<AddGroupOutput> rpcResult = null;
502 rpcResult = groupStats.get(WAIT_TIME_FOR_SYNC_INSTALL, TimeUnit.MILLISECONDS);
503 if (rpcResult != null && rpcResult.isSuccessful()) {
504 LOG.info("installGroupOnDpn: Group {} with key {} has been successfully installed directly on dpn {}.",
505 groupId, nextHopKey, dpnId);
507 LOG.error("installGroupOnDpn: Unable to install group {} with key {} directly on dpn {} due to {}.",
508 groupId, nextHopKey, dpnId, rpcResult != null ? rpcResult.getErrors() : null);
510 } catch (InterruptedException | ExecutionException e) {
511 LOG.error("installGroupOnDpn: Error while installing group {} directly on dpn {}", groupId, dpnId);
512 } catch (TimeoutException e) {
513 LOG.error("installGroupOnDpn: Group {} installation on dpn {} timed out.", groupId, dpnId);
517 protected void addVpnNexthopToDS(Uint64 dpnId, Uint32 vpnId, String primaryIpAddr,
518 String currIpAddr, long egressPointer) {
519 InstanceIdentifierBuilder<VpnNexthops> idBuilder = InstanceIdentifier.builder(L3nexthop.class)
520 .child(VpnNexthops.class, new VpnNexthopsKey(vpnId));
522 List<IpAdjacencies> ipPrefixesList = new ArrayList<>();
523 IpAdjacencies prefix = new IpAdjacenciesBuilder().setIpAdjacency(currIpAddr).build();
524 ipPrefixesList.add(prefix);
525 // Add nexthop to vpn node
526 VpnNexthop nh = new VpnNexthopBuilder()
527 .withKey(new VpnNexthopKey(primaryIpAddr))
529 .setIpAdjacencies(ipPrefixesList)
530 .setEgressPointer(egressPointer).build();
532 InstanceIdentifier<VpnNexthop> id1 = idBuilder
533 .child(VpnNexthop.class, new VpnNexthopKey(primaryIpAddr)).build();
534 LOG.trace("Adding vpnnextHop {} to Operational DS", nh);
535 MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL, id1, nh);
539 protected InstanceIdentifier<IpAdjacencies> getVpnNextHopIpPrefixIdentifier(Uint32 vpnId, String primaryIpAddress,
541 InstanceIdentifier<IpAdjacencies> id = InstanceIdentifier.builder(L3nexthop.class)
542 .child(VpnNexthops.class, new VpnNexthopsKey(vpnId))
543 .child(VpnNexthop.class, new VpnNexthopKey(primaryIpAddress))
544 .child(IpAdjacencies.class, new IpAdjacenciesKey(ipPrefix)).build();
549 protected VpnNexthop getVpnNexthop(Uint32 vpnId, String ipAddress) {
551 // check if vpn node is there
552 InstanceIdentifierBuilder<VpnNexthops> idBuilder =
553 InstanceIdentifier.builder(L3nexthop.class).child(VpnNexthops.class,
554 new VpnNexthopsKey(vpnId));
555 InstanceIdentifier<VpnNexthops> id = idBuilder.build();
556 Optional<VpnNexthops> vpnNexthops = MDSALUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, id);
557 if (vpnNexthops.isPresent()) {
558 // get nexthops list for vpn
559 List<VpnNexthop> nexthops = vpnNexthops.get().nonnullVpnNexthop();
560 for (VpnNexthop nexthop : nexthops) {
561 if (Objects.equals(nexthop.getIpAddress(), ipAddress)) {
563 LOG.trace("VpnNextHop : {}", nexthop);
567 // return null if not found
573 public AdjacencyResult getRemoteNextHopPointer(Uint64 remoteDpnId, Uint32 vpnId, String prefixIp,
574 @Nullable String nextHopIp, Class<? extends TunnelTypeBase> tunnelType) {
575 String egressIfName = null;
576 LOG.trace("getRemoteNextHopPointer: input [remoteDpnId {}, vpnId {}, prefixIp {}, nextHopIp {} ]", remoteDpnId,
577 vpnId, prefixIp, nextHopIp);
579 Class<? extends InterfaceType> egressIfType;
580 ElanInstance elanInstance = getElanInstanceForPrefix(vpnId, prefixIp);
581 if (elanInstance != null) {
582 egressIfType = getInterfaceType(elanInstance);
584 LOG.warn("Failed to determine network type for prefixIp {} using tunnel", prefixIp);
585 egressIfType = Tunnel.class;
588 if (Tunnel.class.equals(egressIfType)) {
589 egressIfName = getTunnelRemoteNextHopPointer(remoteDpnId, nextHopIp, tunnelType);
591 egressIfName = getExtPortRemoteNextHopPointer(remoteDpnId, elanInstance);
594 LOG.trace("NextHop pointer for prefixIp {} vpnId {} dpnId {} is {}", prefixIp, vpnId, remoteDpnId,
596 return egressIfName != null ? new AdjacencyResult(egressIfName, egressIfType, nextHopIp,
600 private void removeVpnNexthopFromDS(Uint32 vpnId, String ipPrefix) {
602 InstanceIdentifierBuilder<VpnNexthop> idBuilder = InstanceIdentifier.builder(L3nexthop.class)
603 .child(VpnNexthops.class, new VpnNexthopsKey(vpnId))
604 .child(VpnNexthop.class, new VpnNexthopKey(ipPrefix));
605 InstanceIdentifier<VpnNexthop> id = idBuilder.build();
607 LOG.trace("Removing vpn next hop from datastore : {}", id);
608 MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.OPERATIONAL, id);
611 public void removeLocalNextHop(Uint64 dpnId, Uint32 vpnId, String primaryIpAddress, String currDestIpPrefix) {
612 String nextHopLockStr = vpnId + primaryIpAddress;
614 if (FibUtil.lockCluster(lockManager, nextHopLockStr, WAIT_TIME_TO_ACQUIRE_LOCK)) {
615 VpnNexthop nh = getVpnNexthop(vpnId, primaryIpAddress);
617 List<IpAdjacencies> prefixesList = new ArrayList<>(nh.nonnullIpAdjacencies());
618 IpAdjacencies prefix = new IpAdjacenciesBuilder().setIpAdjacency(currDestIpPrefix).build();
619 prefixesList.remove(prefix);
620 if (prefixesList.isEmpty()) { //remove the group only if there are no more flows using this group
621 GroupEntity groupEntity = MDSALUtil.buildGroupEntity(dpnId, nh.getEgressPointer().toJava(),
622 primaryIpAddress, GroupTypes.GroupAll, Collections.emptyList());
624 mdsalApiManager.removeGroup(groupEntity);
626 removeVpnNexthopFromDS(vpnId, primaryIpAddress);
628 removeNextHopPointer(getNextHopKey(vpnId, primaryIpAddress));
629 LOG.debug("Local Next hop {} for {} {} on dpn {} successfully deleted",
630 nh.getEgressPointer(), vpnId, primaryIpAddress, dpnId);
632 //remove the currIpPrefx from IpPrefixList of the vpnNexthop
633 LOG.trace("Removing the prefix {} from vpnNextHop {} Operational DS", currDestIpPrefix, nh);
634 MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.OPERATIONAL,
635 getVpnNextHopIpPrefixIdentifier(vpnId, primaryIpAddress, currDestIpPrefix));
639 LOG.error("Local NextHop for VpnId {} curIpPrefix {} on dpn {} primaryIpAddress {} not deleted",
640 vpnId, currDestIpPrefix, dpnId, primaryIpAddress);
644 FibUtil.unlockCluster(lockManager, nextHopLockStr);
648 public void setConfTransType(String service, String transportType) {
650 if (!service.equalsIgnoreCase("L3VPN")) {
651 LOG.error("Incorrect service {} provided for setting the transport type.", service);
655 L3VPNTransportTypes transType = L3VPNTransportTypes.validateTransportType(transportType
656 .toUpperCase(Locale.getDefault()));
658 if (transType != L3VPNTransportTypes.Invalid) {
659 configuredTransportTypeL3VPN = transType;
663 public void writeConfTransTypeConfigDS() {
664 MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, getConfTransportTypeIdentifier(),
665 createConfTransportType(configuredTransportTypeL3VPN.getTransportType()));
668 public L3VPNTransportTypes getConfiguredTransportTypeL3VPN() {
669 return this.configuredTransportTypeL3VPN;
672 public String getReqTransType() {
673 if (configuredTransportTypeL3VPN == L3VPNTransportTypes.Invalid) {
675 * Restart scenario, Read from the ConfigDS.
676 * if the value is Unset, cache value as VxLAN.
678 LOG.trace("configureTransportType is not yet set.");
679 Optional<ConfTransportTypeL3vpn> configuredTransTypeFromConfig =
680 MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, getConfTransportTypeIdentifier());
682 if (configuredTransTypeFromConfig.isPresent()) {
683 if (TunnelTypeGre.class.equals(configuredTransTypeFromConfig.get().getTransportType())) {
684 configuredTransportTypeL3VPN = L3VPNTransportTypes.GRE;
686 configuredTransportTypeL3VPN = L3VPNTransportTypes.VxLAN;
688 LOG.trace("configuredTransportType set from config DS to {}",
689 getConfiguredTransportTypeL3VPN().getTransportType());
691 setConfTransType("L3VPN", L3VPNTransportTypes.VxLAN.getTransportType());
692 LOG.trace("configuredTransportType is not set in the Config DS. VxLAN as default will be used.");
695 LOG.trace("configuredTransportType is set as {}", getConfiguredTransportTypeL3VPN().getTransportType());
697 return getConfiguredTransportTypeL3VPN().getTransportType();
700 public InstanceIdentifier<ConfTransportTypeL3vpn> getConfTransportTypeIdentifier() {
701 return InstanceIdentifier.builder(ConfTransportTypeL3vpn.class).build();
704 private ConfTransportTypeL3vpn createConfTransportType(String type) {
705 ConfTransportTypeL3vpn confTransType;
707 case ITMConstants.TUNNEL_TYPE_GRE:
708 confTransType = new ConfTransportTypeL3vpnBuilder().setTransportType(TunnelTypeGre.class).build();
709 LOG.trace("Setting the confTransportType to GRE.");
711 case ITMConstants.TUNNEL_TYPE_VXLAN:
712 confTransType = new ConfTransportTypeL3vpnBuilder().setTransportType(TunnelTypeVxlan.class).build();
713 LOG.trace("Setting the confTransportType to VxLAN.");
716 LOG.trace("Invalid transport type {} passed to Config DS ", type);
717 confTransType = null;
720 return confTransType;
723 public Class<? extends TunnelTypeBase> getReqTunType(String transportType) {
724 switch (transportType) {
726 return TunnelTypeVxlan.class;
728 return TunnelTypeGre.class;
730 return TunnelTypeMplsOverGre.class;
734 public String getTransportTypeStr(String tunType) {
735 if (tunType.equals(TunnelTypeVxlan.class.toString())) {
736 return ITMConstants.TUNNEL_TYPE_VXLAN;
737 } else if (tunType.equals(TunnelTypeGre.class.toString())) {
738 return ITMConstants.TUNNEL_TYPE_GRE;
739 } else if (tunType.equals(TunnelTypeMplsOverGre.class.toString())) {
740 return ITMConstants.TUNNEL_TYPE_MPLSoGRE;
742 return ITMConstants.TUNNEL_TYPE_INVALID;
748 public void close() {
749 LOG.info("{} close", getClass().getSimpleName());
752 // TODO Clean up the exception handling
753 @SuppressWarnings("checkstyle:IllegalCatch")
755 private String getTunnelRemoteNextHopPointer(Uint64 remoteDpnId, String nextHopIp,
756 Class<? extends TunnelTypeBase> tunnelType) {
757 if (nextHopIp != null && !nextHopIp.isEmpty()) {
759 // here use the config for tunnel type param
760 return getTunnelInterfaceName(remoteDpnId,
761 org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddressBuilder
762 .getDefaultInstance(nextHopIp), tunnelType);
763 } catch (Exception ex) {
764 LOG.error("Error while retrieving nexthop pointer for nexthop {} remoteDpn {}",
765 nextHopIp, remoteDpnId, ex);
772 private String getExtPortRemoteNextHopPointer(Uint64 remoteDpnId, ElanInstance elanInstance) {
773 return elanService.getExternalElanInterface(elanInstance.getElanInstanceName(), remoteDpnId);
777 * Get the interface type associated with the type of ELAN used for routing
778 * traffic to/from remote compute nodes.
780 * @param elanInstance The elan instance
781 * @return L2vlan for flat/VLAN network type and Tunnel otherwise
783 private Class<? extends InterfaceType> getInterfaceType(ElanInstance elanInstance) {
784 Class<? extends SegmentTypeBase> segmentType = elanInstance.getSegmentType();
785 if (SegmentTypeFlat.class.equals(segmentType) || SegmentTypeVlan.class.equals(segmentType)) {
792 private ElanInstance getElanInstanceForPrefix(Uint32 vpnId, String prefixIp) {
793 ElanInstance elanInstance = null;
794 Prefixes prefix = fibUtil.getPrefixToInterface(vpnId, prefixIp);
795 if (prefix != null) {
796 if (prefix.getNetworkId() != null) {
797 elanInstance = elanService.getElanInstance(prefix.getNetworkId().getValue());
804 static class AdjacencyResult {
805 private final String interfaceName;
806 private final Class<? extends InterfaceType> interfaceType;
807 private final String nextHopIp;
808 private final String prefix;
810 AdjacencyResult(String interfaceName, Class<? extends InterfaceType> interfaceType, String nextHopIp,
812 this.interfaceName = interfaceName;
813 this.interfaceType = interfaceType;
814 this.nextHopIp = nextHopIp;
815 this.prefix = prefix;
818 public String getInterfaceName() {
819 return interfaceName;
822 public Class<? extends InterfaceType> getInterfaceType() {
823 return interfaceType;
826 public String getNextHopIp() {
830 public String getPrefix() {
835 public int hashCode() {
836 final int prime = 31;
838 result = prime * result + (interfaceName == null ? 0 : interfaceName.hashCode());
843 public boolean equals(Object obj) {
848 if (getClass() != obj.getClass()) {
851 AdjacencyResult other = (AdjacencyResult) obj;
852 return interfaceName.equals(other.interfaceName);
857 protected long setupLoadBalancingNextHop(Uint32 parentVpnId, Uint64 dpnId,
858 String destPrefix, List<BucketInfo> localBucketInfo, List<BucketInfo> remoteBucketInfo) {
859 long remoteGroupId = createNextHopPointer(getRemoteSelectGroupKey(parentVpnId, destPrefix));
860 if (remoteGroupId == FibConstants.INVALID_GROUP_ID) {
861 LOG.error("Unable to allocate/retrieve remote groupId for vpnId {} , prefix {}", parentVpnId, destPrefix);
862 return remoteGroupId;
864 long localGroupId = createNextHopPointer(getLocalSelectGroupKey(parentVpnId, destPrefix));
865 if (localGroupId == FibConstants.INVALID_GROUP_ID) {
866 LOG.error("Unable to allocate/retrieve local groupId for vpnId {} , prefix {}",
867 parentVpnId, destPrefix);
868 return remoteGroupId;
870 List<BucketInfo> combinedBucketInfo = new ArrayList<>();
871 combinedBucketInfo.addAll(localBucketInfo);
872 combinedBucketInfo.addAll(remoteBucketInfo);
873 GroupEntity remoteGroupEntity = MDSALUtil.buildGroupEntity(
874 dpnId, remoteGroupId, destPrefix, GroupTypes.GroupSelect, combinedBucketInfo);
875 GroupEntity localGroupEntity = MDSALUtil.buildGroupEntity(
876 dpnId, localGroupId, destPrefix, GroupTypes.GroupSelect, localBucketInfo);
877 String jobKey = FibUtil.getCreateLocalNextHopJobKey(parentVpnId, dpnId, destPrefix);
878 jobCoordinator.enqueueJob(jobKey, () -> {
879 mdsalApiManager.syncInstallGroup(remoteGroupEntity);
880 if (!localBucketInfo.isEmpty()) {
881 mdsalApiManager.syncInstallGroup(localGroupEntity);
883 if (LOG.isDebugEnabled()) {
884 LOG.debug("Finished installing GroupEntity with jobCoordinator key {} remoteGroupEntity.groupId {}"
885 + "localGroupEntity.groupId {} groupEntity.groupType {}", jobKey,
886 remoteGroupEntity.getGroupId(), localGroupEntity.getGroupId(),
887 remoteGroupEntity.getGroupType());
889 // Delete local group(if exists) if there is no local info.
890 // Local group has to be deleted if all VMs in a compute is deleted.
891 if (localBucketInfo.isEmpty()) {
892 LOG.debug("Deleting local group {} since no local nhs present for "
893 + "prefix {}", localGroupEntity.getGroupId(), destPrefix);
894 mdsalApiManager.syncRemoveGroup(localGroupEntity);
896 return Collections.emptyList();
898 return remoteGroupId;
901 protected void deleteLoadBalancingNextHop(Uint32 parentVpnId, Uint64 dpnId, String destPrefix) {
902 long remoteGroupId = createNextHopPointer(getRemoteSelectGroupKey(parentVpnId, destPrefix));
903 if (remoteGroupId == FibConstants.INVALID_GROUP_ID) {
904 LOG.error("Unable to allocate/retrieve remote groupId for vpnId {} , prefix {}", parentVpnId, destPrefix);
906 long localGroupId = createNextHopPointer(getLocalSelectGroupKey(parentVpnId, destPrefix));
907 if (localGroupId == FibConstants.INVALID_GROUP_ID) {
908 LOG.error("Unable to allocate/retrieve local groupId for vpnId {} , prefix {}", parentVpnId, destPrefix);
910 GroupEntity remoteGroupEntity = MDSALUtil.buildGroupEntity(
911 dpnId, remoteGroupId, destPrefix, GroupTypes.GroupSelect, Collections.emptyList());
912 GroupEntity localGroupEntity = MDSALUtil.buildGroupEntity(
913 dpnId, localGroupId, destPrefix, GroupTypes.GroupSelect, Collections.emptyList());
914 String jobKey = FibUtil.getCreateLocalNextHopJobKey(parentVpnId, dpnId, destPrefix);
915 jobCoordinator.enqueueJob(jobKey, () -> {
916 mdsalApiManager.syncRemoveGroup(remoteGroupEntity);
917 mdsalApiManager.syncRemoveGroup(localGroupEntity);
918 if (LOG.isDebugEnabled()) {
919 LOG.debug("Finished removing GroupEntity with jobCoordinator key {} remoteGroupEntity.groupId {}"
920 + "localGroupEntity.groupId {}", jobKey, remoteGroupId, localGroupId);
922 return Collections.emptyList();
926 long createNextHopGroups(Uint32 vpnId, String rd, Uint64 dpnId, VrfEntry vrfEntry,
927 @Nullable Routes routes, List<Routes> vpnExtraRoutes) {
928 List<BucketInfo> localBucketInfo = new ArrayList<>();
929 List<Routes> clonedVpnExtraRoutes = new ArrayList<>(vpnExtraRoutes);
930 if (clonedVpnExtraRoutes.contains(routes)) {
931 localBucketInfo.addAll(getBucketsForLocalNexthop(vpnId, dpnId, vrfEntry, routes));
932 clonedVpnExtraRoutes.remove(routes);
934 List<BucketInfo> remoteBucketInfo =
935 new ArrayList<>(getBucketsForRemoteNexthop(vpnId, dpnId, vrfEntry, rd, clonedVpnExtraRoutes));
936 return setupLoadBalancingNextHop(vpnId, dpnId,
937 vrfEntry.getDestPrefix(), localBucketInfo, remoteBucketInfo);
940 private List<BucketInfo> getBucketsForLocalNexthop(Uint32 vpnId, Uint64 dpnId,
941 VrfEntry vrfEntry, Routes routes) {
942 @Nullable List<String> nexthopIpList = routes.getNexthopIpList();
943 if (LOG.isDebugEnabled()) {
944 LOG.debug("NexthopManager.getBucketsForLocalNexthop invoked with vpnId {} dpnId {} "
945 + " vrfEntry.routePaths {}, routes.nexthopList {}", vpnId, dpnId, vrfEntry.getRoutePaths(),
948 List<BucketInfo> listBucketInfo = new CopyOnWriteArrayList<>();
949 if (nexthopIpList != null) {
950 nexthopIpList.parallelStream().forEach(nextHopIp -> {
951 String localNextHopIP;
952 if (isIpv4Address(nextHopIp)) {
953 localNextHopIP = nextHopIp + NwConstants.IPV4PREFIX;
955 localNextHopIP = nextHopIp + NwConstants.IPV6PREFIX;
957 Prefixes localNextHopInfo = fibUtil.getPrefixToInterface(vpnId, localNextHopIP);
958 if (localNextHopInfo != null) {
959 long groupId = getLocalNextHopGroup(vpnId, localNextHopIP);
960 if (groupId == FibConstants.INVALID_GROUP_ID) {
961 LOG.error("Unable to allocate groupId for vpnId {} , prefix {} , interface {}", vpnId,
962 vrfEntry.getDestPrefix(), localNextHopInfo.getVpnInterfaceName());
965 List<ActionInfo> actionsInfos =
966 Collections.singletonList(new ActionGroup(groupId));
967 BucketInfo bucket = new BucketInfo(actionsInfos);
969 listBucketInfo.add(bucket);
973 LOG.trace("LOCAL: listbucket {}, vpnId {}, dpnId {}, routes {}", listBucketInfo, vpnId, dpnId, routes);
974 return listBucketInfo;
977 private List<BucketInfo> getBucketsForRemoteNexthop(Uint32 vpnId, Uint64 dpnId, VrfEntry vrfEntry, String rd,
978 List<Routes> vpnExtraRoutes) {
979 List<BucketInfo> listBucketInfo = new ArrayList<>();
980 Map<String, List<ActionInfo>> egressActionMap = new HashMap<>();
981 vpnExtraRoutes.stream().filter(vpnExtraRoute -> vpnExtraRoute.getNexthopIpList() != null).forEach(
982 vpnExtraRoute -> vpnExtraRoute.getNexthopIpList().forEach(nextHopIp -> {
983 String nextHopPrefixIp;
984 if (isIpv4Address(nextHopIp)) {
985 nextHopPrefixIp = nextHopIp + NwConstants.IPV4PREFIX;
987 nextHopPrefixIp = nextHopIp + NwConstants.IPV6PREFIX;
989 List<String> tepIpAddresses = fibUtil.getNextHopAddresses(rd, nextHopPrefixIp);
990 if (tepIpAddresses.isEmpty()) {
993 // There would be only one nexthop address for a VM ip which would be the tep Ip
994 String tepIp = tepIpAddresses.get(0);
995 AdjacencyResult adjacencyResult = getRemoteNextHopPointer(dpnId, vpnId,
996 vrfEntry.getDestPrefix(), tepIp, TunnelTypeVxlan.class);
997 if (adjacencyResult == null) {
1000 String egressInterface = adjacencyResult.getInterfaceName();
1001 if (!FibUtil.isTunnelInterface(adjacencyResult)) {
1004 Class<? extends TunnelTypeBase> tunnelType =
1005 VpnExtraRouteHelper.getTunnelType(itmManager, egressInterface);
1006 StateTunnelList ifState = null;
1008 ifState = fibUtil.getTunnelState(egressInterface);
1009 if (ifState == null || ifState.getOperState() != TunnelOperStatus.Up) {
1010 LOG.trace("Tunnel is not up for interface {}", egressInterface);
1013 } catch (ReadFailedException e) {
1014 LOG.error("getBucketsForRemoteNexthop: error in fetching tunnel state for interface {}",
1015 egressInterface, e);
1018 if (!TunnelTypeVxlan.class.equals(tunnelType)) {
1021 Uint32 label = FibUtil.getLabelFromRoutePaths(vrfEntry).get();
1022 Prefixes prefixInfo = fibUtil.getPrefixToInterface(vpnId, nextHopPrefixIp);
1023 if (prefixInfo == null) {
1024 LOG.error("No prefix info found for prefix {} in rd {} for VPN {}", nextHopPrefixIp, rd,
1029 if (FibUtil.isVxlanNetwork(prefixInfo.getNetworkType())) {
1030 tunnelId = prefixInfo.getSegmentationId();
1032 LOG.warn("Network is not of type VXLAN for prefix {}."
1033 + "Going with default Lport Tag.", prefixInfo.toString());
1036 List<ActionInfo> actionInfos = new ArrayList<>();
1037 actionInfos.add(new ActionSetFieldTunnelId(Uint64.valueOf(tunnelId.longValue())));
1038 String ifName = prefixInfo.getVpnInterfaceName();
1039 String vpnName = fibUtil.getVpnNameFromId(vpnId);
1040 if (vpnName == null) {
1043 String macAddress = fibUtil.getMacAddressFromPrefix(ifName, vpnName, nextHopPrefixIp);
1044 actionInfos.add(new ActionSetFieldEthernetDestination(actionInfos.size(),
1045 new MacAddress(macAddress)));
1046 List<ActionInfo> egressActions;
1047 if (egressActionMap.containsKey(egressInterface)) {
1048 egressActions = egressActionMap.get(egressInterface);
1050 egressActions = getEgressActionsForInterface(egressInterface, actionInfos.size(),
1051 true, vpnId, vrfEntry.getDestPrefix());
1052 if (egressActions.isEmpty()) {
1053 LOG.error("Skipping getBucketsForRemoteNexthop: Empty list of egress actions received for "
1054 + "interface {} on dpn {} for vpn {} prefix {} nextHop {}", ifName, dpnId,
1055 vpnId, vrfEntry.getDestPrefix(), nextHopPrefixIp);
1057 egressActionMap.put(egressInterface, egressActions);
1059 if (egressActions.isEmpty()) {
1060 LOG.error("Failed to retrieve egress action for prefix {} route-paths {}"
1061 + " interface {}." + " Aborting remote FIB entry creation.",
1062 vrfEntry.getDestPrefix(), vrfEntry.getRoutePaths(), egressInterface);
1064 actionInfos.addAll(egressActions);
1065 BucketInfo bucket = new BucketInfo(actionInfos);
1066 bucket.setWeight(1);
1067 listBucketInfo.add(bucket);
1069 LOG.trace("LOCAL: listbucket {}, rd {}, dpnId {}, routes {}", listBucketInfo, rd, dpnId, vpnExtraRoutes);
1070 return listBucketInfo;
1073 public void createDcGwLoadBalancingGroup(Uint64 dpnId, String destinationIp,
1074 Class<? extends TunnelTypeBase> tunnelType) {
1075 jobCoordinator.enqueueJob(getJobKey(dpnId), () -> {
1076 List<ListenableFuture<Void>> futures = new ArrayList<>();
1077 futures.add(txRunner.callWithNewReadWriteTransactionAndSubmit(OPERATIONAL, operationalTx -> {
1078 synchronized (getDcGateWaySyncKey(destinationIp)) {
1079 FibUtil.addL3vpnDcGateWay(destinationIp, operationalTx);
1081 futures.add(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, configTx -> {
1082 List<String> availableDcGws = getDcGwIps();
1083 Preconditions.checkNotNull(availableDcGws, "There are no dc-gws present");
1084 int noOfDcGws = availableDcGws.size();
1085 if (noOfDcGws == 1) {
1086 LOG.trace("There are no enough DC GateWays {} present to program LB group", availableDcGws);
1089 if (availableDcGws.contains(destinationIp)) {
1090 availableDcGws.remove(destinationIp);
1092 availableDcGws.forEach(dcGwIp -> {
1093 List<String> dcGws = Arrays.asList(dcGwIp, destinationIp);
1094 Collections.sort(dcGws);
1095 String groupIdKey = FibUtil.getGreLbGroupKey(dcGws);
1096 Long groupId = createNextHopPointer(groupIdKey);
1097 List<Bucket> listBucket = new ArrayList<>();
1098 for (int index = 0; index < dcGws.size(); index++) {
1099 if (isTunnelUp(dcGws.get(index), dpnId, tunnelType)) {
1100 listBucket.add(buildBucketForDcGwLbGroup(dcGws.get(index),
1101 dpnId, index, tunnelType, true));
1104 Group group = MDSALUtil.buildGroup(groupId, groupIdKey, GroupTypes.GroupSelect,
1105 MDSALUtil.buildBucketLists(listBucket));
1106 mdsalApiManager.addGroup(configTx, dpnId, group);
1107 FibUtil.updateLbGroupInfo(dpnId, groupIdKey, groupId.toString(), operationalTx);
1108 LOG.trace("LB group {} towards DC-GW installed on dpn {}. Group - {}",
1109 groupIdKey, dpnId, group);
1117 private String getJobKey(Uint64 dpnId) {
1118 return new StringBuilder().append(NEXTHOPMANAGER_JOB_KEY_PREFIX).append(dpnId).toString();
1121 private String getDcGateWaySyncKey(String destinationIp) {
1122 String mutex = new StringBuilder().append("L3vpncDcGateWay").append(destinationIp).toString();
1123 return mutex.intern();
1126 private List<String> getDcGwIps() {
1127 InstanceIdentifier<DcGatewayIpList> dcGatewayIpListid =
1128 InstanceIdentifier.builder(DcGatewayIpList.class).build();
1129 DcGatewayIpList dcGatewayIpListConfig =
1130 MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, dcGatewayIpListid).orNull();
1131 if (dcGatewayIpListConfig == null) {
1132 return Collections.emptyList();
1134 return dcGatewayIpListConfig.getDcGatewayIp()
1136 .filter(dcGwIp -> dcGwIp.getTunnnelType().equals(TunnelTypeMplsOverGre.class))
1137 .map(dcGwIp -> dcGwIp.getIpAddress().stringValue()).sorted()
1141 private boolean isTunnelUp(String dcGwIp, Uint64 dpnId, Class<? extends TunnelTypeBase> tunnelType) {
1142 String tunnelName = getTunnelRemoteNextHopPointer(dpnId, dcGwIp, tunnelType);
1143 if (tunnelName != null) {
1144 InstanceIdentifier<StateTunnelList> tunnelStateId =
1145 InstanceIdentifier.builder(TunnelsState.class).child(
1146 StateTunnelList.class, new StateTunnelListKey(tunnelName)).build();
1147 return MDSALUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, tunnelStateId)
1148 .toJavaUtil().map(StateTunnelList::getOperState)
1149 .orElse(TunnelOperStatus.Down) == TunnelOperStatus.Up;
1154 private List<Action> getEgressActions(String interfaceName, int actionKey) {
1155 List<Action> actions = Collections.emptyList();
1157 GetEgressActionsForInterfaceInputBuilder egressAction =
1158 new GetEgressActionsForInterfaceInputBuilder().setIntfName(interfaceName).setActionKey(actionKey);
1159 Future<RpcResult<GetEgressActionsForInterfaceOutput>> result =
1160 odlInterfaceRpcService.getEgressActionsForInterface(egressAction.build());
1161 RpcResult<GetEgressActionsForInterfaceOutput> rpcResult = result.get();
1162 if (!rpcResult.isSuccessful()) {
1163 LOG.warn("RPC Call to Get egress actions for interface {} returned with Errors {}",
1164 interfaceName, rpcResult.getErrors());
1166 actions = rpcResult.getResult().nonnullAction();
1168 } catch (InterruptedException | ExecutionException e) {
1169 LOG.warn("Exception when egress actions for interface {}", interfaceName, e);
1175 * This method is invoked when the neighbor is removed from DS.
1176 * All the LB groups which point to the given destination will be deleted.
1178 public void removeDcGwLoadBalancingGroup(Uint64 dpnId,
1179 String destinationIp) {
1180 jobCoordinator.enqueueJob(getJobKey(dpnId), () -> {
1181 List<String> availableDcGws = fibUtil.getL3VpnDcGateWays();
1182 if (availableDcGws.contains(destinationIp)) {
1183 availableDcGws.remove(destinationIp);
1185 List<ListenableFuture<Void>> futures = new ArrayList<>();
1186 futures.add(txRunner.callWithNewReadWriteTransactionAndSubmit(OPERATIONAL, operationalTx -> {
1187 futures.add(txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, configTx -> {
1188 availableDcGws.forEach(dcGwIp -> {
1189 List<String> dcGws = Arrays.asList(dcGwIp, destinationIp);
1190 Collections.sort(dcGws);
1191 String nextHopKey = FibUtil.getGreLbGroupKey(dcGws);
1192 Optional<Nexthops> optionalNextHops = fibUtil.getNexthops(nextHopKey);
1193 if (!optionalNextHops.isPresent()) {
1196 Nexthops nexthops = optionalNextHops.get();
1197 final String groupId = nexthops.getGroupId();
1198 final long groupIdValue = Long.parseLong(groupId);
1199 Group group = MDSALUtil.buildGroup(groupIdValue, nextHopKey, GroupTypes.GroupSelect,
1200 MDSALUtil.buildBucketLists(Collections.emptyList()));
1201 LOG.trace("Removed LB group {} on dpn {}", group, dpnId);
1203 mdsalApiManager.removeGroup(configTx, dpnId, group);
1204 } catch (ExecutionException | InterruptedException e) {
1205 LOG.error("Group removal failed for group {} with exception", groupId, e);
1207 removeNextHopPointer(nextHopKey);
1208 FibUtil.removeOrUpdateNextHopInfo(dpnId, nextHopKey, groupId, nexthops, operationalTx);
1210 synchronized (getDcGateWaySyncKey(destinationIp)) {
1211 FibUtil.removeL3vpnDcGateWay(destinationIp, operationalTx);
1220 * This method is invoked when the tunnel status is deleted.
1221 * All the buckets which point to given destination will be marked down.
1223 public void updateDcGwLoadBalancingGroup(Uint64 dpnId, String destinationIp,
1224 boolean isTunnelUp, Class<? extends TunnelTypeBase> tunnelType) {
1225 jobCoordinator.enqueueJob(getJobKey(dpnId), () -> {
1226 List<String> availableDcGws = fibUtil.getL3VpnDcGateWays();
1227 if (availableDcGws.contains(destinationIp)) {
1228 availableDcGws.remove(destinationIp);
1230 List<ListenableFuture<Void>> futures = new ArrayList<>();
1231 futures.add(txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, configTx -> {
1232 availableDcGws.forEach(dcGwIp -> {
1233 List<String> dcGws = Arrays.asList(dcGwIp, destinationIp);
1234 Collections.sort(dcGws);
1235 String nextHopKey = FibUtil.getGreLbGroupKey(dcGws);
1236 int bucketId = dcGws.indexOf(destinationIp);
1237 Optional<Nexthops> optionalNextHops = fibUtil.getNexthops(nextHopKey);
1238 if (!optionalNextHops.isPresent()) {
1241 Nexthops nexthops = optionalNextHops.get();
1242 final String groupId = nexthops.getGroupId();
1243 final long groupIdValue = Long.parseLong(groupId);
1244 Bucket bucket = buildBucketForDcGwLbGroup(destinationIp, dpnId, bucketId, tunnelType, isTunnelUp);
1245 LOG.trace("updated bucket {} to group {} on dpn {}.", bucket, groupId, dpnId);
1247 mdsalApiManager.addBucket(configTx, dpnId, groupIdValue, bucket);
1248 } catch (ExecutionException | InterruptedException e) {
1249 LOG.error("Bucket addition failed for bucket {} with exception", bucketId, e);
1257 private Bucket buildBucketForDcGwLbGroup(String ipAddress, Uint64 dpnId,
1258 int index, Class<? extends TunnelTypeBase> tunnelType, boolean isTunnelUp) {
1259 List<Action> listAction = new ArrayList<>();
1260 // ActionKey 0 goes to mpls label.
1262 listAction.add(new ActionPushMpls().buildAction());
1263 listAction.add(new ActionRegMove(actionKey++, FibConstants.NXM_REG_MAPPING
1264 .get(index), 0, 19).buildAction());
1265 String tunnelInterfaceName = getTunnelInterfaceName(dpnId, IpAddressBuilder.getDefaultInstance(ipAddress),
1267 List<Action> egressActions = getEgressActions(tunnelInterfaceName, actionKey++);
1268 if (!egressActions.isEmpty()) {
1269 listAction.addAll(getEgressActions(tunnelInterfaceName, actionKey++));
1271 // clear off actions if there is no egress actions.
1272 listAction = Collections.emptyList();
1274 long watchPort = MDSALUtil.WATCH_PORT;
1276 watchPort = 0xFFFFFFFEL;
1278 //OVS expects a non-zero weight value for load balancing to happen in select groups
1279 return MDSALUtil.buildBucket(listAction, SELECT_GROUP_WEIGHT, index,
1280 watchPort, MDSALUtil.WATCH_GROUP);
1283 public void programDcGwLoadBalancingGroup(Uint64 dpnId, String destinationIp,
1284 int addRemoveOrUpdate, boolean isTunnelUp,
1285 Class<? extends TunnelTypeBase> tunnelType) {
1286 if (NwConstants.ADD_FLOW == addRemoveOrUpdate) {
1287 createDcGwLoadBalancingGroup(dpnId, destinationIp, tunnelType);
1288 } else if (NwConstants.DEL_FLOW == addRemoveOrUpdate) {
1289 removeDcGwLoadBalancingGroup(dpnId, destinationIp);
1290 } else if (NwConstants.MOD_FLOW == addRemoveOrUpdate) {
1291 updateDcGwLoadBalancingGroup(dpnId, destinationIp, isTunnelUp, tunnelType);