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