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