BUG-3768 : fixed AdjRibsOutWriter
[bgpcep.git] / bgp / rib-impl / src / main / java / org / opendaylight / protocol / bgp / rib / impl / AdjRibOutListener.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 package org.opendaylight.protocol.bgp.rib.impl;
9
10 import com.google.common.base.Preconditions;
11 import java.util.Collection;
12 import java.util.Collections;
13 import javax.annotation.Nonnull;
14 import javax.annotation.concurrent.NotThreadSafe;
15 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
16 import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeListener;
17 import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeService;
18 import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeIdentifier;
19 import org.opendaylight.protocol.bgp.rib.impl.spi.RIBSupportContextRegistry;
20 import org.opendaylight.protocol.bgp.rib.spi.RIBSupport;
21 import org.opendaylight.protocol.bgp.rib.spi.RibSupportUtils;
22 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.Update;
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.path.attributes.Attributes;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.PeerId;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.Peer;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.peer.AdjRibOut;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.Tables;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.TablesKey;
29 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
30 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
31 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
32 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes;
33 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
34 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
37
38 /**
39  * Instantiated for each peer and table, listens on a particular peer's adj-rib-out,
40  * performs transcoding to BA form (message) and sends it down the channel.
41  */
42 @NotThreadSafe
43 final class AdjRibOutListener implements DOMDataTreeChangeListener {
44
45     private static final Logger LOG = LoggerFactory.getLogger(AdjRibOutListener.class);
46
47     private final ChannelOutputLimiter session;
48     private final RIBSupportContextImpl context;
49     private final RIBSupport support;
50
51     private AdjRibOutListener(final PeerId peerId, final TablesKey tablesKey, final YangInstanceIdentifier ribId, final DOMDataTreeChangeService service, final RIBSupportContextRegistry registry, final ChannelOutputLimiter session) {
52         this.session = Preconditions.checkNotNull(session);
53         this.context = (RIBSupportContextImpl) registry.getRIBSupportContext(tablesKey);
54         this.support = this.context.getRibSupport();
55         final YangInstanceIdentifier adjRibOutId =  ribId.node(Peer.QNAME).node(IdentifierUtils.domPeerId(peerId)).node(AdjRibOut.QNAME).node(Tables.QNAME).node(RibSupportUtils.toYangTablesKey(tablesKey));
56         service.registerDataTreeChangeListener(new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, adjRibOutId), this);
57     }
58
59     static AdjRibOutListener create(@Nonnull final PeerId peerId, @Nonnull final TablesKey tablesKey, @Nonnull final YangInstanceIdentifier ribId, @Nonnull final DOMDataTreeChangeService service, @Nonnull final RIBSupportContextRegistry registry, @Nonnull final ChannelOutputLimiter session) {
60         return new AdjRibOutListener(peerId, tablesKey, ribId, service, registry, session);
61     }
62
63     @Override
64     public void onDataTreeChanged(final Collection<DataTreeCandidate> changes) {
65         LOG.debug("Data change received for AdjRibOut {}", changes);
66         for (final DataTreeCandidate tc : changes) {
67             LOG.trace("Change {} type {}", tc.getRootNode(), tc.getRootNode().getModificationType());
68             for (final DataTreeCandidateNode child : tc.getRootNode().getChildNodes()) {
69                 for (final DataTreeCandidateNode route : this.context.getRibSupport().changedRoutes(child)) {
70                     final Update update;
71
72                     switch (route.getModificationType()) {
73                     case UNMODIFIED:
74                         LOG.debug("Skipping unmodified route {}", route.getIdentifier());
75                         continue;
76                     case DELETE:
77                         // FIXME: we can batch deletions into a single batch
78                         update = withdraw((MapEntryNode) route.getDataBefore().get());
79                         break;
80                     case SUBTREE_MODIFIED:
81                     case WRITE:
82                         update = advertise((MapEntryNode) route.getDataAfter().get());
83                         break;
84                     default:
85                         LOG.warn("Ignoring unhandled modification type {}", route.getModificationType());
86                         continue;
87                     }
88
89                     LOG.debug("Writing update {}", update);
90                     this.session.write(update);
91                 }
92             }
93         }
94         this.session.flush();
95     }
96
97     private Attributes routeAttributes(final MapEntryNode route) {
98         if (LOG.isDebugEnabled()) {
99             LOG.debug("AdjRibOut parsing route {}", NormalizedNodes.toStringTree(route));
100         }
101
102         final ContainerNode advertisedAttrs = (ContainerNode) NormalizedNodes.findNode(route, this.support.routeAttributesIdentifier()).orNull();
103         return this.context.deserializeAttributes(advertisedAttrs);
104     }
105
106     private Update withdraw(final MapEntryNode route) {
107         return this.support.buildUpdate(Collections.<MapEntryNode>emptyList(), Collections.singleton(route), routeAttributes(route));
108     }
109
110     private Update advertise(final MapEntryNode route) {
111         return this.support.buildUpdate(Collections.singleton(route), Collections.<MapEntryNode>emptyList(), routeAttributes(route));
112     }
113 }