package org.opendaylight.protocol.bgp.rib.impl;
import static java.util.Objects.requireNonNull;
+import static org.opendaylight.protocol.bgp.rib.spi.RIBNodeIdentifiers.ADJRIBOUT_NID;
+import static org.opendaylight.protocol.bgp.rib.spi.RIBNodeIdentifiers.TABLES_NID;
import com.google.common.base.MoreObjects;
import com.google.common.base.Stopwatch;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
-import com.google.common.net.InetAddresses;
import com.google.common.util.concurrent.FluentFuture;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import java.util.stream.Collectors;
import org.checkerframework.checker.lock.qual.GuardedBy;
import org.checkerframework.checker.lock.qual.Holding;
+import org.eclipse.jdt.annotation.NonNull;
import org.opendaylight.mdsal.binding.api.RpcProviderService;
-import org.opendaylight.mdsal.binding.api.Transaction;
-import org.opendaylight.mdsal.binding.api.TransactionChain;
import org.opendaylight.mdsal.common.api.CommitInfo;
import org.opendaylight.mdsal.dom.api.DOMDataTreeTransaction;
import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
+import org.opendaylight.mdsal.dom.api.DOMTransactionChainListener;
import org.opendaylight.protocol.bgp.openconfig.spi.BGPTableTypeRegistryConsumer;
import org.opendaylight.protocol.bgp.parser.BGPDocumentedException;
import org.opendaylight.protocol.bgp.parser.BGPError;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.BgpTableType;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.RouteRefresh;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.SendReceive;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.attributes.reach.MpReachNlri;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.attributes.reach.MpReachNlriBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.attributes.reach.mp.reach.nlri.AdvertizedRoutesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.attributes.unreach.MpUnreachNlri;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.attributes.unreach.MpUnreachNlriBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.attributes.unreach.mp.unreach.nlri.WithdrawnRoutesBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.mp.capabilities.GracefulRestartCapability;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.mp.capabilities.add.path.capability.AddressFamilies;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.MpReachNlri;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.MpReachNlriBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.MpUnreachNlri;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.MpUnreachNlriBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.mp.reach.nlri.AdvertizedRoutesBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.mp.unreach.nlri.WithdrawnRoutesBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.peer.rpc.rev180329.BgpPeerRpcService;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.PeerRole;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.rib.PeerKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.rib.peer.AdjRibOut;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.Tables;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.TablesKey;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev200120.AddressFamily;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev200120.ClusterIdentifier;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev200120.UnicastSubsequentAddressFamily;
import org.opendaylight.yangtools.concepts.ObjectRegistration;
import org.opendaylight.yangtools.concepts.Registration;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
import org.opendaylight.yangtools.yang.binding.Notification;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private static final TablesKey IPV4_UCAST_TABLE_KEY = new TablesKey(Ipv4AddressFamily.class,
UnicastSubsequentAddressFamily.class);
- private ImmutableSet<TablesKey> tables = ImmutableSet.of();
private final RIB rib;
+
+ // FIXME: Alright, this right here is a ton of state which has intertwined initialization and dependencies Split
+ // these out into separate behavior objects. This also has relationship with state in AbstractPeer -- which
+ // hints at an obvious layer of indirection. Yeah, yeah, we can always add one of those, but the point
+ // is that this class is a mutable meeting point, whereas the behaviour has captured invariants.
+ private final LoadingCache<NodeIdentifierWithPredicates, YangInstanceIdentifier> tablesIId =
+ CacheBuilder.newBuilder().build(new CacheLoader<NodeIdentifierWithPredicates, YangInstanceIdentifier>() {
+ @Override
+ public YangInstanceIdentifier load(final NodeIdentifierWithPredicates key) {
+ return peerRibOutIId.node(TABLES_NID).node(key).toOptimized();
+ }
+ });
+
+ private ImmutableSet<TablesKey> tables = ImmutableSet.of();
private final Map<TablesKey, AdjRibOutListener> adjRibOutListenerSet = new HashMap<>();
private final List<RouteTarget> rtMemberships = new ArrayList<>();
private final RpcProviderService rpcRegistry;
private final BGPTableTypeRegistryConsumer tableTypeRegistry;
private final BgpPeer bgpPeer;
- private InstanceIdentifier<AdjRibOut> peerRibOutIId;
- private KeyedInstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib
- .rev180329.bgp.rib.rib.Peer, PeerKey> peerIId;
+
+ // FIXME: This should be a constant co-located with ApplicationPeer.peerId
+ private YangInstanceIdentifier peerRibOutIId;
@GuardedBy("this")
private Registration trackerRegistration;
- private final LoadingCache<TablesKey, KeyedInstanceIdentifier<Tables, TablesKey>> tablesIId
- = CacheBuilder.newBuilder()
- .build(new CacheLoader<TablesKey, KeyedInstanceIdentifier<Tables, TablesKey>>() {
- @Override
- public KeyedInstanceIdentifier<Tables, TablesKey> load(final TablesKey tablesKey) {
- return BGPPeer.this.peerRibOutIId.child(Tables.class, tablesKey);
- }
- });
@GuardedBy("this")
private BGPSession currentSession;
private EffectiveRibInWriter effRibInWriter;
private ObjectRegistration<BgpPeerRpcService> rpcRegistration;
private Map<TablesKey, SendReceive> addPathTableMaps = Collections.emptyMap();
+ // FIXME: This should be a constant co-located with ApplicationPeer.peerId
private YangInstanceIdentifier peerPath;
+ // FIXME: This is for supportsTable() -- a trivial behavior thing, where 'peer-down' type states always return false
private boolean sessionUp;
private boolean llgrSupport;
private Stopwatch peerRestartStopwatch;
.collect(Collectors.toList());
final MpReachNlriBuilder b = new MpReachNlriBuilder().setAfi(Ipv4AddressFamily.class).setSafi(
UnicastSubsequentAddressFamily.class).setAdvertizedRoutes(
- new AdvertizedRoutesBuilder().setDestinationType(
+ new AdvertizedRoutesBuilder().setDestinationType(
new DestinationIpv4CaseBuilder().setDestinationIpv4(
new DestinationIpv4Builder().setIpv4Prefixes(prefixes).build()).build()).build());
if (message.getAttributes() != null) {
.setWithdrawnRoutes(new WithdrawnRoutesBuilder().setDestinationType(new org.opendaylight.yang.gen.v1
.urn.opendaylight.params.xml.ns.yang.bgp.inet.rev180329.update.attributes.mp.unreach.nlri
.withdrawn.routes.destination.type.DestinationIpv4CaseBuilder().setDestinationIpv4(
- new DestinationIpv4Builder().setIpv4Prefixes(prefixes).build()).build()).build()).build();
+ new DestinationIpv4Builder().setIpv4Prefixes(prefixes).build()).build()).build()).build();
}
private static Map<TablesKey, SendReceive> mapTableTypesFamilies(final List<AddressFamilies> addPathTablesType) {
- return ImmutableMap.copyOf(addPathTablesType.stream().collect(Collectors.toMap(af -> new TablesKey(af.getAfi(),
- af.getSafi()), BgpAddPathTableType::getSendReceive)));
+ return addPathTablesType.stream().collect(ImmutableMap.toImmutableMap(
+ af -> new TablesKey(af.getAfi(), af.getSafi()), BgpAddPathTableType::getSendReceive));
}
public synchronized void instantiateServiceInstance() {
createDomChain();
- this.ribWriter = AdjRibInWriter.create(this.rib.getYangRibId(), this.peerRole, this);
+ this.ribWriter = AdjRibInWriter.create(this.rib.getYangRibId(), getRole(), this);
setActive(true);
}
@Override
public synchronized FluentFuture<? extends CommitInfo> close() {
- final FluentFuture<? extends CommitInfo> future = releaseConnection();
+ final FluentFuture<? extends CommitInfo> future = releaseConnection(true);
closeDomChain();
setActive(false);
return future;
private void checkMandatoryAttributesPresence(final Update message) throws BGPDocumentedException {
if (MessageUtil.isAnyNlriPresent(message)) {
final Attributes attrs = message.getAttributes();
- if (this.peerRole == PeerRole.Ibgp && (attrs == null || attrs.getLocalPref() == null)) {
+ if (getRole() == PeerRole.Ibgp && (attrs == null || attrs.getLocalPref() == null)) {
throw new BGPDocumentedException(BGPError.MANDATORY_ATTR_MISSING_MSG + "LOCAL_PREF",
BGPError.WELL_KNOWN_ATTR_MISSING,
new byte[]{LocalPreferenceAttributeParser.TYPE});
public synchronized void onSessionUp(final BGPSession session) {
this.currentSession = session;
this.sessionUp = true;
- this.bindingChain = this.rib.createPeerChain(this);
+
+ this.ribOutChain = this.rib.createPeerDOMChain(new DOMTransactionChainListener() {
+ @Override
+ public void onTransactionChainSuccessful(final DOMTransactionChain chain) {
+ LOG.debug("RibOut transaction chain {} successful.", chain);
+ }
+
+ @Override
+ public void onTransactionChainFailed(final DOMTransactionChain chain,
+ final DOMDataTreeTransaction transaction, final Throwable cause) {
+ onRibOutChainFailed(cause);
+ }
+ });
+
if (this.currentSession instanceof BGPSessionStateProvider) {
((BGPSessionStateProvider) this.currentSession).registerMessagesCounter(this);
}
final List<AddressFamilies> addPathTablesType = session.getAdvertisedAddPathTableTypes();
final Set<BgpTableType> advertizedTableTypes = session.getAdvertisedTableTypes();
- LOG.info("Session with peer {} went up with tables {} and Add Path tables {}", this.name,
+ LOG.info("Session with peer {} went up with tables {} and Add Path tables {}", getName(),
advertizedTableTypes, addPathTablesType);
final Set<TablesKey> setTables = advertizedTableTypes.stream().map(t -> new TablesKey(t.getAfi(), t.getSafi()))
.collect(Collectors.toSet());
addBgp4Support();
}
if (!isRestartingGracefully()) {
- this.rawIdentifier = InetAddresses.forString(session.getBgpId().getValue()).getAddress();
this.peerId = RouterIds.createPeerId(session.getBgpId());
- this.peerIId = getInstanceIdentifier().child(org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns
- .yang.bgp.rib.rev180329.bgp.rib.rib.Peer.class, new PeerKey(this.peerId));
- this.peerPath = createPeerPath();
- this.peerRibOutIId = peerIId.child(AdjRibOut.class);
+
+ final KeyedInstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib
+ .rev180329.bgp.rib.rib.Peer, PeerKey> peerIId = getInstanceIdentifier().child(org.opendaylight.yang.gen
+ .v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.rib.Peer.class,
+ new PeerKey(this.peerId));
+ this.peerPath = createPeerPath(this.peerId);
+ this.peerRibOutIId = peerPath.node(ADJRIBOUT_NID);
this.trackerRegistration = this.rib.getPeerTracker().registerPeer(this);
createEffRibInWriter();
registerPrefixesCounters(this.effRibInWriter, this.effRibInWriter);
} else {
forwardingTables = advertisedTables.values().stream()
.filter(table -> table.getAfiFlags() != null)
- .filter(table -> table.getAfiFlags().isForwardingState())
+ .filter(table -> table.getAfiFlags().getForwardingState())
.map(table -> new TablesKey(table.getAfi(), table.getSafi()))
.collect(Collectors.toSet());
}
@Holding("this")
private void createAdjRibOutListener(final TablesKey key, final boolean mpSupport) {
- final RIBSupport<?, ?, ?, ?> ribSupport = this.rib.getRibSupportContext().getRIBSupport(key);
+ final RIBSupport<?, ?> ribSupport = this.rib.getRibSupportContext().getRIBSupport(key);
// not particularly nice
if (ribSupport != null && this.currentSession instanceof BGPSessionImpl) {
@Override
public synchronized void onSessionDown(final BGPSession session, final Exception exc) {
if (exc.getMessage().equals(BGPSessionImpl.END_OF_INPUT)) {
- LOG.info("Session with peer {} went down", this.name);
+ LOG.info("Session with peer {} went down", getName());
} else {
- LOG.info("Session with peer {} went down", this.name, exc);
+ LOG.info("Session with peer {} went down", getName(), exc);
}
releaseConnectionGracefully();
}
@Override
public synchronized void onSessionTerminated(final BGPSession session, final BGPTerminationReason cause) {
- LOG.info("Session with peer {} terminated: {}", this.name, cause);
+ LOG.info("Session with peer {} terminated: {}", getName(), cause);
releaseConnectionGracefully();
}
@Override
public String toString() {
- return MoreObjects.toStringHelper(this).add("name", this.name).add("tables", this.tables).toString();
+ return MoreObjects.toStringHelper(this).add("name", getName()).add("tables", this.tables).toString();
}
@Override
public synchronized FluentFuture<? extends CommitInfo> releaseConnection() {
+ return releaseConnection(true);
+ }
+
+ /**
+ * On transaction chain failure, we don't want to wait for future.
+ *
+ * @param isWaitForSubmitted if true, wait for submitted future before closing binding chain. if false, don't wait.
+ */
+ @Holding("this")
+ private @NonNull FluentFuture<? extends CommitInfo> releaseConnection(final boolean isWaitForSubmitted) {
LOG.info("Closing session with peer");
this.sessionUp = false;
this.adjRibOutListenerSet.values().forEach(AdjRibOutListener::close);
this.adjRibOutListenerSet.clear();
final FluentFuture<? extends CommitInfo> future;
- if (!isRestartingGracefully()) {
- future = terminateConnection();
- } else {
+ // FIXME: this is a typical example of something which should be handled by a behavior into which we have
+ // transitioned way before this method is called. This really begs to be an abstract base class with
+ // a 'clearTables' or similar callout
+ if (isRestartingGracefully()) {
final Set<TablesKey> gracefulTables = getGracefulTables();
this.ribWriter.storeStaleRoutes(gracefulTables);
future = this.ribWriter.clearTables(Sets.difference(this.tables, gracefulTables));
this.peerRestartStopwatch = Stopwatch.createStarted();
handleRestartTimer();
}
+ } else {
+ future = terminateConnection();
}
- releaseBindingChain();
+ releaseRibOutChain(isWaitForSubmitted);
closeSession();
return future;
TimeUnit.NANOSECONDS);
}
+ @Holding("this")
private void releaseConnectionGracefully() {
if (getPeerRestartTime() > 0) {
setRestartingState();
}
- releaseConnection();
+ releaseConnection(true);
}
@SuppressFBWarnings("IS2_INCONSISTENT_SYNC")
@Override
public boolean supportsTable(final TablesKey tableKey) {
- return this.sessionUp && getAfiSafisAdvertized().contains(tableKey);
+ return this.sessionUp && getAfiSafisAdvertized().contains(tableKey) && this.tables.contains(tableKey);
}
@Override
- public KeyedInstanceIdentifier<Tables, TablesKey> getRibOutIId(final TablesKey tablesKey) {
- return this.tablesIId.getUnchecked(tablesKey);
+ public YangInstanceIdentifier getRibOutIId(final NodeIdentifierWithPredicates tablekey) {
+ return this.tablesIId.getUnchecked(tablekey);
}
@Override
public synchronized void onTransactionChainFailed(final DOMTransactionChain chain,
final DOMDataTreeTransaction transaction, final Throwable cause) {
LOG.error("Transaction domChain failed.", cause);
- releaseConnection();
+ releaseConnection(true);
}
- @Override
- public synchronized void onTransactionChainFailed(final TransactionChain chain, final Transaction transaction,
- final Throwable cause) {
- LOG.error("Transaction domChain failed.", cause);
- releaseConnection();
+ @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD",
+ justification = "https://github.com/spotbugs/spotbugs/issues/811")
+ private synchronized void onRibOutChainFailed(final Throwable cause) {
+ LOG.error("RibOut transaction chain failed.", cause);
+ releaseConnection(false);
}
@Override
setGracefulPreferences(true, tablesToPreserve);
this.currentSelectionDeferralTimerSeconds = selectionDeferralTimerSeconds;
setLocalRestartingState(true);
- return releaseConnection();
+ return releaseConnection(true);
}
@Override