Migrate deprecated submit() to commit() for BGP/BMP
[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.Map;
23 import java.util.Optional;
24 import java.util.concurrent.ExecutionException;
25 import javax.annotation.concurrent.GuardedBy;
26 import org.opendaylight.controller.md.sal.binding.api.ClusteredDataTreeChangeListener;
27 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
28 import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
29 import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
30 import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
31 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
32 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
33 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
34 import org.opendaylight.mdsal.common.api.CommitInfo;
35 import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonServiceProvider;
36 import org.opendaylight.protocol.bgp.openconfig.spi.BGPTableTypeRegistryConsumer;
37 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.peer.group.PeerGroup;
38 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.peer.group.PeerGroupKey;
39 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.top.Bgp;
40 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.top.bgp.Global;
41 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.top.bgp.Neighbors;
42 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.top.bgp.PeerGroups;
43 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.network.instance.top.NetworkInstances;
44 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.network.instance.top.network.instances.NetworkInstance;
45 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.network.instance.top.network.instances.NetworkInstanceBuilder;
46 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.network.instance.top.network.instances.NetworkInstanceKey;
47 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.network.instance.top.network.instances.network.instance.Protocols;
48 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.network.instance.top.network.instances.network.instance.ProtocolsBuilder;
49 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.network.instance.top.network.instances.network.instance.protocols.Protocol;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.openconfig.extensions.rev180329.NetworkInstanceProtocol;
51 import org.opendaylight.yangtools.concepts.ListenerRegistration;
52 import org.opendaylight.yangtools.yang.binding.DataObject;
53 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
54 import org.osgi.framework.BundleContext;
55 import org.osgi.service.blueprint.container.BlueprintContainer;
56 import org.slf4j.Logger;
57 import org.slf4j.LoggerFactory;
58
59 public final class BgpDeployerImpl implements ClusteredDataTreeChangeListener<Bgp>, PeerGroupConfigLoader,
60         AutoCloseable {
61     private static final Logger LOG = LoggerFactory.getLogger(BgpDeployerImpl.class);
62     private final InstanceIdentifier<NetworkInstance> networkInstanceIId;
63     private final BlueprintContainer container;
64     private final BundleContext bundleContext;
65     private final BGPTableTypeRegistryConsumer tableTypeRegistry;
66     private final ClusterSingletonServiceProvider provider;
67     private final LoadingCache<InstanceIdentifier<PeerGroup>, Optional<PeerGroup>> peerGroups = CacheBuilder.newBuilder()
68             .build(new CacheLoader<InstanceIdentifier<PeerGroup>, Optional<PeerGroup>>() {
69                 @Override
70                 public Optional<PeerGroup> load(final InstanceIdentifier<PeerGroup> key)
71                         throws ExecutionException, InterruptedException {
72                     return loadPeerGroup(key);
73                 }
74             });
75     private ListenerRegistration<BgpDeployerImpl> registration;
76     @GuardedBy("this")
77     private final Map<InstanceIdentifier<Bgp>, BGPClusterSingletonService> bgpCss = new HashMap<>();
78     private final DataBroker dataBroker;
79     private final String networkInstanceName;
80     @GuardedBy("this")
81     private boolean closed;
82
83     public BgpDeployerImpl(final String networkInstanceName, final ClusterSingletonServiceProvider provider,
84             final BlueprintContainer container,
85             final BundleContext bundleContext, final DataBroker dataBroker,
86             final BGPTableTypeRegistryConsumer mappingService) {
87         this.dataBroker = requireNonNull(dataBroker);
88         this.provider = requireNonNull(provider);
89         this.networkInstanceName = requireNonNull(networkInstanceName);
90         this.container = requireNonNull(container);
91         this.bundleContext = requireNonNull(bundleContext);
92         this.tableTypeRegistry = requireNonNull(mappingService);
93         this.networkInstanceIId = InstanceIdentifier.create(NetworkInstances.class)
94                 .child(NetworkInstance.class, new NetworkInstanceKey(networkInstanceName));
95         initializeNetworkInstance(dataBroker, this.networkInstanceIId).addCallback(new FutureCallback<CommitInfo>() {
96             @Override
97             public void onSuccess(final CommitInfo result) {
98                 LOG.debug("Network Instance {} initialized successfully.", networkInstanceName);
99             }
100
101             @Override
102             public void onFailure(final Throwable throwable) {
103                 LOG.error("Failed to initialize Network Instance {}.", networkInstanceName, throwable);
104             }
105         }, MoreExecutors.directExecutor());
106     }
107
108     public synchronized void init() {
109         this.registration = this.dataBroker.registerDataTreeChangeListener(
110                 new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION,
111                         this.networkInstanceIId.child(Protocols.class).child(Protocol.class)
112                                 .augmentation(NetworkInstanceProtocol.class).child(Bgp.class)), this);
113         LOG.info("BGP Deployer {} started.", this.networkInstanceName);
114     }
115
116     private Optional<PeerGroup> loadPeerGroup(final InstanceIdentifier<PeerGroup> peerGroupIid)
117             throws ExecutionException, InterruptedException {
118         final ReadOnlyTransaction tr = this.dataBroker.newReadOnlyTransaction();
119         return tr.read(LogicalDatastoreType.CONFIGURATION, peerGroupIid).get().toJavaUtil();
120     }
121
122     @Override
123     public synchronized void onDataTreeChanged(final Collection<DataTreeModification<Bgp>> changes) {
124         if (this.closed) {
125             LOG.trace("BGP Deployer was already closed, skipping changes.");
126             return;
127         }
128         for (final DataTreeModification<Bgp> dataTreeModification : changes) {
129             final InstanceIdentifier<Bgp> rootIdentifier = dataTreeModification.getRootPath().getRootIdentifier();
130             final DataObjectModification<Bgp> rootNode = dataTreeModification.getRootNode();
131             LOG.trace("BGP configuration has changed: {}", rootNode);
132             for (final DataObjectModification<? extends DataObject> dataObjectModification :
133                     rootNode.getModifiedChildren()) {
134                 if (dataObjectModification.getDataType().equals(Global.class)) {
135                     onGlobalChanged((DataObjectModification<Global>) dataObjectModification, rootIdentifier);
136                 } else if (dataObjectModification.getDataType().equals(Neighbors.class)) {
137                     onNeighborsChanged((DataObjectModification<Neighbors>) dataObjectModification, rootIdentifier);
138                 } else if (dataObjectModification.getDataType().equals(PeerGroups.class)) {
139                     rebootNeighbors((DataObjectModification<PeerGroups>) dataObjectModification);
140                 }
141             }
142         }
143     }
144
145     private synchronized void rebootNeighbors(final DataObjectModification<PeerGroups> dataObjectModification) {
146         PeerGroups peerGroups = dataObjectModification.getDataAfter();
147         if (peerGroups == null) {
148             peerGroups = dataObjectModification.getDataBefore();
149         }
150         if (peerGroups == null) {
151             return;
152         }
153         for (final PeerGroup peerGroup: peerGroups.getPeerGroup()) {
154             this.bgpCss.values().forEach(css->css.restartNeighbors(peerGroup.getPeerGroupName()));
155         }
156     }
157
158     @Override
159     public synchronized void close() {
160         LOG.info("Closing BGP Deployer.");
161         if (this.registration != null) {
162             this.registration.close();
163             this.registration = null;
164         }
165         this.closed = true;
166
167         this.bgpCss.values().iterator().forEachRemaining(service -> {
168             try {
169                 service.close();
170             } catch (Exception e) {
171                 LOG.warn("Failed to close BGP Cluster Singleton Service.");
172             }
173         });
174
175     }
176
177     private static FluentFuture<? extends CommitInfo> initializeNetworkInstance(
178             final DataBroker dataBroker, final InstanceIdentifier<NetworkInstance> networkInstance) {
179         final WriteTransaction wTx = dataBroker.newWriteOnlyTransaction();
180         wTx.merge(LogicalDatastoreType.CONFIGURATION, networkInstance,
181                 new NetworkInstanceBuilder().setName(networkInstance.firstKeyOf(NetworkInstance.class).getName())
182                         .setProtocols(new ProtocolsBuilder().build()).build());
183         return wTx.commit();
184     }
185
186     @VisibleForTesting
187     synchronized void onGlobalChanged(final DataObjectModification<Global> dataObjectModification,
188             final InstanceIdentifier<Bgp> bgpInstanceIdentifier) {
189         BGPClusterSingletonService old = this.bgpCss.get(bgpInstanceIdentifier);
190         if (old == null) {
191             old = new BGPClusterSingletonService(this, this.provider, this.tableTypeRegistry,
192                     this.container, this.bundleContext, bgpInstanceIdentifier);
193             this.bgpCss.put(bgpInstanceIdentifier, old);
194         }
195         old.onGlobalChanged(dataObjectModification);
196     }
197
198     @VisibleForTesting
199     synchronized void onNeighborsChanged(final DataObjectModification<Neighbors> dataObjectModification,
200             final InstanceIdentifier<Bgp> bgpInstanceIdentifier) {
201         BGPClusterSingletonService old = this.bgpCss.get(bgpInstanceIdentifier);
202         if (old == null) {
203             old = new BGPClusterSingletonService(this, this.provider, this.tableTypeRegistry,
204                     this.container, this.bundleContext, bgpInstanceIdentifier);
205             this.bgpCss.put(bgpInstanceIdentifier, old);
206         }
207         old.onNeighborsChanged(dataObjectModification);
208     }
209
210     @VisibleForTesting
211     BGPTableTypeRegistryConsumer getTableTypeRegistry() {
212         return this.tableTypeRegistry;
213     }
214
215     @Override
216     public PeerGroup getPeerGroup(final InstanceIdentifier<Bgp> bgpIid, final String peerGroupName) {
217         final InstanceIdentifier<PeerGroup> peerGroupsIid =
218         bgpIid.child(PeerGroups.class).child(PeerGroup.class, new PeerGroupKey(peerGroupName));
219         return this.peerGroups.getUnchecked(peerGroupsIid).orElse(null);
220     }
221 }