Make deeper comparison for best path selection.
[bgpcep.git] / bgp / rib-impl / src / main / java / org / opendaylight / protocol / bgp / rib / impl / BestPathState.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.rib.impl;
9
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.collect.ImmutableList;
15 import java.util.Collection;
16 import java.util.List;
17 import javax.annotation.concurrent.NotThreadSafe;
18 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.AsNumber;
19 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.path.attributes.attributes.AsPath;
20 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.path.attributes.attributes.LocalPref;
21 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.path.attributes.attributes.MultiExitDisc;
22 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.path.attributes.attributes.Origin;
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.path.attributes.attributes.as.path.Segments;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.BgpOrigin;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.as.path.segment.c.segment.AListCase;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.as.path.segment.c.segment.ASetCase;
27 import org.opendaylight.yangtools.yang.common.QName;
28 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
29 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
30 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
31 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
32 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
33 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes;
34 import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
35 import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
36
37 @NotThreadSafe
38 final class BestPathState {
39     private static final Collection<PathArgument> AS_PATH = ImmutableList.<PathArgument>of(new NodeIdentifier(AsPath.QNAME), new NodeIdentifier(Segments.QNAME));
40     private static final Collection<PathArgument> LOCAL_PREF = ImmutableList.<PathArgument>of(new NodeIdentifier(LocalPref.QNAME), new NodeIdentifier(QName.create(LocalPref.QNAME, "pref")));
41     private static final Collection<PathArgument> MED = ImmutableList.<PathArgument>of(new NodeIdentifier(MultiExitDisc.QNAME), new NodeIdentifier(QName.create(MultiExitDisc.QNAME, "med")));
42     private static final Collection<PathArgument> ORIGIN = ImmutableList.<PathArgument>of(new NodeIdentifier(Origin.QNAME), new NodeIdentifier(QName.create(Origin.QNAME, "value")));
43
44     private final ContainerNode attributes;
45     private Long localPref;
46     private Long multiExitDisc;
47     private BgpOrigin origin;
48     private static final Long peerAs = 0L;
49     private static final int asPathLength = 0;
50     private boolean resolved;
51
52     BestPathState(final ContainerNode attributes) {
53         this.attributes = Preconditions.checkNotNull(attributes);
54     }
55
56     private static BgpOrigin fromString(final String originStr) {
57         switch (originStr) {
58         case "igp":
59             return BgpOrigin.Igp;
60         case "egp":
61             return BgpOrigin.Egp;
62         case "incomplete":
63             return BgpOrigin.Incomplete;
64         default:
65             throw new IllegalArgumentException("Unhandled origin value " + originStr);
66         }
67     }
68
69     private void resolveValues() {
70         if (this.resolved) {
71             return;
72         }
73
74         final Optional<NormalizedNode<?, ?>> maybeLocalPref = NormalizedNodes.findNode(this.attributes, LOCAL_PREF);
75         if (maybeLocalPref.isPresent()) {
76             this.localPref = (Long) ((LeafNode<?>)maybeLocalPref.get()).getValue();
77         } else {
78             this.localPref = null;
79         }
80
81         final Optional<NormalizedNode<?, ?>> maybeMultiExitDisc = NormalizedNodes.findNode(this.attributes, MED);
82         if (maybeMultiExitDisc.isPresent()) {
83             this.multiExitDisc = (Long) ((LeafNode<?>)maybeMultiExitDisc.get()).getValue();
84         } else {
85             this.multiExitDisc = null;
86         }
87
88         final Optional<NormalizedNode<?, ?>> maybeOrigin = NormalizedNodes.findNode(this.attributes, ORIGIN);
89         if (maybeOrigin.isPresent()) {
90             this.origin = fromString((String) ((LeafNode<?>)maybeOrigin.get()).getValue());
91         } else {
92             this.origin = null;
93         }
94
95         final Optional<NormalizedNode<?, ?>> maybeSegments = NormalizedNodes.findNode(this.attributes, AS_PATH);
96         if (maybeSegments.isPresent()) {
97             final UnkeyedListNode segments = (UnkeyedListNode) maybeSegments.get();
98
99             if (segments.getSize() != 0) {
100                 // FIXME: peer AS number
101
102                 // FIXME: asPathLength = countAsPath(this.bestState.getAsPath().getSegments());
103                 final boolean haveSegment;
104                 for (final UnkeyedListEntryNode s : segments.getValue()) {
105
106                 }
107             }
108         }
109
110         this.resolved = true;
111     }
112
113     Long getLocalPref() {
114         resolveValues();
115         return this.localPref;
116     }
117
118     Long getMultiExitDisc() {
119         resolveValues();
120         return this.multiExitDisc;
121     }
122
123     BgpOrigin getOrigin() {
124         resolveValues();
125         return this.origin;
126     }
127
128     Long getPeerAs() {
129         resolveValues();
130         return this.peerAs;
131     }
132
133     int getAsPathLength() {
134         resolveValues();
135         return asPathLength;
136     }
137
138     private static int countAsPath(final List<Segments> segments) {
139         // an AS_SET counts as 1, no matter how many ASs are in the set.
140         int count = 0;
141         boolean setPresent = false;
142         for (final Segments s : segments) {
143             if (s.getCSegment() instanceof ASetCase) {
144                 setPresent = true;
145             } else {
146                 final AListCase list = (AListCase) s.getCSegment();
147                 count += list.getAList().getAsSequence().size();
148             }
149         }
150         return (setPresent) ? count + 1 : count;
151     }
152
153     private static AsNumber getPeerAs(final List<Segments> segments) {
154         if (segments.isEmpty()) {
155             return null;
156         }
157
158         final AListCase first = (AListCase) segments.get(0).getCSegment();
159         return first.getAList().getAsSequence().get(0).getAs();
160     }
161
162     ContainerNode getAttributes() {
163         return this.attributes;
164     }
165
166     private ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
167         toStringHelper.add("attributes", this.attributes);
168         toStringHelper.add("localPref", this.localPref);
169         toStringHelper.add("multiExitDisc", this.multiExitDisc);
170         toStringHelper.add("origin", this.origin);
171         toStringHelper.add("resolved", this.resolved);
172         return toStringHelper;
173     }
174
175     @Override
176     public String toString() {
177         return addToStringAttributes(MoreObjects.toStringHelper(this)).toString();
178     }
179
180     @Override
181     public int hashCode() {
182         final int prime = 31;
183         int result = 1;
184         result = prime * result + this.attributes.hashCode();
185         result = prime * result + ((this.localPref == null) ? 0 : this.localPref.hashCode());
186         result = prime * result + ((this.multiExitDisc == null) ? 0 : this.multiExitDisc.hashCode());
187         result = prime * result + ((this.origin == null) ? 0 : this.origin.hashCode());
188         return result;
189     }
190
191     @Override
192     public boolean equals(final Object obj) {
193         if (this == obj) {
194             return true;
195         }
196         if (!(obj instanceof BestPathState)) {
197             return false;
198         }
199         final BestPathState other = (BestPathState) obj;
200         if (!this.attributes.equals(other.attributes)) {
201             return false;
202         }
203         if (this.localPref == null) {
204             if (other.localPref != null) {
205                 return false;
206             }
207         } else if (!this.localPref.equals(other.localPref)) {
208             return false;
209         }
210         if (this.multiExitDisc == null) {
211             if (other.multiExitDisc != null) {
212                 return false;
213             }
214         } else if (!this.multiExitDisc.equals(other.multiExitDisc)) {
215             return false;
216         }
217         if (this.origin != other.origin) {
218             return false;
219         }
220         return true;
221     }
222 }