Merge "Natservice module bug fixes"
[vpnservice.git] / natservice / natservice-impl / src / main / java / org / opendaylight / vpnservice / natservice / internal / VpnFloatingIpHandler.java
1 /*
2  * Copyright (c) 2016 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.natservice.internal;
9
10 import java.math.BigInteger;
11 import java.util.ArrayList;
12 import java.util.concurrent.Future;
13 import java.util.List;
14
15 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
16 import org.opendaylight.bgpmanager.api.IBgpManager;
17 import org.opendaylight.vpnservice.mdsalutil.ActionInfo;
18 import org.opendaylight.vpnservice.mdsalutil.ActionType;
19 import org.opendaylight.vpnservice.mdsalutil.InstructionInfo;
20 import org.opendaylight.vpnservice.mdsalutil.InstructionType;
21 import org.opendaylight.vpnservice.mdsalutil.MDSALUtil;
22 import org.opendaylight.vpnservice.mdsalutil.MatchFieldType;
23 import org.opendaylight.vpnservice.mdsalutil.MatchInfo;
24 import org.opendaylight.vpnservice.mdsalutil.MetaDataUtil;
25 import org.opendaylight.vpnservice.mdsalutil.NwConstants;
26 import org.opendaylight.vpnservice.mdsalutil.interfaces.IMdsalApiManager;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
28 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.fib.rpc.rev160121.CreateFibEntryInput;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.fib.rpc.rev160121.CreateFibEntryInputBuilder;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.fib.rpc.rev160121.RemoveFibEntryInput;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.fib.rpc.rev160121.RemoveFibEntryInputBuilder;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.fib.rpc.rev160121.FibRpcService;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.vpn.rpc.rev160201.VpnRpcService;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.vpn.rpc.rev160201.GenerateVpnLabelInput;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.vpn.rpc.rev160201.GenerateVpnLabelInputBuilder;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.vpn.rpc.rev160201.GenerateVpnLabelOutput;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.vpn.rpc.rev160201.RemoveVpnLabelInput;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.vpn.rpc.rev160201.RemoveVpnLabelInputBuilder;
41 import org.opendaylight.yangtools.yang.common.RpcResult;
42 import org.slf4j.Logger;
43 import org.slf4j.LoggerFactory;
44
45 import com.google.common.util.concurrent.AsyncFunction;
46 import com.google.common.util.concurrent.FutureCallback;
47 import com.google.common.util.concurrent.Futures;
48 import com.google.common.util.concurrent.JdkFutureAdapters;
49 import com.google.common.util.concurrent.ListenableFuture;
50
51 public class VpnFloatingIpHandler implements FloatingIPHandler {
52     private static final Logger LOG = LoggerFactory.getLogger(VpnFloatingIpHandler.class);
53     private VpnRpcService vpnService;
54     private FibRpcService fibService;
55     private IBgpManager bgpManager;
56     private DataBroker dataBroker;
57     private IMdsalApiManager mdsalManager;
58     private FloatingIPListener listener;
59
60     static final BigInteger COOKIE_TUNNEL = new BigInteger("9000000", 16);
61     static final String FLOWID_PREFIX = "NAT.";
62     static final BigInteger COOKIE_VM_LFIB_TABLE = new BigInteger("8000002", 16);
63
64     public VpnFloatingIpHandler(VpnRpcService vpnService, IBgpManager bgpManager, FibRpcService fibService) {
65         this.vpnService = vpnService;
66         this.fibService = fibService;
67         this.bgpManager = bgpManager;
68     }
69
70     void setListener(FloatingIPListener listener) {
71         this.listener = listener;
72     }
73
74     void setBroker(DataBroker broker) {
75         dataBroker = broker;
76     }
77
78     void setMdsalManager(IMdsalApiManager mdsalManager) {
79         this.mdsalManager = mdsalManager;
80     }
81
82     @Override
83     public void onAddFloatingIp(final BigInteger dpnId, final String routerId,
84                                 Uuid networkId, final String interfaceName, final String externalIp, final String internalIp) {
85         final String vpnName = NatUtil.getAssociatedVPN(dataBroker, networkId, LOG);
86         if(vpnName == null) {
87             LOG.info("No VPN associated with ext nw {} to handle add floating ip configuration {} in router {}",
88                     networkId, externalIp, routerId);
89             return;
90         }
91
92         GenerateVpnLabelInput labelInput = new GenerateVpnLabelInputBuilder().setVpnName(vpnName).setIpPrefix(externalIp).build();
93         Future<RpcResult<GenerateVpnLabelOutput>> labelFuture = vpnService.generateVpnLabel(labelInput);
94
95         ListenableFuture<RpcResult<Void>> future = Futures.transform(JdkFutureAdapters.listenInPoolThread(labelFuture), new AsyncFunction<RpcResult<GenerateVpnLabelOutput>, RpcResult<Void>>() {
96
97             @Override
98             public ListenableFuture<RpcResult<Void>> apply(RpcResult<GenerateVpnLabelOutput> result) throws Exception {
99                 if(result.isSuccessful()) {
100                     GenerateVpnLabelOutput output = result.getResult();
101                     long label = output.getLabel();
102                     LOG.debug("Generated label {} for prefix {}", label, externalIp);
103                     listener.updateOperationalDS(routerId, interfaceName, (int)label, internalIp, externalIp);
104
105                     //Inform BGP
106                     String rd = NatUtil.getVpnRd(dataBroker, vpnName);
107                     String nextHopIp = NatUtil.getEndpointIpAddressForDPN(dataBroker, dpnId);
108                     LOG.debug("Nexthop ip for prefix {} is {}", externalIp, nextHopIp);
109                     NatUtil.addPrefixToBGP(bgpManager, rd, externalIp + "/32", nextHopIp, label, LOG);
110
111                     List<Instruction> instructions = new ArrayList<Instruction>();
112                     List<ActionInfo> actionsInfos = new ArrayList<ActionInfo>();
113                     actionsInfos.add(new ActionInfo(ActionType.nx_resubmit, new String[] { Integer.toString(NatConstants.PDNAT_TABLE) }));
114                     instructions.add(new InstructionInfo(InstructionType.apply_actions, actionsInfos).buildInstruction(0));
115                     makeTunnelTableEntry(dpnId, label, instructions);
116
117                     //Install custom FIB routes
118                     List<Instruction> customInstructions = new ArrayList<>();
119                     customInstructions.add(new InstructionInfo(InstructionType.goto_table, new long[] { NatConstants.PDNAT_TABLE }).buildInstruction(0));
120                     makeLFibTableEntry(dpnId, label, NatConstants.PDNAT_TABLE);
121                     CreateFibEntryInput input = new CreateFibEntryInputBuilder().setVpnName(vpnName).setSourceDpid(dpnId).setInstruction(customInstructions)
122                             .setIpAddress(externalIp + "/32").setServiceId(label).setInstruction(customInstructions).build();
123                     //Future<RpcResult<java.lang.Void>> createFibEntry(CreateFibEntryInput input);
124                     Future<RpcResult<Void>> future = fibService.createFibEntry(input);
125                     return JdkFutureAdapters.listenInPoolThread(future);
126                 } else {
127                     String errMsg = String.format("Could not retrieve the label for prefix %s in VPN %s, %s", externalIp, vpnName, result.getErrors());
128                     LOG.error(errMsg);
129                     return Futures.immediateFailedFuture(new RuntimeException(errMsg));
130                 }
131             }
132         });
133
134         Futures.addCallback(future, new FutureCallback<RpcResult<Void>>() {
135
136             @Override
137             public void onFailure(Throwable error) {
138                 LOG.error("Error in generate label or fib install process", error);
139             }
140
141             @Override
142             public void onSuccess(RpcResult<Void> result) {
143                 if(result.isSuccessful()) {
144                     LOG.info("Successfully installed custom FIB routes for prefix {}", externalIp);
145                 } else {
146                     LOG.error("Error in rpc call to create custom Fib entries for prefix {} in DPN {}, {}", externalIp, dpnId, result.getErrors());
147                 }
148             }
149         });
150     }
151
152     @Override
153     public void onRemoveFloatingIp(final BigInteger dpnId, String routerId, Uuid networkId, final String externalIp,
154                                    String internalIp, final long label) {
155         final String vpnName = NatUtil.getAssociatedVPN(dataBroker, networkId, LOG);
156         if(vpnName == null) {
157             LOG.info("No VPN associated with ext nw {} to handle remove floating ip configuration {} in router {}",
158                     networkId, externalIp, routerId);
159             return;
160         }
161         //Remove Prefix from BGP
162         String rd = NatUtil.getVpnRd(dataBroker, vpnName);
163         removePrefixFromBGP(rd, externalIp + "/32");
164
165         //Remove custom FIB routes
166         //Future<RpcResult<java.lang.Void>> removeFibEntry(RemoveFibEntryInput input);
167         RemoveFibEntryInput input = new RemoveFibEntryInputBuilder().setVpnName(vpnName).setSourceDpid(dpnId).setIpAddress(externalIp + "/32").setServiceId(label).build();
168         Future<RpcResult<Void>> future = fibService.removeFibEntry(input);
169
170         ListenableFuture<RpcResult<Void>> labelFuture = Futures.transform(JdkFutureAdapters.listenInPoolThread(future), new AsyncFunction<RpcResult<Void>, RpcResult<Void>>() {
171
172             @Override
173             public ListenableFuture<RpcResult<Void>> apply(RpcResult<Void> result) throws Exception {
174                 //Release label
175                 if(result.isSuccessful()) {
176                     removeTunnelTableEntry(dpnId, label);
177                     removeLFibTableEntry(dpnId, label);
178                     RemoveVpnLabelInput labelInput = new RemoveVpnLabelInputBuilder().setVpnName(vpnName).setIpPrefix(externalIp).build();
179                     Future<RpcResult<Void>> labelFuture = vpnService.removeVpnLabel(labelInput);
180                     return JdkFutureAdapters.listenInPoolThread(labelFuture);
181                 } else {
182                     String errMsg = String.format("RPC call to remove custom FIB entries on dpn %s for prefix %s Failed - %s", dpnId, externalIp, result.getErrors());
183                     LOG.error(errMsg);
184                     return Futures.immediateFailedFuture(new RuntimeException(errMsg));
185                 }
186             }
187         });
188
189         Futures.addCallback(labelFuture, new FutureCallback<RpcResult<Void>>() {
190
191             @Override
192             public void onFailure(Throwable error) {
193                 LOG.error("Error in removing the label or custom fib entries", error);
194             }
195
196             @Override
197             public void onSuccess(RpcResult<Void> result) {
198                 if(result.isSuccessful()) {
199                     LOG.debug("Successfully removed the label for the prefix {} from VPN {}", externalIp, vpnName);
200                 } else {
201                     LOG.error("Error in removing the label for prefix {} from VPN {}, {}", externalIp, vpnName, result.getErrors());
202                 }
203             }
204         });
205     }
206
207     private void removePrefixFromBGP(String rd, String prefix) {
208         try {
209             bgpManager.deletePrefix(rd, prefix);
210         } catch(Exception e) {
211             LOG.error("Delete prefix failed", e);
212         }
213     }
214
215     void cleanupFibEntries(final BigInteger dpnId, final String vpnName, final String externalIp, final long label ) {
216         //Remove Prefix from BGP
217         String rd = NatUtil.getVpnRd(dataBroker, vpnName);
218         removePrefixFromBGP(rd, externalIp + "/32");
219
220         //Remove custom FIB routes
221
222         //Future<RpcResult<java.lang.Void>> removeFibEntry(RemoveFibEntryInput input);
223         RemoveFibEntryInput input = new RemoveFibEntryInputBuilder().setVpnName(vpnName).setSourceDpid(dpnId).setIpAddress(externalIp).setServiceId(label).build();
224         Future<RpcResult<Void>> future = fibService.removeFibEntry(input);
225
226         ListenableFuture<RpcResult<Void>> labelFuture = Futures.transform(JdkFutureAdapters.listenInPoolThread(future), 
227             new AsyncFunction<RpcResult<Void>, RpcResult<Void>>() {
228
229             @Override
230             public ListenableFuture<RpcResult<Void>> apply(RpcResult<Void> result) throws Exception {
231                 //Release label
232                 if(result.isSuccessful()) {
233                     removeTunnelTableEntry(dpnId, label);
234                     removeLFibTableEntry(dpnId, label);
235                     RemoveVpnLabelInput labelInput = new RemoveVpnLabelInputBuilder().setVpnName(vpnName).setIpPrefix(externalIp).build();
236                     Future<RpcResult<Void>> labelFuture = vpnService.removeVpnLabel(labelInput);
237                     return JdkFutureAdapters.listenInPoolThread(labelFuture);
238                 } else {
239                     String errMsg = String.format("RPC call to remove custom FIB entries on dpn %s for prefix %s Failed - %s", dpnId, externalIp, result.getErrors());
240                     LOG.error(errMsg);
241                     return Futures.immediateFailedFuture(new RuntimeException(errMsg));
242                 }
243             }
244         });
245
246         Futures.addCallback(labelFuture, new FutureCallback<RpcResult<Void>>() {
247
248             @Override
249             public void onFailure(Throwable error) {
250                 LOG.error("Error in removing the label or custom fib entries", error);
251             }
252
253             @Override
254             public void onSuccess(RpcResult<Void> result) {
255                 if(result.isSuccessful()) {
256                     LOG.debug("Successfully removed the label for the prefix {} from VPN {}", externalIp, vpnName);
257                 } else {
258                     LOG.error("Error in removing the label for prefix {} from VPN {}, {}", externalIp, vpnName, result.getErrors());
259                 }
260             }
261         });
262     }
263
264     private String getFlowRef(BigInteger dpnId, short tableId, long id, String ipAddress) {
265         return new StringBuilder(64).append(FLOWID_PREFIX).append(dpnId).append(NwConstants.FLOWID_SEPARATOR)
266                 .append(tableId).append(NwConstants.FLOWID_SEPARATOR)
267                 .append(id).append(NwConstants.FLOWID_SEPARATOR).append(ipAddress).toString();
268     }
269
270     private void removeTunnelTableEntry(BigInteger dpnId, long serviceId) {
271         LOG.info("remove terminatingServiceActions called with DpnId = {} and label = {}", dpnId , serviceId);
272         List<MatchInfo> mkMatches = new ArrayList<MatchInfo>();
273         // Matching metadata
274         mkMatches.add(new MatchInfo(MatchFieldType.tunnel_id, new BigInteger[] {BigInteger.valueOf(serviceId)}));
275         Flow flowEntity = MDSALUtil.buildFlowNew(NwConstants.INTERNAL_TUNNEL_TABLE,
276                 getFlowRef(dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, serviceId, ""),
277                 5, String.format("%s:%d","TST Flow Entry ",serviceId), 0, 0,
278                 COOKIE_TUNNEL.add(BigInteger.valueOf(serviceId)), mkMatches, null);
279         mdsalManager.removeFlow(dpnId, flowEntity);
280         LOG.debug("Terminating service Entry for dpID {} : label : {} removed successfully {}",dpnId, serviceId);
281     }
282
283     private void makeTunnelTableEntry(BigInteger dpnId, long serviceId, List<Instruction> customInstructions) {
284         List<MatchInfo> mkMatches = new ArrayList<MatchInfo>();
285
286         LOG.info("create terminatingServiceAction on DpnId = {} and serviceId = {} and actions = {}", dpnId , serviceId);
287
288         mkMatches.add(new MatchInfo(MatchFieldType.tunnel_id, new BigInteger[] {BigInteger.valueOf(serviceId)}));
289
290         Flow terminatingServiceTableFlowEntity = MDSALUtil.buildFlowNew(NwConstants.INTERNAL_TUNNEL_TABLE,
291                 getFlowRef(dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, serviceId, ""), 5, String.format("%s:%d","TST Flow Entry ",serviceId),
292                 0, 0, COOKIE_TUNNEL.add(BigInteger.valueOf(serviceId)),mkMatches, customInstructions);
293
294         mdsalManager.installFlow(dpnId, terminatingServiceTableFlowEntity);
295     }
296
297     private void makeLFibTableEntry(BigInteger dpId, long serviceId, long tableId) {
298         List<MatchInfo> matches = new ArrayList<MatchInfo>();
299         matches.add(new MatchInfo(MatchFieldType.eth_type,
300                 new long[] { 0x8847L }));
301         matches.add(new MatchInfo(MatchFieldType.mpls_label, new String[]{Long.toString(serviceId)}));
302
303         List<Instruction> instructions = new ArrayList<Instruction>();
304         List<ActionInfo> actionsInfos = new ArrayList<ActionInfo>();
305         actionsInfos.add(new ActionInfo(ActionType.pop_mpls, new String[]{}));
306         Instruction writeInstruction = new InstructionInfo(InstructionType.apply_actions, actionsInfos).buildInstruction(0);
307         instructions.add(writeInstruction);
308         instructions.add(new InstructionInfo(InstructionType.goto_table, new long[]{tableId}).buildInstruction(1));
309
310         // Install the flow entry in L3_LFIB_TABLE
311         String flowRef = getFlowRef(dpId, NwConstants.L3_LFIB_TABLE, serviceId, "");
312
313         Flow flowEntity = MDSALUtil.buildFlowNew(NwConstants.L3_LFIB_TABLE, flowRef,
314                 10, flowRef, 0, 0,
315                 COOKIE_VM_LFIB_TABLE, matches, instructions);
316
317         mdsalManager.installFlow(dpId, flowEntity);
318
319         LOG.debug("LFIB Entry for dpID {} : label : {} modified successfully {}",dpId, serviceId );
320     }
321
322     private void removeLFibTableEntry(BigInteger dpnId, long serviceId) {
323         List<MatchInfo> matches = new ArrayList<MatchInfo>();
324         matches.add(new MatchInfo(MatchFieldType.eth_type,
325                                   new long[] { 0x8847L }));
326         matches.add(new MatchInfo(MatchFieldType.mpls_label, new String[]{Long.toString(serviceId)}));
327
328         String flowRef = getFlowRef(dpnId, NwConstants.L3_LFIB_TABLE, serviceId, "");
329
330         LOG.debug("removing LFib entry with flow ref {}", flowRef);
331
332         Flow flowEntity = MDSALUtil.buildFlowNew(NwConstants.L3_LFIB_TABLE, flowRef,
333                                                10, flowRef, 0, 0,
334                                                COOKIE_VM_LFIB_TABLE, matches, null);
335
336         mdsalManager.removeFlow(dpnId, flowEntity);
337
338         LOG.debug("LFIB Entry for dpID : {} label : {} removed successfully {}",dpnId, serviceId);
339     }
340
341 }
342