2 * Copyright (c) 2015 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.nexthopmgr;
10 import org.opendaylight.vpnservice.mdsalutil.FlowEntity;
11 import org.opendaylight.vpnservice.mdsalutil.InstructionInfo;
12 import org.opendaylight.vpnservice.mdsalutil.InstructionType;
13 import org.opendaylight.vpnservice.mdsalutil.MatchFieldType;
14 import org.opendaylight.vpnservice.mdsalutil.MatchInfo;
15 import org.opendaylight.vpnservice.mdsalutil.MetaDataUtil;
16 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
17 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rev150331.L3tunnel;
18 import java.math.BigInteger;
19 import java.util.ArrayList;
20 import java.util.List;
21 import java.util.concurrent.Future;
22 import com.google.common.base.Optional;
23 import com.google.common.util.concurrent.Futures;
24 import com.google.common.util.concurrent.FutureCallback;
25 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
26 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
27 import org.opendaylight.yangtools.yang.binding.DataObject;
28 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
29 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.InstanceIdentifierBuilder;
30 import org.opendaylight.yangtools.yang.common.RpcResult;
31 import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
32 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
33 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
34 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnInstances;
35 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.instances.VpnInstance;
36 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.instances.VpnInstanceKey;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupTypes;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.VpnInstance1;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.CreateIdPoolInput;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.CreateIdPoolInputBuilder;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.GetUniqueIdOutput;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.GetUniqueIdInputBuilder;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.GetUniqueIdInput;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.l3nexthop.rev150409.*;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.l3nexthop.rev150409.l3nexthop.*;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.l3nexthop.rev150409.l3nexthop.tunnelnexthops.*;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.l3nexthop.rev150409.l3nexthop.vpnnexthops.*;
48 import org.opendaylight.vpnservice.interfacemgr.interfaces.IInterfaceManager;
49 import org.opendaylight.vpnservice.mdsalutil.ActionInfo;
50 import org.opendaylight.vpnservice.mdsalutil.ActionType;
51 import org.opendaylight.vpnservice.mdsalutil.BucketInfo;
52 import org.opendaylight.vpnservice.mdsalutil.GroupEntity;
53 import org.opendaylight.vpnservice.mdsalutil.MDSALUtil;
54 import org.opendaylight.vpnservice.mdsalutil.interfaces.IMdsalApiManager;
55 import org.opendaylight.idmanager.IdManager;
56 import java.util.concurrent.ExecutionException;
57 import org.slf4j.Logger;
58 import org.slf4j.LoggerFactory;
60 public class NexthopManager implements L3nexthopService, AutoCloseable {
61 private static final Logger LOG = LoggerFactory.getLogger(NexthopManager.class);
62 private final DataBroker broker;
63 private IMdsalApiManager mdsalManager;
64 private IInterfaceManager interfaceManager;
65 private IdManager idManager;
66 private static final short LPORT_INGRESS_TABLE = 0;
67 private static final short LFIB_TABLE = 20;
68 private static final short FIB_TABLE = 21;
69 private static final short DEFAULT_FLOW_PRIORITY = 10;
71 private static final FutureCallback<Void> DEFAULT_CALLBACK =
72 new FutureCallback<Void>() {
73 public void onSuccess(Void result) {
74 LOG.debug("Success in Datastore write operation");
76 public void onFailure(Throwable error) {
77 LOG.error("Error in Datastore write operation", error);
82 * Provides nexthop functions
83 * Creates group ID pool
85 * @param db - dataBroker reference
87 public NexthopManager(final DataBroker db) {
92 public void close() throws Exception {
93 LOG.info("NextHop Manager Closed");
96 public void setInterfaceManager(IInterfaceManager ifManager) {
97 this.interfaceManager = ifManager;
100 public void setMdsalManager(IMdsalApiManager mdsalManager) {
101 this.mdsalManager = mdsalManager;
104 public void setIdManager(IdManager idManager) {
105 this.idManager = idManager;
108 protected void createNexthopPointerPool() {
109 CreateIdPoolInput createPool = new CreateIdPoolInputBuilder()
110 .setPoolName("nextHopPointerPool")
112 .setPoolSize(new BigInteger("65535"))
114 //TODO: Error handling
115 Future<RpcResult<Void>> result = idManager.createIdPool(createPool);
116 LOG.trace("NextHopPointerPool result : {}", result);
118 // LOG.info("Result2: {}",result.get());
119 // } catch (InterruptedException | ExecutionException e) {
120 // // TODO Auto-generated catch block
121 // LOG.error("Error in result.get");
127 protected long getVpnId(String vpnName) {
128 InstanceIdentifierBuilder<VpnInstance> idBuilder = InstanceIdentifier.builder(VpnInstances.class)
129 .child(VpnInstance.class, new VpnInstanceKey(vpnName));
131 InstanceIdentifier<VpnInstance> id = idBuilder.build();
132 InstanceIdentifier<VpnInstance1> idx = id.augmentation(VpnInstance1.class);
133 Optional<VpnInstance1> vpn = read(LogicalDatastoreType.OPERATIONAL, idx);
135 if (vpn.isPresent()) {
136 LOG.debug("VPN id returned: {}", vpn.get().getVpnId());
137 return vpn.get().getVpnId();
143 private long getDpnId(String ifName) {
144 String[] fields = ifName.split(":");
145 long dpn = Integer.parseInt(fields[1]);
146 LOG.debug("DpnId: {}", dpn);
150 protected int createNextHopPointer(String nexthopKey) {
151 GetUniqueIdInput getIdInput = new GetUniqueIdInputBuilder()
152 .setPoolName("nextHopPointerPool").setIdKey(nexthopKey)
154 //TODO: Proper error handling once IdManager code is complete
156 Future<RpcResult<GetUniqueIdOutput>> result = idManager.getUniqueId(getIdInput);
157 RpcResult<GetUniqueIdOutput> rpcResult = result.get();
158 return rpcResult.getResult().getIdValue().intValue();
159 } catch (NullPointerException | InterruptedException | ExecutionException e) {
165 public void createLocalNextHop(String ifName, String vpnName, String ipAddress, String macAddress) {
166 String nhKey = new String("nexthop." + vpnName + ipAddress);
167 int groupId = createNextHopPointer(nhKey);
169 long vpnId = getVpnId(vpnName);
170 long dpnId = interfaceManager.getDpnForInterface(ifName);
171 VpnNexthop nexthop = getVpnNexthop(vpnId, ipAddress, 0);
172 LOG.trace("nexthop: {}", nexthop);
173 if (nexthop == null) {
174 List<BucketInfo> listBucketInfo = new ArrayList<BucketInfo>();
175 List<ActionInfo> listActionInfo = interfaceManager.getInterfaceEgressActions(ifName);
176 BucketInfo bucket = new BucketInfo(listActionInfo);
178 if (macAddress != null) {
179 listActionInfo.add(0, new ActionInfo(ActionType.set_field_eth_dest, new String[]{macAddress}));
180 listActionInfo.add(0, new ActionInfo(ActionType.pop_mpls, new String[]{}));
182 //FIXME: Log message here.
183 LOG.debug("mac address for new local nexthop is null");
185 listBucketInfo.add(bucket);
186 GroupEntity groupEntity = MDSALUtil.buildGroupEntity(
187 dpnId, groupId, ipAddress, GroupTypes.GroupIndirect, listBucketInfo);
190 mdsalManager.installGroup(groupEntity);
193 addVpnNexthopToDS(vpnId, ipAddress, groupId);
199 public void createRemoteNextHop(String ifName, String ofPortId, String ipAddress) {
200 String nhKey = new String("nexthop." + ifName + ipAddress);
201 int groupId = createNextHopPointer(nhKey);
203 long dpnId = getDpnId(ofPortId);
204 TunnelNexthop nexthop = getTunnelNexthop(dpnId, ipAddress);
205 if (nexthop == null) {
206 List<BucketInfo> listBucketInfo = new ArrayList<BucketInfo>();
207 List<ActionInfo> listActionInfo = interfaceManager.getInterfaceEgressActions(ifName);
208 BucketInfo bucket = new BucketInfo(listActionInfo);
210 listBucketInfo.add(bucket);
211 GroupEntity groupEntity = MDSALUtil.buildGroupEntity(
212 dpnId, groupId, ipAddress, GroupTypes.GroupIndirect, listBucketInfo);
213 mdsalManager.installGroup(groupEntity);
214 addRemoteFlow(dpnId, ifName);
217 addTunnelNexthopToDS(dpnId, ipAddress, groupId);
223 private void addRemoteFlow(long dpId, String ifName) {
225 long portNo = interfaceManager.getPortForInterface(ifName);
226 String flowRef = getTunnelInterfaceFlowRef(dpId, LPORT_INGRESS_TABLE, portNo);
228 String flowName = ifName;
229 BigInteger COOKIE_VM_INGRESS_TABLE = new BigInteger("8000001", 16);
231 int priority = DEFAULT_FLOW_PRIORITY;
232 short gotoTableId = LFIB_TABLE;
234 List<InstructionInfo> mkInstructions = new ArrayList<InstructionInfo>();
235 mkInstructions.add(new InstructionInfo(InstructionType.goto_table, new long[] { gotoTableId }));
237 List<MatchInfo> matches = new ArrayList<MatchInfo>();
238 matches.add(new MatchInfo(MatchFieldType.in_port, new long[] {
241 FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, LPORT_INGRESS_TABLE, flowRef,
242 priority, flowName, 0, 0, COOKIE_VM_INGRESS_TABLE, matches, mkInstructions);
244 mdsalManager.installFlow(flowEntity);
247 private String getTunnelInterfaceFlowRef(long dpId, short tableId, long portNo) {
248 return new StringBuilder().append(dpId).append(tableId).append(portNo).toString();
251 protected void addVpnNexthopToDS(long vpnId, String ipPrefix, long egressPointer) {
253 InstanceIdentifierBuilder<VpnNexthops> idBuilder = InstanceIdentifier.builder(L3nexthop.class)
254 .child(VpnNexthops.class, new VpnNexthopsKey(vpnId));
256 // Add nexthop to vpn node
257 VpnNexthop nh = new VpnNexthopBuilder().
258 setKey(new VpnNexthopKey(ipPrefix)).
259 setIpAddress(ipPrefix).
260 setEgressPointer(egressPointer).build();
262 InstanceIdentifier<VpnNexthop> id1 = idBuilder
263 .child(VpnNexthop.class, new VpnNexthopKey(ipPrefix)).build();
264 LOG.trace("Adding vpnnextHop {} to Operational DS", nh);
265 syncWrite(LogicalDatastoreType.OPERATIONAL, id1, nh, DEFAULT_CALLBACK);
269 private void addTunnelNexthopToDS(long dpnId, String ipPrefix, long egressPointer) {
270 InstanceIdentifierBuilder<TunnelNexthops> idBuilder = InstanceIdentifier.builder(L3nexthop.class)
271 .child(TunnelNexthops.class, new TunnelNexthopsKey(dpnId));
273 // Add nexthop to dpn node
274 TunnelNexthop nh = new TunnelNexthopBuilder().
275 setKey(new TunnelNexthopKey(ipPrefix)).
276 setIpAddress(ipPrefix).
277 setEgressPointer(egressPointer).build();
279 InstanceIdentifier<TunnelNexthop> id1 = idBuilder
280 .child(TunnelNexthop.class, new TunnelNexthopKey(ipPrefix)).build();
281 LOG.trace("Adding tunnelnextHop {} to Operational DS for a dpn node", nh);
282 asyncWrite(LogicalDatastoreType.OPERATIONAL, id1, nh, DEFAULT_CALLBACK);
286 protected VpnNexthop getVpnNexthop(long vpnId, String ipAddress, int retryCount) {
288 // check if vpn node is there
289 InstanceIdentifierBuilder<VpnNexthops> idBuilder =
290 InstanceIdentifier.builder(L3nexthop.class).child(VpnNexthops.class, new VpnNexthopsKey(vpnId));
291 InstanceIdentifier<VpnNexthops> id = idBuilder.build();
293 for (int retry = 0; retry <= retryCount; retry++) {
294 Optional<VpnNexthops> vpnNexthops = read(LogicalDatastoreType.OPERATIONAL, id);
295 if (vpnNexthops.isPresent()) {
297 // get nexthops list for vpn
298 List<VpnNexthop> nexthops = vpnNexthops.get().getVpnNexthop();
299 for (VpnNexthop nexthop : nexthops) {
300 if (nexthop.getIpAddress().equals(ipAddress)) {
302 LOG.trace("VpnNextHop : {}", nexthop);
309 } catch (InterruptedException e) {
312 // return null if not found
316 private TunnelNexthop getTunnelNexthop(long dpnId, String ipAddress) {
318 InstanceIdentifierBuilder<TunnelNexthops> idBuilder = InstanceIdentifier.builder(L3nexthop.class)
319 .child(TunnelNexthops.class, new TunnelNexthopsKey(dpnId));
321 // check if vpn node is there
322 InstanceIdentifier<TunnelNexthops> id = idBuilder.build();
323 Optional<TunnelNexthops> dpnNexthops = read(LogicalDatastoreType.OPERATIONAL, id);
324 if (dpnNexthops.isPresent()) {
325 List<TunnelNexthop> nexthops = dpnNexthops.get().getTunnelNexthop();
326 for (TunnelNexthop nexthop : nexthops) {
327 if (nexthop.getIpAddress().equals(ipAddress)) {
328 LOG.trace("TunnelNextHop : {}",nexthop);
336 public long getNextHopPointer(long dpnId, long vpnId, String prefixIp, String nextHopIp) {
337 String endpointIp = interfaceManager.getEndpointIpForDpn(dpnId);
338 if (nextHopIp.equals(endpointIp)) {
339 VpnNexthop vpnNextHop = getVpnNexthop(vpnId, prefixIp, 0);
340 return vpnNextHop.getEgressPointer();
342 TunnelNexthop tunnelNextHop = getTunnelNexthop(dpnId, nextHopIp);
343 LOG.trace("NExtHopPointer : {}", tunnelNextHop.getEgressPointer());
344 return tunnelNextHop.getEgressPointer();
348 private void removeTunnelNexthopFromDS(long dpnId, String ipPrefix) {
350 InstanceIdentifierBuilder<TunnelNexthop> idBuilder = InstanceIdentifier.builder(L3nexthop.class)
351 .child(TunnelNexthops.class, new TunnelNexthopsKey(dpnId))
352 .child(TunnelNexthop.class, new TunnelNexthopKey(ipPrefix));
353 InstanceIdentifier<TunnelNexthop> id = idBuilder.build();
355 LOG.trace("Removing tunnel next hop from datastore : {}", id);
356 delete(LogicalDatastoreType.OPERATIONAL, id);
359 private void removeVpnNexthopFromDS(long vpnId, String ipPrefix) {
361 InstanceIdentifierBuilder<VpnNexthop> idBuilder = InstanceIdentifier.builder(L3nexthop.class)
362 .child(VpnNexthops.class, new VpnNexthopsKey(vpnId))
363 .child(VpnNexthop.class, new VpnNexthopKey(ipPrefix));
364 InstanceIdentifier<VpnNexthop> id = idBuilder.build();
366 LOG.trace("Removing vpn next hop from datastore : {}", id);
367 delete(LogicalDatastoreType.OPERATIONAL, id);
371 public void removeLocalNextHop(Long dpId, Long vpnId, String ipAddress) {
373 VpnNexthop nh = getVpnNexthop(vpnId, ipAddress, 0);
375 // how to inform and remove dependent FIB entries??
376 // we need to do it before the group is removed
377 GroupEntity groupEntity = MDSALUtil.buildGroupEntity(
378 dpId, nh.getEgressPointer(), ipAddress, GroupTypes.GroupIndirect, null);
380 mdsalManager.removeGroup(groupEntity);
382 removeVpnNexthopFromDS(vpnId, ipAddress);
385 LOG.error("removal of local next hop failed");
390 public void removeRemoteNextHop(long dpnId, String ipAddress) {
392 TunnelNexthop nh = getTunnelNexthop(dpnId, ipAddress);
394 // how to inform and remove dependent FIB entries??
395 // we need to do it before the group is removed
398 GroupEntity groupEntity = MDSALUtil.buildGroupEntity(
399 dpnId, nh.getEgressPointer(), ipAddress, GroupTypes.GroupIndirect, null);
401 mdsalManager.removeGroup(groupEntity);
403 removeTunnelNexthopFromDS(dpnId, ipAddress);
406 LOG.error("removal of remote next hop failed : dpnid : {}, ipaddress : {}", dpnId, ipAddress);
412 public Future<RpcResult<GetEgressPointerOutput>> getEgressPointer(
413 GetEgressPointerInput input) {
415 GetEgressPointerOutputBuilder output = new GetEgressPointerOutputBuilder();
417 String endpointIp = interfaceManager.getEndpointIpForDpn(input.getDpnId());
418 LOG.trace("getEgressPointer: input {}, endpointIp {}", input, endpointIp);
419 if (input.getNexthopIp().equals(endpointIp)) {
420 VpnNexthop vpnNextHop = getVpnNexthop(input.getVpnId(), input.getIpPrefix(), 5);
421 output.setEgressPointer(vpnNextHop.getEgressPointer());
422 output.setLocalDestination(true);
424 TunnelNexthop tunnelNextHop = getTunnelNexthop(input.getDpnId(), input.getNexthopIp());
425 output.setEgressPointer(tunnelNextHop.getEgressPointer());
426 output.setLocalDestination(false);
429 RpcResultBuilder<GetEgressPointerOutput> rpcResultBuilder = RpcResultBuilder.success();
430 rpcResultBuilder.withResult(output.build());
432 return Futures.immediateFuture(rpcResultBuilder.build());
436 private <T extends DataObject> Optional<T> read(LogicalDatastoreType datastoreType,
437 InstanceIdentifier<T> path) {
439 ReadOnlyTransaction tx = broker.newReadOnlyTransaction();
441 Optional<T> result = Optional.absent();
443 result = tx.read(datastoreType, path).get();
444 } catch (Exception e) {
445 throw new RuntimeException(e);
451 private <T extends DataObject> void asyncWrite(LogicalDatastoreType datastoreType,
452 InstanceIdentifier<T> path, T data, FutureCallback<Void> callback) {
453 WriteTransaction tx = broker.newWriteOnlyTransaction();
454 tx.put(datastoreType, path, data, true);
455 Futures.addCallback(tx.submit(), callback);
458 private <T extends DataObject> void syncWrite(LogicalDatastoreType datastoreType,
459 InstanceIdentifier<T> path, T data, FutureCallback<Void> callback) {
460 WriteTransaction tx = broker.newWriteOnlyTransaction();
461 tx.put(datastoreType, path, data, true);
465 private <T extends DataObject> void delete(LogicalDatastoreType datastoreType, InstanceIdentifier<T> path) {
466 WriteTransaction tx = broker.newWriteOnlyTransaction();
467 tx.delete(datastoreType, path);
468 Futures.addCallback(tx.submit(), DEFAULT_CALLBACK);
472 public Future<RpcResult<Void>> removeLocalNextHop(RemoveLocalNextHopInput input) {
473 VpnNexthop vpnNextHop = getVpnNexthop(input.getVpnId(), input.getIpPrefix(), 0);
474 RpcResultBuilder<Void> rpcResultBuilder;
475 LOG.debug("vpnnexthop is: {}", vpnNextHop);
477 removeLocalNextHop(input.getDpnId(),input.getVpnId(), input.getIpPrefix());
478 rpcResultBuilder = RpcResultBuilder.success();
481 LOG.error("Removal of local next hop for vpnNextHop {} failed {}" ,vpnNextHop, e);
482 rpcResultBuilder = RpcResultBuilder.failed();
484 return Futures.immediateFuture(rpcResultBuilder.build());