0804d72c58d5ce3cb842f170d400d7735c5ae839
[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 org.opendaylight.protocol.bgp.rib.impl.config.OpenConfigMappingUtil.getNeighborInstanceIdentifier;
12 import static org.opendaylight.protocol.bgp.rib.impl.config.OpenConfigMappingUtil.getNeighborInstanceName;
13 import static org.opendaylight.protocol.bgp.rib.impl.config.OpenConfigMappingUtil.getRibInstanceName;
14
15 import com.google.common.base.Preconditions;
16 import com.google.common.util.concurrent.CheckedFuture;
17 import com.google.common.util.concurrent.FutureCallback;
18 import com.google.common.util.concurrent.Futures;
19 import com.google.common.util.concurrent.ListenableFuture;
20 import java.util.ArrayList;
21 import java.util.Collection;
22 import java.util.Dictionary;
23 import java.util.HashMap;
24 import java.util.Hashtable;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.Map.Entry;
28 import javax.annotation.concurrent.GuardedBy;
29 import org.opendaylight.controller.md.sal.binding.api.ClusteredDataTreeChangeListener;
30 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
31 import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
32 import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
33 import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
34 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
35 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
36 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
37 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
38 import org.opendaylight.protocol.bgp.openconfig.spi.BGPOpenConfigMappingService;
39 import org.opendaylight.protocol.bgp.rib.impl.spi.BgpDeployer;
40 import org.opendaylight.protocol.bgp.rib.impl.spi.InstanceType;
41 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.neighbors.Neighbor;
42 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.top.Bgp;
43 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.top.bgp.Global;
44 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.top.bgp.Neighbors;
45 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.network.instance.top.NetworkInstances;
46 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.network.instance.top.network.instances.NetworkInstance;
47 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.network.instance.top.network.instances.NetworkInstanceBuilder;
48 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.network.instance.top.network.instances.NetworkInstanceKey;
49 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.network.instance.top.network.instances.network.instance.Protocols;
50 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.network.instance.top.network.instances.network.instance.ProtocolsBuilder;
51 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.network.instance.top.network.instances.network.instance.protocols.Protocol;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.openconfig.extensions.rev160614.Protocol1;
53 import org.opendaylight.yangtools.concepts.ListenerRegistration;
54 import org.opendaylight.yangtools.yang.binding.DataObject;
55 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
56 import org.osgi.framework.BundleContext;
57 import org.osgi.framework.ServiceRegistration;
58 import org.osgi.service.blueprint.container.BlueprintContainer;
59 import org.slf4j.Logger;
60 import org.slf4j.LoggerFactory;
61
62 public final class BgpDeployerImpl implements BgpDeployer, ClusteredDataTreeChangeListener<Bgp>, AutoCloseable {
63     private static final Logger LOG = LoggerFactory.getLogger(BgpDeployerImpl.class);
64     private final InstanceIdentifier<NetworkInstance> networkInstanceIId;
65     private final BlueprintContainer container;
66     private final BundleContext bundleContext;
67     private final BGPOpenConfigMappingService mappingService;
68     private final ListenerRegistration<BgpDeployerImpl> registration;
69     @GuardedBy("this")
70     private final Map<InstanceIdentifier<Bgp>, RibImpl> ribs = new HashMap<>();
71     @GuardedBy("this")
72     private final Map<InstanceIdentifier<Neighbor>, PeerBean> peers = new HashMap<>();
73     private final DataBroker dataBroker;
74     @GuardedBy("this")
75     private boolean closed;
76
77     public BgpDeployerImpl(final String networkInstanceName, final BlueprintContainer container, final BundleContext bundleContext, final DataBroker dataBroker,
78         final BGPOpenConfigMappingService mappingService) {
79         this.dataBroker = Preconditions.checkNotNull(dataBroker);
80         this.container = Preconditions.checkNotNull(container);
81         this.bundleContext = Preconditions.checkNotNull(bundleContext);
82         this.mappingService = Preconditions.checkNotNull(mappingService);
83         this.networkInstanceIId = InstanceIdentifier
84             .create(NetworkInstances.class)
85             .child(NetworkInstance.class, new NetworkInstanceKey(networkInstanceName));
86         Futures.addCallback(initializeNetworkInstance(dataBroker, this.networkInstanceIId), new FutureCallback<Void>() {
87             @Override
88             public void onSuccess(final Void result) {
89                 LOG.debug("Network Instance {} initialized successfully.", networkInstanceName);
90             }
91
92             @Override
93             public void onFailure(final Throwable t) {
94                 LOG.error("Failed to initialize Network Instance {}.", networkInstanceName, t);
95             }
96         });
97         this.registration = dataBroker.registerDataTreeChangeListener(new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION,
98             this.networkInstanceIId.child(Protocols.class)
99                 .child(Protocol.class)
100                 .augmentation(Protocol1.class)
101                 .child(Bgp.class)), this);
102         LOG.info("BGP Deployer {} started.", networkInstanceName);
103     }
104
105     @Override
106     public synchronized void onDataTreeChanged(final Collection<DataTreeModification<Bgp>> changes) {
107         if (this.closed) {
108             LOG.trace("BGP Deployer was already closed, skipping changes.");
109             return;
110         }
111         for (final DataTreeModification<Bgp> dataTreeModification : changes) {
112             final InstanceIdentifier<Bgp> rootIdentifier = dataTreeModification.getRootPath().getRootIdentifier();
113             final DataObjectModification<Bgp> rootNode = dataTreeModification.getRootNode();
114             LOG.trace("BGP configuration has changed: {}", rootNode);
115             for (final DataObjectModification<? extends DataObject> dataObjectModification : rootNode.getModifiedChildren()) {
116                 if (dataObjectModification.getDataType().equals(Global.class)) {
117                     onGlobalChanged((DataObjectModification<Global>) dataObjectModification, rootIdentifier);
118                 } else if (dataObjectModification.getDataType().equals(Neighbors.class)) {
119                     onNeighborsChanged((DataObjectModification<Neighbors>) dataObjectModification, rootIdentifier);
120                 }
121             }
122         }
123     }
124
125     @Override
126     public InstanceIdentifier<NetworkInstance> getInstanceIdentifier() {
127         return this.networkInstanceIId;
128     }
129
130     @Override
131     public synchronized void close() throws Exception {
132         this.registration.close();
133         this.peers.values().forEach(PeerBean::close);
134         this.peers.clear();
135         this.ribs.values().forEach(RibImpl::close);
136         this.ribs.clear();
137         this.closed = true;
138     }
139
140     private static CheckedFuture<Void, TransactionCommitFailedException> initializeNetworkInstance(final DataBroker dataBroker,
141         final InstanceIdentifier<NetworkInstance> networkInstance) {
142         final WriteTransaction wTx = dataBroker.newWriteOnlyTransaction();
143         wTx.merge(LogicalDatastoreType.CONFIGURATION, networkInstance,
144             new NetworkInstanceBuilder().setName(networkInstance.firstKeyOf(NetworkInstance.class).getName()).setProtocols(new ProtocolsBuilder().build()).build());
145         return wTx.submit();
146     }
147
148     private void onGlobalChanged(final DataObjectModification<Global> dataObjectModification,
149         final InstanceIdentifier<Bgp> rootIdentifier) {
150         switch (dataObjectModification.getModificationType()) {
151         case DELETE:
152             onGlobalRemoved(rootIdentifier);
153             break;
154         case SUBTREE_MODIFIED:
155         case WRITE:
156             onGlobalModified(rootIdentifier, dataObjectModification.getDataAfter());
157             break;
158         default:
159             break;
160         }
161     }
162
163     private void onGlobalModified(final InstanceIdentifier<Bgp> rootIdentifier, final Global global) {
164         LOG.debug("Modifing RIB instance with configuration: {}", global);
165         //restart existing rib instance with a new configuration
166         final RibImpl ribImpl = this.ribs.get(rootIdentifier);
167         if(ribImpl == null ) {
168             //if not exists, create a new instance
169             onGlobalCreated(rootIdentifier, global, null);
170         } else if (!ribImpl.isGlobalEqual(global)) {
171             final List<PeerBean> closedPeers = closeAllBindedPeers(rootIdentifier);
172             ribImpl.close();
173             initiateRibInstance(rootIdentifier, global, ribImpl, null);
174             closedPeers.forEach(peer -> peer.restart(ribImpl, this.mappingService));
175         }
176         LOG.debug("RIB instance modified {}", ribImpl);
177     }
178
179     private List<PeerBean> closeAllBindedPeers(final InstanceIdentifier<Bgp> rootIdentifier) {
180         final List<PeerBean> filtered = new ArrayList<>();
181         for (final Entry<InstanceIdentifier<Neighbor>, PeerBean> entry : this.peers.entrySet()) {
182             if (entry.getKey().contains(rootIdentifier)) {
183                 final PeerBean peer = entry.getValue();
184                 peer.close();
185                 filtered.add(peer);
186             }
187         }
188         return filtered;
189     }
190
191     @Override
192     public synchronized void onGlobalCreated(final InstanceIdentifier<Bgp> rootIdentifier, final Global global, final WriteConfiguration
193         configurationWriter) {
194         LOG.debug("Creating RIB instance with configuration: {}", global);
195         final RibImpl ribImpl = (RibImpl) this.container.getComponentInstance(InstanceType.RIB.getBeanName());
196         initiateRibInstance(rootIdentifier, global, ribImpl, configurationWriter);
197         this.ribs.put(rootIdentifier, ribImpl);
198         LOG.debug("RIB instance created {}", ribImpl);
199     }
200
201     @Override
202     public synchronized void onGlobalRemoved(final InstanceIdentifier<Bgp> rootIdentifier) {
203         LOG.debug("Removing RIB instance: {}", rootIdentifier);
204         final RibImpl ribImpl = this.ribs.remove(rootIdentifier);
205         if (ribImpl != null) {
206             ribImpl.close();
207             LOG.debug("RIB instance removed {}", ribImpl);
208         }
209     }
210
211     private void registerRibInstance(final RibImpl ribImpl, final String ribInstanceName) {
212         final Dictionary<String, String> properties = new Hashtable<>();
213         properties.put(InstanceType.RIB.getBeanName(), ribInstanceName);
214         final ServiceRegistration<?> serviceRegistration = this.bundleContext.registerService(InstanceType.RIB.getServices(), ribImpl, properties);
215         ribImpl.setServiceRegistration(serviceRegistration);
216     }
217
218     private void initiateRibInstance(final InstanceIdentifier<Bgp> rootIdentifier, final Global global,
219         final RibImpl ribImpl, final WriteConfiguration configurationWriter) {
220         final String ribInstanceName = getRibInstanceName(rootIdentifier);
221         ribImpl.start(global, ribInstanceName, this.mappingService, configurationWriter);
222         registerRibInstance(ribImpl, ribInstanceName);
223     }
224
225     private void onNeighborsChanged(final DataObjectModification<Neighbors> dataObjectModification,
226         final InstanceIdentifier<Bgp> rootIdentifier) {
227         for (final DataObjectModification<? extends DataObject> neighborModification : dataObjectModification.getModifiedChildren()) {
228             switch (neighborModification.getModificationType()) {
229             case DELETE:
230                 onNeighborRemoved(rootIdentifier, (Neighbor) neighborModification.getDataBefore());
231                 break;
232             case SUBTREE_MODIFIED:
233             case WRITE:
234                 onNeighborModified(rootIdentifier, (Neighbor) neighborModification.getDataAfter());
235                 break;
236             default:
237                 break;
238             }
239         }
240     }
241
242     private void onNeighborModified(final InstanceIdentifier<Bgp> rootIdentifier, final Neighbor neighbor) {
243         LOG.debug("Modifing Peer instance with configuration: {}", neighbor);
244         //restart peer instance with a new configuration
245         final PeerBean bgpPeer = this.peers.get(getNeighborInstanceIdentifier(rootIdentifier, neighbor.getKey()));
246         if (bgpPeer != null) {
247             bgpPeer.close();
248             final InstanceIdentifier<Neighbor> neighborInstanceIdentifier = getNeighborInstanceIdentifier(rootIdentifier, neighbor.getKey());
249             initiatePeerInstance(rootIdentifier, neighborInstanceIdentifier, neighbor, bgpPeer);
250         } else {
251             //create new instance, if none is present
252             onNeighborCreated(rootIdentifier, neighbor);
253         }
254         LOG.debug("Peer instance modified {}", bgpPeer);
255     }
256
257     private void onNeighborCreated(final InstanceIdentifier<Bgp> rootIdentifier, final Neighbor neighbor) {
258         //create, start and register peer instance
259         LOG.debug("Creating Peer instance with configuration: {}", neighbor);
260         final PeerBean bgpPeer;
261         if (this.mappingService.isApplicationPeer(neighbor)) {
262             bgpPeer = (PeerBean) this.container.getComponentInstance(InstanceType.APP_PEER.getBeanName());
263         } else {
264             bgpPeer = (PeerBean) this.container.getComponentInstance(InstanceType.PEER.getBeanName());
265         }
266         final InstanceIdentifier<Neighbor> neighborInstanceIdentifier = getNeighborInstanceIdentifier(rootIdentifier, neighbor.getKey());
267         initiatePeerInstance(rootIdentifier, neighborInstanceIdentifier, neighbor, bgpPeer);
268         this.peers.put(neighborInstanceIdentifier, bgpPeer);
269         LOG.debug("Peer instance created {}", bgpPeer);
270     }
271
272     private void onNeighborRemoved(final InstanceIdentifier<Bgp> rootIdentifier, final Neighbor neighbor) {
273         //destroy peer instance
274         LOG.debug("Removing Peer instance: {}", rootIdentifier);
275         final PeerBean bgpPeer = this.peers.remove(getNeighborInstanceIdentifier(rootIdentifier, neighbor.getKey()));
276         if (bgpPeer != null) {
277             bgpPeer.close();
278             LOG.debug("Peer instance removed {}", bgpPeer);
279         }
280     }
281
282     private void registerPeerInstance(final BgpPeer bgpPeer, final String peerInstanceName) {
283         final Dictionary<String, String> properties = new Hashtable<>();
284         properties.put(InstanceType.PEER.getBeanName(), peerInstanceName);
285         final ServiceRegistration<?> serviceRegistration = this.bundleContext.registerService(InstanceType.PEER.getServices(), bgpPeer, properties);
286         bgpPeer.setServiceRegistration(serviceRegistration);
287     }
288
289     private void initiatePeerInstance(final InstanceIdentifier<Bgp> rootIdentifier, final InstanceIdentifier<Neighbor> neighborIdentifier, final Neighbor neighbor,
290             final PeerBean bgpPeer) {
291         final String peerInstanceName = getNeighborInstanceName(neighborIdentifier);
292         final RibImpl rib = this.ribs.get(rootIdentifier);
293         if (rib != null) {
294             bgpPeer.start(rib, neighbor, this.mappingService);
295             if (bgpPeer instanceof BgpPeer) {
296                 registerPeerInstance((BgpPeer) bgpPeer, peerInstanceName);
297             }
298         }
299     }
300
301     @Override
302     public BGPOpenConfigMappingService getMappingService() {
303         return this.mappingService;
304     }
305
306     @Override
307     public <T extends DataObject> ListenableFuture<Void> writeConfiguration(final T data, final InstanceIdentifier<T> identifier) {
308         final ReadWriteTransaction wTx = this.dataBroker.newReadWriteTransaction();
309         wTx.put(LogicalDatastoreType.CONFIGURATION, identifier, data);
310         return wTx.submit();
311     }
312
313     @Override
314     public <T extends DataObject> ListenableFuture<Void> removeConfiguration(
315         final InstanceIdentifier<T> identifier) {
316         final WriteTransaction wTx = this.dataBroker.newWriteOnlyTransaction();
317         wTx.delete(LogicalDatastoreType.CONFIGURATION, identifier);
318         return wTx.submit();
319     }
320 }