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