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 java.util.Collection;
16 import java.util.Iterator;
17 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
18 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.path.attributes.AsPath;
19 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.path.attributes.ClusterId;
20 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.path.attributes.OriginatorId;
21 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.path.attributes.as.path.Segments;
22 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.ClusterIdentifier;
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.as.path.segment.CSegment;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.as.path.segment.c.segment.a.list._case.AList;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.as.path.segment.c.segment.a.list._case.a.list.AsSequence;
26 import org.opendaylight.yangtools.yang.common.QName;
27 import org.opendaylight.yangtools.yang.common.QNameModule;
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.ChoiceNode;
31 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
32 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
33 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
34 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
35 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
36 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
37 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes;
38 import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
39 import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
40 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
41 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
42 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
43 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
44 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
45 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.ListNodeBuilder;
46 import org.slf4j.Logger;
47 import org.slf4j.LoggerFactory;
50 * Utility class for working with route attributes in binding independent form. An instance
51 * is always bound to a certain namespace, so it maintains cached attribute identifiers.
53 final class AttributeOperations {
54 private static final Logger LOG = LoggerFactory.getLogger(AttributeOperations.class);
55 private static final LoadingCache<QNameModule, AttributeOperations> ATTRIBUTES_CACHE = CacheBuilder.newBuilder().weakKeys().weakValues().build(
56 new CacheLoader<QNameModule, AttributeOperations>() {
58 public AttributeOperations load(final QNameModule key) throws Exception {
59 return new AttributeOperations(key);
63 private final Iterable<PathArgument> originatorIdPath;
64 private final Iterable<PathArgument> clusterListPath;
65 private final NodeIdentifier originatorIdContainer;
66 private final NodeIdentifier originatorIdLeaf;
67 private final NodeIdentifier clusterListContainer;
68 private final NodeIdentifier clusterListLeaf;
69 private final NodeIdentifier asPathContainer;
70 private final NodeIdentifier asPathSegments;
71 private final NodeIdentifier asPathChoice;
72 private final NodeIdentifier asPathList;
73 private final NodeIdentifier asPathSequence;
74 private final NodeIdentifier asPathId;
76 private AttributeOperations(final QNameModule namespace) {
77 this.asPathContainer = new NodeIdentifier(QName.cachedReference(QName.create(namespace, AsPath.QNAME.getLocalName())));
78 this.asPathSegments = new NodeIdentifier(QName.cachedReference(QName.create(namespace, Segments.QNAME.getLocalName())));
79 this.asPathChoice = new NodeIdentifier(QName.cachedReference(QName.create(namespace, CSegment.QNAME.getLocalName())));
80 this.asPathList = new NodeIdentifier(QName.cachedReference(QName.create(namespace, AList.QNAME.getLocalName())));
81 this.asPathSequence = new NodeIdentifier(QName.cachedReference(QName.create(namespace, AsSequence.QNAME.getLocalName())));
82 this.asPathId = new NodeIdentifier(QName.cachedReference(QName.create(namespace, "as")));
84 this.clusterListContainer = new NodeIdentifier(QName.cachedReference(QName.create(namespace, ClusterId.QNAME.getLocalName())));
85 this.clusterListLeaf = new NodeIdentifier(QName.cachedReference(QName.create(namespace, "cluster")));
86 this.clusterListPath = ImmutableList.<PathArgument>of(clusterListContainer, clusterListLeaf);
87 this.originatorIdContainer = new NodeIdentifier(QName.cachedReference(QName.create(namespace, OriginatorId.QNAME.getLocalName())));
88 this.originatorIdLeaf = new NodeIdentifier(QName.cachedReference(QName.create(namespace, "originator")));
89 this.originatorIdPath = ImmutableList.<PathArgument>of(originatorIdContainer, originatorIdLeaf);
92 static AttributeOperations getInstance(final ContainerNode attributes) {
93 return ATTRIBUTES_CACHE.getUnchecked(QNameModule.cachedReference(attributes.getNodeType().getModule()));
96 private Collection<UnkeyedListEntryNode> reusableSequence(final UnkeyedListEntryNode segment) {
97 final Optional<NormalizedNode<?, ?>> maybeAsSequence = NormalizedNodes.findNode(segment, asPathChoice, asPathList, asPathSequence);
98 if (maybeAsSequence.isPresent()) {
99 final UnkeyedListNode asList = (UnkeyedListNode) maybeAsSequence.get();
100 if (asList.getSize() < 255) {
101 return asList.getValue();
108 ContainerNode exportedAttributes(final ContainerNode attributes, final Long localAs) {
109 final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> b = Builders.containerBuilder();
110 b.withNodeIdentifier(attributes.getIdentifier());
112 // First filter out non-transitive attributes
113 // FIXME: removes MULTI_EXIT_DISC, too.
114 spliceTransitives(b, attributes);
117 * This is very ugly, as our AS_PATH model is needlessly complex. The top-level container contains a list
118 * of segments, each of which is a choice containing another list. Both are unkeyed lists, so we need to
119 * perform a wholesale replace.
121 final CollectionNodeBuilder<UnkeyedListEntryNode, UnkeyedListNode> sb = Builders.unkeyedListBuilder();
122 sb.withNodeIdentifier(asPathSegments);
124 final Optional<NormalizedNode<?, ?>> maybeOldAsSegments = NormalizedNodes.findNode(attributes, asPathContainer, asPathSegments);
125 if (maybeOldAsSegments.isPresent()) {
126 // Builder of inner list
127 final CollectionNodeBuilder<UnkeyedListEntryNode, UnkeyedListNode> ilb = Builders.unkeyedListBuilder();
128 ilb.withNodeIdentifier(asPathSegments);
129 ilb.withChild(Builders.unkeyedListEntryBuilder().withNodeIdentifier(asPathSequence).withChild(ImmutableNodes.leafNode(asPathId, localAs)).build());
132 * We need to check the first entry in the outer list, to check if the choice is a-list. If it is and its
133 * total number of elements is less than 255, we need to modify that one. Otherwise we need to create a
136 final Iterator<UnkeyedListEntryNode> oldAsSegments = ((UnkeyedListNode) maybeOldAsSegments.get()).getValue().iterator();
137 final UnkeyedListEntryNode firstSegment = oldAsSegments.next();
138 final Collection<UnkeyedListEntryNode> reusable = reusableSequence(firstSegment);
139 if (reusable != null) {
140 for (UnkeyedListEntryNode child : reusable) {
141 ilb.withChild(child);
145 // Builder of inner container
146 final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> icb = Builders.containerBuilder();
147 icb.withNodeIdentifier(asPathList);
148 icb.withChild(ilb.build());
150 // Choice inside the outer list
151 final DataContainerNodeBuilder<NodeIdentifier, ChoiceNode> ocb = Builders.choiceBuilder();
152 ocb.withNodeIdentifier(asPathChoice);
153 ocb.withChild(icb.build());
155 // Add the new first segment
156 sb.withChild(Builders.unkeyedListEntryBuilder().withNodeIdentifier(asPathSegments).withChild(ocb.build()).build());
158 // If we did not merge into the original segment, add it
159 if (reusable == null) {
160 sb.withChild(firstSegment);
163 // Add all subsequent segments
164 while (oldAsSegments.hasNext()) {
165 sb.withChild(oldAsSegments.next());
168 // Segments are completely empty, create a completely new AS_PATH container with
170 sb.withChild(Builders.unkeyedListEntryBuilder().withNodeIdentifier(asPathSegments).withChild(
171 Builders.choiceBuilder().withNodeIdentifier(asPathChoice).withChild(
172 Builders.containerBuilder().withNodeIdentifier(asPathList).withChild(
173 Builders.unkeyedListBuilder().withNodeIdentifier(asPathSequence).withChild(
174 Builders.unkeyedListEntryBuilder().withNodeIdentifier(asPathSequence).withChild(
175 ImmutableNodes.leafNode(asPathId, localAs)).build()).build()).build()).build()).build());
179 b.withChild(Builders.containerBuilder().withNodeIdentifier(asPathContainer).withChild(sb.build()).build());
184 // Attributes when reflecting a route
185 ContainerNode reflectedAttributes(final ContainerNode attributes, final Ipv4Address originatorId, final ClusterIdentifier clusterId) {
186 final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> b = Builders.containerBuilder(attributes);
188 // Create a new CLUSTER_LIST builder
189 final ListNodeBuilder<Object, LeafSetEntryNode<Object>> clb = Builders.leafSetBuilder();
190 clb.withNodeIdentifier(clusterListLeaf);
192 // prepend local CLUSTER_ID
193 clb.withChild(Builders.leafSetEntryBuilder().withValue(clusterId).build());
195 // if there was a CLUSTER_LIST attribute, add all other entries
196 final Optional<NormalizedNode<?, ?>> maybeClusterList = NormalizedNodes.findNode(attributes, clusterListPath);
197 if (maybeClusterList.isPresent()) {
198 final NormalizedNode<?, ?> clusterList = maybeClusterList.get();
199 if (clusterList instanceof LeafSetNode) {
200 for (LeafSetEntryNode<?> n : ((LeafSetNode<?>)clusterList).getValue()) {
201 // There's no way we can safely avoid this cast
202 @SuppressWarnings("unchecked")
203 final LeafSetEntryNode<Object> child = (LeafSetEntryNode<Object>)n;
207 LOG.warn("Ignoring malformed CLUSTER_LIST {}", clusterList);
210 LOG.debug("Creating fresh CLUSTER_LIST attribute");
213 // Now wrap it in a container and add it to attributes
214 final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> cb = Builders.containerBuilder();
215 cb.withNodeIdentifier(clusterListContainer);
216 cb.withChild(clb.build());
217 b.withChild(cb.build());
219 // add ORIGINATOR_ID if not present
220 final Optional<NormalizedNode<?, ?>> maybeOriginatorId = NormalizedNodes.findNode(attributes, originatorIdPath);
221 if (!maybeOriginatorId.isPresent()) {
222 final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> oib = Builders.containerBuilder();
223 oib.withNodeIdentifier(originatorIdContainer);
224 oib.withChild(ImmutableNodes.leafNode(originatorIdLeaf, originatorId.getValue()));
225 b.withChild(oib.build());
231 private boolean isTransitiveAttribute(final DataContainerChild<? extends PathArgument, ?> child) {
232 // FIXME: perform a filtering operation
236 private boolean spliceTransitives(final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> target, final ContainerNode attributes) {
237 // We want to reuse attributes as much as possible, so the result of the loop
238 // indicates whether we performed a modification. If we have not modified the
239 // attributes, we can reuse them.
241 for (DataContainerChild<? extends PathArgument, ?> child : attributes.getValue()) {
242 if (isTransitiveAttribute(child)) {
243 target.withChild(child);
253 * Filter out all non-transitive attributes.
255 * @param attributes Input attributes
256 * @return Output attributes, transitive only.
258 ContainerNode transitiveAttributes(final ContainerNode attributes) {
259 final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> b = Builders.containerBuilder();
260 b.withNodeIdentifier(attributes.getIdentifier());
262 boolean modified = spliceTransitives(b, attributes);
263 return modified ? b.build() : attributes;
266 LeafSetNode<?> getClusterList(final ContainerNode attributes) {
267 final Optional<NormalizedNode<?, ?>> maybeClusterList = NormalizedNodes.findNode(attributes, clusterListPath);
268 if (maybeClusterList.isPresent()) {
269 final NormalizedNode<?, ?> clusterList = maybeClusterList.get();
270 if (clusterList instanceof LeafSetNode) {
271 return (LeafSetNode<?>) clusterList;
274 LOG.warn("Unexpected CLUSTER_LIST node {}, ignoring it", clusterList);
280 Object getOriginatorId(final ContainerNode attributes) {
281 final Optional<NormalizedNode<?, ?>> maybeOriginatorId = NormalizedNodes.findNode(attributes, originatorIdPath);
282 if (!maybeOriginatorId.isPresent()) {
283 LOG.debug("No ORIGINATOR_ID present");
287 final NormalizedNode<?, ?> originatorId = maybeOriginatorId.get();
288 if (originatorId instanceof LeafNode) {
289 return ((LeafNode<?>)originatorId).getValue();
292 LOG.warn("Unexpected ORIGINATOR_ID node {}, ignoring it", originatorId);