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