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;
13 import static org.opendaylight.netvirt.fibmanager.FibUtil.nullToEmpty;
15 import com.google.common.base.Optional;
16 import com.google.common.base.Preconditions;
17 import java.math.BigInteger;
18 import java.util.ArrayList;
19 import java.util.Collections;
20 import java.util.HashMap;
21 import java.util.List;
22 import java.util.Locale;
24 import java.util.Objects;
25 import java.util.concurrent.CopyOnWriteArrayList;
26 import java.util.concurrent.ExecutionException;
27 import java.util.concurrent.Future;
28 import javax.annotation.Nullable;
29 import javax.annotation.PreDestroy;
30 import javax.inject.Inject;
31 import javax.inject.Singleton;
32 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
33 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
34 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
35 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
36 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
37 import org.opendaylight.genius.interfacemanager.interfaces.IInterfaceManager;
38 import org.opendaylight.genius.itm.globals.ITMConstants;
39 import org.opendaylight.genius.mdsalutil.ActionInfo;
40 import org.opendaylight.genius.mdsalutil.BucketInfo;
41 import org.opendaylight.genius.mdsalutil.GroupEntity;
42 import org.opendaylight.genius.mdsalutil.MDSALUtil;
43 import org.opendaylight.genius.mdsalutil.NwConstants;
44 import org.opendaylight.genius.mdsalutil.actions.ActionGroup;
45 import org.opendaylight.genius.mdsalutil.actions.ActionNxResubmit;
46 import org.opendaylight.genius.mdsalutil.actions.ActionOutput;
47 import org.opendaylight.genius.mdsalutil.actions.ActionPushMpls;
48 import org.opendaylight.genius.mdsalutil.actions.ActionPushVlan;
49 import org.opendaylight.genius.mdsalutil.actions.ActionRegLoad;
50 import org.opendaylight.genius.mdsalutil.actions.ActionRegMove;
51 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldEthernetDestination;
52 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldEthernetSource;
53 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldTunnelId;
54 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldVlanVid;
55 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
56 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
57 import org.opendaylight.infrautils.utils.concurrent.ListenableFutures;
58 import org.opendaylight.netvirt.elanmanager.api.IElanService;
59 import org.opendaylight.netvirt.fibmanager.api.L3VPNTransportTypes;
60 import org.opendaylight.netvirt.vpnmanager.api.VpnExtraRouteHelper;
61 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana._if.type.rev170119.L2vlan;
62 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana._if.type.rev170119.Tunnel;
63 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddressBuilder;
64 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.InterfaceType;
65 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.OutputActionCase;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.PushVlanActionCase;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetFieldCase;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.AllocateIdInput;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.AllocateIdInputBuilder;
72 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.AllocateIdOutput;
73 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.CreateIdPoolInput;
74 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.CreateIdPoolInputBuilder;
75 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.CreateIdPoolOutput;
76 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
77 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.ReleaseIdInput;
78 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.ReleaseIdInputBuilder;
79 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.ReleaseIdOutput;
80 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeBase;
81 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeGre;
82 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeMplsOverGre;
83 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeVxlan;
84 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetEgressActionsForInterfaceInputBuilder;
85 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetEgressActionsForInterfaceOutput;
86 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.OdlInterfaceRpcService;
87 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.TunnelOperStatus;
88 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.TunnelsState;
89 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.tunnels_state.StateTunnelList;
90 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.tunnels_state.StateTunnelListKey;
91 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetEgressActionsForTunnelInputBuilder;
92 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetEgressActionsForTunnelOutput;
93 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetInternalOrExternalInterfaceNameInputBuilder;
94 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetInternalOrExternalInterfaceNameOutput;
95 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetTunnelInterfaceNameInputBuilder;
96 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetTunnelInterfaceNameOutput;
97 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.ItmRpcService;
98 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.lockmanager.rev160413.LockManagerService;
99 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.AddGroupInput;
100 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.AddGroupInputBuilder;
101 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.AddGroupOutput;
102 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.SalGroupService;
103 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupId;
104 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupRef;
105 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupTypes;
106 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.group.Buckets;
107 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.group.buckets.Bucket;
108 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.Group;
109 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
110 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.SegmentTypeBase;
111 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.SegmentTypeFlat;
112 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.SegmentTypeVlan;
113 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.instances.ElanInstance;
114 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentries.VrfEntry;
115 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.L3nexthop;
116 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.VpnNexthops;
117 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.VpnNexthopsKey;
118 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.vpnnexthops.VpnNexthop;
119 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.vpnnexthops.VpnNexthopBuilder;
120 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.vpnnexthops.VpnNexthopKey;
121 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.vpnnexthops.vpnnexthop.IpAdjacencies;
122 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.vpnnexthops.vpnnexthop.IpAdjacenciesBuilder;
123 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.vpnnexthops.vpnnexthop.IpAdjacenciesKey;
124 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.ConfTransportTypeL3vpn;
125 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.ConfTransportTypeL3vpnBuilder;
126 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.dpid.l3vpn.lb.nexthops.DpnLbNexthops;
127 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.l3vpn.lb.nexthops.Nexthops;
128 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.prefix.to._interface.vpn.ids.Prefixes;
129 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.to.extraroutes.vpn.extra.routes.Routes;
130 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg6;
131 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.add.group.input.buckets.bucket.action.action.NxActionResubmitRpcAddGroupCase;
132 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;
133 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.nx.action.reg.load.grouping.NxRegLoad;
134 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
135 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.InstanceIdentifierBuilder;
136 import org.opendaylight.yangtools.yang.common.RpcResult;
137 import org.slf4j.Logger;
138 import org.slf4j.LoggerFactory;
141 public class NexthopManager implements AutoCloseable {
142 private static final Logger LOG = LoggerFactory.getLogger(NexthopManager.class);
143 private static final String NEXTHOP_ID_POOL_NAME = "nextHopPointerPool";
144 private static final long WAIT_TIME_FOR_SYNC_INSTALL = Long.getLong("wait.time.sync.install", 300L);
145 private static final long WAIT_TIME_TO_ACQUIRE_LOCK = 3000L;
146 private static final int SELECT_GROUP_WEIGHT = 1;
148 private final DataBroker dataBroker;
149 private final ManagedNewTransactionRunner txRunner;
150 private final IMdsalApiManager mdsalApiManager;
151 private final OdlInterfaceRpcService odlInterfaceRpcService;
152 private final ItmRpcService itmManager;
153 private final IdManagerService idManager;
154 private final IElanService elanService;
155 private final LockManagerService lockManager;
156 private final SalGroupService salGroupService;
157 private final JobCoordinator jobCoordinator;
158 private final FibUtil fibUtil;
159 private final IInterfaceManager interfaceManager;
160 private volatile L3VPNTransportTypes configuredTransportTypeL3VPN = L3VPNTransportTypes.Invalid;
163 * Provides nexthop functions.
164 * Creates group ID pool
166 * @param dataBroker - dataBroker reference
167 * @param mdsalApiManager - mdsalApiManager reference
168 * @param idManager - idManager reference
169 * @param odlInterfaceRpcService - odlInterfaceRpcService reference
170 * @param itmManager - itmManager reference
173 public NexthopManager(final DataBroker dataBroker,
174 final IMdsalApiManager mdsalApiManager,
175 final IdManagerService idManager,
176 final OdlInterfaceRpcService odlInterfaceRpcService,
177 final ItmRpcService itmManager,
178 final LockManagerService lockManager,
179 final IElanService elanService,
180 final SalGroupService salGroupService,
181 final JobCoordinator jobCoordinator,
182 final FibUtil fibUtil,
183 final IInterfaceManager interfaceManager) {
184 this.dataBroker = dataBroker;
185 this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
186 this.mdsalApiManager = mdsalApiManager;
187 this.idManager = idManager;
188 this.odlInterfaceRpcService = odlInterfaceRpcService;
189 this.itmManager = itmManager;
190 this.elanService = elanService;
191 this.salGroupService = salGroupService;
192 this.jobCoordinator = jobCoordinator;
193 this.fibUtil = fibUtil;
194 this.lockManager = lockManager;
195 this.interfaceManager = interfaceManager;
199 private void createIdPool() {
200 CreateIdPoolInput createPool = new CreateIdPoolInputBuilder()
201 .setPoolName(NEXTHOP_ID_POOL_NAME)
206 Future<RpcResult<CreateIdPoolOutput>> result = idManager.createIdPool(createPool);
207 if (result != null && result.get().isSuccessful()) {
208 LOG.info("Created IdPool for NextHopPointerPool");
210 } catch (InterruptedException | ExecutionException e) {
211 LOG.error("Failed to create idPool for NextHopPointerPool", e);
215 private String getNextHopKey(long vpnId, String ipAddress) {
216 return "nexthop." + vpnId + ipAddress;
219 String getRemoteSelectGroupKey(long vpnId, String ipAddress) {
220 return "remote.ecmp.nexthop." + vpnId + ipAddress;
223 String getLocalSelectGroupKey(long vpnId, String ipAddress) {
224 return "local.ecmp.nexthop." + vpnId + ipAddress;
227 public ItmRpcService getItmManager() {
231 protected long createNextHopPointer(String nexthopKey) {
232 AllocateIdInput getIdInput = new AllocateIdInputBuilder()
233 .setPoolName(NEXTHOP_ID_POOL_NAME).setIdKey(nexthopKey)
235 //TODO: Proper error handling once IdManager code is complete
237 Future<RpcResult<AllocateIdOutput>> result = idManager.allocateId(getIdInput);
238 RpcResult<AllocateIdOutput> rpcResult = result.get();
239 return rpcResult.getResult().getIdValue();
240 } catch (NullPointerException | InterruptedException | ExecutionException e) {
246 protected void removeNextHopPointer(String nexthopKey) {
247 ReleaseIdInput idInput = new ReleaseIdInputBuilder()
248 .setPoolName(NEXTHOP_ID_POOL_NAME)
249 .setIdKey(nexthopKey).build();
251 RpcResult<ReleaseIdOutput> rpcResult = idManager.releaseId(idInput).get();
252 if (!rpcResult.isSuccessful()) {
253 LOG.error("RPC Call to Get Unique Id for nexthopKey {} returned with Errors {}",
254 nexthopKey, rpcResult.getErrors());
256 } catch (InterruptedException | ExecutionException e) {
257 LOG.warn("Exception when getting Unique Id for key {}", nexthopKey, e);
261 protected List<ActionInfo> getEgressActionsForInterface(final String ifName, int actionKey,
262 boolean isTunnelInterface) {
263 List<Action> actions;
265 if (isTunnelInterface && interfaceManager.isItmDirectTunnelsEnabled()) {
266 RpcResult<GetEgressActionsForTunnelOutput> rpcResult =
267 itmManager.getEgressActionsForTunnel(new GetEgressActionsForTunnelInputBuilder()
268 .setIntfName(ifName).build()).get();
269 if (!rpcResult.isSuccessful()) {
270 LOG.error("RPC Call to Get egress tunnel actions for interface {} returned with Errors {}",
271 ifName, rpcResult.getErrors());
272 return Collections.emptyList();
274 actions = rpcResult.getResult().getAction();
277 RpcResult<GetEgressActionsForInterfaceOutput> rpcResult = odlInterfaceRpcService
278 .getEgressActionsForInterface(new GetEgressActionsForInterfaceInputBuilder()
279 .setIntfName(ifName).build()).get();
280 if (!rpcResult.isSuccessful()) {
281 LOG.error("RPC Call to Get egress vm actions for interface {} returned with Errors {}",
282 ifName, rpcResult.getErrors());
283 return Collections.emptyList();
285 actions = rpcResult.getResult().getAction();
288 List<ActionInfo> listActionInfo = new ArrayList<>();
289 for (Action action : nullToEmpty(actions)) {
290 actionKey = action.key().getOrder() + actionKey;
291 org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action
292 actionClass = action.getAction();
293 if (actionClass instanceof OutputActionCase) {
294 listActionInfo.add(new ActionOutput(actionKey,
295 ((OutputActionCase) actionClass).getOutputAction().getOutputNodeConnector()));
296 } else if (actionClass instanceof PushVlanActionCase) {
297 listActionInfo.add(new ActionPushVlan(actionKey));
298 } else if (actionClass instanceof SetFieldCase) {
299 if (((SetFieldCase) actionClass).getSetField().getVlanMatch() != null) {
300 int vlanVid = ((SetFieldCase) actionClass).getSetField().getVlanMatch()
301 .getVlanId().getVlanId().getValue();
302 listActionInfo.add(new ActionSetFieldVlanVid(actionKey, vlanVid));
304 } else if (actionClass instanceof NxActionResubmitRpcAddGroupCase) {
305 Short tableId = ((NxActionResubmitRpcAddGroupCase) actionClass).getNxResubmit().getTable();
306 listActionInfo.add(new ActionNxResubmit(actionKey, tableId));
307 } else if (actionClass instanceof NxActionRegLoadNodesNodeTableFlowApplyActionsCase) {
308 NxRegLoad nxRegLoad =
309 ((NxActionRegLoadNodesNodeTableFlowApplyActionsCase) actionClass).getNxRegLoad();
310 listActionInfo.add(new ActionRegLoad(actionKey, NxmNxReg6.class,
311 nxRegLoad.getDst().getStart(), nxRegLoad.getDst().getEnd(),
312 nxRegLoad.getValue().longValue()));
315 return listActionInfo;
316 } catch (InterruptedException | ExecutionException e) {
317 LOG.warn("Exception when egress actions for interface {}", ifName, e);
319 LOG.warn("Exception when egress actions for interface {}", ifName);
320 return Collections.emptyList();
324 protected String getTunnelInterfaceName(BigInteger srcDpId, BigInteger dstDpId) {
325 Class<? extends TunnelTypeBase> tunType = getReqTunType(getReqTransType().toUpperCase(Locale.getDefault()));
326 Future<RpcResult<GetTunnelInterfaceNameOutput>> result;
328 result = itmManager.getTunnelInterfaceName(new GetTunnelInterfaceNameInputBuilder()
329 .setSourceDpid(srcDpId)
330 .setDestinationDpid(dstDpId)
331 .setTunnelType(tunType)
333 RpcResult<GetTunnelInterfaceNameOutput> rpcResult = result.get();
334 if (!rpcResult.isSuccessful()) {
335 LOG.warn("RPC Call to getTunnelInterfaceId returned with Errors {}", rpcResult.getErrors());
337 return rpcResult.getResult().getInterfaceName();
339 } catch (InterruptedException | ExecutionException e) {
340 LOG.warn("Exception when getting tunnel interface Id for tunnel between {} and {}", srcDpId, dstDpId, e);
346 protected String getTunnelInterfaceName(BigInteger srcDpId, org.opendaylight.yang.gen.v1.urn.ietf.params
347 .xml.ns.yang.ietf.inet.types.rev130715.IpAddress dstIp, Class<? extends TunnelTypeBase> tunnelType) {
348 Future<RpcResult<GetInternalOrExternalInterfaceNameOutput>> result;
350 LOG.debug("Trying to fetch tunnel interface name for source dpn {} destIp {} tunType {}", srcDpId,
351 dstIp.stringValue(), tunnelType.getName());
352 result = itmManager.getInternalOrExternalInterfaceName(new GetInternalOrExternalInterfaceNameInputBuilder()
353 .setSourceDpid(srcDpId)
354 .setDestinationIp(dstIp)
355 .setTunnelType(tunnelType)
357 RpcResult<GetInternalOrExternalInterfaceNameOutput> rpcResult = result.get();
358 if (!rpcResult.isSuccessful()) {
359 LOG.warn("RPC Call to getTunnelInterfaceName returned with Errors {}", rpcResult.getErrors());
361 return rpcResult.getResult().getInterfaceName();
363 } catch (InterruptedException | ExecutionException e) {
364 LOG.warn("Exception when getting tunnel interface Id for tunnel between {} and {}", srcDpId, dstIp, e);
370 public long getLocalNextHopGroup(long vpnId,
371 String ipNextHopAddress) {
372 long groupId = createNextHopPointer(getNextHopKey(vpnId, ipNextHopAddress));
373 if (groupId == FibConstants.INVALID_GROUP_ID) {
374 LOG.error("Unable to allocate groupId for vpnId {} , prefix {}", vpnId, ipNextHopAddress);
379 public long getLocalSelectGroup(long vpnId,
380 String ipNextHopAddress) {
381 long groupId = createNextHopPointer(getLocalSelectGroupKey(vpnId, ipNextHopAddress));
382 if (groupId == FibConstants.INVALID_GROUP_ID) {
383 LOG.error("Unable to allocate groupId for vpnId {} , prefix {}", vpnId, ipNextHopAddress);
388 public long createLocalNextHop(long vpnId, BigInteger dpnId, String ifName,
389 String primaryIpAddress, String currDestIpPrefix,
390 String gwMacAddress) {
391 String vpnName = fibUtil.getVpnNameFromId(vpnId);
392 if (vpnName == null) {
395 String macAddress = fibUtil.getMacAddressFromPrefix(ifName, vpnName, primaryIpAddress);
397 long groupId = createNextHopPointer(getNextHopKey(vpnId, primaryIpAddress));
399 LOG.error("Unable to allocate groupId for vpnId {} , IntfName {}, primaryIpAddress {} curIpPrefix {}",
400 vpnId, ifName, primaryIpAddress, currDestIpPrefix);
403 String nextHopLockStr = vpnId + primaryIpAddress;
404 String jobKey = FibUtil.getCreateLocalNextHopJobKey(vpnId, dpnId, currDestIpPrefix);
405 jobCoordinator.enqueueJob(jobKey, () -> {
407 if (FibUtil.lockCluster(lockManager, nextHopLockStr, WAIT_TIME_TO_ACQUIRE_LOCK)) {
408 VpnNexthop nexthop = getVpnNexthop(vpnId, primaryIpAddress);
409 LOG.trace("nexthop: {} retrieved for vpnId {}, prefix {}, ifName {} on dpn {}", nexthop, vpnId,
410 primaryIpAddress, ifName, dpnId);
411 if (nexthop == null) {
412 String encMacAddress = macAddress == null
413 ? fibUtil.getMacAddressFromPrefix(ifName, vpnName, primaryIpAddress) : macAddress;
414 List<BucketInfo> listBucketInfo = new ArrayList<>();
415 List<ActionInfo> listActionInfo = new ArrayList<>();
418 if (encMacAddress != null) {
419 if (gwMacAddress != null) {
420 LOG.trace("The Local NextHop Group Source Mac {} for VpnInterface {} on VPN {}",
421 gwMacAddress, ifName, vpnId);
422 listActionInfo.add(new ActionSetFieldEthernetSource(actionKey++,
423 new MacAddress(gwMacAddress)));
425 listActionInfo.add(new ActionSetFieldEthernetDestination(actionKey++,
426 new MacAddress(encMacAddress)));
427 // listActionInfo.add(0, new ActionPopMpls());
429 // FIXME: Log message here.
430 LOG.debug("mac address for new local nexthop is null");
432 listActionInfo.addAll(getEgressActionsForInterface(ifName, actionKey, false));
433 BucketInfo bucket = new BucketInfo(listActionInfo);
435 listBucketInfo.add(bucket);
436 GroupEntity groupEntity = MDSALUtil.buildGroupEntity(dpnId, groupId, primaryIpAddress,
437 GroupTypes.GroupAll, listBucketInfo);
438 LOG.trace("Install LNH Group: id {}, mac address {}, interface {} for prefix {}", groupId,
439 encMacAddress, ifName, primaryIpAddress);
440 //Try to install group directly on the DPN bypassing the FRM, in order to avoid waiting for the
441 // group to get installed before programming the flows
442 installGroupOnDpn(groupId, dpnId, primaryIpAddress, listBucketInfo,
443 getNextHopKey(vpnId, primaryIpAddress), GroupTypes.GroupAll);
445 mdsalApiManager.syncInstallGroup(groupEntity);
447 addVpnNexthopToDS(dpnId, vpnId, primaryIpAddress, currDestIpPrefix, groupId);
450 // Ignore adding new prefix , if it already exists
451 List<IpAdjacencies> prefixesList = nexthop.getIpAdjacencies();
452 IpAdjacencies prefix = new IpAdjacenciesBuilder().setIpAdjacency(currDestIpPrefix).build();
453 if (prefixesList != null && prefixesList.contains(prefix)) {
454 LOG.trace("Prefix {} is already present in l3nextHop {} ", currDestIpPrefix, nexthop);
456 IpAdjacenciesBuilder ipPrefixesBuilder =
457 new IpAdjacenciesBuilder().withKey(new IpAdjacenciesKey(currDestIpPrefix));
458 LOG.trace("Updating prefix {} to vpnNextHop {} Operational DS", currDestIpPrefix, nexthop);
459 MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL,
460 getVpnNextHopIpPrefixIdentifier(vpnId, primaryIpAddress, currDestIpPrefix),
461 ipPrefixesBuilder.build());
466 FibUtil.unlockCluster(lockManager, nextHopLockStr);
468 return Collections.emptyList();
473 private void installGroupOnDpn(long groupId, BigInteger dpnId, String groupName, List<BucketInfo> bucketsInfo,
474 String nextHopKey, GroupTypes groupType) {
475 NodeRef nodeRef = FibUtil.buildNodeRef(dpnId);
476 Buckets buckets = FibUtil.buildBuckets(bucketsInfo);
477 GroupRef groupRef = new GroupRef(FibUtil.buildGroupInstanceIdentifier(groupId, dpnId));
478 AddGroupInput input = new AddGroupInputBuilder().setNode(nodeRef).setGroupId(new GroupId(groupId))
479 .setBuckets(buckets).setGroupRef(groupRef).setGroupType(groupType)
480 .setGroupName(groupName).build();
481 Future<RpcResult<AddGroupOutput>> groupStats = salGroupService.addGroup(input);
482 RpcResult<AddGroupOutput> rpcResult = null;
484 rpcResult = groupStats.get();
485 if (rpcResult != null && rpcResult.isSuccessful()) {
486 LOG.info("Group {} with key {} has been successfully installed directly on dpn {}.", groupId,
489 LOG.error("Unable to install group {} with key {} directly on dpn {} due to {}.", groupId, nextHopKey,
490 dpnId, rpcResult != null ? rpcResult.getErrors() : null);
492 } catch (InterruptedException | ExecutionException e) {
493 LOG.error("Error while installing group {} directly on dpn {}", groupId, dpnId);
497 protected void addVpnNexthopToDS(BigInteger dpnId, long vpnId, String primaryIpAddr,
498 String currIpAddr, long egressPointer) {
499 InstanceIdentifierBuilder<VpnNexthops> idBuilder = InstanceIdentifier.builder(L3nexthop.class)
500 .child(VpnNexthops.class, new VpnNexthopsKey(vpnId));
502 List<IpAdjacencies> ipPrefixesList = new ArrayList<>();
503 IpAdjacencies prefix = new IpAdjacenciesBuilder().setIpAdjacency(currIpAddr).build();
504 ipPrefixesList.add(prefix);
505 // Add nexthop to vpn node
506 VpnNexthop nh = new VpnNexthopBuilder()
507 .withKey(new VpnNexthopKey(primaryIpAddr))
509 .setIpAdjacencies(ipPrefixesList)
510 .setEgressPointer(egressPointer).build();
512 InstanceIdentifier<VpnNexthop> id1 = idBuilder
513 .child(VpnNexthop.class, new VpnNexthopKey(primaryIpAddr)).build();
514 LOG.trace("Adding vpnnextHop {} to Operational DS", nh);
515 MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL, id1, nh);
519 protected InstanceIdentifier<IpAdjacencies> getVpnNextHopIpPrefixIdentifier(long vpnId, String primaryIpAddress,
521 InstanceIdentifier<IpAdjacencies> id = InstanceIdentifier.builder(L3nexthop.class)
522 .child(VpnNexthops.class, new VpnNexthopsKey(vpnId))
523 .child(VpnNexthop.class, new VpnNexthopKey(primaryIpAddress))
524 .child(IpAdjacencies.class, new IpAdjacenciesKey(ipPrefix)).build();
529 protected VpnNexthop getVpnNexthop(long vpnId, String ipAddress) {
531 // check if vpn node is there
532 InstanceIdentifierBuilder<VpnNexthops> idBuilder =
533 InstanceIdentifier.builder(L3nexthop.class).child(VpnNexthops.class,
534 new VpnNexthopsKey(vpnId));
535 InstanceIdentifier<VpnNexthops> id = idBuilder.build();
536 Optional<VpnNexthops> vpnNexthops = MDSALUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, id);
537 if (vpnNexthops.isPresent()) {
538 // get nexthops list for vpn
539 List<VpnNexthop> nexthops = nullToEmpty(vpnNexthops.get().getVpnNexthop());
540 for (VpnNexthop nexthop : nexthops) {
541 if (Objects.equals(nexthop.getIpAddress(), ipAddress)) {
543 LOG.trace("VpnNextHop : {}", nexthop);
547 // return null if not found
553 public AdjacencyResult getRemoteNextHopPointer(BigInteger remoteDpnId, long vpnId, String prefixIp,
554 @Nullable String nextHopIp, Class<? extends TunnelTypeBase> tunnelType) {
555 String egressIfName = null;
556 LOG.trace("getRemoteNextHopPointer: input [remoteDpnId {}, vpnId {}, prefixIp {}, nextHopIp {} ]", remoteDpnId,
557 vpnId, prefixIp, nextHopIp);
559 Class<? extends InterfaceType> egressIfType;
560 ElanInstance elanInstance = getElanInstanceForPrefix(vpnId, prefixIp);
561 if (elanInstance != null) {
562 egressIfType = getInterfaceType(elanInstance);
564 LOG.warn("Failed to determine network type for prefixIp {} using tunnel", prefixIp);
565 egressIfType = Tunnel.class;
568 if (Tunnel.class.equals(egressIfType)) {
569 egressIfName = getTunnelRemoteNextHopPointer(remoteDpnId, nextHopIp, tunnelType);
571 egressIfName = getExtPortRemoteNextHopPointer(remoteDpnId, elanInstance);
574 LOG.trace("NextHop pointer for prefixIp {} vpnId {} dpnId {} is {}", prefixIp, vpnId, remoteDpnId,
576 return egressIfName != null ? new AdjacencyResult(egressIfName, egressIfType, nextHopIp,
581 public BigInteger getDpnForPrefix(long vpnId, String prefixIp) {
582 VpnNexthop vpnNexthop = getVpnNexthop(vpnId, prefixIp);
583 return vpnNexthop == null ? null : vpnNexthop.getDpnId();
586 private void removeVpnNexthopFromDS(long vpnId, String ipPrefix) {
588 InstanceIdentifierBuilder<VpnNexthop> idBuilder = InstanceIdentifier.builder(L3nexthop.class)
589 .child(VpnNexthops.class, new VpnNexthopsKey(vpnId))
590 .child(VpnNexthop.class, new VpnNexthopKey(ipPrefix));
591 InstanceIdentifier<VpnNexthop> id = idBuilder.build();
593 LOG.trace("Removing vpn next hop from datastore : {}", id);
594 MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.OPERATIONAL, id);
597 public void removeLocalNextHop(BigInteger dpnId, Long vpnId, String primaryIpAddress, String currDestIpPrefix) {
598 String nextHopLockStr = vpnId + primaryIpAddress;
600 if (FibUtil.lockCluster(lockManager, nextHopLockStr, WAIT_TIME_TO_ACQUIRE_LOCK)) {
601 VpnNexthop nh = getVpnNexthop(vpnId, primaryIpAddress);
603 List<IpAdjacencies> prefixesList = new ArrayList<>(nullToEmpty(nh.getIpAdjacencies()));
604 IpAdjacencies prefix = new IpAdjacenciesBuilder().setIpAdjacency(currDestIpPrefix).build();
605 prefixesList.remove(prefix);
606 if (prefixesList.isEmpty()) { //remove the group only if there are no more flows using this group
607 GroupEntity groupEntity = MDSALUtil.buildGroupEntity(dpnId, nh.getEgressPointer(),
608 primaryIpAddress, GroupTypes.GroupAll, Collections.EMPTY_LIST);
610 mdsalApiManager.removeGroup(groupEntity);
612 removeVpnNexthopFromDS(vpnId, primaryIpAddress);
614 removeNextHopPointer(getNextHopKey(vpnId, primaryIpAddress));
615 LOG.debug("Local Next hop {} for {} {} on dpn {} successfully deleted",
616 nh.getEgressPointer(), vpnId, primaryIpAddress, dpnId);
618 //remove the currIpPrefx from IpPrefixList of the vpnNexthop
619 LOG.trace("Removing the prefix {} from vpnNextHop {} Operational DS", currDestIpPrefix, nh);
620 MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.OPERATIONAL,
621 getVpnNextHopIpPrefixIdentifier(vpnId, primaryIpAddress, currDestIpPrefix));
625 LOG.error("Local NextHop for VpnId {} curIpPrefix {} on dpn {} primaryIpAddress {} not deleted",
626 vpnId, currDestIpPrefix, dpnId, primaryIpAddress);
630 FibUtil.unlockCluster(lockManager, nextHopLockStr);
634 public void setConfTransType(String service, String transportType) {
636 if (!service.equalsIgnoreCase("L3VPN")) {
637 LOG.error("Incorrect service {} provided for setting the transport type.", service);
641 L3VPNTransportTypes transType = L3VPNTransportTypes.validateTransportType(transportType
642 .toUpperCase(Locale.getDefault()));
644 if (transType != L3VPNTransportTypes.Invalid) {
645 configuredTransportTypeL3VPN = transType;
649 public void writeConfTransTypeConfigDS() {
650 MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, getConfTransportTypeIdentifier(),
651 createConfTransportType(configuredTransportTypeL3VPN.getTransportType()));
654 public L3VPNTransportTypes getConfiguredTransportTypeL3VPN() {
655 return this.configuredTransportTypeL3VPN;
658 public String getReqTransType() {
659 if (configuredTransportTypeL3VPN == L3VPNTransportTypes.Invalid) {
661 * Restart scenario, Read from the ConfigDS.
662 * if the value is Unset, cache value as VxLAN.
664 LOG.trace("configureTransportType is not yet set.");
665 Optional<ConfTransportTypeL3vpn> configuredTransTypeFromConfig =
666 MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, getConfTransportTypeIdentifier());
668 if (configuredTransTypeFromConfig.isPresent()) {
669 if (TunnelTypeGre.class.equals(configuredTransTypeFromConfig.get().getTransportType())) {
670 configuredTransportTypeL3VPN = L3VPNTransportTypes.GRE;
672 configuredTransportTypeL3VPN = L3VPNTransportTypes.VxLAN;
674 LOG.trace("configuredTransportType set from config DS to {}",
675 getConfiguredTransportTypeL3VPN().getTransportType());
677 setConfTransType("L3VPN", L3VPNTransportTypes.VxLAN.getTransportType());
678 LOG.trace("configuredTransportType is not set in the Config DS. VxLAN as default will be used.");
681 LOG.trace("configuredTransportType is set as {}", getConfiguredTransportTypeL3VPN().getTransportType());
683 return getConfiguredTransportTypeL3VPN().getTransportType();
686 public InstanceIdentifier<ConfTransportTypeL3vpn> getConfTransportTypeIdentifier() {
687 return InstanceIdentifier.builder(ConfTransportTypeL3vpn.class).build();
690 private ConfTransportTypeL3vpn createConfTransportType(String type) {
691 ConfTransportTypeL3vpn confTransType;
693 case ITMConstants.TUNNEL_TYPE_GRE:
694 confTransType = new ConfTransportTypeL3vpnBuilder().setTransportType(TunnelTypeGre.class).build();
695 LOG.trace("Setting the confTransportType to GRE.");
697 case ITMConstants.TUNNEL_TYPE_VXLAN:
698 confTransType = new ConfTransportTypeL3vpnBuilder().setTransportType(TunnelTypeVxlan.class).build();
699 LOG.trace("Setting the confTransportType to VxLAN.");
702 LOG.trace("Invalid transport type {} passed to Config DS ", type);
703 confTransType = null;
706 return confTransType;
709 public Class<? extends TunnelTypeBase> getReqTunType(String transportType) {
710 switch (transportType) {
712 return TunnelTypeVxlan.class;
714 return TunnelTypeGre.class;
716 return TunnelTypeMplsOverGre.class;
720 public String getTransportTypeStr(String tunType) {
721 if (tunType.equals(TunnelTypeVxlan.class.toString())) {
722 return ITMConstants.TUNNEL_TYPE_VXLAN;
723 } else if (tunType.equals(TunnelTypeGre.class.toString())) {
724 return ITMConstants.TUNNEL_TYPE_GRE;
725 } else if (tunType.equals(TunnelTypeMplsOverGre.class.toString())) {
726 return ITMConstants.TUNNEL_TYPE_MPLSoGRE;
728 return ITMConstants.TUNNEL_TYPE_INVALID;
734 public void close() {
735 LOG.info("{} close", getClass().getSimpleName());
738 // TODO Clean up the exception handling
739 @SuppressWarnings("checkstyle:IllegalCatch")
741 private String getTunnelRemoteNextHopPointer(BigInteger remoteDpnId, String nextHopIp,
742 Class<? extends TunnelTypeBase> tunnelType) {
743 if (nextHopIp != null && !nextHopIp.isEmpty()) {
745 // here use the config for tunnel type param
746 return getTunnelInterfaceName(remoteDpnId,
747 org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddressBuilder
748 .getDefaultInstance(nextHopIp), tunnelType);
749 } catch (Exception ex) {
750 LOG.error("Error while retrieving nexthop pointer for nexthop {} remoteDpn {}",
751 nextHopIp, remoteDpnId, ex);
758 private String getExtPortRemoteNextHopPointer(BigInteger remoteDpnId, ElanInstance elanInstance) {
759 return elanService.getExternalElanInterface(elanInstance.getElanInstanceName(), remoteDpnId);
763 * Get the interface type associated with the type of ELAN used for routing
764 * traffic to/from remote compute nodes.
766 * @param elanInstance The elan instance
767 * @return L2vlan for flat/VLAN network type and Tunnel otherwise
769 private Class<? extends InterfaceType> getInterfaceType(ElanInstance elanInstance) {
770 Class<? extends SegmentTypeBase> segmentType = elanInstance.getSegmentType();
771 if (SegmentTypeFlat.class.equals(segmentType) || SegmentTypeVlan.class.equals(segmentType)) {
778 private ElanInstance getElanInstanceForPrefix(long vpnId, String prefixIp) {
779 ElanInstance elanInstance = null;
780 Prefixes prefix = fibUtil.getPrefixToInterface(vpnId, prefixIp);
781 if (prefix != null) {
782 if (prefix.getNetworkId() != null) {
783 elanInstance = elanService.getElanInstance(prefix.getNetworkId().getValue());
790 static class AdjacencyResult {
791 private final String interfaceName;
792 private final Class<? extends InterfaceType> interfaceType;
793 private final String nextHopIp;
794 private final String prefix;
796 AdjacencyResult(String interfaceName, Class<? extends InterfaceType> interfaceType, String nextHopIp,
798 this.interfaceName = interfaceName;
799 this.interfaceType = interfaceType;
800 this.nextHopIp = nextHopIp;
801 this.prefix = prefix;
804 public String getInterfaceName() {
805 return interfaceName;
808 public Class<? extends InterfaceType> getInterfaceType() {
809 return interfaceType;
812 public String getNextHopIp() {
816 public String getPrefix() {
821 public int hashCode() {
822 final int prime = 31;
824 result = prime * result + (interfaceName == null ? 0 : interfaceName.hashCode());
829 public boolean equals(Object obj) {
834 if (getClass() != obj.getClass()) {
837 AdjacencyResult other = (AdjacencyResult) obj;
838 return interfaceName.equals(other.interfaceName);
843 protected long setupLoadBalancingNextHop(Long parentVpnId, BigInteger dpnId,
844 String destPrefix, List<BucketInfo> localBucketInfo, List<BucketInfo> remoteBucketInfo) {
845 long remoteGroupId = createNextHopPointer(getRemoteSelectGroupKey(parentVpnId, destPrefix));
846 if (remoteGroupId == FibConstants.INVALID_GROUP_ID) {
847 LOG.error("Unable to allocate/retrieve remote groupId for vpnId {} , prefix {}", parentVpnId, destPrefix);
848 return remoteGroupId;
850 long localGroupId = createNextHopPointer(getLocalSelectGroupKey(parentVpnId, destPrefix));
851 if (localGroupId == FibConstants.INVALID_GROUP_ID) {
852 LOG.error("Unable to allocate/retrieve local groupId for vpnId {} , prefix {}",
853 parentVpnId, destPrefix);
854 return remoteGroupId;
856 List<BucketInfo> combinedBucketInfo = new ArrayList<>();
857 combinedBucketInfo.addAll(localBucketInfo);
858 combinedBucketInfo.addAll(remoteBucketInfo);
859 GroupEntity remoteGroupEntity = MDSALUtil.buildGroupEntity(
860 dpnId, remoteGroupId, destPrefix, GroupTypes.GroupSelect, combinedBucketInfo);
861 GroupEntity localGroupEntity = MDSALUtil.buildGroupEntity(
862 dpnId, localGroupId, destPrefix, GroupTypes.GroupSelect, localBucketInfo);
863 String jobKey = FibUtil.getCreateLocalNextHopJobKey(parentVpnId, dpnId, destPrefix);
864 jobCoordinator.enqueueJob(jobKey, () -> {
865 mdsalApiManager.syncInstallGroup(remoteGroupEntity);
866 if (!localBucketInfo.isEmpty()) {
867 mdsalApiManager.syncInstallGroup(localGroupEntity);
869 if (LOG.isDebugEnabled()) {
870 LOG.debug("Finished installing GroupEntity with jobCoordinator key {} remoteGroupEntity.groupId {}"
871 + "localGroupEntity.groupId {} groupEntity.groupType {}", jobKey,
872 remoteGroupEntity.getGroupId(), localGroupEntity.getGroupId(),
873 remoteGroupEntity.getGroupType());
875 // Delete local group(if exists) if there is no local info.
876 // Local group has to be deleted if all VMs in a compute is deleted.
877 if (localBucketInfo.isEmpty()) {
878 LOG.debug("Deleting local group {} since no local nhs present for "
879 + "prefix {}", localGroupEntity.getGroupId(), destPrefix);
880 mdsalApiManager.syncRemoveGroup(localGroupEntity);
882 return Collections.emptyList();
884 return remoteGroupId;
887 protected void deleteLoadBalancingNextHop(Long parentVpnId, BigInteger dpnId, String destPrefix) {
888 long remoteGroupId = createNextHopPointer(getRemoteSelectGroupKey(parentVpnId, destPrefix));
889 if (remoteGroupId == FibConstants.INVALID_GROUP_ID) {
890 LOG.error("Unable to allocate/retrieve remote groupId for vpnId {} , prefix {}", parentVpnId, destPrefix);
892 long localGroupId = createNextHopPointer(getLocalSelectGroupKey(parentVpnId, destPrefix));
893 if (localGroupId == FibConstants.INVALID_GROUP_ID) {
894 LOG.error("Unable to allocate/retrieve local groupId for vpnId {} , prefix {}", parentVpnId, destPrefix);
896 GroupEntity remoteGroupEntity = MDSALUtil.buildGroupEntity(
897 dpnId, remoteGroupId, destPrefix, GroupTypes.GroupSelect, Collections.emptyList());
898 GroupEntity localGroupEntity = MDSALUtil.buildGroupEntity(
899 dpnId, localGroupId, destPrefix, GroupTypes.GroupSelect, Collections.emptyList());
900 String jobKey = FibUtil.getCreateLocalNextHopJobKey(parentVpnId, dpnId, destPrefix);
901 jobCoordinator.enqueueJob(jobKey, () -> {
902 mdsalApiManager.syncRemoveGroup(remoteGroupEntity);
903 mdsalApiManager.syncRemoveGroup(localGroupEntity);
904 if (LOG.isDebugEnabled()) {
905 LOG.debug("Finished removing GroupEntity with jobCoordinator key {} remoteGroupEntity.groupId {}"
906 + "localGroupEntity.groupId {}", jobKey, remoteGroupId, localGroupId);
908 return Collections.emptyList();
912 long createNextHopGroups(Long vpnId, String rd, BigInteger dpnId, VrfEntry vrfEntry,
913 @Nullable Routes routes, List<Routes> vpnExtraRoutes) {
914 List<BucketInfo> localBucketInfo = new ArrayList<>();
915 List<Routes> clonedVpnExtraRoutes = new ArrayList<>(vpnExtraRoutes);
916 if (clonedVpnExtraRoutes.contains(routes)) {
917 localBucketInfo.addAll(getBucketsForLocalNexthop(vpnId, dpnId, vrfEntry, routes));
918 clonedVpnExtraRoutes.remove(routes);
920 List<BucketInfo> remoteBucketInfo =
921 new ArrayList<>(getBucketsForRemoteNexthop(vpnId, dpnId, vrfEntry, rd, clonedVpnExtraRoutes));
922 return setupLoadBalancingNextHop(vpnId, dpnId,
923 vrfEntry.getDestPrefix(), localBucketInfo, remoteBucketInfo);
926 private List<BucketInfo> getBucketsForLocalNexthop(Long vpnId, BigInteger dpnId,
927 VrfEntry vrfEntry, Routes routes) {
928 if (LOG.isDebugEnabled()) {
929 LOG.debug("NexthopManager.getBucketsForLocalNexthop invoked with vpnId {} dpnId {} "
930 + " vrfEntry.routePaths {}, routes.nexthopList {}", vpnId, dpnId, vrfEntry.getRoutePaths(),
931 routes.getNexthopIpList());
933 List<BucketInfo> listBucketInfo = new CopyOnWriteArrayList<>();
934 nullToEmpty(routes.getNexthopIpList()).parallelStream().forEach(nextHopIp -> {
935 String localNextHopIP;
936 if (isIpv4Address(nextHopIp)) {
937 localNextHopIP = nextHopIp + NwConstants.IPV4PREFIX;
939 localNextHopIP = nextHopIp + NwConstants.IPV6PREFIX;
941 Prefixes localNextHopInfo = fibUtil.getPrefixToInterface(vpnId, localNextHopIP);
942 if (localNextHopInfo != null) {
943 long groupId = getLocalNextHopGroup(vpnId, localNextHopIP);
944 if (groupId == FibConstants.INVALID_GROUP_ID) {
945 LOG.error("Unable to allocate groupId for vpnId {} , prefix {} , interface {}", vpnId,
946 vrfEntry.getDestPrefix(), localNextHopInfo.getVpnInterfaceName());
949 List<ActionInfo> actionsInfos =
950 Collections.singletonList(new ActionGroup(groupId));
951 BucketInfo bucket = new BucketInfo(actionsInfos);
953 listBucketInfo.add(bucket);
956 LOG.trace("LOCAL: listbucket {}, vpnId {}, dpnId {}, routes {}", listBucketInfo, vpnId, dpnId, routes);
957 return listBucketInfo;
960 private List<BucketInfo> getBucketsForRemoteNexthop(Long vpnId, BigInteger dpnId, VrfEntry vrfEntry, String rd,
961 List<Routes> vpnExtraRoutes) {
962 List<BucketInfo> listBucketInfo = new ArrayList<>();
963 Map<String, List<ActionInfo>> egressActionMap = new HashMap<>();
964 vpnExtraRoutes.forEach(vpnExtraRoute -> nullToEmpty(vpnExtraRoute.getNexthopIpList()).forEach(nextHopIp -> {
965 String nextHopPrefixIp;
966 if (isIpv4Address(nextHopIp)) {
967 nextHopPrefixIp = nextHopIp + NwConstants.IPV4PREFIX;
969 nextHopPrefixIp = nextHopIp + NwConstants.IPV6PREFIX;
971 List<String> tepIpAddresses = fibUtil.getNextHopAddresses(rd, nextHopPrefixIp);
972 if (tepIpAddresses.isEmpty()) {
975 // There would be only one nexthop address for a VM ip which would be the tep Ip
976 String tepIp = tepIpAddresses.get(0);
977 AdjacencyResult adjacencyResult = getRemoteNextHopPointer(dpnId, vpnId,
978 vrfEntry.getDestPrefix(), tepIp, TunnelTypeVxlan.class);
979 if (adjacencyResult == null) {
982 String egressInterface = adjacencyResult.getInterfaceName();
983 if (!FibUtil.isTunnelInterface(adjacencyResult)) {
986 Class<? extends TunnelTypeBase> tunnelType = VpnExtraRouteHelper.getTunnelType(itmManager, egressInterface);
987 StateTunnelList ifState = null;
989 ifState = fibUtil.getTunnelState(egressInterface);
990 if (ifState == null || ifState.getOperState() != TunnelOperStatus.Up) {
991 LOG.trace("Tunnel is not up for interface {}", egressInterface);
994 } catch (ReadFailedException e) {
995 LOG.error("getBucketsForRemoteNexthop: error in fetching tunnel state for interface {}",
999 if (!TunnelTypeVxlan.class.equals(tunnelType)) {
1002 Long label = FibUtil.getLabelFromRoutePaths(vrfEntry).get();
1003 Prefixes prefixInfo = fibUtil.getPrefixToInterface(vpnId, nextHopPrefixIp);
1004 if (prefixInfo == null) {
1005 LOG.error("No prefix info found for prefix {} in rd {} for VPN {}", nextHopPrefixIp, rd,
1009 BigInteger tunnelId;
1010 if (FibUtil.isVxlanNetwork(prefixInfo.getNetworkType())) {
1011 tunnelId = BigInteger.valueOf(prefixInfo.getSegmentationId());
1013 LOG.warn("Network is not of type VXLAN for prefix {}."
1014 + "Going with default Lport Tag.", prefixInfo.toString());
1015 tunnelId = BigInteger.valueOf(label);
1017 List<ActionInfo> actionInfos = new ArrayList<>();
1018 actionInfos.add(new ActionSetFieldTunnelId(tunnelId));
1019 String ifName = prefixInfo.getVpnInterfaceName();
1020 String vpnName = fibUtil.getVpnNameFromId(vpnId);
1021 if (vpnName == null) {
1024 String macAddress = fibUtil.getMacAddressFromPrefix(ifName, vpnName, nextHopPrefixIp);
1025 actionInfos.add(new ActionSetFieldEthernetDestination(actionInfos.size(),
1026 new MacAddress(macAddress)));
1027 List<ActionInfo> egressActions;
1028 if (egressActionMap.containsKey(egressInterface)) {
1029 egressActions = egressActionMap.get(egressInterface);
1031 egressActions = getEgressActionsForInterface(egressInterface, actionInfos.size(), true);
1032 egressActionMap.put(egressInterface, egressActions);
1034 if (egressActions.isEmpty()) {
1035 LOG.error("Failed to retrieve egress action for prefix {} route-paths {}"
1036 + " interface {}." + " Aborting remote FIB entry creation.",
1037 vrfEntry.getDestPrefix(), vrfEntry.getRoutePaths(), egressInterface);
1039 actionInfos.addAll(egressActions);
1040 BucketInfo bucket = new BucketInfo(actionInfos);
1041 bucket.setWeight(1);
1042 listBucketInfo.add(bucket);
1044 LOG.trace("LOCAL: listbucket {}, rd {}, dpnId {}, routes {}", listBucketInfo, rd, dpnId, vpnExtraRoutes);
1045 return listBucketInfo;
1048 public void createDcGwLoadBalancingGroup(List<String> availableDcGws, BigInteger dpnId, String destinationIp,
1049 Class<? extends TunnelTypeBase> tunnelType) {
1050 Preconditions.checkNotNull(availableDcGws, "There are no dc-gws present");
1051 int noOfDcGws = availableDcGws.size();
1052 if (noOfDcGws == 1) {
1053 LOG.trace("There are no enough DC GateWays {} present to program LB group", availableDcGws);
1056 // TODO : Place the logic to construct all possible DC-GW combination here.
1057 String groupIdKey = FibUtil.getGreLbGroupKey(availableDcGws);
1058 Long groupId = createNextHopPointer(groupIdKey);
1059 List<Bucket> listBucket = new ArrayList<>();
1060 for (int index = 0; index < noOfDcGws; index++) {
1061 if (isTunnelUp(availableDcGws.get(index), dpnId, tunnelType)) {
1062 listBucket.add(buildBucketForDcGwLbGroup(availableDcGws.get(index), dpnId, index, tunnelType));
1065 Group group = MDSALUtil.buildGroup(groupId, groupIdKey, GroupTypes.GroupSelect,
1066 MDSALUtil.buildBucketLists(listBucket));
1067 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION,
1068 confTx -> mdsalApiManager.addGroup(confTx, dpnId, group)), LOG, "Error adding load-balancing group");
1069 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(OPERATIONAL,
1070 operTx -> FibUtil.updateLbGroupInfo(dpnId, destinationIp, groupIdKey, groupId.toString(), operTx)), LOG,
1071 "Error updating load-balancing group info");
1072 LOG.trace("LB group {} towards DC-GW installed on dpn {}. Group - {}", groupIdKey, dpnId, group);
1075 private boolean isTunnelUp(String dcGwIp, BigInteger dpnId, Class<? extends TunnelTypeBase> tunnelType) {
1076 String tunnelName = getTunnelRemoteNextHopPointer(dpnId, dcGwIp, tunnelType);
1077 if (tunnelName != null) {
1078 InstanceIdentifier<StateTunnelList> tunnelStateId =
1079 InstanceIdentifier.builder(TunnelsState.class).child(
1080 StateTunnelList.class, new StateTunnelListKey(tunnelName)).build();
1081 return MDSALUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, tunnelStateId)
1082 .toJavaUtil().map(StateTunnelList::getOperState)
1083 .orElse(TunnelOperStatus.Down) == TunnelOperStatus.Up;
1088 private List<Action> getEgressActions(String interfaceName, int actionKey) {
1089 List<Action> actions = Collections.emptyList();
1091 GetEgressActionsForInterfaceInputBuilder egressAction =
1092 new GetEgressActionsForInterfaceInputBuilder().setIntfName(interfaceName).setActionKey(actionKey);
1093 Future<RpcResult<GetEgressActionsForInterfaceOutput>> result =
1094 odlInterfaceRpcService.getEgressActionsForInterface(egressAction.build());
1095 RpcResult<GetEgressActionsForInterfaceOutput> rpcResult = result.get();
1096 if (!rpcResult.isSuccessful()) {
1097 LOG.warn("RPC Call to Get egress actions for interface {} returned with Errors {}",
1098 interfaceName, rpcResult.getErrors());
1100 actions = nullToEmpty(rpcResult.getResult().getAction());
1102 } catch (InterruptedException | ExecutionException e) {
1103 LOG.warn("Exception when egress actions for interface {}", interfaceName, e);
1109 * This method is invoked when the tunnel state is removed from DS.
1110 * If the there is just one DC-GW left in configuration then the LB groups can be deleted.
1111 * Otherwise, the groups are just updated.
1113 public void removeOrUpdateDcGwLoadBalancingGroup(List<String> availableDcGws, BigInteger dpnId,
1114 String destinationIp) {
1115 Preconditions.checkNotNull(availableDcGws, "There are no dc-gws present");
1116 ListenableFutures.addErrorLogging(txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, confTx -> {
1117 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(OPERATIONAL, operTx -> {
1118 int noOfDcGws = availableDcGws.size();
1119 // If availableDcGws does not contain the destination Ip it means this is a configuration delete.
1120 if (!availableDcGws.contains(destinationIp)) {
1121 availableDcGws.add(destinationIp);
1122 Collections.sort(availableDcGws);
1124 // TODO : Place the logic to construct all possible DC-GW combination here.
1125 int bucketId = availableDcGws.indexOf(destinationIp);
1126 Optional<DpnLbNexthops> dpnLbNextHops = fibUtil.getDpnLbNexthops(dpnId, destinationIp);
1127 if (!dpnLbNextHops.isPresent()) {
1130 List<String> nextHopKeys = nullToEmpty(dpnLbNextHops.get().getNexthopKey());
1131 for (String nextHopKey : nextHopKeys) {
1132 Optional<Nexthops> optionalNextHops = fibUtil.getNexthops(nextHopKey);
1133 if (!optionalNextHops.isPresent()) {
1136 Nexthops nexthops = optionalNextHops.get();
1137 final String groupId = nexthops.getGroupId();
1138 final long groupIdValue = Long.parseLong(groupId);
1139 if (noOfDcGws > 1) {
1140 mdsalApiManager.removeBucket(confTx, dpnId, groupIdValue, bucketId);
1142 LOG.trace("Removed LB group {} on dpn {}", groupIdValue, dpnId);
1143 mdsalApiManager.removeGroup(confTx, dpnId, groupIdValue);
1144 removeNextHopPointer(nextHopKey);
1146 // When the DC-GW is removed from configuration.
1147 if (noOfDcGws != availableDcGws.size()) {
1148 FibUtil.removeOrUpdateNextHopInfo(dpnId, nextHopKey, groupId, nexthops, operTx);
1151 FibUtil.removeDpnIdToNextHopInfo(destinationIp, dpnId, operTx);
1152 }), LOG, "Error removing or updating load-balancing group");
1153 }), LOG, "Error removing or updating load-balancing group");
1157 * This method is invoked when the tunnel status is updated.
1158 * The bucket is directly removed/added based on the operational status of the tunnel.
1160 public void updateDcGwLoadBalancingGroup(List<String> availableDcGws,
1161 BigInteger dpnId, String destinationIp, boolean isTunnelUp, Class<? extends TunnelTypeBase> tunnelType) {
1162 Preconditions.checkNotNull(availableDcGws, "There are no dc-gws present");
1163 ListenableFutures.addErrorLogging(txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, confTx -> {
1164 // TODO : Place the logic to construct all possible DC-GW combination here.
1165 int bucketId = availableDcGws.indexOf(destinationIp);
1166 Optional<DpnLbNexthops> dpnLbNextHops = fibUtil.getDpnLbNexthops(dpnId, destinationIp);
1167 if (!dpnLbNextHops.isPresent()) {
1170 List<String> nextHopKeys = nullToEmpty(dpnLbNextHops.get().getNexthopKey());
1171 for (String nextHopKey : nextHopKeys) {
1172 Optional<Nexthops> optionalNextHops = fibUtil.getNexthops(nextHopKey);
1173 if (!optionalNextHops.isPresent()) {
1176 Nexthops nexthops = optionalNextHops.get();
1177 final String groupId = nexthops.getGroupId();
1178 final long groupIdValue = Long.parseLong(groupId);
1180 Bucket bucket = buildBucketForDcGwLbGroup(destinationIp, dpnId, bucketId, tunnelType);
1181 LOG.trace("Added bucket {} to group {} on dpn {}.", bucket, groupId, dpnId);
1182 mdsalApiManager.addBucket(confTx, dpnId, groupIdValue, bucket);
1184 LOG.trace("Removed bucketId {} from group {} on dpn {}.", bucketId, groupId, dpnId);
1185 mdsalApiManager.removeBucket(confTx, dpnId, groupIdValue, bucketId);
1188 }), LOG, "Error updating load-balancing group");
1191 private Bucket buildBucketForDcGwLbGroup(String ipAddress, BigInteger dpnId, int index,
1192 Class<? extends TunnelTypeBase> tunnelType) {
1193 List<Action> listAction = new ArrayList<>();
1194 // ActionKey 0 goes to mpls label.
1196 listAction.add(new ActionPushMpls().buildAction());
1197 listAction.add(new ActionRegMove(actionKey++, FibConstants.NXM_REG_MAPPING
1198 .get(index), 0, 19).buildAction());
1199 String tunnelInterfaceName = getTunnelInterfaceName(dpnId, IpAddressBuilder.getDefaultInstance(ipAddress),
1201 List<Action> egressActions = getEgressActions(tunnelInterfaceName, actionKey++);
1202 if (!egressActions.isEmpty()) {
1203 listAction.addAll(getEgressActions(tunnelInterfaceName, actionKey++));
1205 // clear off actions if there is no egress actions.
1206 listAction = Collections.emptyList();
1208 //OVS expects a non-zero weight value for load balancing to happen in select groups
1209 return MDSALUtil.buildBucket(listAction, SELECT_GROUP_WEIGHT, index,
1210 MDSALUtil.WATCH_PORT, MDSALUtil.WATCH_GROUP);
1213 public void programDcGwLoadBalancingGroup(List<String> availableDcGws, BigInteger dpnId, String destinationIp,
1214 int addRemoveOrUpdate, boolean isTunnelUp,
1215 Class<? extends TunnelTypeBase> tunnelType) {
1216 if (NwConstants.ADD_FLOW == addRemoveOrUpdate) {
1217 createDcGwLoadBalancingGroup(availableDcGws, dpnId, destinationIp, tunnelType);
1218 } else if (NwConstants.DEL_FLOW == addRemoveOrUpdate) {
1219 removeOrUpdateDcGwLoadBalancingGroup(availableDcGws, dpnId, destinationIp);
1220 } else if (NwConstants.MOD_FLOW == addRemoveOrUpdate) {
1221 updateDcGwLoadBalancingGroup(availableDcGws, dpnId, destinationIp, isTunnelUp, tunnelType);