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.rib.impl;
10 import com.google.common.base.Optional;
11 import com.google.common.cache.CacheBuilder;
12 import com.google.common.cache.CacheLoader;
13 import com.google.common.cache.LoadingCache;
14 import com.google.common.collect.ImmutableList;
15 import com.google.common.collect.ImmutableSet;
16 import java.util.Iterator;
17 import org.opendaylight.protocol.util.Values;
18 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address;
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.ClusterId;
21 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.path.attributes.attributes.Communities;
22 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.path.attributes.attributes.ExtendedCommunities;
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.path.attributes.attributes.Origin;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.path.attributes.attributes.OriginatorId;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.path.attributes.attributes.UnrecognizedAttributes;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.path.attributes.attributes.as.path.Segments;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.ClusterIdentifier;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.next.hop.CNextHop;
29 import org.opendaylight.yangtools.yang.common.QName;
30 import org.opendaylight.yangtools.yang.common.QNameModule;
31 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
32 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
33 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
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.DataContainerChild;
37 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
38 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
39 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
40 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
41 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes;
42 import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
43 import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
44 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
45 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
46 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
47 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
48 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.ListNodeBuilder;
49 import org.slf4j.Logger;
50 import org.slf4j.LoggerFactory;
53 * Utility class for working with route attributes in binding independent form. An instance
54 * is always bound to a certain namespace, so it maintains cached attribute identifiers.
56 final class AttributeOperations {
57 private static final Logger LOG = LoggerFactory.getLogger(AttributeOperations.class);
58 private static final LoadingCache<QNameModule, AttributeOperations> ATTRIBUTES_CACHE = CacheBuilder.newBuilder().weakKeys().weakValues().build(
59 new CacheLoader<QNameModule, AttributeOperations>() {
61 public AttributeOperations load(final QNameModule key) throws Exception {
62 return new AttributeOperations(key);
65 private static final LoadingCache<QNameModule, ImmutableSet<QName>> TRANSITIVE_CACHE = CacheBuilder.newBuilder()
68 new CacheLoader<QNameModule, ImmutableSet<QName>>() {
70 public ImmutableSet<QName> load(final QNameModule key) {
71 return ImmutableSet.of(QName.create(key, Origin.QNAME.getLocalName()).intern(),
72 QName.create(key, AsPath.QNAME.getLocalName()).intern(),
73 QName.create(key, CNextHop.QNAME.getLocalName()).intern(),
74 QName.create(key, Communities.QNAME.getLocalName()).intern(),
75 QName.create(key, ExtendedCommunities.QNAME.getLocalName()).intern());
78 private final ImmutableSet<QName> transitiveCollection;
79 private final Iterable<PathArgument> originatorIdPath;
80 private final Iterable<PathArgument> clusterListPath;
81 private final NodeIdentifier originatorIdContainer;
82 private final NodeIdentifier originatorIdLeaf;
83 private final NodeIdentifier clusterListContainer;
84 private final NodeIdentifier clusterListLeaf;
85 private final QName clusterQname;
86 private final NodeIdentifier asPathContainer;
87 private final NodeIdentifier asPathSegments;
88 private final NodeIdentifier asPathSequence;
89 private final QName asNumberQname;
90 private final NodeIdentifier transitiveLeaf;
92 private AttributeOperations(final QNameModule namespace) {
93 this.asPathContainer = new NodeIdentifier(QName.create(namespace, AsPath.QNAME.getLocalName()).intern());
94 this.asPathSegments = new NodeIdentifier(QName.create(namespace, Segments.QNAME.getLocalName()).intern());
95 this.asPathSequence = new NodeIdentifier(QName.create(namespace, "as-sequence").intern());
96 this.asNumberQname = QName.create(namespace, "as-number").intern();
98 this.clusterListContainer = new NodeIdentifier(QName.create(namespace, ClusterId.QNAME.getLocalName()).intern());
99 this.clusterQname = QName.create(namespace, "cluster").intern();
100 this.clusterListLeaf = new NodeIdentifier(this.clusterQname);
101 this.clusterListPath = ImmutableList.of(this.clusterListContainer, this.clusterListLeaf);
102 this.originatorIdContainer = new NodeIdentifier(QName.create(namespace, OriginatorId.QNAME.getLocalName()).intern());
103 this.originatorIdLeaf = new NodeIdentifier(QName.create(namespace, "originator").intern());
104 this.originatorIdPath = ImmutableList.of(this.originatorIdContainer, this.originatorIdLeaf);
106 this.transitiveLeaf = new NodeIdentifier(QName.create(UnrecognizedAttributes.QNAME, "transitive").intern());
107 this.transitiveCollection = TRANSITIVE_CACHE.getUnchecked(namespace);
110 static AttributeOperations getInstance(final ContainerNode attributes) {
111 return ATTRIBUTES_CACHE.getUnchecked(attributes.getNodeType().getModule());
114 private LeafSetNode<?> reusableSegment(final UnkeyedListEntryNode segment) {
115 final Optional<NormalizedNode<?, ?>> maybeAsSequence = NormalizedNodes.findNode(segment, this.asPathSequence);
116 if (maybeAsSequence.isPresent()) {
117 final LeafSetNode<?> asList = (LeafSetNode<?>) maybeAsSequence.get();
118 if (asList.getValue().size() < Values.UNSIGNED_BYTE_MAX_VALUE) {
125 ContainerNode exportedAttributes(final ContainerNode attributes, final Long localAs) {
126 final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> containerBuilder = Builders.containerBuilder();
127 containerBuilder.withNodeIdentifier(attributes.getIdentifier());
129 // First filter out non-transitive attributes
130 // FIXME: removes MULTI_EXIT_DISC, too.
131 spliceTransitives(containerBuilder, attributes);
133 final CollectionNodeBuilder<UnkeyedListEntryNode, UnkeyedListNode> segmentsBuilder = Builders.unkeyedListBuilder();
134 segmentsBuilder.withNodeIdentifier(this.asPathSegments);
136 final Optional<NormalizedNode<?, ?>> maybeOldAsSegments = NormalizedNodes.findNode(attributes, this.asPathContainer, this.asPathSegments);
137 if (maybeOldAsSegments.isPresent() && !((UnkeyedListNode) maybeOldAsSegments.get()).getValue().isEmpty()) {
140 * We need to check the first segment.
141 * If it has as-set then new as-sequence with local AS is prepended.
142 * If it has as-sequence, we may add local AS when it has less than 255 elements.
143 * Otherwise we need to create new as-sequence for local AS.
146 final ListNodeBuilder<Object,LeafSetEntryNode<Object>> asSequenceBuilder = Builders.orderedLeafSetBuilder();
148 asSequenceBuilder.withNodeIdentifier(this.asPathSequence).addChild(Builders.leafSetEntryBuilder().withNodeIdentifier(
149 new NodeWithValue<>(this.asNumberQname, localAs)).withValue(localAs).build());
151 final Iterator<UnkeyedListEntryNode> oldAsSegments = ((UnkeyedListNode) maybeOldAsSegments.get()).getValue().iterator();
152 final UnkeyedListEntryNode firstSegment = oldAsSegments.next();
153 final LeafSetNode<?> reusableAsSeq = reusableSegment(firstSegment);
154 // first segment contains as-sequence with less then 255 elements and it's append to local AS
155 if (reusableAsSeq != null) {
156 for (final LeafSetEntryNode<?> child : reusableAsSeq.getValue()) {
157 asSequenceBuilder.withChild(Builders.leafSetEntryBuilder().withNodeIdentifier(new NodeWithValue<>(
158 this.asNumberQname, child.getValue())).withValue(child.getValue()).build());
161 // Add the new first segment
162 segmentsBuilder.withChild(Builders.unkeyedListEntryBuilder().withNodeIdentifier(this.asPathSegments).withChild(asSequenceBuilder.build()).build());
164 // When first segment contains as-set or full as-sequence, append it
165 if (reusableAsSeq == null) {
166 segmentsBuilder.withChild(firstSegment);
169 // Add all subsequent segments
170 while (oldAsSegments.hasNext()) {
171 segmentsBuilder.withChild(oldAsSegments.next());
174 // Segments are completely empty, create a completely new AS_PATH container with
176 segmentsBuilder.withChild(Builders.unkeyedListEntryBuilder().withNodeIdentifier(this.asPathSegments).withChild(
177 Builders.orderedLeafSetBuilder().withNodeIdentifier(this.asPathSequence).addChild(
178 Builders.leafSetEntryBuilder().withNodeIdentifier(new NodeWithValue<>(this.asNumberQname, localAs))
179 .withValue(localAs).build()).build()).build());
182 containerBuilder.withChild(Builders.containerBuilder().withNodeIdentifier(this.asPathContainer).withChild(segmentsBuilder.build()).build());
183 return containerBuilder.build();
187 * Attributes when reflecting a route from Internal iBGP (Application Peer)
191 ContainerNode reflectedAttributes(final ContainerNode attributes) {
192 final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> attributesContainer = Builders.containerBuilder(attributes);
194 // if there was a CLUSTER_LIST attribute, add it
195 final Optional<NormalizedNode<?, ?>> maybeClusterList = NormalizedNodes.findNode(attributes, this.clusterListPath);
196 if (maybeClusterList.isPresent()) {
197 // Create a new CLUSTER_LIST builder
198 final ListNodeBuilder<Object, LeafSetEntryNode<Object>> clusterBuilder = Builders.orderedLeafSetBuilder();
199 clusterBuilder.withNodeIdentifier(this.clusterListLeaf);
200 AttributeOperations.addOtherClusterEntries(maybeClusterList, clusterBuilder);
201 // Now wrap it in a container and add it to attributes
202 final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> clusterListCont= Builders.containerBuilder();
203 clusterListCont.withNodeIdentifier(this.clusterListContainer);
204 clusterListCont.withChild(clusterBuilder.build());
205 attributesContainer.withChild(clusterListCont.build());
208 return attributesContainer.build();
211 // Attributes when reflecting a route
212 ContainerNode reflectedAttributes(final ContainerNode attributes, final Ipv4Address originatorId, final ClusterIdentifier clusterId) {
213 final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> attributesContainer = Builders.containerBuilder(attributes);
215 // Create a new CLUSTER_LIST builder
216 final ListNodeBuilder<Object, LeafSetEntryNode<Object>> clusterBuilder = Builders.orderedLeafSetBuilder();
217 clusterBuilder.withNodeIdentifier(this.clusterListLeaf);
219 // prepend local CLUSTER_ID
220 clusterBuilder.withChild(Builders.leafSetEntryBuilder().withNodeIdentifier(new NodeWithValue<>(
221 this.clusterQname, clusterId.getValue())).withValue(clusterId.getValue()).build());
223 // if there was a CLUSTER_LIST attribute, add all other entries
224 final Optional<NormalizedNode<?, ?>> maybeClusterList = NormalizedNodes.findNode(attributes, this.clusterListPath);
225 if (maybeClusterList.isPresent()) {
226 AttributeOperations.addOtherClusterEntries(maybeClusterList, clusterBuilder);
228 LOG.debug("Creating fresh CLUSTER_LIST attribute");
231 // Now wrap it in a container and add it to attributes
232 final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> clusterListCont = Builders.containerBuilder();
233 clusterListCont.withNodeIdentifier(this.clusterListContainer);
234 clusterListCont.withChild(clusterBuilder.build());
235 attributesContainer.withChild(clusterListCont.build());
237 // add ORIGINATOR_ID if not present
238 final Optional<NormalizedNode<?, ?>> maybeOriginatorId = NormalizedNodes.findNode(attributes, this.originatorIdPath);
239 if (!maybeOriginatorId.isPresent()) {
240 final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> originatorIDBuilder = Builders.containerBuilder();
241 originatorIDBuilder.withNodeIdentifier(this.originatorIdContainer);
242 originatorIDBuilder.withChild(ImmutableNodes.leafNode(this.originatorIdLeaf, originatorId.getValue()));
243 attributesContainer.withChild(originatorIDBuilder.build());
246 return attributesContainer.build();
249 private static void addOtherClusterEntries(final Optional<NormalizedNode<?, ?>> maybeClusterList, final ListNodeBuilder<Object,
250 LeafSetEntryNode<Object>> clb) {
251 final NormalizedNode<?, ?> clusterList = maybeClusterList.get();
252 if (clusterList instanceof LeafSetNode) {
253 for (final LeafSetEntryNode<?> n : ((LeafSetNode<?>) clusterList).getValue()) {
254 // There's no way we can safely avoid this cast
255 @SuppressWarnings("unchecked")
256 final LeafSetEntryNode<Object> child = (LeafSetEntryNode<Object>) n;
260 LOG.warn("Ignoring malformed CLUSTER_LIST {}", clusterList);
264 private boolean isTransitiveAttribute(final DataContainerChild<? extends PathArgument, ?> child) {
265 if (child.getIdentifier() instanceof AugmentationIdentifier) {
266 final AugmentationIdentifier ai = (AugmentationIdentifier) child.getIdentifier();
267 for (final QName name : ai.getPossibleChildNames()) {
268 LOG.trace("Augmented QNAME {}", name);
269 if (this.transitiveCollection.contains(name)) {
275 if (this.transitiveCollection.contains(child.getNodeType())) {
278 if (UnrecognizedAttributes.QNAME.equals(child.getNodeType())) {
279 final Optional<NormalizedNode<?, ?>> maybeTransitive = NormalizedNodes.findNode(child, this.transitiveLeaf);
280 if (maybeTransitive.isPresent()) {
281 return (Boolean) maybeTransitive.get().getValue();
287 private boolean spliceTransitives(final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> target, final ContainerNode attributes) {
288 // We want to reuse attributes as much as possible, so the result of the loop
289 // indicates whether we performed a modification. If we have not modified the
290 // attributes, we can reuse them.
292 for (final DataContainerChild<? extends PathArgument, ?> child : attributes.getValue()) {
293 if (isTransitiveAttribute(child)) {
294 target.withChild(child);
304 * Filter out all non-transitive attributes.
306 * @param attributes Input attributes
307 * @return Output attributes, transitive only.
309 ContainerNode transitiveAttributes(final ContainerNode attributes) {
310 final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> b = Builders.containerBuilder();
311 b.withNodeIdentifier(attributes.getIdentifier());
313 final boolean modified = spliceTransitives(b, attributes);
314 return modified ? b.build() : attributes;
317 LeafSetNode<?> getClusterList(final ContainerNode attributes) {
318 final Optional<NormalizedNode<?, ?>> maybeClusterList = NormalizedNodes.findNode(attributes, this.clusterListPath);
319 if (maybeClusterList.isPresent()) {
320 final NormalizedNode<?, ?> clusterList = maybeClusterList.get();
321 if (clusterList instanceof LeafSetNode) {
322 return (LeafSetNode<?>) clusterList;
325 LOG.warn("Unexpected CLUSTER_LIST node {}, ignoring it", clusterList);
331 Object getOriginatorId(final ContainerNode attributes) {
332 final Optional<NormalizedNode<?, ?>> maybeOriginatorId = NormalizedNodes.findNode(attributes, this.originatorIdPath);
333 if (!maybeOriginatorId.isPresent()) {
334 LOG.debug("No ORIGINATOR_ID present");
338 final NormalizedNode<?, ?> originatorId = maybeOriginatorId.get();
339 if (originatorId instanceof LeafNode) {
340 return originatorId.getValue();
343 LOG.warn("Unexpected ORIGINATOR_ID node {}, ignoring it", originatorId);