<groupId>org.opendaylight.controller.thirdparty</groupId>
<artifactId>ganymed</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.apache.sshd</groupId>
+ <artifactId>sshd-core</artifactId>
+ </dependency>
<dependency>
<groupId>org.openexi</groupId>
<artifactId>nagasena</artifactId>
<feature version='${project.version}'>odl-netconf-util</feature>
<bundle>mvn:org.opendaylight.controller/netconf-netty-util/${project.version}</bundle>
<bundle>mvn:org.opendaylight.controller.thirdparty/ganymed/${ganymed.version}</bundle>
+ <bundle>mvn:org.apache.sshd/sshd-core/${sshd-core.version}</bundle>
<bundle>mvn:org.openexi/nagasena/${exi.nagasena.version}</bundle>
<bundle>mvn:io.netty/netty-codec/${netty.version}</bundle>
<bundle>mvn:io.netty/netty-handler/${netty.version}</bundle>
<bundle>mvn:org.opendaylight.controller/netconf-monitoring/${project.version}</bundle>
</feature>
-</features>
\ No newline at end of file
+</features>
--- /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">
+ <modelVersion>4.0.0</modelVersion>
+
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>opendaylight-configfile-archetype</artifactId>
+ <version>1.1-SNAPSHOT</version>
+ <packaging>maven-archetype</packaging>
+
+
+ <build>
+ <extensions>
+ <extension>
+ <groupId>org.apache.maven.archetype</groupId>
+ <artifactId>archetype-packaging</artifactId>
+ <version>2.2</version>
+ </extension>
+ </extensions>
+
+ <pluginManagement>
+ <plugins>
+ <plugin>
+ <artifactId>maven-archetype-plugin</artifactId>
+ <version>2.2</version>
+ </plugin>
+ </plugins>
+ </pluginManagement>
+ </build>
+
+ <description>Configuration files for md-sal</description>
+
+ <scm>
+ <connection>scm:git:ssh://git.opendaylight.org:29418/controller.git/md-sal-config</connection>
+ <developerConnection>scm:git:ssh://git.opendaylight.org:29418/controller.git/md-sal-config</developerConnection>
+ <url>https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL/md-sal-config</url>
+ </scm>
+
+ <distributionManagement>
+ <repository>
+ <id>opendaylight-release</id>
+ <url>http://nexus.opendaylight.org/content/repositories/opendaylight.release/</url>
+ </repository>
+ <snapshotRepository>
+ <id>opendaylight-snapshot</id>
+ <url>http://nexus.opendaylight.org/content/repositories/opendaylight.snapshot/</url>
+ </snapshotRepository>
+ <site>
+ <id>website</id>
+ <url>dav:http://nexus.opendaylight.org/content/sites/site/sal-parent</url>
+ </site>
+ </distributionManagement>
+</project>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<archetype-descriptor xsi:schemaLocation="http://maven.apache.org/plugins/maven-archetype-plugin/archetype-descriptor/1.0.0 http://maven.apache.org/xsd/archetype-descriptor-1.0.0.xsd" name="md-sal-config"
+ xmlns="http://maven.apache.org/plugins/maven-archetype-plugin/archetype-descriptor/1.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <requiredProperties>
+ <requiredProperty key="repoName"/>
+ </requiredProperties>
+ <fileSets>
+ <fileSet filtered="true" encoding="UTF-8">
+ <directory>src/main/resources</directory>
+ <includes>
+ <include>**/*.xml</include>
+ </includes>
+ </fileSet>
+ </fileSets>
+</archetype-descriptor>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (c) 2014 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
+--><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">
+
+ <modelVersion>4.0.0</modelVersion>
+ <!--
+ Necessary TODO: Hookup your parent pom here, else you will not get necessary versions,
+ maven repos etc. If you run this archetype in a subdirectory of your project, it
+ will pick the pom.xml from the parent directory as the parent pom, which may or may
+ not be correct.
+ -->
+ <artifactId>${artifactId}</artifactId>
+ <groupId>${groupId}</groupId>
+ <description>Configuration files for md-sal</description>
+ <!-- Optional TODO: Uncomment version if you are not using a parent pom.xml
+ <version>${version}</version>
+ -->
+ <packaging>jar</packaging>
+ <properties>
+ <!-- Optional TODO: Rename your configfile to taste -->
+ <configfile>80-configfile.xml</configfile>
+ </properties>
+ <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/${configfile}</file>
+ <type>xml</type>
+ <classifier>config</classifier>
+ </artifact>
+ </artifacts>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ <scm>
+ <connection>scm:git:ssh://git.opendaylight.org:29418/${repoName}.git</connection>
+ <developerConnection>scm:git:ssh://git.opendaylight.org:29418/${repoName}.git</developerConnection>
+ <tag>HEAD</tag>
+ <url>https://git.opendaylight.org/gerrit/gitweb?p=${repoName}.git;a=summary</url>
+ </scm>
+</project>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (c) 2013 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>
+ <required-capabilities>
+ <!-- Necessary TODO put your required capabilities here
+
+ Examples:
+ <capability>urn:opendaylight:params:xml:ns:yang:controller:netty?module=netty&revision=2013-11-19</capability>
+ <capability>urn:opendaylight:params:xml:ns:yang:controller:netty:eventexecutor?module=netty-event-executor&revision=2013-11-12</capability>
+ <capability>urn:opendaylight:params:xml:ns:yang:controller:netty:threadgroup?module=threadgroup&revision=2013-11-07</capability>
+ <capability>urn:opendaylight:params:xml:ns:yang:controller:netty:timer?module=netty-timer&revision=2013-11-19</capability>
+ -->
+ </required-capabilities>
+ <configuration>
+
+ <data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+ <!-- Optional TODO: Add your modules definitions here
+ Examples:
+ <module>
+ <type xmlns:netty="urn:opendaylight:params:xml:ns:yang:controller:netty:threadgroup">netty:netty-threadgroup-fixed</type>
+ <name>global-boss-group</name>
+ </module>
+ <module>
+ <type xmlns:netty="urn:opendaylight:params:xml:ns:yang:controller:netty:threadgroup">netty:netty-threadgroup-fixed</type>
+ <name>global-worker-group</name>
+ </module>
+ <module>
+ <type xmlns:netty="urn:opendaylight:params:xml:ns:yang:controller:netty:timer">netty:netty-hashed-wheel-timer</type>
+ <name>global-timer</name>
+ </module>
+ <module>
+ <type xmlns:netty="urn:opendaylight:params:xml:ns:yang:controller:netty:eventexecutor">netty:netty-global-event-executor</type>
+ <name>singleton</name>
+ </module>
+ -->
+ </modules>
+
+ <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+ <!-- Optional TODO: Put your service instance definitions here
+ Examples:
+ <service>
+ <type xmlns:netty="urn:opendaylight:params:xml:ns:yang:controller:netty">netty:netty-threadgroup</type>
+ <instance>
+ <name>global-boss-group</name>
+ <provider>/modules/module[type='netty-threadgroup-fixed'][name='global-boss-group']</provider>
+ </instance>
+ <instance>
+ <name>global-worker-group</name>
+ <provider>/modules/module[type='netty-threadgroup-fixed'][name='global-worker-group']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:netty="urn:opendaylight:params:xml:ns:yang:controller:netty">netty:netty-event-executor</type>
+ <instance>
+ <name>global-event-executor</name>
+ <provider>/modules/module[type='netty-global-event-executor'][name='singleton']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:netty="urn:opendaylight:params:xml:ns:yang:controller:netty">netty:netty-timer</type>
+ <instance>
+ <name>global-timer</name>
+ <provider>/modules/module[type='netty-hashed-wheel-timer'][name='global-timer']</provider>
+ </instance>
+ </service>
+ -->
+ </services>
+ </data>
+
+ </configuration>
+</snapshot>
--- /dev/null
+#Mon Aug 25 05:45:18 CDT 2014
+package=it.pkg
+version=0.1-SNAPSHOT
+groupId=archetype.it
+artifactId=basic
+repoName=foo
--- /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">
+ <modelVersion>4.0.0</modelVersion>
+
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>opendaylight-karaf-distro-archetype</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ <packaging>maven-archetype</packaging>
+
+ <name>distribution-karaf-archetype</name>
+
+ <build>
+ <extensions>
+ <extension>
+ <groupId>org.apache.maven.archetype</groupId>
+ <artifactId>archetype-packaging</artifactId>
+ <version>2.2</version>
+ </extension>
+ </extensions>
+
+ <pluginManagement>
+ <plugins>
+ <plugin>
+ <artifactId>maven-archetype-plugin</artifactId>
+ <version>2.2</version>
+ </plugin>
+ </plugins>
+ </pluginManagement>
+ </build>
+
+ <url>https://wiki.opendaylight.org/view/CrossProject:Integration_Group/distribution-karaf</url>
+
+ <scm>
+ <connection>scm:git:ssh://git.opendaylight.org:29418/controller.git</connection>
+ <developerConnection>scm:git:ssh://git.opendaylight.org:29418/controller.git</developerConnection>
+ <url>https://wiki.opendaylight.org/view/OpenDaylight_Controller:Main</url>
+ </scm>
+</project>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<archetype-descriptor xsi:schemaLocation="http://maven.apache.org/plugins/maven-archetype-plugin/archetype-descriptor/1.0.0 http://maven.apache.org/xsd/archetype-descriptor-1.0.0.xsd"
+ xmlns="http://maven.apache.org/plugins/maven-archetype-plugin/archetype-descriptor/1.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <requiredProperties>
+ <requiredProperty key="repoName"/>
+ </requiredProperties>
+</archetype-descriptor>
--- /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">
+ <modelVersion>4.0.0</modelVersion>
+ <!--
+ Necessary TODO: Hookup your parent pom here, else you will not get necessary versions,
+ maven repos etc. If you run this archetype in a subdirectory of your project, it
+ will pick the pom.xml from the parent directory as the parent pom, which may or may
+ not be correct.
+ -->
+ <artifactId>${artifactId}</artifactId>
+ <groupId>${groupId}</groupId>
+ <!-- Optional TODO: Uncomment version if you are not using a parent pom.xml
+ <version>${version}</version>
+ -->
+ <packaging>pom</packaging>
+ <prerequisites>
+ <maven>3.0</maven>
+ </prerequisites>
+ <properties>
+ <!-- Optional TODO: Move these properties to your parent pom and possibly
+ DependencyManagement section of your parent pom -->
+ <branding.version>1.0.0-SNAPSHOT</branding.version>
+ <karaf.resources.version>1.4.2-SNAPSHOT</karaf.resources.version>
+ <karaf.version>3.0.1</karaf.version>
+ </properties>
+
+ <dependencies>
+ <!-- Basic Karaf dependencies -->
+ <dependency>
+ <groupId>org.apache.karaf.features</groupId>
+ <artifactId>framework</artifactId>
+ <version>${karaf.version}</version>
+ <type>kar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.karaf.features</groupId>
+ <artifactId>standard</artifactId>
+ <version>${karaf.version}</version>
+ <classifier>features</classifier>
+ <type>xml</type>
+ <scope>runtime</scope>
+ </dependency>
+
+ <!-- ODL Branding -->
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>karaf.branding</artifactId>
+ <version>${branding.version}</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <!-- ODL Resources needed for karaf -->
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>opendaylight-karaf-resources</artifactId>
+ <version>${karaf.resources.version}</version>
+ </dependency>
+
+ <!-- Project local feautures -->
+ <!--
+ Necessary TODO put your features here.
+
+ Note: they will need to be <type>xml</xml>
+ and <classifier>features</classifier>.
+
+ Note: they must be <scope>runtime</scope>
+
+ Note: usually you would only need to depend
+ on your own feature file here for your local distro,
+ and possible the features-mdsal for odl-restconf
+ (although, strange situations do exist :) )
+
+ Example:
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>features-mdsal</artifactId>
+ <classifier>features</classifier>
+ <type>xml</type>
+ <scope>runtime</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.openflowplugin</groupId>
+ <artifactId>features-openflowplugin</artifactId>
+ <version>0.0.3-SNAPSHOT</version>
+ <classifier>features</classifier>
+ <type>xml</type>
+ <scope>runtime</scope>
+ </dependency>
+ -->
+ </dependencies>
+
+ <build>
+ <pluginManagement>
+ <plugins>
+ <plugin>
+ <groupId>org.eclipse.m2e</groupId>
+ <artifactId>lifecycle-mapping</artifactId>
+ <version>1.0.0</version>
+ <configuration>
+ <lifecycleMappingMetadata>
+ <pluginExecutions>
+ <pluginExecution>
+ <pluginExecutionFilter>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <versionRange>[0,)</versionRange>
+ <goals>
+ <goal>cleanVersions</goal>
+ </goals>
+ </pluginExecutionFilter>
+ <action>
+ <ignore></ignore>
+ </action>
+ </pluginExecution>
+ <pluginExecution>
+ <pluginExecutionFilter>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <versionRange>[0,)</versionRange>
+ <goals>
+ <goal>copy</goal>
+ <goal>unpack</goal>
+ </goals>
+ </pluginExecutionFilter>
+ <action>
+ <ignore></ignore>
+ </action>
+ </pluginExecution>
+ <pluginExecution>
+ <pluginExecutionFilter>
+ <groupId>org.apache.karaf.tooling</groupId>
+ <artifactId>karaf-maven-plugin</artifactId>
+ <versionRange>[0,)</versionRange>
+ <goals>
+ <goal>commands-generate-help</goal>
+ </goals>
+ </pluginExecutionFilter>
+ <action>
+ <ignore></ignore>
+ </action>
+ </pluginExecution>
+ <pluginExecution>
+ <pluginExecutionFilter>
+ <groupId>org.fusesource.scalate</groupId>
+ <artifactId>maven-scalate-plugin</artifactId>
+ <versionRange>[0,)</versionRange>
+ <goals>
+ <goal>sitegen</goal>
+ </goals>
+ </pluginExecutionFilter>
+ <action>
+ <ignore></ignore>
+ </action>
+ </pluginExecution>
+ <pluginExecution>
+ <pluginExecutionFilter>
+ <groupId>org.apache.servicemix.tooling</groupId>
+ <artifactId>depends-maven-plugin</artifactId>
+ <versionRange>[0,)</versionRange>
+ <goals>
+ <goal>generate-depends-file</goal>
+ </goals>
+ </pluginExecutionFilter>
+ <action>
+ <ignore></ignore>
+ </action>
+ </pluginExecution>
+ </pluginExecutions>
+ </lifecycleMappingMetadata>
+ </configuration>
+ </plugin>
+ </plugins>
+ </pluginManagement>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.karaf.tooling</groupId>
+ <artifactId>karaf-maven-plugin</artifactId>
+ <version>${karaf.version}</version>
+ <extensions>true</extensions>
+ <configuration>
+ <bootFeatures>
+ <feature>standard</feature>
+ <!--
+ Optional TODO: Add entries here for the features you want in your local distro
+ Note: odl-restconf is a separate feature from odl-mdsal-broker. If you want
+ restconf, you need to list it here explicitely.
+ Examples:
+ <feature>odl-openflowplugin-flow-services</feature>
+ <feature>odl-restconf</feature>
+ -->
+ <!-- Final TODO: Remove TODO Comments ;) -->
+ </bootFeatures>
+ </configuration>
+ <executions>
+ <execution>
+ <id>process-resources</id>
+ <goals>
+ <goal>install-kars</goal>
+ </goals>
+ <phase>process-resources</phase>
+ </execution>
+ <execution>
+ <id>package</id>
+ <goals>
+ <goal>instance-create-archive</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <version>2.6</version>
+ <executions>
+ <execution>
+ <id>copy</id>
+ <goals>
+ <goal>copy</goal>
+ </goals>
+ <phase>generate-resources</phase>
+ <configuration>
+ <artifactItems>
+ <artifactItem>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>karaf.branding</artifactId>
+ <version>${karaf.branding.version}</version>
+ <outputDirectory>target/assembly/lib</outputDirectory>
+ <destFileName>karaf.branding-${branding.version}.jar</destFileName>
+ </artifactItem>
+ </artifactItems>
+ </configuration>
+ </execution>
+ <execution>
+ <id>unpack-karaf-resources</id>
+ <goals>
+ <goal>unpack-dependencies</goal>
+ </goals>
+ <phase>prepare-package</phase>
+ <configuration>
+ <outputDirectory>${project.build.directory}/assembly</outputDirectory>
+ <groupId>org.opendaylight.controller</groupId>
+ <includeArtifactIds>opendaylight-karaf-resources</includeArtifactIds>
+ <excludes>META-INF\/**</excludes>
+ <excludeTransitive>true</excludeTransitive>
+ <ignorePermissions>false</ignorePermissions>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-antrun-plugin</artifactId>
+ <executions>
+ <execution>
+ <phase>prepare-package</phase>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ <configuration>
+ <tasks>
+ <chmod perm="755">
+ <fileset dir="${project.build.directory}/assembly/bin">
+ <include name="karaf" />
+ <include name="instance" />
+ <include name="start"/>
+ <include name="stop"/>
+ <include name="status"/>
+ <include name="client"/>
+ <include name="shell"/>
+ </fileset>
+ </chmod>
+ </tasks>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ <scm>
+ <connection>scm:git:ssh://git.opendaylight.org:29418/${repoName}.git</connection>
+ <developerConnection>scm:git:ssh://git.opendaylight.org:29418/${repoName}.git</developerConnection>
+ <tag>HEAD</tag>
+ <url>https://git.opendaylight.org/gerrit/gitweb?p=${repoName}.git;a=summary</url>
+ </scm>
+</project>
--- /dev/null
+#Thu Aug 21 14:44:29 CDT 2014
+package=it.pkg
+version=0.1-SNAPSHOT
+groupId=archetype.it
+artifactId=basic
+repoName=foo
--- /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">
+ <modelVersion>4.0.0</modelVersion>
+
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>opendaylight-karaf-features-archetype</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ <packaging>maven-archetype</packaging>
+
+ <name>opendaylight-karaf-features-archetype</name>
+
+ <build>
+ <extensions>
+ <extension>
+ <groupId>org.apache.maven.archetype</groupId>
+ <artifactId>archetype-packaging</artifactId>
+ <version>2.2</version>
+ </extension>
+ </extensions>
+
+ <pluginManagement>
+ <plugins>
+ <plugin>
+ <artifactId>maven-archetype-plugin</artifactId>
+ <version>2.2</version>
+ </plugin>
+ </plugins>
+ </pluginManagement>
+ </build>
+
+ <scm>
+ <connection>scm:git:ssh://git.opendaylight.org:29418/controller.git/</connection>
+ <developerConnection>scm:git:ssh://git.opendaylight.org:29418/controller.git/</developerConnection>
+ <url>https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=summary</url>
+ </scm>
+</project>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<archetype-descriptor xsi:schemaLocation="http://maven.apache.org/plugins/maven-archetype-plugin/archetype-descriptor/1.0.0 http://maven.apache.org/xsd/archetype-descriptor-1.0.0.xsd" name="features-integration"
+ xmlns="http://maven.apache.org/plugins/maven-archetype-plugin/archetype-descriptor/1.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <requiredProperties>
+ <requiredProperty key="repoName"/>
+ </requiredProperties>
+ <fileSets>
+ <fileSet filtered="true" encoding="UTF-8">
+ <directory>src/main/resources</directory>
+ <include>**/*.xml</include>
+ </fileSet>
+ </fileSets>
+</archetype-descriptor>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Necessary TODO: Put your copyright here.
+
+ 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
+-->
+<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">
+ <modelVersion>4.0.0</modelVersion>
+ <!--
+ Necessary TODO: Hookup your parent pom here, else you will not get necessary versions,
+ maven repos etc. If you run this archetype in a subdirectory of your project, it
+ will pick the pom.xml from the parent directory as the parent pom, which may or may
+ not be correct.
+ -->
+ <artifactId>features-${repoName}</artifactId>
+ <groupId>${groupId}</groupId>
+ <!-- Optional TODO: Uncomment version if you are not using a parent pom.xml
+ <version>${version}</version>
+ -->
+ <packaging>jar</packaging>
+ <properties>
+ <features.file>features.xml</features.file>
+ <!-- Optional TODO: Move these properties to your parent pom and possibly
+ DependencyManagement section of your parent pom -->
+ <branding.version>1.0.0-SNAPSHOT</branding.version>
+ <karaf.resources.version>1.4.2-SNAPSHOT</karaf.resources.version>
+ <karaf.version>3.0.1</karaf.version>
+ <feature.test.version>0.6.2-SNAPSHOT</feature.test.version>
+ <karaf.empty.version>1.4.2-SNAPSHOT</karaf.empty.version>
+ <surefire.version>2.16</surefire.version>
+ </properties>
+ <dependencies>
+ <!--
+ Necessary TODO: Put dependencies on any feature repos
+ you use in your features.xml file.
+
+ Note: they will need to be <type>xml</xml>
+ and <classifier>features</classifier>.
+ One other thing to watch for is to make sure they are
+ <scope>compile</compile>, which they should be by default,
+ but be cautious lest they be at a different scope in a parent pom.
+
+ Examples:
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>features-yangtools</artifactId>
+ <version>0.6.2-SNAPSHOT</version>
+ <classifier>features</classifier>
+ <type>xml</type>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>features-mdsal</artifactId>
+ <version>1.1-SNAPSHOT</version>
+ <classifier>features</classifier>
+ <type>xml</type>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.openflowplugin</groupId>
+ <artifactId>features-openflowplugin</artifactId>
+ <version>0.0.3-SNAPSHOT</version>
+ <classifier>features</classifier>
+ <type>xml</type>
+ </dependency>
+ -->
+
+ <!--
+ Necessary TODO: Put dependencies for bundles directly referenced
+ in your features.xml file. For every <bundle> reference in your
+ features.xml file, you need a corresponding dependency here.
+
+ Examples:
+ <dependency>
+ <groupId>${groupId}</groupId>
+ <artifactId>${repoName}-provider</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${groupId}</groupId>
+ <artifactId>${repoName}-model</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ -->
+
+ <!--
+ Necessary TODO: Put dependencies for configfiles directly referenced
+ in your features.xml file. For every <configfile> reference in your
+ features.xml file, you need a corresponding dependency here.
+
+ Example (presuming here version is coming from the parent pom):
+ <dependency>
+ <groupId>${groupId}</groupId>
+ <artifactId>${repoName}-config</artifactId>
+ <version>${project.version}</version>
+ <type>xml</type>
+ <classifier>config</classifier>
+ </dependency>
+ -->
+
+ <!--
+ Optional TODO: Remove TODO comments.
+ -->
+ <!-- test to validate features.xml -->
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>features-test</artifactId>
+ <version>${feature.test.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <!-- dependency for opendaylight-karaf-empty for use by testing -->
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>opendaylight-karaf-empty</artifactId>
+ <version>${karaf.empty.version}</version>
+ <type>zip</type>
+ </dependency>
+ <!-- Uncomment this if you get an error : java.lang.NoSuchMethodError: org.slf4j.helpers.MessageFormatter.format(Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;)Lorg/slf4j/helpers/FormattingTuple;
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-simple</artifactId>
+ <version>1.7.2</version>
+ </dependency>
+ -->
+
+ </dependencies>
+ <build>
+ <resources>
+ <resource>
+ <directory>src/main/resources</directory>
+ <filtering>true</filtering>
+ </resource>
+ </resources>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-resources-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>filter</id>
+ <phase>generate-resources</phase>
+ <goals>
+ <goal>resources</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>build-helper-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>attach-artifacts</id>
+ <phase>package</phase>
+ <goals>
+ <goal>attach-artifact</goal>
+ </goals>
+ <configuration>
+ <artifacts>
+ <artifact>
+ <file>${project.build.directory}/classes/${features.file}</file>
+ <type>xml</type>
+ <classifier>features</classifier>
+ </artifact>
+ </artifacts>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <version>${surefire.version}</version>
+ <configuration>
+ <systemPropertyVariables>
+ <karaf.distro.groupId>org.opendaylight.controller</karaf.distro.groupId>
+ <karaf.distro.artifactId>opendaylight-karaf-empty</karaf.distro.artifactId>
+ <karaf.distro.version>${karaf.empty.version}</karaf.distro.version>
+ </systemPropertyVariables>
+ <dependenciesToScan>
+ <dependency>org.opendaylight.yangtools:features-test</dependency>
+ </dependenciesToScan>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ <scm>
+ <connection>scm:git:ssh://git.opendaylight.org:29418/${repoName}.git</connection>
+ <developerConnection>scm:git:ssh://git.opendaylight.org:29418/${repoName}.git</developerConnection>
+ <tag>HEAD</tag>
+ <url>https://git.opendaylight.org/gerrit/gitweb?p=${repoName}.git;a=summary</url>
+ </scm>
+</project>
--- /dev/null
+#set( $symbol_pound = '#' )
+#set( $symbol_dollar = '$' )
+#set( $symbol_escape = '\' )
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ Necessary TODO: Put your copyright statement here
+
+ 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
+-->
+<features name="odl-${repoName}-${symbol_dollar}{project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.2.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://karaf.apache.org/xmlns/features/v1.2.0 http://karaf.apache.org/xmlns/features/v1.2.0">
+ <!--
+ Necessary TODO: Please read the features guidelines:
+ https://wiki.opendaylight.org/view/Runtime:Karaf_Features_Guidelines#Feature_Best_Practices
+ -->
+ <!--
+ Necessary TODO: Add repo entries for the repositories of features you refer to
+ in this feature file but do not define here.
+ Examples:
+ <repository>mvn:org.opendaylight.yangtools/features-yangtools/0.6.2-SNAPSHOT/xml/features</repository>
+ <repository>mvn:org.opendaylight.controller/features-mdsal/1.1-SNAPSHOT/xml/features</repository>
+ <repository>mvn:org.opendaylight.openflowplugin/features-openflowplugin/0.0.3-SNAPSHOT/xml/features</repository>
+ -->
+ <feature name='odl-${repoName}-all' version='${symbol_dollar}{project.version}' description='OpenDaylight :: ${repoName} :: All'>
+ <!--
+ Necessary TODO:
+ List all of the user consumable features you define in this feature file here.
+ Generally you would *not* list individual bundles here, but only features defined in *this* file.
+ It is useful to list them in the same order they occur in the file.
+
+ Examples:
+ <feature version='${symbol_dollar}{project.version}'>odl-${repoName}-provider</feature>
+ <feature version='${symbol_dollar}{project.version}'>odl-${repoName}-model</feature>
+ -->
+ </feature>
+ <!--
+ Necessary TODO: Define your features. It is useful to list then in order of dependency. So if A depends on B, list A first.
+ When naming your features please be mindful of the guidelines:
+ https://wiki.opendaylight.org/view/Runtime:Karaf_Features_Guidelines
+ Particularly:
+ a) Prefixing names with 'odl-': https://wiki.opendaylight.org/view/Runtime:Karaf_Features_Guidelines#Feature_Naming
+ b) Descriptions: https://wiki.opendaylight.org/view/Runtime:Karaf_Features_Guidelines#Description
+ c) Avoid start-levels: https://wiki.opendaylight.org/view/Runtime:Karaf_Features_Guidelines#Avoid_start-levels
+
+ It's also nice to list inside a feature, first the features it needs, then the bundles it needs, then the configfiles.
+ Examples:
+
+ * Basic MD-SAL Provider
+ <feature name='odl-${repoName}-provider' version='${symbol_dollar}{project.version}' description='OpenDaylight :: ${repoName} :: Provider '>
+ <feature version='1.1-SNAPSHOT'>odl-mdsal-broker</feature>
+ <feature version='${symbol_dollar}{project.version}'>odl-${repoName}-model</feature>
+ <bundle>mvn:${groupId}/${repoName}-provider/${symbol_dollar}{project.version}</bundle>
+ ... whatever other bundles you need
+ </feature>
+
+ * Basic MD-SAL Model feature
+ <feature name='odl-${repoName}-model' version='${symbol_dollar}{project.version}' description='OpenDaylight :: ${repoName} :: Model'>
+ <feature version='0.6.2-SNAPSHOT'>odl-yangtools-binding</feature>
+ <feature version='0.6.2-SNAPSHOT'>odl-yangtools-models</feature>
+ <bundle>mvn:${groupId}/${repoName}-model/${symbol_dollar}{project.version}</bundle>
+ ... whatever other bundles you need
+ </feature>
+
+ * Config Subsystem example - the config file is your config subsystem configuration
+ <feature name='odl-${repoName}-provider' version='${symbol_dollar}{project.version}' description='OpenDaylight :: ${repoName} :: Provider'>
+ <feature version='1.1-SNAPSHOT'>odl-mdsal-broker</feature>
+ <bundle>mvn:${groupId}/${repoName}-provider/${symbol_dollar}{project.version}</bundle>
+ <configfile finalname="etc/opendaylight/karaf/80-${repoName}.xml">mvn:${groupId}/${repoName}-config/${symbol_dollar}{project.version}/xml/config</configfile>
+ ... whatever other bundles you need
+ </feature>
+
+ * Basic MD-SAL Provider that uses openflowplugin-flow-services (which brings along odl-mdsal-broker)
+ <feature name='odl-${repoName}-provider' version='${symbol_dollar}{project.version}' description='OpenDaylight :: ${repoName} :: Provider'>
+ <feature version='0.0.3-SNAPSHOT'>odl-openflowplugin-flow-services</feature>
+ <bundle>mvn:${groupId}/${repoName}-provider/${symbol_dollar}{project.version}</bundle>
+ ... whatever other bundles you need
+ </feature>
+
+ -->
+ <!-- Optional TODO: Remove TODO Comments -->
+
+</features>
--- /dev/null
+#Thu Aug 21 23:46:25 CDT 2014
+package=it.pkg
+version=0.1-SNAPSHOT
+groupId=archetype.it
+artifactId=basic
+repoName=foo
</prerequisites>
<parent>
<groupId>org.opendaylight.controller</groupId>
- <artifactId>commons.parent</artifactId>
- <version>1.0.1-SNAPSHOT</version>
- <relativePath>../commons/parent</relativePath>
+ <artifactId>commons.opendaylight</artifactId>
+ <version>1.4.2-SNAPSHOT</version>
+ <relativePath>../commons/opendaylight</relativePath>
</parent>
<scm>
<connection>scm:git:ssh://git.opendaylight.org:29418/controller.git</connection>
</distributionManagement>
<modules>
<module>odl-model-project</module>
+ <module>opendaylight-configfile-archetype</module>
+ <module>opendaylight-karaf-distro-archetype</module>
+ <module>opendaylight-karaf-features</module>
</modules>
</project>
<yang-ext.version>2013.09.07.4-SNAPSHOT</yang-ext.version>
<yang-jmx-generator.version>1.0.0-SNAPSHOT</yang-jmx-generator.version>
<yangtools.version>0.6.2-SNAPSHOT</yangtools.version>
+ <sshd-core.version>0.12.0</sshd-core.version>
</properties>
<dependencyManagement>
<artifactId>netconf-netty-util</artifactId>
<version>${netconf.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.apache.sshd</groupId>
+ <artifactId>sshd-core</artifactId>
+ <version>${sshd-core.version}</version>
+ </dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<groupId>org.opendaylight.controller.thirdparty</groupId>
<artifactId>ganymed</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.apache.sshd</groupId>
+ <artifactId>sshd-core</artifactId>
+ </dependency>
<dependency>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>binding-generator-api</artifactId>
mavenBundle(CONTROLLER, "netconf-client").versionAsInProject(), //
mavenBundle(CONTROLLER, "netconf-util").versionAsInProject(), //
mavenBundle(CONTROLLER, "netconf-netty-util").versionAsInProject(), //
+ mavenBundle("org.apache.sshd", "sshd-core").versionAsInProject(), //
mavenBundle("org.openexi", "nagasena").versionAsInProject(), //
mavenBundle("org.openexi", "nagasena-rta").versionAsInProject(), //
mavenBundle(CONTROLLER + ".thirdparty", "ganymed").versionAsInProject(), //
import java.io.IOException;
import org.opendaylight.controller.netconf.nettyutil.AbstractChannelInitializer;
import org.opendaylight.controller.netconf.nettyutil.handler.ssh.authentication.AuthenticationHandler;
-import org.opendaylight.controller.netconf.nettyutil.handler.ssh.client.SshHandler;
+import org.opendaylight.controller.netconf.nettyutil.handler.ssh.client.AsyncSshHandler;
import org.opendaylight.protocol.framework.SessionListenerFactory;
final class SshClientChannelInitializer extends AbstractChannelInitializer<NetconfClientSession> {
@Override
public void initialize(final Channel ch, final Promise<NetconfClientSession> promise) {
try {
- ch.pipeline().addFirst(SshHandler.createForNetconfSubsystem(authenticationHandler));
+ ch.pipeline().addFirst(AsyncSshHandler.createForNetconfSubsystem(authenticationHandler));
super.initialize(ch,promise);
} catch (final IOException e) {
throw new RuntimeException(e);
package org.opendaylight.controller.netconf.it;
import static java.util.Arrays.asList;
-import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
-import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
-import ch.ethz.ssh2.Connection;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
import org.opendaylight.controller.config.manager.impl.factoriesresolver.HardcodedModuleFactoriesResolver;
import org.opendaylight.controller.config.spi.ModuleFactory;
import org.opendaylight.controller.netconf.api.NetconfMessage;
import org.opendaylight.controller.netconf.impl.NetconfServerDispatcher;
import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceFactoryListenerImpl;
import org.opendaylight.controller.netconf.nettyutil.handler.ssh.authentication.AuthenticationHandler;
+import org.opendaylight.controller.netconf.nettyutil.handler.ssh.authentication.LoginPassword;
import org.opendaylight.controller.netconf.ssh.NetconfSSHServer;
import org.opendaylight.controller.netconf.ssh.authentication.AuthProvider;
import org.opendaylight.controller.netconf.ssh.authentication.AuthProviderImpl;
}
public AuthenticationHandler getAuthHandler() throws IOException {
- final AuthenticationHandler authHandler = mock(AuthenticationHandler.class);
- doAnswer(new Answer() {
- @Override
- public Object answer(final InvocationOnMock invocation) throws Throwable {
- Connection conn = (Connection) invocation.getArguments()[0];
- conn.authenticateWithPassword("user", "pwd");
- return null;
- }
- }).when(authHandler).authenticate(any(Connection.class));
- doReturn("auth handler").when(authHandler).toString();
- return authHandler;
+ return new LoginPassword("user", "pwd");
}
}
<groupId>org.opendaylight.controller.thirdparty</groupId>
<artifactId>ganymed</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.apache.sshd</groupId>
+ <artifactId>sshd-core</artifactId>
+ </dependency>
<dependency>
<groupId>org.openexi</groupId>
<artifactId>nagasena</artifactId>
<groupId>xmlunit</groupId>
<artifactId>xmlunit</artifactId>
</dependency>
+
</dependencies>
<build>
<artifactId>maven-bundle-plugin</artifactId>
<configuration>
<instructions>
- <Import-Package>ch.ethz.ssh2, com.google.common.base, com.google.common.collect, io.netty.buffer,
+ <Import-Package>org.apache.sshd.*, ch.ethz.ssh2, com.google.common.base, com.google.common.collect, io.netty.buffer,
io.netty.channel, io.netty.channel.socket, io.netty.handler.codec, io.netty.handler.ssl, io.netty.util,
io.netty.util.concurrent, javax.xml.transform, javax.xml.transform.dom, javax.xml.transform.sax,
javax.xml.transform.stream, org.opendaylight.controller.netconf.api,
package org.opendaylight.controller.netconf.nettyutil.handler.ssh.authentication;
import ch.ethz.ssh2.Connection;
-
import java.io.IOException;
+import org.apache.sshd.ClientSession;
/**
* Class providing authentication facility to SSH handler.
*/
public abstract class AuthenticationHandler {
public abstract void authenticate(Connection connection) throws IOException;
+
+
+ public abstract String getUsername();
+
+ public abstract org.apache.sshd.client.future.AuthFuture authenticate(final ClientSession session) throws IOException;
}
package org.opendaylight.controller.netconf.nettyutil.handler.ssh.authentication;
-import ch.ethz.ssh2.Connection;
-
import java.io.IOException;
+import org.apache.sshd.ClientSession;
+import org.apache.sshd.client.future.AuthFuture;
+
+import ch.ethz.ssh2.Connection;
+
/**
* Class Providing username/password authentication option to
* {@link org.opendaylight.controller.netconf.nettyutil.handler.ssh.client.SshHandler}
@Override
public void authenticate(Connection connection) throws IOException {
- boolean isAuthenticated = connection.authenticateWithPassword(username, password);
+ final boolean isAuthenticated = connection.authenticateWithPassword(username, password);
if (!isAuthenticated) {
throw new IOException("Authentication failed.");
}
}
+
+ @Override
+ public String getUsername() {
+ return username;
+ }
+
+ @Override
+ public AuthFuture authenticate(final ClientSession session) throws IOException {
+ session.addPasswordIdentity(password);
+ return session.auth();
+ }
}
--- /dev/null
+/*
+ * Copyright (c) 2014 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.netconf.nettyutil.handler.ssh.client;
+
+import com.google.common.base.Preconditions;
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelOutboundHandlerAdapter;
+import io.netty.channel.ChannelPromise;
+import java.io.IOException;
+import java.net.SocketAddress;
+import org.apache.sshd.ClientChannel;
+import org.apache.sshd.ClientSession;
+import org.apache.sshd.SshClient;
+import org.apache.sshd.client.future.AuthFuture;
+import org.apache.sshd.client.future.ConnectFuture;
+import org.apache.sshd.client.future.OpenFuture;
+import org.apache.sshd.common.future.CloseFuture;
+import org.apache.sshd.common.future.SshFutureListener;
+import org.apache.sshd.common.io.IoInputStream;
+import org.apache.sshd.common.io.IoReadFuture;
+import org.apache.sshd.common.util.Buffer;
+import org.opendaylight.controller.netconf.nettyutil.handler.ssh.authentication.AuthenticationHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Netty SSH handler class. Acts as interface between Netty and SSH library.
+ */
+public class AsyncSshHandler extends ChannelOutboundHandlerAdapter {
+
+ private static final Logger logger = LoggerFactory.getLogger(AsyncSshHandler.class);
+ public static final String SUBSYSTEM = "netconf";
+
+ public static final SshClient DEFAULT_CLIENT = SshClient.setUpDefaultClient();
+
+ public static final int SSH_DEFAULT_NIO_WORKERS = 8;
+
+ static {
+ // TODO make configurable, or somehow reuse netty threadpool
+ DEFAULT_CLIENT.setNioWorkers(SSH_DEFAULT_NIO_WORKERS);
+ DEFAULT_CLIENT.start();
+ }
+
+ private final AuthenticationHandler authenticationHandler;
+ private final SshClient sshClient;
+
+ private SshReadAsyncListener sshReadAsyncListener;
+ private ClientChannel channel;
+ private ClientSession session;
+ private ChannelPromise connectPromise;
+
+ public static AsyncSshHandler createForNetconfSubsystem(final AuthenticationHandler authenticationHandler) throws IOException {
+ return new AsyncSshHandler(authenticationHandler, DEFAULT_CLIENT);
+ }
+
+ /**
+ *
+ * @param authenticationHandler
+ * @param sshClient started SshClient
+ * @throws IOException
+ */
+ public AsyncSshHandler(final AuthenticationHandler authenticationHandler, final SshClient sshClient) throws IOException {
+ this.authenticationHandler = Preconditions.checkNotNull(authenticationHandler);
+ this.sshClient = Preconditions.checkNotNull(sshClient);
+ // Start just in case
+ sshClient.start();
+ }
+
+ private void startSsh(final ChannelHandlerContext ctx, final SocketAddress address) {
+ logger.debug("Starting SSH to {} on channel: {}", address, ctx.channel());
+
+ final ConnectFuture sshConnectionFuture = sshClient.connect(authenticationHandler.getUsername(), address);
+ sshConnectionFuture.addListener(new SshFutureListener<ConnectFuture>() {
+ @Override
+ public void operationComplete(final ConnectFuture future) {
+ if (future.isConnected()) {
+ handleSshSessionCreated(future, ctx);
+ } else {
+ handleSshSetupFailure(ctx, future.getException());
+ }
+ }
+ });
+ }
+
+ private synchronized void handleSshSessionCreated(final ConnectFuture future, final ChannelHandlerContext ctx) {
+ try {
+ logger.trace("SSH session created on channel: {}", ctx.channel());
+
+ session = future.getSession();
+ final AuthFuture authenticateFuture = authenticationHandler.authenticate(session);
+ authenticateFuture.addListener(new SshFutureListener<AuthFuture>() {
+ @Override
+ public void operationComplete(final AuthFuture future) {
+ if (future.isSuccess()) {
+ handleSshAuthenticated(session, ctx);
+ } else {
+ handleSshSetupFailure(ctx, future.getException());
+ }
+ }
+ });
+ } catch (final IOException e) {
+ handleSshSetupFailure(ctx, e);
+ }
+ }
+
+ private synchronized void handleSshAuthenticated(final ClientSession session, final ChannelHandlerContext ctx) {
+ try {
+ logger.debug("SSH session authenticated on channel: {}, server version: {}", ctx.channel(), session.getServerVersion());
+
+ channel = session.createSubsystemChannel(SUBSYSTEM);
+ channel.setStreaming(ClientChannel.Streaming.Async);
+ channel.open().addListener(new SshFutureListener<OpenFuture>() {
+ @Override
+ public void operationComplete(final OpenFuture future) {
+ if(future.isOpened()) {
+ handleSshChanelOpened(ctx);
+ } else {
+ handleSshSetupFailure(ctx, future.getException());
+ }
+ }
+ });
+
+
+ } catch (final IOException e) {
+ handleSshSetupFailure(ctx, e);
+ }
+ }
+
+ private synchronized void handleSshChanelOpened(final ChannelHandlerContext ctx) {
+ logger.trace("SSH subsystem channel opened successfully on channel: {}", ctx.channel());
+
+ connectPromise.setSuccess();
+ connectPromise = null;
+ ctx.fireChannelActive();
+
+ final IoInputStream asyncOut = channel.getAsyncOut();
+ sshReadAsyncListener = new SshReadAsyncListener(ctx, asyncOut);
+ }
+
+ private synchronized void handleSshSetupFailure(final ChannelHandlerContext ctx, final Throwable e) {
+ logger.warn("Unable to setup SSH connection on channel: {}", ctx.channel(), e);
+ connectPromise.setFailure(e);
+ connectPromise = null;
+ throw new IllegalStateException("Unable to setup SSH connection on channel: " + ctx.channel(), e);
+ }
+
+ @Override
+ public synchronized void write(final ChannelHandlerContext ctx, final Object msg, final ChannelPromise promise) {
+ try {
+ if(channel.getAsyncIn().isClosed() || channel.getAsyncIn().isClosing()) {
+ handleSshSessionClosed(ctx);
+ } else {
+ channel.getAsyncIn().write(toBuffer(msg));
+ ((ByteBuf) msg).release();
+ }
+ } catch (final Exception e) {
+ logger.warn("Exception while writing to SSH remote on channel {}", ctx.channel(), e);
+ throw new IllegalStateException("Exception while writing to SSH remote on channel " + ctx.channel(),e);
+ }
+ }
+
+ private static void handleSshSessionClosed(final ChannelHandlerContext ctx) {
+ logger.debug("SSH session closed on channel: {}", ctx.channel());
+ ctx.fireChannelInactive();
+ }
+
+ private Buffer toBuffer(final Object msg) {
+ // TODO Buffer vs ByteBuf translate, Can we handle that better ?
+ Preconditions.checkState(msg instanceof ByteBuf);
+ final ByteBuf byteBuf = (ByteBuf) msg;
+ final byte[] temp = new byte[byteBuf.readableBytes()];
+ byteBuf.readBytes(temp, 0, byteBuf.readableBytes());
+ return new Buffer(temp);
+ }
+
+ @Override
+ public synchronized void connect(final ChannelHandlerContext ctx, final SocketAddress remoteAddress, final SocketAddress localAddress, final ChannelPromise promise) throws Exception {
+ this.connectPromise = promise;
+ startSsh(ctx, remoteAddress);
+ }
+
+ @Override
+ public void close(final ChannelHandlerContext ctx, final ChannelPromise promise) throws Exception {
+ disconnect(ctx, promise);
+ }
+
+ @Override
+ public synchronized void disconnect(final ChannelHandlerContext ctx, final ChannelPromise promise) throws Exception {
+ if(sshReadAsyncListener != null) {
+ sshReadAsyncListener.close();
+ }
+
+ session.close(false).addListener(new SshFutureListener<CloseFuture>() {
+ @Override
+ public void operationComplete(final CloseFuture future) {
+ if(future.isClosed() == false) {
+ session.close(true);
+ }
+ session = null;
+ }
+ });
+
+ channel = null;
+ }
+
+ /**
+ * Listener over async input stream from SSH session.
+ * This listeners schedules reads in a loop until the session is closed or read fails.
+ */
+ private static class SshReadAsyncListener implements SshFutureListener<IoReadFuture>, AutoCloseable {
+ private static final int BUFFER_SIZE = 8192;
+
+ private final ChannelHandlerContext ctx;
+
+ private IoInputStream asyncOut;
+ private Buffer buf;
+ private IoReadFuture currentReadFuture;
+
+ public SshReadAsyncListener(final ChannelHandlerContext ctx, final IoInputStream asyncOut) {
+ this.ctx = ctx;
+ this.asyncOut = asyncOut;
+ buf = new Buffer(BUFFER_SIZE);
+ asyncOut.read(buf).addListener(this);
+ }
+
+ @Override
+ public synchronized void operationComplete(final IoReadFuture future) {
+ if(future.getException() != null) {
+
+ if(asyncOut.isClosed() || asyncOut.isClosing()) {
+ // We are closing
+ handleSshSessionClosed(ctx);
+ } else {
+ logger.warn("Exception while reading from SSH remote on channel {}", ctx.channel(), future.getException());
+ throw new IllegalStateException("Exception while reading from SSH remote on channel " + ctx.channel(), future.getException());
+ }
+ }
+
+ if (future.getRead() > 0) {
+ ctx.fireChannelRead(Unpooled.wrappedBuffer(buf.array(), 0, future.getRead()));
+
+ // Schedule next read
+ buf = new Buffer(BUFFER_SIZE);
+ currentReadFuture = asyncOut.read(buf);
+ currentReadFuture.addListener(this);
+ }
+ }
+
+ @Override
+ public synchronized void close() throws Exception {
+ // Remove self as listener on close to prevent reading from closed input
+ if(currentReadFuture != null) {
+ currentReadFuture.removeListener(this);
+ }
+
+ asyncOut = null;
+ }
+ }
+}
+++ /dev/null
-/*
- * Copyright (c) 2013 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.netconf.nettyutil.handler.ssh.client;
-
-import java.io.IOException;
-
-/**
- * Abstract class providing mechanism of invoking various SSH level services.
- * Class is not allowed to be extended, as it provides its own implementations via instance initiators.
- */
-abstract class Invoker {
- private boolean invoked = false;
-
- private Invoker() {
- }
-
- protected boolean isInvoked() {
- return invoked;
- }
-
- public void setInvoked() {
- this.invoked = true;
- }
-
- abstract void invoke(SshSession session) throws IOException;
-
- public static Invoker netconfSubsystem(){
- return subsystem("netconf");
- }
-
- public static Invoker subsystem(final String subsystem) {
- return new Invoker() {
- @Override
- synchronized void invoke(SshSession session) throws IOException {
- if (isInvoked()) {
- throw new IllegalStateException("Already invoked.");
- }
- try {
- session.startSubSystem(subsystem);
- } finally {
- setInvoked();
- }
- }
- };
- }
-}
+++ /dev/null
-/*
- * Copyright (c) 2013 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.netconf.nettyutil.handler.ssh.client;
-
-import ch.ethz.ssh2.Connection;
-import ch.ethz.ssh2.Session;
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.Map;
-import org.opendaylight.controller.netconf.nettyutil.handler.ssh.authentication.AuthenticationHandler;
-import org.opendaylight.controller.netconf.nettyutil.handler.ssh.virtualsocket.VirtualSocket;
-
-/**
- * Wrapper class around GANYMED SSH java library.
- */
-class SshClient {
- private final VirtualSocket socket;
- private final Map<Integer, SshSession> openSessions = new HashMap<>();
- private final AuthenticationHandler authenticationHandler;
- private Connection connection;
-
- public SshClient(VirtualSocket socket, AuthenticationHandler authenticationHandler) throws IOException {
- this.socket = socket;
- this.authenticationHandler = authenticationHandler;
- }
-
- public SshSession openSession() throws IOException {
- if (connection == null) {
- connect();
- }
-
- Session session = connection.openSession();
- SshSession sshSession = new SshSession(session);
- openSessions.put(openSessions.size(), sshSession);
-
- return sshSession;
- }
-
- private void connect() throws IOException {
- connection = new Connection(socket);
-
- connection.connect();
- authenticationHandler.authenticate(connection);
- }
-
-
- public void close() {
- for (SshSession session : openSessions.values()){
- session.close();
- }
-
- openSessions.clear();
-
- if (connection != null) {
- connection.close();
- }
- }
-
- @Override
- public String toString() {
- return "SshClient{" +
- "socket=" + socket +
- '}';
- }
-}
+++ /dev/null
-/*
- * Copyright (c) 2013 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.netconf.nettyutil.handler.ssh.client;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.base.Preconditions.checkState;
-
-import io.netty.buffer.ByteBuf;
-import io.netty.buffer.Unpooled;
-import io.netty.channel.ChannelFuture;
-import io.netty.channel.ChannelHandlerContext;
-import io.netty.channel.ChannelPromise;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.LinkedList;
-import java.util.Queue;
-import java.util.concurrent.atomic.AtomicBoolean;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-
-/**
- * Worker thread class. Handles all downstream and upstream events in SSH Netty
- * pipeline.
- */
-class SshClientAdapter implements Runnable {
- private static final Logger logger = LoggerFactory.getLogger(SshClientAdapter.class);
-
- private static final int BUFFER_SIZE = 1024;
-
- private final SshClient sshClient;
- private final Invoker invoker;
-
- private OutputStream stdIn;
-
- private final Queue<ByteBuf> postponed = new LinkedList<>();
-
- private ChannelHandlerContext ctx;
- private ChannelPromise disconnectPromise;
-
- private final AtomicBoolean stopRequested = new AtomicBoolean(false);
-
- private final Object lock = new Object();
-
- public SshClientAdapter(final SshClient sshClient, final Invoker invoker) {
- this.sshClient = sshClient;
- this.invoker = invoker;
- }
-
- // TODO ganymed spawns a Thread that receives the data from remote inside TransportManager
- // Get rid of this thread and reuse Ganymed internal thread (not sure if its possible without modifications in ganymed)
- public void run() {
- try {
- final SshSession session = sshClient.openSession();
- invoker.invoke(session);
- final InputStream stdOut = session.getStdout();
-
- synchronized (lock) {
- stdIn = session.getStdin();
- while (postponed.peek() != null) {
- writeImpl(postponed.poll());
- }
- }
-
- while (!stopRequested.get()) {
- final byte[] readBuff = new byte[BUFFER_SIZE];
- final int c = stdOut.read(readBuff);
- if (c == -1) {
- continue;
- }
-
- ctx.fireChannelRead(Unpooled.copiedBuffer(readBuff, 0, c));
- }
- } catch (final Exception e) {
- logger.error("Unexpected exception", e);
- } finally {
- sshClient.close();
-
- synchronized (lock) {
- if (disconnectPromise != null) {
- ctx.disconnect(disconnectPromise);
- }
- }
- }
- }
-
- // TODO: needs rework to match netconf framer API.
- public void write(final ByteBuf message) throws IOException {
- synchronized (lock) {
- if (stdIn == null) {
- postponed.add(message);
- return;
- }
- writeImpl(message);
- }
- }
-
- private void writeImpl(final ByteBuf message) throws IOException {
- message.getBytes(0, stdIn, message.readableBytes());
- message.release();
- stdIn.flush();
- }
-
- public void stop(final ChannelPromise promise) {
- synchronized (lock) {
- stopRequested.set(true);
- disconnectPromise = promise;
- }
- }
-
- public Thread start(final ChannelHandlerContext ctx, final ChannelFuture channelFuture) {
- checkArgument(channelFuture.isSuccess());
- checkNotNull(ctx.channel().remoteAddress());
- synchronized (this) {
- checkState(this.ctx == null);
- this.ctx = ctx;
- }
- final String threadName = toString();
- final Thread thread = new Thread(this, threadName);
- thread.start();
- return thread;
- }
-
- @Override
- public String toString() {
- return "SshClientAdapter{" +
- "sshClient=" + sshClient +
- '}';
- }
-}
+++ /dev/null
-/*
- * Copyright (c) 2013 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.netconf.nettyutil.handler.ssh.client;
-
-import io.netty.buffer.ByteBuf;
-import io.netty.channel.ChannelFuture;
-import io.netty.channel.ChannelFutureListener;
-import io.netty.channel.ChannelHandlerContext;
-import io.netty.channel.ChannelOutboundHandlerAdapter;
-import io.netty.channel.ChannelPromise;
-import java.io.IOException;
-import java.net.SocketAddress;
-import org.opendaylight.controller.netconf.nettyutil.handler.ssh.authentication.AuthenticationHandler;
-import org.opendaylight.controller.netconf.nettyutil.handler.ssh.virtualsocket.VirtualSocket;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Netty SSH handler class. Acts as interface between Netty and SSH library. All standard Netty message handling
- * stops at instance of this class. All downstream events are handed of to wrapped {@link org.opendaylight.controller.netconf.nettyutil.handler.ssh.client.SshClientAdapter};
- */
-public class SshHandler extends ChannelOutboundHandlerAdapter {
- private static final Logger logger = LoggerFactory.getLogger(SshHandler.class);
- private static final String SOCKET = "socket";
-
- private final VirtualSocket virtualSocket = new VirtualSocket();
- private final SshClientAdapter sshClientAdapter;
-
-
- public static SshHandler createForNetconfSubsystem(AuthenticationHandler authenticationHandler) throws IOException {
- return new SshHandler(authenticationHandler, Invoker.netconfSubsystem());
- }
-
-
- public SshHandler(AuthenticationHandler authenticationHandler, Invoker invoker) throws IOException {
- SshClient sshClient = new SshClient(virtualSocket, authenticationHandler);
- this.sshClientAdapter = new SshClientAdapter(sshClient, invoker);
- }
-
- @Override
- public void handlerAdded(ChannelHandlerContext ctx){
- if (ctx.channel().pipeline().get(SOCKET) == null) {
- ctx.channel().pipeline().addFirst(SOCKET, virtualSocket);
- }
- }
-
- @Override
- public void handlerRemoved(ChannelHandlerContext ctx) {
- if (ctx.channel().pipeline().get(SOCKET) != null) {
- ctx.channel().pipeline().remove(SOCKET);
- }
- }
-
- @Override
- public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws IOException {
- this.sshClientAdapter.write((ByteBuf) msg);
- }
-
- @Override
- public void connect(final ChannelHandlerContext ctx,
- SocketAddress remoteAddress,
- SocketAddress localAddress,
- ChannelPromise promise) {
- ctx.connect(remoteAddress, localAddress, promise);
-
- promise.addListener(new ChannelFutureListener() {
- public void operationComplete(ChannelFuture channelFuture) {
- if (channelFuture.isSuccess()) {
- sshClientAdapter.start(ctx, channelFuture);
- } else {
- logger.debug("Failed to connect to remote host");
- }
- }}
- );
- }
-
- @Override
- public void disconnect(ChannelHandlerContext ctx, ChannelPromise promise) {
- sshClientAdapter.stop(promise);
- }
-}
+++ /dev/null
-/*
- * Copyright (c) 2013 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.netconf.nettyutil.handler.ssh.client;
-
-import ch.ethz.ssh2.Session;
-import ch.ethz.ssh2.channel.Channel;
-import java.io.Closeable;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-
-/**
- * Wrapper class for proprietary SSH sessions implementations
- */
-class SshSession implements Closeable {
- private final Session session;
-
- public SshSession(final Session session) {
- this.session = session;
- }
-
- public void startSubSystem(final String name) throws IOException {
- session.startSubSystem(name);
- }
-
- public InputStream getStdout() {
- return session.getStdout();
- }
-
- // FIXME according to http://www.ganymed.ethz.ch/ssh2/FAQ.html#blocking you should read data from both stdout and stderr to prevent window filling up (since stdout and stderr share a window)
- // FIXME stdErr is not used anywhere
- public InputStream getStderr() {
- return session.getStderr();
- }
-
- public OutputStream getStdin() {
- return session.getStdin();
- }
-
- @Override
- public void close() {
- if (session.getState() == Channel.STATE_OPEN || session.getState() == Channel.STATE_OPENING) {
- session.close();
- }
- }
-}
<groupId>org.opendaylight.controller.thirdparty</groupId>
<artifactId>ganymed</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.apache.sshd</groupId>
+ <artifactId>sshd-core</artifactId>
+ </dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
import org.junit.Test;
import org.opendaylight.controller.netconf.netty.EchoClientHandler.State;
import org.opendaylight.controller.netconf.nettyutil.handler.ssh.authentication.LoginPassword;
-import org.opendaylight.controller.netconf.nettyutil.handler.ssh.client.SshHandler;
+import org.opendaylight.controller.netconf.nettyutil.handler.ssh.client.AsyncSshHandler;
import org.opendaylight.controller.netconf.ssh.NetconfSSHServer;
import org.opendaylight.controller.netconf.ssh.authentication.AuthProvider;
import org.opendaylight.controller.netconf.ssh.authentication.AuthProviderImpl;
ChannelInitializer<NioSocketChannel> channelInitializer = new ChannelInitializer<NioSocketChannel>() {
@Override
public void initChannel(NioSocketChannel ch) throws Exception {
- ch.pipeline().addFirst(SshHandler.createForNetconfSubsystem(new LoginPassword("a", "a")));
+ ch.pipeline().addFirst(AsyncSshHandler.createForNetconfSubsystem(new LoginPassword("a", "a")));
ch.pipeline().addLast(echoClientHandler);
}
};
<module>opendaylight/distribution/opendaylight-karaf</module>
<module>opendaylight/distribution/opendaylight-karaf-resources</module>
<module>features</module>
+
+ <!-- archetypes -->
+ <module>opendaylight/archetypes</module>
</modules>
<scm>
<connection>scm:git:ssh://git.opendaylight.org:29418/controller.git</connection>