2 * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.protocol.bmp.impl.app;
10 import static java.util.Objects.requireNonNull;
12 import com.google.common.base.Preconditions;
13 import com.google.common.net.InetAddresses;
14 import com.google.common.util.concurrent.FutureCallback;
15 import com.google.common.util.concurrent.MoreExecutors;
16 import java.util.HashMap;
17 import java.util.Iterator;
18 import java.util.List;
20 import java.util.Optional;
21 import java.util.concurrent.ExecutionException;
22 import org.checkerframework.checker.lock.qual.GuardedBy;
23 import org.checkerframework.checker.lock.qual.Holding;
24 import org.opendaylight.mdsal.binding.dom.codec.api.BindingCodecTree;
25 import org.opendaylight.mdsal.common.api.CommitInfo;
26 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
27 import org.opendaylight.mdsal.dom.api.DOMDataBroker;
28 import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteTransaction;
29 import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
30 import org.opendaylight.protocol.bgp.rib.spi.RIBExtensionConsumerContext;
31 import org.opendaylight.protocol.bmp.api.BmpSession;
32 import org.opendaylight.protocol.bmp.impl.spi.BmpRouter;
33 import org.opendaylight.protocol.bmp.impl.spi.BmpRouterPeer;
34 import org.opendaylight.protocol.util.Ipv4Util;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev200120.OpenMessage;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.PeerId;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bmp.message.rev200120.InitiationMessage;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bmp.message.rev200120.PeerDownNotification;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bmp.message.rev200120.PeerHeader;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bmp.message.rev200120.PeerUpNotification;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bmp.message.rev200120.string.informations.StringInformation;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bmp.monitor.rev200120.RouterId;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bmp.monitor.rev200120.peers.Peer;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bmp.monitor.rev200120.routers.Router;
45 import org.opendaylight.yangtools.yang.binding.Notification;
46 import org.opendaylight.yangtools.yang.common.Empty;
47 import org.opendaylight.yangtools.yang.common.QName;
48 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
49 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
50 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
51 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
52 import org.slf4j.Logger;
53 import org.slf4j.LoggerFactory;
55 public final class BmpRouterImpl implements BmpRouter, FutureCallback<Empty> {
57 private static final Logger LOG = LoggerFactory.getLogger(BmpRouterImpl.class);
59 private static final QName ROUTER_ID_QNAME = QName.create(Router.QNAME, "router-id").intern();
60 private static final QName ROUTER_STATUS_QNAME = QName.create(Router.QNAME, "status").intern();
61 private static final QName ROUTER_NAME_QNAME = QName.create(Router.QNAME, "name").intern();
62 private static final QName ROUTER_DESCRIPTION_QNAME = QName.create(Router.QNAME, "description").intern();
63 private static final QName ROUTER_INFO_QNAME = QName.create(Router.QNAME, "info").intern();
64 private static final String UP = "up";
65 private static final String DOWN = "down";
67 private final RouterSessionManager sessionManager;
69 private final Map<PeerId, BmpRouterPeer> peers = new HashMap<>();
70 private final DOMTransactionChain domTxChain;
71 private final DOMDataBroker domDataBroker;
72 private final RIBExtensionConsumerContext extensions;
73 private final BindingCodecTree tree;
74 private BmpSession session;
75 private RouterId routerId;
76 private String routerIp;
78 private YangInstanceIdentifier routerYangIId;
80 private YangInstanceIdentifier peersYangIId;
82 public BmpRouterImpl(final RouterSessionManager sessionManager) {
83 this.sessionManager = requireNonNull(sessionManager);
84 domDataBroker = sessionManager.getDomDataBroker();
85 domTxChain = domDataBroker.createMergingTransactionChain();
86 domTxChain.addCallback(this);
87 extensions = sessionManager.getExtensions();
88 tree = sessionManager.getCodecTree();
92 public synchronized void onSessionUp(final BmpSession psession) {
94 routerIp = InetAddresses.toAddrString(session.getRemoteAddress());
95 routerId = new RouterId(Ipv4Util.getIpAddress(session.getRemoteAddress()));
96 // check if this session is redundant
97 if (!sessionManager.addSessionListener(this)) {
98 LOG.warn("Redundant BMP session with remote router {} ({}) detected. This BMP session will be abandoned.",
102 routerYangIId = YangInstanceIdentifier.builder(sessionManager.getRoutersYangIId())
103 .nodeWithKey(Router.QNAME, ROUTER_ID_QNAME, routerIp).build();
104 peersYangIId = YangInstanceIdentifier.builder(routerYangIId).node(Peer.QNAME).build();
106 LOG.info("BMP session with remote router {} ({}) is up now.", routerIp, session);
111 public synchronized void onSessionDown(final Exception exception) {
112 // we want to tear down as we want to do clean up like closing the transaction chain, etc.
113 // even when datastore is not writable (routerYangIId == null / redundant session)
118 public void onMessage(final Notification<?> message) {
119 if (message instanceof InitiationMessage) {
120 onInitiate((InitiationMessage) message);
121 } else if (message instanceof PeerUpNotification) {
122 onPeerUp((PeerUpNotification) message);
123 } else if (message instanceof PeerHeader) {
124 delegateToPeer(message);
129 public synchronized RouterId getRouterId() {
134 @SuppressWarnings("checkstyle:IllegalCatch")
135 public synchronized void close() {
136 if (session != null) {
139 } catch (final Exception exc) {
140 LOG.error("Fail to close session.", exc);
146 @SuppressWarnings("checkstyle:IllegalCatch")
147 private synchronized void tearDown() {
148 // the session has been teared down before
149 if (session == null) {
152 // we want to display remote router's IP here, as sometimes this.session.close() is already
153 // invoked before tearDown(), and session channel is null in this case, which leads to unuseful
155 LOG.info("BMP Session with remote router {} ({}) went down.", routerIp, session);
157 final Iterator<BmpRouterPeer> it = peers.values().iterator();
159 while (it.hasNext()) {
164 } catch (final Exception e) {
165 LOG.error("Failed to properly close BMP application.", e);
167 // remove session only when session is valid, otherwise
168 // we would remove the original valid session when a redundant connection happens
169 // as the routerId is the same for both connection
170 if (isDatastoreWritable()) {
172 // it means the session was closed before it was written to datastore
173 final DOMDataTreeWriteTransaction wTx = domDataBroker.newWriteOnlyTransaction();
174 wTx.delete(LogicalDatastoreType.OPERATIONAL, routerYangIId);
176 } catch (final InterruptedException | ExecutionException e) {
177 LOG.error("Failed to remove BMP router data from DS.", e);
179 sessionManager.removeSessionListener(this);
185 public synchronized void onFailure(final Throwable cause) {
186 LOG.error("Transaction chain failed.", cause);
190 public void onSuccess(final Empty value) {
191 LOG.debug("Transaction chain finished successfully.");
194 private synchronized boolean isDatastoreWritable() {
195 return routerYangIId != null;
198 private synchronized void createRouterEntry() {
199 Preconditions.checkState(isDatastoreWritable());
200 final DOMDataTreeWriteTransaction wTx = domTxChain.newWriteOnlyTransaction();
201 wTx.put(LogicalDatastoreType.OPERATIONAL, routerYangIId,
202 Builders.mapEntryBuilder()
203 .withNodeIdentifier(NodeIdentifierWithPredicates.of(Router.QNAME, ROUTER_ID_QNAME, routerIp))
204 .withChild(ImmutableNodes.leafNode(ROUTER_ID_QNAME, routerIp))
205 .withChild(ImmutableNodes.leafNode(ROUTER_STATUS_QNAME, DOWN))
206 .withChild(ImmutableNodes.mapNodeBuilder(Peer.QNAME).build()).build());
207 wTx.commit().addCallback(new FutureCallback<CommitInfo>() {
209 public void onSuccess(final CommitInfo result) {
210 LOG.trace("Successful commit");
214 public void onFailure(final Throwable trw) {
215 LOG.error("Failed commit", trw);
217 }, MoreExecutors.directExecutor());
220 private synchronized void onInitiate(final InitiationMessage initiation) {
221 Preconditions.checkState(isDatastoreWritable());
222 final DOMDataTreeWriteTransaction wTx = domTxChain.newWriteOnlyTransaction();
223 wTx.merge(LogicalDatastoreType.OPERATIONAL, routerYangIId,
224 Builders.mapEntryBuilder()
225 .withNodeIdentifier(NodeIdentifierWithPredicates.of(Router.QNAME, ROUTER_ID_QNAME, routerIp))
226 .withChild(ImmutableNodes.leafNode(ROUTER_NAME_QNAME, initiation.getTlvs().getNameTlv().getName()))
227 .withChild(ImmutableNodes.leafNode(ROUTER_DESCRIPTION_QNAME, initiation.getTlvs().getDescriptionTlv()
229 .withChild(ImmutableNodes.leafNode(ROUTER_INFO_QNAME, getStringInfo(initiation.getTlvs()
230 .getStringInformation())))
231 .withChild(ImmutableNodes.leafNode(ROUTER_STATUS_QNAME, UP)).build());
232 wTx.commit().addCallback(new FutureCallback<CommitInfo>() {
234 public void onSuccess(final CommitInfo result) {
235 LOG.trace("Successful commit");
239 public void onFailure(final Throwable trw) {
240 LOG.error("Failed commit", trw);
242 }, MoreExecutors.directExecutor());
245 private synchronized void onPeerUp(final PeerUpNotification peerUp) {
246 final PeerId peerId = getPeerIdFromOpen(peerUp.getReceivedOpen());
247 if (!getPeer(peerId).isPresent()) {
248 final BmpRouterPeer peer = BmpRouterPeerImpl.createRouterPeer(domTxChain, peersYangIId, peerUp,
249 extensions, tree, peerId);
250 peers.put(peerId, peer);
251 LOG.debug("Router {}: Peer {} goes up.", routerIp, peerId.getValue());
253 LOG.debug("Peer: {} for Router: {} already exists.", peerId.getValue(), routerIp);
257 private synchronized void delegateToPeer(final Notification<?> perPeerMessage) {
258 final PeerId peerId = getPeerId((PeerHeader) perPeerMessage);
259 final Optional<BmpRouterPeer> maybePeer = getPeer(peerId);
260 if (maybePeer.isPresent()) {
261 maybePeer.orElseThrow().onPeerMessage(perPeerMessage);
262 if (perPeerMessage instanceof PeerDownNotification) {
263 peers.remove(peerId);
264 LOG.debug("Router {}: Peer {} removed.", routerIp, peerId.getValue());
267 LOG.debug("Peer: {} for Router: {} was not found.", peerId.getValue(), routerIp);
271 private Optional<BmpRouterPeer> getPeer(final PeerId peerId) {
272 return Optional.ofNullable(peers.get(peerId));
275 private static PeerId getPeerId(final PeerHeader peerHeader) {
276 return new PeerId(peerHeader.getPeerHeader().getBgpId().getValue());
279 private static PeerId getPeerIdFromOpen(final OpenMessage open) {
280 return new PeerId(open.getBgpIdentifier().getValue());
283 private static String getStringInfo(final List<StringInformation> info) {
284 final StringBuilder builder = new StringBuilder();
286 for (final StringInformation string : info) {
287 if (string.getStringTlv() != null) {
288 builder.append(string.getStringTlv().getStringInfo());
293 return builder.toString();