4c1710179063b822ca4078e4c3aebba8788a9d00
[netvirt.git] / vpnservice / fibmanager / fibmanager-impl / src / main / java / org / opendaylight / netvirt / fibmanager / EVPNVrfEntryProcessor.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 package org.opendaylight.netvirt.fibmanager;
9
10 import com.google.common.base.Optional;
11 import com.google.common.base.Preconditions;
12 import com.google.common.util.concurrent.ListenableFuture;
13 import java.math.BigInteger;
14 import java.util.ArrayList;
15 import java.util.Collections;
16 import java.util.List;
17 import java.util.concurrent.Callable;
18 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
19 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
20 import org.opendaylight.genius.datastoreutils.DataStoreJobCoordinator;
21 import org.opendaylight.genius.mdsalutil.ActionInfo;
22 import org.opendaylight.genius.mdsalutil.InstructionInfo;
23 import org.opendaylight.genius.mdsalutil.NwConstants;
24 import org.opendaylight.genius.mdsalutil.actions.ActionGroup;
25 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldEthernetDestination;
26 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldTunnelId;
27 import org.opendaylight.genius.mdsalutil.instructions.InstructionApplyActions;
28 import org.opendaylight.netvirt.fibmanager.api.RouteOrigin;
29 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface;
30 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.fibentries.VrfTables;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.fibentries.VrfTablesKey;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentries.VrfEntry;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.vpnnexthops.VpnNexthop;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.prefix.to._interface.vpn.ids.Prefixes;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntry;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.VpnToDpnList;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.to.extraroutes.vpn.extra.routes.Routes;
39 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
40 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory;
42
43 public class EVPNVrfEntryProcessor {
44     private final Logger logger = LoggerFactory.getLogger(EVPNVrfEntryProcessor.class);
45     private VrfEntry vrfEntry;
46     private VrfEntry updatedVrfEntry;
47     private InstanceIdentifier<VrfEntry> identifier;
48     private DataBroker dataBroker;
49     private VrfEntryListener vrfEntryListener;
50
51     EVPNVrfEntryProcessor(InstanceIdentifier<VrfEntry> identifier, VrfEntry vrfEntry, DataBroker broker,
52                           VrfEntryListener vrfEntryListener) {
53         this.identifier = identifier;
54         this.vrfEntry = vrfEntry;
55         this.dataBroker = broker;
56         this.vrfEntryListener = vrfEntryListener;
57     }
58
59     EVPNVrfEntryProcessor(InstanceIdentifier<VrfEntry> identifier, VrfEntry original, VrfEntry update,
60                           DataBroker broker, VrfEntryListener vrfEntryListener) {
61         this(identifier, original, broker, vrfEntryListener);
62         this.updatedVrfEntry = update;
63     }
64
65     public void installFlows() {
66         logger.info("Initiating creation of Evpn Flows");
67         final VrfTablesKey vrfTableKey = identifier.firstKeyOf(VrfTables.class);
68         final String rd = vrfTableKey.getRouteDistinguisher();
69         final VpnInstanceOpDataEntry vpnInstance = FibUtil.getVpnInstanceOpData(dataBroker,
70                 vrfTableKey.getRouteDistinguisher()).get();
71         Long vpnId = vpnInstance.getVpnId();
72         Preconditions.checkNotNull(vpnInstance, "Vpn Instance not available " + vrfTableKey.getRouteDistinguisher());
73         Preconditions.checkNotNull(vpnId, "Vpn Instance with rd " + vpnInstance.getVrfId()
74                 + " has null vpnId!");
75         Prefixes localNextHopInfo = FibUtil.getPrefixToInterface(dataBroker, vpnInstance.getVpnId(),
76                 vrfEntry.getDestPrefix());
77         List<BigInteger> localDpnId = new ArrayList<>();
78         boolean isNatPrefix = false;
79         if (Boolean.TRUE.equals(localNextHopInfo.isNatPrefix())) {
80             logger.info("NAT Prefix {} with vpnId {} rd {}. Skip local dpn {} FIB processing",
81                     vrfEntry.getDestPrefix(), vpnId, rd, localNextHopInfo.getDpnId());
82             localDpnId.add(localNextHopInfo.getDpnId());
83             isNatPrefix = true;
84         } else {
85             localDpnId = createLocalEvpnFlows(vpnInstance.getVpnId(), rd, vrfEntry,
86                     localNextHopInfo);
87         }
88         createRemoteEvpnFlows(rd, vrfEntry, vpnInstance, localDpnId, vrfTableKey, isNatPrefix);
89     }
90
91     private List<BigInteger> createLocalEvpnFlows(long vpnId, String rd, VrfEntry vrfEntry,
92                                                   Prefixes localNextHopInfo) {
93         List<BigInteger> returnLocalDpnId = new ArrayList<>();
94         if (localNextHopInfo == null) {
95             //Handle extra routes and imported routes
96             Routes extraRoute = vrfEntryListener.getVpnToExtraroute(rd, vrfEntry.getDestPrefix());
97             if (extraRoute != null) {
98                 for (String nextHopIp : extraRoute.getNexthopIpList()) {
99                     logger.info("NextHop IP for destination {} is {}", vrfEntry.getDestPrefix(), nextHopIp);
100                     if (nextHopIp != null) {
101                         localNextHopInfo = FibUtil.getPrefixToInterface(dataBroker, vpnId, nextHopIp + "/32");
102                         if (localNextHopInfo != null) {
103                             String localNextHopIP = nextHopIp + "/32";
104                             BigInteger dpnId = checkCreateLocalEvpnFlows(localNextHopInfo, localNextHopIP, vpnId,
105                                     rd, vrfEntry);
106                             returnLocalDpnId.add(dpnId);
107                         }
108                     }
109                 }
110             }
111         } else {
112             logger.info("Creating local EVPN flows for prefix {} rd {} route-paths {} evi {}.",
113                     vrfEntry.getDestPrefix(), rd, vrfEntry.getRoutePaths(), vrfEntry.getL3vni());
114             String localNextHopIP = vrfEntry.getDestPrefix();
115             BigInteger dpnId = checkCreateLocalEvpnFlows(localNextHopInfo, localNextHopIP, vpnId,
116                     rd, vrfEntry);
117             returnLocalDpnId.add(dpnId);
118         }
119         return returnLocalDpnId;
120     }
121
122     private BigInteger checkCreateLocalEvpnFlows(Prefixes localNextHopInfo, String localNextHopIP,
123                                                 final Long vpnId, final String rd,
124                                                 final VrfEntry vrfEntry) {
125         final BigInteger dpnId = localNextHopInfo.getDpnId();
126         String jobKey = "FIB-" + vpnId.toString() + "-" + dpnId.toString() + "-" + vrfEntry.getDestPrefix();
127         final long groupId = vrfEntryListener.getNextHopManager().createLocalNextHop(vpnId, dpnId,
128                 localNextHopInfo.getVpnInterfaceName(), localNextHopIP, vrfEntry.getDestPrefix(),
129                 vrfEntry.getGatewayMacAddress(), jobKey);
130         logger.debug("LocalNextHopGroup {} created/reused for prefix {} rd {} evi {} route-paths {}", groupId,
131                 vrfEntry.getDestPrefix(), rd, vrfEntry.getL3vni(), vrfEntry.getRoutePaths());
132
133         final List<InstructionInfo> instructions = Collections.singletonList(
134                 new InstructionApplyActions(
135                         Collections.singletonList(new ActionGroup(groupId))));
136         DataStoreJobCoordinator dataStoreCoordinator = DataStoreJobCoordinator.getInstance();
137         dataStoreCoordinator.enqueueJob("FIB-" + vpnId.toString() + "-" + dpnId.toString()
138                 + "-" + vrfEntry.getDestPrefix(),
139                 new Callable<List<ListenableFuture<Void>>>() {
140                     @Override
141                     public List<ListenableFuture<Void>> call() throws Exception {
142                         WriteTransaction tx = dataBroker.newWriteOnlyTransaction();
143                         vrfEntryListener.makeConnectedRoute(dpnId, vpnId, vrfEntry, rd, instructions,
144                                 NwConstants.ADD_FLOW, tx);
145
146                         List<ListenableFuture<Void>> futures = new ArrayList<>();
147                         futures.add(tx.submit());
148                         return futures;
149                     }
150                 });
151         return dpnId;
152     }
153
154     private void createRemoteEvpnFlows(String rd, VrfEntry vrfEntry, VpnInstanceOpDataEntry vpnInstance,
155                                        List<BigInteger> localDpnId, VrfTablesKey vrfTableKey, boolean isNatPrefix) {
156         logger.info("Creating remote EVPN flows for prefix {} rd {} route-paths {} evi {}",
157                 vrfEntry.getDestPrefix(), rd, vrfEntry.getRoutePaths(), vrfEntry.getL3vni());
158         List<VpnToDpnList> vpnToDpnList = vpnInstance.getVpnToDpnList();
159         if (vpnToDpnList != null) {
160             DataStoreJobCoordinator dataStoreCoordinator = DataStoreJobCoordinator.getInstance();
161             dataStoreCoordinator = DataStoreJobCoordinator.getInstance();
162             dataStoreCoordinator.enqueueJob("FIB" + rd.toString() + vrfEntry.getDestPrefix(),
163                     new Callable<List<ListenableFuture<Void>>>() {
164                         @Override
165                         public List<ListenableFuture<Void>> call() throws Exception {
166                             WriteTransaction tx = dataBroker.newWriteOnlyTransaction();
167                             List<ListenableFuture<Void>> futures = new ArrayList<>();
168                             for (VpnToDpnList vpnDpn : vpnToDpnList) {
169                                 if (!localDpnId.contains(vpnDpn.getDpnId())) {
170                                     if (vpnDpn.getDpnState() == VpnToDpnList.DpnState.Active) {
171                                         createRemoteFibEntry(vpnDpn.getDpnId(), vpnInstance.getVpnId(),
172                                                 vrfTableKey, vrfEntry, isNatPrefix, tx);
173                                     }
174                                 }
175                             }
176                             futures.add(tx.submit());
177                             return futures;
178                         }
179                     });
180         }
181     }
182
183     private void createRemoteFibEntry(final BigInteger remoteDpnId, final long vpnId, final VrfTablesKey vrfTableKey,
184                                       final VrfEntry vrfEntry, boolean isNatPrefix, WriteTransaction tx) {
185         Boolean wrTxPresent = true;
186         if (tx == null) {
187             wrTxPresent = false;
188             tx = dataBroker.newWriteOnlyTransaction();
189         }
190         String rd = vrfTableKey.getRouteDistinguisher();
191         logger.debug("createremotefibentry: adding route {} for rd {} with transaction {}",
192                 vrfEntry.getDestPrefix(), rd, tx);
193         List<NexthopManager.AdjacencyResult> tunnelInterfaceList = vrfEntryListener
194                 .resolveAdjacency(remoteDpnId, vpnId, vrfEntry, rd);
195
196         if (tunnelInterfaceList.isEmpty()) {
197             logger.error("Could not get interface for route-paths: {} in vpn {}",
198                     vrfEntry.getRoutePaths(), rd);
199             logger.warn("Failed to add Route: {} in vpn: {}",
200                     vrfEntry.getDestPrefix(), rd);
201             return;
202         }
203
204         for (NexthopManager.AdjacencyResult adjacencyResult : tunnelInterfaceList) {
205             List<ActionInfo> actionInfos = new ArrayList<>();
206             BigInteger tunnelId;
207             String prefix = adjacencyResult.getPrefix();
208             Prefixes prefixInfo = FibUtil.getPrefixToInterface(dataBroker, vpnId, prefix);
209             String interfaceName = prefixInfo.getVpnInterfaceName();
210             if (vrfEntry.getOrigin().equals(RouteOrigin.BGP) || isNatPrefix) {
211                 tunnelId = BigInteger.valueOf(vrfEntry.getL3vni());
212             } else {
213                 Interface interfaceState = FibUtil.getInterfaceStateFromOperDS(dataBroker, interfaceName);
214                 tunnelId = BigInteger.valueOf(interfaceState.getIfIndex());
215             }
216             logger.debug("adding set tunnel id action for label {}", tunnelId);
217             String macAddress = null;
218             if (interfaceName != null) {
219                 macAddress = FibUtil.getMacAddressFromPrefix(dataBroker, interfaceName, prefix);
220                 actionInfos.add(new ActionSetFieldEthernetDestination(new MacAddress(macAddress)));
221             }
222             actionInfos.add(new ActionSetFieldTunnelId(tunnelId));
223             List<ActionInfo> egressActions = vrfEntryListener.getNextHopManager()
224                     .getEgressActionsForInterface(adjacencyResult.getInterfaceName(), actionInfos.size());
225             if (egressActions.isEmpty()) {
226                 logger.error("Failed to retrieve egress action for prefix {} route-paths {} interface {}."
227                         + " Aborting remote FIB entry creation..", vrfEntry.getDestPrefix(),
228                         vrfEntry.getRoutePaths(), adjacencyResult.getInterfaceName());
229                 return;
230             }
231             actionInfos.addAll(egressActions);
232             List<InstructionInfo> instructions = new ArrayList<>();
233             instructions.add(new InstructionApplyActions(actionInfos));
234             vrfEntryListener.makeConnectedRoute(remoteDpnId, vpnId, vrfEntry, rd, instructions,
235                     NwConstants.ADD_FLOW, tx);
236         }
237         if (!wrTxPresent) {
238             tx.submit();
239         }
240         logger.debug("Successfully added FIB entry for prefix {} in rd {}", vrfEntry.getDestPrefix(), rd);
241     }
242
243     public void removeFlows() {
244         final VrfTablesKey vrfTableKey = identifier.firstKeyOf(VrfTables.class);
245         final String rd  = vrfTableKey.getRouteDistinguisher();
246         final VpnInstanceOpDataEntry vpnInstance = FibUtil.getVpnInstanceOpData(dataBroker,
247                 vrfTableKey.getRouteDistinguisher()).get();
248         if (vpnInstance == null) {
249             logger.error("VPN Instance for rd {} is not available from VPN Op Instance Datastore", rd);
250             return;
251         }
252         VpnNexthop localNextHopInfo = vrfEntryListener.getNextHopManager().getVpnNexthop(vpnInstance.getVpnId(),
253                 vrfEntry.getDestPrefix());
254         List<BigInteger> localDpnId = checkDeleteLocalEvpnFLows(vpnInstance.getVpnId(), rd, vrfEntry, localNextHopInfo);
255         deleteRemoteEvpnFlows(rd, vpnInstance, vrfTableKey, localDpnId);
256         vrfEntryListener.cleanUpOpDataForFib(vpnInstance.getVpnId(), rd, vrfEntry);
257     }
258
259     private void deleteRemoteEvpnFlows(String rd, VpnInstanceOpDataEntry vpnInstance, VrfTablesKey vrfTableKey,
260                                        List<BigInteger> localDpnIdList) {
261         List<VpnToDpnList> vpnToDpnList = vpnInstance.getVpnToDpnList();
262         if (vpnToDpnList != null) {
263             DataStoreJobCoordinator dataStoreCoordinator = DataStoreJobCoordinator.getInstance();
264             dataStoreCoordinator.enqueueJob("FIB" + rd.toString() + vrfEntry.getDestPrefix(),
265                     new Callable<List<ListenableFuture<Void>>>() {
266                         @Override
267                         public List<ListenableFuture<Void>> call() throws Exception {
268                             WriteTransaction tx = dataBroker.newWriteOnlyTransaction();
269                             final Optional<Routes> extraRouteOptional = Optional.absent();
270                             if (localDpnIdList.size() <= 0) {
271                                 for (VpnToDpnList curDpn : vpnToDpnList) {
272                                     if (RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.BGP) {
273                                         if (curDpn.getDpnState() == VpnToDpnList.DpnState.Active) {
274                                             vrfEntryListener.deleteRemoteRoute(BigInteger.ZERO, curDpn.getDpnId(),
275                                                     vpnInstance.getVpnId(), vrfTableKey, vrfEntry,
276                                                     extraRouteOptional, tx);
277                                         }
278                                     } else {
279                                         vrfEntryListener.deleteRemoteRoute(BigInteger.ZERO, curDpn.getDpnId(),
280                                                 vpnInstance.getVpnId(), vrfTableKey, vrfEntry,
281                                                 extraRouteOptional, tx);
282                                     }
283                                 }
284                             } else {
285                                 for (BigInteger localDpnId : localDpnIdList) {
286                                     for (VpnToDpnList curDpn : vpnToDpnList) {
287                                         if (!curDpn.getDpnId().equals(localDpnId)) {
288                                             if (RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.BGP) {
289                                                 if (curDpn.getDpnState() == VpnToDpnList.DpnState.Active) {
290                                                     vrfEntryListener.deleteRemoteRoute(localDpnId, curDpn.getDpnId(),
291                                                             vpnInstance.getVpnId(), vrfTableKey, vrfEntry,
292                                                             extraRouteOptional, tx);
293                                                 }
294                                             } else {
295                                                 vrfEntryListener.deleteRemoteRoute(localDpnId, curDpn.getDpnId(),
296                                                         vpnInstance.getVpnId(), vrfTableKey, vrfEntry,
297                                                         extraRouteOptional, tx);
298                                             }
299                                         }
300                                     }
301                                 }
302                             }
303                             List<ListenableFuture<Void>> futures = new ArrayList<>();
304                             futures.add(tx.submit());
305                             return futures;
306                         }
307                     });
308         }
309     }
310
311     private List<BigInteger> checkDeleteLocalEvpnFLows(long vpnId, String rd, VrfEntry vrfEntry,
312                                                  VpnNexthop localNextHopInfo) {
313         List<BigInteger> returnLocalDpnId = new ArrayList<>();
314         if (localNextHopInfo == null) {
315             //Handle extra routes and imported routes
316         } else {
317             final BigInteger dpnId = localNextHopInfo.getDpnId();
318             DataStoreJobCoordinator dataStoreCoordinator = DataStoreJobCoordinator.getInstance();
319             dataStoreCoordinator.enqueueJob("FIB-" + rd + "-" + vrfEntry.getDestPrefix(),
320                     new Callable<List<ListenableFuture<Void>>>() {
321                         @Override
322                         public List<ListenableFuture<Void>> call() throws Exception {
323                             WriteTransaction tx = dataBroker.newWriteOnlyTransaction();
324                             vrfEntryListener.makeConnectedRoute(dpnId, vpnId, vrfEntry, rd, null /* instructions */,
325                                     NwConstants.DEL_FLOW, tx);
326                             List<ListenableFuture<Void>> futures = new ArrayList<>();
327                             futures.add(tx.submit());
328                             return futures;
329                         }
330                     });
331             //TODO: verify below adjacency call need to be optimized (?)
332             vrfEntryListener.deleteLocalAdjacency(dpnId, vpnId, vrfEntry.getDestPrefix(), vrfEntry.getDestPrefix());
333             returnLocalDpnId.add(dpnId);
334         }
335         return returnLocalDpnId;
336     }
337 }