Fix checkstyle complains
[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 java.util.Objects.requireNonNull;
12
13 import com.google.common.annotations.VisibleForTesting;
14 import com.google.common.cache.CacheBuilder;
15 import com.google.common.cache.CacheLoader;
16 import com.google.common.cache.LoadingCache;
17 import com.google.common.util.concurrent.FluentFuture;
18 import com.google.common.util.concurrent.FutureCallback;
19 import com.google.common.util.concurrent.MoreExecutors;
20 import java.util.Collection;
21 import java.util.HashMap;
22 import java.util.List;
23 import java.util.Map;
24 import java.util.Optional;
25 import java.util.concurrent.ExecutionException;
26 import java.util.stream.Collectors;
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.ReadOnlyTransaction;
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.mdsal.common.api.CommitInfo;
37 import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonServiceProvider;
38 import org.opendaylight.protocol.bgp.openconfig.spi.BGPTableTypeRegistryConsumer;
39 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.peer.group.PeerGroup;
40 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.peer.group.PeerGroupKey;
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.bgp.rev151009.bgp.top.bgp.PeerGroups;
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.rev180329.NetworkInstanceProtocol;
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.service.blueprint.container.BlueprintContainer;
58 import org.slf4j.Logger;
59 import org.slf4j.LoggerFactory;
60
61 public final class BgpDeployerImpl implements ClusteredDataTreeChangeListener<Bgp>, PeerGroupConfigLoader,
62         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 BGPTableTypeRegistryConsumer tableTypeRegistry;
68     private final ClusterSingletonServiceProvider provider;
69     @GuardedBy("this")
70     private final Map<InstanceIdentifier<Bgp>, BGPClusterSingletonService> bgpCss = new HashMap<>();
71     private final DataBroker dataBroker;
72     private final LoadingCache<InstanceIdentifier<PeerGroup>, Optional<PeerGroup>> peerGroups
73             = CacheBuilder.newBuilder()
74             .build(new CacheLoader<InstanceIdentifier<PeerGroup>, Optional<PeerGroup>>() {
75                 @Override
76                 public Optional<PeerGroup> load(final InstanceIdentifier<PeerGroup> key)
77                         throws ExecutionException, InterruptedException {
78                     return loadPeerGroup(key);
79                 }
80             });
81     private final String networkInstanceName;
82     private ListenerRegistration<BgpDeployerImpl> registration;
83     @GuardedBy("this")
84     private boolean closed;
85
86     public BgpDeployerImpl(final String networkInstanceName, final ClusterSingletonServiceProvider provider,
87             final BlueprintContainer container,
88             final BundleContext bundleContext, final DataBroker dataBroker,
89             final BGPTableTypeRegistryConsumer mappingService) {
90         this.dataBroker = requireNonNull(dataBroker);
91         this.provider = requireNonNull(provider);
92         this.networkInstanceName = requireNonNull(networkInstanceName);
93         this.container = requireNonNull(container);
94         this.bundleContext = requireNonNull(bundleContext);
95         this.tableTypeRegistry = requireNonNull(mappingService);
96         this.networkInstanceIId = InstanceIdentifier.create(NetworkInstances.class)
97                 .child(NetworkInstance.class, new NetworkInstanceKey(networkInstanceName));
98         initializeNetworkInstance(dataBroker, this.networkInstanceIId).addCallback(new FutureCallback<CommitInfo>() {
99             @Override
100             public void onSuccess(final CommitInfo result) {
101                 LOG.debug("Network Instance {} initialized successfully.", networkInstanceName);
102             }
103
104             @Override
105             public void onFailure(final Throwable throwable) {
106                 LOG.error("Failed to initialize Network Instance {}.", networkInstanceName, throwable);
107             }
108         }, MoreExecutors.directExecutor());
109     }
110
111     public synchronized void init() {
112         this.registration = this.dataBroker.registerDataTreeChangeListener(
113                 new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION,
114                         this.networkInstanceIId.child(Protocols.class).child(Protocol.class)
115                                 .augmentation(NetworkInstanceProtocol.class).child(Bgp.class)), this);
116         LOG.info("BGP Deployer {} started.", this.networkInstanceName);
117     }
118
119     private Optional<PeerGroup> loadPeerGroup(final InstanceIdentifier<PeerGroup> peerGroupIid)
120             throws ExecutionException, InterruptedException {
121         final ReadOnlyTransaction tr = this.dataBroker.newReadOnlyTransaction();
122         return tr.read(LogicalDatastoreType.CONFIGURATION, peerGroupIid).get().toJavaUtil();
123     }
124
125     @Override
126     public synchronized void onDataTreeChanged(final Collection<DataTreeModification<Bgp>> changes) {
127         if (this.closed) {
128             LOG.trace("BGP Deployer was already closed, skipping changes.");
129             return;
130         }
131
132         for (final DataTreeModification<Bgp> dataTreeModification : changes) {
133             final InstanceIdentifier<Bgp> rootIdentifier = dataTreeModification.getRootPath().getRootIdentifier();
134             final DataObjectModification<Bgp> rootNode = dataTreeModification.getRootNode();
135             final List<DataObjectModification<? extends DataObject>> deletedConfig
136                     = rootNode.getModifiedChildren().stream()
137                     .filter(mod -> mod.getModificationType() == DataObjectModification.ModificationType.DELETE)
138                     .collect(Collectors.toList());
139             final List<DataObjectModification<? extends DataObject>> changedConfig
140                     = rootNode.getModifiedChildren().stream()
141                     .filter(mod -> mod.getModificationType() != DataObjectModification.ModificationType.DELETE)
142                     .collect(Collectors.toList());
143             handleDeletions(deletedConfig, rootIdentifier);
144             handleModifications(changedConfig, rootIdentifier);
145         }
146     }
147
148     private void handleModifications(final List<DataObjectModification<? extends DataObject>> changedConfig,
149             final InstanceIdentifier<Bgp> rootIdentifier) {
150         final List<DataObjectModification<? extends DataObject>> globalMod = changedConfig.stream()
151                 .filter(mod -> mod.getDataType().equals(Global.class))
152                 .collect(Collectors.toList());
153         final List<DataObjectModification<? extends DataObject>> peerMod = changedConfig.stream()
154                 .filter(mod -> !mod.getDataType().equals(Global.class))
155                 .collect(Collectors.toList());
156         if (!globalMod.isEmpty()) {
157             handleGlobalChange(globalMod, rootIdentifier);
158         }
159         if (!peerMod.isEmpty()) {
160             handlePeersChange(peerMod, rootIdentifier);
161         }
162     }
163
164     private void handleDeletions(final List<DataObjectModification<? extends DataObject>> deletedConfig,
165             final InstanceIdentifier<Bgp> rootIdentifier) {
166         final List<DataObjectModification<? extends DataObject>> globalMod = deletedConfig.stream()
167                 .filter(mod -> mod.getDataType().equals(Global.class))
168                 .collect(Collectors.toList());
169         final List<DataObjectModification<? extends DataObject>> peerMod = deletedConfig.stream()
170                 .filter(mod -> !mod.getDataType().equals(Global.class))
171                 .collect(Collectors.toList());
172         if (!peerMod.isEmpty()) {
173             handleGlobalChange(peerMod, rootIdentifier);
174         }
175         if (!globalMod.isEmpty()) {
176             handlePeersChange(globalMod, rootIdentifier);
177         }
178     }
179
180     private void handleGlobalChange(
181             final List<DataObjectModification<? extends DataObject>> config,
182             final InstanceIdentifier<Bgp> rootIdentifier) {
183         for (final DataObjectModification<? extends DataObject> dataObjectModification : config) {
184             onGlobalChanged((DataObjectModification<Global>) dataObjectModification, rootIdentifier);
185         }
186     }
187
188     private void handlePeersChange(
189             final List<DataObjectModification<? extends DataObject>> config,
190             final InstanceIdentifier<Bgp> rootIdentifier) {
191         for (final DataObjectModification<? extends DataObject> dataObjectModification : config) {
192             if (dataObjectModification.getDataType().equals(Neighbors.class)) {
193                 onNeighborsChanged((DataObjectModification<Neighbors>) dataObjectModification, rootIdentifier);
194             } else if (dataObjectModification.getDataType().equals(PeerGroups.class)) {
195                 rebootNeighbors((DataObjectModification<PeerGroups>) dataObjectModification);
196             }
197         }
198     }
199
200     private synchronized void rebootNeighbors(final DataObjectModification<PeerGroups> dataObjectModification) {
201         PeerGroups peerGroups = dataObjectModification.getDataAfter();
202         if (peerGroups == null) {
203             peerGroups = dataObjectModification.getDataBefore();
204         }
205         if (peerGroups == null) {
206             return;
207         }
208         for (final PeerGroup peerGroup: peerGroups.getPeerGroup()) {
209             this.bgpCss.values().forEach(css->css.restartNeighbors(peerGroup.getPeerGroupName()));
210         }
211     }
212
213     @Override
214     public synchronized void close() {
215         LOG.info("Closing BGP Deployer.");
216         if (this.registration != null) {
217             this.registration.close();
218             this.registration = null;
219         }
220         this.closed = true;
221
222         this.bgpCss.values().iterator().forEachRemaining(service -> {
223             try {
224                 service.close();
225             } catch (Exception e) {
226                 LOG.warn("Failed to close BGP Cluster Singleton Service.");
227             }
228         });
229
230     }
231
232     private static FluentFuture<? extends CommitInfo> initializeNetworkInstance(
233             final DataBroker dataBroker, final InstanceIdentifier<NetworkInstance> networkInstance) {
234         final WriteTransaction wTx = dataBroker.newWriteOnlyTransaction();
235         wTx.merge(LogicalDatastoreType.CONFIGURATION, networkInstance,
236                 new NetworkInstanceBuilder().setName(networkInstance.firstKeyOf(NetworkInstance.class).getName())
237                         .setProtocols(new ProtocolsBuilder().build()).build());
238         return wTx.commit();
239     }
240
241     @VisibleForTesting
242     synchronized void onGlobalChanged(final DataObjectModification<Global> dataObjectModification,
243             final InstanceIdentifier<Bgp> bgpInstanceIdentifier) {
244         BGPClusterSingletonService old = this.bgpCss.get(bgpInstanceIdentifier);
245         if (old == null) {
246             old = new BGPClusterSingletonService(this, this.provider, this.tableTypeRegistry,
247                     this.container, this.bundleContext, bgpInstanceIdentifier);
248             this.bgpCss.put(bgpInstanceIdentifier, old);
249         }
250         old.onGlobalChanged(dataObjectModification);
251     }
252
253     @VisibleForTesting
254     synchronized void onNeighborsChanged(final DataObjectModification<Neighbors> dataObjectModification,
255             final InstanceIdentifier<Bgp> bgpInstanceIdentifier) {
256         BGPClusterSingletonService old = this.bgpCss.get(bgpInstanceIdentifier);
257         if (old == null) {
258             old = new BGPClusterSingletonService(this, this.provider, this.tableTypeRegistry,
259                     this.container, this.bundleContext, bgpInstanceIdentifier);
260             this.bgpCss.put(bgpInstanceIdentifier, old);
261         }
262         old.onNeighborsChanged(dataObjectModification);
263     }
264
265     @VisibleForTesting
266     BGPTableTypeRegistryConsumer getTableTypeRegistry() {
267         return this.tableTypeRegistry;
268     }
269
270     @Override
271     public PeerGroup getPeerGroup(final InstanceIdentifier<Bgp> bgpIid, final String peerGroupName) {
272         final InstanceIdentifier<PeerGroup> peerGroupsIid =
273         bgpIid.child(PeerGroups.class).child(PeerGroup.class, new PeerGroupKey(peerGroupName));
274         return this.peerGroups.getUnchecked(peerGroupsIid).orElse(null);
275     }
276 }