BUG-5032: BGP Operational State
[bgpcep.git] / bgp / rib-impl / src / main / java / org / opendaylight / protocol / bgp / rib / impl / config / BgpDeployerImpl.java
1 /*
2  * Copyright (c) 2016 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.bgp.rib.impl.config;
10
11 import static org.opendaylight.protocol.bgp.rib.impl.config.OpenConfigMappingUtil.getNeighborInstanceIdentifier;
12 import static org.opendaylight.protocol.bgp.rib.impl.config.OpenConfigMappingUtil.getNeighborInstanceName;
13 import static org.opendaylight.protocol.bgp.rib.impl.config.OpenConfigMappingUtil.getRibInstanceName;
14
15 import com.google.common.base.Preconditions;
16 import com.google.common.util.concurrent.CheckedFuture;
17 import com.google.common.util.concurrent.FutureCallback;
18 import com.google.common.util.concurrent.Futures;
19 import com.google.common.util.concurrent.ListenableFuture;
20 import java.util.ArrayList;
21 import java.util.Collection;
22 import java.util.Dictionary;
23 import java.util.HashMap;
24 import java.util.Hashtable;
25 import java.util.List;
26 import java.util.Map;
27 import javax.annotation.concurrent.GuardedBy;
28 import org.opendaylight.controller.md.sal.binding.api.ClusteredDataTreeChangeListener;
29 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
30 import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
31 import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
32 import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
33 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
34 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
35 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
36 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
37 import org.opendaylight.protocol.bgp.openconfig.spi.BGPTableTypeRegistryConsumer;
38 import org.opendaylight.protocol.bgp.rib.impl.spi.BgpDeployer;
39 import org.opendaylight.protocol.bgp.rib.impl.spi.InstanceType;
40 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.neighbors.Neighbor;
41 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.top.Bgp;
42 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.top.bgp.Global;
43 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.top.bgp.Neighbors;
44 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.network.instance.top.NetworkInstances;
45 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.network.instance.top.network.instances.NetworkInstance;
46 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.network.instance.top.network.instances.NetworkInstanceBuilder;
47 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.network.instance.top.network.instances.NetworkInstanceKey;
48 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.network.instance.top.network.instances.network.instance.Protocols;
49 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.network.instance.top.network.instances.network.instance.ProtocolsBuilder;
50 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.network.instance.top.network.instances.network.instance.protocols.Protocol;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.openconfig.extensions.rev160614.Protocol1;
52 import org.opendaylight.yangtools.concepts.ListenerRegistration;
53 import org.opendaylight.yangtools.yang.binding.DataObject;
54 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
55 import org.osgi.framework.BundleContext;
56 import org.osgi.framework.ServiceRegistration;
57 import org.osgi.service.blueprint.container.BlueprintContainer;
58 import org.slf4j.Logger;
59 import org.slf4j.LoggerFactory;
60
61 public final class BgpDeployerImpl implements BgpDeployer, ClusteredDataTreeChangeListener<Bgp>, AutoCloseable {
62     private static final Logger LOG = LoggerFactory.getLogger(BgpDeployerImpl.class);
63     private final InstanceIdentifier<NetworkInstance> networkInstanceIId;
64     private final BlueprintContainer container;
65     private final BundleContext bundleContext;
66     private final BGPTableTypeRegistryConsumer tableTypeRegistry;
67     private final ListenerRegistration<BgpDeployerImpl> registration;
68     @GuardedBy("this")
69     private final Map<InstanceIdentifier<Bgp>, RibImpl> ribs = new HashMap<>();
70     @GuardedBy("this")
71     private final Map<InstanceIdentifier<Neighbor>, PeerBean> peers = new HashMap<>();
72     private final DataBroker dataBroker;
73     @GuardedBy("this")
74     private boolean closed;
75
76     public BgpDeployerImpl(final String networkInstanceName, final BlueprintContainer container, final BundleContext bundleContext, final DataBroker dataBroker,
77         final BGPTableTypeRegistryConsumer mappingService) {
78         this.dataBroker = Preconditions.checkNotNull(dataBroker);
79         this.container = Preconditions.checkNotNull(container);
80         this.bundleContext = Preconditions.checkNotNull(bundleContext);
81         this.tableTypeRegistry = Preconditions.checkNotNull(mappingService);
82         this.networkInstanceIId = InstanceIdentifier.create(NetworkInstances.class)
83             .child(NetworkInstance.class, new NetworkInstanceKey(networkInstanceName));
84         Futures.addCallback(initializeNetworkInstance(dataBroker, this.networkInstanceIId), new FutureCallback<Void>() {
85             @Override
86             public void onSuccess(final Void result) {
87                 LOG.debug("Network Instance {} initialized successfully.", networkInstanceName);
88             }
89
90             @Override
91             public void onFailure(final Throwable t) {
92                 LOG.error("Failed to initialize Network Instance {}.", networkInstanceName, t);
93             }
94         });
95         this.registration = dataBroker.registerDataTreeChangeListener(new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION,
96             this.networkInstanceIId.child(Protocols.class).child(Protocol.class).augmentation(Protocol1.class).child(Bgp.class)), this);
97         LOG.info("BGP Deployer {} started.", networkInstanceName);
98     }
99
100     @Override
101     public synchronized void onDataTreeChanged(final Collection<DataTreeModification<Bgp>> changes) {
102         if (this.closed) {
103             LOG.trace("BGP Deployer was already closed, skipping changes.");
104             return;
105         }
106         for (final DataTreeModification<Bgp> dataTreeModification : changes) {
107             final InstanceIdentifier<Bgp> rootIdentifier = dataTreeModification.getRootPath().getRootIdentifier();
108             final DataObjectModification<Bgp> rootNode = dataTreeModification.getRootNode();
109             LOG.trace("BGP configuration has changed: {}", rootNode);
110             for (final DataObjectModification<? extends DataObject> dataObjectModification : rootNode.getModifiedChildren()) {
111                 if (dataObjectModification.getDataType().equals(Global.class)) {
112                     onGlobalChanged((DataObjectModification<Global>) dataObjectModification, rootIdentifier);
113                 } else if (dataObjectModification.getDataType().equals(Neighbors.class)) {
114                     onNeighborsChanged((DataObjectModification<Neighbors>) dataObjectModification, rootIdentifier);
115                 }
116             }
117         }
118     }
119
120     @Override
121     public InstanceIdentifier<NetworkInstance> getInstanceIdentifier() {
122         return this.networkInstanceIId;
123     }
124
125     @Override
126     public synchronized void close() throws Exception {
127         this.registration.close();
128         this.peers.values().forEach(PeerBean::close);
129         this.peers.clear();
130         this.ribs.values().forEach(RibImpl::close);
131         this.ribs.clear();
132         this.closed = true;
133     }
134
135     private static CheckedFuture<Void, TransactionCommitFailedException> initializeNetworkInstance(final DataBroker dataBroker,
136         final InstanceIdentifier<NetworkInstance> networkInstance) {
137         final WriteTransaction wTx = dataBroker.newWriteOnlyTransaction();
138         wTx.merge(LogicalDatastoreType.CONFIGURATION, networkInstance,
139             new NetworkInstanceBuilder().setName(networkInstance.firstKeyOf(NetworkInstance.class).getName()).setProtocols(new ProtocolsBuilder().build()).build());
140         return wTx.submit();
141     }
142
143     private void onGlobalChanged(final DataObjectModification<Global> dataObjectModification,
144         final InstanceIdentifier<Bgp> rootIdentifier) {
145         switch (dataObjectModification.getModificationType()) {
146         case DELETE:
147             onGlobalRemoved(rootIdentifier);
148             break;
149         case SUBTREE_MODIFIED:
150         case WRITE:
151             onGlobalModified(rootIdentifier, dataObjectModification.getDataAfter(), null);
152             break;
153         default:
154             break;
155         }
156     }
157
158     @Override
159     public synchronized void onGlobalModified(final InstanceIdentifier<Bgp> rootIdentifier, final Global global,
160         final WriteConfiguration configurationWriter) {
161         final RibImpl ribImpl = this.ribs.get(rootIdentifier);
162         if(ribImpl == null ) {
163             onGlobalCreated(rootIdentifier, global, configurationWriter);
164         } else if (!ribImpl.isGlobalEqual(global)) {
165             onGlobalUpdated(rootIdentifier, global, ribImpl, configurationWriter);
166         }
167     }
168
169     private List<PeerBean> closeAllBindedPeers(final InstanceIdentifier<Bgp> rootIdentifier) {
170         final List<PeerBean> filtered = new ArrayList<>();
171         this.peers.entrySet().stream().filter(entry -> entry.getKey().firstIdentifierOf(Bgp.class).contains(rootIdentifier)).forEach(entry -> {
172             final PeerBean peer = entry.getValue();
173             peer.close();
174             filtered.add(peer);
175         });
176         return filtered;
177     }
178
179     private synchronized void onGlobalCreated(final InstanceIdentifier<Bgp> rootIdentifier, final Global global,
180         final WriteConfiguration configurationWriter) {
181         LOG.debug("Creating RIB instance with configuration: {}", global);
182         final RibImpl ribImpl = (RibImpl) this.container.getComponentInstance(InstanceType.RIB.getBeanName());
183         initiateRibInstance(rootIdentifier, global, ribImpl, configurationWriter);
184         this.ribs.put(rootIdentifier, ribImpl);
185         LOG.debug("RIB instance created: {}", ribImpl);
186     }
187
188     private void onGlobalUpdated(final InstanceIdentifier<Bgp> rootIdentifier, final Global global, final RibImpl ribImpl,
189         final WriteConfiguration configurationWriter) {
190         LOG.debug("Modifying RIB instance with configuration: {}", global);
191         final List<PeerBean> closedPeers = closeAllBindedPeers(rootIdentifier);
192         ribImpl.close();
193         initiateRibInstance(rootIdentifier, global, ribImpl, configurationWriter);
194         closedPeers.forEach(peer -> peer.restart(ribImpl, this.tableTypeRegistry));
195         LOG.debug("RIB instance created: {}", ribImpl);
196     }
197
198     @Override
199     public synchronized void onGlobalRemoved(final InstanceIdentifier<Bgp> rootIdentifier) {
200         LOG.debug("Removing RIB instance: {}", rootIdentifier);
201         final RibImpl ribImpl = this.ribs.remove(rootIdentifier);
202         if (ribImpl != null) {
203             LOG.debug("RIB instance removed {}", ribImpl);
204             ribImpl.close();
205         }
206     }
207
208     private void registerRibInstance(final RibImpl ribImpl, final String ribInstanceName) {
209         final Dictionary<String, String> properties = new Hashtable<>();
210         properties.put(InstanceType.RIB.getBeanName(), ribInstanceName);
211         final ServiceRegistration<?> serviceRegistration = this.bundleContext.registerService(InstanceType.RIB.getServices(), ribImpl, properties);
212         ribImpl.setServiceRegistration(serviceRegistration);
213     }
214
215     private void initiateRibInstance(final InstanceIdentifier<Bgp> rootIdentifier, final Global global,
216         final RibImpl ribImpl, final WriteConfiguration configurationWriter) {
217         final String ribInstanceName = getRibInstanceName(rootIdentifier);
218         ribImpl.start(global, ribInstanceName, this.tableTypeRegistry, configurationWriter);
219         registerRibInstance(ribImpl, ribInstanceName);
220     }
221
222     private void onNeighborsChanged(final DataObjectModification<Neighbors> dataObjectModification,
223         final InstanceIdentifier<Bgp> rootIdentifier) {
224         for (final DataObjectModification<? extends DataObject> neighborModification : dataObjectModification.getModifiedChildren()) {
225             switch (neighborModification.getModificationType()) {
226             case DELETE:
227                 onNeighborRemoved(rootIdentifier, (Neighbor) neighborModification.getDataBefore());
228                 break;
229             case SUBTREE_MODIFIED:
230             case WRITE:
231                 onNeighborModified(rootIdentifier, (Neighbor) neighborModification.getDataAfter(), null);
232                 break;
233             default:
234                 break;
235             }
236         }
237     }
238
239     @Override
240     public synchronized void onNeighborModified(final InstanceIdentifier<Bgp> rootIdentifier, final Neighbor neighbor,
241         final WriteConfiguration configurationWriter) {
242         //restart peer instance with a new configuration
243         final PeerBean bgpPeer = this.peers.get(getNeighborInstanceIdentifier(rootIdentifier, neighbor.getKey()));
244         if (bgpPeer == null) {
245             onNeighborCreated(rootIdentifier, neighbor, configurationWriter);
246         } else if(!bgpPeer.containsEqualConfiguration(neighbor)){
247             onNeighborUpdated(bgpPeer, rootIdentifier, neighbor, configurationWriter);
248         }
249     }
250
251     private synchronized void onNeighborCreated(final InstanceIdentifier<Bgp> rootIdentifier, final Neighbor neighbor,
252         final WriteConfiguration configurationWriter) {
253         LOG.debug("Creating Peer instance with configuration: {}", neighbor);
254         final PeerBean bgpPeer;
255         if (OpenConfigMappingUtil.isApplicationPeer(neighbor)) {
256             bgpPeer = (PeerBean) this.container.getComponentInstance(InstanceType.APP_PEER.getBeanName());
257         } else {
258             bgpPeer = (PeerBean) this.container.getComponentInstance(InstanceType.PEER.getBeanName());
259         }
260         final InstanceIdentifier<Neighbor> neighborInstanceIdentifier = getNeighborInstanceIdentifier(rootIdentifier, neighbor.getKey());
261         initiatePeerInstance(rootIdentifier, neighborInstanceIdentifier, neighbor, bgpPeer, configurationWriter);
262         this.peers.put(neighborInstanceIdentifier, bgpPeer);
263         LOG.debug("Peer instance created {}", bgpPeer);
264     }
265
266     private void onNeighborUpdated(final PeerBean bgpPeer, final InstanceIdentifier<Bgp> rootIdentifier, final Neighbor neighbor,
267             final WriteConfiguration configurationWriter) {
268         LOG.debug("Updating Peer instance with configuration: {}", neighbor);
269         bgpPeer.close();
270         final InstanceIdentifier<Neighbor> neighborInstanceIdentifier = getNeighborInstanceIdentifier(rootIdentifier, neighbor.getKey());
271         initiatePeerInstance(rootIdentifier, neighborInstanceIdentifier, neighbor, bgpPeer, configurationWriter);
272         LOG.debug("Peer instance updated {}", bgpPeer);
273     }
274
275     @Override
276     public synchronized void onNeighborRemoved(final InstanceIdentifier<Bgp> rootIdentifier, final Neighbor neighbor) {
277         LOG.debug("Removing Peer instance: {}", rootIdentifier);
278         final PeerBean bgpPeer = this.peers.remove(getNeighborInstanceIdentifier(rootIdentifier, neighbor.getKey()));
279         if (bgpPeer != null) {
280             bgpPeer.close();
281             LOG.debug("Peer instance removed {}", bgpPeer);
282         }
283     }
284
285     private void registerPeerInstance(final BgpPeer bgpPeer, final String peerInstanceName) {
286         final Dictionary<String, String> properties = new Hashtable<>();
287         properties.put(InstanceType.PEER.getBeanName(), peerInstanceName);
288         final ServiceRegistration<?> serviceRegistration = this.bundleContext.registerService(InstanceType.PEER.getServices(), bgpPeer, properties);
289         bgpPeer.setServiceRegistration(serviceRegistration);
290     }
291
292     private void registerAppPeerInstance(final AppPeer appPeer, final String peerInstanceName) {
293         final Dictionary<String, String> properties = new Hashtable<>();
294         properties.put(InstanceType.PEER.getBeanName(), peerInstanceName);
295         final ServiceRegistration<?> serviceRegistration = this.bundleContext
296             .registerService(InstanceType.APP_PEER.getServices(), appPeer, properties);
297         appPeer.setServiceRegistration(serviceRegistration);
298     }
299
300     private void initiatePeerInstance(final InstanceIdentifier<Bgp> rootIdentifier, final InstanceIdentifier<Neighbor> neighborIdentifier, final Neighbor neighbor,
301         final PeerBean bgpPeer, final WriteConfiguration configurationWriter) {
302         final String peerInstanceName = getNeighborInstanceName(neighborIdentifier);
303         final RibImpl rib = this.ribs.get(rootIdentifier);
304         if (rib != null) {
305             bgpPeer.start(rib, neighbor, this.tableTypeRegistry, configurationWriter);
306             if (bgpPeer instanceof BgpPeer) {
307                 registerPeerInstance((BgpPeer) bgpPeer, peerInstanceName);
308             } else if(bgpPeer instanceof AppPeer) {
309                 registerAppPeerInstance((AppPeer) bgpPeer, peerInstanceName);
310             }
311         }
312     }
313
314     @Override
315     public BGPTableTypeRegistryConsumer getTableTypeRegistry() {
316         return this.tableTypeRegistry;
317     }
318
319     @Override
320     public <T extends DataObject> ListenableFuture<Void> writeConfiguration(final T data, final InstanceIdentifier<T> identifier) {
321         final ReadWriteTransaction wTx = this.dataBroker.newReadWriteTransaction();
322         wTx.put(LogicalDatastoreType.CONFIGURATION, identifier, data, true);
323         return wTx.submit();
324     }
325
326     @Override
327     public <T extends DataObject> ListenableFuture<Void> removeConfiguration(
328         final InstanceIdentifier<T> identifier) {
329         final WriteTransaction wTx = this.dataBroker.newWriteOnlyTransaction();
330         wTx.delete(LogicalDatastoreType.CONFIGURATION, identifier);
331         return wTx.submit();
332     }
333 }