Allow MDSALManager exceptions
[netvirt.git] / natservice / impl / src / main / java / org / opendaylight / netvirt / natservice / internal / EvpnSnatFlowProgrammer.java
1 /*
2  * Copyright (c) 2017 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
9 package org.opendaylight.netvirt.natservice.internal;
10
11 import static org.opendaylight.genius.infra.Datastore.CONFIGURATION;
12
13 import com.google.common.util.concurrent.FutureCallback;
14 import com.google.common.util.concurrent.Futures;
15 import com.google.common.util.concurrent.ListenableFuture;
16 import com.google.common.util.concurrent.MoreExecutors;
17 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
18 import java.math.BigInteger;
19 import java.util.ArrayList;
20 import java.util.List;
21 import javax.annotation.Nonnull;
22 import javax.inject.Inject;
23 import javax.inject.Singleton;
24 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
25 import org.opendaylight.genius.infra.Datastore.Configuration;
26 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
27 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
28 import org.opendaylight.genius.infra.TypedReadWriteTransaction;
29 import org.opendaylight.genius.infra.TypedWriteTransaction;
30 import org.opendaylight.genius.mdsalutil.MDSALUtil;
31 import org.opendaylight.genius.mdsalutil.MatchInfo;
32 import org.opendaylight.genius.mdsalutil.NwConstants;
33 import org.opendaylight.genius.mdsalutil.instructions.InstructionGotoTable;
34 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
35 import org.opendaylight.genius.mdsalutil.matches.MatchTunnelId;
36 import org.opendaylight.infrautils.utils.concurrent.ListenableFutures;
37 import org.opendaylight.netvirt.bgpmanager.api.IBgpManager;
38 import org.opendaylight.netvirt.fibmanager.api.IFibManager;
39 import org.opendaylight.netvirt.fibmanager.api.RouteOrigin;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.CreateFibEntryInput;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.CreateFibEntryInputBuilder;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.CreateFibEntryOutput;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.FibRpcService;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.RemoveFibEntryInput;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.RemoveFibEntryInputBuilder;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.RemoveFibEntryOutput;
50 import org.opendaylight.yangtools.yang.common.RpcResult;
51 import org.slf4j.Logger;
52 import org.slf4j.LoggerFactory;
53
54 @Singleton
55 public class EvpnSnatFlowProgrammer {
56     private static final Logger LOG = LoggerFactory.getLogger(EvpnSnatFlowProgrammer.class);
57
58     private static final BigInteger COOKIE_TUNNEL = new BigInteger("9000000", 16);
59
60     private final DataBroker dataBroker;
61     private final ManagedNewTransactionRunner txRunner;
62     private final IMdsalApiManager mdsalManager;
63     private final IBgpManager bgpManager;
64     private final IFibManager fibManager;
65     private final FibRpcService fibService;
66     private final IdManagerService idManager;
67
68     @Inject
69     public EvpnSnatFlowProgrammer(final DataBroker dataBroker, final IMdsalApiManager mdsalManager,
70                            final IBgpManager bgpManager,
71                            final IFibManager fibManager,
72                            final FibRpcService fibService,
73                            final IdManagerService idManager) {
74         this.dataBroker = dataBroker;
75         this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
76         this.mdsalManager = mdsalManager;
77         this.bgpManager = bgpManager;
78         this.fibManager = fibManager;
79         this.fibService = fibService;
80         this.idManager = idManager;
81     }
82
83     public void evpnAdvToBgpAndInstallFibAndTsFlows(final BigInteger dpnId, final short tableId,
84         final String externalIp, final String vpnName, final String rd, final String nextHopIp,
85         final long routerId, final String routerName, TypedWriteTransaction<Configuration> confTx) {
86      /*
87       * 1) Install the flow INTERNAL_TUNNEL_TABLE (table=36)-> INBOUND_NAPT_TABLE (table=44)
88       *    (FIP VM on DPN1 is responding back to external fixed IP on DPN2) {DNAT to SNAT traffic on
89       *     different Hypervisor}
90       *
91       * 2) Install the flow L3_GW_MAC_TABLE (table=19)-> INBOUND_NAPT_TABLE (table=44)
92       *    (FIP VM on DPN1 is responding back to external fixed IP on beyond DC-GW VM){DNAT to SNAT Inter DC traffic}
93       *
94       * 3) Install the flow PDNAT_TABLE (table=25)-> INBOUND_NAPT_TABLE (table=44)
95       *    (If there is no FIP Match on table 25 (PDNAT_TABLE) then default flow to INBOUND_NAPT_TABLE (table=44))
96       *
97       * 4) Install the flow L3_FIB_TABLE (table=21)-> INBOUND_NAPT_TABLE (table=44)
98       *    (FIP VM on DPN1 is responding back to external fixed Ip on DPN1 itself. ie. same Hypervisor)
99       *    {DNAT to SNAT Intra DC traffic}
100       */
101         LOG.info("evpnAdvToBgpAndInstallFibAndTsFlows : Handling SNAT Reverse Traffic for External Fixed IP {} for "
102                 + "RouterId {}", externalIp, routerId);
103         // Get the External Gateway MAC Address which is Router gateway MAC address for SNAT
104         String gwMacAddress = NatUtil.getExtGwMacAddFromRouterName(dataBroker, routerName);
105         if (gwMacAddress == null) {
106             LOG.error("evpnAdvToBgpAndInstallFibAndTsFlows : Unable to Retrieve External Gateway MAC address "
107                     + "from Router ID {}", routerId);
108             return;
109         }
110         //get l3Vni value for external VPN
111         long l3Vni = NatEvpnUtil.getL3Vni(dataBroker, rd);
112         if (l3Vni == NatConstants.DEFAULT_L3VNI_VALUE) {
113             LOG.debug("evpnAdvToBgpAndInstallFibAndTsFlows : L3VNI value is not configured in Internet VPN {}"
114                     + " and RD {} Carve-out L3VNI value from OpenDaylight VXLAN VNI Pool and continue with "
115                     + "installing SNAT flows for External Fixed IP {}", vpnName, rd, externalIp);
116             l3Vni = NatOverVxlanUtil.getInternetVpnVni(idManager, vpnName, routerId).longValue();
117         }
118
119         long vpnId = NatUtil.getVpnId(dataBroker, vpnName);
120         if (vpnId == NatConstants.INVALID_ID) {
121             LOG.error("evpnAdvToBgpAndInstallFibAndTsFlows : Invalid Vpn Id is found for Vpn Name {}",
122                     vpnName);
123             return;
124         }
125         /* As of now neither SNAT nor DNAT will use mac-address while advertising to FIB and BGP instead
126          * use only gwMacAddress. Hence default value of macAddress is null
127          */
128         //Inform to BGP
129         NatEvpnUtil.addRoutesForVxLanProvType(dataBroker, bgpManager, fibManager, vpnName, rd, externalIp,
130                 nextHopIp, l3Vni, null /*InterfaceName*/, gwMacAddress, confTx, RouteOrigin.STATIC, dpnId);
131
132         //Install custom FIB routes - FIB table.
133         List<Instruction> customInstructions = new ArrayList<>();
134         customInstructions.add(new InstructionGotoTable(tableId).buildInstruction(0));
135         final String externalFixedIp = NatUtil.validateAndAddNetworkMask(externalIp);
136
137         CreateFibEntryInput input = new CreateFibEntryInputBuilder().setVpnName(vpnName)
138                 .setSourceDpid(dpnId).setIpAddress(externalFixedIp)
139                 .setServiceId(l3Vni).setIpAddressSource(CreateFibEntryInput.IpAddressSource.ExternalFixedIP)
140                 .setInstruction(customInstructions).build();
141         LOG.debug("evpnAdvToBgpAndInstallFibAndTsFlows : Installing custom FIB table {} --> table {} flow on "
142                 + "NAPT Switch {} with l3Vni {}, ExternalFixedIp {}, ExternalVpnName {} for RouterId {}",
143                 NwConstants.L3_FIB_TABLE, tableId, dpnId, l3Vni, externalIp, vpnName, routerId);
144
145         ListenableFuture<RpcResult<CreateFibEntryOutput>> futureVxlan = fibService.createFibEntry(input);
146
147         final long finalL3Vni = l3Vni;
148         Futures.addCallback(futureVxlan, new FutureCallback<RpcResult<CreateFibEntryOutput>>() {
149             @Override
150             public void onFailure(@Nonnull Throwable error) {
151                 LOG.error("evpnAdvToBgpAndInstallFibAndTsFlows : Error in custom fib routes install process for "
152                         + "External Fixed IP {} on DPN {} with l3Vni {}, ExternalVpnName {} for RouterId {}",
153                         externalIp, dpnId, finalL3Vni, vpnName, routerId, error);
154             }
155
156             @Override
157             public void onSuccess(@Nonnull RpcResult<CreateFibEntryOutput> result) {
158                 if (result.isSuccessful()) {
159                     LOG.info("evpnAdvToBgpAndInstallFibAndTsFlows : Successfully installed custom FIB routes for "
160                             + "External Fixed IP {} on DPN {} with l3Vni {}, ExternalVpnName {} for RouterId {}",
161                             externalIp, dpnId, finalL3Vni, vpnName, routerId);
162
163                  /* Install the flow INTERNAL_TUNNEL_TABLE (table=36)-> INBOUND_NAPT_TABLE (table=44)
164                   * (SNAT to DNAT reverse Traffic: If traffic is Initiated from NAPT to FIP VM on different Hypervisor)
165                   */
166                     makeTunnelTableEntry(dpnId, finalL3Vni, customInstructions, tableId, confTx);
167                  /* Install the flow L3_GW_MAC_TABLE (table=19)-> INBOUND_NAPT_TABLE (table=44)
168                   * (SNAT reverse traffic: If the traffic is Initiated from DC-GW to VM (SNAT Reverse traffic))
169                   */
170                     NatEvpnUtil.makeL3GwMacTableEntry(dpnId, vpnId, gwMacAddress, customInstructions, mdsalManager,
171                             confTx);
172
173                  /* Install the flow PDNAT_TABLE (table=25)-> INBOUND_NAPT_TABLE (table=44)
174                   * If there is no FIP Match on table 25 (PDNAT_TABLE)
175                   */
176                     NatUtil.makePreDnatToSnatTableEntry(mdsalManager, dpnId, tableId, confTx);
177                 }
178             }
179         }, MoreExecutors.directExecutor());
180     }
181
182     public void evpnDelFibTsAndReverseTraffic(final BigInteger dpnId, final long routerId, final String externalIp,
183         final String vpnName, String extGwMacAddress) {
184      /*
185       * 1) Remove the flow INTERNAL_TUNNEL_TABLE (table=36)-> INBOUND_NAPT_TABLE (table=44)
186       *    (FIP VM on DPN1 is responding back to external fixed IP on DPN2) {DNAT to SNAT traffic on
187       *     different Hypervisor}
188       *
189       * 2) Remove the flow L3_GW_MAC_TABLE (table=19)-> INBOUND_NAPT_TABLE (table=44)
190       *    (FIP VM on DPN1 is responding back to external fixed IP on DPN1 itself){DNAT to SNAT traffic on
191       *     Same Hypervisor}
192       *
193       * 3) Remove the flow PDNAT_TABLE (table=25)-> INBOUND_NAPT_TABLE (table=44)
194       *    (If there is no FIP Match on table 25 (PDNAT_TABLE) then default flow to INBOUND_NAPT_TABLE (table=44))
195       *
196       * 4) Remove the flow L3_FIB_TABLE (table=21)-> INBOUND_NAPT_TABLE (table=44)
197       *    (FIP VM on DPN1 is responding back to external fixed Ip on DPN1 itself. ie. same Hypervisor)
198       *    {DNAT to SNAT Intra DC traffic}
199       */
200         String rd = NatUtil.getVpnRd(dataBroker, vpnName);
201         if (rd == null) {
202             LOG.error("evpnDelFibTsAndReverseTraffic : Could not retrieve RD value from VPN Name {}", vpnName);
203             return;
204         }
205         long vpnId = NatUtil.getVpnId(dataBroker, vpnName);
206         if (vpnId == NatConstants.INVALID_ID) {
207             LOG.error("evpnDelFibTsAndReverseTraffic : Invalid Vpn Id is found for Vpn Name {}", vpnName);
208             return;
209         }
210         if (extGwMacAddress == null) {
211             LOG.error("evpnDelFibTsAndReverseTraffic : Unable to Get External Gateway MAC address for "
212                     + "External Router ID {} ", routerId);
213             return;
214         }
215         long l3Vni = NatEvpnUtil.getL3Vni(dataBroker, rd);
216         if (l3Vni == NatConstants.DEFAULT_L3VNI_VALUE) {
217             LOG.debug("evpnDelFibTsAndReverseTraffic : L3VNI value is not configured in Internet VPN {} and RD {} "
218                     + "Carve-out L3VNI value from OpenDaylight VXLAN VNI Pool and continue with installing "
219                     + "SNAT flows for External Fixed IP {}", vpnName, rd, externalIp);
220             l3Vni = NatOverVxlanUtil.getInternetVpnVni(idManager, vpnName, routerId).longValue();
221         }
222
223         final String externalFixedIp = NatUtil.validateAndAddNetworkMask(externalIp);
224         RemoveFibEntryInput input = new RemoveFibEntryInputBuilder()
225                 .setVpnName(vpnName).setSourceDpid(dpnId).setIpAddress(externalFixedIp)
226                 .setIpAddressSource(RemoveFibEntryInput.IpAddressSource.ExternalFixedIP).setServiceId(l3Vni).build();
227         LOG.debug("evpnDelFibTsAndReverseTraffic : Removing custom FIB table {} --> table {} flow on "
228                         + "NAPT Switch {} with l3Vni {}, ExternalFixedIp {}, ExternalVpnName {} for RouterId {}",
229                 NwConstants.L3_FIB_TABLE, NwConstants.INBOUND_NAPT_TABLE, dpnId, l3Vni, externalIp, vpnName, routerId);
230
231         ListenableFuture<RpcResult<RemoveFibEntryOutput>> futureVxlan = fibService.removeFibEntry(input);
232         final long finalL3Vni = l3Vni;
233         Futures.addCallback(futureVxlan, new FutureCallback<RpcResult<RemoveFibEntryOutput>>() {
234             @Override
235             public void onFailure(@Nonnull Throwable error) {
236                 LOG.error("evpnDelFibTsAndReverseTraffic : Error in custom fib routes remove process for "
237                         + "External Fixed IP {} on DPN {} with l3Vni {}, ExternalVpnName {} for RouterId {}",
238                         externalIp, dpnId, finalL3Vni, vpnName, routerId, error);
239             }
240
241             @Override
242             public void onSuccess(@Nonnull RpcResult<RemoveFibEntryOutput> result) {
243                 if (result.isSuccessful()) {
244                     ListenableFutures.addErrorLogging(txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION,
245                         innerConfTx -> {
246                             LOG.info("evpnDelFibTsAndReverseTraffic : Successfully removed custom FIB routes for "
247                                 + "External Fixed IP {} on DPN {} with l3Vni {}, ExternalVpnName {} for "
248                                 + "RouterId {}", externalIp, dpnId, finalL3Vni, vpnName, routerId);
249
250                             //remove INTERNAL_TUNNEL_TABLE (table=36)-> INBOUND_NAPT_TABLE (table=44) flow
251                             removeTunnelTableEntry(dpnId, finalL3Vni, innerConfTx);
252                             //remove L3_GW_MAC_TABLE (table=19)-> INBOUND_NAPT_TABLE (table=44) flow
253                             NatUtil.removePreDnatToSnatTableEntry(innerConfTx, mdsalManager, dpnId);
254                             //remove PDNAT_TABLE (table=25)-> INBOUND_NAPT_TABLE (table=44) flow
255                             NatEvpnUtil.removeL3GwMacTableEntry(dpnId, vpnId, extGwMacAddress, mdsalManager,
256                                 innerConfTx);
257                         }), LOG, "Error removing EVPN SNAT table entries");
258                 }
259             }
260         }, MoreExecutors.directExecutor());
261     }
262
263     public void makeTunnelTableEntry(BigInteger dpnId, long l3Vni, List<Instruction> customInstructions,
264                                      short tableId, TypedWriteTransaction<Configuration> confTx) {
265         LOG.debug("makeTunnelTableEntry : Create terminating service table {} --> table {} flow on NAPT DpnId {} "
266                 + "with l3Vni {} as matching parameter", NwConstants.INTERNAL_TUNNEL_TABLE, tableId, dpnId, l3Vni);
267         List<MatchInfo> mkMatches = new ArrayList<>();
268         mkMatches.add(new MatchTunnelId(BigInteger.valueOf(l3Vni)));
269
270         Flow terminatingServiceTableFlowEntity = MDSALUtil.buildFlowNew(NwConstants.INTERNAL_TUNNEL_TABLE,
271                 NatEvpnUtil.getFlowRef(dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, l3Vni, NatConstants.SNAT_FLOW_NAME), 5,
272                 String.format("%s:%d", "TST Flow Entry ", l3Vni),
273                 0, 0, COOKIE_TUNNEL.add(BigInteger.valueOf(l3Vni)), mkMatches, customInstructions);
274         mdsalManager.addFlow(confTx, dpnId, terminatingServiceTableFlowEntity);
275         LOG.debug("makeTunnelTableEntry : Successfully installed terminating service table flow {} on DpnId {}",
276                 terminatingServiceTableFlowEntity, dpnId);
277     }
278
279     // TODO skitt Fix the exception handling here
280     @SuppressWarnings("checkstyle:IllegalCatch")
281     @SuppressFBWarnings("REC_CATCH_EXCEPTION")
282     public void removeTunnelTableEntry(BigInteger dpnId, long l3Vni, TypedReadWriteTransaction<Configuration> confTx) {
283         LOG.debug("removeTunnelTableEntry : Remove terminating service table {} --> table {} flow on NAPT DpnId {} "
284                 + "with l3Vni {} as matching parameter", NwConstants.INTERNAL_TUNNEL_TABLE,
285                 NwConstants.INBOUND_NAPT_TABLE, dpnId, l3Vni);
286         List<MatchInfo> mkMatches = new ArrayList<>();
287         // Matching metadata
288         mkMatches.add(new MatchTunnelId(BigInteger.valueOf(l3Vni)));
289         Flow flowEntity = MDSALUtil.buildFlowNew(NwConstants.INTERNAL_TUNNEL_TABLE,
290                 NatEvpnUtil.getFlowRef(dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, l3Vni, NatConstants.SNAT_FLOW_NAME),
291                 5, String.format("%s:%d", "TST Flow Entry ", l3Vni), 0, 0,
292                 COOKIE_TUNNEL.add(BigInteger.valueOf(l3Vni)), mkMatches, null);
293         try {
294             mdsalManager.removeFlow(confTx, dpnId, flowEntity);
295         } catch (Exception e) {
296             LOG.error("Error removing flow", e);
297             throw new RuntimeException("Error removing flow", e);
298         }
299         LOG.debug("removeTunnelTableEntry : Successfully removed terminating service table flow {} on DpnId {}",
300                 flowEntity, dpnId);
301     }
302 }