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