Remove redundant names in paths
[netvirt.git] / vpnmanager / impl / src / main / java / org / opendaylight / netvirt / vpnmanager / intervpnlink / InterVpnLinkLocator.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.vpnmanager.intervpnlink;
9
10 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
11 import java.math.BigInteger;
12 import java.util.ArrayList;
13 import java.util.HashSet;
14 import java.util.List;
15 import java.util.Set;
16 import java.util.function.Predicate;
17 import java.util.stream.Collectors;
18 import javax.inject.Inject;
19 import javax.inject.Singleton;
20 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
21 import org.opendaylight.genius.mdsalutil.NWUtil;
22 import org.opendaylight.netvirt.vpnmanager.VpnUtil;
23 import org.opendaylight.netvirt.vpnmanager.api.intervpnlink.InterVpnLinkCache;
24 import org.opendaylight.netvirt.vpnmanager.api.intervpnlink.InterVpnLinkDataComposite;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntry;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.VpnTargets;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.vpntargets.VpnTarget;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.inter.vpn.link.rev160311.inter.vpn.links.InterVpnLink;
29 import org.slf4j.Logger;
30 import org.slf4j.LoggerFactory;
31
32 /**
33  * This class is responsible for searching the best possible DPN(s) to place
34  * an InterVpnLink.
35  *
36  */
37 @Singleton
38 public class InterVpnLinkLocator {
39
40     private static final Logger LOG = LoggerFactory.getLogger(InterVpnLinkLocator.class);
41     protected static final String NBR_OF_DPNS_PROPERTY_NAME = "vpnservice.intervpnlink.number.dpns";
42
43     private final DataBroker dataBroker;
44     private final InterVpnLinkCache interVpnLinkCache;
45
46     @Inject
47     public InterVpnLinkLocator(final DataBroker dataBroker, final InterVpnLinkCache interVpnLinkCache) {
48         this.dataBroker = dataBroker;
49         this.interVpnLinkCache = interVpnLinkCache;
50     }
51
52     /**
53      * Retrieves a list of randomly selected DPNs avoiding to select DPNs
54      * where there is already an InterVpnLink of the same group (i.e., an
55      * InterVpnLink that links similar L3VPNs).
56      *
57      * @param interVpnLink InterVpnLink to find suitable DPNs for.
58      * @return the list of the selected DPN Ids
59      */
60     public List<BigInteger> selectSuitableDpns(InterVpnLink interVpnLink) {
61         int numberOfDpns = Integer.getInteger(NBR_OF_DPNS_PROPERTY_NAME, 1);
62         List<BigInteger> dpnIdPool = NWUtil.getOperativeDPNs(dataBroker);
63         LOG.trace("selectSuitableDpns for {} with numberOfDpns={} and availableDpns={}",
64                   interVpnLink.getName(), numberOfDpns, dpnIdPool);
65         int poolSize = dpnIdPool.size();
66         if (poolSize <= numberOfDpns) {
67             // You requested more than there is, I give you all I have.
68             return dpnIdPool;
69         }
70
71         List<InterVpnLinkDataComposite> allInterVpnLinks = interVpnLinkCache.getAllInterVpnLinks();
72
73         // 1st criteria is to select those DPNs where there is no InterVpnLink at all
74         List<BigInteger> dpnsWithNoIVL = findDPNsWithNoInterVpnLink(dpnIdPool, allInterVpnLinks);
75         if (dpnsWithNoIVL.size() >= numberOfDpns) {
76             return dpnsWithNoIVL.subList(0, numberOfDpns); // Best case scenario
77         }
78
79         // Not enough. 2nd criteria is to avoid DPNs where there are InterVpnLinks of the same group
80         List<BigInteger> result = new ArrayList<>(dpnsWithNoIVL);
81         dpnIdPool.removeAll(result);
82         int pendingDPNs = numberOfDpns - result.size();
83
84         List<BigInteger> dpnsToAvoid = findDpnsWithSimilarIVpnLinks(interVpnLink, allInterVpnLinks);
85         result.addAll(dpnIdPool.stream().filter(dpId -> dpnsToAvoid == null || !dpnsToAvoid.contains(dpId))
86                                .limit(pendingDPNs).collect(Collectors.toList()));
87
88         int currentNbrOfItems = result.size();
89         if (currentNbrOfItems < numberOfDpns) {
90             // Still not enough. 3rd criteria: whatever is available
91             dpnIdPool.removeAll(result);
92             pendingDPNs = numberOfDpns - currentNbrOfItems;
93             result.addAll(dpnIdPool.subList(0, Math.max(dpnIdPool.size(), pendingDPNs)));
94         }
95
96         return result;
97     }
98
99     /*
100      * Given a list of Dpn Ids and a list of InterVpnLinks, this method finds
101      * the DPNs in the first list where no InterVpnLink is instantiated there.
102      *
103      * @param dpnList A list of DPN IDs
104      * @param interVpnLinks A List of InterVpnLinks to avoid
105      *
106      * @return the list of available DPNs among the specified ones
107      */
108     private List<BigInteger> findDPNsWithNoInterVpnLink(List<BigInteger> dpnList,
109                                                         List<InterVpnLinkDataComposite> interVpnLinks) {
110         List<BigInteger> occupiedDpns = new ArrayList<>();
111         for (InterVpnLinkDataComposite ivl : interVpnLinks) {
112             if (ivl.isActive()) {
113                 occupiedDpns.addAll(ivl.getFirstEndpointDpns());
114                 occupiedDpns.addAll(ivl.getSecondEndpointDpns());
115             }
116         }
117
118         List<BigInteger> result = new ArrayList<>(dpnList);
119         result.removeAll(occupiedDpns);
120         return result;
121     }
122
123     /*
124      * Given an InterVpnLink, this method finds those DPNs where there is an
125      * InterVpnLink of the same group. Two InterVpnLinks are in the same group
126      * if they link 2 L3VPNs that are from the same group, and 2 L3VPNs are in
127      * the same group if their iRTs match.
128      *
129      * @param interVpnLink InterVpnLink to be checked
130      * @return the list of dpnIds where the specified InterVpnLink should not
131      *     be installed
132      */
133     private List<BigInteger> findDpnsWithSimilarIVpnLinks(InterVpnLink interVpnLink,
134                                                           List<InterVpnLinkDataComposite> allInterVpnLinks) {
135         List<InterVpnLinkDataComposite> sameGroupInterVpnLinks = findInterVpnLinksSameGroup(interVpnLink,
136                                                                                             allInterVpnLinks);
137         Set<BigInteger> resultDpns = new HashSet<>();
138         for (InterVpnLinkDataComposite ivl : sameGroupInterVpnLinks) {
139             resultDpns.addAll(ivl.getFirstEndpointDpns());
140             resultDpns.addAll(ivl.getSecondEndpointDpns());
141         }
142         return new ArrayList<>(resultDpns);
143     }
144
145     private List<String> getRts(VpnInstanceOpDataEntry vpnInstance, VpnTarget.VrfRTType rtType) {
146         String name = vpnInstance.getVpnInstanceName();
147         VpnTargets targets = vpnInstance.getVpnTargets();
148         if (targets == null) {
149             LOG.trace("vpn targets not available for {}", name);
150             return new ArrayList<>();
151         }
152         List<VpnTarget> vpnTargets = targets.getVpnTarget();
153         if (vpnTargets == null) {
154             LOG.trace("vpnTarget values not available for {}", name);
155             return new ArrayList<>();
156         }
157         return vpnTargets.stream()
158             .filter(target -> target.getVrfRTType().equals(rtType)
159                 || target.getVrfRTType().equals(VpnTarget.VrfRTType.Both))
160             .map(VpnTarget::getVrfRTValue)
161             .collect(Collectors.toList());
162     }
163
164     private List<String> getIRTsByVpnName(String vpnName) {
165         String vpn1Rd = VpnUtil.getVpnRd(dataBroker, vpnName);
166         final VpnInstanceOpDataEntry vpnInstance = VpnUtil.getVpnInstanceOpData(dataBroker, vpn1Rd);
167         return getRts(vpnInstance, VpnTarget.VrfRTType.ImportExtcommunity);
168     }
169
170     @SuppressFBWarnings("NP_NULL_ON_SOME_PATH_MIGHT_BE_INFEASIBLE")
171     private boolean haveSameIRTs(List<String> irts1, List<String> irts2) {
172         if (irts1 == null && irts2 == null) {
173             return true;
174         }
175         if (irts1 == null && irts2 != null || irts1 != null && irts2 == null) {
176             return false;
177         }
178
179         // FindBugs reports "Possible null pointer dereference of irts1 on branch that might be infeasible" but irts1
180         // can't be null here.
181         if (irts1.size() != irts2.size()) {
182             return false;
183         }
184         irts1.sort(/*comparator*/ null);
185         irts2.sort(/*comparator*/ null);
186         return irts1.equals(irts2);
187     }
188
189     public List<InterVpnLinkDataComposite> findInterVpnLinksSameGroup(InterVpnLink ivpnLinkToMatch,
190                                                                       List<InterVpnLinkDataComposite> interVpnLinks) {
191
192         List<String> vpnToMatch1IRTs = getIRTsByVpnName(ivpnLinkToMatch.getFirstEndpoint().getVpnUuid().getValue());
193         List<String> vpnToMatch2IRTs = getIRTsByVpnName(ivpnLinkToMatch.getSecondEndpoint().getVpnUuid().getValue());
194
195         Predicate<InterVpnLinkDataComposite> areSameGroup = (ivl) -> {
196             if (ivl.getInterVpnLinkName().equals(ivpnLinkToMatch.getName())) {
197                 return false; // ivl and ivpnLinlToMatch are the same InterVpnLink
198             }
199             String vpn1Name = ivl.getFirstEndpointVpnUuid().orNull();
200             String vpn2Name = ivl.getSecondEndpointVpnUuid().orNull();
201             if (vpn1Name == null) {
202                 return false;
203             }
204             List<String> vpn1IRTs = getIRTsByVpnName(vpn1Name);
205             List<String> vpn2IRTs = getIRTsByVpnName(vpn2Name);
206             return haveSameIRTs(vpn1IRTs, vpnToMatch1IRTs) && haveSameIRTs(vpn2IRTs, vpnToMatch2IRTs)
207                 || haveSameIRTs(vpn1IRTs, vpnToMatch2IRTs) && haveSameIRTs(vpn2IRTs, vpnToMatch1IRTs);
208         };
209
210         return interVpnLinks.stream().filter(areSameGroup).collect(Collectors.toList());
211     }
212 }