2 * Copyright (c) 2017 AT&T Intellectual Property. All rights reserved.
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
9 package org.opendaylight.protocol.bgp.rib.impl.config;
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;
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;
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;
54 public final class BGPClusterSingletonService implements ClusterSingletonService, AutoCloseable {
56 private static final Logger LOG = LoggerFactory.getLogger(BGPClusterSingletonService.class);
58 private static final long TIMEOUT_NS = TimeUnit.SECONDS.toNanos(5);
59 private final InstanceIdentifier<Bgp> bgpIid;
61 private final Map<InstanceIdentifier<Neighbor>, PeerBean> peers = new HashMap<>();
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;
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;
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);
92 public synchronized void instantiateServiceInstance() {
93 if (this.ribImpl != null) {
94 this.ribImpl.instantiateServiceInstance();
95 this.peers.values().forEach(PeerBean::instantiateServiceInstance);
97 this.instantiated.set(true);
98 LOG.info("BGPClusterSingletonService {} instantiated", this.serviceGroupIdentifier.getValue());
102 public synchronized ListenableFuture<? extends CommitInfo> closeServiceInstance() {
103 LOG.info("BGPClusterSingletonService {} close service instance", this.serviceGroupIdentifier.getValue());
104 this.instantiated.set(false);
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();
110 final ListenableFuture<List<CommitInfo>> futureResult = Futures.allAsList(futurePeerCloseList);
111 Futures.addCallback(futureResult, new FutureCallback<List<? extends CommitInfo>>() {
113 public void onSuccess(final List<? extends CommitInfo> result) {
114 done.setFuture(Futures.transform(BGPClusterSingletonService.this.ribImpl.closeServiceInstance(),
115 input -> null, MoreExecutors.directExecutor()));
119 public void onFailure(final Throwable throwable) {
120 LOG.warn("Failed removing peers {}", throwable);
122 }, MoreExecutors.directExecutor());
128 public ServiceGroupIdentifier getIdentifier() {
129 return this.serviceGroupIdentifier;
132 synchronized void onGlobalChanged(final DataObjectModification<Global> dataObjectModification) {
133 switch (dataObjectModification.getModificationType()) {
135 LOG.debug("Removing RIB instance: {}", this.bgpIid);
136 if (this.ribImpl != null) {
137 LOG.debug("RIB instance removed {}", this.ribImpl);
138 closeAllBindedPeers();
142 case SUBTREE_MODIFIED:
144 final Global global = dataObjectModification.getDataAfter();
145 if (this.ribImpl == null) {
146 onGlobalCreated(global);
147 } else if (!this.ribImpl.isGlobalEqual(global)) {
148 onGlobalUpdated(global);
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);
163 private synchronized void onGlobalUpdated(final Global global) {
164 LOG.debug("Modifying RIB instance with configuration: {}", global);
165 final List<PeerBean> closedPeers = closeAllBindedPeers();
167 initiateRibInstance(global, this.ribImpl);
168 for (final PeerBean peer : closedPeers) {
169 peer.restart(this.ribImpl, this.bgpIid, this.peerGroupLoader, this.tableTypeRegistry);
171 if (this.instantiated.get()) {
172 closedPeers.forEach(PeerBean::instantiateServiceInstance);
174 LOG.debug("RIB instance created: {}", this.ribImpl);
177 private void closeRibService() {
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);
183 this.ribImpl.close();
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();
195 private synchronized List<PeerBean> closeAllBindedPeers() {
196 final List<PeerBean> filtered = new ArrayList<>();
197 this.peers.forEach((key, peer) -> {
199 peer.closeServiceInstance().get();
200 } catch (final Exception e) {
201 LOG.error("Peer instance failed to close service instance", e);
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);
218 public void close() {
219 LOG.info("BGPClusterSingletonService {} close", this.serviceGroupIdentifier.getValue());
220 this.peers.values().iterator().forEachRemaining(PeerBean::close);
221 this.ribImpl.close();
226 synchronized void onNeighborsChanged(final DataObjectModification<Neighbors> dataObjectModification) {
227 for (final DataObjectModification<? extends DataObject> neighborModification :
228 dataObjectModification.getModifiedChildren()) {
229 switch (neighborModification.getModificationType()) {
231 onNeighborRemoved((Neighbor) neighborModification.getDataBefore());
233 case SUBTREE_MODIFIED:
235 onNeighborModified((Neighbor) neighborModification.getDataAfter());
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);
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());
260 bgpPeer = (PeerBean) this.container.getComponentInstance(InstanceType.PEER.getBeanName());
262 final InstanceIdentifier<Neighbor> neighborInstanceIdentifier =
263 getNeighborInstanceIdentifier(this.bgpIid, neighbor.getKey());
264 initiatePeerInstance(neighborInstanceIdentifier, neighbor, bgpPeer);
265 this.peers.put(neighborInstanceIdentifier, bgpPeer);
267 final String groupName= getPeerGroupName(neighbor.getConfig());
268 String peerGroupName = null;
269 if (groupName != null) {
270 peerGroupName = StringUtils.substringBetween(groupName, "=\"", "\"");
273 if (peerGroupName != null) {
274 this.peersGroups.computeIfAbsent(peerGroupName, k -> new ArrayList<>()).add(bgpPeer);
276 LOG.debug("Peer instance created {}", bgpPeer);
279 private String getPeerGroupName(final Config config) {
280 if (config != null && config.getAugmentation(NeighborPeerGroupConfig.class) != null) {
281 return config.getAugmentation(NeighborPeerGroupConfig.class).getPeerGroup();
286 private synchronized void onNeighborUpdated(final PeerBean bgpPeer, final Neighbor neighbor) {
287 LOG.debug("Updating Peer instance with configuration: {}", neighbor);
290 final InstanceIdentifier<Neighbor> neighborInstanceIdentifier
291 = getNeighborInstanceIdentifier(this.bgpIid, neighbor.getKey());
292 initiatePeerInstance(neighborInstanceIdentifier, neighbor, bgpPeer);
293 LOG.debug("Peer instance updated {}", bgpPeer);
296 private void closePeer(final PeerBean bgpPeer) {
297 if (bgpPeer != null) {
299 bgpPeer.closeServiceInstance().get();
301 LOG.debug("Peer instance closed {}", bgpPeer);
302 } catch (final Exception e) {
303 LOG.error("Peer instance failed to close service instance", e);
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()));
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;
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);
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);
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);
349 if (this.instantiated.get()) {
350 bgpPeer.instantiateServiceInstance();
354 public synchronized void restartNeighbors(final String peerGroupName) {
355 final List<PeerBean> peerGroup = this.peersGroups.get(peerGroupName);
356 if (peerGroup == null) {
359 for (final PeerBean peer : peerGroup) {
361 peer.closeServiceInstance().get();
362 } catch (final Exception e) {
363 LOG.error("Peer instance failed to close service instance", e);
365 peer.restart(this.ribImpl, this.bgpIid, this.peerGroupLoader, this.tableTypeRegistry);
366 peer.instantiateServiceInstance();