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.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;
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;
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;
57 public final class BGPClusterSingletonService implements ClusterSingletonService, AutoCloseable {
59 private static final Logger LOG = LoggerFactory.getLogger(BGPClusterSingletonService.class);
61 private static final long TIMEOUT_NS = TimeUnit.SECONDS.toNanos(5);
62 private final InstanceIdentifier<Bgp> bgpIid;
64 private final Map<InstanceIdentifier<Neighbor>, PeerBean> peers = new HashMap<>();
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;
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;
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);
95 public synchronized void instantiateServiceInstance() {
96 if (this.ribImpl != null) {
97 this.ribImpl.instantiateServiceInstance();
98 this.peers.values().forEach(PeerBean::instantiateServiceInstance);
100 this.instantiated.set(true);
101 LOG.info("BGPClusterSingletonService {} instantiated", this.serviceGroupIdentifier.getValue());
105 public synchronized ListenableFuture<? extends CommitInfo> closeServiceInstance() {
106 LOG.info("BGPClusterSingletonService {} close service instance", this.serviceGroupIdentifier.getValue());
107 this.instantiated.set(false);
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();
113 final ListenableFuture<List<CommitInfo>> futureResult = Futures.allAsList(futurePeerCloseList);
114 Futures.addCallback(futureResult, new FutureCallback<List<? extends CommitInfo>>() {
116 public void onSuccess(final List<? extends CommitInfo> result) {
117 done.setFuture(Futures.transform(BGPClusterSingletonService.this.ribImpl.closeServiceInstance(),
118 input -> null, MoreExecutors.directExecutor()));
122 public void onFailure(final Throwable throwable) {
123 LOG.warn("Failed removing peers {}", throwable);
125 }, MoreExecutors.directExecutor());
131 public ServiceGroupIdentifier getIdentifier() {
132 return this.serviceGroupIdentifier;
135 synchronized void onGlobalChanged(final DataObjectModification<Global> dataObjectModification) {
136 switch (dataObjectModification.getModificationType()) {
138 LOG.debug("Removing RIB instance: {}", this.bgpIid);
139 if (this.ribImpl != null) {
140 LOG.debug("RIB instance removed {}", this.ribImpl);
141 closeAllBindedPeers();
146 case SUBTREE_MODIFIED:
148 final Global global = dataObjectModification.getDataAfter();
149 if (this.ribImpl == null) {
150 onGlobalCreated(global);
151 } else if (!this.ribImpl.isGlobalEqual(global)) {
152 onGlobalUpdated(global);
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);
167 private synchronized void onGlobalUpdated(final Global global) {
168 LOG.debug("Modifying RIB instance with configuration: {}", global);
169 final List<PeerBean> closedPeers = closeAllBindedPeers();
171 initiateRibInstance(global, this.ribImpl);
172 for (final PeerBean peer : closedPeers) {
173 peer.restart(this.ribImpl, this.bgpIid, this.peerGroupLoader, this.tableTypeRegistry);
175 if (this.instantiated.get()) {
176 closedPeers.forEach(PeerBean::instantiateServiceInstance);
178 LOG.debug("RIB instance created: {}", this.ribImpl);
181 private void closeRibService() {
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);
187 this.ribImpl.close();
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();
199 private synchronized List<PeerBean> closeAllBindedPeers() {
200 final List<PeerBean> filtered = new ArrayList<>();
201 this.peers.forEach((key, peer) -> {
203 peer.closeServiceInstance().get();
204 } catch (final Exception e) {
205 LOG.error("Peer instance failed to close service instance", e);
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);
222 public void close() {
223 LOG.info("BGPClusterSingletonService {} close", this.serviceGroupIdentifier.getValue());
224 this.peers.values().iterator().forEachRemaining(PeerBean::close);
225 this.ribImpl.close();
230 synchronized void onNeighborsChanged(final DataObjectModification<Neighbors> dataObjectModification) {
231 for (final DataObjectModification<? extends DataObject> neighborModification :
232 dataObjectModification.getModifiedChildren()) {
233 switch (neighborModification.getModificationType()) {
235 onNeighborRemoved((Neighbor) neighborModification.getDataBefore());
237 case SUBTREE_MODIFIED:
239 onNeighborModified((Neighbor) neighborModification.getDataAfter());
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);
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());
263 bgpPeer = (PeerBean) this.container.getComponentInstance(InstanceType.PEER.getBeanName());
265 final InstanceIdentifier<Neighbor> neighborInstanceIdentifier =
266 getNeighborInstanceIdentifier(this.bgpIid, neighbor.key());
267 initiatePeerInstance(neighborInstanceIdentifier, neighbor, bgpPeer);
268 this.peers.put(neighborInstanceIdentifier, bgpPeer);
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());
275 private static Optional<String> getPeerGroupName(final Config config) {
276 if (config == null) {
277 return Optional.empty();
279 final NeighborPeerGroupConfig aug = config.augmentation(NeighborPeerGroupConfig.class);
280 if(aug == null || aug.getPeerGroup() == null) {
281 return Optional.empty();
283 final String peerGroupName = aug.getPeerGroup();
284 if (peerGroupName.equals(APPLICATION_PEER_GROUP_NAME)) {
285 return APPLICATION_PEER_GROUP_NAME_OPT;
287 return Optional.of(StringUtils.substringBetween(peerGroupName, "=\"", "\""));
290 private synchronized void onNeighborUpdated(final PeerBean bgpPeer, final Neighbor neighbor) {
291 LOG.debug("Updating Peer instance with configuration: {}", neighbor);
294 final InstanceIdentifier<Neighbor> neighborInstanceIdentifier
295 = getNeighborInstanceIdentifier(this.bgpIid, neighbor.key());
296 initiatePeerInstance(neighborInstanceIdentifier, neighbor, bgpPeer);
297 LOG.debug("Peer instance updated {}", bgpPeer);
300 private static void closePeer(final PeerBean bgpPeer) {
301 if (bgpPeer != null) {
303 bgpPeer.closeServiceInstance().get();
305 LOG.debug("Peer instance closed {}", bgpPeer);
306 } catch (final Exception e) {
307 LOG.error("Peer instance failed to close service instance", e);
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()));
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;
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);
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);
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);
351 if (this.instantiated.get()) {
352 bgpPeer.instantiateServiceInstance();
356 synchronized void restartNeighbors(final String peerGroupName) {
357 final List<PeerBean> peerGroup = this.peersGroups.get(peerGroupName);
358 if (peerGroup == null) {
361 for (final PeerBean peer : peerGroup) {
363 peer.closeServiceInstance().get();
364 } catch (final Exception e) {
365 LOG.error("Peer instance failed to close service instance", e);
367 peer.restart(this.ribImpl, this.bgpIid, this.peerGroupLoader, this.tableTypeRegistry);
368 peer.instantiateServiceInstance();