Add shared schema repository and netconf topology binding.
Create a default schema repository instance.
Add topology config.
Simplifiy netconf connector model to use new netconf
topology with backwards compatibility.
Bump netconf connector schema revision.
Change-Id: I3978a0a7e6e3c08dba52e65912bfe33ad46b4cf8
Signed-off-by: Tomas Cere <tcere@cisco.com>
<packaging>jar</packaging>
<properties>
<features.file>features.xml</features.file>
+ <netconf.connector.version>1.3.0-SNAPSHOT</netconf.connector.version>
</properties>
<dependencyManagement>
<groupId>${project.groupId}</groupId>
<artifactId>netconf-connector-config</artifactId>
</dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>netconf-topology</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>netconf-topology-config</artifactId>
+ <classifier>config</classifier>
+ <type>xml</type>
+ </dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>netconf-tcp</artifactId>
<feature version='${yangtools.version}'>odl-mdsal-models</feature>
<bundle>mvn:org.opendaylight.netconf/sal-netconf-connector/${controller.mdsal.version}</bundle>
<bundle>mvn:org.opendaylight.controller.model/model-inventory/${controller.mdsal.version}</bundle>
+ <bundle>mvn:org.opendaylight.netconf/netconf-topology/${netconf.version}</bundle>
+ <bundle>mvn:org.opendaylight.netconf/sal-netconf-connector/${netconf.connector.version}</bundle>
+ <bundle>mvn:org.opendaylight.netconf/netconf-config-dispatcher/${netconf.version}</bundle>
+ <configfile finalname='${config.configfile.directory}/${config.netconf.client.configfile}'>mvn:org.opendaylight.netconf/netconf-config/${netconf.version}/xml/config</configfile>
+ <configfile finalname='${config.configfile.directory}/${config.netconf.topology.configfile}'>mvn:org.opendaylight.netconf/netconf-topology-config/${netconf.version}/xml/config</configfile>
</feature>
<feature name='odl-netconf-connector-ssh' version='${project.version}' description="OpenDaylight :: Netconf Connector :: Netconf Connector + Netconf SSH Server + loopback connection configuration">
<artifactId>mdsal-netconf-notification</artifactId>
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>netconf-topology</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>netconf-topology-config</artifactId>
+ <version>${project.version}</version>
+ <classifier>config</classifier>
+ <type>xml</type>
+ </dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>netconf-client</artifactId>
</data>
</configuration>
<required-capabilities>
- <capability>urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf?module=odl-sal-netconf-connector-cfg&revision=2013-10-28</capability>
+ <capability>urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf?module=odl-sal-netconf-connector-cfg&revision=2015-08-03</capability>
</required-capabilities>
</snapshot>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <artifactId>netconf-subsystem</artifactId>
+ <groupId>org.opendaylight.netconf</groupId>
+ <version>1.0.0-SNAPSHOT</version>
+ <relativePath>../</relativePath>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>netconf-topology-config</artifactId>
+ <description>Configuration files for netconf topology</description>
+ <packaging>jar</packaging>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>build-helper-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>attach-artifacts</id>
+ <goals>
+ <goal>attach-artifact</goal>
+ </goals>
+ <phase>package</phase>
+ <configuration>
+ <artifacts>
+ <artifact>
+ <file>${project.build.directory}/classes/initial/02-netconf-topology.xml</file>
+ <type>xml</type>
+ <classifier>config</classifier>
+ </artifact>
+ </artifacts>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ 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
+-->
+<snapshot>
+ <configuration>
+ <data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netconf:topology">prefix:netconf-topology-impl</type>
+ <name>default-netconf-topology</name>
+ <event-executor xmlns="urn:opendaylight:params:xml:ns:yang:controller:netconf:topology">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netty">prefix:netty-event-executor</type>
+ <name>global-event-executor</name>
+ </event-executor>
+ <binding-registry xmlns="urn:opendaylight:params:xml:ns:yang:controller:netconf:topology">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">prefix:binding-broker-osgi-registry</type>
+ <name>binding-osgi-broker</name>
+ </binding-registry>
+ <dom-registry xmlns="urn:opendaylight:params:xml:ns:yang:controller:netconf:topology">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">prefix:dom-broker-osgi-registry</type>
+ <name>dom-broker</name>
+ </dom-registry>
+ <client-dispatcher xmlns="urn:opendaylight:params:xml:ns:yang:controller:netconf:topology">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:netconf">prefix:netconf-client-dispatcher</type>
+ <name>global-netconf-dispatcher</name>
+ </client-dispatcher>
+ <processing-executor xmlns="urn:opendaylight:params:xml:ns:yang:controller:netconf:topology">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:threadpool">prefix:threadpool</type>
+ <name>global-netconf-processing-executor</name>
+ </processing-executor>
+ <keepalive-executor xmlns="urn:opendaylight:params:xml:ns:yang:controller:netconf:topology">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:threadpool">prefix:scheduled-threadpool</type>
+ <name>global-netconf-ssh-scheduled-executor</name>
+ </keepalive-executor>
+ <shared-schema-repository xmlns="urn:opendaylight:params:xml:ns:yang:controller:netconf:topology">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netconf:topology:shared:schema:repository">prefix:shared-schema-repository</type>
+ <name>default-shared-schema-repository</name>
+ </shared-schema-repository>
+ </module>
+ </modules>
+
+ <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netconf:topology">prefix:netconf-topology</type>
+ <instance>
+ <name>default-netconf-topology</name>
+ <provider>/modules/module[type='netconf-topology-impl'][name='default-netconf-topology']</provider>
+ </instance>
+ </service>
+ </services>
+ </data>
+ </configuration>
+ <required-capabilities>
+ <capability>urn:opendaylight:params:xml:ns:yang:controller:netconf:topology?module=netconf-topology&revision=2015-07-27</capability>
+ </required-capabilities>
+</snapshot>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <artifactId>netconf-subsystem</artifactId>
+ <groupId>org.opendaylight.netconf</groupId>
+ <version>1.0.0-SNAPSHOT</version>
+ <relativePath>../</relativePath>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>netconf-topology</artifactId>
+ <packaging>bundle</packaging>
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-binding-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-binding-config</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.mdsal</groupId>
+ <artifactId>mdsal-binding-generator-impl</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>netconf-config-dispatcher</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-core-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>threadpool-config-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-parser-impl</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>yang-jmx-generator-plugin</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.netconf</groupId>
+ <artifactId>sal-netconf-connector</artifactId>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Import-Package>*</Import-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+
+ <plugin>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>config</id>
+ <goals>
+ <goal>generate-sources</goal>
+ </goals>
+ <configuration>
+ <codeGenerators>
+ <generator>
+ <codeGeneratorClass>org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator</codeGeneratorClass>
+ <outputBaseDir>${jmxGeneratorPath}</outputBaseDir>
+ <additionalConfiguration>
+ <namespaceToPackage1>urn:opendaylight:params:xml:ns:yang:controller==org.opendaylight.controller.config.yang</namespaceToPackage1>
+ </additionalConfiguration>
+ </generator>
+ <generator>
+ <codeGeneratorClass>org.opendaylight.yangtools.maven.sal.api.gen.plugin.CodeGeneratorImpl</codeGeneratorClass>
+ <outputBaseDir>${salGeneratorPath}</outputBaseDir>
+ </generator>
+ </codeGenerators>
+ <inspectDependencies>true</inspectDependencies>
+ </configuration>
+ </execution>
+ </executions>
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>yang-jmx-generator-plugin</artifactId>
+ <version>${config.version}</version>
+ </dependency>
+ </dependencies>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>build-helper-maven-plugin</artifactId>
+ <version>1.8</version>
+ <executions>
+ <execution>
+ <id>add-source</id>
+ <phase>generate-sources</phase>
+ <goals>
+ <goal>add-source</goal>
+ </goals>
+ <configuration>
+ <sources>
+ <source>${project.build.directory}/generated-sources/config</source>;
+ </sources>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
\ No newline at end of file
--- /dev/null
+/*
+ * 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.controller.config.yang.netconf.topology;
+
+import org.opendaylight.netconf.topology.impl.NetconfTopologyImpl;
+
+public class NetconfTopologyModule extends org.opendaylight.controller.config.yang.netconf.topology.AbstractNetconfTopologyModule {
+ public NetconfTopologyModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+ super(identifier, dependencyResolver);
+ }
+
+ public NetconfTopologyModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, org.opendaylight.controller.config.yang.netconf.topology.NetconfTopologyModule oldModule, AutoCloseable oldInstance) {
+ super(identifier, dependencyResolver, oldModule, oldInstance);
+ }
+
+ @Override
+ public void customValidation() {
+ // add custom validation form module attributes here.
+ this.getClientDispatcherDependency();
+ }
+
+ @Override
+ public AutoCloseable createInstance() {
+ return new NetconfTopologyImpl(getTopologyId(), getClientDispatcherDependency(), getBindingRegistryDependency(),
+ getDomRegistryDependency(), getEventExecutorDependency(), getKeepaliveExecutorDependency(),
+ getProcessingExecutorDependency(), getSharedSchemaRepositoryDependency());
+ }
+
+}
--- /dev/null
+/*
+ * 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
+ */
+/*
+* Generated file
+*
+* Generated from: yang module name: netconf-topology yang module local name: netconf-topology-impl
+* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+* Generated at: Tue Jul 28 15:33:44 CEST 2015
+*
+* Do not modify this file unless it is present under src/main directory
+*/
+package org.opendaylight.controller.config.yang.netconf.topology;
+
+public class NetconfTopologyModuleFactory extends org.opendaylight.controller.config.yang.netconf.topology.AbstractNetconfTopologyModuleFactory {
+
+}
--- /dev/null
+package org.opendaylight.controller.config.yang.netconf.topology.shared.schema.repository;
+
+import org.opendaylight.netconf.topology.SchemaRepositoryProvider;
+import org.opendaylight.yangtools.yang.parser.repo.SharedSchemaRepository;
+
+public class SchemaRepositoryImplModule extends org.opendaylight.controller.config.yang.netconf.topology.shared.schema.repository.AbstractSchemaRepositoryImplModule {
+ public SchemaRepositoryImplModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+ super(identifier, dependencyResolver);
+ }
+
+ public SchemaRepositoryImplModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, org.opendaylight.controller.config.yang.netconf.topology.shared.schema.repository.SchemaRepositoryImplModule oldModule, java.lang.AutoCloseable oldInstance) {
+ super(identifier, dependencyResolver, oldModule, oldInstance);
+ }
+
+ @Override
+ public void customValidation() {
+ // add custom validation form module attributes here.
+ }
+
+ @Override
+ public java.lang.AutoCloseable createInstance() {
+ return new SchemaRepositoryProviderAutoCloseAble(this);
+ }
+
+ private static class SchemaRepositoryProviderAutoCloseAble implements SchemaRepositoryProvider, AutoCloseable {
+
+ private final SharedSchemaRepository schemaRepository;
+
+ public SchemaRepositoryProviderAutoCloseAble(SchemaRepositoryImplModule module) {
+ schemaRepository = new SharedSchemaRepository(module.getIdentifier().getInstanceName());
+ }
+
+ @Override
+ public void close() throws Exception {
+ //NOOP
+ }
+
+ @Override
+ public SharedSchemaRepository getSharedSchemaRepository() {
+ return schemaRepository;
+ }
+ }
+}
--- /dev/null
+/*
+* Generated file
+*
+* Generated from: yang module name: shared-schema-repository yang module local name: shared-schema-repository-impl
+* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+* Generated at: Tue Sep 08 13:43:39 CEST 2015
+*
+* Do not modify this file unless it is present under src/main directory
+*/
+package org.opendaylight.controller.config.yang.netconf.topology.shared.schema.repository;
+
+import com.google.common.collect.Sets;
+import java.util.Set;
+import org.opendaylight.controller.config.api.DependencyResolverFactory;
+import org.opendaylight.controller.config.api.ModuleIdentifier;
+import org.osgi.framework.BundleContext;
+
+public class SchemaRepositoryImplModuleFactory extends org.opendaylight.controller.config.yang.netconf.topology.shared.schema.repository.AbstractSchemaRepositoryImplModuleFactory {
+
+ public static final ModuleIdentifier defaultInstanceId = new ModuleIdentifier(NAME, "default-shared-schema-repository");
+
+ @Override
+ public Set<SchemaRepositoryImplModule> getDefaultModules(DependencyResolverFactory dependencyResolverFactory, BundleContext bundleContext) {
+ SchemaRepositoryImplModule defaultModule = new SchemaRepositoryImplModule(
+ defaultInstanceId, dependencyResolverFactory.createDependencyResolver(defaultInstanceId));
+ return Sets.newHashSet(defaultModule);
+ }
+}
--- /dev/null
+/*
+ * 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;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.netconf.sal.connect.api.RemoteDeviceHandler;
+import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCapabilities;
+import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfSessionPreferences;
+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.network.topology.topology.Node;
+
+// TODO maybe rename to NetconfTopologyDispatcher?
+public interface NetconfTopology {
+
+ String getTopologyId();
+
+ DataBroker getDataBroker();
+
+ ListenableFuture<NetconfDeviceCapabilities> connectNode(NodeId nodeId, Node configNode);
+
+ ListenableFuture<Void> disconnectNode(NodeId nodeId);
+
+ void registerConnectionStatusListener(NodeId node, RemoteDeviceHandler<NetconfSessionPreferences> listener);
+}
--- /dev/null
+/*
+ * 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;
+
+import org.opendaylight.yangtools.yang.parser.repo.SharedSchemaRepository;
+
+public interface SchemaRepositoryProvider{
+
+ SharedSchemaRepository getSharedSchemaRepository();
+}
--- /dev/null
+/*
+ * 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;
+
+import com.google.common.base.Preconditions;
+import java.util.ArrayList;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
+import org.opendaylight.controller.md.sal.dom.api.DOMNotification;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
+import org.opendaylight.controller.sal.core.api.Broker;
+import org.opendaylight.netconf.sal.connect.api.RemoteDeviceHandler;
+import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfSessionPreferences;
+import org.opendaylight.netconf.sal.connect.netconf.sal.NetconfDeviceDataBroker;
+import org.opendaylight.netconf.sal.connect.netconf.sal.NetconfDeviceNotificationService;
+import org.opendaylight.netconf.sal.connect.netconf.sal.NetconfDeviceSalProvider;
+import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class TopologyMountPointFacade implements AutoCloseable, RemoteDeviceHandler<NetconfSessionPreferences> {
+
+ private static final Logger LOG = LoggerFactory.getLogger(TopologyMountPointFacade.class);
+
+ private final RemoteDeviceId id;
+ private final Broker domBroker;
+ private final BindingAwareBroker bindingBroker;
+ private final long defaultRequestTimeoutMillis;
+
+ private SchemaContext remoteSchemaContext = null;
+ private NetconfSessionPreferences netconfSessionPreferences = null;
+ private DOMRpcService deviceRpc = null;
+ private final NetconfDeviceSalProvider salProvider;
+
+ private final ArrayList<RemoteDeviceHandler<NetconfSessionPreferences>> connectionStatusListeners = new ArrayList<>();
+
+ public TopologyMountPointFacade(final RemoteDeviceId id,
+ final Broker domBroker,
+ final BindingAwareBroker bindingBroker,
+ long defaultRequestTimeoutMillis) {
+
+ this.id = id;
+ this.domBroker = domBroker;
+ this.bindingBroker = bindingBroker;
+ this.defaultRequestTimeoutMillis = defaultRequestTimeoutMillis;
+ this.salProvider = new NetconfDeviceSalProvider(id);
+ registerToSal(domBroker, bindingBroker);
+ }
+
+ public void registerToSal(final Broker domRegistryDependency, final BindingAwareBroker bindingBroker) {
+ domRegistryDependency.registerProvider(salProvider);
+ bindingBroker.registerProvider(salProvider);
+ }
+
+ @Override
+ public void onDeviceConnected(final SchemaContext remoteSchemaContext,
+ final NetconfSessionPreferences netconfSessionPreferences,
+ final DOMRpcService deviceRpc) {
+ // prepare our prerequisites for mountpoint
+ this.remoteSchemaContext = remoteSchemaContext;
+ this.netconfSessionPreferences = netconfSessionPreferences;
+ this.deviceRpc = deviceRpc;
+ for (RemoteDeviceHandler<NetconfSessionPreferences> listener : connectionStatusListeners) {
+ listener.onDeviceConnected(remoteSchemaContext, netconfSessionPreferences, deviceRpc);
+ }
+ }
+
+ @Override
+ public void onDeviceDisconnected() {
+ salProvider.getMountInstance().onTopologyDeviceDisconnected();
+ for (RemoteDeviceHandler<NetconfSessionPreferences> listener : connectionStatusListeners) {
+ listener.onDeviceDisconnected();
+ }
+ }
+
+ @Override
+ public void onDeviceFailed(Throwable throwable) {
+ salProvider.getMountInstance().onTopologyDeviceDisconnected();
+ for (RemoteDeviceHandler<NetconfSessionPreferences> listener : connectionStatusListeners) {
+ listener.onDeviceFailed(throwable);
+ }
+ }
+
+ @Override
+ public void onNotification(DOMNotification domNotification) {
+ salProvider.getMountInstance().publish(domNotification);
+ }
+
+ public void registerMountPoint() {
+ Preconditions.checkNotNull(id);
+ Preconditions.checkNotNull(remoteSchemaContext);
+ Preconditions.checkNotNull(netconfSessionPreferences);
+
+ final DOMDataBroker netconfDeviceDataBroker = new NetconfDeviceDataBroker(id, remoteSchemaContext, deviceRpc, netconfSessionPreferences, defaultRequestTimeoutMillis);
+ final NetconfDeviceNotificationService notificationService = new NetconfDeviceNotificationService();
+
+ salProvider.getMountInstance().onTopologyDeviceConnected(remoteSchemaContext, netconfDeviceDataBroker, deviceRpc, notificationService);
+ }
+
+ public void unregisterMountPoint() {
+ salProvider.getMountInstance().onTopologyDeviceDisconnected();
+ }
+
+ public void registerConnectionStatusListener(final RemoteDeviceHandler<NetconfSessionPreferences> listener) {
+ connectionStatusListeners.add(listener);
+ }
+
+ @Override
+ public void close() {
+ closeGracefully(salProvider);
+ }
+
+ private void closeGracefully(final AutoCloseable resource) {
+ if (resource != null) {
+ try {
+ resource.close();
+ } catch (final Exception e) {
+ LOG.warn("{}: Ignoring exception while closing {}", id, resource, e);
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ * 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.base.Preconditions;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import io.netty.util.concurrent.EventExecutor;
+import java.math.BigDecimal;
+import java.net.InetSocketAddress;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import org.opendaylight.controller.config.threadpool.ScheduledThreadPool;
+import org.opendaylight.controller.config.threadpool.ThreadPool;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderContext;
+import org.opendaylight.controller.sal.binding.api.BindingAwareProvider;
+import org.opendaylight.controller.sal.core.api.Broker;
+import org.opendaylight.controller.sal.core.api.Broker.ProviderSession;
+import org.opendaylight.controller.sal.core.api.Provider;
+import org.opendaylight.netconf.client.NetconfClientDispatcher;
+import org.opendaylight.netconf.client.conf.NetconfClientConfiguration;
+import org.opendaylight.netconf.client.conf.NetconfReconnectingClientConfiguration;
+import org.opendaylight.netconf.client.conf.NetconfReconnectingClientConfigurationBuilder;
+import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.AuthenticationHandler;
+import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.LoginPassword;
+import org.opendaylight.netconf.sal.connect.api.RemoteDeviceHandler;
+import org.opendaylight.netconf.sal.connect.netconf.NetconfDevice;
+import org.opendaylight.netconf.sal.connect.netconf.NetconfStateSchemas;
+import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCapabilities;
+import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCommunicator;
+import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfSessionPreferences;
+import org.opendaylight.netconf.sal.connect.netconf.sal.KeepaliveSalFacade;
+import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId;
+import org.opendaylight.netconf.topology.NetconfTopology;
+import org.opendaylight.netconf.topology.SchemaRepositoryProvider;
+import org.opendaylight.netconf.topology.TopologyMountPointFacade;
+import org.opendaylight.protocol.framework.ReconnectStrategy;
+import org.opendaylight.protocol.framework.ReconnectStrategyFactory;
+import org.opendaylight.protocol.framework.TimedReconnectStrategy;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Host;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.Credentials;
+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.network.topology.topology.Node;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactory;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class NetconfTopologyImpl implements NetconfTopology, BindingAwareProvider, Provider, AutoCloseable {
+
+ private static final Logger LOG = LoggerFactory.getLogger(NetconfTopologyImpl.class);
+
+ private final String topologyId;
+ private final NetconfClientDispatcher clientDispatcher;
+ private final BindingAwareBroker bindingAwareBroker;
+ private final Broker domBroker;
+ private final EventExecutor eventExecutor;
+ private final ScheduledThreadPool keepaliveExecutor;
+ private final ThreadPool processingExecutor;
+ private final SchemaRepositoryProvider sharedSchemaRepository;
+
+ private SchemaSourceRegistry schemaSourceRegistry = null;
+ private SchemaContextFactory schemaContextFactory = null;
+
+ private DOMMountPointService mountPointService = null;
+ private DataBroker dataBroker = null;
+ private final HashMap<NodeId, NetconfConnectorDTO> activeConnectors = new HashMap<>();
+
+ public NetconfTopologyImpl(final String topologyId, final NetconfClientDispatcher clientDispatcher,
+ final BindingAwareBroker bindingAwareBroker, final Broker domBroker,
+ final EventExecutor eventExecutor, final ScheduledThreadPool keepaliveExecutor,
+ final ThreadPool processingExecutor, final SchemaRepositoryProvider sharedSchemaRepository) {
+ this.topologyId = topologyId;
+ this.clientDispatcher = clientDispatcher;
+ this.bindingAwareBroker = bindingAwareBroker;
+ this.domBroker = domBroker;
+ this.eventExecutor = eventExecutor;
+ this.keepaliveExecutor = keepaliveExecutor;
+ this.processingExecutor = processingExecutor;
+ this.sharedSchemaRepository = sharedSchemaRepository;
+
+ registerToSal(this, this);
+ }
+
+ private void registerToSal(BindingAwareProvider baProvider, Provider provider) {
+ domBroker.registerProvider(provider);
+ bindingAwareBroker.registerProvider(baProvider);
+ }
+
+ @Override
+ public void close() throws Exception {
+ // close all existing connectors, delete whole topology in datastore?
+ for (NetconfConnectorDTO connectorDTO : activeConnectors.values()) {
+ connectorDTO.getCommunicator().disconnect();
+ }
+ activeConnectors.clear();
+ }
+
+ @Override
+ public String getTopologyId() {
+ return topologyId;
+ }
+
+ @Override
+ public DataBroker getDataBroker() {
+ return Preconditions.checkNotNull(dataBroker, "DataBroker not initialized yet");
+ }
+
+ @Override
+ public ListenableFuture<NetconfDeviceCapabilities> connectNode(NodeId nodeId, Node configNode) {
+ return setupConnection(nodeId, configNode);
+ }
+
+ @Override
+ public ListenableFuture<Void> disconnectNode(NodeId nodeId) {
+ if (!activeConnectors.containsKey(nodeId)) {
+ return Futures.immediateFailedFuture(new IllegalStateException("Unable to disconnect device that is not connected"));
+ }
+
+ // retrieve connection, and disconnect it
+ activeConnectors.remove(nodeId).getCommunicator().disconnect();
+ return Futures.immediateFuture(null);
+ }
+
+ @Override
+ public void registerConnectionStatusListener(NodeId node, RemoteDeviceHandler<NetconfSessionPreferences> listener) {
+ activeConnectors.get(node).getMountPointFacade().registerConnectionStatusListener(listener);
+ }
+
+ private ListenableFuture<NetconfDeviceCapabilities> setupConnection(final NodeId nodeId,
+ final Node configNode) {
+ final NetconfNode netconfNode = configNode.getAugmentation(NetconfNode.class);
+
+ final NetconfConnectorDTO deviceCommunicatorDTO = createDeviceCommunicator(nodeId, netconfNode);
+ final NetconfDeviceCommunicator deviceCommunicator = deviceCommunicatorDTO.getCommunicator();
+ final NetconfReconnectingClientConfiguration clientConfig = getClientConfig(deviceCommunicator, netconfNode);
+ final ListenableFuture<NetconfDeviceCapabilities> future = deviceCommunicator.initializeRemoteConnection(clientDispatcher, clientConfig);
+
+ Futures.addCallback(future, new FutureCallback<NetconfDeviceCapabilities>() {
+ @Override
+ public void onSuccess(NetconfDeviceCapabilities result) {
+ LOG.debug("Connector for : " + nodeId.getValue() + " started succesfully");
+ activeConnectors.put(nodeId, deviceCommunicatorDTO);
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ LOG.error("Connector for : " + nodeId.getValue() + " failed");
+ // remove this node from active connectors?
+ }
+ });
+
+ return future;
+ }
+
+ private NetconfConnectorDTO createDeviceCommunicator(final NodeId nodeId,
+ final NetconfNode node) {
+ IpAddress ipAddress = node.getHost().getIpAddress();
+ InetSocketAddress address = new InetSocketAddress(ipAddress.getIpv4Address() != null ?
+ ipAddress.getIpv4Address().getValue() : ipAddress.getIpv6Address().getValue(),
+ node.getPort().getValue());
+ RemoteDeviceId remoteDeviceId = new RemoteDeviceId(nodeId.getValue(), address);
+
+ // we might need to create a new SalFacade to maintain backwards compatibility with special case loopback connection
+ TopologyMountPointFacade mountPointFacade =
+ new TopologyMountPointFacade(remoteDeviceId, domBroker, bindingAwareBroker, node.getDefaultRequestTimeoutMillis());
+ RemoteDeviceHandler<NetconfSessionPreferences> salFacade = mountPointFacade;
+ if (node.getKeepaliveDelay() > 0) {
+ salFacade = new KeepaliveSalFacade(remoteDeviceId, mountPointFacade, keepaliveExecutor.getExecutor(), node.getKeepaliveDelay());
+ }
+
+ NetconfDevice.SchemaResourcesDTO schemaResourcesDTO =
+ new NetconfDevice.SchemaResourcesDTO(schemaSourceRegistry, schemaContextFactory, new NetconfStateSchemas.NetconfStateSchemasResolverImpl());
+
+ NetconfDevice device = new NetconfDevice(schemaResourcesDTO, remoteDeviceId, salFacade,
+ processingExecutor.getExecutor(), node.isReconnectOnChangedSchema());
+
+ return new NetconfConnectorDTO(new NetconfDeviceCommunicator(remoteDeviceId, device), mountPointFacade);
+ }
+
+ public NetconfReconnectingClientConfiguration getClientConfig(final NetconfDeviceCommunicator listener, NetconfNode node) {
+ final InetSocketAddress socketAddress = getSocketAddress(node.getHost(), node.getPort().getValue());
+ final long clientConnectionTimeoutMillis = node.getDefaultRequestTimeoutMillis();
+
+ final ReconnectStrategyFactory sf = new TimedReconnectStrategyFactory(eventExecutor,
+ node.getMaxConnectionAttempts(), node.getBetweenAttemptsTimeoutMillis(), node.getSleepFactor());
+ final ReconnectStrategy strategy = sf.createReconnectStrategy();
+
+ final AuthenticationHandler authHandler;
+ final Credentials credentials = node.getCredentials();
+ if (credentials instanceof org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPassword) {
+ authHandler = new LoginPassword(
+ ((org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPassword) credentials).getUsername(),
+ ((org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPassword) credentials).getPassword());
+ } else {
+ throw new IllegalStateException("Only login/password authentification is supported");
+ }
+
+ return NetconfReconnectingClientConfigurationBuilder.create()
+ .withAddress(socketAddress)
+ .withConnectionTimeoutMillis(clientConnectionTimeoutMillis)
+ .withReconnectStrategy(strategy)
+ .withAuthHandler(authHandler)
+ .withProtocol(node.isTcpOnly() ?
+ NetconfClientConfiguration.NetconfClientProtocol.TCP :
+ NetconfClientConfiguration.NetconfClientProtocol.SSH)
+ .withConnectStrategyFactory(sf)
+ .withSessionListener(listener)
+ .build();
+ }
+
+ @Override
+ public void onSessionInitiated(ProviderSession session) {
+ mountPointService = session.getService(DOMMountPointService.class);
+ }
+
+ @Override
+ public Collection<ProviderFunctionality> getProviderFunctionality() {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public void onSessionInitiated(ProviderContext session) {
+ dataBroker = session.getSALService(DataBroker.class);
+ }
+
+ private static final class NetconfConnectorDTO {
+
+ private final NetconfDeviceCommunicator communicator;
+ private final TopologyMountPointFacade mountPointFacade;
+
+ private NetconfConnectorDTO(final NetconfDeviceCommunicator communicator, final TopologyMountPointFacade mountPointFacade) {
+ this.communicator = communicator;
+ this.mountPointFacade = mountPointFacade;
+ }
+
+ public NetconfDeviceCommunicator getCommunicator() {
+ return communicator;
+ }
+
+ public TopologyMountPointFacade getMountPointFacade() {
+ return mountPointFacade;
+ }
+ }
+
+ private static final class TimedReconnectStrategyFactory implements ReconnectStrategyFactory {
+ private final Long connectionAttempts;
+ private final EventExecutor executor;
+ private final double sleepFactor;
+ private final int minSleep;
+
+ TimedReconnectStrategyFactory(final EventExecutor executor, final Long maxConnectionAttempts, final int minSleep, final BigDecimal sleepFactor) {
+ if (maxConnectionAttempts != null && maxConnectionAttempts > 0) {
+ connectionAttempts = maxConnectionAttempts;
+ } else {
+ connectionAttempts = null;
+ }
+
+ this.sleepFactor = sleepFactor.doubleValue();
+ this.executor = executor;
+ this.minSleep = minSleep;
+ }
+
+ @Override
+ public ReconnectStrategy createReconnectStrategy() {
+ final Long maxSleep = null;
+ final Long deadline = null;
+
+ return new TimedReconnectStrategy(executor, minSleep,
+ minSleep, sleepFactor, maxSleep, connectionAttempts, deadline);
+ }
+ }
+
+ private InetSocketAddress getSocketAddress(final Host host, int port) {
+ if(host.getDomainName() != null) {
+ return new InetSocketAddress(host.getDomainName().getValue(), port);
+ } else {
+ final IpAddress ipAddress = host.getIpAddress();
+ final String ip = ipAddress.getIpv4Address() != null ? ipAddress.getIpv4Address().getValue() : ipAddress.getIpv6Address().getValue();
+ return new InetSocketAddress(ip, port);
+ }
+ }
+}
--- /dev/null
+module netconf-topology {
+
+ yang-version 1;
+ namespace "urn:opendaylight:params:xml:ns:yang:controller:netconf:topology";
+ prefix "nt";
+
+ import config { prefix config; revision-date 2013-04-05; }
+ import threadpool {prefix th;}
+ import netty {prefix netty;}
+ import opendaylight-md-sal-dom {prefix dom;}
+ import opendaylight-md-sal-binding {prefix md-sal-binding; revision-date 2013-10-28;}
+ import odl-netconf-cfg { prefix cfg-net; revision-date 2014-04-08; }
+ import shared-schema-repository { prefix sh; revision-date 2015-07-27; }
+
+ description
+ "Module definition for Netconf topolgy. Netconf topology provides a set of common configuration ";
+
+ revision "2015-07-27" {
+ description
+ "Initial revision";
+ }
+
+ identity netconf-topology {
+ base config:service-type;
+ config:java-class "org.opendaylight.netconf.topology.NetconfTopology";
+ }
+
+ identity netconf-topology-impl {
+ base config:module-type;
+ config:java-name-prefix NetconfTopology;
+ config:provided-service netconf-topology;
+ }
+
+ augment "/config:modules/config:module/config:configuration" {
+ case netconf-topology-impl {
+ when "/config:modules/config:module/config:type = 'netconf-topology-impl'";
+
+ leaf topology-id {
+ mandatory true;
+ type string;
+ }
+
+ container dom-registry {
+ uses config:service-ref {
+ refine type {
+ mandatory true;
+ config:required-identity dom:dom-broker-osgi-registry;
+ }
+ }
+ }
+
+ container binding-registry {
+ uses config:service-ref {
+ refine type {
+ mandatory true;
+ config:required-identity md-sal-binding:binding-broker-osgi-registry;
+ }
+ }
+ }
+
+ container event-executor {
+ uses config:service-ref {
+ refine type {
+ mandatory true;
+ config:required-identity netty:netty-event-executor;
+ }
+ }
+ }
+
+ container processing-executor {
+ uses config:service-ref {
+ refine type {
+ mandatory true;
+ config:required-identity th:threadpool;
+ }
+ }
+
+ description "Makes up for flaws in netty threading design";
+ }
+
+ container client-dispatcher {
+ uses config:service-ref {
+ refine type {
+ mandatory false;
+ config:required-identity cfg-net:netconf-client-dispatcher;
+ }
+ }
+ }
+
+ container keepalive-executor {
+ uses config:service-ref {
+ refine type {
+ mandatory false;
+ config:required-identity th:scheduled-threadpool;
+ }
+ }
+
+ description "Dedicated solely to keepalive execution";
+ }
+
+ container shared-schema-repository {
+ uses config:service-ref {
+ refine type {
+ mandatory true;
+ config:required-identity sh:shared-schema-repository;
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+module shared-schema-repository {
+
+ yang-version 1;
+ namespace "urn:opendaylight:params:xml:ns:yang:controller:netconf:topology:shared:schema:repository";
+ prefix "ssr";
+
+ import config { prefix config; revision-date 2013-04-05; }
+
+ description
+ "Module definition for Shared schema repository.";
+
+ revision "2015-07-27" {
+ description
+ "Initial revision";
+ }
+
+ identity shared-schema-repository {
+ base "config:service-type";
+ config:java-class "org.opendaylight.netconf.topology.SchemaRepositoryProvider";
+ }
+
+ identity shared-schema-repository-impl {
+ base "config:module-type";
+ config:provided-service shared-schema-repository;
+ config:java-name-prefix SchemaRepositoryImpl;
+ }
+
+ augment "/config:modules/config:module/config:configuration" {
+ case shared-schema-repository-impl {
+ when "/config:modules/config:module/config:type = 'shared-schema-repository-impl'";
+
+ container schema-repository-fallbacks {
+ list schema-repository-fallback {
+ leaf repository-url {
+ type string;
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
<module>aaa-authn-odl-plugin</module>
<module>netconf-notifications-impl</module>
<module>netconf-notifications-api</module>
+ <module>netconf-topology</module>
+ <module>netconf-topology-config</module>
<module>sal-netconf-connector</module>
<module>messagebus-netconf</module>
<module>models</module>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>yang-data-impl</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-model-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-model-util</artifactId>
+ </dependency>
<dependency>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>yang-parser-impl</artifactId>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Import-Package>*</Import-Package>
+ </instructions>
+ </configuration>
</plugin>
+
<plugin>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>yang-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>config</id>
+ <goals>
+ <goal>generate-sources</goal>
+ </goals>
+ <configuration>
+ <codeGenerators>
+ <generator>
+ <codeGeneratorClass>org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator</codeGeneratorClass>
+ <outputBaseDir>${jmxGeneratorPath}</outputBaseDir>
+ <additionalConfiguration>
+ <namespaceToPackage1>urn:opendaylight:params:xml:ns:yang:controller==org.opendaylight.controller.config.yang</namespaceToPackage1>
+ </additionalConfiguration>
+ </generator>
+ <generator>
+ <codeGeneratorClass>org.opendaylight.yangtools.maven.sal.api.gen.plugin.CodeGeneratorImpl</codeGeneratorClass>
+ <outputBaseDir>${salGeneratorPath}</outputBaseDir>
+ </generator>
+ </codeGenerators>
+ <inspectDependencies>true</inspectDependencies>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>build-helper-maven-plugin</artifactId>
+ <version>1.8</version>
+ <executions>
+ <execution>
+ <id>add-source</id>
+ <phase>generate-sources</phase>
+ <goals>
+ <goal>add-source</goal>
+ </goals>
+ <configuration>
+ <sources>
+ <source>${project.build.directory}/generated-sources/config</source>;
+ </sources>
+ </configuration>
+ </execution>
+ </executions>
</plugin>
</plugins>
</build>
import com.google.common.base.Optional;
import io.netty.util.concurrent.EventExecutor;
+import java.io.File;
import java.math.BigDecimal;
import java.net.InetSocketAddress;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;
import org.opendaylight.controller.config.api.JmxAttributeValidationException;
+import org.opendaylight.controller.config.threadpool.ScheduledThreadPool;
+import org.opendaylight.controller.config.threadpool.ThreadPool;
import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
import org.opendaylight.controller.sal.core.api.Broker;
import org.opendaylight.netconf.client.NetconfClientDispatcher;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Host;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactory;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaRepository;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceFilter;
+import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry;
+import org.opendaylight.yangtools.yang.model.repo.util.FilesystemSchemaSourceCache;
+import org.opendaylight.yangtools.yang.parser.repo.SharedSchemaRepository;
+import org.opendaylight.yangtools.yang.parser.util.TextToASTTransformer;
import org.osgi.framework.BundleContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
{
private static final Logger LOG = LoggerFactory.getLogger(NetconfConnectorModule.class);
+ private static FilesystemSchemaSourceCache<YangTextSchemaSource> CACHE = null;
+ //when no topology is defined in connector config, we need to add a default schema repository to mantain backwards compatibility
+ private static SharedSchemaRepository DEFAULT_SCHEMA_REPOSITORY = null;
+
+ //keep track of already initialized repositories to avoid adding redundant listeners
+ private static final Set<SchemaRepository> INITIALIZED_SCHEMA_REPOSITORIES = new HashSet<>();
+
private BundleContext bundleContext;
private Optional<NetconfSessionPreferences> userCapabilities;
private SchemaSourceRegistry schemaRegistry;
private SchemaContextFactory schemaContextFactory;
+ private Broker domRegistry;
+ private NetconfClientDispatcher clientDispatcher;
+ private BindingAwareBroker bindingRegistry;
+ private ThreadPool processingExecutor;
+ private ScheduledThreadPool keepaliveExecutor;
+ private SharedSchemaRepository sharedSchemaRepository;
+ private EventExecutor eventExecutor;
+
public NetconfConnectorModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier, final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
super(identifier, dependencyResolver);
}
checkNotNull(getAddress(), addressJmxAttribute);
checkCondition(isHostAddressPresent(getAddress()), "Host address not present in " + getAddress(), addressJmxAttribute);
checkNotNull(getPort(), portJmxAttribute);
- checkNotNull(getDomRegistry(), portJmxAttribute);
- checkNotNull(getDomRegistry(), domRegistryJmxAttribute);
checkNotNull(getConnectionTimeoutMillis(), connectionTimeoutMillisJmxAttribute);
checkCondition(getConnectionTimeoutMillis() > 0, "must be > 0", connectionTimeoutMillisJmxAttribute);
checkNotNull(getBetweenAttemptsTimeoutMillis(), betweenAttemptsTimeoutMillisJmxAttribute);
checkCondition(getBetweenAttemptsTimeoutMillis() > 0, "must be > 0", betweenAttemptsTimeoutMillisJmxAttribute);
- checkNotNull(getClientDispatcher(), clientDispatcherJmxAttribute);
- checkNotNull(getBindingRegistry(), bindingRegistryJmxAttribute);
- checkNotNull(getProcessingExecutor(), processingExecutorJmxAttribute);
+// if (getNetconfTopology() == null) {
+// checkNotNull(getDomRegistry(), domRegistryJmxAttribute);
+// checkNotNull(getClientDispatcher(), clientDispatcherJmxAttribute);
+// checkNotNull(getBindingRegistry(), bindingRegistryJmxAttribute);
+// checkNotNull(getProcessingExecutor(), processingExecutorJmxAttribute);
+// }
// Check username + password in case of ssh
if(getTcpOnly() == false) {
}
userCapabilities = getUserCapabilities();
-
- if(getKeepaliveExecutor() == null) {
- LOG.warn("Keepalive executor missing. Using default instance for now, the configuration needs to be updated");
-
- // Instantiate the default executor, now we know its necessary
- if(DEFAULT_KEEPALIVE_EXECUTOR == null) {
- DEFAULT_KEEPALIVE_EXECUTOR = Executors.newScheduledThreadPool(2, new ThreadFactory() {
- @Override
- public Thread newThread(final Runnable r) {
- final Thread thread = new Thread(r);
- thread.setName("netconf-southound-keepalives-" + thread.getId());
- thread.setDaemon(true);
- return thread;
- }
- });
- }
- }
}
private boolean isHostAddressPresent(final Host address) {
@Override
public java.lang.AutoCloseable createInstance() {
+ initDependenciesFromTopology();
final RemoteDeviceId id = new RemoteDeviceId(getIdentifier(), getSocketAddress());
- final ExecutorService globalProcessingExecutor = getProcessingExecutorDependency().getExecutor();
-
- final Broker domBroker = getDomRegistryDependency();
- final BindingAwareBroker bindingBroker = getBindingRegistryDependency();
+ final ExecutorService globalProcessingExecutor = processingExecutor.getExecutor();
RemoteDeviceHandler<NetconfSessionPreferences> salFacade
- = new NetconfDeviceSalFacade(id, domBroker, bindingBroker, getDefaultRequestTimeoutMillis());
+ = new NetconfDeviceSalFacade(id, domRegistry, bindingRegistry, getDefaultRequestTimeoutMillis());
final Long keepaliveDelay = getKeepaliveDelay();
- if(shouldSendKeepalive()) {
+ if (shouldSendKeepalive()) {
// Keepalive executor is optional for now and a default instance is supported
- final ScheduledExecutorService executor = getKeepaliveExecutor() == null ?
- DEFAULT_KEEPALIVE_EXECUTOR : getKeepaliveExecutorDependency().getExecutor();
+ final ScheduledExecutorService executor = keepaliveExecutor == null ? DEFAULT_KEEPALIVE_EXECUTOR : keepaliveExecutor.getExecutor();
+
salFacade = new KeepaliveSalFacade(id, salFacade, executor, keepaliveDelay);
}
final NetconfDeviceCommunicator listener = userCapabilities.isPresent() ?
new NetconfDeviceCommunicator(id, device, userCapabilities.get()) : new NetconfDeviceCommunicator(id, device);
- if(shouldSendKeepalive()) {
+ if (shouldSendKeepalive()) {
((KeepaliveSalFacade) salFacade).setListener(listener);
}
final NetconfReconnectingClientConfiguration clientConfig = getClientConfig(listener);
- final NetconfClientDispatcher dispatcher = getClientDispatcherDependency();
-
- listener.initializeRemoteConnection(dispatcher, clientConfig);
+ listener.initializeRemoteConnection(clientDispatcher, clientConfig);
return new SalConnectorCloseable(listener, salFacade);
}
+ private void initDependenciesFromTopology() {
+// topology = getNetconfTopologyDependency();
+
+// domRegistry = topology.getDomRegistryDependency();
+// clientDispatcher = topology.getNetconfClientDispatcherDependency();
+// bindingRegistry = topology.getBindingAwareBroker();
+// processingExecutor = topology.getProcessingExecutorDependency();
+// keepaliveExecutor = topology.getKeepaliveExecutorDependency();
+// sharedSchemaRepository = topology.getSharedSchemaRepository().getSharedSchemaRepository();
+// eventExecutor = topology.getEventExecutorDependency();
+
+// initFilesystemSchemaSourceCache(sharedSchemaRepository);
+ domRegistry = getDomRegistryDependency();
+ clientDispatcher = getClientDispatcherDependency();
+ bindingRegistry = getBindingRegistryDependency();
+ processingExecutor = getProcessingExecutorDependency();
+ eventExecutor = getEventExecutorDependency();
+
+ if(getKeepaliveExecutor() == null) {
+ LOG.warn("Keepalive executor missing. Using default instance for now, the configuration needs to be updated");
+
+ // Instantiate the default executor, now we know its necessary
+ if(DEFAULT_KEEPALIVE_EXECUTOR == null) {
+ DEFAULT_KEEPALIVE_EXECUTOR = Executors.newScheduledThreadPool(2, new ThreadFactory() {
+ @Override
+ public Thread newThread(final Runnable r) {
+ final Thread thread = new Thread(r);
+ thread.setName("netconf-southound-keepalives-" + thread.getId());
+ thread.setDaemon(true);
+ return thread;
+ }
+ });
+ }
+ }
+
+ LOG.warn("No topology defined in config, using default-shared-schema-repo");
+ if (DEFAULT_SCHEMA_REPOSITORY == null) {
+ DEFAULT_SCHEMA_REPOSITORY = new SharedSchemaRepository("default shared schema repo");
+ }
+ initFilesystemSchemaSourceCache(DEFAULT_SCHEMA_REPOSITORY);
+ }
+
+ private void initFilesystemSchemaSourceCache(SharedSchemaRepository repository) {
+ LOG.warn("Schema repository used: {}", repository.getIdentifier());
+ if (CACHE == null) {
+ CACHE = new FilesystemSchemaSourceCache<>(repository, YangTextSchemaSource.class, new File("cache/schema"));
+ }
+ if (!INITIALIZED_SCHEMA_REPOSITORIES.contains(repository)) {
+ repository.registerSchemaSourceListener(CACHE);
+ repository.registerSchemaSourceListener(TextToASTTransformer.create(repository, repository));
+ INITIALIZED_SCHEMA_REPOSITORIES.add(repository);
+ }
+ setSchemaRegistry(repository);
+ setSchemaContextFactory(repository.createSchemaContextFactory(SchemaSourceFilter.ALWAYS_ACCEPT));
+ }
+
private boolean shouldSendKeepalive() {
return getKeepaliveDelay() > 0;
}
final InetSocketAddress socketAddress = getSocketAddress();
final long clientConnectionTimeoutMillis = getConnectionTimeoutMillis();
- final ReconnectStrategyFactory sf = new TimedReconnectStrategyFactory(
- getEventExecutorDependency(), getMaxConnectionAttempts(), getBetweenAttemptsTimeoutMillis(), getSleepFactor());
+ final ReconnectStrategyFactory sf = new TimedReconnectStrategyFactory(eventExecutor,
+ getMaxConnectionAttempts(), getBetweenAttemptsTimeoutMillis(), getSleepFactor());
final ReconnectStrategy strategy = sf.createReconnectStrategy();
return NetconfReconnectingClientConfigurationBuilder.create()
*/
package org.opendaylight.controller.config.yang.md.sal.connector.netconf;
-import java.io.File;
import org.opendaylight.controller.config.api.DependencyResolver;
import org.opendaylight.controller.config.api.DynamicMBeanWithInstance;
import org.opendaylight.controller.config.spi.Module;
-import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactory;
-import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceFilter;
-import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
-import org.opendaylight.yangtools.yang.model.repo.util.FilesystemSchemaSourceCache;
-import org.opendaylight.yangtools.yang.parser.repo.SharedSchemaRepository;
-import org.opendaylight.yangtools.yang.parser.util.TextToASTTransformer;
import org.osgi.framework.BundleContext;
/**
public class NetconfConnectorModuleFactory extends
org.opendaylight.controller.config.yang.md.sal.connector.netconf.AbstractNetconfConnectorModuleFactory {
- // TODO this should be injected
- // Netconf devices have separated schema registry + factory from controller
- private final SharedSchemaRepository repository = new SharedSchemaRepository(NAME);
- private final SchemaContextFactory schemaContextFactory
- = repository.createSchemaContextFactory(SchemaSourceFilter.ALWAYS_ACCEPT);
-
- public NetconfConnectorModuleFactory() {
- // Start cache and Text to AST transformer
- final FilesystemSchemaSourceCache<YangTextSchemaSource> cache = new FilesystemSchemaSourceCache<>(repository, YangTextSchemaSource.class, new File("cache/schema"));
- repository.registerSchemaSourceListener(cache);
- repository.registerSchemaSourceListener(TextToASTTransformer.create(repository, repository));
- }
-
@Override
public Module createModule(final String instanceName, final DependencyResolver dependencyResolver,
final DynamicMBeanWithInstance old, final BundleContext bundleContext) throws Exception {
final NetconfConnectorModule module = (NetconfConnectorModule) super.createModule(instanceName, dependencyResolver,
old, bundleContext);
-
- module.setSchemaRegistry(repository);
- module.setSchemaContextFactory(schemaContextFactory);
return module;
}
public Module createModule(final String instanceName, final DependencyResolver dependencyResolver, final BundleContext bundleContext) {
final NetconfConnectorModule module = (NetconfConnectorModule) super.createModule(instanceName, dependencyResolver,
bundleContext);
- module.setSchemaRegistry(repository);
- module.setSchemaContextFactory(schemaContextFactory);
return module;
}
}
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-final class NetconfDeviceDataBroker implements DOMDataBroker {
+public final class NetconfDeviceDataBroker implements DOMDataBroker {
private final RemoteDeviceId id;
private final NetconfBaseOps netconfOps;
private final long requestTimeoutMillis;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
import org.opendaylight.yangtools.yang.model.api.SchemaPath;
-class NetconfDeviceNotificationService implements DOMNotificationService {
+public class NetconfDeviceNotificationService implements DOMNotificationService {
private final Multimap<SchemaPath, DOMNotificationListener> listeners = HashMultimap.create();
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-final class NetconfDeviceSalProvider implements AutoCloseable, Provider, BindingAwareProvider {
+public final class NetconfDeviceSalProvider implements AutoCloseable, Provider, BindingAwareProvider {
private static final Logger logger = LoggerFactory.getLogger(NetconfDeviceSalProvider.class);
topologyDatastoreAdapter = null;
}
- static final class MountInstance implements AutoCloseable {
+ public static final class MountInstance implements AutoCloseable {
private DOMMountPointService mountService;
private final RemoteDeviceId id;
}
}
- synchronized void onTopologyDeviceConnected(final SchemaContext initialCtx,
+ public synchronized void onTopologyDeviceConnected(final SchemaContext initialCtx,
final DOMDataBroker broker, final DOMRpcService rpc,
final NetconfDeviceNotificationService notificationService) {
}
- synchronized void onTopologyDeviceDisconnected() {
+ public synchronized void onTopologyDeviceDisconnected() {
if(topologyRegistration == null) {
logger.trace("{}: Not removing TOPOLOGY mountpoint from MD-SAL, mountpoint was not registered yet", id);
return;
}
@Override
- synchronized public void close() throws Exception {
+ public synchronized void close() throws Exception {
onDeviceDisconnected();
onTopologyDeviceDisconnected();
mountService = null;
description
"Service definition for Binding Aware MD-SAL.";
+ revision "2015-08-03" {
+ description
+ "Add netconf topology from which dependencies are inherited, change
+ existing dependencies to non-mandatory to preserve backwards compatibility";
+ }
+
revision "2013-10-28" {
description
"Initial revision";
container dom-registry {
uses config:service-ref {
refine type {
- mandatory true;
+ mandatory false;
config:required-identity dom:dom-broker-osgi-registry;
}
}
container binding-registry {
uses config:service-ref {
refine type {
- mandatory true;
+ mandatory false;
config:required-identity md-sal-binding:binding-broker-osgi-registry;
}
}
container event-executor {
uses config:service-ref {
refine type {
- mandatory true;
+ mandatory false;
config:required-identity netty:netty-event-executor;
}
}
container processing-executor {
uses config:service-ref {
refine type {
- mandatory true;
+ mandatory false;
config:required-identity th:threadpool;
}
}