package org.opendaylight.protocol.bmp.impl.app;
-import com.google.common.base.Optional;
+import static java.util.Objects.requireNonNull;
+
import com.google.common.base.Preconditions;
import com.google.common.net.InetAddresses;
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.dom.api.DOMDataBroker;
import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingCodecTree;
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.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bmp.monitor.rev150512.RouterId;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bmp.monitor.rev150512.peers.Peer;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bmp.monitor.rev150512.routers.Router;
-import org.opendaylight.yangtools.binding.data.codec.api.BindingCodecTree;
import org.opendaylight.yangtools.yang.binding.Notification;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
private static final Logger LOG = LoggerFactory.getLogger(BmpRouterImpl.class);
- private static final QName ROUTER_ID_QNAME = QName.cachedReference(QName.create(Router.QNAME, "router-id"));
- private static final QName ROUTER_STATUS_QNAME = QName.cachedReference(QName.create(Router.QNAME, "status"));
- private static final QName ROUTER_NAME_QNAME = QName.cachedReference(QName.create(Router.QNAME, "name"));
- private static final QName ROUTER_DESCRIPTION_QNAME = QName.cachedReference(QName.create(Router.QNAME, "description"));
- private static final QName ROUTER_INFO_QNAME = QName.cachedReference(QName.create(Router.QNAME, "info"));
+ private static final QName ROUTER_ID_QNAME = QName.create(Router.QNAME, "router-id").intern();
+ private static final QName ROUTER_STATUS_QNAME = QName.create(Router.QNAME, "status").intern();
+ private static final QName ROUTER_NAME_QNAME = QName.create(Router.QNAME, "name").intern();
+ private static final QName ROUTER_DESCRIPTION_QNAME = QName.create(Router.QNAME, "description").intern();
+ private static final QName ROUTER_INFO_QNAME = QName.create(Router.QNAME, "info").intern();
private static final String UP = "up";
private static final String DOWN = "down";
private YangInstanceIdentifier peersYangIId;
public BmpRouterImpl(final RouterSessionManager sessionManager) {
- this.sessionManager = Preconditions.checkNotNull(sessionManager);
+ this.sessionManager = requireNonNull(sessionManager);
this.domDataBroker = sessionManager.getDomDataBroker();
- this.domTxChain = sessionManager.getDomDataBroker().createTransactionChain(this);
+ this.domTxChain = this.domDataBroker.createTransactionChain(this);
this.extensions = sessionManager.getExtensions();
this.tree = sessionManager.getCodecTree();
}
@Override
public void onSessionUp(final BmpSession session) {
this.session = session;
- this.routerIp = InetAddresses.toAddrString(session.getRemoteAddress());
- this.routerId = new RouterId(Ipv4Util.getIpAddress(session.getRemoteAddress()));
- this.routerYangIId = YangInstanceIdentifier.builder(this.sessionManager.getRoutersYangIId()).nodeWithKey(Router.QNAME,
- ROUTER_ID_QNAME, this.routerIp).build();
- this.peersYangIId = YangInstanceIdentifier.builder(routerYangIId).node(Peer.QNAME).build();
- createRouterEntry();
- this.sessionManager.addSessionListener(this);
+ this.routerIp = InetAddresses.toAddrString(this.session.getRemoteAddress());
+ this.routerId = new RouterId(Ipv4Util.getIpAddress(this.session.getRemoteAddress()));
+ // check if this session is redundant
+ if (!this.sessionManager.addSessionListener(this)) {
+ LOG.warn("Redundant BMP session with remote router {} ({}) detected. This BMP session will be abandoned.",
+ this.routerIp, this.session);
+ this.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();
+ createRouterEntry();
+ LOG.info("BMP session with remote router {} ({}) is up now.", this.routerIp, this.session);
+ }
}
@Override
- public void onSessionDown(final BmpSession session, final Exception e) {
- LOG.info("Session {} went down.", session);
+ public void onSessionDown(final Exception e) {
+ // 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 BmpSession session, 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 routerId;
+ return this.routerId;
}
@Override
- public synchronized void close() throws Exception {
+ public synchronized void close() {
if (this.session != null) {
- this.session.close();
+ try {
+ this.session.close();
+ } catch (final Exception e) {
+ LOG.error("Fail to close session.", e);
+ }
}
}
@GuardedBy("this")
private synchronized void tearDown() {
+ // the session has been teared down before
+ if (this.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();
try {
} catch(final Exception e) {
LOG.error("Failed to properly close BMP application.", e);
} finally {
- try {
- final DOMDataWriteTransaction wTx = this.domDataBroker.newWriteOnlyTransaction();
- wTx.delete(LogicalDatastoreType.OPERATIONAL, this.routerYangIId);
- wTx.submit().checkedGet();
- } catch (final TransactionCommitFailedException e) {
- LOG.error("Failed to remove BMP router data from DS.", e);
+ // remove session only when session is valid, otherwise
+ // we would remove the original valid session when a redundant connection happens
+ // as the routerId is the same for both connection
+ 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) {
+ LOG.error("Failed to remove BMP router data from DS.", e);
+ }
+ this.sessionManager.removeSessionListener(this);
}
}
- this.sessionManager.removeSessionListener(this);
}
@Override
- public void onTransactionChainFailed(final TransactionChain<?, ?> chain, final AsyncTransaction<?, ?> transaction, final Throwable cause) {
+ public void onTransactionChainFailed(final TransactionChain<?, ?> chain, final AsyncTransaction<?, ?> transaction,
+ final Throwable cause) {
LOG.error("Transaction chain failed.", cause);
}
@Override
public void onTransactionChainSuccessful(final TransactionChain<?, ?> chain) {
- LOG.debug("Transaction chain {} successfull.", chain);
+ LOG.debug("Transaction chain {} successfully.", chain);
+ }
+
+ private boolean isDatastoreWritable() {
+ return (this.routerYangIId != null);
}
- private void createRouterEntry() {
+ private synchronized void createRouterEntry() {
+ Preconditions.checkState(isDatastoreWritable());
final DOMDataWriteTransaction wTx = this.domTxChain.newWriteOnlyTransaction();
wTx.put(LogicalDatastoreType.OPERATIONAL, this.routerYangIId,
Builders.mapEntryBuilder()
.withNodeIdentifier(new NodeIdentifierWithPredicates(Router.QNAME, ROUTER_ID_QNAME, this.routerIp))
- .withChild(ImmutableNodes.leafNode(ROUTER_ID_QNAME, routerIp))
+ .withChild(ImmutableNodes.leafNode(ROUTER_ID_QNAME, this.routerIp))
.withChild(ImmutableNodes.leafNode(ROUTER_STATUS_QNAME, DOWN))
.withChild(ImmutableNodes.mapNodeBuilder(Peer.QNAME).build()).build());
wTx.submit();
}
- private void onInitiate(final InitiationMessage initiation) {
+ private synchronized void onInitiate(final InitiationMessage initiation) {
+ Preconditions.checkState(isDatastoreWritable());
final DOMDataWriteTransaction wTx = this.domTxChain.newWriteOnlyTransaction();
wTx.merge(LogicalDatastoreType.OPERATIONAL, this.routerYangIId,
Builders.mapEntryBuilder()
private 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);
+ 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());
} else {
}
private Optional<BmpRouterPeer> getPeer(final PeerId peerId) {
- return Optional.fromNullable(this.peers.get(peerId));
+ return Optional.ofNullable(this.peers.get(peerId));
}
private static PeerId getPeerId(final PeerHeader peerHeader) {