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