Simplify initTopology()
[netconf.git] / netconf / netconf-topology-impl / src / main / java / org / opendaylight / netconf / topology / impl / NetconfTopologyImpl.java
1 /*
2  * Copyright (c) 2015 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.netconf.topology.impl;
9
10 import static java.util.Objects.requireNonNull;
11
12 import com.google.common.annotations.VisibleForTesting;
13 import com.google.common.util.concurrent.FutureCallback;
14 import com.google.common.util.concurrent.MoreExecutors;
15 import io.netty.util.concurrent.EventExecutor;
16 import java.util.Collection;
17 import org.opendaylight.aaa.encrypt.AAAEncryptionService;
18 import org.opendaylight.controller.config.threadpool.ScheduledThreadPool;
19 import org.opendaylight.controller.config.threadpool.ThreadPool;
20 import org.opendaylight.mdsal.binding.api.DataBroker;
21 import org.opendaylight.mdsal.binding.api.DataObjectModification;
22 import org.opendaylight.mdsal.binding.api.DataTreeChangeListener;
23 import org.opendaylight.mdsal.binding.api.DataTreeIdentifier;
24 import org.opendaylight.mdsal.binding.api.DataTreeModification;
25 import org.opendaylight.mdsal.binding.api.RpcProviderService;
26 import org.opendaylight.mdsal.binding.api.WriteTransaction;
27 import org.opendaylight.mdsal.common.api.CommitInfo;
28 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
29 import org.opendaylight.mdsal.dom.api.DOMMountPointService;
30 import org.opendaylight.netconf.client.NetconfClientDispatcher;
31 import org.opendaylight.netconf.sal.connect.api.DeviceActionFactory;
32 import org.opendaylight.netconf.sal.connect.api.RemoteDeviceHandler;
33 import org.opendaylight.netconf.sal.connect.api.SchemaResourceManager;
34 import org.opendaylight.netconf.sal.connect.netconf.sal.NetconfDeviceSalFacade;
35 import org.opendaylight.netconf.sal.connect.netconf.schema.mapping.BaseNetconfSchemas;
36 import org.opendaylight.netconf.sal.connect.util.NetconfTopologyRPCProvider;
37 import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId;
38 import org.opendaylight.netconf.topology.spi.AbstractNetconfTopology;
39 import org.opendaylight.netconf.topology.spi.NetconfConnectorDTO;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNodeTopologyService;
41 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
42 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
43 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId;
44 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
45 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyBuilder;
46 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
47 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
48 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey;
49 import org.opendaylight.yangtools.concepts.ListenerRegistration;
50 import org.opendaylight.yangtools.concepts.ObjectRegistration;
51 import org.opendaylight.yangtools.yang.binding.Identifier;
52 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
53 import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
54 import org.slf4j.Logger;
55 import org.slf4j.LoggerFactory;
56
57 public class NetconfTopologyImpl extends AbstractNetconfTopology
58         implements DataTreeChangeListener<Node>, AutoCloseable {
59
60     private static final Logger LOG = LoggerFactory.getLogger(NetconfTopologyImpl.class);
61
62     private final RpcProviderService rpcProviderService;
63     private ListenerRegistration<NetconfTopologyImpl> datastoreListenerRegistration = null;
64     private ObjectRegistration<?> rpcReg = null;
65
66     public NetconfTopologyImpl(final String topologyId, final NetconfClientDispatcher clientDispatcher,
67             final EventExecutor eventExecutor, final ScheduledThreadPool keepaliveExecutor,
68             final ThreadPool processingExecutor, final SchemaResourceManager schemaRepositoryProvider,
69             final DataBroker dataBroker, final DOMMountPointService mountPointService,
70             final AAAEncryptionService encryptionService, final RpcProviderService rpcProviderService,
71             final BaseNetconfSchemas baseSchemas) {
72         this(topologyId, clientDispatcher, eventExecutor, keepaliveExecutor, processingExecutor,
73                 schemaRepositoryProvider, dataBroker, mountPointService, encryptionService, rpcProviderService,
74                 baseSchemas, null);
75     }
76
77     public NetconfTopologyImpl(final String topologyId, final NetconfClientDispatcher clientDispatcher,
78             final EventExecutor eventExecutor, final ScheduledThreadPool keepaliveExecutor,
79             final ThreadPool processingExecutor, final SchemaResourceManager schemaRepositoryProvider,
80             final DataBroker dataBroker, final DOMMountPointService mountPointService,
81             final AAAEncryptionService encryptionService, final RpcProviderService rpcProviderService,
82             final BaseNetconfSchemas baseSchemas, final DeviceActionFactory deviceActionFactory) {
83         super(topologyId, clientDispatcher, eventExecutor, keepaliveExecutor, processingExecutor,
84                 schemaRepositoryProvider, dataBroker, mountPointService, encryptionService, deviceActionFactory,
85                 baseSchemas);
86         this.rpcProviderService = requireNonNull(rpcProviderService);
87     }
88
89     @Override
90     public void close() {
91         if (rpcReg != null) {
92             rpcReg.close();
93             rpcReg = null;
94         }
95
96         // close all existing connectors, delete whole topology in datastore?
97         for (final NetconfConnectorDTO connectorDTO : activeConnectors.values()) {
98             connectorDTO.close();
99         }
100         activeConnectors.clear();
101
102         if (datastoreListenerRegistration != null) {
103             datastoreListenerRegistration.close();
104             datastoreListenerRegistration = null;
105         }
106     }
107
108     @Override
109     protected RemoteDeviceHandler createSalFacade(final RemoteDeviceId id) {
110         return new NetconfDeviceSalFacade(id, mountPointService, dataBroker, topologyId);
111     }
112
113     /**
114      * Invoked by blueprint.
115      */
116     public void init() {
117         final WriteTransaction wtx = dataBroker.newWriteOnlyTransaction();
118         initTopology(wtx, LogicalDatastoreType.CONFIGURATION);
119         initTopology(wtx, LogicalDatastoreType.OPERATIONAL);
120         wtx.commit().addCallback(new FutureCallback<CommitInfo>() {
121             @Override
122             public void onSuccess(final CommitInfo result) {
123                 LOG.debug("topology initialization successful");
124             }
125
126             @Override
127             public void onFailure(final Throwable throwable) {
128                 LOG.error("Unable to initialize netconf-topology", throwable);
129             }
130         }, MoreExecutors.directExecutor());
131
132         LOG.debug("Registering datastore listener");
133         datastoreListenerRegistration = dataBroker.registerDataTreeChangeListener(DataTreeIdentifier.create(
134             LogicalDatastoreType.CONFIGURATION, createTopologyListPath(topologyId).child(Node.class)), this);
135         rpcReg = rpcProviderService.registerRpcImplementation(NetconfNodeTopologyService.class,
136             new NetconfTopologyRPCProvider(dataBroker, encryptionService, topologyId));
137     }
138
139     @Override
140     public void onDataTreeChanged(final Collection<DataTreeModification<Node>> collection) {
141         for (final DataTreeModification<Node> change : collection) {
142             final DataObjectModification<Node> rootNode = change.getRootNode();
143             final NodeId nodeId;
144             switch (rootNode.getModificationType()) {
145                 case SUBTREE_MODIFIED:
146                     nodeId = getNodeId(rootNode.getIdentifier());
147                     LOG.debug("Config for node {} updated", nodeId);
148                     disconnectNode(nodeId);
149                     connectNode(nodeId, rootNode.getDataAfter());
150                     break;
151                 case WRITE:
152                     nodeId = getNodeId(rootNode.getIdentifier());
153                     LOG.debug("Config for node {} created", nodeId);
154                     if (activeConnectors.containsKey(nodeId)) {
155                         LOG.warn("RemoteDevice{{}} was already configured, reconfiguring..", nodeId);
156                         disconnectNode(nodeId);
157                     }
158                     connectNode(nodeId, rootNode.getDataAfter());
159                     break;
160                 case DELETE:
161                     nodeId = getNodeId(rootNode.getIdentifier());
162                     LOG.debug("Config for node {} deleted", nodeId);
163                     disconnectNode(nodeId);
164                     break;
165                 default:
166                     LOG.debug("Unsupported modification type: {}.", rootNode.getModificationType());
167             }
168         }
169     }
170
171     private void initTopology(final WriteTransaction wtx, final LogicalDatastoreType datastoreType) {
172         // FIXME: this should be a put(), as we are initializing and will be re-populating the datastore with all the
173         //        devices. Whathever has been there before should be nuked to properly re-align lifecycle
174         wtx.merge(datastoreType, InstanceIdentifier.builder(NetworkTopology.class)
175             .child(Topology.class, new TopologyKey(new TopologyId(topologyId)))
176             .build(), new TopologyBuilder().setTopologyId(new TopologyId(topologyId)).build());
177     }
178
179     /**
180      * Determines the Netconf Node Node ID, given the node's instance
181      * identifier.
182      *
183      * @param pathArgument Node's path argument
184      * @return     NodeId for the node
185      */
186     @VisibleForTesting
187     static NodeId getNodeId(final InstanceIdentifier.PathArgument pathArgument) {
188         if (pathArgument instanceof InstanceIdentifier.IdentifiableItem<?, ?>) {
189             final Identifier<?> key = ((InstanceIdentifier.IdentifiableItem<?, ?>) pathArgument).getKey();
190             if (key instanceof NodeKey) {
191                 return ((NodeKey) key).getNodeId();
192             }
193         }
194         throw new IllegalStateException("Unable to create NodeId from: " + pathArgument);
195     }
196
197     @VisibleForTesting
198     static KeyedInstanceIdentifier<Topology, TopologyKey> createTopologyListPath(final String topologyId) {
199         final InstanceIdentifier<NetworkTopology> networkTopology = InstanceIdentifier.create(NetworkTopology.class);
200         return networkTopology.child(Topology.class, new TopologyKey(new TopologyId(topologyId)));
201     }
202 }