2 * Copyright (c) 2015 - 2016 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.vpnservice.fibmanager;
10 import com.google.common.base.Optional;
11 import com.google.common.util.concurrent.FutureCallback;
12 import com.google.common.util.concurrent.Futures;
14 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
15 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
16 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
17 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
18 import org.opendaylight.vpnservice.mdsalutil.ActionInfo;
19 import org.opendaylight.vpnservice.mdsalutil.ActionType;
20 import org.opendaylight.vpnservice.mdsalutil.BucketInfo;
21 import org.opendaylight.vpnservice.mdsalutil.GroupEntity;
22 import org.opendaylight.vpnservice.mdsalutil.MDSALUtil;
23 import org.opendaylight.vpnservice.mdsalutil.interfaces.IMdsalApiManager;
24 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnInterfaces;
25 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterface;
26 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterfaceKey;
27 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddressBuilder;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.OutputActionCase;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.PushVlanActionCase;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetFieldCase;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupTypes;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.Adjacencies;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.adjacency.list.Adjacency;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.adjacency.list.AdjacencyKey;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.itm.rpcs.rev151217.GetTunnelInterfaceIdInputBuilder;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.itm.rpcs.rev151217.GetTunnelInterfaceIdOutput;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.itm.rpcs.rev151217.ItmRpcService;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.AllocateIdInput;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.AllocateIdInputBuilder;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.AllocateIdOutput;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.CreateIdPoolInput;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.CreateIdPoolInputBuilder;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.IdManagerService;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.ReleaseIdInput;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.ReleaseIdInputBuilder;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rpcs.rev151003.GetEgressActionsForInterfaceInputBuilder;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rpcs.rev151003.GetEgressActionsForInterfaceOutput;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rpcs.rev151003.OdlInterfaceRpcService;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.l3nexthop.rev150409.L3nexthop;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.l3nexthop.rev150409.l3nexthop.VpnNexthops;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.l3nexthop.rev150409.l3nexthop.VpnNexthopsKey;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.l3nexthop.rev150409.l3nexthop.vpnnexthops.VpnNexthop;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.l3nexthop.rev150409.l3nexthop.vpnnexthops.VpnNexthopBuilder;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.l3nexthop.rev150409.l3nexthop.vpnnexthops.VpnNexthopKey;
56 import org.opendaylight.yangtools.yang.binding.DataObject;
57 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
58 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.InstanceIdentifierBuilder;
59 import org.opendaylight.yangtools.yang.common.RpcResult;
60 import org.slf4j.Logger;
61 import org.slf4j.LoggerFactory;
63 import java.math.BigInteger;
64 import java.util.ArrayList;
65 import java.util.List;
66 import java.util.concurrent.ExecutionException;
67 import java.util.concurrent.Future;
69 public class NexthopManager implements AutoCloseable {
70 private static final Logger LOG = LoggerFactory.getLogger(NexthopManager.class);
71 private final DataBroker broker;
72 private IMdsalApiManager mdsalManager;
73 private OdlInterfaceRpcService interfaceManager;
74 private ItmRpcService itmManager;
75 private IdManagerService idManager;
76 private static final short LPORT_INGRESS_TABLE = 0;
77 private static final short LFIB_TABLE = 20;
78 private static final short FIB_TABLE = 21;
79 private static final short DEFAULT_FLOW_PRIORITY = 10;
80 private static final String NEXTHOP_ID_POOL_NAME = "nextHopPointerPool";
82 private static final FutureCallback<Void> DEFAULT_CALLBACK =
83 new FutureCallback<Void>() {
84 public void onSuccess(Void result) {
85 LOG.debug("Success in Datastore write operation");
87 public void onFailure(Throwable error) {
88 LOG.error("Error in Datastore write operation", error);
93 * Provides nexthop functions
94 * Creates group ID pool
96 * @param db - dataBroker reference
98 public NexthopManager(final DataBroker db) {
103 public void close() throws Exception {
104 LOG.info("NextHop Manager Closed");
107 public void setInterfaceManager(OdlInterfaceRpcService ifManager) {
108 this.interfaceManager = ifManager;
111 public void setMdsalManager(IMdsalApiManager mdsalManager) {
112 this.mdsalManager = mdsalManager;
115 public void setIdManager(IdManagerService idManager) {
116 this.idManager = idManager;
117 createNexthopPointerPool();
120 public void setITMRpcService(ItmRpcService itmManager) {
121 this.itmManager = itmManager;
124 protected void createNexthopPointerPool() {
125 CreateIdPoolInput createPool = new CreateIdPoolInputBuilder()
126 .setPoolName(NEXTHOP_ID_POOL_NAME)
130 //TODO: Error handling
131 Future<RpcResult<Void>> result = idManager.createIdPool(createPool);
132 LOG.trace("NextHopPointerPool result : {}", result);
135 private BigInteger getDpnId(String ofPortId) {
136 String[] fields = ofPortId.split(":");
137 BigInteger dpn = new BigInteger(fields[1]);
138 LOG.debug("DpnId: {}", dpn);
142 private String getNextHopKey(long vpnId, String ipAddress){
143 String nhKey = new String("nexthop." + vpnId + ipAddress);
147 private String getNextHopKey(String ifName, String ipAddress){
148 String nhKey = new String("nexthop." + ifName + ipAddress);
152 protected long createNextHopPointer(String nexthopKey) {
153 AllocateIdInput getIdInput = new AllocateIdInputBuilder()
154 .setPoolName(NEXTHOP_ID_POOL_NAME).setIdKey(nexthopKey)
156 //TODO: Proper error handling once IdManager code is complete
158 Future<RpcResult<AllocateIdOutput>> result = idManager.allocateId(getIdInput);
159 RpcResult<AllocateIdOutput> rpcResult = result.get();
160 return rpcResult.getResult().getIdValue();
161 } catch (NullPointerException | InterruptedException | ExecutionException e) {
167 protected void removeNextHopPointer(String nexthopKey) {
168 ReleaseIdInput idInput = new ReleaseIdInputBuilder().
169 setPoolName(NEXTHOP_ID_POOL_NAME)
170 .setIdKey(nexthopKey).build();
172 Future<RpcResult<Void>> result = idManager.releaseId(idInput);
173 RpcResult<Void> rpcResult = result.get();
174 if(!rpcResult.isSuccessful()) {
175 LOG.warn("RPC Call to Get Unique Id returned with Errors {}", rpcResult.getErrors());
177 } catch (InterruptedException | ExecutionException e) {
178 LOG.warn("Exception when getting Unique Id for key {}", nexthopKey, e);
182 protected List<ActionInfo> getEgressActionsForInterface(String ifName) {
183 List<ActionInfo> listActionInfo = new ArrayList<ActionInfo>();
185 Future<RpcResult<GetEgressActionsForInterfaceOutput>> result =
186 interfaceManager.getEgressActionsForInterface(
187 new GetEgressActionsForInterfaceInputBuilder().setIntfName(ifName).build());
188 RpcResult<GetEgressActionsForInterfaceOutput> rpcResult = result.get();
189 if(!rpcResult.isSuccessful()) {
190 LOG.warn("RPC Call to Get egress actions for interface {} returned with Errors {}", ifName, rpcResult.getErrors());
192 List<org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action> actions =
193 rpcResult.getResult().getAction();
194 for (Action action : actions) {
195 org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action actionClass = action.getAction();
196 if (actionClass instanceof OutputActionCase) {
197 listActionInfo.add(new ActionInfo(ActionType.output,
198 new String[] {((OutputActionCase)actionClass).getOutputAction()
199 .getOutputNodeConnector().getValue()}));
200 } else if (actionClass instanceof PushVlanActionCase) {
201 listActionInfo.add(new ActionInfo(ActionType.push_vlan, new String[] {}));
202 } else if (actionClass instanceof SetFieldCase) {
203 if (((SetFieldCase)actionClass).getSetField().getVlanMatch() != null) {
204 int vlanVid = ((SetFieldCase)actionClass).getSetField().getVlanMatch().getVlanId().getVlanId().getValue();
205 listActionInfo.add(new ActionInfo(ActionType.set_field_vlan_vid,
206 new String[] { Long.toString(vlanVid) }));
211 } catch (InterruptedException | ExecutionException e) {
212 LOG.warn("Exception when egress actions for interface {}", ifName, e);
214 return listActionInfo;
217 protected Integer getTunnelInterfaceId(BigInteger srcDpId, BigInteger dstDpId) {
218 // FIXME: Enable during itm integration
221 Future<RpcResult<GetTunnelInterfaceIdOutput>> result = itmManager.getTunnelInterfaceId(new GetTunnelInterfaceIdInputBuilder()
222 .setSourceDpid(srcDpId)
223 .setDestinationDpid(dstDpId).build());
224 RpcResult<GetTunnelInterfaceIdOutput> rpcResult = result.get();
225 if(!rpcResult.isSuccessful()) {
226 LOG.warn("RPC Call to getTunnelInterfaceId returned with Errors {}", rpcResult.getErrors());
228 return rpcResult.getResult().getInterfaceid();
230 } catch (InterruptedException | ExecutionException e) {
231 LOG.warn("Exception when getting tunnel interface Id for tunnel between {} and {}", srcDpId, dstDpId, e);
237 public long createLocalNextHop(long vpnId, BigInteger dpnId,
238 String ifName, String ipAddress) {
239 long groupId = createNextHopPointer(getNextHopKey(vpnId, ipAddress));
240 String nextHopLockStr = new String(vpnId + ipAddress);
241 synchronized (nextHopLockStr.intern()) {
242 VpnNexthop nexthop = getVpnNexthop(vpnId, ipAddress);
243 LOG.trace("nexthop: {}", nexthop);
244 if (nexthop == null) {
245 Optional<Adjacency> adjacencyData =
246 read(LogicalDatastoreType.OPERATIONAL, getAdjacencyIdentifier(ifName, ipAddress));
247 String macAddress = adjacencyData.isPresent() ? adjacencyData.get().getMacAddress() : null;
248 List<BucketInfo> listBucketInfo = new ArrayList<BucketInfo>();
249 List<ActionInfo> listActionInfo = getEgressActionsForInterface(ifName);
250 BucketInfo bucket = new BucketInfo(listActionInfo);
252 if (macAddress != null) {
253 listActionInfo.add(0, new ActionInfo(ActionType.set_field_eth_dest, new String[]{macAddress}));
254 //listActionInfo.add(0, new ActionInfo(ActionType.pop_mpls, new String[]{}));
256 //FIXME: Log message here.
257 LOG.debug("mac address for new local nexthop is null");
259 listBucketInfo.add(bucket);
260 GroupEntity groupEntity = MDSALUtil.buildGroupEntity(
261 dpnId, groupId, ipAddress, GroupTypes.GroupIndirect, listBucketInfo);
264 addVpnNexthopToDS(dpnId, vpnId, ipAddress, groupId);
267 // FIXME: mdsalManager.syncInstallGroup(groupEntity);
268 mdsalManager.installGroup(groupEntity);
271 //nexthop exists already; a new flow is going to point to it, increment the flowrefCount by 1
272 int flowrefCnt = nexthop.getFlowrefCount() + 1;
273 VpnNexthop nh = new VpnNexthopBuilder().setKey(new VpnNexthopKey(ipAddress)).setFlowrefCount(flowrefCnt).build();
274 LOG.trace("Updating vpnnextHop {} for refCount {} to Operational DS", nh, flowrefCnt);
275 syncWrite(LogicalDatastoreType.OPERATIONAL, getVpnNextHopIdentifier(vpnId, ipAddress), nh, DEFAULT_CALLBACK);
283 protected void addVpnNexthopToDS(BigInteger dpnId, long vpnId, String ipPrefix, long egressPointer) {
285 InstanceIdentifierBuilder<VpnNexthops> idBuilder = InstanceIdentifier.builder(
287 .child(VpnNexthops.class, new VpnNexthopsKey(vpnId));
289 // Add nexthop to vpn node
290 VpnNexthop nh = new VpnNexthopBuilder().
291 setKey(new VpnNexthopKey(ipPrefix)).
293 setIpAddress(ipPrefix).
295 setEgressPointer(egressPointer).build();
297 InstanceIdentifier<VpnNexthop> id1 = idBuilder
298 .child(VpnNexthop.class, new VpnNexthopKey(ipPrefix)).build();
299 LOG.trace("Adding vpnnextHop {} to Operational DS", nh);
300 syncWrite(LogicalDatastoreType.OPERATIONAL, id1, nh, DEFAULT_CALLBACK);
306 protected InstanceIdentifier<VpnNexthop> getVpnNextHopIdentifier(long vpnId, String ipAddress) {
307 InstanceIdentifier<VpnNexthop> id = InstanceIdentifier.builder(
309 .child(VpnNexthops.class, new VpnNexthopsKey(vpnId)).child(VpnNexthop.class, new VpnNexthopKey(ipAddress)).build();
313 protected VpnNexthop getVpnNexthop(long vpnId, String ipAddress) {
315 // check if vpn node is there
316 InstanceIdentifierBuilder<VpnNexthops> idBuilder =
317 InstanceIdentifier.builder(L3nexthop.class).child(VpnNexthops.class,
318 new VpnNexthopsKey(vpnId));
319 InstanceIdentifier<VpnNexthops> id = idBuilder.build();
320 Optional<VpnNexthops> vpnNexthops = read(LogicalDatastoreType.OPERATIONAL, id);
321 if (vpnNexthops.isPresent()) {
322 // get nexthops list for vpn
323 List<VpnNexthop> nexthops = vpnNexthops.get().getVpnNexthop();
324 for (VpnNexthop nexthop : nexthops) {
325 if (nexthop.getIpAddress().equals(ipAddress)) {
327 LOG.trace("VpnNextHop : {}", nexthop);
331 // return null if not found
337 public List<ActionInfo> getRemoteNextHopPointer(BigInteger localDpnId, BigInteger remoteDpnId,
338 long vpnId, String prefixIp, String nextHopIp) {
339 List<ActionInfo> remoteNextHopActions = null;
340 LOG.trace("getRemoteNextHopPointer: input [localDpnId {} remoteDpnId {}, vpnId {}, prefixIp {}, nextHopIp {} ]",
341 localDpnId, remoteDpnId, vpnId, prefixIp, nextHopIp);
343 // check if the incoming VM is within the same DC. If so, retrieve the local tunnel group pointer.
344 // Else retrieve the tunnel to DC gateway group pointer.
346 if (localDpnId == null || BigInteger.ZERO.equals(localDpnId)) {
347 VpnNexthop vpnNexthop = getVpnNexthop(vpnId, prefixIp);
348 //If the vrf entry is a static/extra route, the nexthop of the entry would be a adjacency in the vpn
349 if(vpnNexthop == null) {
350 vpnNexthop = getVpnNexthop(vpnId, nextHopIp + "/32");
352 localDpnId = (vpnNexthop == null) ? null : vpnNexthop.getDpnId();
354 LOG.trace("getRemoteNextHopPointer: Calling ITM with localDpnId {} ", localDpnId);
356 if(localDpnId != null){
357 Integer interfaceId = getTunnelInterfaceId(remoteDpnId, localDpnId);
358 if(interfaceId != null) {
359 remoteNextHopActions =
360 getEgressActionsForInterface(
361 getTunnelInterfaceId(remoteDpnId, localDpnId).toString());
364 // FIXME: dynamically build and use tunnel to dc gateway.
365 // remoteNextHopActions = itmManager.getEgressOutputForDCGateway(remoteDpnId,
366 // IpAddressBuilder.getDefaultInstance(nextHopIp));
368 }catch(Exception ex){
369 LOG.error("Error while retrieving nexthop pointer for DC Gateway : ", ex.getMessage());
371 return remoteNextHopActions;
374 public BigInteger getDpnForPrefix(long vpnId, String prefixIp) {
375 VpnNexthop vpnNexthop = getVpnNexthop(vpnId, prefixIp);
376 BigInteger localDpnId = (vpnNexthop == null) ? null : vpnNexthop.getDpnId();
381 private void removeVpnNexthopFromDS(long vpnId, String ipPrefix) {
383 InstanceIdentifierBuilder<VpnNexthop> idBuilder = InstanceIdentifier.builder(L3nexthop.class)
384 .child(VpnNexthops.class, new VpnNexthopsKey(vpnId))
385 .child(VpnNexthop.class, new VpnNexthopKey(ipPrefix));
386 InstanceIdentifier<VpnNexthop> id = idBuilder.build();
388 LOG.trace("Removing vpn next hop from datastore : {}", id);
389 delete(LogicalDatastoreType.OPERATIONAL, id);
393 public void removeLocalNextHop(BigInteger dpnId, Long vpnId, String ipAddress) {
395 String nextHopLockStr = new String(vpnId + ipAddress);
396 synchronized (nextHopLockStr.intern()) {
397 VpnNexthop nh = getVpnNexthop(vpnId, ipAddress);
399 int newFlowrefCnt = nh.getFlowrefCount() - 1;
400 if (newFlowrefCnt == 0) { //remove the group only if there are no more flows using this group
401 GroupEntity groupEntity = MDSALUtil.buildGroupEntity(
402 dpnId, nh.getEgressPointer(), ipAddress, GroupTypes.GroupIndirect, null);
404 // FIXME: mdsalManager.syncRemoveGroup(groupEntity);
405 mdsalManager.removeGroup(groupEntity);
407 removeVpnNexthopFromDS(vpnId, ipAddress);
409 removeNextHopPointer(getNextHopKey(vpnId, ipAddress));
410 LOG.debug("Local Next hop for {} on dpn {} successfully deleted", ipAddress, dpnId);
412 //just update the flowrefCount of the vpnNexthop
413 VpnNexthop currNh = new VpnNexthopBuilder().setKey(new VpnNexthopKey(ipAddress)).setFlowrefCount(newFlowrefCnt).build();
414 LOG.trace("Updating vpnnextHop {} for refCount {} to Operational DS", currNh, newFlowrefCnt);
415 syncWrite(LogicalDatastoreType.OPERATIONAL, getVpnNextHopIdentifier(vpnId, ipAddress), currNh, DEFAULT_CALLBACK);
419 LOG.error("Local Next hop for {} on dpn {} not deleted", ipAddress, dpnId);
426 private <T extends DataObject> Optional<T> read(LogicalDatastoreType datastoreType,
427 InstanceIdentifier<T> path) {
429 ReadOnlyTransaction tx = broker.newReadOnlyTransaction();
431 Optional<T> result = Optional.absent();
433 result = tx.read(datastoreType, path).get();
434 } catch (Exception e) {
435 throw new RuntimeException(e);
441 private <T extends DataObject> void asyncWrite(LogicalDatastoreType datastoreType,
442 InstanceIdentifier<T> path, T data, FutureCallback<Void> callback) {
443 WriteTransaction tx = broker.newWriteOnlyTransaction();
444 tx.merge(datastoreType, path, data, true);
445 Futures.addCallback(tx.submit(), callback);
448 private <T extends DataObject> void syncWrite(LogicalDatastoreType datastoreType,
449 InstanceIdentifier<T> path, T data, FutureCallback<Void> callback) {
450 WriteTransaction tx = broker.newWriteOnlyTransaction();
451 tx.merge(datastoreType, path, data, true);
455 private <T extends DataObject> void delete(LogicalDatastoreType datastoreType, InstanceIdentifier<T> path) {
456 WriteTransaction tx = broker.newWriteOnlyTransaction();
457 tx.delete(datastoreType, path);
458 Futures.addCallback(tx.submit(), DEFAULT_CALLBACK);
461 private InstanceIdentifier<Adjacency> getAdjacencyIdentifier(String vpnInterfaceName, String ipAddress) {
462 return InstanceIdentifier.builder(VpnInterfaces.class)
463 .child(VpnInterface.class, new VpnInterfaceKey(vpnInterfaceName)).augmentation(
464 Adjacencies.class).child(Adjacency.class, new AdjacencyKey(ipAddress)).build();