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