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 com.google.common.base.MoreObjects;
11 import com.google.common.base.MoreObjects.ToStringHelper;
12 import com.google.common.base.Optional;
13 import com.google.common.base.Preconditions;
14 import com.google.common.cache.Cache;
15 import com.google.common.cache.CacheBuilder;
16 import com.google.common.collect.ImmutableList;
17 import java.util.ArrayList;
18 import java.util.Collection;
19 import java.util.List;
20 import java.util.concurrent.ExecutionException;
21 import javax.annotation.concurrent.NotThreadSafe;
22 import org.opendaylight.protocol.bgp.mode.api.BestPathState;
23 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.AsNumber;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.path.attributes.attributes.AsPath;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.path.attributes.attributes.LocalPref;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.path.attributes.attributes.MultiExitDisc;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.path.attributes.attributes.Origin;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.path.attributes.attributes.as.path.Segments;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.path.attributes.attributes.as.path.SegmentsBuilder;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.BgpOrigin;
31 import org.opendaylight.yangtools.yang.common.QName;
32 import org.opendaylight.yangtools.yang.common.QNameModule;
33 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
34 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
35 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
36 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
37 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
38 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
39 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes;
40 import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
41 import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
42 import org.slf4j.Logger;
43 import org.slf4j.LoggerFactory;
46 public final class BestPathStateImpl implements BestPathState {
47 private static final class NamespaceSpecificIds {
48 private final Collection<PathArgument> asPath;
49 private final Collection<PathArgument> locPref;
50 private final Collection<PathArgument> med;
51 private final Collection<PathArgument> orig;
52 private final NodeIdentifier asSetNid;
53 private final NodeIdentifier asSeqNid;
55 NamespaceSpecificIds(final QName namespace) {
56 NodeIdentifier container = new NodeIdentifier(QName.create(namespace, AsPath.QNAME.getLocalName().intern()));
57 NodeIdentifier leaf = new NodeIdentifier(QName.create(namespace, "segments").intern());
58 this.asPath = ImmutableList.of(container, leaf);
60 container = new NodeIdentifier(QName.create(namespace, LocalPref.QNAME.getLocalName()).intern());
61 leaf = new NodeIdentifier(QName.create(namespace, "pref").intern());
62 this.locPref = ImmutableList.of(container, leaf);
64 container = new NodeIdentifier(QName.create(namespace, MultiExitDisc.QNAME.getLocalName()).intern());
65 leaf = new NodeIdentifier(QName.create(namespace, "med").intern());
66 this.med = ImmutableList.of(container, leaf);
68 container = new NodeIdentifier(QName.create(namespace, Origin.QNAME.getLocalName()).intern());
69 leaf = new NodeIdentifier(QName.create(namespace, "value").intern());
70 this.orig = ImmutableList.of(container, leaf);
72 this.asSetNid = new NodeIdentifier(QName.create(namespace, "as-set").intern());
73 this.asSeqNid = new NodeIdentifier(QName.create(namespace, "as-sequence").intern());
76 Collection<PathArgument> getAsPath() {
80 Collection<PathArgument> getLocPref() {
84 Collection<PathArgument> getMed() {
88 Collection<PathArgument> getOrig() {
92 NodeIdentifier getAsSet() {
96 NodeIdentifier getAsSeq() {
101 private static final Logger LOG = LoggerFactory.getLogger(BestPathStateImpl.class);
102 private static final Cache<QNameModule, NamespaceSpecificIds> PATH_CACHE = CacheBuilder.newBuilder().weakKeys().weakValues().build();
104 private long peerAs = 0L;
105 private int asPathLength = 0;
107 private final ContainerNode attributes;
108 private final NamespaceSpecificIds ids;
109 private Long localPref;
110 private Long multiExitDisc;
111 private BgpOrigin origin;
112 private boolean resolved;
114 public BestPathStateImpl(final ContainerNode attributes) {
115 final NamespaceSpecificIds col;
117 col = PATH_CACHE.get(attributes.getNodeType().getModule(), () -> new NamespaceSpecificIds(attributes.getNodeType()));
118 } catch (final ExecutionException e) {
119 LOG.error("Error creating namespace-specific attributes collection.", e);
120 throw new IllegalStateException("Error creating namespace-specific attributes collection.", e);
123 this.attributes = Preconditions.checkNotNull(attributes);
128 private static BgpOrigin fromString(final String originStr) {
131 return BgpOrigin.Igp;
133 return BgpOrigin.Egp;
135 return BgpOrigin.Incomplete;
137 throw new IllegalArgumentException("Unhandled origin value " + originStr);
141 private void resolveValues() {
146 final Optional<NormalizedNode<?, ?>> maybeLocalPref = NormalizedNodes.findNode(this.attributes, this.ids.getLocPref());
147 if (maybeLocalPref.isPresent()) {
148 this.localPref = (Long) maybeLocalPref.get().getValue();
150 this.localPref = null;
153 final Optional<NormalizedNode<?, ?>> maybeMultiExitDisc = NormalizedNodes.findNode(this.attributes, this.ids.getMed());
154 if (maybeMultiExitDisc.isPresent()) {
155 this.multiExitDisc = (Long) maybeMultiExitDisc.get().getValue();
157 this.multiExitDisc = null;
160 final Optional<NormalizedNode<?, ?>> maybeOrigin = NormalizedNodes.findNode(this.attributes, this.ids.getOrig());
161 if (maybeOrigin.isPresent()) {
162 this.origin = fromString((String) maybeOrigin.get().getValue());
167 final Optional<NormalizedNode<?, ?>> maybeSegments = NormalizedNodes.findNode(this.attributes, this.ids.getAsPath());
168 if (maybeSegments.isPresent()) {
169 final UnkeyedListNode segments = (UnkeyedListNode) maybeSegments.get();
170 final List<Segments> segs = extractSegments(segments);
171 if (!segs.isEmpty()) {
172 this.peerAs = getPeerAs(segs).getValue();
173 this.asPathLength = countAsPath(segs);
176 this.resolved = true;
180 public Long getLocalPref() {
182 return this.localPref;
186 public Long getMultiExitDisc() {
188 return this.multiExitDisc;
192 public BgpOrigin getOrigin() {
198 public Long getPeerAs() {
204 public int getAsPathLength() {
206 return this.asPathLength;
209 private static int countAsPath(final List<Segments> segments) {
210 // an AS_SET counts as 1, no matter how many ASs are in the set.
212 boolean setPresent = false;
213 for (final Segments s : segments) {
214 if (s.getAsSet() != null && !setPresent) {
217 } else if (s.getAsSequence() != null) {
218 count += s.getAsSequence().size();
224 private static AsNumber getPeerAs(final List<Segments> segments) {
225 if (segments.isEmpty()) {
226 return new AsNumber(0L);
228 for (final Segments seg : segments) {
229 if (seg.getAsSequence() != null && !seg.getAsSequence().isEmpty()) {
230 return segments.get(0).getAsSequence().get(0);
233 return new AsNumber(0L);
236 public List<Segments> extractSegments(final UnkeyedListNode segments) {
238 final List<Segments> extracted = new ArrayList<>();
239 for (final UnkeyedListEntryNode segment : segments.getValue()) {
240 final SegmentsBuilder sb = new SegmentsBuilder();
241 // We are expecting that segment contains either as-sequence or as-set, so just one of them will be set, other would be null
242 sb.setAsSequence(extractAsList(segment, this.ids.getAsSeq())).setAsSet(extractAsList(segment, this.ids.getAsSet()));
243 extracted.add(sb.build());
248 private List<AsNumber> extractAsList(final UnkeyedListEntryNode segment, final NodeIdentifier nid) {
249 final List<AsNumber> ases = new ArrayList<>();
250 final Optional<NormalizedNode<?, ?>> maybeAsList = NormalizedNodes.findNode(segment, nid);
251 if (maybeAsList.isPresent()) {
252 final LeafSetNode<?> list = (LeafSetNode<?>)maybeAsList.get();
253 for (final LeafSetEntryNode<?> as : list.getValue()) {
254 ases.add(new AsNumber((Long)as.getValue()));
262 public ContainerNode getAttributes() {
263 return this.attributes;
266 private ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
267 toStringHelper.add("attributes", this.attributes);
268 toStringHelper.add("localPref", this.localPref);
269 toStringHelper.add("multiExitDisc", this.multiExitDisc);
270 toStringHelper.add("origin", this.origin);
271 toStringHelper.add("resolved", this.resolved);
272 return toStringHelper;
276 public String toString() {
277 return addToStringAttributes(MoreObjects.toStringHelper(this)).toString();
281 public int hashCode() {
282 final int prime = 31;
284 result = prime * result + this.attributes.hashCode();
285 result = prime * result + ((this.localPref == null) ? 0 : this.localPref.hashCode());
286 result = prime * result + ((this.multiExitDisc == null) ? 0 : this.multiExitDisc.hashCode());
287 result = prime * result + ((this.origin == null) ? 0 : this.origin.hashCode());
292 public boolean equals(final Object obj) {
296 if (!(obj instanceof BestPathStateImpl)) {
299 final BestPathStateImpl other = (BestPathStateImpl) obj;
300 if (!this.attributes.equals(other.attributes)) {
303 if (this.localPref == null) {
304 if (other.localPref != null) {
307 } else if (!this.localPref.equals(other.localPref)) {
310 if (this.multiExitDisc == null) {
311 if (other.multiExitDisc != null) {
314 } else if (!this.multiExitDisc.equals(other.multiExitDisc)) {
317 if (this.origin != other.origin) {