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