1e05c55607c753fb799cb4f5d3d58eae72dc62b3
[bgpcep.git] / bgp / rib-impl / src / main / java / org / opendaylight / protocol / bgp / rib / impl / config / BGPClusterSingletonService.java
1 /*
2  * Copyright (c) 2017 AT&T Intellectual Property. 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.util.concurrent.FutureCallback;
16 import com.google.common.util.concurrent.Futures;
17 import com.google.common.util.concurrent.ListenableFuture;
18 import com.google.common.util.concurrent.MoreExecutors;
19 import com.google.common.util.concurrent.SettableFuture;
20 import java.util.ArrayList;
21 import java.util.Dictionary;
22 import java.util.HashMap;
23 import java.util.Hashtable;
24 import java.util.List;
25 import java.util.Map;
26 import java.util.concurrent.TimeUnit;
27 import java.util.concurrent.atomic.AtomicBoolean;
28 import java.util.stream.Collectors;
29 import javax.annotation.Nonnull;
30 import javax.annotation.concurrent.GuardedBy;
31 import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
32 import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonService;
33 import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonServiceProvider;
34 import org.opendaylight.mdsal.singleton.common.api.ServiceGroupIdentifier;
35 import org.opendaylight.protocol.bgp.openconfig.spi.BGPTableTypeRegistryConsumer;
36 import org.opendaylight.protocol.bgp.rib.impl.spi.InstanceType;
37 import org.opendaylight.protocol.bgp.rib.spi.util.ClusterSingletonServiceRegistrationHelper;
38 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.neighbor.group.Config;
39 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.neighbors.Neighbor;
40 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.top.Bgp;
41 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.top.bgp.Global;
42 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.top.bgp.Neighbors;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.openconfig.extensions.rev171207.Config2;
44 import org.opendaylight.yangtools.yang.binding.DataObject;
45 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
46 import org.osgi.framework.BundleContext;
47 import org.osgi.framework.ServiceRegistration;
48 import org.osgi.service.blueprint.container.BlueprintContainer;
49 import org.slf4j.Logger;
50 import org.slf4j.LoggerFactory;
51
52 public final class BGPClusterSingletonService implements ClusterSingletonService, AutoCloseable {
53
54     private static final Logger LOG = LoggerFactory.getLogger(BGPClusterSingletonService.class);
55
56     private static final long TIMEOUT_NS = TimeUnit.SECONDS.toNanos(5);
57     private final InstanceIdentifier<Bgp> bgpIid;
58     @GuardedBy("this")
59     private final Map<InstanceIdentifier<Neighbor>, PeerBean> peers = new HashMap<>();
60     @GuardedBy("this")
61     private final Map<String, List<PeerBean>> peersGroups = new HashMap<>();
62     private final BGPTableTypeRegistryConsumer tableTypeRegistry;
63     private final BlueprintContainer container;
64     private final BundleContext bundleContext;
65     private final ServiceGroupIdentifier serviceGroupIdentifier;
66     private final AtomicBoolean instantiated = new AtomicBoolean(false);
67     private final PeerGroupConfigLoader peerGroupLoader;
68     private RibImpl ribImpl;
69
70     BGPClusterSingletonService(
71             @Nonnull final PeerGroupConfigLoader peerGroupLoader,
72             @Nonnull final ClusterSingletonServiceProvider provider,
73             @Nonnull final BGPTableTypeRegistryConsumer tableTypeRegistry,
74             @Nonnull final BlueprintContainer container,
75             @Nonnull final BundleContext bundleContext,
76             @Nonnull final InstanceIdentifier<Bgp> bgpIid) {
77         this.peerGroupLoader = peerGroupLoader;
78         this.tableTypeRegistry = tableTypeRegistry;
79         this.container = container;
80         this.bundleContext = bundleContext;
81         this.bgpIid = bgpIid;
82         final String ribInstanceName = getRibInstanceName(bgpIid);
83         this.serviceGroupIdentifier = ServiceGroupIdentifier.create(ribInstanceName + "-service-group");
84         LOG.info("BGPClusterSingletonService {} registered", this.serviceGroupIdentifier.getValue());
85         ClusterSingletonServiceRegistrationHelper
86                 .registerSingletonService(provider, this);
87     }
88
89     @Override
90     public synchronized void instantiateServiceInstance() {
91         if (this.ribImpl != null) {
92             this.ribImpl.instantiateServiceInstance();
93             this.peers.values().forEach(PeerBean::instantiateServiceInstance);
94         }
95         this.instantiated.set(true);
96         LOG.info("BGPClusterSingletonService {} instantiated", this.serviceGroupIdentifier.getValue());
97     }
98
99     @Override
100     public synchronized ListenableFuture<Void> closeServiceInstance() {
101         LOG.info("BGPClusterSingletonService {} close service instance", this.serviceGroupIdentifier.getValue());
102         this.instantiated.set(false);
103
104         final List<ListenableFuture<Void>> futurePeerCloseList = this.peers.values().stream()
105                 .map(PeerBean::closeServiceInstance).collect(Collectors.toList());
106         final SettableFuture<Void> done = SettableFuture.create();
107         Futures.addCallback(Futures.allAsList(futurePeerCloseList), new FutureCallback<List<Void>>() {
108             @Override
109             public void onSuccess(final List<Void> result) {
110                 done.setFuture(BGPClusterSingletonService.this.ribImpl.closeServiceInstance());
111             }
112
113             @Override
114             public void onFailure(final Throwable throwable) {
115                 LOG.warn("Failed removing peers {}", throwable);
116             }
117         }, MoreExecutors.directExecutor());
118         return done;
119     }
120
121     @Nonnull
122     @Override
123     public ServiceGroupIdentifier getIdentifier() {
124         return this.serviceGroupIdentifier;
125     }
126
127     synchronized void onGlobalChanged(final DataObjectModification<Global> dataObjectModification) {
128         switch (dataObjectModification.getModificationType()) {
129             case DELETE:
130                 LOG.debug("Removing RIB instance: {}", this.bgpIid);
131                 if (this.ribImpl != null) {
132                     LOG.debug("RIB instance removed {}", this.ribImpl);
133                     closeAllBindedPeers();
134                     closeRibService();
135                 }
136                 break;
137             case SUBTREE_MODIFIED:
138             case WRITE:
139                 final Global global = dataObjectModification.getDataAfter();
140                 if (this.ribImpl == null) {
141                     onGlobalCreated(global);
142                 } else if (!this.ribImpl.isGlobalEqual(global)) {
143                     onGlobalUpdated(global);
144                 }
145                 break;
146             default:
147                 break;
148         }
149     }
150
151     private synchronized void onGlobalCreated(final Global global) {
152         LOG.debug("Creating RIB instance with configuration: {}", global);
153         this.ribImpl = (RibImpl) this.container.getComponentInstance(InstanceType.RIB.getBeanName());
154         initiateRibInstance(global, this.ribImpl);
155         LOG.debug("RIB instance created: {}", this.ribImpl);
156     }
157
158     private synchronized void onGlobalUpdated(final Global global) {
159         LOG.debug("Modifying RIB instance with configuration: {}", global);
160         final List<PeerBean> closedPeers = closeAllBindedPeers();
161         closeRibService();
162         initiateRibInstance(global, this.ribImpl);
163         for (final PeerBean peer : closedPeers) {
164             peer.restart(this.ribImpl, this.bgpIid, this.peerGroupLoader, this.tableTypeRegistry);
165         }
166         if (this.instantiated.get()) {
167             closedPeers.forEach(PeerBean::instantiateServiceInstance);
168         }
169         LOG.debug("RIB instance created: {}", this.ribImpl);
170     }
171
172     private void closeRibService() {
173         try {
174             this.ribImpl.closeServiceInstance().get(TIMEOUT_NS, TimeUnit.NANOSECONDS);
175         } catch (final Exception e) {
176             LOG.error("RIB instance failed to close service instance", e);
177         }
178         this.ribImpl.close();
179     }
180
181     private synchronized void initiateRibInstance(final Global global, final RibImpl ribImpl) {
182         final String ribInstanceName = getRibInstanceName(this.bgpIid);
183         ribImpl.start(global, ribInstanceName, this.tableTypeRegistry);
184         registerRibInstance(ribImpl, ribInstanceName);
185         if (this.instantiated.get()) {
186             this.ribImpl.instantiateServiceInstance();
187         }
188     }
189
190     private synchronized List<PeerBean> closeAllBindedPeers() {
191         final List<PeerBean> filtered = new ArrayList<>();
192         this.peers.forEach((key, peer) -> {
193             try {
194                 peer.closeServiceInstance().get();
195             } catch (final Exception e) {
196                 LOG.error("Peer instance failed to close service instance", e);
197             }
198             peer.close();
199             filtered.add(peer);
200         });
201         return filtered;
202     }
203
204     private synchronized void registerRibInstance(final RibImpl ribImpl, final String ribInstanceName) {
205         final Dictionary<String, String> properties = new Hashtable<>();
206         properties.put(InstanceType.RIB.getBeanName(), ribInstanceName);
207         final ServiceRegistration<?> serviceRegistration = this.bundleContext.registerService(
208                 InstanceType.RIB.getServices(), ribImpl, properties);
209         ribImpl.setServiceRegistration(serviceRegistration);
210     }
211
212     @Override
213     public void close() {
214         LOG.info("BGPClusterSingletonService {} close", this.serviceGroupIdentifier.getValue());
215         this.peers.values().iterator().forEachRemaining(PeerBean::close);
216         this.ribImpl.close();
217         this.peers.clear();
218         this.ribImpl = null;
219     }
220
221     synchronized void onNeighborsChanged(final DataObjectModification<Neighbors> dataObjectModification) {
222         for (final DataObjectModification<? extends DataObject> neighborModification :
223                 dataObjectModification.getModifiedChildren()) {
224             switch (neighborModification.getModificationType()) {
225                 case DELETE:
226                     onNeighborRemoved((Neighbor) neighborModification.getDataBefore());
227                     break;
228                 case SUBTREE_MODIFIED:
229                 case WRITE:
230                     onNeighborModified((Neighbor) neighborModification.getDataAfter());
231                     break;
232                 default:
233                     break;
234             }
235         }
236     }
237
238     private synchronized void onNeighborModified(final Neighbor neighbor) {
239         //restart peer instance with a new configuration
240         final PeerBean bgpPeer = this.peers.get(getNeighborInstanceIdentifier(this.bgpIid, neighbor.getKey()));
241         if (bgpPeer == null) {
242             onNeighborCreated(neighbor);
243         } else if (!bgpPeer.containsEqualConfiguration(neighbor)) {
244             onNeighborUpdated(bgpPeer, neighbor);
245         }
246     }
247
248
249     private synchronized void onNeighborCreated(final Neighbor neighbor) {
250         LOG.debug("Creating Peer instance with configuration: {}", neighbor);
251         final PeerBean bgpPeer;
252         if (OpenConfigMappingUtil.isApplicationPeer(neighbor)) {
253             bgpPeer = (PeerBean) this.container.getComponentInstance(InstanceType.APP_PEER.getBeanName());
254         } else {
255             bgpPeer = (PeerBean) this.container.getComponentInstance(InstanceType.PEER.getBeanName());
256         }
257         final InstanceIdentifier<Neighbor> neighborInstanceIdentifier =
258                 getNeighborInstanceIdentifier(this.bgpIid, neighbor.getKey());
259         initiatePeerInstance(neighborInstanceIdentifier, neighbor, bgpPeer);
260         this.peers.put(neighborInstanceIdentifier, bgpPeer);
261
262         final String groupName = getPeerGroupName(neighbor.getConfig());
263         if (groupName != null) {
264             this.peersGroups.computeIfAbsent(groupName, k -> new ArrayList<>()).add(bgpPeer);
265         }
266         LOG.debug("Peer instance created {}", bgpPeer);
267     }
268
269     private String getPeerGroupName(final Config config) {
270         if (config != null && config.getAugmentation(Config2.class) != null) {
271             return config.getAugmentation(Config2.class).getPeerGroup();
272         }
273         return null;
274     }
275
276     private synchronized void onNeighborUpdated(final PeerBean bgpPeer, final Neighbor neighbor) {
277         LOG.debug("Updating Peer instance with configuration: {}", neighbor);
278         closePeer(bgpPeer);
279
280         final InstanceIdentifier<Neighbor> neighborInstanceIdentifier
281                 = getNeighborInstanceIdentifier(this.bgpIid, neighbor.getKey());
282         initiatePeerInstance(neighborInstanceIdentifier, neighbor, bgpPeer);
283         LOG.debug("Peer instance updated {}", bgpPeer);
284     }
285
286     private void closePeer(final PeerBean bgpPeer) {
287         if (bgpPeer != null) {
288             try {
289                 bgpPeer.closeServiceInstance().get();
290                 bgpPeer.close();
291                 LOG.debug("Peer instance closed {}", bgpPeer);
292             } catch (final Exception e) {
293                 LOG.error("Peer instance failed to close service instance", e);
294             }
295         }
296     }
297
298     private synchronized void onNeighborRemoved(final Neighbor neighbor) {
299         LOG.debug("Removing Peer instance: {}", neighbor);
300         final PeerBean bgpPeer = this.peers.remove(getNeighborInstanceIdentifier(this.bgpIid, neighbor.getKey()));
301
302         final String groupName = getPeerGroupName(neighbor.getConfig());
303         if (groupName != null) {
304             this.peersGroups.computeIfPresent(groupName, (k, peers) -> {
305                 peers.remove(bgpPeer);
306                 return peers.isEmpty() ? null : peers;
307             });
308         }
309         closePeer(bgpPeer);
310     }
311
312     private synchronized void registerPeerInstance(final BgpPeer bgpPeer, final String peerInstanceName) {
313         final Dictionary<String, String> properties = new Hashtable<>();
314         properties.put(InstanceType.PEER.getBeanName(), peerInstanceName);
315         final ServiceRegistration<?> serviceRegistration = this.bundleContext
316                 .registerService(InstanceType.PEER.getServices(), bgpPeer, properties);
317         bgpPeer.setServiceRegistration(serviceRegistration);
318     }
319
320     private synchronized void registerAppPeerInstance(final AppPeer appPeer, final String peerInstanceName) {
321         final Dictionary<String, String> properties = new Hashtable<>();
322         properties.put(InstanceType.PEER.getBeanName(), peerInstanceName);
323         final ServiceRegistration<?> serviceRegistration = this.bundleContext
324                 .registerService(InstanceType.APP_PEER.getServices(), appPeer, properties);
325         appPeer.setServiceRegistration(serviceRegistration);
326     }
327
328     private synchronized void initiatePeerInstance(final InstanceIdentifier<Neighbor> neighborIdentifier,
329             final Neighbor neighbor, final PeerBean bgpPeer) {
330         final String peerInstanceName = getNeighborInstanceName(neighborIdentifier);
331         if (this.ribImpl != null) {
332             bgpPeer.start(this.ribImpl, neighbor, this.bgpIid, this.peerGroupLoader, this.tableTypeRegistry);
333             if (bgpPeer instanceof BgpPeer) {
334                 registerPeerInstance((BgpPeer) bgpPeer, peerInstanceName);
335             } else if (bgpPeer instanceof AppPeer) {
336                 registerAppPeerInstance((AppPeer) bgpPeer, peerInstanceName);
337             }
338         }
339         if (this.instantiated.get()) {
340             bgpPeer.instantiateServiceInstance();
341         }
342     }
343
344     public synchronized void restartNeighbors(final String peerGroupName) {
345         for (final PeerBean peer : this.peersGroups.get(peerGroupName)) {
346             peer.restart(this.ribImpl, this.bgpIid, this.peerGroupLoader, this.tableTypeRegistry);
347         }
348     }
349 }