Bump upstream versions
[bgpcep.git] / bgp / path-selection-mode / src / main / java / org / opendaylight / protocol / bgp / mode / impl / BestPathStateImpl.java
1 /*
2  * Copyright (c) 2015 Cisco Systems, Inc. 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.protocol.bgp.mode.impl;
9
10 import static com.google.common.base.Verify.verifyNotNull;
11 import static java.util.Objects.requireNonNull;
12
13 import com.google.common.base.MoreObjects;
14 import com.google.common.base.MoreObjects.ToStringHelper;
15 import com.google.common.cache.CacheBuilder;
16 import com.google.common.cache.CacheLoader;
17 import com.google.common.cache.LoadingCache;
18 import java.util.ArrayList;
19 import java.util.Collection;
20 import java.util.LinkedHashSet;
21 import java.util.List;
22 import java.util.Objects;
23 import java.util.Optional;
24 import org.eclipse.jdt.annotation.NonNull;
25 import org.opendaylight.protocol.bgp.mode.BesthPathStateUtil;
26 import org.opendaylight.protocol.bgp.mode.api.BestPathState;
27 import org.opendaylight.protocol.bgp.parser.impl.message.update.CommunityUtil;
28 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.AsNumber;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev200120.path.attributes.attributes.AsPath;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev200120.path.attributes.attributes.Communities;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev200120.path.attributes.attributes.LocalPref;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev200120.path.attributes.attributes.MultiExitDisc;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev200120.path.attributes.attributes.Origin;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev200120.path.attributes.attributes.as.path.Segments;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev200120.path.attributes.attributes.as.path.SegmentsBuilder;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev200120.BgpOrigin;
37 import org.opendaylight.yangtools.yang.common.QName;
38 import org.opendaylight.yangtools.yang.common.QNameModule;
39 import org.opendaylight.yangtools.yang.common.Uint16;
40 import org.opendaylight.yangtools.yang.common.Uint32;
41 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
42 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
43 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
44 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
45 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
46 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
47 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes;
48 import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
49 import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
50
51 public final class BestPathStateImpl implements BestPathState {
52     private static final class NamespaceSpecificIds {
53         final List<PathArgument> asPath;
54         final List<PathArgument> locPref;
55         final List<PathArgument> med;
56         final List<PathArgument> orig;
57         final NodeIdentifier asSetNid;
58         final NodeIdentifier asSeqNid;
59         final NodeIdentifier communities;
60         final NodeIdentifier asNumber;
61         final NodeIdentifier semantics;
62
63         NamespaceSpecificIds(final @NonNull QNameModule namespace) {
64             asPath = List.of(NodeIdentifier.create(AsPath.QNAME.bindTo(namespace).intern()),
65                 NodeIdentifier.create(QName.create(namespace, "segments").intern()));
66             locPref = List.of(NodeIdentifier.create(LocalPref.QNAME.bindTo(namespace).intern()),
67                 NodeIdentifier.create(QName.create(namespace, "pref").intern()));
68             med = List.of(NodeIdentifier.create(MultiExitDisc.QNAME.bindTo(namespace).intern()),
69                 NodeIdentifier.create(QName.create(namespace, "med").intern()));
70             orig = List.of(NodeIdentifier.create(Origin.QNAME.bindTo(namespace).intern()),
71                 NodeIdentifier.create(QName.create(namespace, "value").intern()));
72             asSetNid = NodeIdentifier.create(QName.create(namespace, "as-set").intern());
73             asSeqNid = NodeIdentifier.create(QName.create(namespace, "as-sequence").intern());
74             communities = NodeIdentifier.create(Communities.QNAME.bindTo(namespace).intern());
75             asNumber = NodeIdentifier.create(QName.create(namespace, "as-number").intern());
76             semantics = NodeIdentifier.create(QName.create(namespace, "semantics").intern());
77         }
78     }
79
80     private static final LoadingCache<QNameModule, NamespaceSpecificIds> PATH_CACHE =
81         CacheBuilder.newBuilder().weakKeys().weakValues().build(new CacheLoader<>() {
82             @Override
83             public NamespaceSpecificIds load(final QNameModule key) {
84                 return new NamespaceSpecificIds(key);
85             }
86         });
87
88     private static final Uint32 LLGR_STALE_AS_NUMBER = CommunityUtil.LLGR_STALE.getAsNumber().getValue().intern();
89     private static final Uint16 LLGR_STALE_SEMANTICS = CommunityUtil.LLGR_STALE.getSemantics().intern();
90
91     private final ContainerNode attributes;
92
93     private long peerAs = 0L;
94     private int asPathLength = 0;
95     private Uint32 localPref;
96     private long multiExitDisc;
97     private BgpOrigin origin;
98     private boolean depreferenced;
99     private boolean resolved;
100
101     public BestPathStateImpl(final ContainerNode attributes) {
102         this.attributes = requireNonNull(attributes);
103         resolveValues();
104     }
105
106     private void resolveValues() {
107         if (resolved) {
108             return;
109         }
110
111         final NamespaceSpecificIds ids = PATH_CACHE.getUnchecked(attributes.getIdentifier().getNodeType().getModule());
112         localPref = (Uint32) NormalizedNodes.findNode(attributes, ids.locPref)
113             .map(NormalizedNode::body)
114             .orElse(null);
115
116         final Optional<NormalizedNode> maybeMultiExitDisc = NormalizedNodes.findNode(attributes, ids.med);
117         if (maybeMultiExitDisc.isPresent()) {
118             multiExitDisc = ((Uint32) maybeMultiExitDisc.get().body()).toJava();
119         } else {
120             multiExitDisc = 0L;
121         }
122
123         final Optional<NormalizedNode> maybeOrigin = NormalizedNodes.findNode(attributes, ids.orig);
124         if (maybeOrigin.isPresent()) {
125             final String originStr = (String) maybeOrigin.get().body();
126             origin = BgpOrigin.forName(originStr)
127                 .orElseThrow(() -> new IllegalArgumentException("Unhandled origin value " + originStr));
128         } else {
129             origin = null;
130         }
131
132         final Optional<NormalizedNode> maybeSegments = NormalizedNodes.findNode(attributes, ids.asPath);
133         if (maybeSegments.isPresent()) {
134             final UnkeyedListNode segments = (UnkeyedListNode) maybeSegments.get();
135             final List<Segments> segs = extractSegments(segments, ids);
136             if (!segs.isEmpty()) {
137                 peerAs = BesthPathStateUtil.getPeerAs(segs);
138                 asPathLength = countAsPath(segs);
139             }
140         }
141
142         final Optional<NormalizedNode> maybeCommunities = NormalizedNodes.findNode(attributes, ids.communities);
143         if (maybeCommunities.isPresent()) {
144             depreferenced = ((UnkeyedListNode) maybeCommunities.orElseThrow()).body().stream()
145                 .anyMatch(community -> isStale(ids, community));
146         } else {
147             depreferenced = false;
148         }
149
150         resolved = true;
151     }
152
153     private static boolean isStale(final NamespaceSpecificIds ids, final UnkeyedListEntryNode community) {
154         return LLGR_STALE_AS_NUMBER.equals(verifyNotNull(community.childByArg(ids.asNumber)).body())
155             && LLGR_STALE_SEMANTICS.equals(verifyNotNull(community.childByArg(ids.semantics)).body());
156     }
157
158     @Override
159     public Uint32 getLocalPref() {
160         resolveValues();
161         return localPref;
162     }
163
164     @Override
165     public long getMultiExitDisc() {
166         resolveValues();
167         return multiExitDisc;
168     }
169
170     @Override
171     public BgpOrigin getOrigin() {
172         resolveValues();
173         return origin;
174     }
175
176     @Override
177     public long getPeerAs() {
178         resolveValues();
179         return peerAs;
180     }
181
182     @Override
183     public int getAsPathLength() {
184         resolveValues();
185         return asPathLength;
186     }
187
188     @Override
189     public ContainerNode getAttributes() {
190         return attributes;
191     }
192
193     @Override
194     public boolean isDepreferenced() {
195         resolveValues();
196         return depreferenced;
197     }
198
199     private ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
200         toStringHelper.add("attributes", attributes);
201         toStringHelper.add("localPref", localPref);
202         toStringHelper.add("multiExitDisc", multiExitDisc);
203         toStringHelper.add("origin", origin);
204         toStringHelper.add("resolved", resolved);
205         toStringHelper.add("depreferenced", depreferenced);
206         return toStringHelper;
207     }
208
209     @Override
210     public String toString() {
211         return addToStringAttributes(MoreObjects.toStringHelper(this)).toString();
212     }
213
214     @Override
215     public int hashCode() {
216         final int prime = 31;
217         int result = 1;
218         result = prime * result + attributes.hashCode();
219         result = prime * result + (localPref == null ? 0 : localPref.hashCode());
220         result = prime * result + Long.hashCode(multiExitDisc);
221         result = prime * result + (origin == null ? 0 : origin.hashCode());
222         result = prime * result + Boolean.hashCode(depreferenced);
223         return result;
224     }
225
226     @Override
227     public boolean equals(final Object obj) {
228         if (this == obj) {
229             return true;
230         }
231         if (!(obj instanceof BestPathStateImpl)) {
232             return false;
233         }
234         final BestPathStateImpl other = (BestPathStateImpl) obj;
235         if (!attributes.equals(other.attributes)) {
236             return false;
237         }
238         if (!Objects.equals(localPref, other.localPref)) {
239             return false;
240         }
241         if (multiExitDisc != other.multiExitDisc) {
242             return false;
243         }
244         if (origin != other.origin) {
245             return false;
246         }
247         return depreferenced == other.depreferenced;
248     }
249
250     private static List<Segments> extractSegments(final UnkeyedListNode segments, final NamespaceSpecificIds ids) {
251         // list segments
252         final List<Segments> extracted = new ArrayList<>();
253         for (final UnkeyedListEntryNode segment : segments.body()) {
254             final SegmentsBuilder sb = new SegmentsBuilder();
255             // We are expecting that segment contains either as-sequence or as-set,
256             // so just one of them will be set, other would be null
257             sb.setAsSequence(extractAsList(new ArrayList<>(), segment, ids.asSeqNid))
258                     .setAsSet(extractAsList(new LinkedHashSet<>(), segment, ids.asSetNid));
259             extracted.add(sb.build());
260         }
261         return extracted;
262     }
263
264     private static <T extends Collection<AsNumber>> T extractAsList(final T ases,
265             final UnkeyedListEntryNode segment, final NodeIdentifier nid) {
266         final Optional<NormalizedNode> maybeAsList = NormalizedNodes.findNode(segment, nid);
267         if (maybeAsList.isPresent()) {
268             final LeafSetNode<?> list = (LeafSetNode<?>)maybeAsList.get();
269             for (final LeafSetEntryNode<?> as : list.body())  {
270                 ases.add(new AsNumber((Uint32) as.body()));
271             }
272             return ases;
273         }
274         return null;
275     }
276
277     private static int countAsPath(final List<Segments> segments) {
278         // an AS_SET counts as 1, no matter how many ASs are in the set.
279         int count = 0;
280         boolean setPresent = false;
281         for (final Segments s : segments) {
282             if (s.getAsSet() != null && !setPresent) {
283                 setPresent = true;
284                 count++;
285             } else {
286                 final List<AsNumber> seq = s.getAsSequence();
287                 if (seq != null) {
288                     count += seq.size();
289                 }
290             }
291         }
292         return count;
293     }
294 }