Bug 5061: Integrate RIBImplModule with blueprint
[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.getRibInstanceName;
12
13 import com.google.common.base.Optional;
14 import com.google.common.base.Preconditions;
15 import com.google.common.util.concurrent.CheckedFuture;
16 import com.google.common.util.concurrent.FutureCallback;
17 import com.google.common.util.concurrent.Futures;
18 import com.google.common.util.concurrent.ListenableFuture;
19 import java.util.Collection;
20 import java.util.Dictionary;
21 import java.util.HashMap;
22 import java.util.Hashtable;
23 import java.util.Map;
24 import javax.annotation.concurrent.GuardedBy;
25 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
26 import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
27 import org.opendaylight.controller.md.sal.binding.api.DataTreeChangeListener;
28 import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
29 import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
30 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
31 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
32 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
33 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
34 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
35 import org.opendaylight.protocol.bgp.openconfig.spi.BGPOpenConfigMappingService;
36 import org.opendaylight.protocol.bgp.rib.impl.spi.BgpDeployer;
37 import org.opendaylight.protocol.bgp.rib.impl.spi.InstanceType;
38 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.top.Bgp;
39 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.top.bgp.Global;
40 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.network.instance.top.NetworkInstances;
41 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.network.instance.top.network.instances.NetworkInstance;
42 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.network.instance.top.network.instances.NetworkInstanceBuilder;
43 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.network.instance.top.network.instances.NetworkInstanceKey;
44 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.network.instance.top.network.instances.network.instance.Protocols;
45 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.network.instance.top.network.instances.network.instance.ProtocolsBuilder;
46 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.network.instance.top.network.instances.network.instance.protocols.Protocol;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.openconfig.extensions.rev160614.Protocol1;
48 import org.opendaylight.yangtools.concepts.ListenerRegistration;
49 import org.opendaylight.yangtools.yang.binding.DataObject;
50 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
51 import org.osgi.framework.BundleContext;
52 import org.osgi.framework.ServiceRegistration;
53 import org.osgi.service.blueprint.container.BlueprintContainer;
54 import org.slf4j.Logger;
55 import org.slf4j.LoggerFactory;
56
57 public final class BgpDeployerImpl implements BgpDeployer, DataTreeChangeListener<Bgp>, AutoCloseable {
58
59     private static final Logger LOG = LoggerFactory.getLogger(BgpDeployerImpl.class);
60
61     private final InstanceIdentifier<NetworkInstance> networkInstanceIId;
62     private final BlueprintContainer container;
63     private final BundleContext bundleContext;
64     private final BGPOpenConfigMappingService mappingService;
65     private final ListenerRegistration<BgpDeployerImpl>  registration;
66     @GuardedBy("this")
67     private final Map<InstanceIdentifier<Bgp>, RibImpl> ribs = new HashMap<>();
68     @GuardedBy("this")
69     private boolean closed;
70
71     private final DataBroker dataBroker;
72
73     public BgpDeployerImpl(final String networkInstanceName, final BlueprintContainer container, final BundleContext bundleContext, final DataBroker dataBroker,
74             final BGPOpenConfigMappingService mappingService) {
75         this.dataBroker = Preconditions.checkNotNull(dataBroker);
76         this.container = Preconditions.checkNotNull(container);
77         this.bundleContext = Preconditions.checkNotNull(bundleContext);
78         this.mappingService = Preconditions.checkNotNull(mappingService);
79         this.networkInstanceIId = InstanceIdentifier
80                 .create(NetworkInstances.class)
81                 .child(NetworkInstance.class, new NetworkInstanceKey(networkInstanceName));
82         Futures.addCallback(initializeNetworkInstance(dataBroker, this.networkInstanceIId), new FutureCallback<Void>() {
83             @Override
84             public void onSuccess(final Void result) {
85                 LOG.debug("Network Instance {} initialized successfully.", networkInstanceName);
86             }
87             @Override
88             public void onFailure(final Throwable t) {
89                 LOG.error("Failed to initialize Network Instance {}.", networkInstanceName, t);
90             }
91         });
92         this.registration = dataBroker.registerDataTreeChangeListener(new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION, this.networkInstanceIId.child(Protocols.class)
93                 .child(Protocol.class)
94                 .augmentation(Protocol1.class)
95                 .child(Bgp.class)), this);
96         LOG.info("BGP Deployer {} started.", networkInstanceName);
97     }
98
99     @Override
100     public synchronized void onDataTreeChanged(final Collection<DataTreeModification<Bgp>> changes) {
101         if (this.closed) {
102             LOG.trace("BGP Deployer was already closed, skipping changes.");
103             return;
104         }
105         for (final DataTreeModification<Bgp> dataTreeModification : changes) {
106             final InstanceIdentifier<Bgp> rootIdentifier = dataTreeModification.getRootPath().getRootIdentifier();
107             final DataObjectModification<Bgp> rootNode = dataTreeModification.getRootNode();
108             LOG.trace("BGP configuration has changed: {}", rootNode);
109             for (final DataObjectModification<? extends DataObject> dataObjectModification : rootNode.getModifiedChildren()) {
110                 if (dataObjectModification.getDataType().equals(Global.class)) {
111                     onGlobalChanged((DataObjectModification<Global>) dataObjectModification, rootIdentifier);
112                 }
113             }
114         }
115     }
116
117     @Override
118     public InstanceIdentifier<NetworkInstance> getInstanceIdentifier() {
119         return this.networkInstanceIId;
120     }
121
122     @Override
123     public synchronized void close() throws Exception {
124         this.registration.close();
125         this.ribs.values().forEach(rib -> rib.close());
126         this.ribs.clear();
127         this.closed = true;
128     }
129
130     private static CheckedFuture<Void, TransactionCommitFailedException> initializeNetworkInstance(final DataBroker dataBroker,
131             final InstanceIdentifier<NetworkInstance> networkInstance) {
132         final WriteTransaction wTx = dataBroker.newWriteOnlyTransaction();
133         wTx.merge(LogicalDatastoreType.CONFIGURATION, networkInstance,
134                 new NetworkInstanceBuilder().setName(networkInstance.firstKeyOf(NetworkInstance.class).getName()).setProtocols(new ProtocolsBuilder().build()).build());
135         return wTx.submit();
136     }
137
138     private void onGlobalChanged(final DataObjectModification<Global> dataObjectModification,
139             final InstanceIdentifier<Bgp> rootIdentifier) {
140         switch (dataObjectModification.getModificationType()) {
141         case DELETE:
142             onGlobalRemoved(rootIdentifier);
143             break;
144         case SUBTREE_MODIFIED:
145         case WRITE:
146             onGlobalModified(rootIdentifier, dataObjectModification.getDataAfter());
147             break;
148         default:
149             break;
150         }
151     }
152
153     private void onGlobalModified(final InstanceIdentifier<Bgp> rootIdentifier, final Global global) {
154         LOG.debug("Modifing RIB instance with configuration: {}", global);
155         //restart existing rib instance with a new configuration
156         final RibImpl ribImpl = this.ribs.get(rootIdentifier);
157         if (ribImpl != null) {
158             ribImpl.close();
159             initiateRibInstance(rootIdentifier, global, ribImpl);
160         } else {
161             //if not exists, create a new instance
162             onGlobalCreated(rootIdentifier, global);
163         }
164         LOG.debug("RIB instance modified {}", ribImpl);
165     }
166
167     private void onGlobalCreated(final InstanceIdentifier<Bgp> rootIdentifier, final Global global) {
168         //create, start and register rib instance
169         LOG.debug("Creating RIB instance with configuration: {}", global);
170         final RibImpl ribImpl = (RibImpl) this.container.getComponentInstance(InstanceType.RIB.getBeanName());
171         initiateRibInstance(rootIdentifier, global, ribImpl);
172         this.ribs.put(rootIdentifier, ribImpl);
173         LOG.debug("RIB instance created {}", ribImpl);
174     }
175
176     private void onGlobalRemoved(final InstanceIdentifier<Bgp> rootIdentifier) {
177         //destroy rib instance
178         LOG.debug("Removing RIB instance: {}", rootIdentifier);
179         final RibImpl ribImpl = this.ribs.remove(rootIdentifier);
180         if (ribImpl != null) {
181             ribImpl.close();
182             LOG.debug("RIB instance removed {}", ribImpl);
183         }
184     }
185
186     private void registerRibInstance(final RibImpl ribImpl, final String ribInstanceName) {
187         final Dictionary<String, String> properties = new Hashtable<>();
188         properties.put(InstanceType.RIB.getBeanName(), ribInstanceName);
189         final ServiceRegistration<?> serviceRegistration = this.bundleContext.registerService(InstanceType.RIB.getServices(), ribImpl, properties);
190         ribImpl.setServiceRegistration(serviceRegistration);
191     }
192
193     private void initiateRibInstance(final InstanceIdentifier<Bgp> rootIdentifier, final Global global,
194             final RibImpl ribImpl) {
195         final String ribInstanceName = getRibInstanceName(rootIdentifier);
196         ribImpl.start(global, ribInstanceName, this.mappingService);
197         registerRibInstance(ribImpl, ribInstanceName);
198     }
199
200     @Override
201     public <T extends DataObject> ListenableFuture<Void> writeConfiguration(final T data,
202             final InstanceIdentifier<T> identifier) {
203         final ReadWriteTransaction wTx = this.dataBroker.newReadWriteTransaction();
204         final CheckedFuture<Optional<T>, ReadFailedException> readFuture = wTx.read(LogicalDatastoreType.CONFIGURATION, identifier);
205         Futures.addCallback(readFuture, new FutureCallback<Optional<T>>() {
206             @Override
207             public void onSuccess(final Optional<T> result) {
208                 if (!result.isPresent()) {
209                     wTx.put(LogicalDatastoreType.CONFIGURATION, identifier, data);
210                 }
211             }
212             @Override
213             public void onFailure(final Throwable t) {
214                 LOG.debug("Failed to ensure presence of {}, try to write configuration.", identifier, t);
215                 wTx.put(LogicalDatastoreType.CONFIGURATION, identifier, data);
216             }
217         });
218         return wTx.submit();
219
220     }
221
222     @Override
223     public <T extends DataObject> ListenableFuture<Void> removeConfiguration(
224             final InstanceIdentifier<T> identifier) {
225         final WriteTransaction wTx = this.dataBroker.newWriteOnlyTransaction();
226         wTx.delete(LogicalDatastoreType.CONFIGURATION, identifier);
227         return wTx.submit();
228     }
229
230     @Override
231     public BGPOpenConfigMappingService getMappingService() {
232         return this.mappingService;
233     }
234
235 }