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.HashMap;
20 import java.util.List;
22 import java.util.Optional;
23 import java.util.concurrent.ExecutionException;
24 import java.util.stream.Collectors;
25 import javax.annotation.PostConstruct;
26 import javax.annotation.PreDestroy;
27 import javax.inject.Inject;
28 import javax.inject.Singleton;
29 import org.checkerframework.checker.lock.qual.GuardedBy;
30 import org.opendaylight.mdsal.binding.api.DataBroker;
31 import org.opendaylight.mdsal.binding.api.DataObjectModification;
32 import org.opendaylight.mdsal.binding.api.DataTreeChangeListener;
33 import org.opendaylight.mdsal.binding.api.DataTreeIdentifier;
34 import org.opendaylight.mdsal.binding.api.DataTreeModification;
35 import org.opendaylight.mdsal.binding.api.ReadTransaction;
36 import org.opendaylight.mdsal.binding.api.RpcProviderService;
37 import org.opendaylight.mdsal.binding.api.WriteTransaction;
38 import org.opendaylight.mdsal.common.api.CommitInfo;
39 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
40 import org.opendaylight.mdsal.dom.api.DOMDataBroker;
41 import org.opendaylight.mdsal.singleton.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.impl.spi.CodecsRegistry;
46 import org.opendaylight.protocol.bgp.rib.spi.RIBExtensionConsumerContext;
47 import org.opendaylight.protocol.bgp.rib.spi.state.BGPStateProviderRegistry;
48 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.peer.group.PeerGroup;
49 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.peer.group.PeerGroupKey;
50 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.top.Bgp;
51 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.top.bgp.Global;
52 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.top.bgp.Neighbors;
53 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.top.bgp.PeerGroups;
54 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.OpenconfigNetworkInstanceData;
55 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.network.instance.top.NetworkInstances;
56 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.network.instance.top.network.instances.NetworkInstance;
57 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.network.instance.top.network.instances.NetworkInstanceBuilder;
58 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.network.instance.top.network.instances.NetworkInstanceKey;
59 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.network.instance.top.network.instances.network.instance.Protocols;
60 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.network.instance.top.network.instances.network.instance.ProtocolsBuilder;
61 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.network.instance.top.network.instances.network.instance.protocols.Protocol;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.openconfig.extensions.rev180329.NetworkInstanceProtocol;
63 import org.opendaylight.yangtools.concepts.Registration;
64 import org.opendaylight.yangtools.yang.binding.DataObject;
65 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
66 import org.slf4j.Logger;
67 import org.slf4j.LoggerFactory;
70 // Non-final because of Mockito.spy()
71 public class DefaultBgpDeployer implements DataTreeChangeListener<Bgp>, PeerGroupConfigLoader, AutoCloseable {
72 private static final Logger LOG = LoggerFactory.getLogger(DefaultBgpDeployer.class);
74 private final InstanceIdentifier<NetworkInstance> networkInstanceIId;
75 private final BGPTableTypeRegistryConsumer tableTypeRegistry;
76 private final ClusterSingletonServiceProvider provider;
77 private final RpcProviderService rpcRegistry;
78 private final RIBExtensionConsumerContext ribExtensionConsumerContext;
79 private final BGPDispatcher bgpDispatcher;
80 private final BGPRibRoutingPolicyFactory routingPolicyFactory;
81 private final BGPStateProviderRegistry stateProviderRegistry;
82 private final CodecsRegistry codecsRegistry;
83 private final DOMDataBroker domDataBroker;
84 private final DataBroker dataBroker;
87 private final Map<InstanceIdentifier<Bgp>, BGPClusterSingletonService> bgpCss = new HashMap<>();
88 private final LoadingCache<InstanceIdentifier<PeerGroup>, Optional<PeerGroup>> peerGroups =
89 CacheBuilder.newBuilder().build(new CacheLoader<InstanceIdentifier<PeerGroup>, Optional<PeerGroup>>() {
91 public Optional<PeerGroup> load(final InstanceIdentifier<PeerGroup> key)
92 throws ExecutionException, InterruptedException {
93 return loadPeerGroup(key);
96 private final String networkInstanceName;
97 private Registration registration;
99 private boolean closed;
102 public DefaultBgpDeployer(final String networkInstanceName,
103 final ClusterSingletonServiceProvider provider,
104 final RpcProviderService rpcRegistry,
105 final RIBExtensionConsumerContext ribExtensionConsumerContext,
106 final BGPDispatcher bgpDispatcher,
107 final BGPRibRoutingPolicyFactory routingPolicyFactory,
108 final CodecsRegistry codecsRegistry,
109 final DOMDataBroker domDataBroker,
110 final DataBroker dataBroker,
111 final BGPTableTypeRegistryConsumer tableTypeRegistry,
112 final BGPStateProviderRegistry stateProviderRegistry) {
113 this.dataBroker = requireNonNull(dataBroker);
114 this.provider = requireNonNull(provider);
115 this.networkInstanceName = requireNonNull(networkInstanceName);
116 this.tableTypeRegistry = requireNonNull(tableTypeRegistry);
117 this.stateProviderRegistry = requireNonNull(stateProviderRegistry);
118 this.rpcRegistry = requireNonNull(rpcRegistry);
119 this.ribExtensionConsumerContext = requireNonNull(ribExtensionConsumerContext);
120 this.bgpDispatcher = requireNonNull(bgpDispatcher);
121 this.routingPolicyFactory = requireNonNull(routingPolicyFactory);
122 this.codecsRegistry = requireNonNull(codecsRegistry);
123 this.domDataBroker = requireNonNull(domDataBroker);
125 InstanceIdentifier.builderOfInherited(OpenconfigNetworkInstanceData.class, NetworkInstances.class).build()
126 .child(NetworkInstance.class, new NetworkInstanceKey(this.networkInstanceName));
127 initializeNetworkInstance(dataBroker, networkInstanceIId).addCallback(new FutureCallback<CommitInfo>() {
129 public void onSuccess(final CommitInfo result) {
130 LOG.debug("Network Instance {} initialized successfully.", networkInstanceName);
134 public void onFailure(final Throwable throwable) {
135 LOG.error("Failed to initialize Network Instance {}.", networkInstanceName, throwable);
137 }, MoreExecutors.directExecutor());
141 // Split out of constructor to support partial mocking
142 public synchronized void init() {
143 registration = dataBroker.registerTreeChangeListener(
144 DataTreeIdentifier.of(LogicalDatastoreType.CONFIGURATION,
145 networkInstanceIId.child(Protocols.class).child(Protocol.class)
146 .augmentation(NetworkInstanceProtocol.class).child(Bgp.class)), this);
147 LOG.info("BGP Deployer {} started.", networkInstanceName);
150 private Optional<PeerGroup> loadPeerGroup(final InstanceIdentifier<PeerGroup> peerGroupIid)
151 throws ExecutionException, InterruptedException {
152 final FluentFuture<Optional<PeerGroup>> future;
153 try (ReadTransaction tx = dataBroker.newReadOnlyTransaction()) {
154 future = tx.read(LogicalDatastoreType.CONFIGURATION, peerGroupIid);
160 public synchronized void onDataTreeChanged(final List<DataTreeModification<Bgp>> changes) {
162 LOG.trace("BGP Deployer was already closed, skipping changes.");
166 for (var dataTreeModification : changes) {
167 final InstanceIdentifier<Bgp> rootIdentifier = dataTreeModification.getRootPath().path();
168 final DataObjectModification<Bgp> rootNode = dataTreeModification.getRootNode();
169 final List<DataObjectModification<? extends DataObject>> deletedConfig = rootNode.modifiedChildren()
171 .filter(mod -> mod.modificationType() == DataObjectModification.ModificationType.DELETE)
172 .collect(Collectors.toList());
173 final List<DataObjectModification<? extends DataObject>> changedConfig = rootNode.modifiedChildren()
175 .filter(mod -> mod.modificationType() != DataObjectModification.ModificationType.DELETE)
176 .collect(Collectors.toList());
177 handleDeletions(deletedConfig, rootIdentifier);
178 handleModifications(changedConfig, rootIdentifier);
182 private void handleModifications(final List<DataObjectModification<? extends DataObject>> changedConfig,
183 final InstanceIdentifier<Bgp> rootIdentifier) {
184 final List<DataObjectModification<? extends DataObject>> globalMod = changedConfig.stream()
185 .filter(mod -> mod.dataType().equals(Global.class))
186 .collect(Collectors.toList());
187 final List<DataObjectModification<? extends DataObject>> peerMod = changedConfig.stream()
188 .filter(mod -> !mod.dataType().equals(Global.class))
189 .collect(Collectors.toList());
190 if (!globalMod.isEmpty()) {
191 handleGlobalChange(globalMod, rootIdentifier);
193 if (!peerMod.isEmpty()) {
194 handlePeersChange(peerMod, rootIdentifier);
198 private void handleDeletions(final List<DataObjectModification<? extends DataObject>> deletedConfig,
199 final InstanceIdentifier<Bgp> rootIdentifier) {
200 final List<DataObjectModification<? extends DataObject>> globalMod = deletedConfig.stream()
201 .filter(mod -> mod.dataType().equals(Global.class))
202 .collect(Collectors.toList());
203 final List<DataObjectModification<? extends DataObject>> peerMod = deletedConfig.stream()
204 .filter(mod -> !mod.dataType().equals(Global.class))
205 .collect(Collectors.toList());
206 if (!globalMod.isEmpty()) {
207 handleGlobalChange(globalMod, rootIdentifier);
209 if (!peerMod.isEmpty()) {
210 handlePeersChange(peerMod, rootIdentifier);
214 private void handleGlobalChange(
215 final List<DataObjectModification<? extends DataObject>> config,
216 final InstanceIdentifier<Bgp> rootIdentifier) {
217 for (final DataObjectModification<? extends DataObject> dataObjectModification : config) {
218 onGlobalChanged((DataObjectModification<Global>) dataObjectModification, rootIdentifier);
222 private void handlePeersChange(
223 final List<DataObjectModification<? extends DataObject>> config,
224 final InstanceIdentifier<Bgp> rootIdentifier) {
225 for (final DataObjectModification<? extends DataObject> dataObjectModification : config) {
226 if (dataObjectModification.dataType().equals(Neighbors.class)) {
227 onNeighborsChanged((DataObjectModification<Neighbors>) dataObjectModification, rootIdentifier);
228 } else if (dataObjectModification.dataType().equals(PeerGroups.class)) {
229 rebootNeighbors((DataObjectModification<PeerGroups>) dataObjectModification);
234 private synchronized void rebootNeighbors(final DataObjectModification<PeerGroups> dataObjectModification) {
235 PeerGroups extPeerGroups = dataObjectModification.dataAfter();
236 if (extPeerGroups == null) {
237 extPeerGroups = dataObjectModification.dataBefore();
239 if (extPeerGroups == null) {
242 for (final PeerGroup peerGroup : extPeerGroups.nonnullPeerGroup().values()) {
243 bgpCss.values().forEach(css -> css.restartPeerGroup(peerGroup.getPeerGroupName()));
249 @SuppressWarnings("checkstyle:illegalCatch")
250 public synchronized void close() {
251 LOG.info("Closing BGP Deployer.");
252 if (registration != null) {
253 registration.close();
258 bgpCss.values().iterator().forEachRemaining(service -> {
261 } catch (Exception e) {
262 LOG.warn("Failed to close BGP Cluster Singleton Service.", e);
267 private static FluentFuture<? extends CommitInfo> initializeNetworkInstance(
268 final DataBroker dataBroker, final InstanceIdentifier<NetworkInstance> networkInstance) {
269 final WriteTransaction wTx = dataBroker.newWriteOnlyTransaction();
270 wTx.merge(LogicalDatastoreType.CONFIGURATION, networkInstance,
271 new NetworkInstanceBuilder().setName(networkInstance.firstKeyOf(NetworkInstance.class).getName())
272 .setProtocols(new ProtocolsBuilder().build()).build());
276 synchronized void onGlobalChanged(final DataObjectModification<Global> dataObjectModification,
277 final InstanceIdentifier<Bgp> bgpInstanceIdentifier) {
278 getBgpClusterSingleton(bgpInstanceIdentifier).onGlobalChanged(dataObjectModification);
281 synchronized void onNeighborsChanged(final DataObjectModification<Neighbors> dataObjectModification,
282 final InstanceIdentifier<Bgp> bgpInstanceIdentifier) {
283 getBgpClusterSingleton(bgpInstanceIdentifier).onNeighborsChanged(dataObjectModification);
287 synchronized BGPClusterSingletonService getBgpClusterSingleton(
288 final InstanceIdentifier<Bgp> bgpInstanceIdentifier) {
289 BGPClusterSingletonService old = bgpCss.get(bgpInstanceIdentifier);
291 old = new BGPClusterSingletonService(this, provider, tableTypeRegistry,
292 rpcRegistry, ribExtensionConsumerContext, bgpDispatcher, routingPolicyFactory,
293 codecsRegistry, stateProviderRegistry, domDataBroker, bgpInstanceIdentifier);
294 bgpCss.put(bgpInstanceIdentifier, old);
300 public PeerGroup getPeerGroup(final InstanceIdentifier<Bgp> bgpIid, final String peerGroupName) {
301 final InstanceIdentifier<PeerGroup> peerGroupsIid = bgpIid.child(PeerGroups.class)
302 .child(PeerGroup.class, new PeerGroupKey(peerGroupName));
303 return peerGroups.getUnchecked(peerGroupsIid).orElse(null);