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.NwConstants;
17 import java.math.BigInteger;
18 import java.util.ArrayList;
19 import java.util.List;
20 import java.util.concurrent.Future;
21 import com.google.common.base.Optional;
22 import com.google.common.util.concurrent.Futures;
23 import com.google.common.util.concurrent.FutureCallback;
24 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
25 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
26 import org.opendaylight.yangtools.yang.binding.DataObject;
27 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
28 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.InstanceIdentifierBuilder;
29 import org.opendaylight.yangtools.yang.common.RpcResult;
30 import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
31 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
32 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
33 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnInstances;
34 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.instances.VpnInstance;
35 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.instances.VpnInstanceKey;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupTypes;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.VpnInstance1;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.AllocateIdInput;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.AllocateIdInputBuilder;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.AllocateIdOutput;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.CreateIdPoolInput;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.CreateIdPoolInputBuilder;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.IdManagerService;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.ReleaseIdInput;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.ReleaseIdInputBuilder;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.l3nexthop.rev150409.*;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.l3nexthop.rev150409.l3nexthop.*;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.l3nexthop.rev150409.l3nexthop.tunnelnexthops.*;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.l3nexthop.rev150409.l3nexthop.vpnnexthops.*;
50 import org.opendaylight.vpnservice.interfacemgr.interfaces.IInterfaceManager;
51 import org.opendaylight.vpnservice.mdsalutil.ActionInfo;
52 import org.opendaylight.vpnservice.mdsalutil.ActionType;
53 import org.opendaylight.vpnservice.mdsalutil.BucketInfo;
54 import org.opendaylight.vpnservice.mdsalutil.GroupEntity;
55 import org.opendaylight.vpnservice.mdsalutil.MDSALUtil;
56 import org.opendaylight.vpnservice.mdsalutil.interfaces.IMdsalApiManager;
57 import org.opendaylight.idmanager.IdManager;
58 import java.util.concurrent.ExecutionException;
59 import org.slf4j.Logger;
60 import org.slf4j.LoggerFactory;
62 public class NexthopManager implements L3nexthopService, AutoCloseable {
63 private static final Logger LOG = LoggerFactory.getLogger(NexthopManager.class);
64 private final DataBroker broker;
65 private IMdsalApiManager mdsalManager;
66 private IInterfaceManager interfaceManager;
67 private IdManager idManager;
68 private static final short LPORT_INGRESS_TABLE = 0;
69 private static final short LFIB_TABLE = 20;
70 private static final short FIB_TABLE = 21;
71 private static final short DEFAULT_FLOW_PRIORITY = 10;
73 private static final FutureCallback<Void> DEFAULT_CALLBACK =
74 new FutureCallback<Void>() {
75 public void onSuccess(Void result) {
76 LOG.debug("Success in Datastore write operation");
78 public void onFailure(Throwable error) {
79 LOG.error("Error in Datastore write operation", error);
84 * Provides nexthop functions
85 * Creates group ID pool
87 * @param db - dataBroker reference
89 public NexthopManager(final DataBroker db) {
94 public void close() throws Exception {
95 LOG.info("NextHop Manager Closed");
98 public void setInterfaceManager(IInterfaceManager ifManager) {
99 this.interfaceManager = ifManager;
102 public void setMdsalManager(IMdsalApiManager mdsalManager) {
103 this.mdsalManager = mdsalManager;
106 public void setIdManager(IdManager idManager) {
107 this.idManager = idManager;
110 protected void createNexthopPointerPool() {
111 CreateIdPoolInput createPool = new CreateIdPoolInputBuilder()
112 .setPoolName("nextHopPointerPool")
116 //TODO: Error handling
117 Future<RpcResult<Void>> result = idManager.createIdPool(createPool);
118 LOG.trace("NextHopPointerPool result : {}", result);
122 protected long getVpnId(String vpnName) {
123 InstanceIdentifierBuilder<VpnInstance> idBuilder = InstanceIdentifier.builder(VpnInstances.class)
124 .child(VpnInstance.class, new VpnInstanceKey(vpnName));
126 InstanceIdentifier<VpnInstance> id = idBuilder.build();
127 InstanceIdentifier<VpnInstance1> idx = id.augmentation(VpnInstance1.class);
128 Optional<VpnInstance1> vpn = read(LogicalDatastoreType.OPERATIONAL, idx);
130 if (vpn.isPresent()) {
131 LOG.debug("VPN id returned: {}", vpn.get().getVpnId());
132 return vpn.get().getVpnId();
138 private BigInteger getDpnId(String ofPortId) {
139 String[] fields = ofPortId.split(":");
140 BigInteger dpn = new BigInteger(fields[1]);
141 LOG.debug("DpnId: {}", dpn);
145 protected long createNextHopPointer(String nexthopKey) {
146 AllocateIdInput getIdInput = new AllocateIdInputBuilder()
147 .setPoolName("nextHopPointerPool").setIdKey(nexthopKey)
149 //TODO: Proper error handling once IdManager code is complete
151 Future<RpcResult<AllocateIdOutput>> result = idManager.allocateId(getIdInput);
152 RpcResult<AllocateIdOutput> rpcResult = result.get();
153 return rpcResult.getResult().getIdValue();
154 } catch (NullPointerException | InterruptedException | ExecutionException e) {
160 public void createLocalNextHop(String ifName, String vpnName, String ipAddress, String macAddress) {
161 String nhKey = new String("nexthop." + vpnName + ipAddress);
162 long groupId = createNextHopPointer(nhKey);
164 long vpnId = getVpnId(vpnName);
165 BigInteger dpnId = interfaceManager.getDpnForInterface(ifName);
166 VpnNexthop nexthop = getVpnNexthop(vpnId, ipAddress, 0);
167 LOG.trace("nexthop: {}", nexthop);
168 if (nexthop == null) {
169 List<BucketInfo> listBucketInfo = new ArrayList<BucketInfo>();
170 List<ActionInfo> listActionInfo = interfaceManager.getInterfaceEgressActions(ifName);
171 BucketInfo bucket = new BucketInfo(listActionInfo);
173 if (macAddress != null) {
174 listActionInfo.add(0, new ActionInfo(ActionType.set_field_eth_dest, new String[]{macAddress}));
175 listActionInfo.add(0, new ActionInfo(ActionType.pop_mpls, new String[]{}));
177 //FIXME: Log message here.
178 LOG.debug("mac address for new local nexthop is null");
180 listBucketInfo.add(bucket);
181 GroupEntity groupEntity = MDSALUtil.buildGroupEntity(
182 dpnId, groupId, ipAddress, GroupTypes.GroupIndirect, listBucketInfo);
185 mdsalManager.installGroup(groupEntity);
188 addVpnNexthopToDS(vpnId, ipAddress, groupId);
194 public void createRemoteNextHop(String ifName, String ipAddress) {
195 String nhKey = new String("nexthop." + ifName + ipAddress);
196 long groupId = createNextHopPointer(nhKey);
198 BigInteger dpnId = interfaceManager.getDpnForInterface(ifName);
199 TunnelNexthop nexthop = getTunnelNexthop(dpnId, ipAddress);
200 if (nexthop == null) {
201 List<BucketInfo> listBucketInfo = new ArrayList<BucketInfo>();
202 List<ActionInfo> listActionInfo = interfaceManager.getInterfaceEgressActions(ifName);
203 BucketInfo bucket = new BucketInfo(listActionInfo);
205 listBucketInfo.add(bucket);
206 GroupEntity groupEntity = MDSALUtil.buildGroupEntity(
207 dpnId, groupId, ipAddress, GroupTypes.GroupIndirect, listBucketInfo);
208 mdsalManager.installGroup(groupEntity);
209 //makeRemoteFlow(dpnId, ifName, NwConstants.ADD_FLOW);
212 addTunnelNexthopToDS(dpnId, ipAddress, groupId);
218 private void makeRemoteFlow(BigInteger dpnId, String ifName, int addOrRemoveFlow) {
220 String flowName = ifName;
221 String flowRef = getTunnelInterfaceFlowRef(dpnId, LPORT_INGRESS_TABLE, ifName);
222 List<MatchInfo> matches = new ArrayList<MatchInfo>();
223 List<InstructionInfo> mkInstructions = new ArrayList<InstructionInfo>();
224 if (NwConstants.ADD_FLOW == addOrRemoveFlow) {
225 portNo = interfaceManager.getPortForInterface(ifName);
226 matches.add(new MatchInfo(MatchFieldType.in_port, new BigInteger[] {
227 dpnId, BigInteger.valueOf(portNo) }));
228 mkInstructions.add(new InstructionInfo(InstructionType.goto_table, new long[] {LFIB_TABLE}));
231 BigInteger COOKIE_VM_INGRESS_TABLE = new BigInteger("8000001", 16);
232 FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpnId, LPORT_INGRESS_TABLE, flowRef,
233 DEFAULT_FLOW_PRIORITY, flowName, 0, 0, COOKIE_VM_INGRESS_TABLE, matches, mkInstructions);
235 if (NwConstants.ADD_FLOW == addOrRemoveFlow) {
236 mdsalManager.installFlow(flowEntity);
238 mdsalManager.removeFlow(flowEntity);
242 private String getTunnelInterfaceFlowRef(BigInteger dpnId, short tableId, String ifName) {
243 return new StringBuilder().append(dpnId).append(tableId).append(ifName).toString();
246 protected void addVpnNexthopToDS(long vpnId, String ipPrefix, long egressPointer) {
248 InstanceIdentifierBuilder<VpnNexthops> idBuilder = InstanceIdentifier.builder(
250 .child(VpnNexthops.class, new VpnNexthopsKey(vpnId));
252 // Add nexthop to vpn node
253 VpnNexthop nh = new VpnNexthopBuilder().
254 setKey(new VpnNexthopKey(ipPrefix)).
255 setIpAddress(ipPrefix).
256 setEgressPointer(egressPointer).build();
258 InstanceIdentifier<VpnNexthop> id1 = idBuilder
259 .child(VpnNexthop.class, new VpnNexthopKey(ipPrefix)).build();
260 LOG.trace("Adding vpnnextHop {} to Operational DS", nh);
261 syncWrite(LogicalDatastoreType.OPERATIONAL, id1, nh, DEFAULT_CALLBACK);
265 private void addTunnelNexthopToDS(BigInteger dpnId, String ipPrefix, long egressPointer) {
266 InstanceIdentifierBuilder<TunnelNexthops> idBuilder = InstanceIdentifier.builder(L3nexthop.class)
267 .child(TunnelNexthops.class, new TunnelNexthopsKey(dpnId));
269 // Add nexthop to dpn node
270 TunnelNexthop nh = new TunnelNexthopBuilder().
271 setKey(new TunnelNexthopKey(ipPrefix)).
272 setIpAddress(ipPrefix).
273 setEgressPointer(egressPointer).build();
275 InstanceIdentifier<TunnelNexthop> id1 = idBuilder
276 .child(TunnelNexthop.class, new TunnelNexthopKey(ipPrefix)).build();
277 LOG.trace("Adding tunnelnextHop {} to Operational DS for a dpn node", nh);
278 asyncWrite(LogicalDatastoreType.OPERATIONAL, id1, nh, DEFAULT_CALLBACK);
282 protected VpnNexthop getVpnNexthop(long vpnId, String ipAddress, int retryCount) {
284 // check if vpn node is there
285 InstanceIdentifierBuilder<VpnNexthops> idBuilder =
286 InstanceIdentifier.builder(L3nexthop.class).child(VpnNexthops.class, new VpnNexthopsKey(vpnId));
287 InstanceIdentifier<VpnNexthops> id = idBuilder.build();
289 for (int retry = 0; retry <= retryCount; retry++) {
290 Optional<VpnNexthops> vpnNexthops = read(LogicalDatastoreType.OPERATIONAL, id);
291 if (vpnNexthops.isPresent()) {
293 // get nexthops list for vpn
294 List<VpnNexthop> nexthops = vpnNexthops.get().getVpnNexthop();
295 for (VpnNexthop nexthop : nexthops) {
296 if (nexthop.getIpAddress().equals(ipAddress)) {
298 LOG.trace("VpnNextHop : {}", nexthop);
305 } catch (InterruptedException e) {
308 // return null if not found
312 private TunnelNexthop getTunnelNexthop(BigInteger dpnId, String ipAddress) {
314 InstanceIdentifierBuilder<TunnelNexthops> idBuilder = InstanceIdentifier.builder(L3nexthop.class)
315 .child(TunnelNexthops.class, new TunnelNexthopsKey(dpnId));
317 // check if vpn node is there
318 InstanceIdentifier<TunnelNexthops> id = idBuilder.build();
319 Optional<TunnelNexthops> dpnNexthops = read(LogicalDatastoreType.OPERATIONAL, id);
320 if (dpnNexthops.isPresent()) {
321 List<TunnelNexthop> nexthops = dpnNexthops.get().getTunnelNexthop();
322 for (TunnelNexthop nexthop : nexthops) {
323 if (nexthop.getIpAddress().equals(ipAddress)) {
324 LOG.trace("TunnelNextHop : {}",nexthop);
332 public long getNextHopPointer(BigInteger dpnId, long vpnId, String prefixIp, String nextHopIp) {
333 String endpointIp = interfaceManager.getEndpointIpForDpn(dpnId);
334 if (nextHopIp.equals(endpointIp)) {
335 VpnNexthop vpnNextHop = getVpnNexthop(vpnId, prefixIp, 0);
336 return vpnNextHop.getEgressPointer();
338 TunnelNexthop tunnelNextHop = getTunnelNexthop(dpnId, nextHopIp);
339 LOG.trace("NExtHopPointer : {}", tunnelNextHop.getEgressPointer());
340 return tunnelNextHop.getEgressPointer();
344 private void removeTunnelNexthopFromDS(BigInteger dpnId, String ipPrefix) {
346 InstanceIdentifierBuilder<TunnelNexthop> idBuilder = InstanceIdentifier.builder(L3nexthop.class)
347 .child(TunnelNexthops.class, new TunnelNexthopsKey(dpnId))
348 .child(TunnelNexthop.class, new TunnelNexthopKey(ipPrefix));
349 InstanceIdentifier<TunnelNexthop> id = idBuilder.build();
351 LOG.trace("Removing tunnel next hop from datastore : {}", id);
352 delete(LogicalDatastoreType.OPERATIONAL, id);
355 private void removeVpnNexthopFromDS(long vpnId, String ipPrefix) {
357 InstanceIdentifierBuilder<VpnNexthop> idBuilder = InstanceIdentifier.builder(L3nexthop.class)
358 .child(VpnNexthops.class, new VpnNexthopsKey(vpnId))
359 .child(VpnNexthop.class, new VpnNexthopKey(ipPrefix));
360 InstanceIdentifier<VpnNexthop> id = idBuilder.build();
362 LOG.trace("Removing vpn next hop from datastore : {}", id);
363 delete(LogicalDatastoreType.OPERATIONAL, id);
367 public void removeLocalNextHop(BigInteger dpnId, Long vpnId, String ipAddress) {
369 VpnNexthop nh = getVpnNexthop(vpnId, ipAddress, 0);
371 // how to inform and remove dependent FIB entries??
372 // we need to do it before the group is removed
373 GroupEntity groupEntity = MDSALUtil.buildGroupEntity(
374 dpnId, nh.getEgressPointer(), ipAddress, GroupTypes.GroupIndirect, null);
376 mdsalManager.removeGroup(groupEntity);
378 removeVpnNexthopFromDS(vpnId, ipAddress);
381 LOG.error("removal of local next hop failed");
386 public void removeRemoteNextHop(BigInteger dpnId, String ifName, String ipAddress) {
388 TunnelNexthop nh = getTunnelNexthop(dpnId, ipAddress);
390 // how to inform and remove dependent FIB entries??
391 // we need to do it before the group is removed
394 GroupEntity groupEntity = MDSALUtil.buildGroupEntity(
395 dpnId, nh.getEgressPointer(), ipAddress, GroupTypes.GroupIndirect, null);
397 mdsalManager.removeGroup(groupEntity);
398 //makeRemoteFlow(dpnId, ifName, NwConstants.DEL_FLOW);
400 removeTunnelNexthopFromDS(dpnId, ipAddress);
403 LOG.error("removal of remote next hop failed : dpnid : {}, ipaddress : {}", dpnId, ipAddress);
409 public Future<RpcResult<GetEgressPointerOutput>> getEgressPointer(
410 GetEgressPointerInput input) {
412 GetEgressPointerOutputBuilder output = new GetEgressPointerOutputBuilder();
414 String endpointIp = interfaceManager.getEndpointIpForDpn(input.getDpnId());
415 LOG.trace("getEgressPointer: input {}, endpointIp {}", input, endpointIp);
416 if (input.getNexthopIp() == null || input.getNexthopIp().equals(endpointIp)) {
417 VpnNexthop vpnNextHop = getVpnNexthop(input.getVpnId(), input.getIpPrefix(), 5);
418 output.setEgressPointer(vpnNextHop.getEgressPointer());
419 output.setLocalDestination(true);
421 TunnelNexthop tunnelNextHop = getTunnelNexthop(input.getDpnId(), input.getNexthopIp());
422 output.setEgressPointer(tunnelNextHop.getEgressPointer());
423 output.setLocalDestination(false);
426 RpcResultBuilder<GetEgressPointerOutput> rpcResultBuilder = RpcResultBuilder.success();
427 rpcResultBuilder.withResult(output.build());
429 return Futures.immediateFuture(rpcResultBuilder.build());
433 <T extends DataObject> Optional<T> read(LogicalDatastoreType datastoreType,
434 InstanceIdentifier<T> path) {
436 ReadOnlyTransaction tx = broker.newReadOnlyTransaction();
438 Optional<T> result = Optional.absent();
440 result = tx.read(datastoreType, path).get();
441 } catch (Exception e) {
442 throw new RuntimeException(e);
448 private <T extends DataObject> void asyncWrite(LogicalDatastoreType datastoreType,
449 InstanceIdentifier<T> path, T data, FutureCallback<Void> callback) {
450 WriteTransaction tx = broker.newWriteOnlyTransaction();
451 tx.merge(datastoreType, path, data, true);
452 Futures.addCallback(tx.submit(), callback);
455 private <T extends DataObject> void syncWrite(LogicalDatastoreType datastoreType,
456 InstanceIdentifier<T> path, T data, FutureCallback<Void> callback) {
457 WriteTransaction tx = broker.newWriteOnlyTransaction();
458 tx.merge(datastoreType, path, data, true);
462 private <T extends DataObject> void delete(LogicalDatastoreType datastoreType, InstanceIdentifier<T> path) {
463 WriteTransaction tx = broker.newWriteOnlyTransaction();
464 tx.delete(datastoreType, path);
465 Futures.addCallback(tx.submit(), DEFAULT_CALLBACK);
469 public Future<RpcResult<Void>> removeLocalNextHop(RemoveLocalNextHopInput input) {
470 VpnNexthop vpnNextHop = getVpnNexthop(input.getVpnId(), input.getIpPrefix(), 0);
471 RpcResultBuilder<Void> rpcResultBuilder;
472 LOG.debug("vpnnexthop is: {}", vpnNextHop);
474 removeLocalNextHop(input.getDpnId(),input.getVpnId(), input.getIpPrefix());
475 rpcResultBuilder = RpcResultBuilder.success();
478 LOG.error("Removal of local next hop for vpnNextHop {} failed {}" ,vpnNextHop, e);
479 rpcResultBuilder = RpcResultBuilder.failed();
481 return Futures.immediateFuture(rpcResultBuilder.build());