2 * Copyright (c) 2021 PANTHEON.tech s.r.o. 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
8 package org.opendaylight.protocol.bgp.rib.impl.config;
10 import static java.util.Objects.requireNonNull;
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 java.util.Collection;
20 import java.util.HashMap;
21 import java.util.List;
23 import java.util.Optional;
24 import java.util.concurrent.ExecutionException;
25 import java.util.stream.Collectors;
26 import javax.annotation.PostConstruct;
27 import javax.annotation.PreDestroy;
28 import javax.inject.Inject;
29 import javax.inject.Singleton;
30 import org.checkerframework.checker.lock.qual.GuardedBy;
31 import org.opendaylight.mdsal.binding.api.ClusteredDataTreeChangeListener;
32 import org.opendaylight.mdsal.binding.api.DataBroker;
33 import org.opendaylight.mdsal.binding.api.DataObjectModification;
34 import org.opendaylight.mdsal.binding.api.DataTreeIdentifier;
35 import org.opendaylight.mdsal.binding.api.DataTreeModification;
36 import org.opendaylight.mdsal.binding.api.ReadTransaction;
37 import org.opendaylight.mdsal.binding.api.RpcProviderService;
38 import org.opendaylight.mdsal.binding.api.WriteTransaction;
39 import org.opendaylight.mdsal.common.api.CommitInfo;
40 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
41 import org.opendaylight.mdsal.dom.api.DOMDataBroker;
42 import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonServiceProvider;
43 import org.opendaylight.protocol.bgp.openconfig.routing.policy.spi.BGPRibRoutingPolicyFactory;
44 import org.opendaylight.protocol.bgp.openconfig.spi.BGPTableTypeRegistryConsumer;
45 import org.opendaylight.protocol.bgp.rib.impl.spi.BGPDispatcher;
46 import org.opendaylight.protocol.bgp.rib.impl.spi.CodecsRegistry;
47 import org.opendaylight.protocol.bgp.rib.spi.RIBExtensionConsumerContext;
48 import org.opendaylight.protocol.bgp.rib.spi.state.BGPStateProviderRegistry;
49 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.peer.group.PeerGroup;
50 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.peer.group.PeerGroupKey;
51 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.top.Bgp;
52 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.top.bgp.Global;
53 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.top.bgp.Neighbors;
54 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.top.bgp.PeerGroups;
55 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.OpenconfigNetworkInstanceData;
56 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.network.instance.top.NetworkInstances;
57 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.network.instance.top.network.instances.NetworkInstance;
58 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.network.instance.top.network.instances.NetworkInstanceBuilder;
59 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.network.instance.top.network.instances.NetworkInstanceKey;
60 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.network.instance.top.network.instances.network.instance.Protocols;
61 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.network.instance.top.network.instances.network.instance.ProtocolsBuilder;
62 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.network.instance.top.network.instances.network.instance.protocols.Protocol;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.openconfig.extensions.rev180329.NetworkInstanceProtocol;
64 import org.opendaylight.yangtools.concepts.ListenerRegistration;
65 import org.opendaylight.yangtools.yang.binding.DataObject;
66 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
67 import org.slf4j.Logger;
68 import org.slf4j.LoggerFactory;
71 // Non-final because of Mockito.spy()
72 public class DefaultBgpDeployer implements ClusteredDataTreeChangeListener<Bgp>, PeerGroupConfigLoader, AutoCloseable {
73 private static final Logger LOG = LoggerFactory.getLogger(DefaultBgpDeployer.class);
75 private final InstanceIdentifier<NetworkInstance> networkInstanceIId;
76 private final BGPTableTypeRegistryConsumer tableTypeRegistry;
77 private final ClusterSingletonServiceProvider provider;
78 private final RpcProviderService rpcRegistry;
79 private final RIBExtensionConsumerContext ribExtensionConsumerContext;
80 private final BGPDispatcher bgpDispatcher;
81 private final BGPRibRoutingPolicyFactory routingPolicyFactory;
82 private final BGPStateProviderRegistry stateProviderRegistry;
83 private final CodecsRegistry codecsRegistry;
84 private final DOMDataBroker domDataBroker;
85 private final DataBroker dataBroker;
88 private final Map<InstanceIdentifier<Bgp>, BGPClusterSingletonService> bgpCss = new HashMap<>();
89 private final LoadingCache<InstanceIdentifier<PeerGroup>, Optional<PeerGroup>> peerGroups =
90 CacheBuilder.newBuilder().build(new CacheLoader<InstanceIdentifier<PeerGroup>, Optional<PeerGroup>>() {
92 public Optional<PeerGroup> load(final InstanceIdentifier<PeerGroup> key)
93 throws ExecutionException, InterruptedException {
94 return loadPeerGroup(key);
97 private final String networkInstanceName;
98 private ListenerRegistration<DefaultBgpDeployer> registration;
100 private boolean closed;
103 public DefaultBgpDeployer(final String networkInstanceName,
104 final ClusterSingletonServiceProvider provider,
105 final RpcProviderService rpcRegistry,
106 final RIBExtensionConsumerContext ribExtensionConsumerContext,
107 final BGPDispatcher bgpDispatcher,
108 final BGPRibRoutingPolicyFactory routingPolicyFactory,
109 final CodecsRegistry codecsRegistry,
110 final DOMDataBroker domDataBroker,
111 final DataBroker dataBroker,
112 final BGPTableTypeRegistryConsumer tableTypeRegistry,
113 final BGPStateProviderRegistry stateProviderRegistry) {
114 this.dataBroker = requireNonNull(dataBroker);
115 this.provider = requireNonNull(provider);
116 this.networkInstanceName = requireNonNull(networkInstanceName);
117 this.tableTypeRegistry = requireNonNull(tableTypeRegistry);
118 this.stateProviderRegistry = requireNonNull(stateProviderRegistry);
119 this.rpcRegistry = requireNonNull(rpcRegistry);
120 this.ribExtensionConsumerContext = requireNonNull(ribExtensionConsumerContext);
121 this.bgpDispatcher = requireNonNull(bgpDispatcher);
122 this.routingPolicyFactory = requireNonNull(routingPolicyFactory);
123 this.codecsRegistry = requireNonNull(codecsRegistry);
124 this.domDataBroker = requireNonNull(domDataBroker);
126 InstanceIdentifier.builderOfInherited(OpenconfigNetworkInstanceData.class, NetworkInstances.class).build()
127 .child(NetworkInstance.class, new NetworkInstanceKey(this.networkInstanceName));
128 initializeNetworkInstance(dataBroker, networkInstanceIId).addCallback(new FutureCallback<CommitInfo>() {
130 public void onSuccess(final CommitInfo result) {
131 LOG.debug("Network Instance {} initialized successfully.", networkInstanceName);
135 public void onFailure(final Throwable throwable) {
136 LOG.error("Failed to initialize Network Instance {}.", networkInstanceName, throwable);
138 }, MoreExecutors.directExecutor());
142 // Split out of constructor to support partial mocking
143 public synchronized void init() {
144 registration = dataBroker.registerDataTreeChangeListener(
145 DataTreeIdentifier.create(LogicalDatastoreType.CONFIGURATION,
146 networkInstanceIId.child(Protocols.class).child(Protocol.class)
147 .augmentation(NetworkInstanceProtocol.class).child(Bgp.class)), this);
148 LOG.info("BGP Deployer {} started.", networkInstanceName);
151 private Optional<PeerGroup> loadPeerGroup(final InstanceIdentifier<PeerGroup> peerGroupIid)
152 throws ExecutionException, InterruptedException {
153 final FluentFuture<Optional<PeerGroup>> future;
154 try (ReadTransaction tx = dataBroker.newReadOnlyTransaction()) {
155 future = tx.read(LogicalDatastoreType.CONFIGURATION, peerGroupIid);
161 public synchronized void onDataTreeChanged(final Collection<DataTreeModification<Bgp>> changes) {
163 LOG.trace("BGP Deployer was already closed, skipping changes.");
167 for (final DataTreeModification<Bgp> dataTreeModification : changes) {
168 final InstanceIdentifier<Bgp> rootIdentifier = dataTreeModification.getRootPath().getRootIdentifier();
169 final DataObjectModification<Bgp> rootNode = dataTreeModification.getRootNode();
170 final List<DataObjectModification<? extends DataObject>> deletedConfig
171 = rootNode.getModifiedChildren().stream()
172 .filter(mod -> mod.getModificationType() == DataObjectModification.ModificationType.DELETE)
173 .collect(Collectors.toList());
174 final List<DataObjectModification<? extends DataObject>> changedConfig
175 = rootNode.getModifiedChildren().stream()
176 .filter(mod -> mod.getModificationType() != DataObjectModification.ModificationType.DELETE)
177 .collect(Collectors.toList());
178 handleDeletions(deletedConfig, rootIdentifier);
179 handleModifications(changedConfig, rootIdentifier);
183 private void handleModifications(final List<DataObjectModification<? extends DataObject>> changedConfig,
184 final InstanceIdentifier<Bgp> rootIdentifier) {
185 final List<DataObjectModification<? extends DataObject>> globalMod = changedConfig.stream()
186 .filter(mod -> mod.getDataType().equals(Global.class))
187 .collect(Collectors.toList());
188 final List<DataObjectModification<? extends DataObject>> peerMod = changedConfig.stream()
189 .filter(mod -> !mod.getDataType().equals(Global.class))
190 .collect(Collectors.toList());
191 if (!globalMod.isEmpty()) {
192 handleGlobalChange(globalMod, rootIdentifier);
194 if (!peerMod.isEmpty()) {
195 handlePeersChange(peerMod, rootIdentifier);
199 private void handleDeletions(final List<DataObjectModification<? extends DataObject>> deletedConfig,
200 final InstanceIdentifier<Bgp> rootIdentifier) {
201 final List<DataObjectModification<? extends DataObject>> globalMod = deletedConfig.stream()
202 .filter(mod -> mod.getDataType().equals(Global.class))
203 .collect(Collectors.toList());
204 final List<DataObjectModification<? extends DataObject>> peerMod = deletedConfig.stream()
205 .filter(mod -> !mod.getDataType().equals(Global.class))
206 .collect(Collectors.toList());
207 if (!globalMod.isEmpty()) {
208 handleGlobalChange(globalMod, rootIdentifier);
210 if (!peerMod.isEmpty()) {
211 handlePeersChange(peerMod, rootIdentifier);
215 private void handleGlobalChange(
216 final List<DataObjectModification<? extends DataObject>> config,
217 final InstanceIdentifier<Bgp> rootIdentifier) {
218 for (final DataObjectModification<? extends DataObject> dataObjectModification : config) {
219 onGlobalChanged((DataObjectModification<Global>) dataObjectModification, rootIdentifier);
223 private void handlePeersChange(
224 final List<DataObjectModification<? extends DataObject>> config,
225 final InstanceIdentifier<Bgp> rootIdentifier) {
226 for (final DataObjectModification<? extends DataObject> dataObjectModification : config) {
227 if (dataObjectModification.getDataType().equals(Neighbors.class)) {
228 onNeighborsChanged((DataObjectModification<Neighbors>) dataObjectModification, rootIdentifier);
229 } else if (dataObjectModification.getDataType().equals(PeerGroups.class)) {
230 rebootNeighbors((DataObjectModification<PeerGroups>) dataObjectModification);
235 private synchronized void rebootNeighbors(final DataObjectModification<PeerGroups> dataObjectModification) {
236 PeerGroups extPeerGroups = dataObjectModification.getDataAfter();
237 if (extPeerGroups == null) {
238 extPeerGroups = dataObjectModification.getDataBefore();
240 if (extPeerGroups == null) {
243 for (final PeerGroup peerGroup : extPeerGroups.nonnullPeerGroup().values()) {
244 bgpCss.values().forEach(css -> css.restartPeerGroup(peerGroup.getPeerGroupName()));
250 @SuppressWarnings("checkstyle:illegalCatch")
251 public synchronized void close() {
252 LOG.info("Closing BGP Deployer.");
253 if (registration != null) {
254 registration.close();
259 bgpCss.values().iterator().forEachRemaining(service -> {
262 } catch (Exception e) {
263 LOG.warn("Failed to close BGP Cluster Singleton Service.", e);
268 private static FluentFuture<? extends CommitInfo> initializeNetworkInstance(
269 final DataBroker dataBroker, final InstanceIdentifier<NetworkInstance> networkInstance) {
270 final WriteTransaction wTx = dataBroker.newWriteOnlyTransaction();
271 wTx.merge(LogicalDatastoreType.CONFIGURATION, networkInstance,
272 new NetworkInstanceBuilder().setName(networkInstance.firstKeyOf(NetworkInstance.class).getName())
273 .setProtocols(new ProtocolsBuilder().build()).build());
277 synchronized void onGlobalChanged(final DataObjectModification<Global> dataObjectModification,
278 final InstanceIdentifier<Bgp> bgpInstanceIdentifier) {
279 getBgpClusterSingleton(bgpInstanceIdentifier).onGlobalChanged(dataObjectModification);
282 synchronized void onNeighborsChanged(final DataObjectModification<Neighbors> dataObjectModification,
283 final InstanceIdentifier<Bgp> bgpInstanceIdentifier) {
284 getBgpClusterSingleton(bgpInstanceIdentifier).onNeighborsChanged(dataObjectModification);
288 synchronized BGPClusterSingletonService getBgpClusterSingleton(
289 final InstanceIdentifier<Bgp> bgpInstanceIdentifier) {
290 BGPClusterSingletonService old = bgpCss.get(bgpInstanceIdentifier);
292 old = new BGPClusterSingletonService(this, provider, tableTypeRegistry,
293 rpcRegistry, ribExtensionConsumerContext, bgpDispatcher, routingPolicyFactory,
294 codecsRegistry, stateProviderRegistry, domDataBroker, bgpInstanceIdentifier);
295 bgpCss.put(bgpInstanceIdentifier, old);
301 public PeerGroup getPeerGroup(final InstanceIdentifier<Bgp> bgpIid, final String peerGroupName) {
302 final InstanceIdentifier<PeerGroup> peerGroupsIid = bgpIid.child(PeerGroups.class)
303 .child(PeerGroup.class, new PeerGroupKey(peerGroupName));
304 return peerGroups.getUnchecked(peerGroupsIid).orElse(null);