2 * Copyright (c) 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 edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
11 import java.util.ArrayList;
12 import java.util.HashSet;
13 import java.util.List;
14 import java.util.Objects;
16 import java.util.concurrent.ExecutionException;
17 import java.util.function.Predicate;
18 import java.util.stream.Collectors;
19 import javax.inject.Inject;
20 import javax.inject.Singleton;
21 import org.eclipse.jdt.annotation.NonNull;
22 import org.opendaylight.genius.mdsalutil.NWUtil;
23 import org.opendaylight.mdsal.binding.api.DataBroker;
24 import org.opendaylight.netvirt.vpnmanager.VpnUtil;
25 import org.opendaylight.netvirt.vpnmanager.api.intervpnlink.InterVpnLinkCache;
26 import org.opendaylight.netvirt.vpnmanager.api.intervpnlink.InterVpnLinkDataComposite;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntry;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.VpnTargets;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.vpntargets.VpnTarget;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.inter.vpn.link.rev160311.inter.vpn.links.InterVpnLink;
31 import org.opendaylight.yangtools.yang.common.Uint64;
32 import org.slf4j.Logger;
33 import org.slf4j.LoggerFactory;
36 * This class is responsible for searching the best possible DPN(s) to place
41 public class InterVpnLinkLocator {
43 private static final Logger LOG = LoggerFactory.getLogger(InterVpnLinkLocator.class);
44 protected static final String NBR_OF_DPNS_PROPERTY_NAME = "vpnservice.intervpnlink.number.dpns";
46 private final DataBroker dataBroker;
47 private final InterVpnLinkCache interVpnLinkCache;
48 private final VpnUtil vpnUtil;
51 public InterVpnLinkLocator(final DataBroker dataBroker, final InterVpnLinkCache interVpnLinkCache,
52 final VpnUtil vpnUtil) {
53 this.dataBroker = dataBroker;
54 this.interVpnLinkCache = interVpnLinkCache;
55 this.vpnUtil = vpnUtil;
59 * Retrieves a list of randomly selected DPNs avoiding to select DPNs
60 * where there is already an InterVpnLink of the same group (i.e., an
61 * InterVpnLink that links similar L3VPNs).
63 * @param interVpnLink InterVpnLink to find suitable DPNs for.
64 * @return the list of the selected DPN Ids
66 public List<Uint64> selectSuitableDpns(InterVpnLink interVpnLink) {
67 int numberOfDpns = Integer.getInteger(NBR_OF_DPNS_PROPERTY_NAME, 1);
68 List<Uint64> dpnIdPool = new ArrayList<>();
70 dpnIdPool = NWUtil.getOperativeDPNs(dataBroker).stream().map(dpn -> dpn).collect(Collectors.toList());
71 } catch (ExecutionException | InterruptedException e) {
72 LOG.error("selectSuitableDpns: Exception while reading Operative DPNs", e);
74 LOG.trace("selectSuitableDpns for {} with numberOfDpns={} and availableDpns={}",
75 interVpnLink.getName(), numberOfDpns, dpnIdPool);
76 int poolSize = dpnIdPool.size();
77 if (poolSize <= numberOfDpns) {
78 // You requested more than there is, I give you all I have.
82 List<InterVpnLinkDataComposite> allInterVpnLinks = interVpnLinkCache.getAllInterVpnLinks();
84 // 1st criteria is to select those DPNs where there is no InterVpnLink at all
85 List<Uint64> dpnsWithNoIVL = findDPNsWithNoInterVpnLink(dpnIdPool, allInterVpnLinks);
86 if (dpnsWithNoIVL.size() >= numberOfDpns) {
87 return dpnsWithNoIVL.subList(0, numberOfDpns); // Best case scenario
90 // Not enough. 2nd criteria is to avoid DPNs where there are InterVpnLinks of the same group
91 List<Uint64> result = new ArrayList<>(dpnsWithNoIVL);
92 dpnIdPool.removeAll(result);
93 int pendingDPNs = numberOfDpns - result.size();
95 List<Uint64> dpnsToAvoid = findDpnsWithSimilarIVpnLinks(interVpnLink, allInterVpnLinks);
96 result.addAll(dpnIdPool.stream().filter(dpId -> !dpnsToAvoid.contains(dpId))
97 .limit(pendingDPNs).collect(Collectors.toList()));
99 int currentNbrOfItems = result.size();
100 if (currentNbrOfItems < numberOfDpns) {
101 // Still not enough. 3rd criteria: whatever is available
102 dpnIdPool.removeAll(result);
103 pendingDPNs = numberOfDpns - currentNbrOfItems;
104 result.addAll(dpnIdPool.subList(0, Math.max(dpnIdPool.size(), pendingDPNs)));
111 * Given a list of Dpn Ids and a list of InterVpnLinks, this method finds
112 * the DPNs in the first list where no InterVpnLink is instantiated there.
114 * @param dpnList A list of DPN IDs
115 * @param interVpnLinks A List of InterVpnLinks to avoid
117 * @return the list of available DPNs among the specified ones
119 private List<Uint64> findDPNsWithNoInterVpnLink(List<Uint64> dpnList,
120 List<InterVpnLinkDataComposite> interVpnLinks) {
121 List<Uint64> occupiedDpns = new ArrayList<>();
122 for (InterVpnLinkDataComposite ivl : interVpnLinks) {
123 if (ivl.isActive()) {
124 occupiedDpns.addAll(ivl.getFirstEndpointDpns());
125 occupiedDpns.addAll(ivl.getSecondEndpointDpns());
129 List<Uint64> result = new ArrayList<>(dpnList);
130 result.removeAll(occupiedDpns);
135 * Given an InterVpnLink, this method finds those DPNs where there is an
136 * InterVpnLink of the same group. Two InterVpnLinks are in the same group
137 * if they link 2 L3VPNs that are from the same group, and 2 L3VPNs are in
138 * the same group if their iRTs match.
140 * @param interVpnLink InterVpnLink to be checked
141 * @return the list of dpnIds where the specified InterVpnLink should not
145 private List<Uint64> findDpnsWithSimilarIVpnLinks(InterVpnLink interVpnLink,
146 List<InterVpnLinkDataComposite> allInterVpnLinks) {
147 List<InterVpnLinkDataComposite> sameGroupInterVpnLinks = findInterVpnLinksSameGroup(interVpnLink,
149 Set<Uint64> resultDpns = new HashSet<>();
150 for (InterVpnLinkDataComposite ivl : sameGroupInterVpnLinks) {
151 resultDpns.addAll(ivl.getFirstEndpointDpns());
152 resultDpns.addAll(ivl.getSecondEndpointDpns());
154 return new ArrayList<>(resultDpns);
157 private List<String> getRts(VpnInstanceOpDataEntry vpnInstance, VpnTarget.VrfRTType rtType) {
158 String name = vpnInstance.getVpnInstanceName();
159 VpnTargets targets = vpnInstance.getVpnTargets();
160 if (targets == null) {
161 LOG.trace("vpn targets not available for {}", name);
162 return new ArrayList<>();
164 List<VpnTarget> vpnTargets = targets.getVpnTarget();
165 if (vpnTargets == null) {
166 LOG.trace("vpnTarget values not available for {}", name);
167 return new ArrayList<>();
169 return vpnTargets.stream()
170 .filter(target -> Objects.equals(target.getVrfRTType(), rtType)
171 || Objects.equals(target.getVrfRTType(), VpnTarget.VrfRTType.Both))
172 .map(VpnTarget::getVrfRTValue)
173 .collect(Collectors.toList());
176 private List<String> getIRTsByVpnName(String vpnName) {
177 String vpn1Rd = vpnUtil.getVpnRd(vpnName);
178 final VpnInstanceOpDataEntry vpnInstance = vpnUtil.getVpnInstanceOpData(vpn1Rd);
179 return getRts(vpnInstance, VpnTarget.VrfRTType.ImportExtcommunity);
182 @SuppressFBWarnings("NP_NULL_ON_SOME_PATH_MIGHT_BE_INFEASIBLE")
183 private boolean haveSameIRTs(List<String> irts1, List<String> irts2) {
184 if (irts1 == null && irts2 == null) {
187 if (irts1 == null || irts2 == null) {
191 // FindBugs reports "Possible null pointer dereference of irts1 on branch that might be infeasible" but irts1
192 // can't be null here.
193 if (irts1.size() != irts2.size()) {
196 irts1.sort(/*comparator*/ null);
197 irts2.sort(/*comparator*/ null);
198 return irts1.equals(irts2);
201 public List<InterVpnLinkDataComposite> findInterVpnLinksSameGroup(InterVpnLink ivpnLinkToMatch,
202 List<InterVpnLinkDataComposite> interVpnLinks) {
204 List<String> vpnToMatch1IRTs = getIRTsByVpnName(ivpnLinkToMatch.getFirstEndpoint().getVpnUuid().getValue());
205 List<String> vpnToMatch2IRTs = getIRTsByVpnName(ivpnLinkToMatch.getSecondEndpoint().getVpnUuid().getValue());
207 Predicate<InterVpnLinkDataComposite> areSameGroup = (ivl) -> {
208 if (ivl.getInterVpnLinkName().equals(ivpnLinkToMatch.getName())) {
209 return false; // ivl and ivpnLinlToMatch are the same InterVpnLink
211 String vpn1Name = ivl.getFirstEndpointVpnUuid().orElse(null);
212 String vpn2Name = ivl.getSecondEndpointVpnUuid().orElse(null);
213 if (vpn1Name == null) {
216 List<String> vpn1IRTs = getIRTsByVpnName(vpn1Name);
217 List<String> vpn2IRTs = getIRTsByVpnName(vpn2Name);
218 return haveSameIRTs(vpn1IRTs, vpnToMatch1IRTs) && haveSameIRTs(vpn2IRTs, vpnToMatch2IRTs)
219 || haveSameIRTs(vpn1IRTs, vpnToMatch2IRTs) && haveSameIRTs(vpn2IRTs, vpnToMatch1IRTs);
222 return interVpnLinks.stream().filter(areSameGroup).collect(Collectors.toList());