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
9 package org.opendaylight.protocol.bmp.impl.app;
11 import static java.util.Objects.requireNonNull;
13 import com.google.common.base.Preconditions;
14 import com.google.common.net.InetAddresses;
15 import com.google.common.util.concurrent.FutureCallback;
16 import com.google.common.util.concurrent.MoreExecutors;
17 import java.util.HashMap;
18 import java.util.Iterator;
19 import java.util.List;
21 import java.util.Optional;
22 import java.util.concurrent.ExecutionException;
23 import javax.annotation.concurrent.GuardedBy;
24 import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction;
25 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
26 import org.opendaylight.controller.md.sal.common.api.data.TransactionChain;
27 import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
28 import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
29 import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
30 import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
31 import org.opendaylight.mdsal.binding.dom.codec.api.BindingCodecTree;
32 import org.opendaylight.mdsal.common.api.CommitInfo;
33 import org.opendaylight.protocol.bgp.rib.spi.RIBExtensionConsumerContext;
34 import org.opendaylight.protocol.bmp.api.BmpSession;
35 import org.opendaylight.protocol.bmp.impl.spi.BmpRouter;
36 import org.opendaylight.protocol.bmp.impl.spi.BmpRouterPeer;
37 import org.opendaylight.protocol.util.Ipv4Util;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.OpenMessage;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.PeerId;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bmp.message.rev180329.InitiationMessage;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bmp.message.rev180329.PeerDownNotification;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bmp.message.rev180329.PeerHeader;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bmp.message.rev180329.PeerUpNotification;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bmp.message.rev180329.string.informations.StringInformation;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bmp.monitor.rev180329.RouterId;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bmp.monitor.rev180329.peers.Peer;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bmp.monitor.rev180329.routers.Router;
48 import org.opendaylight.yangtools.yang.binding.Notification;
49 import org.opendaylight.yangtools.yang.common.QName;
50 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
51 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
52 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
53 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
54 import org.slf4j.Logger;
55 import org.slf4j.LoggerFactory;
57 public final class BmpRouterImpl implements BmpRouter, TransactionChainListener {
59 private static final Logger LOG = LoggerFactory.getLogger(BmpRouterImpl.class);
61 private static final QName ROUTER_ID_QNAME = QName.create(Router.QNAME, "router-id").intern();
62 private static final QName ROUTER_STATUS_QNAME = QName.create(Router.QNAME, "status").intern();
63 private static final QName ROUTER_NAME_QNAME = QName.create(Router.QNAME, "name").intern();
64 private static final QName ROUTER_DESCRIPTION_QNAME = QName.create(Router.QNAME, "description").intern();
65 private static final QName ROUTER_INFO_QNAME = QName.create(Router.QNAME, "info").intern();
66 private static final String UP = "up";
67 private static final String DOWN = "down";
69 private final RouterSessionManager sessionManager;
71 private final Map<PeerId, BmpRouterPeer> peers = new HashMap<>();
72 private final DOMTransactionChain domTxChain;
73 private final DOMDataBroker domDataBroker;
74 private final RIBExtensionConsumerContext extensions;
75 private final BindingCodecTree tree;
76 private BmpSession session;
77 private RouterId routerId;
78 private String routerIp;
80 private YangInstanceIdentifier routerYangIId;
82 private YangInstanceIdentifier peersYangIId;
84 public BmpRouterImpl(final RouterSessionManager sessionManager) {
85 this.sessionManager = requireNonNull(sessionManager);
86 this.domDataBroker = sessionManager.getDomDataBroker();
87 this.domTxChain = this.domDataBroker.createTransactionChain(this);
88 this.extensions = sessionManager.getExtensions();
89 this.tree = sessionManager.getCodecTree();
93 public synchronized void onSessionUp(final BmpSession psession) {
94 this.session = psession;
95 this.routerIp = InetAddresses.toAddrString(this.session.getRemoteAddress());
96 this.routerId = new RouterId(Ipv4Util.getIpAddress(this.session.getRemoteAddress()));
97 // check if this session is redundant
98 if (!this.sessionManager.addSessionListener(this)) {
99 LOG.warn("Redundant BMP session with remote router {} ({}) detected. This BMP session will be abandoned.",
100 this.routerIp, this.session);
103 this.routerYangIId = YangInstanceIdentifier.builder(this.sessionManager.getRoutersYangIId())
104 .nodeWithKey(Router.QNAME, ROUTER_ID_QNAME, this.routerIp).build();
105 this.peersYangIId = YangInstanceIdentifier.builder(this.routerYangIId).node(Peer.QNAME).build();
107 LOG.info("BMP session with remote router {} ({}) is up now.", this.routerIp, this.session);
112 public void onSessionDown(final Exception exception) {
113 // we want to tear down as we want to do clean up like closing the transaction chain, etc.
114 // even when datastore is not writable (routerYangIId == null / redundant session)
119 public void onMessage(final Notification message) {
120 if (message instanceof InitiationMessage) {
121 onInitiate((InitiationMessage) message);
122 } else if (message instanceof PeerUpNotification) {
123 onPeerUp((PeerUpNotification) message);
124 } else if (message instanceof PeerHeader) {
125 delegateToPeer(message);
130 public synchronized RouterId getRouterId() {
131 return this.routerId;
135 @SuppressWarnings("checkstyle:IllegalCatch")
136 public synchronized void close() {
137 if (this.session != null) {
139 this.session.close();
140 } catch (final Exception exc) {
141 LOG.error("Fail to close session.", exc);
147 @SuppressWarnings("checkstyle:IllegalCatch")
148 private synchronized void tearDown() {
149 // the session has been teared down before
150 if (this.session == null) {
153 // we want to display remote router's IP here, as sometimes this.session.close() is already
154 // invoked before tearDown(), and session channel is null in this case, which leads to unuseful
156 LOG.info("BMP Session with remote router {} ({}) went down.", this.routerIp, this.session);
158 final Iterator<BmpRouterPeer> it = this.peers.values().iterator();
160 while (it.hasNext()) {
164 this.domTxChain.close();
165 } catch (final Exception e) {
166 LOG.error("Failed to properly close BMP application.", e);
168 // remove session only when session is valid, otherwise
169 // we would remove the original valid session when a redundant connection happens
170 // as the routerId is the same for both connection
171 if (isDatastoreWritable()) {
173 // it means the session was closed before it was written to datastore
174 final DOMDataWriteTransaction wTx = this.domDataBroker.newWriteOnlyTransaction();
175 wTx.delete(LogicalDatastoreType.OPERATIONAL, this.routerYangIId);
177 } catch (final InterruptedException | ExecutionException e) {
178 LOG.error("Failed to remove BMP router data from DS.", e);
180 this.sessionManager.removeSessionListener(this);
186 public void onTransactionChainFailed(final TransactionChain<?, ?> chain, final AsyncTransaction<?, ?> transaction,
187 final Throwable cause) {
188 LOG.error("Transaction chain failed.", cause);
192 public void onTransactionChainSuccessful(final TransactionChain<?, ?> chain) {
193 LOG.debug("Transaction chain {} successfully.", chain);
196 private boolean isDatastoreWritable() {
197 return (this.routerYangIId != null);
200 private synchronized void createRouterEntry() {
201 Preconditions.checkState(isDatastoreWritable());
202 final DOMDataWriteTransaction wTx = this.domTxChain.newWriteOnlyTransaction();
203 wTx.put(LogicalDatastoreType.OPERATIONAL, this.routerYangIId,
204 Builders.mapEntryBuilder()
205 .withNodeIdentifier(new NodeIdentifierWithPredicates(Router.QNAME, ROUTER_ID_QNAME, this.routerIp))
206 .withChild(ImmutableNodes.leafNode(ROUTER_ID_QNAME, this.routerIp))
207 .withChild(ImmutableNodes.leafNode(ROUTER_STATUS_QNAME, DOWN))
208 .withChild(ImmutableNodes.mapNodeBuilder(Peer.QNAME).build()).build());
209 wTx.commit().addCallback(new FutureCallback<CommitInfo>() {
211 public void onSuccess(final CommitInfo result) {
212 LOG.trace("Successful commit");
216 public void onFailure(final Throwable trw) {
217 LOG.error("Failed commit", trw);
219 }, MoreExecutors.directExecutor());
222 private synchronized void onInitiate(final InitiationMessage initiation) {
223 Preconditions.checkState(isDatastoreWritable());
224 final DOMDataWriteTransaction wTx = this.domTxChain.newWriteOnlyTransaction();
225 wTx.merge(LogicalDatastoreType.OPERATIONAL, this.routerYangIId,
226 Builders.mapEntryBuilder()
227 .withNodeIdentifier(new NodeIdentifierWithPredicates(Router.QNAME, ROUTER_ID_QNAME, this.routerIp))
228 .withChild(ImmutableNodes.leafNode(ROUTER_NAME_QNAME, initiation.getTlvs().getNameTlv().getName()))
229 .withChild(ImmutableNodes.leafNode(ROUTER_DESCRIPTION_QNAME, initiation.getTlvs().getDescriptionTlv()
231 .withChild(ImmutableNodes.leafNode(ROUTER_INFO_QNAME, getStringInfo(initiation.getTlvs()
232 .getStringInformation())))
233 .withChild(ImmutableNodes.leafNode(ROUTER_STATUS_QNAME, UP)).build());
234 wTx.commit().addCallback(new FutureCallback<CommitInfo>() {
236 public void onSuccess(final CommitInfo result) {
237 LOG.trace("Successful commit");
241 public void onFailure(final Throwable trw) {
242 LOG.error("Failed commit", trw);
244 }, MoreExecutors.directExecutor());
247 private synchronized void onPeerUp(final PeerUpNotification peerUp) {
248 final PeerId peerId = getPeerIdFromOpen(peerUp.getReceivedOpen());
249 if (!getPeer(peerId).isPresent()) {
250 final BmpRouterPeer peer = BmpRouterPeerImpl.createRouterPeer(this.domTxChain, this.peersYangIId, peerUp,
251 this.extensions, this.tree, peerId);
252 this.peers.put(peerId, peer);
253 LOG.debug("Router {}: Peer {} goes up.", this.routerIp, peerId.getValue());
255 LOG.debug("Peer: {} for Router: {} already exists.", peerId.getValue(), this.routerIp);
259 private synchronized void delegateToPeer(final Notification perPeerMessage) {
260 final PeerId peerId = getPeerId((PeerHeader) perPeerMessage);
261 final Optional<BmpRouterPeer> maybePeer = getPeer(peerId);
262 if (maybePeer.isPresent()) {
263 maybePeer.get().onPeerMessage(perPeerMessage);
264 if (perPeerMessage instanceof PeerDownNotification) {
265 this.peers.remove(peerId);
266 LOG.debug("Router {}: Peer {} removed.", this.routerIp, peerId.getValue());
269 LOG.debug("Peer: {} for Router: {} was not found.", peerId.getValue(), this.routerIp);
273 private Optional<BmpRouterPeer> getPeer(final PeerId peerId) {
274 return Optional.ofNullable(this.peers.get(peerId));
277 private static PeerId getPeerId(final PeerHeader peerHeader) {
278 return new PeerId(peerHeader.getPeerHeader().getBgpId().getValue());
281 private static PeerId getPeerIdFromOpen(final OpenMessage open) {
282 return new PeerId(open.getBgpIdentifier().getValue());
285 private static String getStringInfo(final List<StringInformation> info) {
286 final StringBuilder builder = new StringBuilder();
288 for (final StringInformation string : info) {
289 if (string.getStringTlv() != null) {
290 builder.append(string.getStringTlv().getStringInfo());
295 return builder.toString();