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