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