Merge "BgpManager: Log categorization, log formatting and moving BgpManager Test...
[vpnservice.git] / vpnmanager / vpnmanager-impl / src / main / java / org / opendaylight / vpnservice / VpnInterfaceManager.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;
9
10 import java.math.BigInteger;
11 import java.util.List;
12 import java.util.ArrayList;
13 import java.util.concurrent.ExecutionException;
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.bgpmanager.api.IBgpManager;
21 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
22 import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
23 import org.opendaylight.vpnservice.interfacemgr.interfaces.IInterfaceManager;
24 import org.opendaylight.vpnservice.mdsalutil.FlowEntity;
25 import org.opendaylight.vpnservice.mdsalutil.InstructionInfo;
26 import org.opendaylight.vpnservice.mdsalutil.InstructionType;
27 import org.opendaylight.vpnservice.mdsalutil.MDSALUtil;
28 import org.opendaylight.vpnservice.mdsalutil.MatchFieldType;
29 import org.opendaylight.vpnservice.mdsalutil.MatchInfo;
30 import org.opendaylight.vpnservice.mdsalutil.MetaDataUtil;
31 import org.opendaylight.vpnservice.mdsalutil.interfaces.IMdsalApiManager;
32 import org.opendaylight.yangtools.concepts.ListenerRegistration;
33 import org.opendaylight.yangtools.yang.binding.DataObject;
34 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
35 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.InstanceIdentifierBuilder;
36 import org.opendaylight.yangtools.yang.common.RpcResult;
37 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
38 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
39 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
40 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
41 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.Interfaces;
42 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
43 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceKey;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.AdjacencyList;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.adjacency.list.Adjacency;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.adjacency.list.AdjacencyBuilder;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.VpnInstance1;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.GetUniqueIdInput;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.GetUniqueIdInputBuilder;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.GetUniqueIdOutput;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.IdManagerService;
52 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnInterfaces;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.Adjacencies;
54 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnAfConfig;
55 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnInstances;
56 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.instances.VpnInstance;
57 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.instances.VpnInstanceKey;
58 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterface;
59 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterfaceKey;
60 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterfaceBuilder;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.AdjacenciesBuilder;
62 import org.slf4j.Logger;
63 import org.slf4j.LoggerFactory;
64
65 public class VpnInterfaceManager extends AbstractDataChangeListener<VpnInterface> implements AutoCloseable {
66     private static final Logger LOG = LoggerFactory.getLogger(VpnInterfaceManager.class);
67     private ListenerRegistration<DataChangeListener> listenerRegistration;
68     private final DataBroker broker;
69     private final IBgpManager bgpManager;
70     private IMdsalApiManager mdsalManager;
71     private IInterfaceManager interfaceManager;
72     private IdManagerService idManager;
73
74     private static final FutureCallback<Void> DEFAULT_CALLBACK =
75             new FutureCallback<Void>() {
76                 public void onSuccess(Void result) {
77                     LOG.debug("Success in Datastore operation");
78                 }
79
80                 public void onFailure(Throwable error) {
81                     LOG.error("Error in Datastore operation", error);
82                 };
83             };
84
85     /**
86      * Responsible for listening to data change related to VPN Interface
87      * Bind VPN Service on the interface and informs the BGP service
88      * 
89      * @param db - dataBroker service reference
90      */
91     public VpnInterfaceManager(final DataBroker db, final IBgpManager bgpManager) {
92         super(VpnInterface.class);
93         broker = db;
94         this.bgpManager = bgpManager;
95         registerListener(db);
96     }
97
98     public void setMdsalManager(IMdsalApiManager mdsalManager) {
99         this.mdsalManager = mdsalManager;
100     }
101
102     public void setInterfaceManager(IInterfaceManager interfaceManager) {
103         this.interfaceManager = interfaceManager;
104     }
105
106     public void setIdManager(IdManagerService idManager) {
107         this.idManager = idManager;
108     }
109
110     @Override
111     public void close() throws Exception {
112         if (listenerRegistration != null) {
113             try {
114                 listenerRegistration.close();
115             } catch (final Exception e) {
116                 LOG.error("Error when cleaning up DataChangeListener.", e);
117             }
118             listenerRegistration = null;
119         }
120         LOG.info("VPN Interface Manager Closed");
121     }
122
123     private void registerListener(final DataBroker db) {
124         try {
125             listenerRegistration = db.registerDataChangeListener(LogicalDatastoreType.CONFIGURATION,
126                     getWildCardPath(), VpnInterfaceManager.this, DataChangeScope.SUBTREE);
127         } catch (final Exception e) {
128             LOG.error("VPN Service DataChange listener registration fail!", e);
129             throw new IllegalStateException("VPN Service registration Listener failed.", e);
130         }
131     }
132
133     @Override
134     protected void add(final InstanceIdentifier<VpnInterface> identifier,
135             final VpnInterface vpnInterface) {
136         LOG.info("key: {} , value: {}", identifier, vpnInterface );
137         addInterface(identifier, vpnInterface);
138     }
139
140     private void addInterface(final InstanceIdentifier<VpnInterface> identifier,
141                               final VpnInterface vpnInterface) {
142         final VpnInterfaceKey key = identifier.firstKeyOf(VpnInterface.class, VpnInterfaceKey.class);
143         String interfaceName = key.getName();
144         InstanceIdentifierBuilder<Interface> idBuilder = 
145                 InstanceIdentifier.builder(Interfaces.class).child(Interface.class, new InterfaceKey(interfaceName));
146         InstanceIdentifier<Interface> id = idBuilder.build();
147         Optional<Interface> port = read(LogicalDatastoreType.CONFIGURATION, id);
148         if (port.isPresent()) {
149             Interface interf = port.get();
150             bindServiceOnInterface(interf, getVpnId(vpnInterface.getVpnInstanceName()));
151             updateNextHops(identifier, vpnInterface);
152         }
153     }
154
155     private void updateNextHops(final InstanceIdentifier<VpnInterface> identifier, VpnInterface intf) {
156         //Read NextHops
157         InstanceIdentifier<Adjacencies> path = identifier.augmentation(Adjacencies.class);
158         Optional<Adjacencies> adjacencies = read(LogicalDatastoreType.CONFIGURATION, path);
159         String intfName = intf.getName();
160
161         if (adjacencies.isPresent()) {
162             List<Adjacency> nextHops = adjacencies.get().getAdjacency();
163             List<Adjacency> value = new ArrayList<>();
164
165             //Get the rd of the vpn instance
166             String rd = getRouteDistinguisher(intf.getVpnInstanceName());
167
168             long dpnId = interfaceManager.getDpnForInterface(intfName);
169             String nextHopIp = interfaceManager.getEndpointIpForDpn(dpnId);
170
171             if (!nextHops.isEmpty()) {
172                 LOG.info("NextHops are {}", nextHops);
173                 for (Adjacency nextHop : nextHops) {
174                     String key = nextHop.getIpAddress();
175                     long label = getUniqueId(key);
176
177                     updatePrefixToBGP(rd, nextHop, nextHopIp, label);
178                     value.add(new AdjacencyBuilder(nextHop).setLabel(label).build());
179                 }
180             }
181             Adjacencies aug = VpnUtil.getVpnInterfaceAugmentation(value);
182             VpnInterface opInterface = VpnUtil.getVpnInterface(intfName, intf.getVpnInstanceName(), aug);
183             InstanceIdentifier<VpnInterface> interfaceId = VpnUtil.getVpnInterfaceIdentifier(intfName);
184             asyncWrite(LogicalDatastoreType.OPERATIONAL, interfaceId, opInterface, DEFAULT_CALLBACK);
185         }
186     }
187
188     private Integer getUniqueId(String idKey) {
189         GetUniqueIdInput getIdInput = new GetUniqueIdInputBuilder()
190                                            .setPoolName(VpnConstants.VPN_IDPOOL_NAME)
191                                            .setIdKey(idKey).build();
192
193         try {
194             Future<RpcResult<GetUniqueIdOutput>> result = idManager.getUniqueId(getIdInput);
195             RpcResult<GetUniqueIdOutput> rpcResult = result.get();
196             if(rpcResult.isSuccessful()) {
197                 return rpcResult.getResult().getIdValue().intValue();
198             } else {
199                 LOG.warn("RPC Call to Get Unique Id returned with Errors {}", rpcResult.getErrors());
200             }
201         } catch (NullPointerException | InterruptedException | ExecutionException e) {
202             LOG.warn("Exception when getting Unique Id",e);
203         }
204         return 0;
205     }
206
207     private long getVpnId(String vpnName) {
208         //TODO: This should be a Util function
209         InstanceIdentifier<VpnInstance1> id = InstanceIdentifier.builder(VpnInstances.class)
210                 .child(VpnInstance.class, new VpnInstanceKey(vpnName)).augmentation(VpnInstance1.class).build();
211         Optional<VpnInstance1> vpnInstance = read(LogicalDatastoreType.OPERATIONAL, id);
212         //TODO: Default vpnid should be a constant.
213         long vpnId = -1;
214         if(vpnInstance.isPresent()) {
215             vpnId = vpnInstance.get().getVpnId();
216         }
217         return vpnId;
218     }
219
220     private String getRouteDistinguisher(String vpnName) {
221         InstanceIdentifier<VpnInstance> id = InstanceIdentifier.builder(VpnInstances.class)
222                                       .child(VpnInstance.class, new VpnInstanceKey(vpnName)).build();
223         Optional<VpnInstance> vpnInstance = read(LogicalDatastoreType.CONFIGURATION, id);
224         String rd = "";
225         if(vpnInstance.isPresent()) {
226             VpnInstance instance = vpnInstance.get();
227             VpnAfConfig config = instance.getIpv4Family();
228             rd = config.getRouteDistinguisher();
229         }
230         return rd;
231     }
232
233     private void bindServiceOnInterface(Interface intf, long vpnId) {
234         LOG.info("Bind service on interface {} for VPN: {}", intf, vpnId);
235
236         long dpId = interfaceManager.getDpnForInterface(intf.getName()); 
237         if(dpId == 0L) {
238             LOG.warn("DPN for interface {} not found. Bind service on this interface aborted.", intf.getName());
239             return;
240         }
241
242         long portNo = interfaceManager.getPortForInterface(intf.getName());
243         String flowRef = getVpnInterfaceFlowRef(dpId, VpnConstants.LPORT_INGRESS_TABLE, vpnId, portNo);
244
245         String flowName = intf.getName();
246         BigInteger COOKIE_VM_INGRESS_TABLE = new BigInteger("8000001", 16);
247
248         int priority = VpnConstants.DEFAULT_FLOW_PRIORITY;
249         short gotoTableId = VpnConstants.FIB_TABLE;
250
251         List<InstructionInfo> mkInstructions = new ArrayList<InstructionInfo>();
252         mkInstructions.add(new InstructionInfo(InstructionType.write_metadata, new BigInteger[] {
253                 BigInteger.valueOf(vpnId), MetaDataUtil.METADATA_MASK_VRFID }));
254
255         mkInstructions.add(new InstructionInfo(InstructionType.goto_table, new long[] { gotoTableId }));
256
257         List<MatchInfo> matches = new ArrayList<MatchInfo>();
258         matches.add(new MatchInfo(MatchFieldType.in_port, new long[] {
259                 dpId, portNo }));
260
261         FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, VpnConstants.LPORT_INGRESS_TABLE, flowRef,
262                           priority, flowName, 0, 0, COOKIE_VM_INGRESS_TABLE, matches, mkInstructions);
263
264         mdsalManager.installFlow(flowEntity);
265     }
266
267     private String getVpnInterfaceFlowRef(long dpId, short tableId,
268             long vpnId, long portNo) {
269         return new StringBuilder().append(dpId).append(tableId).append(vpnId).append(portNo).toString();
270     }
271
272     private void updatePrefixToBGP(String rd, Adjacency nextHop, String nextHopIp, long label) {
273         try {
274             bgpManager.addPrefix(rd, nextHop.getIpAddress(), nextHopIp, (int)label);
275         } catch(Exception e) {
276             LOG.error("Add prefix failed", e);
277         }
278     }
279
280     private <T extends DataObject> Optional<T> read(LogicalDatastoreType datastoreType,
281             InstanceIdentifier<T> path) {
282
283         ReadOnlyTransaction tx = broker.newReadOnlyTransaction();
284
285         Optional<T> result = Optional.absent();
286         try {
287             result = tx.read(datastoreType, path).get();
288         } catch (Exception e) {
289             throw new RuntimeException(e);
290         }
291
292         return result;
293     }
294
295     private InstanceIdentifier<VpnInterface> getWildCardPath() {
296         return InstanceIdentifier.create(VpnInterfaces.class).child(VpnInterface.class);
297     }
298
299     @Override
300     protected void remove( InstanceIdentifier<VpnInterface> identifier, VpnInterface vpnInterface) {
301         LOG.info("Remove event - key: {}, value: {}" ,identifier, vpnInterface );
302         final VpnInterfaceKey key = identifier.firstKeyOf(VpnInterface.class, VpnInterfaceKey.class);
303         String interfaceName = key.getName();
304         InstanceIdentifierBuilder<Interface> idBuilder = 
305                 InstanceIdentifier.builder(Interfaces.class).child(Interface.class, new InterfaceKey(interfaceName));
306         InstanceIdentifier<Interface> id = idBuilder.build();
307         Optional<Interface> port = read(LogicalDatastoreType.CONFIGURATION, id);
308         if (port.isPresent()) {
309             Interface interf = port.get();
310             unbindServiceOnInterface(interf, getVpnId(vpnInterface.getVpnInstanceName()));
311             removeNextHops(identifier, vpnInterface);
312         } else {
313             LOG.info("No nexthops were available to handle remove event {}", interfaceName);
314         }
315     }
316
317     private void removeNextHops(final InstanceIdentifier<VpnInterface> identifier, VpnInterface intf) {
318         //Read NextHops
319         InstanceIdentifier<Adjacencies> path = identifier.augmentation(Adjacencies.class);
320         Optional<Adjacencies> adjacencies = read(LogicalDatastoreType.OPERATIONAL, path);
321         String intfName = intf.getName();
322         String rd = getRouteDistinguisher(intf.getVpnInstanceName());
323         if (adjacencies.isPresent()) {
324             List<Adjacency> nextHops = adjacencies.get().getAdjacency();
325
326             if (!nextHops.isEmpty()) {
327                 LOG.trace("NextHops are " + nextHops);
328                 for (Adjacency nextHop : nextHops) {
329                     removePrefixFromBGP(rd, nextHop);
330                 }
331             }
332         }
333         InstanceIdentifier<VpnInterface> interfaceId = VpnUtil.getVpnInterfaceIdentifier(intfName);
334         delete(LogicalDatastoreType.OPERATIONAL, interfaceId);
335     }
336
337     private <T extends DataObject> void delete(LogicalDatastoreType datastoreType, InstanceIdentifier<T> path) {
338         WriteTransaction tx = broker.newWriteOnlyTransaction();
339         tx.delete(datastoreType, path);
340         Futures.addCallback(tx.submit(), DEFAULT_CALLBACK);
341     }
342
343     private void unbindServiceOnInterface(Interface intf, long vpnId) {
344         LOG.info("Unbind service on interface {} for VPN: {}", intf, vpnId);
345
346         long dpId = interfaceManager.getDpnForInterface(intf.getName());
347         if(dpId == 0L) {
348             LOG.warn("DPN for interface {} not found. Unbind service on this interface aborted.", intf.getName());
349             return;
350         }
351
352         long portNo = interfaceManager.getPortForInterface(intf.getName());
353         String flowRef = getVpnInterfaceFlowRef(dpId, VpnConstants.LPORT_INGRESS_TABLE, vpnId, portNo);
354
355         String flowName = intf.getName();
356
357         int priority = VpnConstants.DEFAULT_FLOW_PRIORITY;
358
359         List<MatchInfo> matches = new ArrayList<MatchInfo>();
360         matches.add(new MatchInfo(MatchFieldType.in_port, new long[] {
361                 dpId, portNo }));
362
363         FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, VpnConstants.LPORT_INGRESS_TABLE, flowRef,
364                           priority, flowName, 0, 0, null, matches, null);
365
366         mdsalManager.removeFlow(flowEntity);
367     }
368
369     private void removePrefixFromBGP(String rd, Adjacency nextHop) {
370         //public void deletePrefix(String rd, String prefix) throws Exception;
371         try {
372             bgpManager.deletePrefix(rd, nextHop.getIpAddress());
373         } catch(Exception e) {
374             LOG.error("Delete prefix failed", e);
375         }
376     }
377
378     @Override
379     protected void update(InstanceIdentifier<VpnInterface> identifier, 
380                                    VpnInterface original, VpnInterface update) {
381         // TODO Auto-generated method stub
382
383     }
384
385     private <T extends DataObject> void asyncWrite(LogicalDatastoreType datastoreType,
386                         InstanceIdentifier<T> path, T data, FutureCallback<Void> callback) {
387         WriteTransaction tx = broker.newWriteOnlyTransaction();
388         tx.put(datastoreType, path, data, true);
389         Futures.addCallback(tx.submit(), callback);
390     }
391 }