2 * Copyright © 2015, 2017 Ericsson India Global Services Pvt Ltd. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.netvirt.fibmanager;
10 import static org.opendaylight.genius.infra.Datastore.CONFIGURATION;
11 import static org.opendaylight.genius.infra.Datastore.OPERATIONAL;
12 import static org.opendaylight.genius.mdsalutil.NWUtil.isIpv4Address;
14 import com.google.common.base.Optional;
15 import com.google.common.base.Preconditions;
16 import java.math.BigInteger;
17 import java.util.ArrayList;
18 import java.util.Collections;
19 import java.util.HashMap;
20 import java.util.List;
21 import java.util.Locale;
23 import java.util.concurrent.CopyOnWriteArrayList;
24 import java.util.concurrent.ExecutionException;
25 import java.util.concurrent.Future;
26 import javax.annotation.PreDestroy;
27 import javax.inject.Inject;
28 import javax.inject.Singleton;
29 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
30 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
31 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
32 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
33 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
34 import org.opendaylight.genius.interfacemanager.interfaces.IInterfaceManager;
35 import org.opendaylight.genius.itm.globals.ITMConstants;
36 import org.opendaylight.genius.mdsalutil.ActionInfo;
37 import org.opendaylight.genius.mdsalutil.BucketInfo;
38 import org.opendaylight.genius.mdsalutil.GroupEntity;
39 import org.opendaylight.genius.mdsalutil.MDSALUtil;
40 import org.opendaylight.genius.mdsalutil.NwConstants;
41 import org.opendaylight.genius.mdsalutil.actions.ActionGroup;
42 import org.opendaylight.genius.mdsalutil.actions.ActionNxResubmit;
43 import org.opendaylight.genius.mdsalutil.actions.ActionOutput;
44 import org.opendaylight.genius.mdsalutil.actions.ActionPushMpls;
45 import org.opendaylight.genius.mdsalutil.actions.ActionPushVlan;
46 import org.opendaylight.genius.mdsalutil.actions.ActionRegLoad;
47 import org.opendaylight.genius.mdsalutil.actions.ActionRegMove;
48 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldEthernetDestination;
49 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldEthernetSource;
50 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldTunnelId;
51 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldVlanVid;
52 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
53 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
54 import org.opendaylight.infrautils.utils.concurrent.ListenableFutures;
55 import org.opendaylight.netvirt.elanmanager.api.IElanService;
56 import org.opendaylight.netvirt.fibmanager.api.L3VPNTransportTypes;
57 import org.opendaylight.netvirt.vpnmanager.api.VpnExtraRouteHelper;
58 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana._if.type.rev140508.L2vlan;
59 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana._if.type.rev140508.Tunnel;
60 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddressBuilder;
61 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.InterfaceType;
62 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
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.GetEgressActionsForTunnelOutput;
90 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetInternalOrExternalInterfaceNameInputBuilder;
91 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetInternalOrExternalInterfaceNameOutput;
92 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetTunnelInterfaceNameInputBuilder;
93 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetTunnelInterfaceNameOutput;
94 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.ItmRpcService;
95 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.lockmanager.rev160413.LockManagerService;
96 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.AddGroupInput;
97 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.AddGroupInputBuilder;
98 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.AddGroupOutput;
99 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.SalGroupService;
100 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupId;
101 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupRef;
102 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupTypes;
103 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.group.Buckets;
104 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.group.buckets.Bucket;
105 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.Group;
106 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
107 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.SegmentTypeBase;
108 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.SegmentTypeFlat;
109 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.SegmentTypeVlan;
110 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.instances.ElanInstance;
111 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentries.VrfEntry;
112 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.L3nexthop;
113 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.VpnNexthops;
114 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.VpnNexthopsKey;
115 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.vpnnexthops.VpnNexthop;
116 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.vpnnexthops.VpnNexthopBuilder;
117 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.vpnnexthops.VpnNexthopKey;
118 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.vpnnexthops.vpnnexthop.IpAdjacencies;
119 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.vpnnexthops.vpnnexthop.IpAdjacenciesBuilder;
120 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.vpnnexthops.vpnnexthop.IpAdjacenciesKey;
121 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.ConfTransportTypeL3vpn;
122 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.ConfTransportTypeL3vpnBuilder;
123 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.dpid.l3vpn.lb.nexthops.DpnLbNexthops;
124 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.l3vpn.lb.nexthops.Nexthops;
125 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.prefix.to._interface.vpn.ids.Prefixes;
126 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.to.extraroutes.vpn.extra.routes.Routes;
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;
143 private static final int SELECT_GROUP_WEIGHT = 1;
145 private final DataBroker dataBroker;
146 private final ManagedNewTransactionRunner txRunner;
147 private final IMdsalApiManager mdsalApiManager;
148 private final OdlInterfaceRpcService odlInterfaceRpcService;
149 private final ItmRpcService itmManager;
150 private final IdManagerService idManager;
151 private final IElanService elanService;
152 private final LockManagerService lockManager;
153 private final SalGroupService salGroupService;
154 private final JobCoordinator jobCoordinator;
155 private final FibUtil fibUtil;
156 private final IInterfaceManager interfaceManager;
157 private volatile L3VPNTransportTypes configuredTransportTypeL3VPN = L3VPNTransportTypes.Invalid;
160 * Provides nexthop functions.
161 * Creates group ID pool
163 * @param dataBroker - dataBroker reference
164 * @param mdsalApiManager - mdsalApiManager reference
165 * @param idManager - idManager reference
166 * @param odlInterfaceRpcService - odlInterfaceRpcService reference
167 * @param itmManager - itmManager reference
170 public NexthopManager(final DataBroker dataBroker,
171 final IMdsalApiManager mdsalApiManager,
172 final IdManagerService idManager,
173 final OdlInterfaceRpcService odlInterfaceRpcService,
174 final ItmRpcService itmManager,
175 final LockManagerService lockManager,
176 final IElanService elanService,
177 final SalGroupService salGroupService,
178 final JobCoordinator jobCoordinator,
179 final FibUtil fibUtil,
180 final IInterfaceManager interfaceManager) {
181 this.dataBroker = dataBroker;
182 this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
183 this.mdsalApiManager = mdsalApiManager;
184 this.idManager = idManager;
185 this.odlInterfaceRpcService = odlInterfaceRpcService;
186 this.itmManager = itmManager;
187 this.elanService = elanService;
188 this.salGroupService = salGroupService;
189 this.jobCoordinator = jobCoordinator;
190 this.fibUtil = fibUtil;
191 this.lockManager = lockManager;
192 this.interfaceManager = interfaceManager;
196 private void createIdPool() {
197 CreateIdPoolInput createPool = new CreateIdPoolInputBuilder()
198 .setPoolName(NEXTHOP_ID_POOL_NAME)
203 Future<RpcResult<CreateIdPoolOutput>> result = idManager.createIdPool(createPool);
204 if (result != null && result.get().isSuccessful()) {
205 LOG.info("Created IdPool for NextHopPointerPool");
207 } catch (InterruptedException | ExecutionException e) {
208 LOG.error("Failed to create idPool for NextHopPointerPool", e);
212 private String getNextHopKey(long vpnId, String ipAddress) {
213 return "nexthop." + vpnId + ipAddress;
216 String getRemoteSelectGroupKey(long vpnId, String ipAddress) {
217 return "remote.ecmp.nexthop." + vpnId + ipAddress;
220 String getLocalSelectGroupKey(long vpnId, String ipAddress) {
221 return "local.ecmp.nexthop." + vpnId + ipAddress;
224 public ItmRpcService getItmManager() {
228 protected long createNextHopPointer(String nexthopKey) {
229 AllocateIdInput getIdInput = new AllocateIdInputBuilder()
230 .setPoolName(NEXTHOP_ID_POOL_NAME).setIdKey(nexthopKey)
232 //TODO: Proper error handling once IdManager code is complete
234 Future<RpcResult<AllocateIdOutput>> result = idManager.allocateId(getIdInput);
235 RpcResult<AllocateIdOutput> rpcResult = result.get();
236 return rpcResult.getResult().getIdValue();
237 } catch (NullPointerException | InterruptedException | ExecutionException e) {
243 protected void removeNextHopPointer(String nexthopKey) {
244 ReleaseIdInput idInput = new ReleaseIdInputBuilder()
245 .setPoolName(NEXTHOP_ID_POOL_NAME)
246 .setIdKey(nexthopKey).build();
248 RpcResult<ReleaseIdOutput> rpcResult = idManager.releaseId(idInput).get();
249 if (!rpcResult.isSuccessful()) {
250 LOG.error("RPC Call to Get Unique Id for nexthopKey {} returned with Errors {}",
251 nexthopKey, rpcResult.getErrors());
253 } catch (InterruptedException | ExecutionException e) {
254 LOG.warn("Exception when getting Unique Id for key {}", nexthopKey, e);
258 protected List<ActionInfo> getEgressActionsForInterface(final String ifName, int actionKey,
259 boolean isTunnelInterface) {
260 List<Action> actions;
262 if (isTunnelInterface && interfaceManager.isItmDirectTunnelsEnabled()) {
263 RpcResult<GetEgressActionsForTunnelOutput> rpcResult =
264 itmManager.getEgressActionsForTunnel(new GetEgressActionsForTunnelInputBuilder()
265 .setIntfName(ifName).build()).get();
266 if (!rpcResult.isSuccessful()) {
267 LOG.error("RPC Call to Get egress tunnel actions for interface {} returned with Errors {}",
268 ifName, rpcResult.getErrors());
269 return Collections.emptyList();
271 actions = rpcResult.getResult().getAction();
274 RpcResult<GetEgressActionsForInterfaceOutput> rpcResult = odlInterfaceRpcService
275 .getEgressActionsForInterface(new GetEgressActionsForInterfaceInputBuilder()
276 .setIntfName(ifName).build()).get();
277 if (!rpcResult.isSuccessful()) {
278 LOG.error("RPC Call to Get egress vm actions for interface {} returned with Errors {}",
279 ifName, rpcResult.getErrors());
280 return Collections.emptyList();
282 actions = rpcResult.getResult().getAction();
285 List<ActionInfo> listActionInfo = new ArrayList<>();
286 for (Action action : actions) {
287 actionKey = action.key().getOrder() + actionKey;
288 org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action
289 actionClass = action.getAction();
290 if (actionClass instanceof OutputActionCase) {
291 listActionInfo.add(new ActionOutput(actionKey,
292 ((OutputActionCase) actionClass).getOutputAction().getOutputNodeConnector()));
293 } else if (actionClass instanceof PushVlanActionCase) {
294 listActionInfo.add(new ActionPushVlan(actionKey));
295 } else if (actionClass instanceof SetFieldCase) {
296 if (((SetFieldCase) actionClass).getSetField().getVlanMatch() != null) {
297 int vlanVid = ((SetFieldCase) actionClass).getSetField().getVlanMatch()
298 .getVlanId().getVlanId().getValue();
299 listActionInfo.add(new ActionSetFieldVlanVid(actionKey, vlanVid));
301 } else if (actionClass instanceof NxActionResubmitRpcAddGroupCase) {
302 Short tableId = ((NxActionResubmitRpcAddGroupCase) actionClass).getNxResubmit().getTable();
303 listActionInfo.add(new ActionNxResubmit(actionKey, tableId));
304 } else if (actionClass instanceof NxActionRegLoadNodesNodeTableFlowApplyActionsCase) {
305 NxRegLoad nxRegLoad =
306 ((NxActionRegLoadNodesNodeTableFlowApplyActionsCase) actionClass).getNxRegLoad();
307 listActionInfo.add(new ActionRegLoad(actionKey, NxmNxReg6.class,
308 nxRegLoad.getDst().getStart(), nxRegLoad.getDst().getEnd(),
309 nxRegLoad.getValue().longValue()));
312 return listActionInfo;
313 } catch (InterruptedException | ExecutionException e) {
314 LOG.warn("Exception when egress actions for interface {}", ifName, e);
316 LOG.warn("Exception when egress actions for interface {}", ifName);
317 return Collections.emptyList();
320 protected String getTunnelInterfaceName(BigInteger srcDpId, BigInteger dstDpId) {
321 Class<? extends TunnelTypeBase> tunType = getReqTunType(getReqTransType().toUpperCase(Locale.getDefault()));
322 Future<RpcResult<GetTunnelInterfaceNameOutput>> result;
324 result = itmManager.getTunnelInterfaceName(new GetTunnelInterfaceNameInputBuilder()
325 .setSourceDpid(srcDpId)
326 .setDestinationDpid(dstDpId)
327 .setTunnelType(tunType)
329 RpcResult<GetTunnelInterfaceNameOutput> rpcResult = result.get();
330 if (!rpcResult.isSuccessful()) {
331 LOG.warn("RPC Call to getTunnelInterfaceId returned with Errors {}", rpcResult.getErrors());
333 return rpcResult.getResult().getInterfaceName();
335 } catch (InterruptedException | ExecutionException e) {
336 LOG.warn("Exception when getting tunnel interface Id for tunnel between {} and {}", srcDpId, dstDpId, e);
341 protected String getTunnelInterfaceName(BigInteger srcDpId, org.opendaylight.yang.gen.v1.urn.ietf.params
342 .xml.ns.yang.ietf.inet.types.rev130715.IpAddress dstIp, Class<? extends TunnelTypeBase> tunnelType) {
343 Future<RpcResult<GetInternalOrExternalInterfaceNameOutput>> result;
345 LOG.debug("Trying to fetch tunnel interface name for source dpn {} destIp {} tunType {}", srcDpId,
346 dstIp.stringValue(), tunnelType.getName());
347 result = itmManager.getInternalOrExternalInterfaceName(new GetInternalOrExternalInterfaceNameInputBuilder()
348 .setSourceDpid(srcDpId)
349 .setDestinationIp(dstIp)
350 .setTunnelType(tunnelType)
352 RpcResult<GetInternalOrExternalInterfaceNameOutput> rpcResult = result.get();
353 if (!rpcResult.isSuccessful()) {
354 LOG.warn("RPC Call to getTunnelInterfaceName returned with Errors {}", rpcResult.getErrors());
356 return rpcResult.getResult().getInterfaceName();
358 } catch (InterruptedException | ExecutionException e) {
359 LOG.warn("Exception when getting tunnel interface Id for tunnel between {} and {}", srcDpId, dstIp, e);
365 public long getLocalNextHopGroup(long vpnId,
366 String ipNextHopAddress) {
367 long groupId = createNextHopPointer(getNextHopKey(vpnId, ipNextHopAddress));
368 if (groupId == FibConstants.INVALID_GROUP_ID) {
369 LOG.error("Unable to allocate groupId for vpnId {} , prefix {}", vpnId, ipNextHopAddress);
374 public long getLocalSelectGroup(long vpnId,
375 String ipNextHopAddress) {
376 long groupId = createNextHopPointer(getLocalSelectGroupKey(vpnId, ipNextHopAddress));
377 if (groupId == FibConstants.INVALID_GROUP_ID) {
378 LOG.error("Unable to allocate groupId for vpnId {} , prefix {}", vpnId, ipNextHopAddress);
383 public long createLocalNextHop(long vpnId, BigInteger dpnId, String ifName,
384 String primaryIpAddress, String currDestIpPrefix,
385 String gwMacAddress) {
386 String vpnName = fibUtil.getVpnNameFromId(vpnId);
387 if (vpnName == null) {
390 String macAddress = fibUtil.getMacAddressFromPrefix(ifName, vpnName, primaryIpAddress);
392 long groupId = createNextHopPointer(getNextHopKey(vpnId, primaryIpAddress));
394 LOG.error("Unable to allocate groupId for vpnId {} , IntfName {}, primaryIpAddress {} curIpPrefix {}",
395 vpnId, ifName, primaryIpAddress, currDestIpPrefix);
398 String nextHopLockStr = vpnId + primaryIpAddress;
399 String jobKey = FibUtil.getCreateLocalNextHopJobKey(vpnId, dpnId, currDestIpPrefix);
400 jobCoordinator.enqueueJob(jobKey, () -> {
402 if (FibUtil.lockCluster(lockManager, nextHopLockStr, WAIT_TIME_TO_ACQUIRE_LOCK)) {
403 VpnNexthop nexthop = getVpnNexthop(vpnId, primaryIpAddress);
404 LOG.trace("nexthop: {} retrieved for vpnId {}, prefix {}, ifName {} on dpn {}", nexthop, vpnId,
405 primaryIpAddress, ifName, dpnId);
406 if (nexthop == null) {
407 String encMacAddress = macAddress == null
408 ? fibUtil.getMacAddressFromPrefix(ifName, vpnName, primaryIpAddress) : macAddress;
409 List<BucketInfo> listBucketInfo = new ArrayList<>();
410 List<ActionInfo> listActionInfo = new ArrayList<>();
413 if (encMacAddress != null) {
414 if (gwMacAddress != null) {
415 LOG.trace("The Local NextHop Group Source Mac {} for VpnInterface {} on VPN {}",
416 gwMacAddress, ifName, vpnId);
417 listActionInfo.add(new ActionSetFieldEthernetSource(actionKey++,
418 new MacAddress(gwMacAddress)));
420 listActionInfo.add(new ActionSetFieldEthernetDestination(actionKey++,
421 new MacAddress(encMacAddress)));
422 // listActionInfo.add(0, new ActionPopMpls());
424 // FIXME: Log message here.
425 LOG.debug("mac address for new local nexthop is null");
427 listActionInfo.addAll(getEgressActionsForInterface(ifName, actionKey, false));
428 BucketInfo bucket = new BucketInfo(listActionInfo);
430 listBucketInfo.add(bucket);
431 GroupEntity groupEntity = MDSALUtil.buildGroupEntity(dpnId, groupId, primaryIpAddress,
432 GroupTypes.GroupAll, listBucketInfo);
433 LOG.trace("Install LNH Group: id {}, mac address {}, interface {} for prefix {}", groupId,
434 encMacAddress, ifName, primaryIpAddress);
435 //Try to install group directly on the DPN bypassing the FRM, in order to avoid waiting for the
436 // group to get installed before programming the flows
437 installGroupOnDpn(groupId, dpnId, primaryIpAddress, listBucketInfo,
438 getNextHopKey(vpnId, primaryIpAddress), GroupTypes.GroupAll);
440 mdsalApiManager.syncInstallGroup(groupEntity);
442 addVpnNexthopToDS(dpnId, vpnId, primaryIpAddress, currDestIpPrefix, groupId);
445 // Ignore adding new prefix , if it already exists
446 List<IpAdjacencies> prefixesList = nexthop.getIpAdjacencies();
447 IpAdjacencies prefix = new IpAdjacenciesBuilder().setIpAdjacency(currDestIpPrefix).build();
448 if (prefixesList != null && prefixesList.contains(prefix)) {
449 LOG.trace("Prefix {} is already present in l3nextHop {} ", currDestIpPrefix, nexthop);
451 IpAdjacenciesBuilder ipPrefixesBuilder =
452 new IpAdjacenciesBuilder().withKey(new IpAdjacenciesKey(currDestIpPrefix));
453 LOG.trace("Updating prefix {} to vpnNextHop {} Operational DS", currDestIpPrefix, nexthop);
454 MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL,
455 getVpnNextHopIpPrefixIdentifier(vpnId, primaryIpAddress, currDestIpPrefix),
456 ipPrefixesBuilder.build());
461 FibUtil.unlockCluster(lockManager, nextHopLockStr);
463 return Collections.emptyList();
468 private void installGroupOnDpn(long groupId, BigInteger dpnId, String groupName, List<BucketInfo> bucketsInfo,
469 String nextHopKey, GroupTypes groupType) {
470 NodeRef nodeRef = FibUtil.buildNodeRef(dpnId);
471 Buckets buckets = FibUtil.buildBuckets(bucketsInfo);
472 GroupRef groupRef = new GroupRef(FibUtil.buildGroupInstanceIdentifier(groupId, dpnId));
473 AddGroupInput input = new AddGroupInputBuilder().setNode(nodeRef).setGroupId(new GroupId(groupId))
474 .setBuckets(buckets).setGroupRef(groupRef).setGroupType(groupType)
475 .setGroupName(groupName).build();
476 Future<RpcResult<AddGroupOutput>> groupStats = salGroupService.addGroup(input);
477 RpcResult<AddGroupOutput> rpcResult = null;
479 rpcResult = groupStats.get();
480 if (rpcResult != null && rpcResult.isSuccessful()) {
481 LOG.info("Group {} with key {} has been successfully installed directly on dpn {}.", groupId,
484 LOG.error("Unable to install group {} with key {} directly on dpn {} due to {}.", groupId, nextHopKey,
485 dpnId, rpcResult != null ? rpcResult.getErrors() : null);
487 } catch (InterruptedException | ExecutionException e) {
488 LOG.error("Error while installing group {} directly on dpn {}", groupId, dpnId);
492 protected void addVpnNexthopToDS(BigInteger dpnId, long vpnId, String primaryIpAddr,
493 String currIpAddr, long egressPointer) {
494 InstanceIdentifierBuilder<VpnNexthops> idBuilder = InstanceIdentifier.builder(L3nexthop.class)
495 .child(VpnNexthops.class, new VpnNexthopsKey(vpnId));
497 List<IpAdjacencies> ipPrefixesList = new ArrayList<>();
498 IpAdjacencies prefix = new IpAdjacenciesBuilder().setIpAdjacency(currIpAddr).build();
499 ipPrefixesList.add(prefix);
500 // Add nexthop to vpn node
501 VpnNexthop nh = new VpnNexthopBuilder()
502 .withKey(new VpnNexthopKey(primaryIpAddr))
504 .setIpAdjacencies(ipPrefixesList)
505 .setEgressPointer(egressPointer).build();
507 InstanceIdentifier<VpnNexthop> id1 = idBuilder
508 .child(VpnNexthop.class, new VpnNexthopKey(primaryIpAddr)).build();
509 LOG.trace("Adding vpnnextHop {} to Operational DS", nh);
510 MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL, id1, nh);
514 protected InstanceIdentifier<IpAdjacencies> getVpnNextHopIpPrefixIdentifier(long vpnId, String primaryIpAddress,
516 InstanceIdentifier<IpAdjacencies> id = InstanceIdentifier.builder(L3nexthop.class)
517 .child(VpnNexthops.class, new VpnNexthopsKey(vpnId))
518 .child(VpnNexthop.class, new VpnNexthopKey(primaryIpAddress))
519 .child(IpAdjacencies.class, new IpAdjacenciesKey(ipPrefix)).build();
523 protected VpnNexthop getVpnNexthop(long vpnId, String ipAddress) {
525 // check if vpn node is there
526 InstanceIdentifierBuilder<VpnNexthops> idBuilder =
527 InstanceIdentifier.builder(L3nexthop.class).child(VpnNexthops.class,
528 new VpnNexthopsKey(vpnId));
529 InstanceIdentifier<VpnNexthops> id = idBuilder.build();
530 Optional<VpnNexthops> vpnNexthops = MDSALUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, id);
531 if (vpnNexthops.isPresent()) {
532 // get nexthops list for vpn
533 List<VpnNexthop> nexthops = vpnNexthops.get().getVpnNexthop();
534 for (VpnNexthop nexthop : nexthops) {
535 if (nexthop.getIpAddress().equals(ipAddress)) {
537 LOG.trace("VpnNextHop : {}", nexthop);
541 // return null if not found
546 public AdjacencyResult getRemoteNextHopPointer(BigInteger remoteDpnId, long vpnId, String prefixIp,
547 String nextHopIp, Class<? extends TunnelTypeBase> tunnelType) {
548 String egressIfName = null;
549 LOG.trace("getRemoteNextHopPointer: input [remoteDpnId {}, vpnId {}, prefixIp {}, nextHopIp {} ]", remoteDpnId,
550 vpnId, prefixIp, nextHopIp);
552 Class<? extends InterfaceType> egressIfType;
553 ElanInstance elanInstance = getElanInstanceForPrefix(vpnId, prefixIp);
554 if (elanInstance != null) {
555 egressIfType = getInterfaceType(elanInstance);
557 LOG.warn("Failed to determine network type for prefixIp {} using tunnel", prefixIp);
558 egressIfType = Tunnel.class;
561 if (Tunnel.class.equals(egressIfType)) {
562 egressIfName = getTunnelRemoteNextHopPointer(remoteDpnId, nextHopIp, tunnelType);
564 egressIfName = getExtPortRemoteNextHopPointer(remoteDpnId, elanInstance);
567 LOG.trace("NextHop pointer for prefixIp {} vpnId {} dpnId {} is {}", prefixIp, vpnId, remoteDpnId,
569 return egressIfName != null ? new AdjacencyResult(egressIfName, egressIfType, nextHopIp,
573 public BigInteger getDpnForPrefix(long vpnId, String prefixIp) {
574 VpnNexthop vpnNexthop = getVpnNexthop(vpnId, prefixIp);
575 BigInteger localDpnId = vpnNexthop == null ? null : vpnNexthop.getDpnId();
579 private void removeVpnNexthopFromDS(long vpnId, String ipPrefix) {
581 InstanceIdentifierBuilder<VpnNexthop> idBuilder = InstanceIdentifier.builder(L3nexthop.class)
582 .child(VpnNexthops.class, new VpnNexthopsKey(vpnId))
583 .child(VpnNexthop.class, new VpnNexthopKey(ipPrefix));
584 InstanceIdentifier<VpnNexthop> id = idBuilder.build();
586 LOG.trace("Removing vpn next hop from datastore : {}", id);
587 MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.OPERATIONAL, id);
590 public void removeLocalNextHop(BigInteger dpnId, Long vpnId, String primaryIpAddress, String currDestIpPrefix) {
591 String nextHopLockStr = vpnId + primaryIpAddress;
593 if (FibUtil.lockCluster(lockManager, nextHopLockStr, WAIT_TIME_TO_ACQUIRE_LOCK)) {
594 VpnNexthop nh = getVpnNexthop(vpnId, primaryIpAddress);
596 List<IpAdjacencies> prefixesList = nh.getIpAdjacencies();
597 IpAdjacencies prefix = new IpAdjacenciesBuilder().setIpAdjacency(currDestIpPrefix).build();
598 prefixesList.remove(prefix);
599 if (prefixesList.isEmpty()) { //remove the group only if there are no more flows using this group
600 GroupEntity groupEntity = MDSALUtil.buildGroupEntity(dpnId, nh.getEgressPointer(),
601 primaryIpAddress, GroupTypes.GroupAll, Collections.EMPTY_LIST);
603 mdsalApiManager.removeGroup(groupEntity);
605 removeVpnNexthopFromDS(vpnId, primaryIpAddress);
607 removeNextHopPointer(getNextHopKey(vpnId, primaryIpAddress));
608 LOG.debug("Local Next hop {} for {} {} on dpn {} successfully deleted",
609 nh.getEgressPointer(), vpnId, primaryIpAddress, dpnId);
611 //remove the currIpPrefx from IpPrefixList of the vpnNexthop
612 LOG.trace("Removing the prefix {} from vpnNextHop {} Operational DS", currDestIpPrefix, nh);
613 MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.OPERATIONAL,
614 getVpnNextHopIpPrefixIdentifier(vpnId, primaryIpAddress, currDestIpPrefix));
618 LOG.error("Local NextHop for VpnId {} curIpPrefix {} on dpn {} primaryIpAddress {} not deleted",
619 vpnId, currDestIpPrefix, dpnId, primaryIpAddress);
623 FibUtil.unlockCluster(lockManager, nextHopLockStr);
627 public void setConfTransType(String service, String transportType) {
629 if (!service.equalsIgnoreCase("L3VPN")) {
630 LOG.error("Incorrect service {} provided for setting the transport type.", service);
634 L3VPNTransportTypes transType = L3VPNTransportTypes.validateTransportType(transportType
635 .toUpperCase(Locale.getDefault()));
637 if (transType != L3VPNTransportTypes.Invalid) {
638 configuredTransportTypeL3VPN = transType;
642 public void writeConfTransTypeConfigDS() {
643 MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, getConfTransportTypeIdentifier(),
644 createConfTransportType(configuredTransportTypeL3VPN.getTransportType()));
647 public L3VPNTransportTypes getConfiguredTransportTypeL3VPN() {
648 return this.configuredTransportTypeL3VPN;
651 public String getReqTransType() {
652 if (configuredTransportTypeL3VPN == L3VPNTransportTypes.Invalid) {
654 * Restart scenario, Read from the ConfigDS.
655 * if the value is Unset, cache value as VxLAN.
657 LOG.trace("configureTransportType is not yet set.");
658 Optional<ConfTransportTypeL3vpn> configuredTransTypeFromConfig =
659 MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, getConfTransportTypeIdentifier());
661 if (configuredTransTypeFromConfig.isPresent()) {
662 if (configuredTransTypeFromConfig.get().getTransportType().equals(TunnelTypeGre.class)) {
663 configuredTransportTypeL3VPN = L3VPNTransportTypes.GRE;
665 configuredTransportTypeL3VPN = L3VPNTransportTypes.VxLAN;
667 LOG.trace("configuredTransportType set from config DS to {}",
668 getConfiguredTransportTypeL3VPN().getTransportType());
670 setConfTransType("L3VPN", L3VPNTransportTypes.VxLAN.getTransportType());
671 LOG.trace("configuredTransportType is not set in the Config DS. VxLAN as default will be used.");
674 LOG.trace("configuredTransportType is set as {}", getConfiguredTransportTypeL3VPN().getTransportType());
676 return getConfiguredTransportTypeL3VPN().getTransportType();
679 public InstanceIdentifier<ConfTransportTypeL3vpn> getConfTransportTypeIdentifier() {
680 return InstanceIdentifier.builder(ConfTransportTypeL3vpn.class).build();
683 private ConfTransportTypeL3vpn createConfTransportType(String type) {
684 ConfTransportTypeL3vpn confTransType;
686 case ITMConstants.TUNNEL_TYPE_GRE:
687 confTransType = new ConfTransportTypeL3vpnBuilder().setTransportType(TunnelTypeGre.class).build();
688 LOG.trace("Setting the confTransportType to GRE.");
690 case ITMConstants.TUNNEL_TYPE_VXLAN:
691 confTransType = new ConfTransportTypeL3vpnBuilder().setTransportType(TunnelTypeVxlan.class).build();
692 LOG.trace("Setting the confTransportType to VxLAN.");
695 LOG.trace("Invalid transport type {} passed to Config DS ", type);
696 confTransType = null;
699 return confTransType;
702 public Class<? extends TunnelTypeBase> getReqTunType(String transportType) {
703 switch (transportType) {
705 return TunnelTypeVxlan.class;
707 return TunnelTypeGre.class;
709 return TunnelTypeMplsOverGre.class;
713 public String getTransportTypeStr(String tunType) {
714 if (tunType.equals(TunnelTypeVxlan.class.toString())) {
715 return ITMConstants.TUNNEL_TYPE_VXLAN;
716 } else if (tunType.equals(TunnelTypeGre.class.toString())) {
717 return ITMConstants.TUNNEL_TYPE_GRE;
718 } else if (tunType.equals(TunnelTypeMplsOverGre.class.toString())) {
719 return ITMConstants.TUNNEL_TYPE_MPLSoGRE;
721 return ITMConstants.TUNNEL_TYPE_INVALID;
727 public void close() {
728 LOG.info("{} close", getClass().getSimpleName());
731 // TODO Clean up the exception handling
732 @SuppressWarnings("checkstyle:IllegalCatch")
733 private String getTunnelRemoteNextHopPointer(BigInteger remoteDpnId, String nextHopIp,
734 Class<? extends TunnelTypeBase> tunnelType) {
735 if (nextHopIp != null && !nextHopIp.isEmpty()) {
737 // here use the config for tunnel type param
738 return getTunnelInterfaceName(remoteDpnId,
739 org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddressBuilder
740 .getDefaultInstance(nextHopIp), tunnelType);
741 } catch (Exception ex) {
742 LOG.error("Error while retrieving nexthop pointer for nexthop {} remoteDpn {}",
743 nextHopIp, remoteDpnId, ex);
750 private String getExtPortRemoteNextHopPointer(BigInteger remoteDpnId, ElanInstance elanInstance) {
751 return elanService.getExternalElanInterface(elanInstance.getElanInstanceName(), remoteDpnId);
755 * Get the interface type associated with the type of ELAN used for routing
756 * traffic to/from remote compute nodes.
758 * @param elanInstance The elan instance
759 * @return L2vlan for flat/VLAN network type and Tunnel otherwise
761 private Class<? extends InterfaceType> getInterfaceType(ElanInstance elanInstance) {
762 Class<? extends SegmentTypeBase> segmentType = elanInstance.getSegmentType();
763 if (SegmentTypeFlat.class.equals(segmentType) || SegmentTypeVlan.class.equals(segmentType)) {
770 private ElanInstance getElanInstanceForPrefix(long vpnId, String prefixIp) {
771 ElanInstance elanInstance = null;
772 Prefixes prefix = fibUtil.getPrefixToInterface(vpnId, prefixIp);
773 if (prefix != null) {
774 if (prefix.getNetworkId() != null) {
775 elanInstance = elanService.getElanInstance(prefix.getNetworkId().getValue());
782 static class AdjacencyResult {
783 private final String interfaceName;
784 private final Class<? extends InterfaceType> interfaceType;
785 private final String nextHopIp;
786 private final String prefix;
788 AdjacencyResult(String interfaceName, Class<? extends InterfaceType> interfaceType, String nextHopIp,
790 this.interfaceName = interfaceName;
791 this.interfaceType = interfaceType;
792 this.nextHopIp = nextHopIp;
793 this.prefix = prefix;
796 public String getInterfaceName() {
797 return interfaceName;
800 public Class<? extends InterfaceType> getInterfaceType() {
801 return interfaceType;
804 public String getNextHopIp() {
808 public String getPrefix() {
813 public int hashCode() {
814 final int prime = 31;
816 result = prime * result + (interfaceName == null ? 0 : interfaceName.hashCode());
821 public boolean equals(Object obj) {
826 boolean result = false;
827 if (getClass() != obj.getClass()) {
830 AdjacencyResult other = (AdjacencyResult) obj;
831 result = interfaceName.equals(other.interfaceName);
837 protected long setupLoadBalancingNextHop(Long parentVpnId, BigInteger dpnId,
838 String destPrefix, List<BucketInfo> localBucketInfo, List<BucketInfo> remoteBucketInfo) {
839 long remoteGroupId = createNextHopPointer(getRemoteSelectGroupKey(parentVpnId, destPrefix));
840 if (remoteGroupId == FibConstants.INVALID_GROUP_ID) {
841 LOG.error("Unable to allocate/retrieve remote groupId for vpnId {} , prefix {}", parentVpnId, destPrefix);
842 return remoteGroupId;
844 long localGroupId = createNextHopPointer(getLocalSelectGroupKey(parentVpnId, destPrefix));
845 if (localGroupId == FibConstants.INVALID_GROUP_ID) {
846 LOG.error("Unable to allocate/retrieve local groupId for vpnId {} , prefix {}",
847 parentVpnId, destPrefix);
848 return remoteGroupId;
850 List<BucketInfo> combinedBucketInfo = new ArrayList<>();
851 combinedBucketInfo.addAll(localBucketInfo);
852 combinedBucketInfo.addAll(remoteBucketInfo);
853 GroupEntity remoteGroupEntity = MDSALUtil.buildGroupEntity(
854 dpnId, remoteGroupId, destPrefix, GroupTypes.GroupSelect, combinedBucketInfo);
855 GroupEntity localGroupEntity = MDSALUtil.buildGroupEntity(
856 dpnId, localGroupId, destPrefix, GroupTypes.GroupSelect, localBucketInfo);
857 String jobKey = FibUtil.getCreateLocalNextHopJobKey(parentVpnId, dpnId, destPrefix);
858 jobCoordinator.enqueueJob(jobKey, () -> {
859 mdsalApiManager.syncInstallGroup(remoteGroupEntity);
860 if (!localBucketInfo.isEmpty()) {
861 mdsalApiManager.syncInstallGroup(localGroupEntity);
863 if (LOG.isDebugEnabled()) {
864 LOG.debug("Finished installing GroupEntity with jobCoordinator key {} remoteGroupEntity.groupId {}"
865 + "localGroupEntity.groupId {} groupEntity.groupType {}", jobKey,
866 remoteGroupEntity.getGroupId(), localGroupEntity.getGroupId(),
867 remoteGroupEntity.getGroupType());
869 // Delete local group(if exists) if there is no local info or no remote info.
870 // Local group has to be deleted if all VMs in a compute is deleted.
871 // Local group(=remote group) is not required if all next hops are present in the same compute.
872 if (localBucketInfo.isEmpty() ^ remoteBucketInfo.isEmpty()) {
873 LOG.debug("Deleting local group {} since no local nhs present for "
874 + "prefix {}", localGroupEntity.getGroupId(), destPrefix);
875 mdsalApiManager.syncRemoveGroup(localGroupEntity);
877 return Collections.emptyList();
879 return remoteGroupId;
882 protected void deleteLoadBalancingNextHop(Long parentVpnId, BigInteger dpnId, String destPrefix) {
883 long remoteGroupId = createNextHopPointer(getRemoteSelectGroupKey(parentVpnId, destPrefix));
884 if (remoteGroupId == FibConstants.INVALID_GROUP_ID) {
885 LOG.error("Unable to allocate/retrieve remote groupId for vpnId {} , prefix {}", parentVpnId, destPrefix);
887 long localGroupId = createNextHopPointer(getLocalSelectGroupKey(parentVpnId, destPrefix));
888 if (localGroupId == FibConstants.INVALID_GROUP_ID) {
889 LOG.error("Unable to allocate/retrieve local groupId for vpnId {} , prefix {}", parentVpnId, destPrefix);
891 GroupEntity remoteGroupEntity = MDSALUtil.buildGroupEntity(
892 dpnId, remoteGroupId, destPrefix, GroupTypes.GroupSelect, Collections.emptyList());
893 GroupEntity localGroupEntity = MDSALUtil.buildGroupEntity(
894 dpnId, localGroupId, destPrefix, GroupTypes.GroupSelect, Collections.emptyList());
895 String jobKey = FibUtil.getCreateLocalNextHopJobKey(parentVpnId, dpnId, destPrefix);
896 jobCoordinator.enqueueJob(jobKey, () -> {
897 mdsalApiManager.syncRemoveGroup(remoteGroupEntity);
898 mdsalApiManager.syncRemoveGroup(localGroupEntity);
899 if (LOG.isDebugEnabled()) {
900 LOG.debug("Finished removing GroupEntity with jobCoordinator key {} remoteGroupEntity.groupId {}"
901 + "localGroupEntity.groupId {}", jobKey, remoteGroupId, localGroupId);
903 return Collections.emptyList();
907 long createNextHopGroups(Long vpnId, String rd, BigInteger dpnId, VrfEntry vrfEntry,
908 Routes routes, List<Routes> vpnExtraRoutes) {
909 List<BucketInfo> localBucketInfo = new ArrayList<>();
910 List<Routes> clonedVpnExtraRoutes = new ArrayList<>(vpnExtraRoutes);
911 if (clonedVpnExtraRoutes.contains(routes)) {
912 localBucketInfo.addAll(getBucketsForLocalNexthop(vpnId, dpnId, vrfEntry, routes));
913 clonedVpnExtraRoutes.remove(routes);
915 List<BucketInfo> remoteBucketInfo = new ArrayList<>();
916 remoteBucketInfo.addAll(getBucketsForRemoteNexthop(vpnId, dpnId, vrfEntry, rd, clonedVpnExtraRoutes));
917 return setupLoadBalancingNextHop(vpnId, dpnId,
918 vrfEntry.getDestPrefix(), localBucketInfo, remoteBucketInfo);
921 private List<BucketInfo> getBucketsForLocalNexthop(Long vpnId, BigInteger dpnId,
922 VrfEntry vrfEntry, Routes routes) {
923 if (LOG.isDebugEnabled()) {
924 LOG.debug("NexthopManager.getBucketsForLocalNexthop invoked with vpnId {} dpnId {} "
925 + " vrfEntry.routePaths {}, routes.nexthopList {}", vpnId, dpnId, vrfEntry.getRoutePaths(),
926 routes.getNexthopIpList());
928 List<BucketInfo> listBucketInfo = new CopyOnWriteArrayList<>();
929 routes.getNexthopIpList().parallelStream().forEach(nextHopIp -> {
930 String localNextHopIP;
931 if (isIpv4Address(nextHopIp)) {
932 localNextHopIP = nextHopIp + NwConstants.IPV4PREFIX;
934 localNextHopIP = nextHopIp + NwConstants.IPV6PREFIX;
936 Prefixes localNextHopInfo = fibUtil.getPrefixToInterface(vpnId, localNextHopIP);
937 if (localNextHopInfo != null) {
938 long groupId = getLocalNextHopGroup(vpnId, localNextHopIP);
939 if (groupId == FibConstants.INVALID_GROUP_ID) {
940 LOG.error("Unable to allocate groupId for vpnId {} , prefix {} , interface {}", vpnId,
941 vrfEntry.getDestPrefix(), localNextHopInfo.getVpnInterfaceName());
944 List<ActionInfo> actionsInfos =
945 Collections.singletonList(new ActionGroup(groupId));
946 BucketInfo bucket = new BucketInfo(actionsInfos);
948 listBucketInfo.add(bucket);
951 LOG.trace("LOCAL: listbucket {}, vpnId {}, dpnId {}, routes {}", listBucketInfo, vpnId, dpnId, routes);
952 return listBucketInfo;
955 private List<BucketInfo> getBucketsForRemoteNexthop(Long vpnId, BigInteger dpnId, VrfEntry vrfEntry, String rd,
956 List<Routes> vpnExtraRoutes) {
957 List<BucketInfo> listBucketInfo = new ArrayList<>();
958 Map<String, List<ActionInfo>> egressActionMap = new HashMap<>();
959 vpnExtraRoutes.forEach(vpnExtraRoute -> vpnExtraRoute.getNexthopIpList().forEach(nextHopIp -> {
960 String nextHopPrefixIp;
961 if (isIpv4Address(nextHopIp)) {
962 nextHopPrefixIp = nextHopIp + NwConstants.IPV4PREFIX;
964 nextHopPrefixIp = nextHopIp + NwConstants.IPV6PREFIX;
966 List<String> tepIpAddresses = fibUtil.getNextHopAddresses(rd, nextHopPrefixIp);
967 if (tepIpAddresses.isEmpty()) {
970 // There would be only one nexthop address for a VM ip which would be the tep Ip
971 String tepIp = tepIpAddresses.get(0);
972 AdjacencyResult adjacencyResult = getRemoteNextHopPointer(dpnId, vpnId,
973 vrfEntry.getDestPrefix(), tepIp, TunnelTypeVxlan.class);
974 if (adjacencyResult == null) {
977 String egressInterface = adjacencyResult.getInterfaceName();
978 if (!FibUtil.isTunnelInterface(adjacencyResult)) {
981 Class<? extends TunnelTypeBase> tunnelType = VpnExtraRouteHelper.getTunnelType(itmManager, egressInterface);
982 StateTunnelList ifState = null;
984 ifState = fibUtil.getTunnelState(egressInterface);
985 if (ifState == null || ifState.getOperState() != TunnelOperStatus.Up) {
986 LOG.trace("Tunnel is not up for interface {}", egressInterface);
989 } catch (ReadFailedException e) {
990 LOG.error("error in fetching tunnel state for interface {}", egressInterface, e);
992 if (!TunnelTypeVxlan.class.equals(tunnelType)) {
995 Long label = FibUtil.getLabelFromRoutePaths(vrfEntry).get();
996 Prefixes prefixInfo = fibUtil.getPrefixToInterface(vpnId, nextHopPrefixIp);
997 if (prefixInfo == null) {
998 LOG.error("No prefix info found for prefix {} in rd {} for VPN {}", nextHopPrefixIp, rd,
1002 BigInteger tunnelId;
1003 if (FibUtil.isVxlanNetwork(prefixInfo.getNetworkType())) {
1004 tunnelId = BigInteger.valueOf(prefixInfo.getSegmentationId());
1006 LOG.warn("Network is not of type VXLAN for prefix {}."
1007 + "Going with default Lport Tag.", prefixInfo.toString());
1008 tunnelId = BigInteger.valueOf(label);
1010 List<ActionInfo> actionInfos = new ArrayList<>();
1011 actionInfos.add(new ActionSetFieldTunnelId(tunnelId));
1012 String ifName = prefixInfo.getVpnInterfaceName();
1013 String vpnName = fibUtil.getVpnNameFromId(vpnId);
1014 if (vpnName == null) {
1017 String macAddress = fibUtil.getMacAddressFromPrefix(ifName, vpnName, nextHopPrefixIp);
1018 actionInfos.add(new ActionSetFieldEthernetDestination(actionInfos.size(),
1019 new MacAddress(macAddress)));
1020 List<ActionInfo> egressActions;
1021 if (egressActionMap.containsKey(egressInterface)) {
1022 egressActions = egressActionMap.get(egressInterface);
1024 egressActions = getEgressActionsForInterface(egressInterface, actionInfos.size(), true);
1025 egressActionMap.put(egressInterface, egressActions);
1027 if (egressActions.isEmpty()) {
1028 LOG.error("Failed to retrieve egress action for prefix {} route-paths {}"
1029 + " interface {}." + " Aborting remote FIB entry creation.",
1030 vrfEntry.getDestPrefix(), vrfEntry.getRoutePaths(), egressInterface);
1032 actionInfos.addAll(egressActions);
1033 BucketInfo bucket = new BucketInfo(actionInfos);
1034 bucket.setWeight(1);
1035 listBucketInfo.add(bucket);
1037 LOG.trace("LOCAL: listbucket {}, rd {}, dpnId {}, routes {}", listBucketInfo, rd, dpnId, vpnExtraRoutes);
1038 return listBucketInfo;
1041 public void createDcGwLoadBalancingGroup(List<String> availableDcGws, BigInteger dpnId, String destinationIp,
1042 Class<? extends TunnelTypeBase> tunnelType) {
1043 Preconditions.checkNotNull(availableDcGws, "There are no dc-gws present");
1044 int noOfDcGws = availableDcGws.size();
1045 if (noOfDcGws == 1) {
1046 LOG.trace("There are no enough DC GateWays {} present to program LB group", availableDcGws);
1049 // TODO : Place the logic to construct all possible DC-GW combination here.
1050 String groupIdKey = FibUtil.getGreLbGroupKey(availableDcGws);
1051 Long groupId = createNextHopPointer(groupIdKey);
1052 List<Bucket> listBucket = new ArrayList<>();
1053 for (int index = 0; index < noOfDcGws; index++) {
1054 if (isTunnelUp(availableDcGws.get(index), dpnId, tunnelType)) {
1055 listBucket.add(buildBucketForDcGwLbGroup(availableDcGws.get(index), dpnId, index, tunnelType));
1058 Group group = MDSALUtil.buildGroup(groupId, groupIdKey, GroupTypes.GroupSelect,
1059 MDSALUtil.buildBucketLists(listBucket));
1060 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION,
1061 confTx -> mdsalApiManager.addGroup(confTx, dpnId, group)), LOG, "Error adding load-balancing group");
1062 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(OPERATIONAL,
1063 operTx -> FibUtil.updateLbGroupInfo(dpnId, destinationIp, groupIdKey, groupId.toString(), operTx)), LOG,
1064 "Error updating load-balancing group info");
1065 LOG.trace("LB group {} towards DC-GW installed on dpn {}. Group - {}", groupIdKey, dpnId, group);
1068 private boolean isTunnelUp(String dcGwIp, BigInteger dpnId, Class<? extends TunnelTypeBase> tunnelType) {
1069 String tunnelName = getTunnelRemoteNextHopPointer(dpnId, dcGwIp, tunnelType);
1070 if (tunnelName != null) {
1071 InstanceIdentifier<StateTunnelList> tunnelStateId =
1072 InstanceIdentifier.builder(TunnelsState.class).child(
1073 StateTunnelList.class, new StateTunnelListKey(tunnelName)).build();
1074 return MDSALUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, tunnelStateId)
1075 .toJavaUtil().map(StateTunnelList::getOperState)
1076 .orElse(TunnelOperStatus.Down) == TunnelOperStatus.Up;
1081 private List<Action> getEgressActions(String interfaceName, int actionKey) {
1082 List<Action> actions = Collections.emptyList();
1084 GetEgressActionsForInterfaceInputBuilder egressAction =
1085 new GetEgressActionsForInterfaceInputBuilder().setIntfName(interfaceName).setActionKey(actionKey);
1086 Future<RpcResult<GetEgressActionsForInterfaceOutput>> result =
1087 odlInterfaceRpcService.getEgressActionsForInterface(egressAction.build());
1088 RpcResult<GetEgressActionsForInterfaceOutput> rpcResult = result.get();
1089 if (!rpcResult.isSuccessful()) {
1090 LOG.warn("RPC Call to Get egress actions for interface {} returned with Errors {}",
1091 interfaceName, rpcResult.getErrors());
1093 actions = rpcResult.getResult().getAction();
1095 } catch (InterruptedException | ExecutionException e) {
1096 LOG.warn("Exception when egress actions for interface {}", interfaceName, e);
1102 * This method is invoked when the tunnel state is removed from DS.
1103 * If the there is just one DC-GW left in configuration then the LB groups can be deleted.
1104 * Otherwise, the groups are just updated.
1106 public void removeOrUpdateDcGwLoadBalancingGroup(List<String> availableDcGws, BigInteger dpnId,
1107 String destinationIp) {
1108 Preconditions.checkNotNull(availableDcGws, "There are no dc-gws present");
1109 ListenableFutures.addErrorLogging(txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, confTx -> {
1110 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(OPERATIONAL, operTx -> {
1111 int noOfDcGws = availableDcGws.size();
1112 // If availableDcGws does not contain the destination Ip it means this is a configuration delete.
1113 if (!availableDcGws.contains(destinationIp)) {
1114 availableDcGws.add(destinationIp);
1115 Collections.sort(availableDcGws);
1117 // TODO : Place the logic to construct all possible DC-GW combination here.
1118 int bucketId = availableDcGws.indexOf(destinationIp);
1119 Optional<DpnLbNexthops> dpnLbNextHops = fibUtil.getDpnLbNexthops(dpnId, destinationIp);
1120 if (!dpnLbNextHops.isPresent()) {
1123 List<String> nextHopKeys = dpnLbNextHops.get().getNexthopKey();
1124 for (String nextHopKey : nextHopKeys) {
1125 Optional<Nexthops> optionalNextHops = fibUtil.getNexthops(nextHopKey);
1126 if (!optionalNextHops.isPresent()) {
1129 Nexthops nexthops = optionalNextHops.get();
1130 final String groupId = nexthops.getGroupId();
1131 final long groupIdValue = Long.parseLong(groupId);
1132 if (noOfDcGws > 1) {
1133 mdsalApiManager.removeBucket(confTx, dpnId, groupIdValue, bucketId);
1135 LOG.trace("Removed LB group {} on dpn {}", groupIdValue, dpnId);
1136 mdsalApiManager.removeGroup(confTx, dpnId, groupIdValue);
1137 removeNextHopPointer(nextHopKey);
1139 // When the DC-GW is removed from configuration.
1140 if (noOfDcGws != availableDcGws.size()) {
1141 FibUtil.removeOrUpdateNextHopInfo(dpnId, nextHopKey, groupId, nexthops, operTx);
1144 FibUtil.removeDpnIdToNextHopInfo(destinationIp, dpnId, operTx);
1145 }), LOG, "Error removing or updating load-balancing group");
1146 }), LOG, "Error removing or updating load-balancing group");
1150 * This method is invoked when the tunnel status is updated.
1151 * The bucket is directly removed/added based on the operational status of the tunnel.
1153 public void updateDcGwLoadBalancingGroup(List<String> availableDcGws,
1154 BigInteger dpnId, String destinationIp, boolean isTunnelUp, Class<? extends TunnelTypeBase> tunnelType) {
1155 Preconditions.checkNotNull(availableDcGws, "There are no dc-gws present");
1156 ListenableFutures.addErrorLogging(txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, confTx -> {
1157 // TODO : Place the logic to construct all possible DC-GW combination here.
1158 int bucketId = availableDcGws.indexOf(destinationIp);
1159 Optional<DpnLbNexthops> dpnLbNextHops = fibUtil.getDpnLbNexthops(dpnId, destinationIp);
1160 if (!dpnLbNextHops.isPresent()) {
1163 List<String> nextHopKeys = dpnLbNextHops.get().getNexthopKey();
1164 for (String nextHopKey : nextHopKeys) {
1165 Optional<Nexthops> optionalNextHops = fibUtil.getNexthops(nextHopKey);
1166 if (!optionalNextHops.isPresent()) {
1169 Nexthops nexthops = optionalNextHops.get();
1170 final String groupId = nexthops.getGroupId();
1171 final long groupIdValue = Long.parseLong(groupId);
1173 Bucket bucket = buildBucketForDcGwLbGroup(destinationIp, dpnId, bucketId, tunnelType);
1174 LOG.trace("Added bucket {} to group {} on dpn {}.", bucket, groupId, dpnId);
1175 mdsalApiManager.addBucket(confTx, dpnId, groupIdValue, bucket);
1177 LOG.trace("Removed bucketId {} from group {} on dpn {}.", bucketId, groupId, dpnId);
1178 mdsalApiManager.removeBucket(confTx, dpnId, groupIdValue, bucketId);
1181 }), LOG, "Error updating load-balancing group");
1184 private Bucket buildBucketForDcGwLbGroup(String ipAddress, BigInteger dpnId, int index,
1185 Class<? extends TunnelTypeBase> tunnelType) {
1186 List<Action> listAction = new ArrayList<>();
1187 // ActionKey 0 goes to mpls label.
1189 listAction.add(new ActionPushMpls().buildAction());
1190 listAction.add(new ActionRegMove(actionKey++, FibConstants.NXM_REG_MAPPING
1191 .get(index), 0, 19).buildAction());
1192 String tunnelInterfaceName = getTunnelInterfaceName(dpnId, IpAddressBuilder.getDefaultInstance(ipAddress),
1194 List<Action> egressActions = getEgressActions(tunnelInterfaceName, actionKey++);
1195 if (!egressActions.isEmpty()) {
1196 listAction.addAll(getEgressActions(tunnelInterfaceName, actionKey++));
1198 // clear off actions if there is no egress actions.
1199 listAction = Collections.emptyList();
1201 //OVS expects a non-zero weight value for load balancing to happen in select groups
1202 return MDSALUtil.buildBucket(listAction, SELECT_GROUP_WEIGHT, index,
1203 MDSALUtil.WATCH_PORT, MDSALUtil.WATCH_GROUP);
1206 public void programDcGwLoadBalancingGroup(List<String> availableDcGws, BigInteger dpnId, String destinationIp,
1207 int addRemoveOrUpdate, boolean isTunnelUp,
1208 Class<? extends TunnelTypeBase> tunnelType) {
1209 if (NwConstants.ADD_FLOW == addRemoveOrUpdate) {
1210 createDcGwLoadBalancingGroup(availableDcGws, dpnId, destinationIp, tunnelType);
1211 } else if (NwConstants.DEL_FLOW == addRemoveOrUpdate) {
1212 removeOrUpdateDcGwLoadBalancingGroup(availableDcGws, dpnId, destinationIp);
1213 } else if (NwConstants.MOD_FLOW == addRemoveOrUpdate) {
1214 updateDcGwLoadBalancingGroup(availableDcGws, dpnId, destinationIp, isTunnelUp, tunnelType);