* terms of the Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
-
package org.opendaylight.protocol.bmp.impl.app;
import static java.util.Objects.requireNonNull;
import com.google.common.base.Preconditions;
import com.google.common.net.InetAddresses;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.MoreExecutors;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
-import javax.annotation.concurrent.GuardedBy;
-import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction;
-import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
-import org.opendaylight.controller.md.sal.common.api.data.TransactionChain;
-import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
-import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
-import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
-import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
-import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
+import java.util.concurrent.ExecutionException;
+import org.checkerframework.checker.lock.qual.GuardedBy;
+import org.checkerframework.checker.lock.qual.Holding;
import org.opendaylight.mdsal.binding.dom.codec.api.BindingCodecTree;
+import org.opendaylight.mdsal.common.api.CommitInfo;
+import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
+import org.opendaylight.mdsal.dom.api.DOMDataBroker;
+import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteTransaction;
+import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
import org.opendaylight.protocol.bgp.rib.spi.RIBExtensionConsumerContext;
import org.opendaylight.protocol.bmp.api.BmpSession;
import org.opendaylight.protocol.bmp.impl.spi.BmpRouter;
import org.opendaylight.protocol.bmp.impl.spi.BmpRouterPeer;
import org.opendaylight.protocol.util.Ipv4Util;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev171207.OpenMessage;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev171207.PeerId;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bmp.message.rev171207.InitiationMessage;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bmp.message.rev171207.PeerDownNotification;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bmp.message.rev171207.PeerHeader;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bmp.message.rev171207.PeerUpNotification;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bmp.message.rev171207.string.informations.StringInformation;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bmp.monitor.rev171207.RouterId;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bmp.monitor.rev171207.peers.Peer;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bmp.monitor.rev171207.routers.Router;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev200120.OpenMessage;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.PeerId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bmp.message.rev200120.InitiationMessage;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bmp.message.rev200120.PeerDownNotification;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bmp.message.rev200120.PeerHeader;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bmp.message.rev200120.PeerUpNotification;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bmp.message.rev200120.string.informations.StringInformation;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bmp.monitor.rev200120.RouterId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bmp.monitor.rev200120.peers.Peer;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bmp.monitor.rev200120.routers.Router;
import org.opendaylight.yangtools.yang.binding.Notification;
+import org.opendaylight.yangtools.yang.common.Empty;
import org.opendaylight.yangtools.yang.common.QName;
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;
-public class BmpRouterImpl implements BmpRouter, TransactionChainListener {
+public final class BmpRouterImpl implements BmpRouter, FutureCallback<Empty> {
private static final Logger LOG = LoggerFactory.getLogger(BmpRouterImpl.class);
private BmpSession session;
private RouterId routerId;
private String routerIp;
+ @GuardedBy("this")
private YangInstanceIdentifier routerYangIId;
+ @GuardedBy("this")
private YangInstanceIdentifier peersYangIId;
public BmpRouterImpl(final RouterSessionManager sessionManager) {
this.sessionManager = requireNonNull(sessionManager);
- this.domDataBroker = sessionManager.getDomDataBroker();
- this.domTxChain = this.domDataBroker.createTransactionChain(this);
- this.extensions = sessionManager.getExtensions();
- this.tree = sessionManager.getCodecTree();
+ domDataBroker = sessionManager.getDomDataBroker();
+ domTxChain = domDataBroker.createMergingTransactionChain();
+ domTxChain.addCallback(this);
+ extensions = sessionManager.getExtensions();
+ tree = sessionManager.getCodecTree();
}
@Override
- public void onSessionUp(final BmpSession session) {
- this.session = session;
- this.routerIp = InetAddresses.toAddrString(this.session.getRemoteAddress());
- this.routerId = new RouterId(Ipv4Util.getIpAddress(this.session.getRemoteAddress()));
+ public synchronized void onSessionUp(final BmpSession psession) {
+ session = psession;
+ routerIp = InetAddresses.toAddrString(session.getRemoteAddress());
+ routerId = new RouterId(Ipv4Util.getIpAddress(session.getRemoteAddress()));
// check if this session is redundant
- if (!this.sessionManager.addSessionListener(this)) {
+ if (!sessionManager.addSessionListener(this)) {
LOG.warn("Redundant BMP session with remote router {} ({}) detected. This BMP session will be abandoned.",
- this.routerIp, this.session);
- this.close();
+ routerIp, session);
+ close();
} else {
- this.routerYangIId = YangInstanceIdentifier.builder(this.sessionManager.getRoutersYangIId())
- .nodeWithKey(Router.QNAME, ROUTER_ID_QNAME, this.routerIp).build();
- this.peersYangIId = YangInstanceIdentifier.builder(this.routerYangIId).node(Peer.QNAME).build();
+ routerYangIId = YangInstanceIdentifier.builder(sessionManager.getRoutersYangIId())
+ .nodeWithKey(Router.QNAME, ROUTER_ID_QNAME, routerIp).build();
+ peersYangIId = YangInstanceIdentifier.builder(routerYangIId).node(Peer.QNAME).build();
createRouterEntry();
- LOG.info("BMP session with remote router {} ({}) is up now.", this.routerIp, this.session);
+ LOG.info("BMP session with remote router {} ({}) is up now.", routerIp, session);
}
}
@Override
- public void onSessionDown(final Exception e) {
+ public synchronized void onSessionDown(final Exception exception) {
// we want to tear down as we want to do clean up like closing the transaction chain, etc.
// even when datastore is not writable (routerYangIId == null / redundant session)
tearDown();
}
@Override
- public void onMessage(final Notification message) {
+ public void onMessage(final Notification<?> message) {
if (message instanceof InitiationMessage) {
onInitiate((InitiationMessage) message);
} else if (message instanceof PeerUpNotification) {
}
@Override
- public RouterId getRouterId() {
- return this.routerId;
+ public synchronized RouterId getRouterId() {
+ return routerId;
}
@Override
+ @SuppressWarnings("checkstyle:IllegalCatch")
public synchronized void close() {
- if (this.session != null) {
+ if (session != null) {
try {
- this.session.close();
- } catch (final Exception e) {
- LOG.error("Fail to close session.", e);
+ session.close();
+ } catch (final Exception exc) {
+ LOG.error("Fail to close session.", exc);
}
}
}
- @GuardedBy("this")
+ @Holding("this")
+ @SuppressWarnings("checkstyle:IllegalCatch")
private synchronized void tearDown() {
// the session has been teared down before
- if (this.session == null) {
+ if (session == null) {
return;
}
// we want to display remote router's IP here, as sometimes this.session.close() is already
// invoked before tearDown(), and session channel is null in this case, which leads to unuseful
// log information
- LOG.info("BMP Session with remote router {} ({}) went down.", this.routerIp, this.session);
- this.session = null;
- final Iterator<BmpRouterPeer> it = this.peers.values().iterator();
+ LOG.info("BMP Session with remote router {} ({}) went down.", routerIp, session);
+ session = null;
+ final Iterator<BmpRouterPeer> it = peers.values().iterator();
try {
while (it.hasNext()) {
it.next().close();
it.remove();
}
- this.domTxChain.close();
- } catch(final Exception e) {
+ domTxChain.close();
+ } catch (final Exception e) {
LOG.error("Failed to properly close BMP application.", e);
} finally {
// remove session only when session is valid, otherwise
if (isDatastoreWritable()) {
try {
// it means the session was closed before it was written to datastore
- final DOMDataWriteTransaction wTx = this.domDataBroker.newWriteOnlyTransaction();
- wTx.delete(LogicalDatastoreType.OPERATIONAL, this.routerYangIId);
- wTx.submit().checkedGet();
- } catch (final TransactionCommitFailedException e) {
+ final DOMDataTreeWriteTransaction wTx = domDataBroker.newWriteOnlyTransaction();
+ wTx.delete(LogicalDatastoreType.OPERATIONAL, routerYangIId);
+ wTx.commit().get();
+ } catch (final InterruptedException | ExecutionException e) {
LOG.error("Failed to remove BMP router data from DS.", e);
}
- this.sessionManager.removeSessionListener(this);
+ sessionManager.removeSessionListener(this);
}
}
}
@Override
- public void onTransactionChainFailed(final TransactionChain<?, ?> chain, final AsyncTransaction<?, ?> transaction,
- final Throwable cause) {
+ public synchronized void onFailure(final Throwable cause) {
LOG.error("Transaction chain failed.", cause);
}
@Override
- public void onTransactionChainSuccessful(final TransactionChain<?, ?> chain) {
- LOG.debug("Transaction chain {} successfully.", chain);
+ public void onSuccess(final Empty value) {
+ LOG.debug("Transaction chain finished successfully.");
}
- private boolean isDatastoreWritable() {
- return (this.routerYangIId != null);
+ private synchronized boolean isDatastoreWritable() {
+ return routerYangIId != null;
}
private synchronized void createRouterEntry() {
Preconditions.checkState(isDatastoreWritable());
- final DOMDataWriteTransaction wTx = this.domTxChain.newWriteOnlyTransaction();
- wTx.put(LogicalDatastoreType.OPERATIONAL, this.routerYangIId,
+ final DOMDataTreeWriteTransaction wTx = domTxChain.newWriteOnlyTransaction();
+ wTx.put(LogicalDatastoreType.OPERATIONAL, routerYangIId,
Builders.mapEntryBuilder()
- .withNodeIdentifier(new NodeIdentifierWithPredicates(Router.QNAME, ROUTER_ID_QNAME, this.routerIp))
- .withChild(ImmutableNodes.leafNode(ROUTER_ID_QNAME, this.routerIp))
+ .withNodeIdentifier(NodeIdentifierWithPredicates.of(Router.QNAME, ROUTER_ID_QNAME, routerIp))
+ .withChild(ImmutableNodes.leafNode(ROUTER_ID_QNAME, routerIp))
.withChild(ImmutableNodes.leafNode(ROUTER_STATUS_QNAME, DOWN))
.withChild(ImmutableNodes.mapNodeBuilder(Peer.QNAME).build()).build());
- wTx.submit();
+ wTx.commit().addCallback(new FutureCallback<CommitInfo>() {
+ @Override
+ public void onSuccess(final CommitInfo result) {
+ LOG.trace("Successful commit");
+ }
+
+ @Override
+ public void onFailure(final Throwable trw) {
+ LOG.error("Failed commit", trw);
+ }
+ }, MoreExecutors.directExecutor());
}
private synchronized void onInitiate(final InitiationMessage initiation) {
Preconditions.checkState(isDatastoreWritable());
- final DOMDataWriteTransaction wTx = this.domTxChain.newWriteOnlyTransaction();
- wTx.merge(LogicalDatastoreType.OPERATIONAL, this.routerYangIId,
+ final DOMDataTreeWriteTransaction wTx = domTxChain.newWriteOnlyTransaction();
+ wTx.merge(LogicalDatastoreType.OPERATIONAL, routerYangIId,
Builders.mapEntryBuilder()
- .withNodeIdentifier(new NodeIdentifierWithPredicates(Router.QNAME, ROUTER_ID_QNAME, this.routerIp))
+ .withNodeIdentifier(NodeIdentifierWithPredicates.of(Router.QNAME, ROUTER_ID_QNAME, routerIp))
.withChild(ImmutableNodes.leafNode(ROUTER_NAME_QNAME, initiation.getTlvs().getNameTlv().getName()))
- .withChild(ImmutableNodes.leafNode(ROUTER_DESCRIPTION_QNAME, initiation.getTlvs().getDescriptionTlv().getDescription()))
- .withChild(ImmutableNodes.leafNode(ROUTER_INFO_QNAME, getStringInfo(initiation.getTlvs().getStringInformation())))
+ .withChild(ImmutableNodes.leafNode(ROUTER_DESCRIPTION_QNAME, initiation.getTlvs().getDescriptionTlv()
+ .getDescription()))
+ .withChild(ImmutableNodes.leafNode(ROUTER_INFO_QNAME, getStringInfo(initiation.getTlvs()
+ .getStringInformation())))
.withChild(ImmutableNodes.leafNode(ROUTER_STATUS_QNAME, UP)).build());
- wTx.submit();
+ wTx.commit().addCallback(new FutureCallback<CommitInfo>() {
+ @Override
+ public void onSuccess(final CommitInfo result) {
+ LOG.trace("Successful commit");
+ }
+
+ @Override
+ public void onFailure(final Throwable trw) {
+ LOG.error("Failed commit", trw);
+ }
+ }, MoreExecutors.directExecutor());
}
- private void onPeerUp(final PeerUpNotification peerUp) {
+ private synchronized void onPeerUp(final PeerUpNotification peerUp) {
final PeerId peerId = getPeerIdFromOpen(peerUp.getReceivedOpen());
if (!getPeer(peerId).isPresent()) {
- final BmpRouterPeer peer = BmpRouterPeerImpl.createRouterPeer(this.domTxChain, this.peersYangIId, peerUp,
- this.extensions, this.tree, peerId);
- this.peers.put(peerId, peer);
- LOG.debug("Router {}: Peer {} goes up.", this.routerIp, peerId.getValue());
+ final BmpRouterPeer peer = BmpRouterPeerImpl.createRouterPeer(domTxChain, peersYangIId, peerUp,
+ extensions, tree, peerId);
+ peers.put(peerId, peer);
+ LOG.debug("Router {}: Peer {} goes up.", routerIp, peerId.getValue());
} else {
- LOG.debug("Peer: {} for Router: {} already exists.", peerId.getValue(), this.routerIp);
+ LOG.debug("Peer: {} for Router: {} already exists.", peerId.getValue(), routerIp);
}
}
- private void delegateToPeer(final Notification perPeerMessage) {
+ private synchronized void delegateToPeer(final Notification<?> perPeerMessage) {
final PeerId peerId = getPeerId((PeerHeader) perPeerMessage);
final Optional<BmpRouterPeer> maybePeer = getPeer(peerId);
if (maybePeer.isPresent()) {
- maybePeer.get().onPeerMessage(perPeerMessage);
+ maybePeer.orElseThrow().onPeerMessage(perPeerMessage);
if (perPeerMessage instanceof PeerDownNotification) {
- this.peers.remove(peerId);
- LOG.debug("Router {}: Peer {} removed.", this.routerIp, peerId.getValue());
+ peers.remove(peerId);
+ LOG.debug("Router {}: Peer {} removed.", routerIp, peerId.getValue());
}
} else {
- LOG.debug("Peer: {} for Router: {} was not found.", peerId.getValue(), this.routerIp);
+ LOG.debug("Peer: {} for Router: {} was not found.", peerId.getValue(), routerIp);
}
}
private Optional<BmpRouterPeer> getPeer(final PeerId peerId) {
- return Optional.ofNullable(this.peers.get(peerId));
+ return Optional.ofNullable(peers.get(peerId));
}
private static PeerId getPeerId(final PeerHeader peerHeader) {