package org.opendaylight.protocol.bgp.rib.impl.config;
-import static org.opendaylight.protocol.bgp.rib.impl.config.OpenConfigMappingUtil.getNeighborInstanceIdentifier;
-import static org.opendaylight.protocol.bgp.rib.impl.config.OpenConfigMappingUtil.getNeighborInstanceName;
-import static org.opendaylight.protocol.bgp.rib.impl.config.OpenConfigMappingUtil.getRibInstanceName;
+import static java.util.Objects.requireNonNull;
-import com.google.common.base.Preconditions;
-import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
-import java.util.ArrayList;
+import com.google.common.util.concurrent.MoreExecutors;
import java.util.Collection;
-import java.util.Dictionary;
import java.util.HashMap;
-import java.util.Hashtable;
-import java.util.List;
import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.ExecutionException;
import javax.annotation.concurrent.GuardedBy;
import org.opendaylight.controller.md.sal.binding.api.ClusteredDataTreeChangeListener;
import org.opendaylight.controller.md.sal.binding.api.DataBroker;
import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
-import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
+import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
-import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonServiceProvider;
import org.opendaylight.protocol.bgp.openconfig.spi.BGPTableTypeRegistryConsumer;
-import org.opendaylight.protocol.bgp.rib.impl.spi.BgpDeployer;
-import org.opendaylight.protocol.bgp.rib.impl.spi.InstanceType;
-import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.neighbors.Neighbor;
+import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.peer.group.PeerGroup;
+import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.peer.group.PeerGroupKey;
import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.top.Bgp;
import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.top.bgp.Global;
import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.top.bgp.Neighbors;
+import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.top.bgp.PeerGroups;
import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.network.instance.top.NetworkInstances;
import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.network.instance.top.network.instances.NetworkInstance;
import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.network.instance.top.network.instances.NetworkInstanceBuilder;
import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.network.instance.top.network.instances.network.instance.Protocols;
import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.network.instance.top.network.instances.network.instance.ProtocolsBuilder;
import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.network.instance.top.network.instances.network.instance.protocols.Protocol;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.openconfig.extensions.rev160614.Protocol1;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.openconfig.extensions.rev180329.NetworkInstanceProtocol;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.osgi.framework.BundleContext;
-import org.osgi.framework.ServiceRegistration;
import org.osgi.service.blueprint.container.BlueprintContainer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public final class BgpDeployerImpl implements BgpDeployer, ClusteredDataTreeChangeListener<Bgp>, AutoCloseable {
+public final class BgpDeployerImpl implements ClusteredDataTreeChangeListener<Bgp>, PeerGroupConfigLoader,
+ AutoCloseable {
private static final Logger LOG = LoggerFactory.getLogger(BgpDeployerImpl.class);
private final InstanceIdentifier<NetworkInstance> networkInstanceIId;
private final BlueprintContainer container;
private final BundleContext bundleContext;
private final BGPTableTypeRegistryConsumer tableTypeRegistry;
- private final ListenerRegistration<BgpDeployerImpl> registration;
- @GuardedBy("this")
- private final Map<InstanceIdentifier<Bgp>, RibImpl> ribs = new HashMap<>();
+ private final ClusterSingletonServiceProvider provider;
+ private final LoadingCache<InstanceIdentifier<PeerGroup>, Optional<PeerGroup>> peerGroups = CacheBuilder.newBuilder()
+ .build(new CacheLoader<InstanceIdentifier<PeerGroup>, Optional<PeerGroup>>() {
+ @Override
+ public Optional<PeerGroup> load(final InstanceIdentifier<PeerGroup> key)
+ throws ExecutionException, InterruptedException {
+ return loadPeerGroup(key);
+ }
+ });
+ private ListenerRegistration<BgpDeployerImpl> registration;
@GuardedBy("this")
- private final Map<InstanceIdentifier<Neighbor>, PeerBean> peers = new HashMap<>();
+ private final Map<InstanceIdentifier<Bgp>, BGPClusterSingletonService> bgpCss = new HashMap<>();
private final DataBroker dataBroker;
+ private final String networkInstanceName;
@GuardedBy("this")
private boolean closed;
- public BgpDeployerImpl(final String networkInstanceName, final BlueprintContainer container,
- final BundleContext bundleContext, final DataBroker dataBroker,
- final BGPTableTypeRegistryConsumer mappingService) {
- this.dataBroker = Preconditions.checkNotNull(dataBroker);
- this.container = Preconditions.checkNotNull(container);
- this.bundleContext = Preconditions.checkNotNull(bundleContext);
- this.tableTypeRegistry = Preconditions.checkNotNull(mappingService);
+ public BgpDeployerImpl(final String networkInstanceName, final ClusterSingletonServiceProvider provider,
+ final BlueprintContainer container,
+ final BundleContext bundleContext, final DataBroker dataBroker,
+ final BGPTableTypeRegistryConsumer mappingService) {
+ this.dataBroker = requireNonNull(dataBroker);
+ this.provider = requireNonNull(provider);
+ this.networkInstanceName = requireNonNull(networkInstanceName);
+ this.container = requireNonNull(container);
+ this.bundleContext = requireNonNull(bundleContext);
+ this.tableTypeRegistry = requireNonNull(mappingService);
this.networkInstanceIId = InstanceIdentifier.create(NetworkInstances.class)
- .child(NetworkInstance.class, new NetworkInstanceKey(networkInstanceName));
+ .child(NetworkInstance.class, new NetworkInstanceKey(networkInstanceName));
Futures.addCallback(initializeNetworkInstance(dataBroker, this.networkInstanceIId), new FutureCallback<Void>() {
@Override
public void onSuccess(final Void result) {
public void onFailure(final Throwable t) {
LOG.error("Failed to initialize Network Instance {}.", networkInstanceName, t);
}
- });
- this.registration = dataBroker.registerDataTreeChangeListener(
- new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION, this.networkInstanceIId.child(Protocols.class)
- .child(Protocol.class).augmentation(Protocol1.class).child(Bgp.class)), this);
- LOG.info("BGP Deployer {} started.", networkInstanceName);
+ }, MoreExecutors.directExecutor());
+ }
+
+ public synchronized void init() {
+ this.registration = this.dataBroker.registerDataTreeChangeListener(
+ new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION,
+ this.networkInstanceIId.child(Protocols.class).child(Protocol.class)
+ .augmentation(NetworkInstanceProtocol.class).child(Bgp.class)), this);
+ LOG.info("BGP Deployer {} started.", this.networkInstanceName);
+ }
+
+ private Optional<PeerGroup> loadPeerGroup(final InstanceIdentifier<PeerGroup> peerGroupIid)
+ throws ExecutionException, InterruptedException {
+ final ReadOnlyTransaction tr = this.dataBroker.newReadOnlyTransaction();
+ return tr.read(LogicalDatastoreType.CONFIGURATION, peerGroupIid).get().toJavaUtil();
}
@Override
final InstanceIdentifier<Bgp> rootIdentifier = dataTreeModification.getRootPath().getRootIdentifier();
final DataObjectModification<Bgp> rootNode = dataTreeModification.getRootNode();
LOG.trace("BGP configuration has changed: {}", rootNode);
- for (final DataObjectModification<? extends DataObject> dataObjectModification : rootNode.getModifiedChildren()) {
+ for (final DataObjectModification<? extends DataObject> dataObjectModification :
+ rootNode.getModifiedChildren()) {
if (dataObjectModification.getDataType().equals(Global.class)) {
onGlobalChanged((DataObjectModification<Global>) dataObjectModification, rootIdentifier);
} else if (dataObjectModification.getDataType().equals(Neighbors.class)) {
onNeighborsChanged((DataObjectModification<Neighbors>) dataObjectModification, rootIdentifier);
+ } else if (dataObjectModification.getDataType().equals(PeerGroups.class)) {
+ rebootNeighbors((DataObjectModification<PeerGroups>) dataObjectModification);
}
}
}
}
- @Override
- public InstanceIdentifier<NetworkInstance> getInstanceIdentifier() {
- return this.networkInstanceIId;
- }
-
- @Override
- public synchronized void close() throws Exception {
- LOG.info("Closing BGP Deployer.");
- this.registration.close();
- this.peers.values().forEach(PeerBean::close);
- this.peers.clear();
- this.ribs.values().forEach(RibImpl::close);
- this.ribs.clear();
- this.closed = true;
- }
-
- private static CheckedFuture<Void, TransactionCommitFailedException> initializeNetworkInstance(
- final DataBroker dataBroker, final InstanceIdentifier<NetworkInstance> networkInstance) {
- final WriteTransaction wTx = dataBroker.newWriteOnlyTransaction();
- wTx.merge(LogicalDatastoreType.CONFIGURATION, networkInstance,
- new NetworkInstanceBuilder().setName(networkInstance.firstKeyOf(NetworkInstance.class).getName())
- .setProtocols(new ProtocolsBuilder().build()).build());
- return wTx.submit();
- }
-
- private synchronized void onGlobalChanged(final DataObjectModification<Global> dataObjectModification,
- final InstanceIdentifier<Bgp> rootIdentifier) {
- switch (dataObjectModification.getModificationType()) {
- case DELETE:
- onGlobalRemoved(rootIdentifier);
- break;
- case SUBTREE_MODIFIED:
- case WRITE:
- onGlobalModified(rootIdentifier, dataObjectModification.getDataAfter(), null);
- break;
- default:
- break;
+ private synchronized void rebootNeighbors(final DataObjectModification<PeerGroups> dataObjectModification) {
+ PeerGroups peerGroups = dataObjectModification.getDataAfter();
+ if (peerGroups == null) {
+ peerGroups = dataObjectModification.getDataBefore();
}
- }
-
- @Override
- public synchronized void onGlobalModified(final InstanceIdentifier<Bgp> rootIdentifier, final Global global,
- final WriteConfiguration configurationWriter) {
- final RibImpl ribImpl = this.ribs.get(rootIdentifier);
- if(ribImpl == null ) {
- onGlobalCreated(rootIdentifier, global, configurationWriter);
- } else if (!ribImpl.isGlobalEqual(global)) {
- onGlobalUpdated(rootIdentifier, global, ribImpl, configurationWriter);
+ if (peerGroups == null) {
+ return;
+ }
+ for (final PeerGroup peerGroup: peerGroups.getPeerGroup()) {
+ this.bgpCss.values().forEach(css->css.restartNeighbors(peerGroup.getPeerGroupName()));
}
- }
-
- private synchronized List<PeerBean> closeAllBindedPeers(final InstanceIdentifier<Bgp> rootIdentifier) {
- final List<PeerBean> filtered = new ArrayList<>();
- this.peers.entrySet().stream().filter(entry -> entry.getKey().firstIdentifierOf(Bgp.class)
- .contains(rootIdentifier)).forEach(entry -> {final PeerBean peer = entry.getValue();
- peer.close();
- filtered.add(peer);
- });
- return filtered;
- }
-
- private synchronized void onGlobalCreated(final InstanceIdentifier<Bgp> rootIdentifier, final Global global,
- final WriteConfiguration configurationWriter) {
- LOG.debug("Creating RIB instance with configuration: {}", global);
- final RibImpl ribImpl = (RibImpl) this.container.getComponentInstance(InstanceType.RIB.getBeanName());
- initiateRibInstance(rootIdentifier, global, ribImpl, configurationWriter);
- this.ribs.put(rootIdentifier, ribImpl);
- LOG.debug("RIB instance created: {}", ribImpl);
- }
-
- private synchronized void onGlobalUpdated(final InstanceIdentifier<Bgp> rootIdentifier, final Global global,
- final RibImpl ribImpl, final WriteConfiguration configurationWriter) {
- LOG.debug("Modifying RIB instance with configuration: {}", global);
- final List<PeerBean> closedPeers = closeAllBindedPeers(rootIdentifier);
- ribImpl.close();
- initiateRibInstance(rootIdentifier, global, ribImpl, configurationWriter);
- closedPeers.forEach(peer -> peer.restart(ribImpl, this.tableTypeRegistry));
- LOG.debug("RIB instance created: {}", ribImpl);
}
@Override
- public synchronized void onGlobalRemoved(final InstanceIdentifier<Bgp> rootIdentifier) {
- LOG.debug("Removing RIB instance: {}", rootIdentifier);
- final RibImpl ribImpl = this.ribs.remove(rootIdentifier);
- if (ribImpl != null) {
- LOG.debug("RIB instance removed {}", ribImpl);
- ribImpl.close();
+ public synchronized void close() {
+ LOG.info("Closing BGP Deployer.");
+ if (this.registration != null) {
+ this.registration.close();
+ this.registration = null;
}
- }
-
- private synchronized void registerRibInstance(final RibImpl ribImpl, final String ribInstanceName) {
- final Dictionary<String, String> properties = new Hashtable<>();
- properties.put(InstanceType.RIB.getBeanName(), ribInstanceName);
- final ServiceRegistration<?> serviceRegistration = this.bundleContext.registerService(
- InstanceType.RIB.getServices(), ribImpl, properties);
- ribImpl.setServiceRegistration(serviceRegistration);
- }
-
- private synchronized void initiateRibInstance(final InstanceIdentifier<Bgp> rootIdentifier, final Global global,
- final RibImpl ribImpl, final WriteConfiguration configurationWriter) {
- final String ribInstanceName = getRibInstanceName(rootIdentifier);
- ribImpl.start(global, ribInstanceName, this.tableTypeRegistry, configurationWriter);
- registerRibInstance(ribImpl, ribInstanceName);
- }
+ this.closed = true;
- private synchronized void onNeighborsChanged(final DataObjectModification<Neighbors> dataObjectModification,
- final InstanceIdentifier<Bgp> rootIdentifier) {
- for (final DataObjectModification<? extends DataObject> neighborModification : dataObjectModification.getModifiedChildren()) {
- switch (neighborModification.getModificationType()) {
- case DELETE:
- onNeighborRemoved(rootIdentifier, (Neighbor) neighborModification.getDataBefore());
- break;
- case SUBTREE_MODIFIED:
- case WRITE:
- onNeighborModified(rootIdentifier, (Neighbor) neighborModification.getDataAfter(), null);
- break;
- default:
- break;
+ this.bgpCss.values().iterator().forEachRemaining(service -> {
+ try {
+ service.close();
+ } catch (Exception e) {
+ LOG.warn("Failed to close BGP Cluster Singleton Service.");
}
- }
- }
-
- @Override
- public synchronized void onNeighborModified(final InstanceIdentifier<Bgp> rootIdentifier, final Neighbor neighbor,
- final WriteConfiguration configurationWriter) {
- //restart peer instance with a new configuration
- final PeerBean bgpPeer = this.peers.get(getNeighborInstanceIdentifier(rootIdentifier, neighbor.getKey()));
- if (bgpPeer == null) {
- onNeighborCreated(rootIdentifier, neighbor, configurationWriter);
- } else if(!bgpPeer.containsEqualConfiguration(neighbor)){
- onNeighborUpdated(bgpPeer, rootIdentifier, neighbor, configurationWriter);
- }
- }
+ });
- private synchronized void onNeighborCreated(final InstanceIdentifier<Bgp> rootIdentifier, final Neighbor neighbor,
- final WriteConfiguration configurationWriter) {
- LOG.debug("Creating Peer instance with configuration: {}", neighbor);
- final PeerBean bgpPeer;
- if (OpenConfigMappingUtil.isApplicationPeer(neighbor)) {
- bgpPeer = (PeerBean) this.container.getComponentInstance(InstanceType.APP_PEER.getBeanName());
- } else {
- bgpPeer = (PeerBean) this.container.getComponentInstance(InstanceType.PEER.getBeanName());
- }
- final InstanceIdentifier<Neighbor> neighborInstanceIdentifier = getNeighborInstanceIdentifier(rootIdentifier, neighbor.getKey());
- initiatePeerInstance(rootIdentifier, neighborInstanceIdentifier, neighbor, bgpPeer, configurationWriter);
- this.peers.put(neighborInstanceIdentifier, bgpPeer);
- LOG.debug("Peer instance created {}", bgpPeer);
}
- private synchronized void onNeighborUpdated(final PeerBean bgpPeer, final InstanceIdentifier<Bgp> rootIdentifier, final Neighbor neighbor,
- final WriteConfiguration configurationWriter) {
- LOG.debug("Updating Peer instance with configuration: {}", neighbor);
- bgpPeer.close();
- final InstanceIdentifier<Neighbor> neighborInstanceIdentifier = getNeighborInstanceIdentifier(rootIdentifier, neighbor.getKey());
- initiatePeerInstance(rootIdentifier, neighborInstanceIdentifier, neighbor, bgpPeer, configurationWriter);
- LOG.debug("Peer instance updated {}", bgpPeer);
+ private static ListenableFuture<Void> initializeNetworkInstance(
+ final DataBroker dataBroker, final InstanceIdentifier<NetworkInstance> networkInstance) {
+ final WriteTransaction wTx = dataBroker.newWriteOnlyTransaction();
+ wTx.merge(LogicalDatastoreType.CONFIGURATION, networkInstance,
+ new NetworkInstanceBuilder().setName(networkInstance.firstKeyOf(NetworkInstance.class).getName())
+ .setProtocols(new ProtocolsBuilder().build()).build());
+ return wTx.submit();
}
- @Override
- public synchronized void onNeighborRemoved(final InstanceIdentifier<Bgp> rootIdentifier, final Neighbor neighbor) {
- LOG.debug("Removing Peer instance: {}", rootIdentifier);
- final PeerBean bgpPeer = this.peers.remove(getNeighborInstanceIdentifier(rootIdentifier, neighbor.getKey()));
- if (bgpPeer != null) {
- bgpPeer.close();
- LOG.debug("Peer instance removed {}", bgpPeer);
+ @VisibleForTesting
+ synchronized void onGlobalChanged(final DataObjectModification<Global> dataObjectModification,
+ final InstanceIdentifier<Bgp> bgpInstanceIdentifier) {
+ BGPClusterSingletonService old = this.bgpCss.get(bgpInstanceIdentifier);
+ if (old == null) {
+ old = new BGPClusterSingletonService(this, this.provider, this.tableTypeRegistry,
+ this.container, this.bundleContext, bgpInstanceIdentifier);
+ this.bgpCss.put(bgpInstanceIdentifier, old);
}
+ old.onGlobalChanged(dataObjectModification);
}
- private synchronized void registerPeerInstance(final BgpPeer bgpPeer, final String peerInstanceName) {
- final Dictionary<String, String> properties = new Hashtable<>();
- properties.put(InstanceType.PEER.getBeanName(), peerInstanceName);
- final ServiceRegistration<?> serviceRegistration = this.bundleContext
- .registerService(InstanceType.PEER.getServices(), bgpPeer, properties);
- bgpPeer.setServiceRegistration(serviceRegistration);
- }
-
- private synchronized void registerAppPeerInstance(final AppPeer appPeer, final String peerInstanceName) {
- final Dictionary<String, String> properties = new Hashtable<>();
- properties.put(InstanceType.PEER.getBeanName(), peerInstanceName);
- final ServiceRegistration<?> serviceRegistration = this.bundleContext
- .registerService(InstanceType.APP_PEER.getServices(), appPeer, properties);
- appPeer.setServiceRegistration(serviceRegistration);
- }
-
- private synchronized void initiatePeerInstance(final InstanceIdentifier<Bgp> rootIdentifier,
- final InstanceIdentifier<Neighbor> neighborIdentifier, final Neighbor neighbor,
- final PeerBean bgpPeer, final WriteConfiguration configurationWriter) {
- final String peerInstanceName = getNeighborInstanceName(neighborIdentifier);
- final RibImpl rib = this.ribs.get(rootIdentifier);
- if (rib != null) {
- bgpPeer.start(rib, neighbor, this.tableTypeRegistry, configurationWriter);
- if (bgpPeer instanceof BgpPeer) {
- registerPeerInstance((BgpPeer) bgpPeer, peerInstanceName);
- } else if(bgpPeer instanceof AppPeer) {
- registerAppPeerInstance((AppPeer) bgpPeer, peerInstanceName);
- }
+ @VisibleForTesting
+ synchronized void onNeighborsChanged(final DataObjectModification<Neighbors> dataObjectModification,
+ final InstanceIdentifier<Bgp> bgpInstanceIdentifier) {
+ BGPClusterSingletonService old = this.bgpCss.get(bgpInstanceIdentifier);
+ if (old == null) {
+ old = new BGPClusterSingletonService(this, this.provider, this.tableTypeRegistry,
+ this.container, this.bundleContext, bgpInstanceIdentifier);
+ this.bgpCss.put(bgpInstanceIdentifier, old);
}
+ old.onNeighborsChanged(dataObjectModification);
}
- @Override
- public BGPTableTypeRegistryConsumer getTableTypeRegistry() {
+ @VisibleForTesting
+ BGPTableTypeRegistryConsumer getTableTypeRegistry() {
return this.tableTypeRegistry;
}
@Override
- public <T extends DataObject> ListenableFuture<Void> writeConfiguration(final T data,
- final InstanceIdentifier<T> identifier) {
- final WriteTransaction wTx = this.dataBroker.newWriteOnlyTransaction();
- wTx.put(LogicalDatastoreType.CONFIGURATION, identifier, data, true);
- return wTx.submit();
- }
-
- @Override
- public <T extends DataObject> ListenableFuture<Void> removeConfiguration(
- final InstanceIdentifier<T> identifier) {
- final WriteTransaction wTx = this.dataBroker.newWriteOnlyTransaction();
- wTx.delete(LogicalDatastoreType.CONFIGURATION, identifier);
- return wTx.submit();
+ public PeerGroup getPeerGroup(final InstanceIdentifier<Bgp> bgpIid, final String peerGroupName) {
+ final InstanceIdentifier<PeerGroup> peerGroupsIid =
+ bgpIid.child(PeerGroups.class).child(PeerGroup.class, new PeerGroupKey(peerGroupName));
+ return this.peerGroups.getUnchecked(peerGroupsIid).orElse(null);
}
}