571cdce3ad91cce5a23fa109fd5d88cb9d46a805
[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 //            try {
110 //                LOG.info("Result2: {}",result.get());
111 //            } catch (InterruptedException | ExecutionException e) {
112 //                // TODO Auto-generated catch block
113 //                LOG.error("Error in result.get");
114 //            }
115
116     }
117
118
119     private long getVpnId(String vpnName) {
120         InstanceIdentifierBuilder<VpnInstance> idBuilder = InstanceIdentifier.builder(VpnInstances.class)
121                 .child(VpnInstance.class, new VpnInstanceKey(vpnName));
122
123         InstanceIdentifier<VpnInstance> id = idBuilder.build();
124         InstanceIdentifier<VpnInstance1> idx = id.augmentation(VpnInstance1.class);
125         Optional<VpnInstance1> vpn = read(LogicalDatastoreType.OPERATIONAL, idx);
126
127         if (vpn.isPresent()) {
128             return vpn.get().getVpnId();
129         } else {
130             return -1;
131         }
132     }
133
134     private long getDpnId(String ifName) {
135         String[] fields = ifName.split(":");
136         long dpn = Integer.parseInt(fields[1]);
137         return dpn;
138     }
139
140     private int createNextHopPointer(String nexthopKey) {
141         GetUniqueIdInput getIdInput = new GetUniqueIdInputBuilder()
142             .setPoolName("nextHopPointerPool").setIdKey(nexthopKey)
143             .build();
144         //TODO: Proper error handling once IdManager code is complete
145         try {
146             Future<RpcResult<GetUniqueIdOutput>> result = idManager.getUniqueId(getIdInput);
147             RpcResult<GetUniqueIdOutput> rpcResult = result.get();
148             return rpcResult.getResult().getIdValue().intValue();
149         } catch (NullPointerException | InterruptedException | ExecutionException e) {
150             LOG.trace("",e);
151         }
152         return 0;
153     }
154
155     public void createLocalNextHop(String ifName, String vpnName, String ipAddress, String macAddress) {
156         String nhKey = new String("nexthop." + vpnName + ipAddress);
157         int groupId = createNextHopPointer(nhKey);
158
159         long vpnId = getVpnId(vpnName);
160         long dpnId = interfaceManager.getDpnForInterface(ifName);
161         VpnNexthop nexthop = getVpnNexthop(vpnId, ipAddress);
162         LOG.trace("nexthop: {}", nexthop);
163         if (nexthop == null) {
164             List<BucketInfo> listBucketInfo = new ArrayList<BucketInfo>();
165             List<ActionInfo> listActionInfo = interfaceManager.getInterfaceEgressActions(ifName);
166             BucketInfo bucket = new BucketInfo(listActionInfo);
167             // MAC re-write
168             if (macAddress != null) {
169                listActionInfo.add(0, new ActionInfo(ActionType.set_field_eth_dest, new String[]{macAddress}));
170                listActionInfo.add(new ActionInfo(ActionType.pop_mpls, new String[]{}));
171             } else {
172                 //FIXME: Log message here.
173             }
174             listBucketInfo.add(bucket);
175             GroupEntity groupEntity = MDSALUtil.buildGroupEntity(
176                 dpnId, groupId, ipAddress, GroupTypes.GroupIndirect, listBucketInfo);
177
178             // install Group
179             mdsalManager.installGroup(groupEntity);
180
181             //update MD-SAL DS
182             addVpnNexthopToDS(vpnId, ipAddress, groupId);
183         } else {
184             //check update
185         }
186     }
187
188     public void createRemoteNextHop(String ifName, String ofPortId, String ipAddress) {
189         String nhKey = new String("nexthop." + ifName + ipAddress);
190         int groupId = createNextHopPointer(nhKey);
191
192         long dpnId = getDpnId(ofPortId);
193         TunnelNexthop nexthop = getTunnelNexthop(dpnId, ipAddress);
194         if (nexthop == null) {
195
196             List<BucketInfo> listBucketInfo = new ArrayList<BucketInfo>();
197             List<ActionInfo> listActionInfo = interfaceManager.getInterfaceEgressActions(ifName);
198             BucketInfo bucket = new BucketInfo(listActionInfo);
199             // MAC re-write??           
200             listBucketInfo.add(bucket);
201             GroupEntity groupEntity = MDSALUtil.buildGroupEntity(
202                 dpnId, groupId, ipAddress, GroupTypes.GroupIndirect, listBucketInfo);
203             mdsalManager.installGroup(groupEntity);
204
205             //update MD-SAL DS
206             addTunnelNexthopToDS(dpnId, ipAddress, groupId);
207         } else {
208             //check update
209         }
210     }
211
212     private void addVpnNexthopToDS(long vpnId, String ipPrefix, long egressPointer) {
213
214         InstanceIdentifierBuilder<VpnNexthops> idBuilder = InstanceIdentifier.builder(L3nexthop.class)
215                 .child(VpnNexthops.class, new VpnNexthopsKey(vpnId));
216
217         // check if vpn node is there or to be created
218         InstanceIdentifier<VpnNexthops> id = idBuilder.build();
219         Optional<VpnNexthops> nexthops = read(LogicalDatastoreType.OPERATIONAL, id);
220         if (!nexthops.isPresent()) {
221             // create a new node
222             VpnNexthops node = new VpnNexthopsBuilder().setKey(new VpnNexthopsKey(vpnId)).setVpnId(vpnId).build();
223             asyncWrite(LogicalDatastoreType.OPERATIONAL, id, node, DEFAULT_CALLBACK);
224         }
225
226         // Add nexthop to vpn node
227         VpnNexthop nh = new VpnNexthopBuilder().
228                 setKey(new VpnNexthopKey(ipPrefix)).
229                 setIpAddress(ipPrefix).
230                 setEgressPointer(egressPointer).build();
231
232         InstanceIdentifier<VpnNexthop> id1 = idBuilder
233                 .child(VpnNexthop.class, new VpnNexthopKey(ipPrefix)).build();
234         LOG.trace("Adding nextHop {} to Operational DS", nh);
235         asyncWrite(LogicalDatastoreType.OPERATIONAL, id1, nh, DEFAULT_CALLBACK);
236
237     }
238
239     private void addTunnelNexthopToDS(long dpnId, String ipPrefix, long egressPointer) {
240         InstanceIdentifierBuilder<TunnelNexthops> idBuilder = InstanceIdentifier.builder(L3nexthop.class)
241                 .child(TunnelNexthops.class, new TunnelNexthopsKey(dpnId));
242
243         // check if dpn node is there or to be created
244         InstanceIdentifier<TunnelNexthops> id = idBuilder.build();
245         Optional<TunnelNexthops> nexthops = read(LogicalDatastoreType.OPERATIONAL, id);
246         if (!nexthops.isPresent()) {
247             // create a new node
248             TunnelNexthops node = new TunnelNexthopsBuilder()
249                 .setKey(new TunnelNexthopsKey(dpnId))
250                 .setDpnId(dpnId)
251                 .build();
252             asyncWrite(LogicalDatastoreType.OPERATIONAL, id, node, DEFAULT_CALLBACK);
253         }
254
255         // Add nexthop to dpn node
256         TunnelNexthop nh = new TunnelNexthopBuilder().
257                 setKey(new TunnelNexthopKey(ipPrefix)).
258                 setIpAddress(ipPrefix).
259                 setEgressPointer(egressPointer).build();
260
261         InstanceIdentifier<TunnelNexthop> id1 = idBuilder
262                 .child(TunnelNexthop.class, new TunnelNexthopKey(ipPrefix)).build();
263
264         asyncWrite(LogicalDatastoreType.OPERATIONAL, id1, nh, DEFAULT_CALLBACK);
265
266     }
267
268     private VpnNexthop getVpnNexthop(long vpnId, String ipAddress) {
269
270         // check if vpn node is there 
271         InstanceIdentifierBuilder<VpnNexthops> idBuilder = InstanceIdentifier.builder(L3nexthop.class)
272                 .child(VpnNexthops.class, new VpnNexthopsKey(vpnId));
273         InstanceIdentifier<VpnNexthops> id = idBuilder.build();
274         Optional<VpnNexthops> vpnNexthops = read(LogicalDatastoreType.OPERATIONAL, id);
275         if (vpnNexthops.isPresent()) {
276
277             // get nexthops list for vpn
278             List<VpnNexthop> nexthops = vpnNexthops.get().getVpnNexthop();
279             for (VpnNexthop nexthop : nexthops) {
280                 if (nexthop.getIpAddress().equals(ipAddress)) {
281                     // return nexthop 
282                     return nexthop;
283                 }
284             }
285         }
286         //return null if not found
287         return null;
288     }
289
290     private TunnelNexthop getTunnelNexthop(long dpnId, String ipAddress) {
291         
292         InstanceIdentifierBuilder<TunnelNexthops> idBuilder = InstanceIdentifier.builder(L3nexthop.class)
293                 .child(TunnelNexthops.class, new TunnelNexthopsKey(dpnId));
294
295         // check if vpn node is there 
296         InstanceIdentifier<TunnelNexthops> id = idBuilder.build();
297         Optional<TunnelNexthops> dpnNexthops = read(LogicalDatastoreType.OPERATIONAL, id);
298         if (dpnNexthops.isPresent()) {
299             List<TunnelNexthop> nexthops = dpnNexthops.get().getTunnelNexthop();
300             for (TunnelNexthop nexthop : nexthops) {
301                 if (nexthop.getIpAddress().equals(ipAddress)) {
302                     return nexthop;
303                 }
304             }
305         }
306         return null;
307     }
308
309     public long getNextHopPointer(long dpnId, long vpnId, String prefixIp, String nextHopIp) {
310         String endpointIp = interfaceManager.getEndpointIpForDpn(dpnId);
311         if (nextHopIp.equals(endpointIp)) {
312             VpnNexthop vpnNextHop = getVpnNexthop(vpnId, prefixIp);
313             return vpnNextHop.getEgressPointer();
314         } else {
315             TunnelNexthop tunnelNextHop = getTunnelNexthop(dpnId, nextHopIp);
316             return tunnelNextHop.getEgressPointer();
317         }
318     }
319
320     private void removeTunnelNexthopFromDS(long dpnId, String ipPrefix) {
321
322         InstanceIdentifierBuilder<TunnelNexthop> idBuilder = InstanceIdentifier.builder(L3nexthop.class)
323                 .child(TunnelNexthops.class, new TunnelNexthopsKey(dpnId))
324                 .child(TunnelNexthop.class, new TunnelNexthopKey(ipPrefix));
325         InstanceIdentifier<TunnelNexthop> id = idBuilder.build();
326         // remove from DS     
327         delete(LogicalDatastoreType.OPERATIONAL, id);
328     }
329
330     private void removeVpnNexthopFromDS(long vpnId, String ipPrefix) {
331
332         InstanceIdentifierBuilder<VpnNexthop> idBuilder = InstanceIdentifier.builder(L3nexthop.class)
333                 .child(VpnNexthops.class, new VpnNexthopsKey(vpnId))
334                 .child(VpnNexthop.class, new VpnNexthopKey(ipPrefix));
335         InstanceIdentifier<VpnNexthop> id = idBuilder.build();
336         // remove from DS
337         delete(LogicalDatastoreType.OPERATIONAL, id);
338     }
339
340  
341     public void removeLocalNextHop(Long dpId, Long vpnId, String ipAddress) {
342
343         VpnNexthop nh = getVpnNexthop(vpnId, ipAddress);
344         if (nh != null) {
345             // how to inform and remove dependent FIB entries??
346             // we need to do it before the group is removed
347             GroupEntity groupEntity = MDSALUtil.buildGroupEntity(
348                     dpId, nh.getEgressPointer(), ipAddress, GroupTypes.GroupIndirect, null);
349             // remove Group ...
350             mdsalManager.removeGroup(groupEntity);
351             //update MD-SAL DS
352             removeVpnNexthopFromDS(vpnId, ipAddress);
353         } else {
354             //throw error
355         }
356
357     }
358
359     public void removeRemoteNextHop(long dpnId, String ipAddress) {
360
361         TunnelNexthop nh = getTunnelNexthop(dpnId, ipAddress);
362         if (nh != null) {
363             // how to inform and remove dependent FIB entries??
364             // we need to do it before the group is removed
365
366             // remove Group ...
367             GroupEntity groupEntity = MDSALUtil.buildGroupEntity(
368                     dpnId, nh.getEgressPointer(), ipAddress, GroupTypes.GroupIndirect, null);
369             // remove Group ...
370             mdsalManager.removeGroup(groupEntity);
371             //update MD-SAL DS
372             removeTunnelNexthopFromDS(dpnId, ipAddress);
373         } else {
374             //throw error
375         }
376
377     }
378
379     @Override
380     public Future<RpcResult<GetEgressPointerOutput>> getEgressPointer(
381             GetEgressPointerInput input) {
382
383         GetEgressPointerOutputBuilder output = new GetEgressPointerOutputBuilder();
384
385         String endpointIp = interfaceManager.getEndpointIpForDpn(input.getDpnId());
386         LOG.trace("getEgressPointer: input {}, endpointIp {}", input, endpointIp);
387         if (input.getNexthopIp().equals(endpointIp)) {
388             VpnNexthop vpnNextHop = getVpnNexthop(input.getVpnId(), input.getIpPrefix());
389             output.setEgressPointer(vpnNextHop.getEgressPointer());
390             output.setLocalDestination(true);
391         } else {
392             TunnelNexthop tunnelNextHop = getTunnelNexthop(input.getDpnId(), input.getNexthopIp());
393             output.setEgressPointer(tunnelNextHop.getEgressPointer());
394             output.setLocalDestination(false);
395         }
396
397         RpcResultBuilder<GetEgressPointerOutput> rpcResultBuilder = RpcResultBuilder.success();
398         rpcResultBuilder.withResult(output.build());
399
400         return Futures.immediateFuture(rpcResultBuilder.build());
401         
402     }
403
404     private <T extends DataObject> Optional<T> read(LogicalDatastoreType datastoreType,
405             InstanceIdentifier<T> path) {
406
407         ReadOnlyTransaction tx = broker.newReadOnlyTransaction();
408
409         Optional<T> result = Optional.absent();
410         try {
411             result = tx.read(datastoreType, path).get();
412         } catch (Exception e) {
413             throw new RuntimeException(e);
414         }
415
416         return result;
417     }
418
419     private <T extends DataObject> void asyncWrite(LogicalDatastoreType datastoreType,
420             InstanceIdentifier<T> path, T data, FutureCallback<Void> callback) {
421         WriteTransaction tx = broker.newWriteOnlyTransaction();
422         tx.put(datastoreType, path, data, true);
423         Futures.addCallback(tx.submit(), callback);
424     }
425
426
427     private <T extends DataObject> void delete(LogicalDatastoreType datastoreType, InstanceIdentifier<T> path) {
428         WriteTransaction tx = broker.newWriteOnlyTransaction();
429         tx.delete(datastoreType, path);
430         Futures.addCallback(tx.submit(), DEFAULT_CALLBACK);
431     }
432
433     @Override
434     public Future<RpcResult<Void>> removeLocalNextHop(RemoveLocalNextHopInput input) {
435         VpnNexthop vpnNextHop = getVpnNexthop(input.getVpnId(), input.getIpPrefix());
436         RpcResultBuilder<Void> rpcResultBuilder;
437         LOG.debug("vpnnexthop is: {}", vpnNextHop);
438         try {
439             removeLocalNextHop(input.getDpnId(),input.getVpnId(), input.getIpPrefix());
440             rpcResultBuilder = RpcResultBuilder.success();
441         }
442         catch(Exception e){
443             LOG.error("Removal of local next hop for vpnNextHop {} failed {}" ,vpnNextHop, e);
444             rpcResultBuilder = RpcResultBuilder.failed();
445         }
446         return Futures.immediateFuture(rpcResultBuilder.build());
447     }
448
449 }