BUG-2823: Simplify AS_PATH encoding
[bgpcep.git] / bgp / rib-impl / src / test / java / org / opendaylight / protocol / bgp / rib / impl / BestPathSelectorTest.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 static org.junit.Assert.assertEquals;
11 import static org.junit.Assert.assertNotEquals;
12 import com.google.common.collect.Lists;
13 import com.google.common.collect.Sets;
14 import com.google.common.primitives.UnsignedInteger;
15 import java.util.ArrayList;
16 import java.util.List;
17 import org.junit.Test;
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.as.path.Segments;
20 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.path.attributes.attributes.as.path.SegmentsBuilder;
21 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.PeerId;
22 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.BgpOrigin;
23 import org.opendaylight.yangtools.yang.common.QName;
24 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
25 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
26 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
27 import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
28 import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
29 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
30 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
31 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
32 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeSchemaAwareBuilder;
33 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafNodeBuilder;
34 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableUnkeyedListNodeBuilder;
35
36 public class BestPathSelectorTest {
37
38     static final QName ATTRS_EXTENSION_Q = QName.create("urn:opendaylight:params:xml:ns:yang:bgp-inet", "2015-03-05", "attributes");
39     private final QName localPrefQName = QName.create(ATTRS_EXTENSION_Q, "local-pref");
40     private final QName multiExitDiscQName = QName.create(ATTRS_EXTENSION_Q, "multi-exit-disc");
41     private final QName originQName = QName.create(ATTRS_EXTENSION_Q, "origin");
42     private final QName asPathQName = QName.create(ATTRS_EXTENSION_Q, "as-path");
43     private final UnsignedInteger ROUTER_ID = RouterIds.routerIdForAddress("127.0.0.1");
44     private final UnsignedInteger ROUTER_ID2 = RouterIds.routerIdForPeerId(new PeerId("bgp://127.0.0.1"));
45     private final UnsignedInteger ROUTER_ID3 = RouterIds.routerIdForPeerId(new PeerId("bgp://127.0.0.2"));
46     private final BestPathState state = new BestPathState(createStateFromPrefMedOriginASPath());
47     private final BestPath originBestPath = new BestPath(this.ROUTER_ID, this.state);
48     private final BestPathSelector selector = new BestPathSelector(20L);
49     private DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> dataContBuilder;
50
51     static final QName AS_NUMBER_Q = QName.create(ATTRS_EXTENSION_Q, "as-number");
52     static final NodeIdentifier SEGMENTS_NID = new NodeIdentifier(QName.create(ATTRS_EXTENSION_Q, Segments.QNAME.getLocalName()));
53     static final NodeIdentifier SET_LEAFLIST_NID = new NodeIdentifier(QName.create(ATTRS_EXTENSION_Q, "as-set"));
54     static final NodeIdentifier SEQ_LEAFLIST_NID = new NodeIdentifier(QName.create(ATTRS_EXTENSION_Q, "as-sequence"));
55
56     static final UnkeyedListEntryNode SET_SEGMENT = Builders.unkeyedListEntryBuilder().withNodeIdentifier(SEGMENTS_NID)
57         .addChild(Builders.leafSetBuilder().withNodeIdentifier(SET_LEAFLIST_NID)
58             .addChild(Builders.leafSetEntryBuilder().withNodeIdentifier(new NodeWithValue(AS_NUMBER_Q, 10L)).withValue(10L).build())
59             .addChild(Builders.leafSetEntryBuilder().withNodeIdentifier(new NodeWithValue(AS_NUMBER_Q, 11L)).withValue(11L).build())
60             .build()).build();
61
62     static final UnkeyedListEntryNode SEQ_SEGMENT = Builders.unkeyedListEntryBuilder().withNodeIdentifier(SEGMENTS_NID)
63         .addChild(Builders.orderedLeafSetBuilder().withNodeIdentifier(SEQ_LEAFLIST_NID)
64             .addChild(Builders.leafSetEntryBuilder().withNodeIdentifier(new NodeWithValue(AS_NUMBER_Q, 1L)).withValue(1L).build())
65             .addChild(Builders.leafSetEntryBuilder().withNodeIdentifier(new NodeWithValue(AS_NUMBER_Q, 2L)).withValue(2L).build())
66             .addChild(Builders.leafSetEntryBuilder().withNodeIdentifier(new NodeWithValue(AS_NUMBER_Q, 3L)).withValue(3L).build())
67             .build()).build();
68
69     static final UnkeyedListEntryNode SEQ_SEGMENT2 = Builders.unkeyedListEntryBuilder().withNodeIdentifier(SEGMENTS_NID)
70         .addChild(Builders.orderedLeafSetBuilder().withNodeIdentifier(SEQ_LEAFLIST_NID)
71             .addChild(Builders.leafSetEntryBuilder().withNodeIdentifier(new NodeWithValue(AS_NUMBER_Q, 20L)).withValue(20L).build())
72             .addChild(Builders.leafSetEntryBuilder().withNodeIdentifier(new NodeWithValue(AS_NUMBER_Q, 2L)).withValue(2L).build())
73             .addChild(Builders.leafSetEntryBuilder().withNodeIdentifier(new NodeWithValue(AS_NUMBER_Q, 3L)).withValue(3L).build())
74             .build()).build();
75
76     @Test
77     public void testBestPathForEquality() {
78         this.selector.processPath(this.ROUTER_ID2, createStateFromPrefMedOriginASPath());
79         final BestPath processedPath = this.selector.result();
80
81         assertEquals(this.originBestPath.getRouterId(), processedPath.getRouterId());
82         assertEquals(this.originBestPath.getState().getLocalPref(), processedPath.getState().getLocalPref());
83         assertEquals(this.originBestPath.getState().getMultiExitDisc(), processedPath.getState().getMultiExitDisc());
84         assertEquals(this.originBestPath.getState().getOrigin(), processedPath.getState().getOrigin());
85         assertEquals(this.originBestPath.getState().getPeerAs(), processedPath.getState().getPeerAs());
86         assertEquals(this.originBestPath.getState().getAsPathLength(), processedPath.getState().getAsPathLength());
87     }
88
89     @Test
90     public void testBestPathWithHigherLocalPref() {
91         this.selector.processPath(this.ROUTER_ID2, createStateFromPrefMedOrigin());   // local-pref 123
92         BestPath processedPath = this.selector.result();
93         assertEquals(123L, processedPath.getState().getLocalPref().longValue());
94
95         this.selector.processPath(this.ROUTER_ID2, createStateFromPrefMedOriginASPath());   // local-pref 321
96         processedPath = this.selector.result();
97         assertEquals(321L, processedPath.getState().getLocalPref().longValue());
98
99         addLowerLocalRef(); // prefer path with higher LOCAL_PREF
100         this.selector.processPath(this.ROUTER_ID2, this.dataContBuilder.build());
101         processedPath = this.selector.result();
102         assertEquals(321L, processedPath.getState().getLocalPref().longValue());
103     }
104
105     @Test
106     public void testBestPathSelectionOptions() {
107         this.selector.processPath(this.ROUTER_ID2, createStateFromPrefMedOriginASPath());
108         BestPath processedPath = this.selector.result();
109         assertEquals(1, processedPath.getState().getOrigin().getIntValue());
110
111         addIgpOrigin(); // prefer the path with the lowest origin type
112         this.selector.processPath(this.ROUTER_ID2, this.dataContBuilder.build());
113         processedPath = this.selector.result();
114         assertEquals(0, processedPath.getState().getOrigin().getIntValue());
115
116         addEgpOrigin();
117         this.selector.processPath(this.ROUTER_ID2, this.dataContBuilder.build());
118         processedPath = this.selector.result();
119         assertEquals(0, processedPath.getState().getOrigin().getIntValue());
120
121         // prefer the path with the lowest multi-exit discriminator (MED)
122         assertEquals(4321L, (long) processedPath.getState().getMultiExitDisc());
123         addIgpOrigin();
124         addLowerMultiExitDisc();
125         this.selector.processPath(this.ROUTER_ID2, this.dataContBuilder.build());
126         processedPath = this.selector.result();
127         assertEquals(1234L, (long) processedPath.getState().getMultiExitDisc());
128
129         addHigherMultiExitDisc();
130         this.selector.processPath(this.ROUTER_ID2, this.dataContBuilder.build());
131         processedPath = this.selector.result();
132         assertEquals(1234L, (long) processedPath.getState().getMultiExitDisc());
133
134         addLowerMultiExitDisc();
135         addAsPath(SEQ_SEGMENT2);
136         assertEquals(1L, (long) processedPath.getState().getPeerAs());
137         assertEquals(3, processedPath.getState().getAsPathLength());
138         this.selector.processPath(this.ROUTER_ID2, this.dataContBuilder.build());
139         processedPath = this.selector.result();
140         assertEquals(1L, (long) processedPath.getState().getPeerAs());
141         assertEquals(3, processedPath.getState().getAsPathLength());
142     }
143
144     @Test
145     public void testBestPathForNonEquality() {
146         this.selector.processPath(this.ROUTER_ID3, createStateFromPrefMedOrigin());
147         final BestPath processedPath = this.selector.result();
148
149         assertNotEquals(this.originBestPath.getRouterId(), processedPath.getRouterId());
150         assertNotEquals(this.originBestPath.getState().getLocalPref(), processedPath.getState().getLocalPref());
151         assertNotEquals(this.originBestPath.getState().getMultiExitDisc(), processedPath.getState().getMultiExitDisc());
152         assertNotEquals(this.originBestPath.getState().getOrigin(), processedPath.getState().getOrigin());
153         assertNotEquals(this.originBestPath.getState().getPeerAs(), processedPath.getState().getPeerAs());
154         assertNotEquals(this.originBestPath.getState().getAsPathLength(), processedPath.getState().getAsPathLength());
155     }
156
157     private ContainerNode createStateFromPrefMedOrigin() {
158         this.dataContBuilder = createContBuilder(ATTRS_EXTENSION_Q);
159         addLowerLocalRef();
160         addLowerMultiExitDisc();
161         addIgpOrigin();
162         return this.dataContBuilder.build();
163     }
164
165     private ContainerNode createStateFromPrefMedOriginASPath() {
166         this.dataContBuilder = createContBuilder(ATTRS_EXTENSION_Q);
167         addHigherLocalRef();
168         addHigherMultiExitDisc();
169         addEgpOrigin();
170         addAsPath(SEQ_SEGMENT);
171         return this.dataContBuilder.build();
172     }
173
174     private void addLowerLocalRef() {
175         this.dataContBuilder.addChild(createContBuilder(this.localPrefQName).addChild(createValueBuilder(123L, this.localPrefQName, "pref").build()).build());
176     }
177
178     private void addHigherLocalRef() {
179         this.dataContBuilder.addChild(createContBuilder(this.localPrefQName).addChild(createValueBuilder(321L, this.localPrefQName, "pref").build()).build());
180     }
181
182     private void addLowerMultiExitDisc() {
183         this.dataContBuilder.addChild(createContBuilder(this.multiExitDiscQName).addChild(createValueBuilder(1234L, this.multiExitDiscQName, "med").build()).build());
184     }
185
186     private void addHigherMultiExitDisc() {
187         this.dataContBuilder.addChild(createContBuilder(this.multiExitDiscQName).addChild(createValueBuilder(4321L, this.multiExitDiscQName, "med").build()).build());
188     }
189
190     private void addIgpOrigin() {
191         this.dataContBuilder.addChild(createContBuilder(this.originQName).addChild(createValueBuilder("igp", this.originQName, "value").build()).build());
192     }
193
194     private void addEgpOrigin() {
195         this.dataContBuilder.addChild(createContBuilder(this.originQName).addChild(createValueBuilder("egp", this.originQName, "value").build()).build());
196     }
197
198     private void addAsPath(final UnkeyedListEntryNode segment) {
199         final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> asPathContBuilder = ImmutableContainerNodeSchemaAwareBuilder.create();
200         asPathContBuilder.withNodeIdentifier(new NodeIdentifier(this.asPathQName));
201
202         final CollectionNodeBuilder<UnkeyedListEntryNode, UnkeyedListNode> segments = ImmutableUnkeyedListNodeBuilder.create();
203         segments.withNodeIdentifier(SEGMENTS_NID);
204         segments.addChild(segment);
205         asPathContBuilder.addChild(segments.build());
206         this.dataContBuilder.addChild(asPathContBuilder.build());
207     }
208
209
210     private static DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> createContBuilder(final QName qname) {
211         return ImmutableContainerNodeSchemaAwareBuilder.create().withNodeIdentifier(new NodeIdentifier(qname));
212     }
213
214     private static <T> ImmutableLeafNodeBuilder<T> createValueBuilder(final T value, final QName qname, final String localName) {
215         final ImmutableLeafNodeBuilder<T> valueBuilder = new ImmutableLeafNodeBuilder<>();
216         valueBuilder.withNodeIdentifier(new NodeIdentifier(QName.create(qname, localName))).withValue(value);
217         return valueBuilder;
218     }
219
220     @Test
221     public void testExtractSegments() {
222         // to be extracted from
223         final CollectionNodeBuilder<UnkeyedListEntryNode, UnkeyedListNode> builder = Builders.unkeyedListBuilder();
224         builder.withNodeIdentifier(SEGMENTS_NID);
225         builder.addChild(SET_SEGMENT);
226         builder.addChild(SEQ_SEGMENT);
227
228         // expected
229         final List<AsNumber> sequences = new ArrayList<>();
230         sequences.add(new AsNumber(1L));
231         sequences.add(new AsNumber(2L));
232         sequences.add(new AsNumber(3L));
233         final List<Segments> expected = new ArrayList<>();
234         expected.add(new SegmentsBuilder().setAsSet(Lists.newArrayList(new AsNumber(11L), new AsNumber(10L))).build());
235         expected.add(new SegmentsBuilder().setAsSequence(sequences).build());
236         // test
237         final List<Segments> actual = this.state.extractSegments(builder.build());
238         assertEquals(expected.size(), actual.size());
239         assertEquals(Sets.newHashSet(1,2,3), Sets.newHashSet(1,3,2));
240         assertEquals(Sets.newHashSet(expected.get(0).getAsSet()), Sets.newHashSet(actual.get(0).getAsSet()));
241         assertEquals(expected.get(1), actual.get(1));
242     }
243
244     @Test(expected=IllegalArgumentException.class)
245     public void testBgpOrigin() {
246         final ContainerNode containerIncom = this.dataContBuilder.addChild(createContBuilder(this.originQName).addChild(createValueBuilder("incomplete", this.originQName, "value").build()).build()).build();
247         this.selector.processPath(this.ROUTER_ID3, containerIncom);
248         final BestPath processedPathIncom = this.selector.result();
249         assertEquals(BgpOrigin.Incomplete, processedPathIncom.getState().getOrigin());
250
251         final ContainerNode containerException = this.dataContBuilder.addChild(createContBuilder(this.originQName).addChild(createValueBuilder("LOL", this.originQName, "value").build()).build()).build();
252         this.selector.processPath(this.ROUTER_ID3, containerException);
253         final BestPath processedPathException = this.selector.result();
254         processedPathException.getState().getOrigin();
255     }
256 }