Integration with MDSAL Util
[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
14 import com.google.common.base.Optional;
15 import com.google.common.util.concurrent.Futures;
16 import com.google.common.util.concurrent.FutureCallback;
17
18 import org.opendaylight.bgpmanager.api.IBgpManager;
19 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
20 import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
21 import org.opendaylight.vpnservice.interfacemgr.interfaces.IInterfaceManager;
22 import org.opendaylight.vpnservice.mdsalutil.FlowEntity;
23 import org.opendaylight.vpnservice.mdsalutil.InstructionInfo;
24 import org.opendaylight.vpnservice.mdsalutil.InstructionType;
25 import org.opendaylight.vpnservice.mdsalutil.MDSALUtil;
26 import org.opendaylight.vpnservice.mdsalutil.MatchFieldType;
27 import org.opendaylight.vpnservice.mdsalutil.MatchInfo;
28 import org.opendaylight.vpnservice.mdsalutil.MetaDataUtil;
29 import org.opendaylight.vpnservice.mdsalutil.interfaces.IMdsalApiManager;
30 import org.opendaylight.yangtools.concepts.ListenerRegistration;
31 import org.opendaylight.yangtools.yang.binding.DataObject;
32 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
33 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.InstanceIdentifierBuilder;
34 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
35 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
36 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
37 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
38 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.Interfaces;
39 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
40 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceKey;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.AdjacencyList;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.adjacency.list.Adjacency;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.adjacency.list.AdjacencyBuilder;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.VpnInstance1;
45 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnInterfaces;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.Adjacencies;
47 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnAfConfig;
48 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnInstances;
49 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.instances.VpnInstance;
50 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.instances.VpnInstanceKey;
51 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterface;
52 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterfaceKey;
53 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterfaceBuilder;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.AdjacenciesBuilder;
55 import org.slf4j.Logger;
56 import org.slf4j.LoggerFactory;
57
58 public class VpnInterfaceManager extends AbstractDataChangeListener<VpnInterface> implements AutoCloseable {
59     private static final Logger LOG = LoggerFactory.getLogger(VpnInterfaceManager.class);
60     private ListenerRegistration<DataChangeListener> listenerRegistration;
61     private final DataBroker broker;
62     private final IBgpManager bgpManager;
63     private IMdsalApiManager mdsalManager;
64     private IInterfaceManager interfaceManager;
65
66     private static final FutureCallback<Void> DEFAULT_CALLBACK =
67             new FutureCallback<Void>() {
68                 public void onSuccess(Void result) {
69                     LOG.debug("Success in Datastore operation");
70                 }
71
72                 public void onFailure(Throwable error) {
73                     LOG.error("Error in Datastore operation", error);
74                 };
75             };
76
77     /**
78      * Responsible for listening to data change related to VPN Interface
79      * Bind VPN Service on the interface and informs the BGP service
80      * 
81      * @param db - dataBroker service reference
82      */
83     public VpnInterfaceManager(final DataBroker db, final IBgpManager bgpManager) {
84         super(VpnInterface.class);
85         broker = db;
86         this.bgpManager = bgpManager;
87         registerListener(db);
88     }
89
90     public void setMdsalManager(IMdsalApiManager mdsalManager) {
91         this.mdsalManager = mdsalManager;
92     }
93
94     @Override
95     public void close() throws Exception {
96         if (listenerRegistration != null) {
97             try {
98                 listenerRegistration.close();
99             } catch (final Exception e) {
100                 LOG.error("Error when cleaning up DataChangeListener.", e);
101             }
102             listenerRegistration = null;
103         }
104         LOG.info("VPN Interface Manager Closed");
105     }
106
107     private void registerListener(final DataBroker db) {
108         try {
109             listenerRegistration = db.registerDataChangeListener(LogicalDatastoreType.CONFIGURATION,
110                     getWildCardPath(), VpnInterfaceManager.this, DataChangeScope.SUBTREE);
111         } catch (final Exception e) {
112             LOG.error("VPN Service DataChange listener registration fail!", e);
113             throw new IllegalStateException("VPN Service registration Listener failed.", e);
114         }
115     }
116
117     @Override
118     protected void add(final InstanceIdentifier<VpnInterface> identifier,
119             final VpnInterface vpnInterface) {
120         LOG.info("key: {} , value: {}", identifier, vpnInterface );
121         addInterface(identifier, vpnInterface);
122     }
123
124     private void addInterface(final InstanceIdentifier<VpnInterface> identifier,
125                               final VpnInterface vpnInterface) {
126         final VpnInterfaceKey key = identifier.firstKeyOf(VpnInterface.class, VpnInterfaceKey.class);
127         String interfaceName = key.getName();
128         InstanceIdentifierBuilder<Interface> idBuilder = 
129                 InstanceIdentifier.builder(Interfaces.class).child(Interface.class, new InterfaceKey(interfaceName));
130         InstanceIdentifier<Interface> id = idBuilder.build();
131         Optional<Interface> port = read(LogicalDatastoreType.CONFIGURATION, id);
132         if (port.isPresent()) {
133             Interface interf = port.get();
134             bindServiceOnInterface(interf, getVpnId(vpnInterface.getVpnInstanceName()));
135             updateNextHops(identifier, vpnInterface);
136         }
137     }
138
139     private void updateNextHops(final InstanceIdentifier<VpnInterface> identifier, VpnInterface intf) {
140         //Read NextHops
141         InstanceIdentifier<Adjacencies> path = identifier.augmentation(Adjacencies.class);
142         Optional<Adjacencies> adjacencies = read(LogicalDatastoreType.CONFIGURATION, path);
143         String intfName = intf.getName();
144
145         if (adjacencies.isPresent()) {
146             List<Adjacency> nextHops = adjacencies.get().getAdjacency();
147             List<Adjacency> value = new ArrayList<>();
148
149             //Get the rd of the vpn instance
150             String rd = getRouteDistinguisher(intf.getVpnInstanceName());
151             //TODO: Get the endpoint IP from interface manager
152             String nextHopIp = "10.0.0.1";
153
154             if (!nextHops.isEmpty()) {
155                 LOG.info("NextHops are {}", nextHops);
156                 for (Adjacency nextHop : nextHops) {
157                     //TODO: Generate label for the prefix and store it in the next hop model
158                     long label = 200;
159
160                     //TODO: Update BGP
161                     updatePrefixToBGP(rd, nextHop, nextHopIp);
162                     value.add(new AdjacencyBuilder(nextHop).setLabel(label).build());
163                 }
164             }
165             Adjacencies aug = VpnUtil.getVpnInterfaceAugmentation(value);
166             VpnInterface opInterface = VpnUtil.getVpnInterface(intfName, intf.getVpnInstanceName(), aug);
167             InstanceIdentifier<VpnInterface> interfaceId = VpnUtil.getVpnInterfaceIdentifier(intfName);
168             asyncWrite(LogicalDatastoreType.OPERATIONAL, interfaceId, opInterface, DEFAULT_CALLBACK);
169         }
170     }
171
172     private long getVpnId(String vpnName) {
173         InstanceIdentifier<VpnInstance1> id = InstanceIdentifier.builder(VpnInstances.class)
174                 .child(VpnInstance.class, new VpnInstanceKey(vpnName)).augmentation(VpnInstance1.class).build();
175         Optional<VpnInstance1> vpnInstance = read(LogicalDatastoreType.CONFIGURATION, id);
176         long vpnId = -1;
177         if(vpnInstance.isPresent()) {
178             vpnId = vpnInstance.get().getVpnId();
179         }
180         return vpnId;
181     }
182
183     private String getRouteDistinguisher(String vpnName) {
184         InstanceIdentifier<VpnInstance> id = InstanceIdentifier.builder(VpnInstances.class)
185                                       .child(VpnInstance.class, new VpnInstanceKey(vpnName)).build();
186         Optional<VpnInstance> vpnInstance = read(LogicalDatastoreType.CONFIGURATION, id);
187         String rd = "";
188         if(vpnInstance.isPresent()) {
189             VpnInstance instance = vpnInstance.get();
190             VpnAfConfig config = instance.getIpv4Family();
191             rd = config.getRouteDistinguisher();
192         }
193         return rd;
194     }
195
196     private void bindServiceOnInterface(Interface intf, long vpnId) {
197         LOG.info("Bind service on interface {} for VPN: {}", intf, vpnId);
198         //TODO: Create Ingress flow on the interface to bind the VPN service
199         //TODO: Get dpn ID from the interface manager
200         long dpId = 1;
201         short LPORT_INGRESS_TABLE = 0;
202         //TODO: Get the port no from interface manager
203         int portNo = 1;
204         String flowRef = getL3InterfaceFlowRef(dpId, LPORT_INGRESS_TABLE, vpnId, portNo);
205
206         String flowName = intf.getName();
207         BigInteger COOKIE_VM_INGRESS_TABLE = new BigInteger("8000001", 16);
208
209         int priority = 10; //L3Constants.DEFAULT_L3_FLOW_PRIORITY;
210         short gotoTableId = 21; //L3Constants.L3_FIB_TABLE;
211
212         List<InstructionInfo> mkInstructions = new ArrayList<InstructionInfo>();
213         mkInstructions.add(new InstructionInfo(InstructionType.write_metadata, new BigInteger[] {
214                 BigInteger.valueOf(vpnId), MetaDataUtil.METADATA_MASK_VRFID }));
215
216         mkInstructions.add(new InstructionInfo(InstructionType.goto_table, new long[] { gotoTableId }));
217
218         List<MatchInfo> matches = new ArrayList<MatchInfo>();
219         matches.add(new MatchInfo(MatchFieldType.in_port, new long[] {
220                 dpId, portNo }));
221
222         FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, LPORT_INGRESS_TABLE, flowRef,
223                           priority, flowName, 0, 0, COOKIE_VM_INGRESS_TABLE, matches, mkInstructions);
224
225         mdsalManager.installFlow(flowEntity);
226     }
227
228     private String getL3InterfaceFlowRef(long dpId, short tableId,
229             long vpnId, int portNo) {
230         return new StringBuilder().append(dpId).append(tableId).append(vpnId).append(portNo).toString();
231     }
232
233     private void updatePrefixToBGP(String rd, Adjacency nextHop, String nextHopIp) {
234         //TODO: Update the Prefix to BGP
235         //public void addPrefix(String rd, String prefix, String nextHop, int vpnLabel)
236         int label = nextHop.getLabel().intValue();
237         try {
238             bgpManager.addPrefix(rd, nextHop.getIpAddress(), nextHopIp, label);
239         } catch(Exception e) {
240             LOG.error("Add prefix failed", e);
241         }
242     }
243
244     private <T extends DataObject> Optional<T> read(LogicalDatastoreType datastoreType,
245             InstanceIdentifier<T> path) {
246
247         ReadOnlyTransaction tx = broker.newReadOnlyTransaction();
248
249         Optional<T> result = Optional.absent();
250         try {
251             result = tx.read(datastoreType, path).get();
252         } catch (Exception e) {
253             throw new RuntimeException(e);
254         }
255
256         return result;
257     }
258
259     private InstanceIdentifier<VpnInterface> getWildCardPath() {
260         return InstanceIdentifier.create(VpnInterfaces.class).child(VpnInterface.class);
261     }
262
263     @Override
264     protected void remove( InstanceIdentifier<VpnInterface> identifier, VpnInterface vpnInterface) {
265         LOG.info("Remove event - key: {}, value: {}" ,identifier, vpnInterface );
266         final VpnInterfaceKey key = identifier.firstKeyOf(VpnInterface.class, VpnInterfaceKey.class);
267         String interfaceName = key.getName();
268         InstanceIdentifierBuilder<Interface> idBuilder = 
269                 InstanceIdentifier.builder(Interfaces.class).child(Interface.class, new InterfaceKey(interfaceName));
270         InstanceIdentifier<Interface> id = idBuilder.build();
271         Optional<Interface> port = read(LogicalDatastoreType.CONFIGURATION, id);
272         if (port.isPresent()) {
273             Interface interf = port.get();
274             unbindServiceOnInterface(interf);
275             removeNextHops(identifier, vpnInterface);
276         } else {
277             LOG.info("No nexthops were available to handle remove event {}", interfaceName);
278         }
279     }
280
281     private void removeNextHops(final InstanceIdentifier<VpnInterface> identifier, VpnInterface intf) {
282         //Read NextHops
283         InstanceIdentifier<Adjacencies> path = identifier.augmentation(Adjacencies.class);
284         Optional<Adjacencies> adjacencies = read(LogicalDatastoreType.OPERATIONAL, path);
285         String intfName = intf.getName();
286
287         if (adjacencies.isPresent()) {
288             List<Adjacency> nextHops = adjacencies.get().getAdjacency();
289
290             if (!nextHops.isEmpty()) {
291                 LOG.trace("NextHops are " + nextHops);
292                 for (Adjacency nextHop : nextHops) {
293                     //TODO: Update BGP
294                     removePrefixFromBGP(nextHop);
295                 }
296             }
297
298             InstanceIdentifier<VpnInterface> interfaceId = VpnUtil.getVpnInterfaceIdentifier(intfName);
299             delete(LogicalDatastoreType.OPERATIONAL, interfaceId);
300         }
301     }
302
303     private <T extends DataObject> void delete(LogicalDatastoreType datastoreType, InstanceIdentifier<T> path) {
304         WriteTransaction tx = broker.newWriteOnlyTransaction();
305         tx.delete(datastoreType, path);
306         Futures.addCallback(tx.submit(), DEFAULT_CALLBACK);
307     }
308
309     private void unbindServiceOnInterface(Interface intf) {
310         //TODO: Remove Ingress flow on the interface to unbind the VPN service
311     }
312
313     private void removePrefixFromBGP(Adjacency nextHop) {
314         //TODO: Update the Prefix to BGP
315     }
316
317     @Override
318     protected void update(InstanceIdentifier<VpnInterface> identifier, 
319                                    VpnInterface original, VpnInterface update) {
320         // TODO Auto-generated method stub
321
322     }
323
324     private <T extends DataObject> void asyncWrite(LogicalDatastoreType datastoreType,
325                         InstanceIdentifier<T> path, T data, FutureCallback<Void> callback) {
326         WriteTransaction tx = broker.newWriteOnlyTransaction();
327         tx.put(datastoreType, path, data, true);
328         Futures.addCallback(tx.submit(), callback);
329     }
330 }