Convert yangtools binding APIs to mdsal bindings
[bgpcep.git] / bgp / bmp-impl / src / main / java / org / opendaylight / protocol / bmp / impl / app / BmpRouterImpl.java
1 /*
2  * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8
9 package org.opendaylight.protocol.bmp.impl.app;
10
11 import com.google.common.base.Preconditions;
12 import com.google.common.net.InetAddresses;
13 import java.util.HashMap;
14 import java.util.Iterator;
15 import java.util.List;
16 import java.util.Map;
17 import java.util.Optional;
18 import javax.annotation.concurrent.GuardedBy;
19 import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction;
20 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
21 import org.opendaylight.controller.md.sal.common.api.data.TransactionChain;
22 import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
23 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
24 import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
25 import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
26 import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
27 import org.opendaylight.mdsal.binding.dom.codec.api.BindingCodecTree;
28 import org.opendaylight.protocol.bgp.rib.spi.RIBExtensionConsumerContext;
29 import org.opendaylight.protocol.bmp.api.BmpSession;
30 import org.opendaylight.protocol.bmp.impl.spi.BmpRouter;
31 import org.opendaylight.protocol.bmp.impl.spi.BmpRouterPeer;
32 import org.opendaylight.protocol.util.Ipv4Util;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.OpenMessage;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.PeerId;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bmp.message.rev150512.InitiationMessage;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bmp.message.rev150512.PeerDownNotification;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bmp.message.rev150512.PeerHeader;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bmp.message.rev150512.PeerUpNotification;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bmp.message.rev150512.string.informations.StringInformation;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bmp.monitor.rev150512.RouterId;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bmp.monitor.rev150512.peers.Peer;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bmp.monitor.rev150512.routers.Router;
43 import org.opendaylight.yangtools.yang.binding.Notification;
44 import org.opendaylight.yangtools.yang.common.QName;
45 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
46 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
47 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
48 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
49 import org.slf4j.Logger;
50 import org.slf4j.LoggerFactory;
51
52 public class BmpRouterImpl implements BmpRouter, TransactionChainListener {
53
54     private static final Logger LOG = LoggerFactory.getLogger(BmpRouterImpl.class);
55
56     private static final QName ROUTER_ID_QNAME = QName.create(Router.QNAME, "router-id").intern();
57     private static final QName ROUTER_STATUS_QNAME = QName.create(Router.QNAME, "status").intern();
58     private static final QName ROUTER_NAME_QNAME = QName.create(Router.QNAME, "name").intern();
59     private static final QName ROUTER_DESCRIPTION_QNAME = QName.create(Router.QNAME, "description").intern();
60     private static final QName ROUTER_INFO_QNAME = QName.create(Router.QNAME, "info").intern();
61     private static final String UP = "up";
62     private static final String DOWN = "down";
63
64     private final RouterSessionManager sessionManager;
65     @GuardedBy("this")
66     private final Map<PeerId, BmpRouterPeer> peers = new HashMap<>();
67     private final DOMTransactionChain domTxChain;
68     private final DOMDataBroker domDataBroker;
69     private final RIBExtensionConsumerContext extensions;
70     private final BindingCodecTree tree;
71     private BmpSession session;
72     private RouterId routerId;
73     private String routerIp;
74     private YangInstanceIdentifier routerYangIId;
75     private YangInstanceIdentifier peersYangIId;
76
77     public BmpRouterImpl(final RouterSessionManager sessionManager) {
78         this.sessionManager = Preconditions.checkNotNull(sessionManager);
79         this.domDataBroker = sessionManager.getDomDataBroker();
80         this.domTxChain = this.domDataBroker.createTransactionChain(this);
81         this.extensions = sessionManager.getExtensions();
82         this.tree = sessionManager.getCodecTree();
83     }
84
85     @Override
86     public void onSessionUp(final BmpSession session) {
87         this.session = session;
88         this.routerIp = InetAddresses.toAddrString(this.session.getRemoteAddress());
89         this.routerId = new RouterId(Ipv4Util.getIpAddress(this.session.getRemoteAddress()));
90         // check if this session is redundant
91         if (!this.sessionManager.addSessionListener(this)) {
92             LOG.warn("Redundant BMP session with remote router {} ({}) detected. This BMP session will be abandoned.", this.routerIp, this.session);
93             this.close();
94         } else {
95             this.routerYangIId = YangInstanceIdentifier.builder(this.sessionManager.getRoutersYangIId()).nodeWithKey(Router.QNAME,
96                 ROUTER_ID_QNAME, this.routerIp).build();
97             this.peersYangIId = YangInstanceIdentifier.builder(routerYangIId).node(Peer.QNAME).build();
98             createRouterEntry();
99             LOG.info("BMP session with remote router {} ({}) is up now.", this.routerIp, this.session);
100         }
101     }
102
103     @Override
104     public void onSessionDown(final Exception e) {
105         // we want to tear down as we want to do clean up like closing the transaction chain, etc.
106         // even when datastore is not writable (routerYangIId == null / redundant session)
107         tearDown();
108     }
109
110     @Override
111     public void onMessage(final Notification message) {
112         if (message instanceof InitiationMessage) {
113             onInitiate((InitiationMessage) message);
114         } else if (message instanceof PeerUpNotification) {
115             onPeerUp((PeerUpNotification) message);
116         } else if (message instanceof PeerHeader) {
117             delegateToPeer(message);
118         }
119     }
120
121     @Override
122     public RouterId getRouterId() {
123         return routerId;
124     }
125
126     @Override
127     public synchronized void close() {
128         if (this.session != null) {
129             try {
130                 this.session.close();
131             } catch (Exception e) {
132                 LOG.error("Fail to close session.", e);
133             }
134         }
135     }
136
137     @GuardedBy("this")
138     private synchronized void tearDown() {
139         // the session has been teared down before
140         if (this.session == null) {
141             return;
142         }
143         // we want to display remote router's IP here, as sometimes this.session.close() is already
144         // invoked before tearDown(), and session channel is null in this case, which leads to unuseful
145         // log information
146         LOG.info("BMP Session with remote router {} ({}) went down.", this.routerIp, this.session);
147         this.session = null;
148         final Iterator<BmpRouterPeer> it = this.peers.values().iterator();
149         try {
150             while (it.hasNext()) {
151                 it.next().close();
152                 it.remove();
153             }
154             this.domTxChain.close();
155         } catch(final Exception e) {
156             LOG.error("Failed to properly close BMP application.", e);
157         } finally {
158             // remove session only when session is valid, otherwise
159             // we would remove the original valid session when a redundant connection happens
160             // as the routerId is the same for both connection
161             if (isDatastoreWritable()) {
162                 try {
163                     // it means the session was closed before it was written to datastore
164                     final DOMDataWriteTransaction wTx = this.domDataBroker.newWriteOnlyTransaction();
165                     wTx.delete(LogicalDatastoreType.OPERATIONAL, this.routerYangIId);
166                     wTx.submit().checkedGet();
167                 } catch (final TransactionCommitFailedException e) {
168                     LOG.error("Failed to remove BMP router data from DS.", e);
169                 }
170                 this.sessionManager.removeSessionListener(this);
171             }
172         }
173     }
174
175     @Override
176     public void onTransactionChainFailed(final TransactionChain<?, ?> chain, final AsyncTransaction<?, ?> transaction, final Throwable cause) {
177         LOG.error("Transaction chain failed.", cause);
178     }
179
180     @Override
181     public void onTransactionChainSuccessful(final TransactionChain<?, ?> chain) {
182         LOG.debug("Transaction chain {} successfully.", chain);
183     }
184
185     private boolean isDatastoreWritable() {
186         return (this.routerYangIId != null);
187     }
188
189     private void createRouterEntry() {
190         Preconditions.checkState(isDatastoreWritable());
191         final DOMDataWriteTransaction wTx = this.domTxChain.newWriteOnlyTransaction();
192         wTx.put(LogicalDatastoreType.OPERATIONAL, this.routerYangIId,
193                 Builders.mapEntryBuilder()
194                 .withNodeIdentifier(new NodeIdentifierWithPredicates(Router.QNAME, ROUTER_ID_QNAME, this.routerIp))
195                 .withChild(ImmutableNodes.leafNode(ROUTER_ID_QNAME, routerIp))
196                 .withChild(ImmutableNodes.leafNode(ROUTER_STATUS_QNAME, DOWN))
197                 .withChild(ImmutableNodes.mapNodeBuilder(Peer.QNAME).build()).build());
198         wTx.submit();
199     }
200
201     private void onInitiate(final InitiationMessage initiation) {
202         Preconditions.checkState(isDatastoreWritable());
203         final DOMDataWriteTransaction wTx = this.domTxChain.newWriteOnlyTransaction();
204         wTx.merge(LogicalDatastoreType.OPERATIONAL, this.routerYangIId,
205                 Builders.mapEntryBuilder()
206                 .withNodeIdentifier(new NodeIdentifierWithPredicates(Router.QNAME, ROUTER_ID_QNAME, this.routerIp))
207                 .withChild(ImmutableNodes.leafNode(ROUTER_NAME_QNAME, initiation.getTlvs().getNameTlv().getName()))
208                 .withChild(ImmutableNodes.leafNode(ROUTER_DESCRIPTION_QNAME, initiation.getTlvs().getDescriptionTlv().getDescription()))
209                 .withChild(ImmutableNodes.leafNode(ROUTER_INFO_QNAME, getStringInfo(initiation.getTlvs().getStringInformation())))
210                 .withChild(ImmutableNodes.leafNode(ROUTER_STATUS_QNAME, UP)).build());
211         wTx.submit();
212     }
213
214     private void onPeerUp(final PeerUpNotification peerUp) {
215         final PeerId peerId = getPeerIdFromOpen(peerUp.getReceivedOpen());
216         if (!getPeer(peerId).isPresent()) {
217             final BmpRouterPeer peer = BmpRouterPeerImpl.createRouterPeer(this.domTxChain, this.peersYangIId, peerUp, this.extensions, this.tree, peerId);
218             this.peers.put(peerId, peer);
219             LOG.debug("Router {}: Peer {} goes up.", this.routerIp, peerId.getValue());
220         } else {
221             LOG.debug("Peer: {} for Router: {} already exists.", peerId.getValue(), this.routerIp);
222         }
223     }
224
225     private void delegateToPeer(final Notification perPeerMessage) {
226         final PeerId peerId = getPeerId((PeerHeader) perPeerMessage);
227         final Optional<BmpRouterPeer> maybePeer = getPeer(peerId);
228         if (maybePeer.isPresent()) {
229             maybePeer.get().onPeerMessage(perPeerMessage);
230             if (perPeerMessage instanceof PeerDownNotification) {
231                 this.peers.remove(peerId);
232                 LOG.debug("Router {}: Peer {} removed.", this.routerIp, peerId.getValue());
233             }
234         } else {
235             LOG.debug("Peer: {} for Router: {} was not found.", peerId.getValue(), this.routerIp);
236         }
237     }
238
239     private Optional<BmpRouterPeer> getPeer(final PeerId peerId) {
240         return Optional.ofNullable(this.peers.get(peerId));
241     }
242
243     private static PeerId getPeerId(final PeerHeader peerHeader) {
244         return new PeerId(peerHeader.getPeerHeader().getBgpId().getValue());
245     }
246
247     private static PeerId getPeerIdFromOpen(final OpenMessage open) {
248         return new PeerId(open.getBgpIdentifier().getValue());
249     }
250
251     private static String getStringInfo(final List<StringInformation> info) {
252         final StringBuilder builder = new StringBuilder();
253         if (info != null) {
254             for (final StringInformation string : info) {
255                 if (string.getStringTlv() != null) {
256                     builder.append(string.getStringTlv().getStringInfo());
257                     builder.append(";");
258                 }
259             }
260         }
261         return builder.toString();
262     }
263
264 }