2 * Copyright (c) 2015 Cisco Systems, Inc. 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.protocol.bgp.mode.impl;
10 import static com.google.common.base.Verify.verifyNotNull;
11 import static java.util.Objects.requireNonNull;
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;
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;
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());
80 private static final LoadingCache<QNameModule, NamespaceSpecificIds> PATH_CACHE =
81 CacheBuilder.newBuilder().weakKeys().weakValues().build(new CacheLoader<>() {
83 public NamespaceSpecificIds load(final QNameModule key) {
84 return new NamespaceSpecificIds(key);
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();
91 private final ContainerNode attributes;
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;
101 public BestPathStateImpl(final ContainerNode attributes) {
102 this.attributes = requireNonNull(attributes);
106 private void resolveValues() {
111 final NamespaceSpecificIds ids = PATH_CACHE.getUnchecked(attributes.getIdentifier().getNodeType().getModule());
112 localPref = (Uint32) NormalizedNodes.findNode(attributes, ids.locPref)
113 .map(NormalizedNode::body)
116 final Optional<NormalizedNode> maybeMultiExitDisc = NormalizedNodes.findNode(attributes, ids.med);
117 if (maybeMultiExitDisc.isPresent()) {
118 multiExitDisc = ((Uint32) maybeMultiExitDisc.get().body()).toJava();
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));
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);
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));
147 depreferenced = false;
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());
159 public Uint32 getLocalPref() {
165 public long getMultiExitDisc() {
167 return multiExitDisc;
171 public BgpOrigin getOrigin() {
177 public long getPeerAs() {
183 public int getAsPathLength() {
189 public ContainerNode getAttributes() {
194 public boolean isDepreferenced() {
196 return depreferenced;
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;
210 public String toString() {
211 return addToStringAttributes(MoreObjects.toStringHelper(this)).toString();
215 public int hashCode() {
216 final int prime = 31;
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);
227 public boolean equals(final Object obj) {
231 if (!(obj instanceof BestPathStateImpl)) {
234 final BestPathStateImpl other = (BestPathStateImpl) obj;
235 if (!attributes.equals(other.attributes)) {
238 if (!Objects.equals(localPref, other.localPref)) {
241 if (multiExitDisc != other.multiExitDisc) {
244 if (origin != other.origin) {
247 return depreferenced == other.depreferenced;
250 private static List<Segments> extractSegments(final UnkeyedListNode segments, final NamespaceSpecificIds ids) {
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());
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()));
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.
280 boolean setPresent = false;
281 for (final Segments s : segments) {
282 if (s.getAsSet() != null && !setPresent) {
286 final List<AsNumber> seq = s.getAsSequence();