Replace Preconditions.CheckNotNull per RequireNonNull
[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 com.google.common.util.concurrent.Futures.transform;
12 import static java.util.Objects.requireNonNull;
13 import static org.opendaylight.protocol.bgp.rib.impl.config.OpenConfigMappingUtil.getNeighborInstanceIdentifier;
14 import static org.opendaylight.protocol.bgp.rib.impl.config.OpenConfigMappingUtil.getNeighborInstanceName;
15 import static org.opendaylight.protocol.bgp.rib.impl.config.OpenConfigMappingUtil.getRibInstanceName;
16
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 com.google.common.util.concurrent.MoreExecutors;
21 import java.util.ArrayList;
22 import java.util.Collection;
23 import java.util.Dictionary;
24 import java.util.HashMap;
25 import java.util.Hashtable;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.stream.Collectors;
29 import javax.annotation.concurrent.GuardedBy;
30 import org.opendaylight.controller.md.sal.binding.api.ClusteredDataTreeChangeListener;
31 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
32 import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
33 import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
34 import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
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.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,
77         final BundleContext bundleContext, final DataBroker dataBroker,
78         final BGPTableTypeRegistryConsumer mappingService) {
79         this.dataBroker = requireNonNull(dataBroker);
80         this.container = requireNonNull(container);
81         this.bundleContext = requireNonNull(bundleContext);
82         this.tableTypeRegistry = requireNonNull(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         }, MoreExecutors.directExecutor());
96         this.registration = dataBroker.registerDataTreeChangeListener(
97             new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION, this.networkInstanceIId.child(Protocols.class)
98                 .child(Protocol.class).augmentation(Protocol1.class).child(Bgp.class)), this);
99         LOG.info("BGP Deployer {} started.", networkInstanceName);
100     }
101
102     @Override
103     public synchronized void onDataTreeChanged(final Collection<DataTreeModification<Bgp>> changes) {
104         if (this.closed) {
105             LOG.trace("BGP Deployer was already closed, skipping changes.");
106             return;
107         }
108         for (final DataTreeModification<Bgp> dataTreeModification : changes) {
109             final InstanceIdentifier<Bgp> rootIdentifier = dataTreeModification.getRootPath().getRootIdentifier();
110             final DataObjectModification<Bgp> rootNode = dataTreeModification.getRootNode();
111             LOG.trace("BGP configuration has changed: {}", rootNode);
112             for (final DataObjectModification<? extends DataObject> dataObjectModification : rootNode.getModifiedChildren()) {
113                 if (dataObjectModification.getDataType().equals(Global.class)) {
114                     onGlobalChanged((DataObjectModification<Global>) dataObjectModification, rootIdentifier);
115                 } else if (dataObjectModification.getDataType().equals(Neighbors.class)) {
116                     onNeighborsChanged((DataObjectModification<Neighbors>) dataObjectModification, rootIdentifier);
117                 }
118             }
119         }
120     }
121
122     @Override
123     public InstanceIdentifier<NetworkInstance> getInstanceIdentifier() {
124         return this.networkInstanceIId;
125     }
126
127     @Override
128     public synchronized void close() throws Exception {
129         LOG.info("Closing BGP Deployer.");
130         this.registration.close();
131         this.closed = true;
132
133         final List<ListenableFuture<Void>> futurePeerCloseList = this.peers.values().stream()
134             .map(PeerBean::closeServiceInstance).collect(Collectors.toList());
135         final ListenableFuture<List<Void>> futureOfRelevance = Futures.allAsList(futurePeerCloseList);
136
137         final ListenableFuture<ListenableFuture<List<Void>>> maxRelevanceFuture = transform(futureOfRelevance,
138             futurePeersClose -> {
139                 this.peers.values().forEach(PeerBean::close);
140                 BgpDeployerImpl.this.peers.clear();
141
142                 final List<ListenableFuture<Void>> futureRIBCloseList = BgpDeployerImpl.this.ribs.values().stream()
143                     .map(RibImpl::closeServiceInstance).collect(Collectors.toList());
144                 return Futures.allAsList(futureRIBCloseList);
145             }, MoreExecutors.directExecutor());
146
147         final ListenableFuture<Void> ribFutureClose = transform(maxRelevanceFuture, futurePeersClose -> {
148             BgpDeployerImpl.this.ribs.values().forEach(RibImpl::close);
149             this.ribs.clear();
150             return null;
151         }, MoreExecutors.directExecutor());
152
153         ribFutureClose.get();
154     }
155
156     private static ListenableFuture<Void> initializeNetworkInstance(
157         final DataBroker dataBroker, final InstanceIdentifier<NetworkInstance> networkInstance) {
158         final WriteTransaction wTx = dataBroker.newWriteOnlyTransaction();
159         wTx.merge(LogicalDatastoreType.CONFIGURATION, networkInstance,
160             new NetworkInstanceBuilder().setName(networkInstance.firstKeyOf(NetworkInstance.class).getName())
161                 .setProtocols(new ProtocolsBuilder().build()).build());
162         return wTx.submit();
163     }
164
165     private synchronized void onGlobalChanged(final DataObjectModification<Global> dataObjectModification,
166         final InstanceIdentifier<Bgp> rootIdentifier) {
167         switch (dataObjectModification.getModificationType()) {
168         case DELETE:
169             onGlobalRemoved(rootIdentifier);
170             break;
171         case SUBTREE_MODIFIED:
172         case WRITE:
173             onGlobalModified(rootIdentifier, dataObjectModification.getDataAfter(), null);
174             break;
175         default:
176             break;
177         }
178     }
179
180     @Override
181     public synchronized void onGlobalModified(final InstanceIdentifier<Bgp> rootIdentifier, final Global global,
182         final WriteConfiguration configurationWriter) {
183         final RibImpl ribImpl = this.ribs.get(rootIdentifier);
184         if(ribImpl == null ) {
185             onGlobalCreated(rootIdentifier, global, configurationWriter);
186         } else if (!ribImpl.isGlobalEqual(global)) {
187             onGlobalUpdated(rootIdentifier, global, ribImpl, configurationWriter);
188         }
189     }
190
191     private synchronized List<PeerBean> closeAllBindedPeers(final InstanceIdentifier<Bgp> rootIdentifier) {
192         final List<PeerBean> filtered = new ArrayList<>();
193         this.peers.entrySet().stream().filter(entry -> entry.getKey().firstIdentifierOf(Bgp.class)
194             .contains(rootIdentifier)).forEach(entry -> {
195             final PeerBean peer = entry.getValue();
196             try {
197                 peer.closeServiceInstance().get();
198             } catch (final Exception e) {
199                 LOG.error("Peer instance failed to close service instance", e);
200             }
201             peer.close();
202             filtered.add(peer);
203         });
204         return filtered;
205     }
206
207     private synchronized void onGlobalCreated(final InstanceIdentifier<Bgp> rootIdentifier, final Global global,
208         final WriteConfiguration configurationWriter) {
209         LOG.debug("Creating RIB instance with configuration: {}", global);
210         final RibImpl ribImpl = (RibImpl) this.container.getComponentInstance(InstanceType.RIB.getBeanName());
211         initiateRibInstance(rootIdentifier, global, ribImpl, configurationWriter);
212         this.ribs.put(rootIdentifier, ribImpl);
213         LOG.debug("RIB instance created: {}", ribImpl);
214     }
215
216     private synchronized void onGlobalUpdated(final InstanceIdentifier<Bgp> rootIdentifier, final Global global,
217         final RibImpl ribImpl, final WriteConfiguration configurationWriter) {
218         LOG.debug("Modifying RIB instance with configuration: {}", global);
219         final List<PeerBean> closedPeers = closeAllBindedPeers(rootIdentifier);
220         try {
221             ribImpl.closeServiceInstance().get();
222         } catch (final Exception e) {
223             LOG.error("RIB instance failed to close service instance", e);
224         }
225         ribImpl.close();
226         initiateRibInstance(rootIdentifier, global, ribImpl, configurationWriter);
227         closedPeers.forEach(peer -> peer.restart(ribImpl, this.tableTypeRegistry));
228         LOG.debug("RIB instance created: {}", ribImpl);
229     }
230
231     @Override
232     public synchronized void onGlobalRemoved(final InstanceIdentifier<Bgp> rootIdentifier) {
233         LOG.debug("Removing RIB instance: {}", rootIdentifier);
234         final RibImpl ribImpl = this.ribs.remove(rootIdentifier);
235         if (ribImpl != null) {
236             LOG.debug("RIB instance removed {}", ribImpl);
237             closeAllBindedPeers(rootIdentifier);
238             ribImpl.closeServiceInstance();
239             ribImpl.close();
240         }
241     }
242
243     private synchronized void registerRibInstance(final RibImpl ribImpl, final String ribInstanceName) {
244         final Dictionary<String, String> properties = new Hashtable<>();
245         properties.put(InstanceType.RIB.getBeanName(), ribInstanceName);
246         final ServiceRegistration<?> serviceRegistration = this.bundleContext.registerService(
247             InstanceType.RIB.getServices(), ribImpl, properties);
248         ribImpl.setServiceRegistration(serviceRegistration);
249     }
250
251     private synchronized void initiateRibInstance(final InstanceIdentifier<Bgp> rootIdentifier, final Global global,
252         final RibImpl ribImpl, final WriteConfiguration configurationWriter) {
253         final String ribInstanceName = getRibInstanceName(rootIdentifier);
254         ribImpl.start(global, ribInstanceName, this.tableTypeRegistry, configurationWriter);
255         registerRibInstance(ribImpl, ribInstanceName);
256     }
257
258     private synchronized void onNeighborsChanged(final DataObjectModification<Neighbors> dataObjectModification,
259         final InstanceIdentifier<Bgp> rootIdentifier) {
260         for (final DataObjectModification<? extends DataObject> neighborModification : dataObjectModification.getModifiedChildren()) {
261             switch (neighborModification.getModificationType()) {
262             case DELETE:
263                 onNeighborRemoved(rootIdentifier, (Neighbor) neighborModification.getDataBefore());
264                 break;
265             case SUBTREE_MODIFIED:
266             case WRITE:
267                 onNeighborModified(rootIdentifier, (Neighbor) neighborModification.getDataAfter(), null);
268                 break;
269             default:
270                 break;
271             }
272         }
273     }
274
275     @Override
276     public synchronized void onNeighborModified(final InstanceIdentifier<Bgp> rootIdentifier, final Neighbor neighbor,
277         final WriteConfiguration configurationWriter) {
278         //restart peer instance with a new configuration
279         final PeerBean bgpPeer = this.peers.get(getNeighborInstanceIdentifier(rootIdentifier, neighbor.getKey()));
280         if (bgpPeer == null) {
281             onNeighborCreated(rootIdentifier, neighbor, configurationWriter);
282         } else if(!bgpPeer.containsEqualConfiguration(neighbor)){
283             onNeighborUpdated(bgpPeer, rootIdentifier, neighbor, configurationWriter);
284         }
285     }
286
287     private synchronized void onNeighborCreated(final InstanceIdentifier<Bgp> rootIdentifier, final Neighbor neighbor,
288         final WriteConfiguration configurationWriter) {
289         LOG.debug("Creating Peer instance with configuration: {}", neighbor);
290         final PeerBean bgpPeer;
291         if (OpenConfigMappingUtil.isApplicationPeer(neighbor)) {
292             bgpPeer = (PeerBean) this.container.getComponentInstance(InstanceType.APP_PEER.getBeanName());
293         } else {
294             bgpPeer = (PeerBean) this.container.getComponentInstance(InstanceType.PEER.getBeanName());
295         }
296         final InstanceIdentifier<Neighbor> neighborInstanceIdentifier = getNeighborInstanceIdentifier(rootIdentifier, neighbor.getKey());
297         initiatePeerInstance(rootIdentifier, neighborInstanceIdentifier, neighbor, bgpPeer, configurationWriter);
298         this.peers.put(neighborInstanceIdentifier, bgpPeer);
299         LOG.debug("Peer instance created {}", bgpPeer);
300     }
301
302     private synchronized void onNeighborUpdated(final PeerBean bgpPeer, final InstanceIdentifier<Bgp> rootIdentifier, final Neighbor neighbor,
303             final WriteConfiguration configurationWriter) {
304         LOG.debug("Updating Peer instance with configuration: {}", neighbor);
305         bgpPeer.close();
306         final InstanceIdentifier<Neighbor> neighborInstanceIdentifier = getNeighborInstanceIdentifier(rootIdentifier, neighbor.getKey());
307         initiatePeerInstance(rootIdentifier, neighborInstanceIdentifier, neighbor, bgpPeer, configurationWriter);
308         LOG.debug("Peer instance updated {}", bgpPeer);
309     }
310
311     @Override
312     public synchronized void onNeighborRemoved(final InstanceIdentifier<Bgp> rootIdentifier, final Neighbor neighbor) {
313         LOG.debug("Removing Peer instance: {}", rootIdentifier);
314         final PeerBean bgpPeer = this.peers.remove(getNeighborInstanceIdentifier(rootIdentifier, neighbor.getKey()));
315         if (bgpPeer != null) {
316             bgpPeer.close();
317             LOG.debug("Peer instance removed {}", bgpPeer);
318         }
319     }
320
321     private synchronized void registerPeerInstance(final BgpPeer bgpPeer, final String peerInstanceName) {
322         final Dictionary<String, String> properties = new Hashtable<>();
323         properties.put(InstanceType.PEER.getBeanName(), peerInstanceName);
324         final ServiceRegistration<?> serviceRegistration = this.bundleContext
325             .registerService(InstanceType.PEER.getServices(), bgpPeer, properties);
326         bgpPeer.setServiceRegistration(serviceRegistration);
327     }
328
329     private synchronized void registerAppPeerInstance(final AppPeer appPeer, final String peerInstanceName) {
330         final Dictionary<String, String> properties = new Hashtable<>();
331         properties.put(InstanceType.PEER.getBeanName(), peerInstanceName);
332         final ServiceRegistration<?> serviceRegistration = this.bundleContext
333             .registerService(InstanceType.APP_PEER.getServices(), appPeer, properties);
334         appPeer.setServiceRegistration(serviceRegistration);
335     }
336
337     private synchronized void initiatePeerInstance(final InstanceIdentifier<Bgp> rootIdentifier,
338         final InstanceIdentifier<Neighbor> neighborIdentifier, final Neighbor neighbor,
339         final PeerBean bgpPeer, final WriteConfiguration configurationWriter) {
340         final String peerInstanceName = getNeighborInstanceName(neighborIdentifier);
341         final RibImpl rib = this.ribs.get(rootIdentifier);
342         if (rib != null) {
343             bgpPeer.start(rib, neighbor, this.tableTypeRegistry, configurationWriter);
344             if (bgpPeer instanceof BgpPeer) {
345                 registerPeerInstance((BgpPeer) bgpPeer, peerInstanceName);
346             } else if(bgpPeer instanceof AppPeer) {
347                 registerAppPeerInstance((AppPeer) bgpPeer, peerInstanceName);
348             }
349         }
350     }
351
352     @Override
353     public BGPTableTypeRegistryConsumer getTableTypeRegistry() {
354         return this.tableTypeRegistry;
355     }
356
357     @Override
358     public <T extends DataObject> ListenableFuture<Void> writeConfiguration(final T data,
359         final InstanceIdentifier<T> identifier) {
360         final WriteTransaction wTx = this.dataBroker.newWriteOnlyTransaction();
361         wTx.put(LogicalDatastoreType.CONFIGURATION, identifier, data, true);
362         return wTx.submit();
363     }
364
365     @Override
366     public <T extends DataObject> ListenableFuture<Void> removeConfiguration(
367         final InstanceIdentifier<T> identifier) {
368         final WriteTransaction wTx = this.dataBroker.newWriteOnlyTransaction();
369         wTx.delete(LogicalDatastoreType.CONFIGURATION, identifier);
370         return wTx.submit();
371     }
372 }