Remove OSGi dependency from bgp modules
[bgpcep.git] / bgp / rib-impl / src / main / java / org / opendaylight / protocol / bgp / rib / impl / config / BgpDeployerImpl.java
1 /*
2  * Copyright (c) 2016 Cisco Systems, Inc. and others.  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 java.util.Objects.requireNonNull;
12
13 import com.google.common.annotations.VisibleForTesting;
14 import com.google.common.cache.CacheBuilder;
15 import com.google.common.cache.CacheLoader;
16 import com.google.common.cache.LoadingCache;
17 import com.google.common.util.concurrent.FluentFuture;
18 import com.google.common.util.concurrent.FutureCallback;
19 import com.google.common.util.concurrent.MoreExecutors;
20 import java.util.Collection;
21 import java.util.HashMap;
22 import java.util.List;
23 import java.util.Map;
24 import java.util.Optional;
25 import java.util.concurrent.ExecutionException;
26 import java.util.stream.Collectors;
27 import javax.annotation.concurrent.GuardedBy;
28 import org.opendaylight.controller.md.sal.binding.api.ClusteredDataTreeChangeListener;
29 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
30 import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
31 import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
32 import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
33 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
34 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
35 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
36 import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
37 import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
38 import org.opendaylight.mdsal.binding.dom.codec.api.BindingCodecTreeFactory;
39 import org.opendaylight.mdsal.common.api.CommitInfo;
40 import org.opendaylight.mdsal.dom.api.DOMSchemaService;
41 import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonServiceProvider;
42 import org.opendaylight.protocol.bgp.openconfig.routing.policy.spi.BGPRibRoutingPolicyFactory;
43 import org.opendaylight.protocol.bgp.openconfig.spi.BGPTableTypeRegistryConsumer;
44 import org.opendaylight.protocol.bgp.rib.impl.spi.BGPDispatcher;
45 import org.opendaylight.protocol.bgp.rib.spi.RIBExtensionConsumerContext;
46 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.peer.group.PeerGroup;
47 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.peer.group.PeerGroupKey;
48 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.top.Bgp;
49 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.top.bgp.Global;
50 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.top.bgp.Neighbors;
51 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.top.bgp.PeerGroups;
52 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.network.instance.top.NetworkInstances;
53 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.network.instance.top.network.instances.NetworkInstance;
54 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.network.instance.top.network.instances.NetworkInstanceBuilder;
55 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.network.instance.top.network.instances.NetworkInstanceKey;
56 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.network.instance.top.network.instances.network.instance.Protocols;
57 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.network.instance.top.network.instances.network.instance.ProtocolsBuilder;
58 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.network.instance.top.network.instances.network.instance.protocols.Protocol;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.openconfig.extensions.rev180329.NetworkInstanceProtocol;
60 import org.opendaylight.yangtools.concepts.ListenerRegistration;
61 import org.opendaylight.yangtools.yang.binding.DataObject;
62 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
63 import org.slf4j.Logger;
64 import org.slf4j.LoggerFactory;
65
66 public final class BgpDeployerImpl implements ClusteredDataTreeChangeListener<Bgp>, PeerGroupConfigLoader,
67         AutoCloseable {
68     private static final Logger LOG = LoggerFactory.getLogger(BgpDeployerImpl.class);
69     private final InstanceIdentifier<NetworkInstance> networkInstanceIId;
70     private final BGPTableTypeRegistryConsumer tableTypeRegistry;
71     private final ClusterSingletonServiceProvider provider;
72     @GuardedBy("this")
73     private final Map<InstanceIdentifier<Bgp>, BGPClusterSingletonService> bgpCss = new HashMap<>();
74     private final DataBroker dataBroker;
75     private final LoadingCache<InstanceIdentifier<PeerGroup>, Optional<PeerGroup>> peerGroups
76             = CacheBuilder.newBuilder()
77             .build(new CacheLoader<InstanceIdentifier<PeerGroup>, Optional<PeerGroup>>() {
78                 @Override
79                 public Optional<PeerGroup> load(final InstanceIdentifier<PeerGroup> key)
80                         throws ExecutionException, InterruptedException {
81                     return loadPeerGroup(key);
82                 }
83             });
84     private final String networkInstanceName;
85     private ListenerRegistration<BgpDeployerImpl> registration;
86     @GuardedBy("this")
87     private boolean closed;
88     private final RIBExtensionConsumerContext ribExtensionContext;
89     private final BGPDispatcher dispatcher;
90     private final BGPRibRoutingPolicyFactory policyFactory;
91     private final BindingCodecTreeFactory codecFactory;
92     private final DOMDataBroker domBroker;
93     private final DOMSchemaService schemaService;
94     private final RpcProviderRegistry rpcRegistry;
95
96     public BgpDeployerImpl(final String networkInstanceName,
97                            final ClusterSingletonServiceProvider provider,
98                            final DataBroker dataBroker,
99                            final BGPTableTypeRegistryConsumer mappingService,
100                            final RIBExtensionConsumerContext ribExtensionContext,
101                            final BGPDispatcher dispatcher,
102                            final BGPRibRoutingPolicyFactory policyFactory,
103                            final BindingCodecTreeFactory codecFactory,
104                            final DOMDataBroker domBroker,
105                            final DOMSchemaService schemaService,
106                            final RpcProviderRegistry rpcRegistry) {
107         this.dataBroker = requireNonNull(dataBroker);
108         this.provider = requireNonNull(provider);
109         this.networkInstanceName = requireNonNull(networkInstanceName);
110         this.tableTypeRegistry = requireNonNull(mappingService);
111         this.ribExtensionContext = requireNonNull(ribExtensionContext);
112         this.dispatcher = requireNonNull(dispatcher);
113         this.policyFactory = requireNonNull(policyFactory);
114         this.codecFactory = requireNonNull(codecFactory);
115         this.domBroker = requireNonNull(domBroker);
116         this.schemaService = requireNonNull(schemaService);
117         this.rpcRegistry = rpcRegistry;
118         this.networkInstanceIId = InstanceIdentifier.create(NetworkInstances.class)
119                 .child(NetworkInstance.class, new NetworkInstanceKey(networkInstanceName));
120         initializeNetworkInstance(dataBroker, this.networkInstanceIId).addCallback(new FutureCallback<CommitInfo>() {
121             @Override
122             public void onSuccess(final CommitInfo result) {
123                 LOG.debug("Network Instance {} initialized successfully.", networkInstanceName);
124             }
125
126             @Override
127             public void onFailure(final Throwable throwable) {
128                 LOG.error("Failed to initialize Network Instance {}.", networkInstanceName, throwable);
129             }
130         }, MoreExecutors.directExecutor());
131     }
132
133     public synchronized void init() {
134         this.registration = this.dataBroker.registerDataTreeChangeListener(
135                 new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION,
136                         this.networkInstanceIId.child(Protocols.class).child(Protocol.class)
137                                 .augmentation(NetworkInstanceProtocol.class).child(Bgp.class)), this);
138         LOG.info("BGP Deployer {} started.", this.networkInstanceName);
139     }
140
141     private Optional<PeerGroup> loadPeerGroup(final InstanceIdentifier<PeerGroup> peerGroupIid)
142             throws ExecutionException, InterruptedException {
143         final ReadOnlyTransaction tr = this.dataBroker.newReadOnlyTransaction();
144         return tr.read(LogicalDatastoreType.CONFIGURATION, peerGroupIid).get().toJavaUtil();
145     }
146
147     @Override
148     public synchronized void onDataTreeChanged(final Collection<DataTreeModification<Bgp>> changes) {
149         if (this.closed) {
150             LOG.trace("BGP Deployer was already closed, skipping changes.");
151             return;
152         }
153
154         for (final DataTreeModification<Bgp> dataTreeModification : changes) {
155             final InstanceIdentifier<Bgp> rootIdentifier = dataTreeModification.getRootPath().getRootIdentifier();
156             final DataObjectModification<Bgp> rootNode = dataTreeModification.getRootNode();
157             final List<DataObjectModification<? extends DataObject>> deletedConfig
158                     = rootNode.getModifiedChildren().stream()
159                     .filter(mod -> mod.getModificationType() == DataObjectModification.ModificationType.DELETE)
160                     .collect(Collectors.toList());
161             final List<DataObjectModification<? extends DataObject>> changedConfig
162                     = rootNode.getModifiedChildren().stream()
163                     .filter(mod -> mod.getModificationType() != DataObjectModification.ModificationType.DELETE)
164                     .collect(Collectors.toList());
165             handleDeletions(deletedConfig, rootIdentifier);
166             handleModifications(changedConfig, rootIdentifier);
167         }
168     }
169
170     private void handleModifications(final List<DataObjectModification<? extends DataObject>> changedConfig,
171             final InstanceIdentifier<Bgp> rootIdentifier) {
172         final List<DataObjectModification<? extends DataObject>> globalMod = changedConfig.stream()
173                 .filter(mod -> mod.getDataType().equals(Global.class))
174                 .collect(Collectors.toList());
175         final List<DataObjectModification<? extends DataObject>> peerMod = changedConfig.stream()
176                 .filter(mod -> !mod.getDataType().equals(Global.class))
177                 .collect(Collectors.toList());
178         if (!globalMod.isEmpty()) {
179             handleGlobalChange(globalMod, rootIdentifier);
180         }
181         if (!peerMod.isEmpty()) {
182             handlePeersChange(peerMod, rootIdentifier);
183         }
184     }
185
186     private void handleDeletions(final List<DataObjectModification<? extends DataObject>> deletedConfig,
187             final InstanceIdentifier<Bgp> rootIdentifier) {
188         final List<DataObjectModification<? extends DataObject>> globalMod = deletedConfig.stream()
189                 .filter(mod -> mod.getDataType().equals(Global.class))
190                 .collect(Collectors.toList());
191         final List<DataObjectModification<? extends DataObject>> peerMod = deletedConfig.stream()
192                 .filter(mod -> !mod.getDataType().equals(Global.class))
193                 .collect(Collectors.toList());
194         if (!globalMod.isEmpty()) {
195             handleGlobalChange(globalMod, rootIdentifier);
196         }
197         if (!peerMod.isEmpty()) {
198             handlePeersChange(peerMod, rootIdentifier);
199         }
200     }
201
202     private void handleGlobalChange(
203             final List<DataObjectModification<? extends DataObject>> config,
204             final InstanceIdentifier<Bgp> rootIdentifier) {
205         for (final DataObjectModification<? extends DataObject> dataObjectModification : config) {
206             onGlobalChanged((DataObjectModification<Global>) dataObjectModification, rootIdentifier);
207         }
208     }
209
210     private void handlePeersChange(
211             final List<DataObjectModification<? extends DataObject>> config,
212             final InstanceIdentifier<Bgp> rootIdentifier) {
213         for (final DataObjectModification<? extends DataObject> dataObjectModification : config) {
214             if (dataObjectModification.getDataType().equals(Neighbors.class)) {
215                 onNeighborsChanged((DataObjectModification<Neighbors>) dataObjectModification, rootIdentifier);
216             } else if (dataObjectModification.getDataType().equals(PeerGroups.class)) {
217                 rebootNeighbors((DataObjectModification<PeerGroups>) dataObjectModification);
218             }
219         }
220     }
221
222     private synchronized void rebootNeighbors(final DataObjectModification<PeerGroups> dataObjectModification) {
223         PeerGroups peerGroups = dataObjectModification.getDataAfter();
224         if (peerGroups == null) {
225             peerGroups = dataObjectModification.getDataBefore();
226         }
227         if (peerGroups == null) {
228             return;
229         }
230         for (final PeerGroup peerGroup : peerGroups.getPeerGroup()) {
231             this.bgpCss.values().forEach(css -> css.restartNeighbors(peerGroup.getPeerGroupName()));
232         }
233     }
234
235     @Override
236     public synchronized void close() {
237         LOG.info("Closing BGP Deployer.");
238         if (this.registration != null) {
239             this.registration.close();
240             this.registration = null;
241         }
242         this.closed = true;
243
244         this.bgpCss.values().iterator().forEachRemaining(service -> {
245             try {
246                 service.close();
247             } catch (Exception e) {
248                 LOG.warn("Failed to close BGP Cluster Singleton Service.");
249             }
250         });
251
252     }
253
254     private static FluentFuture<? extends CommitInfo> initializeNetworkInstance(
255             final DataBroker dataBroker, final InstanceIdentifier<NetworkInstance> networkInstance) {
256         final WriteTransaction wTx = dataBroker.newWriteOnlyTransaction();
257         wTx.merge(LogicalDatastoreType.CONFIGURATION, networkInstance,
258                 new NetworkInstanceBuilder().setName(networkInstance.firstKeyOf(NetworkInstance.class).getName())
259                         .setProtocols(new ProtocolsBuilder().build()).build());
260         return wTx.commit();
261     }
262
263     @VisibleForTesting
264     synchronized void onGlobalChanged(final DataObjectModification<Global> dataObjectModification,
265             final InstanceIdentifier<Bgp> bgpInstanceIdentifier) {
266         BGPClusterSingletonService old = this.bgpCss.get(bgpInstanceIdentifier);
267         if (old == null) {
268             old = new BGPClusterSingletonService(this, this.provider, this.tableTypeRegistry,
269                     bgpInstanceIdentifier, this.ribExtensionContext, this.dispatcher, this.policyFactory,
270                     this.codecFactory, this.domBroker, this.dataBroker, this.schemaService, this.rpcRegistry);
271             this.bgpCss.put(bgpInstanceIdentifier, old);
272         }
273         old.onGlobalChanged(dataObjectModification);
274     }
275
276     @VisibleForTesting
277     synchronized void onNeighborsChanged(final DataObjectModification<Neighbors> dataObjectModification,
278             final InstanceIdentifier<Bgp> bgpInstanceIdentifier) {
279         BGPClusterSingletonService old = this.bgpCss.get(bgpInstanceIdentifier);
280         if (old == null) {
281             old = new BGPClusterSingletonService(this, this.provider, this.tableTypeRegistry,
282                     bgpInstanceIdentifier, this.ribExtensionContext, this.dispatcher, this.policyFactory,
283                     this.codecFactory, this.domBroker, this.dataBroker, this.schemaService, this.rpcRegistry);
284             this.bgpCss.put(bgpInstanceIdentifier, old);
285         }
286         old.onNeighborsChanged(dataObjectModification);
287     }
288
289     @VisibleForTesting
290     BGPTableTypeRegistryConsumer getTableTypeRegistry() {
291         return this.tableTypeRegistry;
292     }
293
294     @Override
295     public PeerGroup getPeerGroup(final InstanceIdentifier<Bgp> bgpIid, final String peerGroupName) {
296         final InstanceIdentifier<PeerGroup> peerGroupsIid = bgpIid.child(PeerGroups.class)
297                 .child(PeerGroup.class, new PeerGroupKey(peerGroupName));
298         return this.peerGroups.getUnchecked(peerGroupsIid).orElse(null);
299     }
300 }