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