29d5acd9bd677fcfd791664a264e651b6c385855
[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
11 import java.math.BigInteger;
12 import java.util.ArrayList;
13 import java.util.List;
14 import java.util.concurrent.Future;
15
16 import com.google.common.base.Optional;
17 import com.google.common.util.concurrent.Futures;
18 import com.google.common.util.concurrent.FutureCallback;
19
20 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
21 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
22 import org.opendaylight.yangtools.yang.binding.DataObject;
23 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
24 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.InstanceIdentifierBuilder;
25 import org.opendaylight.yangtools.yang.common.RpcResult;
26 import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
27 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
28 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
29 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnInstances;
30 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.instances.VpnInstance;
31 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.instances.VpnInstanceKey;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupTypes;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.VpnInstance1;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.CreateIdPoolInput;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.CreateIdPoolInputBuilder;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.GetUniqueIdOutput;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.GetUniqueIdInputBuilder;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.GetUniqueIdInput;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.l3nexthop.rev150409.*;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.l3nexthop.rev150409.l3nexthop.*;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.l3nexthop.rev150409.l3nexthop.tunnelnexthops.*;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.l3nexthop.rev150409.l3nexthop.vpnnexthops.*;
43 import org.opendaylight.vpnservice.interfacemgr.interfaces.IInterfaceManager;
44 import org.opendaylight.vpnservice.mdsalutil.ActionInfo;
45 import org.opendaylight.vpnservice.mdsalutil.ActionType;
46 import org.opendaylight.vpnservice.mdsalutil.BucketInfo;
47 import org.opendaylight.vpnservice.mdsalutil.GroupEntity;
48 import org.opendaylight.vpnservice.mdsalutil.MDSALUtil;
49 import org.opendaylight.vpnservice.mdsalutil.interfaces.IMdsalApiManager;
50 import org.opendaylight.idmanager.IdManager;
51
52 import java.util.concurrent.ExecutionException;
53
54 import org.slf4j.Logger;
55 import org.slf4j.LoggerFactory;
56
57 public class NexthopManager implements L3nexthopService, AutoCloseable {
58     private static final Logger LOG = LoggerFactory.getLogger(NexthopManager.class);
59     private final DataBroker broker;
60     private IMdsalApiManager mdsalManager;
61     private IInterfaceManager interfaceManager;
62     private IdManager idManager;
63
64     private static final FutureCallback<Void> DEFAULT_CALLBACK =
65         new FutureCallback<Void>() {
66             public void onSuccess(Void result) {
67                 LOG.debug("Success in Datastore write operation");
68             }
69             public void onFailure(Throwable error) {
70                 LOG.error("Error in Datastore write operation", error);
71             };
72         };
73
74     /**
75     * Provides nexthop functions
76     * Creates group ID pool
77     *
78     * @param db - dataBroker reference
79     */
80     public NexthopManager(final DataBroker db) {
81         broker = db;
82     }
83
84     @Override
85     public void close() throws Exception {
86         LOG.info("NextHop Manager Closed");
87     }
88
89     public void setInterfaceManager(IInterfaceManager ifManager) {
90         this.interfaceManager = ifManager;
91     }
92
93     public void setMdsalManager(IMdsalApiManager mdsalManager) {
94         this.mdsalManager = mdsalManager;
95     }
96
97     public void setIdManager(IdManager idManager) {
98         this.idManager = idManager;
99     }
100
101     protected void createNexthopPointerPool() {
102         CreateIdPoolInput createPool = new CreateIdPoolInputBuilder()
103             .setPoolName("nextHopPointerPool")
104             .setIdStart(1L)
105             .setPoolSize(new BigInteger("65535"))
106             .build();
107         //TODO: Error handling
108         Future<RpcResult<Void>> result = idManager.createIdPool(createPool);
109         LOG.trace("NextHopPointerPool result : {}", result);
110 //            try {
111 //                LOG.info("Result2: {}",result.get());
112 //            } catch (InterruptedException | ExecutionException e) {
113 //                // TODO Auto-generated catch block
114 //                LOG.error("Error in result.get");
115 //            }
116
117     }
118
119
120     private long getVpnId(String vpnName) {
121         InstanceIdentifierBuilder<VpnInstance> idBuilder = InstanceIdentifier.builder(VpnInstances.class)
122                 .child(VpnInstance.class, new VpnInstanceKey(vpnName));
123
124         InstanceIdentifier<VpnInstance> id = idBuilder.build();
125         InstanceIdentifier<VpnInstance1> idx = id.augmentation(VpnInstance1.class);
126         Optional<VpnInstance1> vpn = read(LogicalDatastoreType.OPERATIONAL, idx);
127
128         if (vpn.isPresent()) {
129             LOG.debug("VPN id returned: {}", vpn.get().getVpnId());
130             return vpn.get().getVpnId();
131         } else {
132             return -1;
133         }
134     }
135
136     private long getDpnId(String ifName) {
137         String[] fields = ifName.split(":");
138         long dpn = Integer.parseInt(fields[1]);
139         LOG.debug("DpnId: {}", dpn);
140         return dpn;
141     }
142
143     private int createNextHopPointer(String nexthopKey) {
144         GetUniqueIdInput getIdInput = new GetUniqueIdInputBuilder()
145             .setPoolName("nextHopPointerPool").setIdKey(nexthopKey)
146             .build();
147         //TODO: Proper error handling once IdManager code is complete
148         try {
149             Future<RpcResult<GetUniqueIdOutput>> result = idManager.getUniqueId(getIdInput);
150             RpcResult<GetUniqueIdOutput> rpcResult = result.get();
151             return rpcResult.getResult().getIdValue().intValue();
152         } catch (NullPointerException | InterruptedException | ExecutionException e) {
153             LOG.trace("",e);
154         }
155         return 0;
156     }
157
158     public void createLocalNextHop(String ifName, String vpnName, String ipAddress, String macAddress) {
159         String nhKey = new String("nexthop." + vpnName + ipAddress);
160         int groupId = createNextHopPointer(nhKey);
161
162         long vpnId = getVpnId(vpnName);
163         long dpnId = interfaceManager.getDpnForInterface(ifName);
164         VpnNexthop nexthop = getVpnNexthop(vpnId, ipAddress);
165         LOG.trace("nexthop: {}", nexthop);
166         if (nexthop == null) {
167             List<BucketInfo> listBucketInfo = new ArrayList<BucketInfo>();
168             List<ActionInfo> listActionInfo = interfaceManager.getInterfaceEgressActions(ifName);
169             BucketInfo bucket = new BucketInfo(listActionInfo);
170             // MAC re-write
171             if (macAddress != null) {
172                listActionInfo.add(0, new ActionInfo(ActionType.set_field_eth_dest, new String[]{macAddress}));
173                listActionInfo.add(0, new ActionInfo(ActionType.pop_mpls, new String[]{}));
174             } else {
175                 //FIXME: Log message here.
176                 LOG.debug("mac address for new local nexthop is null");
177             }
178             listBucketInfo.add(bucket);
179             GroupEntity groupEntity = MDSALUtil.buildGroupEntity(
180                 dpnId, groupId, ipAddress, GroupTypes.GroupIndirect, listBucketInfo);
181
182             // install Group
183             mdsalManager.installGroup(groupEntity);
184
185             //update MD-SAL DS
186             addVpnNexthopToDS(vpnId, ipAddress, groupId);
187         } else {
188             //check update
189         }
190     }
191
192     public void createRemoteNextHop(String ifName, String ofPortId, String ipAddress) {
193         String nhKey = new String("nexthop." + ifName + ipAddress);
194         int groupId = createNextHopPointer(nhKey);
195
196         long dpnId = getDpnId(ofPortId);
197         TunnelNexthop nexthop = getTunnelNexthop(dpnId, ipAddress);
198         if (nexthop == null) {
199
200             List<BucketInfo> listBucketInfo = new ArrayList<BucketInfo>();
201             List<ActionInfo> listActionInfo = interfaceManager.getInterfaceEgressActions(ifName);
202             BucketInfo bucket = new BucketInfo(listActionInfo);
203             // MAC re-write??           
204             listBucketInfo.add(bucket);
205             GroupEntity groupEntity = MDSALUtil.buildGroupEntity(
206                 dpnId, groupId, ipAddress, GroupTypes.GroupIndirect, listBucketInfo);
207             mdsalManager.installGroup(groupEntity);
208
209             //update MD-SAL DS
210             addTunnelNexthopToDS(dpnId, ipAddress, groupId);
211         } else {
212             //check update
213         }
214     }
215
216     private void addVpnNexthopToDS(long vpnId, String ipPrefix, long egressPointer) {
217
218         InstanceIdentifierBuilder<VpnNexthops> idBuilder = InstanceIdentifier.builder(L3nexthop.class)
219                 .child(VpnNexthops.class, new VpnNexthopsKey(vpnId));
220
221         // check if vpn node is there or to be created
222         InstanceIdentifier<VpnNexthops> id = idBuilder.build();
223         Optional<VpnNexthops> nexthops = read(LogicalDatastoreType.OPERATIONAL, id);
224         if (!nexthops.isPresent()) {
225             // create a new node
226             VpnNexthops node = new VpnNexthopsBuilder().setKey(new VpnNexthopsKey(vpnId)).setVpnId(vpnId).build();
227             asyncWrite(LogicalDatastoreType.OPERATIONAL, id, node, DEFAULT_CALLBACK);
228         }
229
230         // Add nexthop to vpn node
231         VpnNexthop nh = new VpnNexthopBuilder().
232                 setKey(new VpnNexthopKey(ipPrefix)).
233                 setIpAddress(ipPrefix).
234                 setEgressPointer(egressPointer).build();
235
236         InstanceIdentifier<VpnNexthop> id1 = idBuilder
237                 .child(VpnNexthop.class, new VpnNexthopKey(ipPrefix)).build();
238         LOG.trace("Adding vpnnextHop {} to Operational DS", nh);
239         asyncWrite(LogicalDatastoreType.OPERATIONAL, id1, nh, DEFAULT_CALLBACK);
240
241     }
242
243     private void addTunnelNexthopToDS(long dpnId, String ipPrefix, long egressPointer) {
244         InstanceIdentifierBuilder<TunnelNexthops> idBuilder = InstanceIdentifier.builder(L3nexthop.class)
245                 .child(TunnelNexthops.class, new TunnelNexthopsKey(dpnId));
246
247         // check if dpn node is there or to be created
248         InstanceIdentifier<TunnelNexthops> id = idBuilder.build();
249         Optional<TunnelNexthops> nexthops = read(LogicalDatastoreType.OPERATIONAL, id);
250         if (!nexthops.isPresent()) {
251             // create a new node
252             TunnelNexthops node = new TunnelNexthopsBuilder()
253                 .setKey(new TunnelNexthopsKey(dpnId))
254                 .setDpnId(dpnId)
255                 .build();
256             LOG.trace("Adding tunnelnextHop {} to Operational DS for a new node", node);
257             asyncWrite(LogicalDatastoreType.OPERATIONAL, id, node, DEFAULT_CALLBACK);
258         }
259
260         // Add nexthop to dpn node
261         TunnelNexthop nh = new TunnelNexthopBuilder().
262                 setKey(new TunnelNexthopKey(ipPrefix)).
263                 setIpAddress(ipPrefix).
264                 setEgressPointer(egressPointer).build();
265
266         InstanceIdentifier<TunnelNexthop> id1 = idBuilder
267                 .child(TunnelNexthop.class, new TunnelNexthopKey(ipPrefix)).build();
268         LOG.trace("Adding tunnelnextHop {} to Operational DS for a dpn node", nh);
269         asyncWrite(LogicalDatastoreType.OPERATIONAL, id1, nh, DEFAULT_CALLBACK);
270
271     }
272
273     private VpnNexthop getVpnNexthop(long vpnId, String ipAddress) {
274
275         // check if vpn node is there 
276         InstanceIdentifierBuilder<VpnNexthops> idBuilder = InstanceIdentifier.builder(L3nexthop.class)
277                 .child(VpnNexthops.class, new VpnNexthopsKey(vpnId));
278         InstanceIdentifier<VpnNexthops> id = idBuilder.build();
279         Optional<VpnNexthops> vpnNexthops = read(LogicalDatastoreType.OPERATIONAL, id);
280         if (vpnNexthops.isPresent()) {
281
282             // get nexthops list for vpn
283             List<VpnNexthop> nexthops = vpnNexthops.get().getVpnNexthop();
284             for (VpnNexthop nexthop : nexthops) {
285                 if (nexthop.getIpAddress().equals(ipAddress)) {
286                     // return nexthop 
287                     LOG.trace("VpnNextHop : {}",nexthop);
288                     return nexthop;
289                 }
290             }
291         }
292         //return null if not found
293         return null;
294     }
295
296     private TunnelNexthop getTunnelNexthop(long dpnId, String ipAddress) {
297         
298         InstanceIdentifierBuilder<TunnelNexthops> idBuilder = InstanceIdentifier.builder(L3nexthop.class)
299                 .child(TunnelNexthops.class, new TunnelNexthopsKey(dpnId));
300
301         // check if vpn node is there 
302         InstanceIdentifier<TunnelNexthops> id = idBuilder.build();
303         Optional<TunnelNexthops> dpnNexthops = read(LogicalDatastoreType.OPERATIONAL, id);
304         if (dpnNexthops.isPresent()) {
305             List<TunnelNexthop> nexthops = dpnNexthops.get().getTunnelNexthop();
306             for (TunnelNexthop nexthop : nexthops) {
307                 if (nexthop.getIpAddress().equals(ipAddress)) {
308                     LOG.trace("TunnelNextHop : {}",nexthop);
309                     return nexthop;
310                 }
311             }
312         }
313         return null;
314     }
315
316     public long getNextHopPointer(long dpnId, long vpnId, String prefixIp, String nextHopIp) {
317         String endpointIp = interfaceManager.getEndpointIpForDpn(dpnId);
318         if (nextHopIp.equals(endpointIp)) {
319             VpnNexthop vpnNextHop = getVpnNexthop(vpnId, prefixIp);
320             return vpnNextHop.getEgressPointer();
321         } else {
322             TunnelNexthop tunnelNextHop = getTunnelNexthop(dpnId, nextHopIp);
323             LOG.trace("NExtHopPointer : {}", tunnelNextHop.getEgressPointer());
324             return tunnelNextHop.getEgressPointer();
325         }
326     }
327
328     private void removeTunnelNexthopFromDS(long dpnId, String ipPrefix) {
329
330         InstanceIdentifierBuilder<TunnelNexthop> idBuilder = InstanceIdentifier.builder(L3nexthop.class)
331                 .child(TunnelNexthops.class, new TunnelNexthopsKey(dpnId))
332                 .child(TunnelNexthop.class, new TunnelNexthopKey(ipPrefix));
333         InstanceIdentifier<TunnelNexthop> id = idBuilder.build();
334         // remove from DS     
335         LOG.trace("Removing tunnel next hop from datastore : {}", id);
336         delete(LogicalDatastoreType.OPERATIONAL, id);
337     }
338
339     private void removeVpnNexthopFromDS(long vpnId, String ipPrefix) {
340
341         InstanceIdentifierBuilder<VpnNexthop> idBuilder = InstanceIdentifier.builder(L3nexthop.class)
342                 .child(VpnNexthops.class, new VpnNexthopsKey(vpnId))
343                 .child(VpnNexthop.class, new VpnNexthopKey(ipPrefix));
344         InstanceIdentifier<VpnNexthop> id = idBuilder.build();
345         // remove from DS
346         LOG.trace("Removing vpn next hop from datastore : {}", id);
347         delete(LogicalDatastoreType.OPERATIONAL, id);
348     }
349
350  
351     public void removeLocalNextHop(Long dpId, Long vpnId, String ipAddress) {
352
353         VpnNexthop nh = getVpnNexthop(vpnId, ipAddress);
354         if (nh != null) {
355             // how to inform and remove dependent FIB entries??
356             // we need to do it before the group is removed
357             GroupEntity groupEntity = MDSALUtil.buildGroupEntity(
358                     dpId, nh.getEgressPointer(), ipAddress, GroupTypes.GroupIndirect, null);
359             // remove Group ...
360             mdsalManager.removeGroup(groupEntity);
361             //update MD-SAL DS
362             removeVpnNexthopFromDS(vpnId, ipAddress);
363         } else {
364             //throw error
365             LOG.error("removal of local next hop failed");
366         }
367
368     }
369
370     public void removeRemoteNextHop(long dpnId, String ipAddress) {
371
372         TunnelNexthop nh = getTunnelNexthop(dpnId, ipAddress);
373         if (nh != null) {
374             // how to inform and remove dependent FIB entries??
375             // we need to do it before the group is removed
376
377             // remove Group ...
378             GroupEntity groupEntity = MDSALUtil.buildGroupEntity(
379                     dpnId, nh.getEgressPointer(), ipAddress, GroupTypes.GroupIndirect, null);
380             // remove Group ...
381             mdsalManager.removeGroup(groupEntity);
382             //update MD-SAL DS
383             removeTunnelNexthopFromDS(dpnId, ipAddress);
384         } else {
385             //throw error
386             LOG.error("removal of remote next hop failed : dpnid : {}, ipaddress : {}", dpnId, ipAddress);
387         }
388
389     }
390
391     @Override
392     public Future<RpcResult<GetEgressPointerOutput>> getEgressPointer(
393             GetEgressPointerInput input) {
394
395         GetEgressPointerOutputBuilder output = new GetEgressPointerOutputBuilder();
396
397         String endpointIp = interfaceManager.getEndpointIpForDpn(input.getDpnId());
398         LOG.trace("getEgressPointer: input {}, endpointIp {}", input, endpointIp);
399         if (input.getNexthopIp().equals(endpointIp)) {
400             VpnNexthop vpnNextHop = getVpnNexthop(input.getVpnId(), input.getIpPrefix());
401             output.setEgressPointer(vpnNextHop.getEgressPointer());
402             output.setLocalDestination(true);
403         } else {
404             TunnelNexthop tunnelNextHop = getTunnelNexthop(input.getDpnId(), input.getNexthopIp());
405             output.setEgressPointer(tunnelNextHop.getEgressPointer());
406             output.setLocalDestination(false);
407         }
408
409         RpcResultBuilder<GetEgressPointerOutput> rpcResultBuilder = RpcResultBuilder.success();
410         rpcResultBuilder.withResult(output.build());
411
412         return Futures.immediateFuture(rpcResultBuilder.build());
413         
414     }
415
416     private <T extends DataObject> Optional<T> read(LogicalDatastoreType datastoreType,
417             InstanceIdentifier<T> path) {
418
419         ReadOnlyTransaction tx = broker.newReadOnlyTransaction();
420
421         Optional<T> result = Optional.absent();
422         try {
423             result = tx.read(datastoreType, path).get();
424         } catch (Exception e) {
425             throw new RuntimeException(e);
426         }
427
428         return result;
429     }
430
431     private <T extends DataObject> void asyncWrite(LogicalDatastoreType datastoreType,
432             InstanceIdentifier<T> path, T data, FutureCallback<Void> callback) {
433         WriteTransaction tx = broker.newWriteOnlyTransaction();
434         tx.put(datastoreType, path, data, true);
435         Futures.addCallback(tx.submit(), callback);
436     }
437
438
439     private <T extends DataObject> void delete(LogicalDatastoreType datastoreType, InstanceIdentifier<T> path) {
440         WriteTransaction tx = broker.newWriteOnlyTransaction();
441         tx.delete(datastoreType, path);
442         Futures.addCallback(tx.submit(), DEFAULT_CALLBACK);
443     }
444
445     @Override
446     public Future<RpcResult<Void>> removeLocalNextHop(RemoveLocalNextHopInput input) {
447         VpnNexthop vpnNextHop = getVpnNexthop(input.getVpnId(), input.getIpPrefix());
448         RpcResultBuilder<Void> rpcResultBuilder;
449         LOG.debug("vpnnexthop is: {}", vpnNextHop);
450         try {
451             removeLocalNextHop(input.getDpnId(),input.getVpnId(), input.getIpPrefix());
452             rpcResultBuilder = RpcResultBuilder.success();
453         }
454         catch(Exception e){
455             LOG.error("Removal of local next hop for vpnNextHop {} failed {}" ,vpnNextHop, e);
456             rpcResultBuilder = RpcResultBuilder.failed();
457         }
458         return Futures.immediateFuture(rpcResultBuilder.build());
459     }
460
461 }