BUG-2383 : make LocalRIB count on complexity of the routes
[bgpcep.git] / bgp / rib-impl / src / main / java / org / opendaylight / protocol / bgp / rib / impl / AbstractRouteEntry.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.primitives.UnsignedInteger;
11 import java.util.Objects;
12 import javax.annotation.concurrent.NotThreadSafe;
13 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
14 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
15 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
16 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
17 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
18 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes;
19 import org.slf4j.Logger;
20 import org.slf4j.LoggerFactory;
21
22 /**
23  * A single route entry inside a route table. Maintains the attributes of
24  * from all contributing peers. The information is stored in arrays with a
25  * shared map of offsets for peers to allow lookups. This is needed to
26  * maintain low memory overhead in face of large number of routes and peers,
27  * where individual object overhead becomes the dominating factor.
28  */
29 @NotThreadSafe
30 abstract class AbstractRouteEntry {
31
32     private static final Logger LOG = LoggerFactory.getLogger(AbstractRouteEntry.class);
33
34     private static final ContainerNode[] EMPTY_ATTRIBUTES = new ContainerNode[0];
35
36     private OffsetMap offsets = OffsetMap.EMPTY;
37     private ContainerNode[] values = EMPTY_ATTRIBUTES;
38     private BestPath bestPath;
39
40     private int addRoute(final UnsignedInteger routerId, final ContainerNode attributes) {
41         int offset = this.offsets.offsetOf(routerId);
42         if (offset < 0) {
43             final OffsetMap newOffsets = this.offsets.with(routerId);
44             offset = newOffsets.offsetOf(routerId);
45
46             final ContainerNode[] newAttributes = newOffsets.expand(this.offsets, this.values, offset);
47             this.values = newAttributes;
48             this.offsets = newOffsets;
49         }
50
51         this.offsets.setValue(this.values, offset, attributes);
52         LOG.trace("Added route from {} attributes {}", routerId, attributes);
53         return offset;
54     }
55
56     protected int addRoute(final UnsignedInteger routerId, final NodeIdentifier attributesIdentifier, final NormalizedNode<?, ?> data) {
57         LOG.trace("Find {} in {}", attributesIdentifier, data);
58         final ContainerNode advertisedAttrs = (ContainerNode) NormalizedNodes.findNode(data, attributesIdentifier).orNull();
59         return addRoute(routerId, advertisedAttrs);
60     }
61
62     // Indicates whether this was the last route
63     protected final boolean removeRoute(final int offset) {
64         if (this.offsets.size() != 1) {
65             // FIXME: actually shrink the array
66             this.offsets.setValue(this.values, offset, null);
67             return false;
68         } else {
69             return true;
70         }
71     }
72
73     // Indicates whether best has changed
74     final boolean selectBest(final long localAs) {
75         /*
76          * FIXME: optimize flaps by making sure we consider stability of currently-selected route.
77          */
78         final BestPathSelector selector = new BestPathSelector(localAs);
79
80         // Select the best route.
81         for (int i = 0; i < this.offsets.size(); ++i) {
82             final UnsignedInteger routerId = this.offsets.getRouterId(i);
83             final ContainerNode attributes = this.offsets.getValue(this.values, i);
84             LOG.trace("Processing router id {} attributes {}", routerId, attributes);
85             selector.processPath(routerId, attributes);
86         }
87
88         // Get the newly-selected best path.
89         final BestPath newBestPath = selector.result();
90         // FIXME: run deeper comparison
91         final boolean ret = !Objects.equals(this.bestPath, newBestPath);
92         LOG.trace("Previous best {}, current best {}, result {}", this.bestPath, newBestPath, ret);
93         this.bestPath = newBestPath;
94         return ret;
95     }
96
97     final ContainerNode attributes() {
98         return this.bestPath.getState().getAttributes();
99     }
100
101     protected final OffsetMap getOffsets() {
102         return this.offsets;
103     }
104
105     protected final UnsignedInteger getBestRouterId() {
106         return this.bestPath.getRouterId();
107     }
108
109     abstract boolean removeRoute(final UnsignedInteger routerId);
110     abstract MapEntryNode createValue(PathArgument routeId);
111 }