MRI version bumpup for Aluminium
[netvirt.git] / vpnmanager / impl / src / main / java / org / opendaylight / netvirt / vpnmanager / intervpnlink / InterVpnLinkUtil.java
1 /*
2  * Copyright (c) 2016, 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.vpnmanager.intervpnlink;
9
10 import com.google.common.util.concurrent.ListenableFuture;
11 import java.util.ArrayList;
12 import java.util.Collections;
13 import java.util.HashMap;
14 import java.util.List;
15 import java.util.Map;
16 import java.util.Optional;
17 import java.util.concurrent.ExecutionException;
18 import java.util.stream.Collectors;
19 import javax.inject.Inject;
20 import javax.inject.Singleton;
21 import org.opendaylight.genius.datastoreutils.SingleTransactionDataBroker;
22 import org.opendaylight.genius.mdsalutil.MDSALUtil;
23 import org.opendaylight.genius.mdsalutil.MatchInfo;
24 import org.opendaylight.genius.mdsalutil.MetaDataUtil;
25 import org.opendaylight.genius.mdsalutil.NwConstants;
26 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
27 import org.opendaylight.genius.mdsalutil.matches.MatchMetadata;
28 import org.opendaylight.genius.utils.ServiceIndex;
29 import org.opendaylight.mdsal.binding.api.DataBroker;
30 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
31 import org.opendaylight.netvirt.bgpmanager.api.IBgpManager;
32 import org.opendaylight.netvirt.fibmanager.api.IFibManager;
33 import org.opendaylight.netvirt.fibmanager.api.RouteOrigin;
34 import org.opendaylight.netvirt.vpnmanager.VpnConstants;
35 import org.opendaylight.netvirt.vpnmanager.VpnFootprintService;
36 import org.opendaylight.netvirt.vpnmanager.VpnUtil;
37 import org.opendaylight.netvirt.vpnmanager.api.InterfaceUtils;
38 import org.opendaylight.netvirt.vpnmanager.api.intervpnlink.InterVpnLinkCache;
39 import org.opendaylight.netvirt.vpnmanager.api.intervpnlink.InterVpnLinkDataComposite;
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.flow.types.rev131026.instruction.list.InstructionKey;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentries.VrfEntry;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.inter.vpn.link.rev160311.InterVpnLinkStates;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.inter.vpn.link.rev160311.InterVpnLinks;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.inter.vpn.link.rev160311.inter.vpn.link.states.InterVpnLinkState;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.inter.vpn.link.rev160311.inter.vpn.link.states.InterVpnLinkStateBuilder;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.inter.vpn.link.rev160311.inter.vpn.link.states.InterVpnLinkStateKey;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.inter.vpn.link.rev160311.inter.vpn.link.states.inter.vpn.link.state.FirstEndpointState;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.inter.vpn.link.rev160311.inter.vpn.link.states.inter.vpn.link.state.FirstEndpointStateBuilder;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.inter.vpn.link.rev160311.inter.vpn.link.states.inter.vpn.link.state.SecondEndpointState;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.inter.vpn.link.rev160311.inter.vpn.link.states.inter.vpn.link.state.SecondEndpointStateBuilder;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.inter.vpn.link.rev160311.inter.vpn.links.InterVpnLink;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.inter.vpn.link.rev160311.inter.vpn.links.InterVpnLinkKey;
55 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
56 import org.opendaylight.yangtools.yang.common.Uint32;
57 import org.opendaylight.yangtools.yang.common.Uint64;
58 import org.slf4j.Logger;
59 import org.slf4j.LoggerFactory;
60
61 /**
62  * This class contains methods to be used as utilities related with inter-vpn-link.
63  */
64 @Singleton
65 public final class InterVpnLinkUtil {
66
67     private static final Logger LOG = LoggerFactory.getLogger(InterVpnLinkUtil.class);
68     private final DataBroker dataBroker;
69     private final IMdsalApiManager mdsalManager;
70     private final VpnUtil vpnUtil;
71     private final VpnFootprintService vpnFootprintService;
72     private final IBgpManager bgpManager;
73     private final IFibManager fibManager;
74
75     @Inject
76     public InterVpnLinkUtil(final VpnUtil vpnUtil, final VpnFootprintService vpnFootprintService,
77                             final DataBroker dataBroker, final IMdsalApiManager mdsalManager,
78                             final IBgpManager bgpManager, final IFibManager fibManager) {
79         this.dataBroker = dataBroker;
80         this.mdsalManager = mdsalManager;
81         this.vpnUtil = vpnUtil;
82         this.vpnFootprintService = vpnFootprintService;
83         this.bgpManager = bgpManager;
84         this.fibManager = fibManager;
85     }
86
87     /**
88      * Retrieves the Instance Identifier that points to an InterVpnLink object
89      * in MD-SAL.
90      *
91      * @param interVpnLinkName The name of the InterVpnLink
92      * @return The requested InstanceIdentifier
93      */
94     public static InstanceIdentifier<InterVpnLink> getInterVpnLinkPath(String interVpnLinkName) {
95         return InstanceIdentifier.builder(InterVpnLinks.class)
96             .child(InterVpnLink.class, new InterVpnLinkKey(interVpnLinkName))
97             .build();
98     }
99
100     /**
101      * Retrieves the Instance Identifier that points to an InterVpnLinkState object
102      * in MD-SAL.
103      *
104      * @param vpnLinkName The name of the InterVpnLink
105      * @return The requested InstanceIdentifier
106      */
107     public static InstanceIdentifier<InterVpnLinkState> getInterVpnLinkStateIid(String vpnLinkName) {
108         return InstanceIdentifier.builder(InterVpnLinkStates.class)
109             .child(InterVpnLinkState.class, new InterVpnLinkStateKey(vpnLinkName))
110             .build();
111     }
112
113     public static String buildInterVpnLinkIfaceName(String vpnName, Uint64 dpnId) {
114         return String.format("InterVpnLink.%s.%s", vpnName, dpnId.toString());
115     }
116
117     /**
118      * Updates VpnToDpn map by adding a fake VpnInterface related to an
119      * InterVpnLink in the corresponding DPNs. If the fake iface is the
120      * first one on the any of the specified DPNs, the installation of
121      * Fib flows on that DPN will be triggered.
122      *
123      * @param vpnName Name of the VPN to which the fake interfaces belong
124      * @param dpnList List of DPNs where the fake InterVpnLink interface must be added
125      */
126     void updateVpnFootprint(String vpnName, String primaryRd, List<Uint64> dpnList) {
127         LOG.debug("updateVpnFootprint (add):  vpn={}  dpnList={}", vpnName, dpnList);
128         // Note: when a set of DPNs is calculated for Vpn1, these DPNs are added to the VpnToDpn map of Vpn2. Why?
129         // because we do the handover from Vpn1 to Vpn2 in those DPNs, so in those DPNs we must know how to reach
130         // to Vpn2 targets. If new Vpn2 targets are added later, the Fib will be maintained in these DPNs even if
131         // Vpn2 is not physically present there.
132         for (Uint64 dpnId : dpnList) {
133             String ifaceName = buildInterVpnLinkIfaceName(vpnName, dpnId);
134             vpnFootprintService.updateVpnToDpnMapping(dpnId, vpnName, primaryRd, ifaceName,
135                     null/*ipAddressSourceValuePair*/, true /* addition */);
136         }
137     }
138
139     /**
140      * Updates VpnToDpn map by removing the fake VpnInterface related to an
141      * InterVpnLink in the corresponding DPNs.
142      *
143      * @param vpnName Name of the VPN to which the fake interfaces belong
144      * @param dpnId DPN where the fake InterVpnLink interface must be removed from
145      */
146     void removeIVpnLinkIfaceFromVpnFootprint(String vpnName, String rd, Uint64 dpnId) {
147         String ifaceName = buildInterVpnLinkIfaceName(vpnName, dpnId);
148         LOG.debug("updateVpnFootprint (remove):  vpn={}  dpn={}  ifaceName={}", vpnName, dpnId, ifaceName);
149         vpnFootprintService.updateVpnToDpnMapping(dpnId, vpnName, rd, ifaceName,
150                 null/*ipAddressSourceValuePair*/, false /* removal */);
151     }
152
153
154     public static FirstEndpointState buildFirstEndpointState(FirstEndpointState original,
155                                                              Optional<List<Uint64>> new1stEndpointDpns,
156                                                              Optional<Long> new1stEndpointLportTag) {
157         FirstEndpointStateBuilder builder = new FirstEndpointStateBuilder(original);
158         if (new1stEndpointDpns.isPresent()) {
159             builder.setDpId(new1stEndpointDpns.get());
160         }
161         if (new1stEndpointLportTag.isPresent()) {
162             builder.setLportTag(new1stEndpointLportTag.get());
163         }
164         return builder.build();
165     }
166
167     public static SecondEndpointState buildSecondEndpointState(SecondEndpointState original,
168                                                                Optional<List<Uint64>> new2ndEndpointDpns,
169                                                                Optional<Long> new2ndEndpointLportTag) {
170         SecondEndpointStateBuilder builder = new SecondEndpointStateBuilder(original);
171         if (new2ndEndpointDpns.isPresent()) {
172             builder.setDpId(new2ndEndpointDpns.get());
173         }
174         if (new2ndEndpointLportTag.isPresent()) {
175             builder.setLportTag(new2ndEndpointLportTag.get());
176         }
177         return builder.build();
178     }
179
180     /**
181      * Creates an InterVpnLinkState out of an existing one and modifying only the desired attributes.
182      *
183      * @param original InterVpnLinkState to start from.
184      * @param new1stEndpointState Sets this FirstEndpointState if present
185      * @param new2ndEndpointState  Sets this SecondEndpointState if present
186      * @param errDescription  Sets this ErrorDescription if present
187      * @return the newly build InterVpnLinkState
188      */
189     public static InterVpnLinkState buildIvlStateFromOriginal(InterVpnLinkState original,
190                                                              Optional<FirstEndpointState> new1stEndpointState,
191                                                              Optional<SecondEndpointState> new2ndEndpointState,
192                                                              Optional<String> errDescription) {
193         InterVpnLinkStateBuilder ivlStateBuilder = new InterVpnLinkStateBuilder(original);
194         if (new1stEndpointState.isPresent()) {
195             ivlStateBuilder.setFirstEndpointState(new1stEndpointState.get());
196         }
197         if (new2ndEndpointState.isPresent()) {
198             ivlStateBuilder.setSecondEndpointState(new2ndEndpointState.get());
199         }
200         if (errDescription.isPresent()) {
201             ivlStateBuilder.setErrorDescription(errDescription.get());
202         }
203         return ivlStateBuilder.build();
204     }
205
206     /**
207      * Updates inter-VPN link state.
208      *
209      * @param vpnLinkName The name of the InterVpnLink
210      * @param state Sets the state of the InterVpnLink to Active or Error
211      * @param newFirstEndpointState Updates the lportTag and/or DPNs of the 1st endpoint of the InterVpnLink
212      * @param newSecondEndpointState Updates the lportTag and/or DPNs of the 2nd endpoint of the InterVpnLink
213      * @param interVpnLinkCache the InterVpnLinkCache
214      */
215     void updateInterVpnLinkState(String vpnLinkName, InterVpnLinkState.State state,
216             FirstEndpointState newFirstEndpointState, SecondEndpointState newSecondEndpointState,
217             InterVpnLinkCache interVpnLinkCache) {
218         Optional<InterVpnLinkState> optOldVpnLinkState = getInterVpnLinkState(vpnLinkName);
219         if (optOldVpnLinkState.isPresent()) {
220             InterVpnLinkState newVpnLinkState =
221                 new InterVpnLinkStateBuilder(optOldVpnLinkState.get()).setState(state)
222                             .setFirstEndpointState(newFirstEndpointState)
223                             .setSecondEndpointState(newSecondEndpointState)
224                             .build();
225             vpnUtil.syncUpdate(LogicalDatastoreType.CONFIGURATION,
226                 InterVpnLinkUtil.getInterVpnLinkStateIid(vpnLinkName), newVpnLinkState);
227             interVpnLinkCache.addInterVpnLinkStateToCaches(newVpnLinkState);
228         } else {
229             InterVpnLinkState newIVpnLinkState =
230                 new InterVpnLinkStateBuilder().withKey(new InterVpnLinkStateKey(vpnLinkName))
231                     .setInterVpnLinkName(vpnLinkName)
232                     .setFirstEndpointState(newFirstEndpointState)
233                     .setSecondEndpointState(newSecondEndpointState)
234                     .setState(InterVpnLinkState.State.Active)
235                     .build();
236             vpnUtil.syncWrite(LogicalDatastoreType.CONFIGURATION,
237                 InterVpnLinkUtil.getInterVpnLinkStateIid(vpnLinkName), newIVpnLinkState);
238             interVpnLinkCache.addInterVpnLinkStateToCaches(newIVpnLinkState);
239         }
240     }
241
242     /**
243      * Installs a Flow in LPortDispatcher table that matches on SI=2 and
244      * the lportTag of one InterVpnLink's endpoint and sets the vrfTag of the
245      * other endpoint and sends to FIB table.
246      *
247      * @param interVpnLinkName Name of the InterVpnLink.
248      * @param dpnList The list of DPNs where this flow must be installed
249      * @param vpnUuidOtherEndpoint UUID of the other endpoint of the InterVpnLink
250      * @param lportTagOfOtherEndpoint Dataplane identifier of the other endpoint of the InterVpnLink
251      * @return the list of Futures for each and every flow that has been installed
252      */
253     List<ListenableFuture<Void>> installLPortDispatcherTableFlow(String interVpnLinkName, List<Uint64> dpnList,
254                                                                  String vpnUuidOtherEndpoint,
255                                                                  Long lportTagOfOtherEndpoint) {
256         List<ListenableFuture<Void>> result = new ArrayList<>();
257         Uint32 vpnId = vpnUtil.getVpnId(vpnUuidOtherEndpoint);
258         for (Uint64 dpnId : dpnList) {
259             // insert into LPortDispatcher table
260             Flow lportDispatcherFlow = buildLPortDispatcherFlow(interVpnLinkName, vpnId,
261                                                                 lportTagOfOtherEndpoint.intValue());
262             result.add(mdsalManager.installFlow(dpnId, lportDispatcherFlow));
263         }
264
265         return result;
266     }
267
268     /**
269      * Builds a Flow to be installed into LPortDispatcher table, that matches on
270      * SI=2 + vpnLinkEndpointPseudoPortTag and sends to FIB.
271      *
272      * @param interVpnLinkName The name of the InterVpnLink
273      * @param vpnId Dataplane identifier of the VPN, the Vrf Tag.
274      * @param lportTag DataPlane identifier of the LogicalPort.
275      * @return the Flow ready to be installed
276      */
277     public static Flow buildLPortDispatcherFlow(String interVpnLinkName, Uint32 vpnId, int lportTag) {
278         LOG.info("Inter-vpn-link : buildLPortDispatcherFlow. vpnId {}   lportTag {} ", vpnId, lportTag);
279         List<MatchInfo> matches = Collections.singletonList(new MatchMetadata(
280                         MetaDataUtil.getMetaDataForLPortDispatcher(lportTag,
281                                 ServiceIndex.getIndex(NwConstants.L3VPN_SERVICE_NAME, NwConstants.L3VPN_SERVICE_INDEX)),
282                         MetaDataUtil.getMetaDataMaskForLPortDispatcher()));
283         String flowRef = getLportDispatcherFlowRef(interVpnLinkName, lportTag);
284
285         return MDSALUtil.buildFlowNew(NwConstants.LPORT_DISPATCHER_TABLE, flowRef,
286                                       VpnConstants.DEFAULT_LPORT_DISPATCHER_FLOW_PRIORITY, flowRef,
287                                       0, 0, VpnUtil.getCookieL3(vpnId.intValue()), matches,
288                                       buildLportDispatcherTableInstructions(vpnId));
289     }
290
291     /**
292      * Builds a flowRef to be assigned to the flow to be installed into
293      * LPortDispatcher table.
294      *
295      * @param interVpnLinkName The name of the InterVpnLink
296      * @param lportTag Dataplane identifier of the LogicalPort
297      * @return the flow reference string
298      */
299     public static String getLportDispatcherFlowRef(String interVpnLinkName, Integer lportTag) {
300         return VpnConstants.FLOWID_PREFIX + "INTERVPNLINK" + NwConstants.FLOWID_SEPARATOR + interVpnLinkName
301              + NwConstants.FLOWID_SEPARATOR + lportTag
302              + NwConstants.FLOWID_SEPARATOR + ServiceIndex.getIndex(NwConstants.L3VPN_SERVICE_NAME,
303                                                                     NwConstants.L3VPN_SERVICE_INDEX)
304              + NwConstants.FLOWID_SEPARATOR + VpnConstants.DEFAULT_LPORT_DISPATCHER_FLOW_PRIORITY;
305     }
306
307
308     public static  Map<InstructionKey, Instruction> buildLportDispatcherTableInstructions(Uint32 vpnId) {
309         int instructionKey = 0;
310         Map<InstructionKey, Instruction> instructions = new HashMap<InstructionKey, Instruction>();
311         instructions.put(new InstructionKey(++instructionKey), MDSALUtil.buildAndGetWriteMetadaInstruction(MetaDataUtil
312                         .getVpnIdMetadata(vpnId.intValue()), MetaDataUtil.METADATA_MASK_VRFID, instructionKey));
313         instructions.put(new InstructionKey(++instructionKey),
314                 MDSALUtil.buildAndGetGotoTableInstruction(NwConstants.L3_FIB_TABLE, instructionKey));
315
316         return instructions;
317     }
318
319     /**
320      * Retrieves the State of an InterVpnLink.
321      *
322      * @param interVpnLinkName The name of the InterVpnLink
323      * @return the object that contains the State of the specified InterVpnLink or Optional.empty() if it doesnt exist
324      */
325     public Optional<InterVpnLinkState> getInterVpnLinkState(String interVpnLinkName) {
326         Optional<InterVpnLinkState> interVpnLinkStateOptional = Optional.empty();
327         try {
328             interVpnLinkStateOptional = SingleTransactionDataBroker.syncReadOptional(dataBroker,
329                     LogicalDatastoreType.CONFIGURATION, getInterVpnLinkStateIid(interVpnLinkName));
330         } catch (InterruptedException | ExecutionException e) {
331             LOG.error("getInterVpnLinkState: Failed to read intervpn link state for {}", interVpnLinkName);
332         }
333         return interVpnLinkStateOptional;
334     }
335
336     public void handleStaticRoute(InterVpnLinkDataComposite interVpnLink, String vpnName,
337         String destination, String nexthop, Uint32 label) throws Exception {
338
339         LOG.debug("handleStaticRoute [vpnLink={} srcVpn={} destination={} nextHop={} label={}]",
340             interVpnLink.getInterVpnLinkName(), vpnName, destination, nexthop, label);
341
342         String vpnRd = vpnUtil.getVpnRd(vpnName);
343         if (vpnRd == null) {
344             LOG.warn("Could not find Route-Distinguisher for VpnName {}", vpnName);
345             return;
346         }
347         LOG.debug("Writing FibEntry to DS:  vpnRd={}, prefix={}, label={}, nexthop={} (interVpnLink)",
348             vpnRd, destination, label, nexthop);
349         fibManager.addOrUpdateFibEntry(vpnRd, null /*macAddress*/, destination,
350                 Collections.singletonList(nexthop), VrfEntry.EncapType.Mplsgre, label,
351                 Uint32.ZERO/*l3vni*/, null /*gatewayMacAddress*/, null /*parentVpnRd*/,
352                 RouteOrigin.STATIC, null /*writeTxn*/);
353
354         // Now advertise to BGP. The nexthop that must be advertised to BGP are the IPs of the DPN where the
355         // VPN's endpoint have been instantiated
356         // List<String> nexthopList = new ArrayList<>(); // The nexthops to be advertised to BGP
357         List<Uint64> endpointDpns = interVpnLink.getEndpointDpnsByVpnName(vpnName);
358         List<String> nexthopList =
359             endpointDpns.stream().map(dpnId -> InterfaceUtils.getEndpointIpAddressForDPN(dataBroker, dpnId))
360                         .collect(Collectors.toList());
361         LOG.debug("advertising IVpnLink route to BGP:  vpnRd={}, prefix={}, label={}, nexthops={}",
362             vpnRd, destination, label, nexthopList);
363         bgpManager.advertisePrefix(vpnRd, null /*macAddress*/, destination, nexthopList,
364                 VrfEntry.EncapType.Mplsgre, label, Uint32.ZERO/*l3vni*/, Uint32.ZERO /*l2vni*/,
365                 null /*gatewayMacAddress*/);
366     }
367 }