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