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