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