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