Merge "Add unit tests for sal-netconf-connector"
[netconf.git] / netconf / netconf-topology-singleton / src / main / java / org / opendaylight / netconf / topology / singleton / impl / NetconfTopologyManager.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.netconf.topology.singleton.impl;
10
11 import akka.actor.ActorSystem;
12 import com.google.common.base.Preconditions;
13 import com.google.common.util.concurrent.FutureCallback;
14 import com.google.common.util.concurrent.Futures;
15 import io.netty.util.concurrent.EventExecutor;
16 import java.util.Collection;
17 import java.util.HashMap;
18 import java.util.Map;
19 import javax.annotation.Nonnull;
20 import org.opendaylight.controller.cluster.ActorSystemProvider;
21 import org.opendaylight.controller.config.threadpool.ScheduledThreadPool;
22 import org.opendaylight.controller.config.threadpool.ThreadPool;
23 import org.opendaylight.controller.md.sal.binding.api.ClusteredDataTreeChangeListener;
24 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
25 import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
26 import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
27 import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
28 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
29 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
30 import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
31 import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
32 import org.opendaylight.controller.sal.core.api.Broker;
33 import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonServiceProvider;
34 import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonServiceRegistration;
35 import org.opendaylight.mdsal.singleton.common.api.ServiceGroupIdentifier;
36 import org.opendaylight.netconf.client.NetconfClientDispatcher;
37 import org.opendaylight.netconf.topology.singleton.api.NetconfTopologySingletonService;
38 import org.opendaylight.netconf.topology.singleton.impl.utils.NetconfTopologySetup;
39 import org.opendaylight.netconf.topology.singleton.impl.utils.NetconfTopologySetup.NetconfTopologySetupBuilder;
40 import org.opendaylight.netconf.topology.singleton.impl.utils.NetconfTopologyUtils;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNode;
42 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
43 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopologyBuilder;
44 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
45 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId;
46 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
47 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyBuilder;
48 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
49 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
50 import org.opendaylight.yangtools.concepts.ListenerRegistration;
51 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
52 import org.slf4j.Logger;
53 import org.slf4j.LoggerFactory;
54
55 public class NetconfTopologyManager
56         implements ClusteredDataTreeChangeListener<Node>, NetconfTopologySingletonService, AutoCloseable {
57
58     private static final Logger LOG = LoggerFactory.getLogger(NetconfTopologyManager.class);
59
60     private final Map<InstanceIdentifier<Node>, NetconfTopologyContext> contexts = new HashMap<>();
61     private final Map<InstanceIdentifier<Node>, ClusterSingletonServiceRegistration>
62             clusterRegistrations = new HashMap<>();
63
64     private ListenerRegistration<NetconfTopologyManager> dataChangeListenerRegistration;
65
66     private final DataBroker dataBroker;
67     private final RpcProviderRegistry rpcProviderRegistry;
68     private final ClusterSingletonServiceProvider clusterSingletonServiceProvider;
69     private final BindingAwareBroker bindingAwareBroker;
70     private final ScheduledThreadPool keepaliveExecutor;
71     private final ThreadPool processingExecutor;
72     private final Broker domBroker;
73     private final ActorSystem actorSystem;
74     private final EventExecutor eventExecutor;
75     private final NetconfClientDispatcher clientDispatcher;
76     private final String topologyId;
77
78     public NetconfTopologyManager(final DataBroker dataBroker, final RpcProviderRegistry rpcProviderRegistry,
79                            final ClusterSingletonServiceProvider clusterSingletonServiceProvider,
80                            final BindingAwareBroker bindingAwareBroker,
81                            final ScheduledThreadPool keepaliveExecutor, final ThreadPool processingExecutor,
82                            final Broker domBroker, final ActorSystemProvider actorSystemProvider, final EventExecutor eventExecutor,
83                            final NetconfClientDispatcher clientDispatcher, final String topologyId) {
84         this.dataBroker = Preconditions.checkNotNull(dataBroker);
85         this.rpcProviderRegistry = Preconditions.checkNotNull(rpcProviderRegistry);
86         this.clusterSingletonServiceProvider = Preconditions.checkNotNull(clusterSingletonServiceProvider);
87         this.bindingAwareBroker = Preconditions.checkNotNull(bindingAwareBroker);
88         this.keepaliveExecutor = Preconditions.checkNotNull(keepaliveExecutor);
89         this.processingExecutor = Preconditions.checkNotNull(processingExecutor);
90         this.domBroker = Preconditions.checkNotNull(domBroker);
91         this.actorSystem = Preconditions.checkNotNull(actorSystemProvider).getActorSystem();
92         this.eventExecutor = Preconditions.checkNotNull(eventExecutor);
93         this.clientDispatcher = Preconditions.checkNotNull(clientDispatcher);
94         this.topologyId = Preconditions.checkNotNull(topologyId);
95     }
96
97     // Blueprint init method
98     public void init() {
99         dataChangeListenerRegistration = registerDataTreeChangeListener(topologyId);
100     }
101
102     @Override
103     public void onDataTreeChanged(@Nonnull final Collection<DataTreeModification<Node>> changes) {
104         for (DataTreeModification<Node> change : changes) {
105             final DataObjectModification<Node> rootNode = change.getRootNode();
106             final InstanceIdentifier<Node> dataModifIdent = change.getRootPath().getRootIdentifier();
107             final NodeId nodeId = NetconfTopologyUtils.getNodeId(rootNode.getIdentifier());
108             switch (rootNode.getModificationType()) {
109                 case SUBTREE_MODIFIED:
110                     LOG.debug("Config for node {} updated", nodeId);
111                     refreshNetconfDeviceContext(dataModifIdent, rootNode.getDataAfter());
112                     break;
113                 case WRITE:
114                     if (contexts.containsKey(dataModifIdent)) {
115                         LOG.debug("RemoteDevice{{}} was already configured, reconfiguring node...", nodeId);
116                         refreshNetconfDeviceContext(dataModifIdent, rootNode.getDataAfter());
117                     } else {
118                         LOG.debug("Config for node {} created", nodeId);
119                         startNetconfDeviceContext(dataModifIdent, rootNode.getDataAfter());
120                     }
121                     break;
122                 case DELETE:
123                     LOG.debug("Config for node {} deleted", nodeId);
124                     stopNetconfDeviceContext(dataModifIdent);
125                     break;
126                 default:
127                     LOG.warn("Unknown operation for {}.", nodeId);
128             }
129         }
130     }
131
132     private void refreshNetconfDeviceContext(InstanceIdentifier<Node> instanceIdentifier, Node node) {
133         final NetconfTopologyContext context = contexts.get(instanceIdentifier);
134         context.refresh(createSetup(instanceIdentifier, node));
135     }
136
137     private void startNetconfDeviceContext(final InstanceIdentifier<Node> instanceIdentifier, final Node node) {
138         final NetconfNode netconfNode = node.getAugmentation(NetconfNode.class);
139         Preconditions.checkNotNull(netconfNode);
140         Preconditions.checkNotNull(netconfNode.getHost());
141         Preconditions.checkNotNull(netconfNode.getHost().getIpAddress());
142
143         final ServiceGroupIdentifier serviceGroupIdent =
144                 ServiceGroupIdentifier.create(instanceIdentifier.toString());
145
146         final NetconfTopologyContext newNetconfTopologyContext =
147                 new NetconfTopologyContext(createSetup(instanceIdentifier, node), serviceGroupIdent);
148
149         final ClusterSingletonServiceRegistration clusterSingletonServiceRegistration  =
150                 clusterSingletonServiceProvider.registerClusterSingletonService(newNetconfTopologyContext);
151
152         clusterRegistrations.put(instanceIdentifier, clusterSingletonServiceRegistration);
153         contexts.put(instanceIdentifier, newNetconfTopologyContext);
154     }
155
156     private void stopNetconfDeviceContext(final InstanceIdentifier<Node> instanceIdentifier) {
157         if (contexts.containsKey(instanceIdentifier)) {
158             try {
159                 clusterRegistrations.get(instanceIdentifier).close();
160                 contexts.get(instanceIdentifier).closeFinal();
161             } catch (Exception e) {
162                 LOG.warn("Error at closing topology context. InstanceIdentifier: " + instanceIdentifier);
163             }
164             contexts.remove(instanceIdentifier);
165             clusterRegistrations.remove(instanceIdentifier);
166         }
167     }
168
169     @Override
170     public void close() {
171         if (dataChangeListenerRegistration != null) {
172             dataChangeListenerRegistration.close();
173             dataChangeListenerRegistration = null;
174         }
175         contexts.forEach((instanceIdentifier, netconfTopologyContext) -> {
176             try {
177                 netconfTopologyContext.closeFinal();
178             } catch (Exception e) {
179                 LOG.error("Error at closing topology context. InstanceIdentifier: " + instanceIdentifier, e);
180             }
181         });
182         clusterRegistrations.forEach((instanceIdentifier, clusterSingletonServiceRegistration) -> {
183             try {
184                 clusterSingletonServiceRegistration.close();
185             } catch (Exception e) {
186                 LOG.error("Error at unregistering from cluster. InstanceIdentifier: " + instanceIdentifier, e);
187             }
188         });
189         contexts.clear();
190         clusterRegistrations.clear();
191     }
192
193     private ListenerRegistration<NetconfTopologyManager> registerDataTreeChangeListener(String topologyId) {
194         final WriteTransaction wtx = dataBroker.newWriteOnlyTransaction();
195         initTopology(wtx, LogicalDatastoreType.CONFIGURATION, topologyId);
196         initTopology(wtx, LogicalDatastoreType.OPERATIONAL, topologyId);
197         Futures.addCallback(wtx.submit(), new FutureCallback<Void>() {
198             @Override
199             public void onSuccess(Void result) {
200                 LOG.debug("topology initialization successful");
201             }
202
203             @Override
204             public void onFailure(@Nonnull Throwable throwable) {
205                 LOG.error("Unable to initialize netconf-topology, {}", throwable);
206             }
207         });
208
209         LOG.debug("Registering datastore listener");
210         return dataBroker.registerDataTreeChangeListener(
211                         new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION,
212                                 NetconfTopologyUtils.createTopologyListPath(topologyId).child(Node.class)), this);
213     }
214
215     private void initTopology(final WriteTransaction wtx, final LogicalDatastoreType datastoreType, String topologyId) {
216         final NetworkTopology networkTopology = new NetworkTopologyBuilder().build();
217         final InstanceIdentifier<NetworkTopology> networkTopologyId =
218                 InstanceIdentifier.builder(NetworkTopology.class).build();
219         wtx.merge(datastoreType, networkTopologyId, networkTopology);
220         final Topology topology = new TopologyBuilder().setTopologyId(new TopologyId(topologyId)).build();
221         wtx.merge(datastoreType, networkTopologyId.child(Topology.class,
222                 new TopologyKey(new TopologyId(topologyId))), topology);
223     }
224
225     private NetconfTopologySetup createSetup(final InstanceIdentifier<Node> instanceIdentifier, final Node node) {
226         final NetconfTopologySetupBuilder builder = NetconfTopologySetupBuilder.create()
227                 .setClusterSingletonServiceProvider(clusterSingletonServiceProvider)
228                 .setDataBroker(dataBroker)
229                 .setInstanceIdentifier(instanceIdentifier)
230                 .setRpcProviderRegistry(rpcProviderRegistry)
231                 .setNode(node)
232                 .setBindingAwareBroker(bindingAwareBroker)
233                 .setActorSystem(actorSystem)
234                 .setEventExecutor(eventExecutor)
235                 .setDomBroker(domBroker)
236                 .setKeepaliveExecutor(keepaliveExecutor)
237                 .setProcessingExecutor(processingExecutor)
238                 .setTopologyId(topologyId)
239                 .setNetconfClientDispatcher(clientDispatcher);
240
241         return builder.build();
242     }
243 }