/* * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ package org.opendaylight.netconf.topology.impl; import com.google.common.annotations.VisibleForTesting; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import io.netty.util.concurrent.EventExecutor; import java.util.Collection; import javax.annotation.PreDestroy; import javax.inject.Inject; import javax.inject.Singleton; import org.opendaylight.aaa.encrypt.AAAEncryptionService; import org.opendaylight.controller.config.threadpool.ScheduledThreadPool; import org.opendaylight.controller.config.threadpool.ThreadPool; import org.opendaylight.mdsal.binding.api.DataBroker; import org.opendaylight.mdsal.binding.api.DataObjectModification; import org.opendaylight.mdsal.binding.api.DataTreeChangeListener; import org.opendaylight.mdsal.binding.api.DataTreeIdentifier; import org.opendaylight.mdsal.binding.api.DataTreeModification; import org.opendaylight.mdsal.binding.api.RpcProviderService; import org.opendaylight.mdsal.common.api.LogicalDatastoreType; import org.opendaylight.mdsal.dom.api.DOMMountPointService; import org.opendaylight.netconf.client.NetconfClientDispatcher; import org.opendaylight.netconf.sal.connect.api.DeviceActionFactory; import org.opendaylight.netconf.sal.connect.api.SchemaResourceManager; import org.opendaylight.netconf.sal.connect.netconf.schema.mapping.BaseNetconfSchemas; import org.opendaylight.netconf.topology.spi.AbstractNetconfTopology; import org.opendaylight.netconf.topology.spi.NetconfConnectorDTO; import org.opendaylight.netconf.topology.spi.NetconfNodeUtils; import org.opendaylight.netconf.topology.spi.NetconfTopologyRPCProvider; import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev221225.NetconfNodeTopologyService; import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology; import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId; import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId; import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology; import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey; import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node; import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey; import org.opendaylight.yangtools.concepts.Registration; import org.opendaylight.yangtools.yang.binding.Identifier; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier; import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Deactivate; import org.osgi.service.component.annotations.Reference; import org.slf4j.Logger; import org.slf4j.LoggerFactory; // Non-final for testing @Singleton @Component(service = { }) public class NetconfTopologyImpl extends AbstractNetconfTopology implements DataTreeChangeListener, AutoCloseable { private static final Logger LOG = LoggerFactory.getLogger(NetconfTopologyImpl.class); private Registration dtclReg; private Registration rpcReg; @Inject @Activate public NetconfTopologyImpl( @Reference(target = "(type=netconf-client-dispatcher)") final NetconfClientDispatcher clientDispatcher, @Reference(target = "(type=global-event-executor)") final EventExecutor eventExecutor, @Reference(target = "(type=global-netconf-ssh-scheduled-executor)") final ScheduledThreadPool keepaliveExecutor, @Reference(target = "(type=global-netconf-processing-executor)") final ThreadPool processingExecutor, @Reference final SchemaResourceManager schemaRepositoryProvider, @Reference final DataBroker dataBroker, @Reference final DOMMountPointService mountPointService, @Reference final AAAEncryptionService encryptionService, @Reference final RpcProviderService rpcProviderService, @Reference final BaseNetconfSchemas baseSchemas, @Reference final DeviceActionFactory deviceActionFactory) { this(NetconfNodeUtils.DEFAULT_TOPOLOGY_NAME, clientDispatcher, eventExecutor, keepaliveExecutor, processingExecutor, schemaRepositoryProvider, dataBroker, mountPointService, encryptionService, rpcProviderService, baseSchemas, deviceActionFactory); } public NetconfTopologyImpl(final String topologyId, final NetconfClientDispatcher clientDispatcher, final EventExecutor eventExecutor, final ScheduledThreadPool keepaliveExecutor, final ThreadPool processingExecutor, final SchemaResourceManager schemaRepositoryProvider, final DataBroker dataBroker, final DOMMountPointService mountPointService, final AAAEncryptionService encryptionService, final RpcProviderService rpcProviderService, final BaseNetconfSchemas baseSchemas) { this(topologyId, clientDispatcher, eventExecutor, keepaliveExecutor, processingExecutor, schemaRepositoryProvider, dataBroker, mountPointService, encryptionService, rpcProviderService, baseSchemas, null); } @SuppressFBWarnings(value = "MC_OVERRIDABLE_METHOD_CALL_IN_CONSTRUCTOR", justification = "DTCL registration of 'this'") public NetconfTopologyImpl(final String topologyId, final NetconfClientDispatcher clientDispatcher, final EventExecutor eventExecutor, final ScheduledThreadPool keepaliveExecutor, final ThreadPool processingExecutor, final SchemaResourceManager schemaRepositoryProvider, final DataBroker dataBroker, final DOMMountPointService mountPointService, final AAAEncryptionService encryptionService, final RpcProviderService rpcProviderService, final BaseNetconfSchemas baseSchemas, final DeviceActionFactory deviceActionFactory) { super(topologyId, clientDispatcher, eventExecutor, keepaliveExecutor, processingExecutor, schemaRepositoryProvider, dataBroker, mountPointService, encryptionService, deviceActionFactory, baseSchemas); LOG.debug("Registering datastore listener"); dtclReg = dataBroker.registerDataTreeChangeListener(DataTreeIdentifier.create( LogicalDatastoreType.CONFIGURATION, createTopologyListPath(topologyId).child(Node.class)), this); rpcReg = rpcProviderService.registerRpcImplementation(NetconfNodeTopologyService.class, new NetconfTopologyRPCProvider(dataBroker, encryptionService, topologyId)); } @PreDestroy @Deactivate @Override public void close() { if (rpcReg != null) { rpcReg.close(); rpcReg = null; } // close all existing connectors, delete whole topology in datastore? for (final NetconfConnectorDTO connectorDTO : activeConnectors.values()) { connectorDTO.close(); } activeConnectors.clear(); if (dtclReg != null) { dtclReg.close(); dtclReg = null; } } @Override public void onDataTreeChanged(final Collection> collection) { for (final DataTreeModification change : collection) { final DataObjectModification rootNode = change.getRootNode(); final NodeId nodeId; switch (rootNode.getModificationType()) { case SUBTREE_MODIFIED: nodeId = getNodeId(rootNode.getIdentifier()); LOG.debug("Config for node {} updated", nodeId); disconnectNode(nodeId); connectNode(nodeId, rootNode.getDataAfter()); break; case WRITE: nodeId = getNodeId(rootNode.getIdentifier()); LOG.debug("Config for node {} created", nodeId); if (activeConnectors.containsKey(nodeId)) { LOG.warn("RemoteDevice{{}} was already configured, reconfiguring..", nodeId); disconnectNode(nodeId); } connectNode(nodeId, rootNode.getDataAfter()); break; case DELETE: nodeId = getNodeId(rootNode.getIdentifier()); LOG.debug("Config for node {} deleted", nodeId); disconnectNode(nodeId); break; default: LOG.debug("Unsupported modification type: {}.", rootNode.getModificationType()); } } } /** * Determines the Netconf Node Node ID, given the node's instance * identifier. * * @param pathArgument Node's path argument * @return NodeId for the node */ @VisibleForTesting static NodeId getNodeId(final InstanceIdentifier.PathArgument pathArgument) { if (pathArgument instanceof InstanceIdentifier.IdentifiableItem) { final Identifier key = ((InstanceIdentifier.IdentifiableItem) pathArgument).getKey(); if (key instanceof NodeKey) { return ((NodeKey) key).getNodeId(); } } throw new IllegalStateException("Unable to create NodeId from: " + pathArgument); } @VisibleForTesting static KeyedInstanceIdentifier createTopologyListPath(final String topologyId) { final InstanceIdentifier networkTopology = InstanceIdentifier.create(NetworkTopology.class); return networkTopology.child(Topology.class, new TopologyKey(new TopologyId(topologyId))); } }