JUnits for Interface Manager
[vpnservice.git] / nexthopmgr / nexthopmgr-impl / src / main / java / org / opendaylight / vpnservice / nexthopmgr / NexthopManager.java
1 /*
2  * Copyright (c) 2015 Ericsson India Global Services Pvt Ltd. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.vpnservice.nexthopmgr;
9
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;
16
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.CreateIdPoolInput;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.CreateIdPoolInputBuilder;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.GetUniqueIdOutput;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.GetUniqueIdInputBuilder;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.GetUniqueIdInput;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.l3nexthop.rev150409.*;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.l3nexthop.rev150409.l3nexthop.*;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.l3nexthop.rev150409.l3nexthop.tunnelnexthops.*;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.l3nexthop.rev150409.l3nexthop.vpnnexthops.*;
47 import org.opendaylight.vpnservice.interfacemgr.interfaces.IInterfaceManager;
48 import org.opendaylight.vpnservice.mdsalutil.ActionInfo;
49 import org.opendaylight.vpnservice.mdsalutil.ActionType;
50 import org.opendaylight.vpnservice.mdsalutil.BucketInfo;
51 import org.opendaylight.vpnservice.mdsalutil.GroupEntity;
52 import org.opendaylight.vpnservice.mdsalutil.MDSALUtil;
53 import org.opendaylight.vpnservice.mdsalutil.interfaces.IMdsalApiManager;
54 import org.opendaylight.idmanager.IdManager;
55 import java.util.concurrent.ExecutionException;
56 import org.slf4j.Logger;
57 import org.slf4j.LoggerFactory;
58
59 public class NexthopManager implements L3nexthopService, AutoCloseable {
60     private static final Logger LOG = LoggerFactory.getLogger(NexthopManager.class);
61     private final DataBroker broker;
62     private IMdsalApiManager mdsalManager;
63     private IInterfaceManager interfaceManager;
64     private IdManager idManager;
65     private static final short LPORT_INGRESS_TABLE = 0;
66     private static final short LFIB_TABLE = 20;
67     private static final short FIB_TABLE = 21;
68     private static final short DEFAULT_FLOW_PRIORITY = 10;
69
70     private static final FutureCallback<Void> DEFAULT_CALLBACK =
71         new FutureCallback<Void>() {
72             public void onSuccess(Void result) {
73                 LOG.debug("Success in Datastore write operation");
74             }
75             public void onFailure(Throwable error) {
76                 LOG.error("Error in Datastore write operation", error);
77             };
78         };
79
80     /**
81     * Provides nexthop functions
82     * Creates group ID pool
83     *
84     * @param db - dataBroker reference
85     */
86     public NexthopManager(final DataBroker db) {
87         broker = db;
88     }
89
90     @Override
91     public void close() throws Exception {
92         LOG.info("NextHop Manager Closed");
93     }
94
95     public void setInterfaceManager(IInterfaceManager ifManager) {
96         this.interfaceManager = ifManager;
97     }
98
99     public void setMdsalManager(IMdsalApiManager mdsalManager) {
100         this.mdsalManager = mdsalManager;
101     }
102
103     public void setIdManager(IdManager idManager) {
104         this.idManager = idManager;
105     }
106
107     protected void createNexthopPointerPool() {
108         CreateIdPoolInput createPool = new CreateIdPoolInputBuilder()
109             .setPoolName("nextHopPointerPool")
110             .setIdStart(1L)
111             .setPoolSize(new BigInteger("65535"))
112             .build();
113         //TODO: Error handling
114         Future<RpcResult<Void>> result = idManager.createIdPool(createPool);
115         LOG.trace("NextHopPointerPool result : {}", result);
116 //            try {
117 //                LOG.info("Result2: {}",result.get());
118 //            } catch (InterruptedException | ExecutionException e) {
119 //                // TODO Auto-generated catch block
120 //                LOG.error("Error in result.get");
121 //            }
122
123     }
124
125
126     protected long getVpnId(String vpnName) {
127         InstanceIdentifierBuilder<VpnInstance> idBuilder = InstanceIdentifier.builder(VpnInstances.class)
128                 .child(VpnInstance.class, new VpnInstanceKey(vpnName));
129
130         InstanceIdentifier<VpnInstance> id = idBuilder.build();
131         InstanceIdentifier<VpnInstance1> idx = id.augmentation(VpnInstance1.class);
132         Optional<VpnInstance1> vpn = read(LogicalDatastoreType.OPERATIONAL, idx);
133
134         if (vpn.isPresent()) {
135             LOG.debug("VPN id returned: {}", vpn.get().getVpnId());
136             return vpn.get().getVpnId();
137         } else {
138             return -1;
139         }
140     }
141
142     private BigInteger getDpnId(String ofPortId) {
143         String[] fields = ofPortId.split(":");
144         BigInteger dpn = new BigInteger(fields[1]);
145         LOG.debug("DpnId: {}", dpn);
146         return dpn;
147     }
148
149     protected int createNextHopPointer(String nexthopKey) {
150         GetUniqueIdInput getIdInput = new GetUniqueIdInputBuilder()
151             .setPoolName("nextHopPointerPool").setIdKey(nexthopKey)
152             .build();
153         //TODO: Proper error handling once IdManager code is complete
154         try {
155             Future<RpcResult<GetUniqueIdOutput>> result = idManager.getUniqueId(getIdInput);
156             RpcResult<GetUniqueIdOutput> rpcResult = result.get();
157             return rpcResult.getResult().getIdValue().intValue();
158         } catch (NullPointerException | InterruptedException | ExecutionException e) {
159             LOG.trace("",e);
160         }
161         return 0;
162     }
163
164     public void createLocalNextHop(String ifName, String vpnName, String ipAddress, String macAddress) {
165         String nhKey = new String("nexthop." + vpnName + ipAddress);
166         int groupId = createNextHopPointer(nhKey);
167
168         long vpnId = getVpnId(vpnName);
169         BigInteger dpnId = interfaceManager.getDpnForInterface(ifName);
170         VpnNexthop nexthop = getVpnNexthop(vpnId, ipAddress, 0);
171         LOG.trace("nexthop: {}", nexthop);
172         if (nexthop == null) {
173             List<BucketInfo> listBucketInfo = new ArrayList<BucketInfo>();
174             List<ActionInfo> listActionInfo = interfaceManager.getInterfaceEgressActions(ifName);
175             BucketInfo bucket = new BucketInfo(listActionInfo);
176             // MAC re-write
177             if (macAddress != null) {
178                listActionInfo.add(0, new ActionInfo(ActionType.set_field_eth_dest, new String[]{macAddress}));
179                listActionInfo.add(0, new ActionInfo(ActionType.pop_mpls, new String[]{}));
180             } else {
181                 //FIXME: Log message here.
182                 LOG.debug("mac address for new local nexthop is null");
183             }
184             listBucketInfo.add(bucket);
185             GroupEntity groupEntity = MDSALUtil.buildGroupEntity(
186                 dpnId, groupId, ipAddress, GroupTypes.GroupIndirect, listBucketInfo);
187
188             // install Group
189             mdsalManager.installGroup(groupEntity);
190
191             //update MD-SAL DS
192             addVpnNexthopToDS(vpnId, ipAddress, groupId);
193         } else {
194             //check update
195         }
196     }
197
198     public void createRemoteNextHop(String ifName, String ipAddress) {
199         String nhKey = new String("nexthop." + ifName + ipAddress);
200         int groupId = createNextHopPointer(nhKey);
201
202         BigInteger dpnId = interfaceManager.getDpnForInterface(ifName);
203         TunnelNexthop nexthop = getTunnelNexthop(dpnId, ipAddress);
204         if (nexthop == null) {
205             List<BucketInfo> listBucketInfo = new ArrayList<BucketInfo>();
206             List<ActionInfo> listActionInfo = interfaceManager.getInterfaceEgressActions(ifName);
207             BucketInfo bucket = new BucketInfo(listActionInfo);
208             // MAC re-write??           
209             listBucketInfo.add(bucket);
210             GroupEntity groupEntity = MDSALUtil.buildGroupEntity(
211                 dpnId, groupId, ipAddress, GroupTypes.GroupIndirect, listBucketInfo);
212             mdsalManager.installGroup(groupEntity);
213             //makeRemoteFlow(dpnId, ifName, NwConstants.ADD_FLOW);
214
215             //update MD-SAL DS
216             addTunnelNexthopToDS(dpnId, ipAddress, groupId);
217         } else {
218             //check update
219         }
220     }
221
222     private void makeRemoteFlow(BigInteger dpnId, String ifName, int addOrRemoveFlow) {
223         long portNo = 0;
224         String flowName = ifName;
225         String flowRef = getTunnelInterfaceFlowRef(dpnId, LPORT_INGRESS_TABLE, ifName);
226         List<MatchInfo> matches = new ArrayList<MatchInfo>();
227         List<InstructionInfo> mkInstructions = new ArrayList<InstructionInfo>();
228         if (NwConstants.ADD_FLOW == addOrRemoveFlow) {
229             portNo = interfaceManager.getPortForInterface(ifName);
230             matches.add(new MatchInfo(MatchFieldType.in_port, new BigInteger[] {
231                 dpnId, BigInteger.valueOf(portNo) }));
232             mkInstructions.add(new InstructionInfo(InstructionType.goto_table, new long[] {LFIB_TABLE}));
233         }
234
235         BigInteger COOKIE_VM_INGRESS_TABLE = new BigInteger("8000001", 16);
236         FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpnId, LPORT_INGRESS_TABLE, flowRef,
237                                                           DEFAULT_FLOW_PRIORITY, flowName, 0, 0, COOKIE_VM_INGRESS_TABLE, matches, mkInstructions);
238
239         if (NwConstants.ADD_FLOW == addOrRemoveFlow) {
240             mdsalManager.installFlow(flowEntity);
241         } else {
242             mdsalManager.removeFlow(flowEntity);
243         }
244     }
245
246     private String getTunnelInterfaceFlowRef(BigInteger dpnId, short tableId, String ifName) {
247                 return new StringBuilder().append(dpnId).append(tableId).append(ifName).toString();
248             }
249
250     protected void addVpnNexthopToDS(long vpnId, String ipPrefix, long egressPointer) {
251
252         InstanceIdentifierBuilder<VpnNexthops> idBuilder = InstanceIdentifier.builder(
253             L3nexthop.class)
254                 .child(VpnNexthops.class, new VpnNexthopsKey(vpnId));
255
256         // Add nexthop to vpn node
257         VpnNexthop nh = new VpnNexthopBuilder().
258                 setKey(new VpnNexthopKey(ipPrefix)).
259                 setIpAddress(ipPrefix).
260                 setEgressPointer(egressPointer).build();
261
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);
266
267     }
268
269     private void addTunnelNexthopToDS(BigInteger dpnId, String ipPrefix, long egressPointer) {
270         InstanceIdentifierBuilder<TunnelNexthops> idBuilder = InstanceIdentifier.builder(L3nexthop.class)
271                 .child(TunnelNexthops.class, new TunnelNexthopsKey(dpnId));
272
273         // Add nexthop to dpn node
274         TunnelNexthop nh = new TunnelNexthopBuilder().
275                 setKey(new TunnelNexthopKey(ipPrefix)).
276                 setIpAddress(ipPrefix).
277                 setEgressPointer(egressPointer).build();
278
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);
283
284     }
285
286     protected VpnNexthop getVpnNexthop(long vpnId, String ipAddress, int retryCount) {
287
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();
292         try {
293             for (int retry = 0; retry <= retryCount; retry++) {
294                 Optional<VpnNexthops> vpnNexthops = read(LogicalDatastoreType.OPERATIONAL, id);
295                 if (vpnNexthops.isPresent()) {
296
297                     // get nexthops list for vpn
298                     List<VpnNexthop> nexthops = vpnNexthops.get().getVpnNexthop();
299                     for (VpnNexthop nexthop : nexthops) {
300                         if (nexthop.getIpAddress().equals(ipAddress)) {
301                             // return nexthop
302                             LOG.trace("VpnNextHop : {}", nexthop);
303                             return nexthop;
304                         }
305                     }
306                 }
307                 Thread.sleep(100L);
308             }
309         } catch (InterruptedException e) {
310             LOG.trace("", e);
311         }
312         // return null if not found
313         return null;
314     }
315
316     private TunnelNexthop getTunnelNexthop(BigInteger dpnId, String ipAddress) {
317         
318         InstanceIdentifierBuilder<TunnelNexthops> idBuilder = InstanceIdentifier.builder(L3nexthop.class)
319                 .child(TunnelNexthops.class, new TunnelNexthopsKey(dpnId));
320
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);
329                     return nexthop;
330                 }
331             }
332         }
333         return null;
334     }
335
336     public long getNextHopPointer(BigInteger 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();
341         } else {
342             TunnelNexthop tunnelNextHop = getTunnelNexthop(dpnId, nextHopIp);
343             LOG.trace("NExtHopPointer : {}", tunnelNextHop.getEgressPointer());
344             return tunnelNextHop.getEgressPointer();
345         }
346     }
347
348     private void removeTunnelNexthopFromDS(BigInteger dpnId, String ipPrefix) {
349
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();
354         // remove from DS     
355         LOG.trace("Removing tunnel next hop from datastore : {}", id);
356         delete(LogicalDatastoreType.OPERATIONAL, id);
357     }
358
359     private void removeVpnNexthopFromDS(long vpnId, String ipPrefix) {
360
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();
365         // remove from DS
366         LOG.trace("Removing vpn next hop from datastore : {}", id);
367         delete(LogicalDatastoreType.OPERATIONAL, id);
368     }
369
370  
371     public void removeLocalNextHop(BigInteger dpnId, Long vpnId, String ipAddress) {
372
373         VpnNexthop nh = getVpnNexthop(vpnId, ipAddress, 0);
374         if (nh != null) {
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                     dpnId, nh.getEgressPointer(), ipAddress, GroupTypes.GroupIndirect, null);
379             // remove Group ...
380             mdsalManager.removeGroup(groupEntity);
381             //update MD-SAL DS
382             removeVpnNexthopFromDS(vpnId, ipAddress);
383         } else {
384             //throw error
385             LOG.error("removal of local next hop failed");
386         }
387
388     }
389
390     public void removeRemoteNextHop(BigInteger dpnId, String ifName, String ipAddress) {
391
392         TunnelNexthop nh = getTunnelNexthop(dpnId, ipAddress);
393         if (nh != null) {
394             // how to inform and remove dependent FIB entries??
395             // we need to do it before the group is removed
396
397             // remove Group ...
398             GroupEntity groupEntity = MDSALUtil.buildGroupEntity(
399                     dpnId, nh.getEgressPointer(), ipAddress, GroupTypes.GroupIndirect, null);
400             // remove Group ...
401             mdsalManager.removeGroup(groupEntity);
402             //makeRemoteFlow(dpnId, ifName, NwConstants.DEL_FLOW);
403             //update MD-SAL DS
404             removeTunnelNexthopFromDS(dpnId, ipAddress);
405         } else {
406             //throw error
407             LOG.error("removal of remote next hop failed : dpnid : {}, ipaddress : {}", dpnId, ipAddress);
408         }
409
410     }
411
412     @Override
413     public Future<RpcResult<GetEgressPointerOutput>> getEgressPointer(
414             GetEgressPointerInput input) {
415
416         GetEgressPointerOutputBuilder output = new GetEgressPointerOutputBuilder();
417
418         String endpointIp = interfaceManager.getEndpointIpForDpn(input.getDpnId());
419         LOG.trace("getEgressPointer: input {}, endpointIp {}", input, endpointIp);
420         if (input.getNexthopIp() == null || input.getNexthopIp().equals(endpointIp)) {
421             VpnNexthop vpnNextHop = getVpnNexthop(input.getVpnId(), input.getIpPrefix(), 5);
422             output.setEgressPointer(vpnNextHop.getEgressPointer());
423             output.setLocalDestination(true);
424         } else {
425             TunnelNexthop tunnelNextHop = getTunnelNexthop(input.getDpnId(), input.getNexthopIp());
426             output.setEgressPointer(tunnelNextHop.getEgressPointer());
427             output.setLocalDestination(false);
428         }
429
430         RpcResultBuilder<GetEgressPointerOutput> rpcResultBuilder = RpcResultBuilder.success();
431         rpcResultBuilder.withResult(output.build());
432
433         return Futures.immediateFuture(rpcResultBuilder.build());
434         
435     }
436
437     <T extends DataObject> Optional<T> read(LogicalDatastoreType datastoreType,
438             InstanceIdentifier<T> path) {
439
440         ReadOnlyTransaction tx = broker.newReadOnlyTransaction();
441
442         Optional<T> result = Optional.absent();
443         try {
444             result = tx.read(datastoreType, path).get();
445         } catch (Exception e) {
446             throw new RuntimeException(e);
447         }
448
449         return result;
450     }
451
452     private <T extends DataObject> void asyncWrite(LogicalDatastoreType datastoreType,
453             InstanceIdentifier<T> path, T data, FutureCallback<Void> callback) {
454         WriteTransaction tx = broker.newWriteOnlyTransaction();
455         tx.merge(datastoreType, path, data, true);
456         Futures.addCallback(tx.submit(), callback);
457     }
458
459     private <T extends DataObject> void syncWrite(LogicalDatastoreType datastoreType,
460             InstanceIdentifier<T> path, T data, FutureCallback<Void> callback) {
461         WriteTransaction tx = broker.newWriteOnlyTransaction();
462         tx.merge(datastoreType, path, data, true);
463         tx.submit();
464     }
465
466     private <T extends DataObject> void delete(LogicalDatastoreType datastoreType, InstanceIdentifier<T> path) {
467         WriteTransaction tx = broker.newWriteOnlyTransaction();
468         tx.delete(datastoreType, path);
469         Futures.addCallback(tx.submit(), DEFAULT_CALLBACK);
470     }
471
472     @Override
473     public Future<RpcResult<Void>> removeLocalNextHop(RemoveLocalNextHopInput input) {
474         VpnNexthop vpnNextHop = getVpnNexthop(input.getVpnId(), input.getIpPrefix(), 0);
475         RpcResultBuilder<Void> rpcResultBuilder;
476         LOG.debug("vpnnexthop is: {}", vpnNextHop);
477         try {
478             removeLocalNextHop(input.getDpnId(),input.getVpnId(), input.getIpPrefix());
479             rpcResultBuilder = RpcResultBuilder.success();
480         }
481         catch(Exception e){
482             LOG.error("Removal of local next hop for vpnNextHop {} failed {}" ,vpnNextHop, e);
483             rpcResultBuilder = RpcResultBuilder.failed();
484         }
485         return Futures.immediateFuture(rpcResultBuilder.build());
486     }
487
488 }