Merge "Re-Enable the pax-exam execution in Eclipse"
authorAlessandro Boch <aboch@cisco.com>
Thu, 28 Nov 2013 21:18:14 +0000 (21:18 +0000)
committerGerrit Code Review <gerrit@opendaylight.org>
Thu, 28 Nov 2013 21:18:14 +0000 (21:18 +0000)
58 files changed:
.gitignore
opendaylight/config/config-persister-api/src/main/java/org/opendaylight/controller/config/persist/api/storage/StorageAdapter.java
opendaylight/config/config-persister-file-adapter/src/main/java/org/opendaylight/controller/config/persist/storage/file/FileStorageAdapter.java
opendaylight/config/pom.xml [changed mode: 0755->0644]
opendaylight/distribution/opendaylight/pom.xml
opendaylight/distribution/opendaylight/src/main/resources/configuration/config.ini
opendaylight/distribution/opendaylight/src/main/resources/configuration/logback.xml
opendaylight/md-sal/forwardingrules-manager/src/main/java/org/opendaylight/controller/forwardingrulesmanager/consumer/impl/GroupConsumerImpl.java
opendaylight/md-sal/forwardingrules-manager/src/main/java/org/opendaylight/controller/forwardingrulesmanager/consumer/impl/MeterConsumerImpl.java
opendaylight/md-sal/forwardingrules-manager/src/main/java/org/opendaylight/controller/forwardingrulesmanager/consumer/impl/TableFeaturesConsumerImpl.java
opendaylight/md-sal/model/model-flow-service/src/main/yang/flow-error.yang
opendaylight/md-sal/model/model-flow-service/src/main/yang/flow-service.yang
opendaylight/md-sal/model/model-flow-service/src/main/yang/group-service.yang
opendaylight/md-sal/model/model-flow-statistics/src/main/yang/group-statistics.yang
opendaylight/md-sal/model/model-flow-statistics/src/main/yang/meter-statistics.yang
opendaylight/md-sal/model/pom.xml
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/RuntimeCodeGenerator.xtend
opendaylight/md-sal/sal-binding-it/src/main/java/org/opendaylight/controller/test/sal/binding/it/TestHelper.java
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/DataModification.java
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/config/yang/md/sal/connector/netconf/NetconfConnectorModule.java
opendaylight/md-sal/sal-netconf-connector/src/main/yang/odl-sal-netconf-connector-cfg.yang
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/api/RestconfService.java
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.xtend
opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/XmlProvidersTest.java
opendaylight/md-sal/statistics-manager/src/main/java/org/opendaylight/controller/md/statistics/manager/StatisticsProvider.java
opendaylight/netconf/config-netconf-connector/pom.xml [changed mode: 0755->0644]
opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/ConfigPersisterNotificationHandler.java
opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/NoOpStorageAdapter.java
opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/PersisterImpl.java
opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/osgi/ConfigPersisterActivator.java
opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/PersisterImplTest.java
opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfSession.java
opendaylight/netconf/netconf-client/pom.xml
opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClient.java
opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientSession.java
opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfSshClientDispatcher.java
opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfImplActivator.java
opendaylight/netconf/netconf-it/pom.xml
opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITTest.java
opendaylight/netconf/netconf-it/src/test/resources/logback.xml [new file with mode: 0644]
opendaylight/netconf/netconf-ssh/pom.xml [new file with mode: 0644]
opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/osgi/NetconfSSHActivator.java [new file with mode: 0644]
opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/NetconfSSHServer.java [new file with mode: 0644]
opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/authentication/KeyStoreHandler.java [new file with mode: 0644]
opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/authentication/RSAKey.java [new file with mode: 0644]
opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/threads/IOThread.java [new file with mode: 0644]
opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/threads/SocketThread.java [new file with mode: 0644]
opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/ssh/SSHServerTest.java [new file with mode: 0644]
opendaylight/netconf/netconf-util/pom.xml
opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/ssh/SshHandler.java
opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/ssh/authentication/LoginPassword.java
opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/ssh/client/SshClient.java
opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/ssh/client/SshClientAdapter.java
opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/osgi/NetconfConfigUtil.java
opendaylight/netconf/pom.xml
opendaylight/protocol_plugins/openflow/src/main/java/org/opendaylight/controller/protocol_plugin/openflow/core/internal/SwitchHandler.java
opendaylight/topologymanager/implementation/pom.xml [changed mode: 0755->0644]
third-party/ganymed/pom.xml

index bb3e26faf5ee24f0f3080cbd311a87c55f7430b2..18817e7c359dbfbe526b0f37e379d859f0dfc97b 100644 (file)
@@ -19,3 +19,4 @@ opendaylight/northbound/integrationtest/logs/*
 *.iws
 .idea
 xtend-gen
+classes
index 9daf4a132502cb1c917b35065a2361fdcbeea552..50503c15cc02e6b384a5affd5dcab0e50d3e43c4 100644 (file)
@@ -9,7 +9,6 @@
 package org.opendaylight.controller.config.persist.api.storage;
 
 import org.opendaylight.controller.config.persist.api.Persister;
-import org.osgi.framework.BundleContext;
 
 /**
  * Plugins for {@link org.opendaylight.controller.config.persist.api.Persister}
@@ -17,6 +16,20 @@ import org.osgi.framework.BundleContext;
  */
 public interface StorageAdapter extends Persister {
 
-    void setProperties(BundleContext bundleContext);
+    void setProperties(PropertiesProvider propertiesProvider);
+
+
+    public interface PropertiesProvider {
+        /**
+         * Get property value for given key. Implementation of this interface is allowed to prefix
+         * the key with a namespace.
+         */
+        String getProperty(String key);
+
+        /**
+         * @return prefix + key as used in getProperty method.
+         */
+        String getFullKeyForReporting(String key);
+    }
 
 }
index 775fb1f88192cb697b38e712716851c3a246e974..5098c6f352b515e43f0adba943aeef65575802be 100644 (file)
@@ -16,7 +16,6 @@ import com.google.common.collect.Sets;
 import com.google.common.io.Files;
 import org.apache.commons.lang3.StringUtils;
 import org.opendaylight.controller.config.persist.api.storage.StorageAdapter;
-import org.osgi.framework.BundleContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.xml.sax.SAXException;
@@ -55,8 +54,8 @@ public class FileStorageAdapter implements StorageAdapter {
     private File storage;
 
     @Override
-    public void setProperties(BundleContext bundleContext) {
-        File storage = extractStorageFileFromProperties(bundleContext);
+    public void setProperties(PropertiesProvider propertiesProvider) {
+        File storage = extractStorageFileFromProperties(propertiesProvider);
         logger.debug("Using file {}", storage.getAbsolutePath());
         // Create file if it does not exist
         File parentFile = storage.getAbsoluteFile().getParentFile();
@@ -92,12 +91,11 @@ public class FileStorageAdapter implements StorageAdapter {
         numberOfStoredBackups = numberOfBackups;
     }
 
-    private static File extractStorageFileFromProperties(BundleContext bundleContext) {
-        String fileStorageProperty = bundleContext.getProperty(FILE_STORAGE_PROP);
-        Preconditions.checkNotNull(fileStorageProperty, "Unable to find " + FILE_STORAGE_PROP
-                + " in received context :" + bundleContext);
+    private static File extractStorageFileFromProperties(PropertiesProvider propertiesProvider) {
+        String fileStorageProperty = propertiesProvider.getProperty(FILE_STORAGE_PROP);
+        Preconditions.checkNotNull(fileStorageProperty, "Unable to find " + propertiesProvider.getFullKeyForReporting(FILE_STORAGE_PROP));
         File result = new File(fileStorageProperty);
-        String numberOfBAckupsAsString = bundleContext.getProperty(NUMBER_OF_BACKUPS);
+        String numberOfBAckupsAsString = propertiesProvider.getProperty(NUMBER_OF_BACKUPS);
         if (numberOfBAckupsAsString != null) {
             numberOfStoredBackups = Integer.valueOf(numberOfBAckupsAsString);
         } else {
old mode 100755 (executable)
new mode 100644 (file)
index 5b08af79f835e39b0d729424d31cf522ea78c256..5f2651659d94b5195e8dc3ef57aa770815ca4c31 100644 (file)
           <artifactId>netconf-mapping-api</artifactId>
           <version>${netconf.version}</version>
         </dependency>
+        <dependency>
+          <groupId>org.opendaylight.controller</groupId>
+          <artifactId>netconf-ssh</artifactId>
+          <version>${netconf.version}</version>
+        </dependency>
         <dependency>
           <groupId>org.opendaylight.controller</groupId>
           <artifactId>config-netconf-connector</artifactId>
           <groupId>org.opendaylight.yangtools</groupId>
           <artifactId>yang-model-api</artifactId>
          </dependency>
-
          <dependency>
           <groupId>org.opendaylight.yangtools.model</groupId>
           <artifactId>yang-ext</artifactId>
          </dependency>
-
         <dependency>
          <groupId>org.opendaylight.controller.thirdparty</groupId>
          <artifactId>ganymed</artifactId>
index bfc3962040de008313256355ae917948d0caf5e4..d9ff11ade7e074ba09af9f8c26b3edfb64e784d5 100644 (file)
@@ -17,14 +17,13 @@ osgi.bundles=\
 netconf.tcp.address=0.0.0.0
 netconf.tcp.port=8383
 
-#netconf.tls.address=127.0.0.1
-#netconf.tls.port=8384
-#netconf.tls.keystore=
-#netconf.tls.keystore.password=
 
+netconf.ssh.address=0.0.0.0
+netconf.ssh.port=1830
 netconf.config.persister.storageAdapterClass=org.opendaylight.controller.config.persist.storage.file.FileStorageAdapter
-fileStorage=configuration/controller.config
-numberOfBackups=1
+netconf.config.persister.fileStorage=configuration/controller.config
+netconf.config.persister.numberOfBackups=1
+
 yangstore.blacklist=.*controller.model.*
 
 # Set Default start level for framework
index 3ad0c61d4f66ffb8763354e65bc2d196d88929df..2815422274fad3c61cfa95ba724969eadf30d001 100644 (file)
@@ -36,7 +36,6 @@
   <!--  Base log level  -->
   <logger name="org.opendaylight" level="INFO"/>
 
-  <logger name="org.opendaylight.yangtools.yang.parser.util.ModuleDependencySort" level="ERROR"/>
 
   <!-- Controller log level -->
   <logger name="org.opendaylight.controller" level="INFO"/>
index 714ac89ba13ccf66f3de6e211276d2aace61aba9..b8dac19bf37956262e9b88a35f24b95b719edc5c 100644 (file)
@@ -73,8 +73,7 @@ public class GroupConsumerImpl implements IForwardingRulesManager {
 
     public GroupConsumerImpl() {
 
-        InstanceIdentifier<? extends DataObject> path = InstanceIdentifier.builder(Groups.class).child(Group.class)
-                .toInstance();
+        InstanceIdentifier<? extends DataObject> path = InstanceIdentifier.builder(Groups.class).toInstance();
         groupService = FRMConsumerImpl.getProviderSession().getRpcService(SalGroupService.class);
 
         clusterGroupContainerService = FRMConsumerImpl.getClusterContainerService();
index 483b9a47196580a2b16c9e0c63b2e41be7a40e9f..cef259b395629a8681bf748d4f54fdf8761d84c7 100644 (file)
@@ -73,8 +73,7 @@ public class MeterConsumerImpl implements IForwardingRulesManager {
     
 
     public MeterConsumerImpl() {
-        InstanceIdentifier<? extends DataObject> path = InstanceIdentifier.builder(Meters.class).child(Meter.class)
-                .toInstance();
+        InstanceIdentifier<? extends DataObject> path = InstanceIdentifier.builder(Meters.class).toInstance();
         meterService = FRMConsumerImpl.getProviderSession().getRpcService(SalMeterService.class);
         clusterMeterContainerService = FRMConsumerImpl.getClusterContainerService();
 
index 30556e47141c8825eebb3d2cda7c29a94cfcb387..11d1189573244abad3c5bdac9f231065737a4da4 100644 (file)
@@ -37,8 +37,7 @@ public class TableFeaturesConsumerImpl {
     private boolean inContainerMode; // being used by global instance only
 
     public TableFeaturesConsumerImpl() {
-        InstanceIdentifier<? extends DataObject> path = InstanceIdentifier.builder(Tables.class).child(Table.class)
-                .toInstance();
+        InstanceIdentifier<? extends DataObject> path = InstanceIdentifier.builder(Tables.class).toInstance();
         tableService = FRMConsumerImpl.getProviderSession().getRpcService(SalTableService.class);
 
         if (null == tableService) {
@@ -115,12 +114,14 @@ public class TableFeaturesConsumerImpl {
         }
 
         Map<InstanceIdentifier<?>, TableFeatures> updates = new HashMap<>();
+        Map<InstanceIdentifier<?>, TableFeatures> createdEntries = new HashMap<>();
 
         /**
          * We create a plan which table features will be updated.
          *
          */
         void prepareUpdate() {
+               Set<Entry<InstanceIdentifier<?>, DataObject>> createdEntries = modification.getCreatedConfigurationData().entrySet();
 
             Set<Entry<InstanceIdentifier<?>, DataObject>> puts = modification.getUpdatedConfigurationData().entrySet();
             for (Entry<InstanceIdentifier<?>, DataObject> entry : puts) {
index 369b279b95264a317ae51001f016290bbdac79cb..3e9d1ee75fe9644def39f3077c9b3d3c7d328f03 100644 (file)
@@ -16,7 +16,7 @@ module flow-errors {
             enum flow-mod-failed;
             enum group-mod-failed;
             enum port-mod-failed;
-            enum table-mod-failed;            
+            enum table-mod-failed;
             enum queue-op-failed;
             enum switch-config-failed;
             enum role-request-failed;
@@ -34,7 +34,7 @@ module flow-errors {
         }
         
         leaf code {
-            type uint16;        
+            type uint16;
         }
         
         leaf data {
@@ -48,15 +48,15 @@ module flow-errors {
         }
         
         leaf exp-type {
-            type uint16;        
+            type uint16;
         }
         
         leaf experimenter-id {
-            type uint32;        
+            type uint32;
         }
         
         leaf data {
             type string;
         }
-    }   
+    }
 }
\ No newline at end of file
index a32a45f2fdfea5cb5ce894c9c569942e0752d48e..07678f57e1b429d8655fa3665d26eaa9c83f74d8 100644 (file)
@@ -52,7 +52,7 @@ module sal-flow {
     rpc add-flow {
         input {
             uses node-flow;
-            uses tr:transaction-aware;            
+            uses tr:transaction-aware;
         }
         output {
             uses tr:transaction-aware;
@@ -62,7 +62,7 @@ module sal-flow {
     rpc remove-flow {
         input {
             uses node-flow;
-            uses tr:transaction-aware;            
+            uses tr:transaction-aware;
         }
         output {
             uses tr:transaction-aware;
@@ -72,7 +72,7 @@ module sal-flow {
     rpc update-flow {
         input {
             uses flow-update;
-            uses tr:transaction-aware;            
+            uses tr:transaction-aware;
         }
         output {
             uses tr:transaction-aware;
index b21423b7d4d94d6db1a850a3cbac99bf5547a2d3..9106bca8a4826619f09062a3c3322b16e3551621 100644 (file)
@@ -32,7 +32,7 @@ module sal-group {
     rpc add-group {
         input {
             uses node-group;
-            uses tr:transaction-aware;            
+            uses tr:transaction-aware;
         }
         output {
             uses tr:transaction-aware;
@@ -42,7 +42,7 @@ module sal-group {
     rpc remove-group {
         input {
             uses node-group;
-            uses tr:transaction-aware;            
+            uses tr:transaction-aware;
         }
         output {
             uses tr:transaction-aware;
@@ -52,7 +52,7 @@ module sal-group {
     rpc update-group {
         input {
             uses group-update;
-            uses tr:transaction-aware;            
+            uses tr:transaction-aware;
         }
         output {
             uses tr:transaction-aware;
index d29ddc0ddd68bcef17b84cfec9ac92eab6d5cc24..1b6a9df9ad514fbccdf4fb0d7de043421e968e4c 100644 (file)
@@ -8,9 +8,9 @@ module opendaylight-group-statistics {
     import flow-capable-transaction {prefix tr;}
     
     contact
-       "Anilkumar Vishnoi
-       Email: avishnoi@in.ibm.com";
-               
+        "Anilkumar Vishnoi
+        Email: avishnoi@in.ibm.com";
+            
     revision "2013-11-11" {
         description "Initial revision of group statistics service";
     }
@@ -18,44 +18,44 @@ module opendaylight-group-statistics {
     augment "/inv:nodes/inv:node" {
         ext:augment-identifier "node-group-statistics";
         container group-statistics {
-               //config "false";
-               uses group-types:group-statistics-reply;
+            //config "false";
+            uses group-types:group-statistics-reply;
         }
     }
 
-       augment "/inv:nodes/inv:node" {
+    augment "/inv:nodes/inv:node" {
         ext:augment-identifier "node-group-desc-stats";
         container group-desc {
-               //config "false";
-               uses group-types:group-desc-stats-reply;
+            //config "false";
+            uses group-types:group-desc-stats-reply;
         }
     }
-       
-       augment "/inv:nodes/inv:node" {
+    
+    augment "/inv:nodes/inv:node" {
         ext:augment-identifier "node-group-features";
         container group-features {
-               //config "false";
-               uses group-types:group-features-reply;
+            //config "false";
+            uses group-types:group-features-reply;
         }
     }
 
-       // RPC calls
-       rpc get-all-group-statistics {
-               input {
-            uses inv:node;
+    // RPC calls
+    rpc get-all-group-statistics {
+        input {
+            uses inv:node-context-ref;
         }
         output {
-               uses group-types:group-statistics-reply;
+            uses group-types:group-statistics-reply;
             uses tr:transaction-aware;
         }
-       
-       }
-       
-       rpc get-group-statistics {
-               input {
-            uses inv:node;
+    
+    }
+    
+    rpc get-group-statistics {
+        input {
+            uses inv:node-context-ref;
             leaf group-id{
-               type group-types:group-id;
+                type group-types:group-id;
             }
            
         }
@@ -63,56 +63,56 @@ module opendaylight-group-statistics {
             uses group-types:group-statistics-reply;
             uses tr:transaction-aware;
         }
-       
-       }
-       
-       rpc get-group-description {
-               input {
-            uses inv:node;
+    
+    }
+    
+    rpc get-group-description {
+        input {
+            uses inv:node-context-ref;
         }
         output {
             uses group-types:group-desc-stats-reply;
             uses tr:transaction-aware;
         }
-       }
-       
-       rpc get-group-features {
-               input {
-            uses inv:node;
+    }
+    
+    rpc get-group-features {
+        input {
+            uses inv:node-context-ref;
         }
         output {
             uses group-types:group-features-reply;
             uses tr:transaction-aware;
         }
-       }
-       
+    }
+    
 
-       //Notification calls
-       
-       notification group-statistics-updated {
-               leaf moreReplies {
-                       type boolean;
-               }
-               uses inv:node;
-               uses group-types:group-statistics-reply;
+    //Notification calls
+    
+    notification group-statistics-updated {
+        leaf moreReplies {
+            type boolean;
+        }
+        uses inv:node;
+        uses group-types:group-statistics-reply;
         uses tr:transaction-aware;
-       }
-       
-       notification group-desc-stats-updated {
-               leaf moreReplies {
-                       type boolean;
-               }
-               uses inv:node;
-               uses group-types:group-desc-stats-reply;
+    }
+    
+    notification group-desc-stats-updated {
+        leaf moreReplies {
+            type boolean;
+        }
+        uses inv:node;
+        uses group-types:group-desc-stats-reply;
         uses tr:transaction-aware;
-       }
+    }
 
-       notification group-features-updated {
-               leaf moreReplies {
-                       type boolean;
-               }
-               uses inv:node;
-               uses group-types:group-features-reply;
+    notification group-features-updated {
+        leaf moreReplies {
+            type boolean;
+        }
+        uses inv:node;
+        uses group-types:group-features-reply;
         uses tr:transaction-aware;
-       }
+    }
 }
index c22bdd3906bb801f1a2432329dff8ef9c9f40292..e3b2a3fc6477430d676e0542b5db91667a704ba5 100644 (file)
@@ -8,8 +8,8 @@ module opendaylight-meter-statistics {
     import flow-capable-transaction {prefix tr;}
     
     contact
-       "Anilkumar Vishnoi
-       Email: avishnoi@in.ibm.com";
+        "Anilkumar Vishnoi
+        Email: avishnoi@in.ibm.com";
 
     revision "2013-11-11" {
         description "Initial revision of meter statistics service";
@@ -18,103 +18,103 @@ module opendaylight-meter-statistics {
     augment "/inv:nodes/inv:node" {
         ext:augment-identifier "node-meter-statistics";
         container meter-statistics {
-               //config "false";
-               uses meter-types:meter-statistics-reply;
+            //config "false";
+            uses meter-types:meter-statistics-reply;
         }
     }
 
-       augment "/inv:nodes/inv:node" {
+    augment "/inv:nodes/inv:node" {
         ext:augment-identifier "node-meter-config-stats";
         container meter-config-stats {
-               //config "false";
-               uses meter-types:meter-config-stats-reply;
+            //config "false";
+            uses meter-types:meter-config-stats-reply;
         }
     }
-       
-       augment "/inv:nodes/inv:node" {
+    
+    augment "/inv:nodes/inv:node" {
         ext:augment-identifier "node-meter-features";
         container meter-features {
-               //config "false";
-               uses meter-types:meter-features-reply;
+            //config "false";
+            uses meter-types:meter-features-reply;
         }
     }
 
-       // RPC calls
-       rpc get-all-meter-statistics {
-               input {
-            uses inv:node;
+    // RPC calls
+    rpc get-all-meter-statistics {
+        input {
+            uses inv:node-context-ref;
         }
         output {
-                       uses meter-types:meter-statistics-reply;
-                       uses tr:transaction-aware;
+            uses meter-types:meter-statistics-reply;
+            uses tr:transaction-aware;
         }
-       
-       }
-       
-       rpc get-meter-statistics {
-               input {
-            uses inv:node;
+    
+    }
+    
+    rpc get-meter-statistics {
+        input {
+            uses inv:node-context-ref;
             leaf meter-id{
-               type meter-types:meter-id;
+                type meter-types:meter-id;
             }
         }
         output {
             uses meter-types:meter-statistics-reply;
             uses tr:transaction-aware;
         }
-       
-       }
-       
-       rpc get-all-meter-config-statistics {
-               input {
-            uses inv:node;
+    
+    }
+    
+    rpc get-all-meter-config-statistics {
+        input {
+            uses inv:node-context-ref;
         }
         output {
-               uses meter-types:meter-config-stats-reply;
+               uses meter-types:meter-config-stats-reply;
             uses tr:transaction-aware;
         }
-       }
-       
-       rpc get-meter-features {
-               input {
-            uses inv:node;
+    }
+    
+    rpc get-meter-features {
+        input {
+            uses inv:node-context-ref;
         }
         output {
-               uses meter-types:meter-features-reply;
+            uses meter-types:meter-features-reply;
             uses tr:transaction-aware;
         }
-       }
-       
+    }
+    
 
-       //Notification calls
-       
-       notification meter-statistics-updated {
-               leaf moreReplies {
-                       type boolean;
-               }
-               
-               uses inv:node;
+    //Notification calls
+    
+    notification meter-statistics-updated {
+        leaf moreReplies {
+            type boolean;
+        }
+        
+        uses inv:node;
         uses meter-types:meter-statistics-reply;
         uses tr:transaction-aware;
-       }
-       
-       notification meter-config-stats-updated {
-               leaf moreReplies {
-                       type boolean;
-               }
-           
+    }
+    
+    notification meter-config-stats-updated {
+        leaf moreReplies {
+            type boolean;
+        }
+        
         uses inv:node;
-           uses meter-types:meter-config-stats-reply;
-       uses tr:transaction-aware;
-       }
+        uses meter-types:meter-config-stats-reply;
+        uses tr:transaction-aware;
+    }
 
-       notification meter-features-updated {
-               leaf moreReplies {
-                       type boolean;
-               }
-               
-               uses inv:node;
+    notification meter-features-updated {
+        leaf moreReplies {
+            type boolean;
+        }
+        
+        uses inv:node;
         uses meter-types:meter-features-reply;
         uses tr:transaction-aware;
-       }
+    }
 }
index 34ad23844f363da800bc33a12b9dfee7eae045cb..2f3b7a036c6113b23d8020dd207c30ce490e6ab6 100644 (file)
@@ -40,6 +40,7 @@
                 <configuration>
                     <instructions>
                         <Bundle-Name>${project.groupId}.${project.artifactId}</Bundle-Name>
+                        <Import-Package>*,org.opendaylight.yangtools.yang.binding.annotations</Import-Package>
                     </instructions>
                 </configuration>
             </plugin>
index 801e4984c0401b04ce268b11fb2f5c9360f71f57..90fcbd99aaaad0a6f6448faf0428cbc46cc52b4d 100644 (file)
@@ -57,7 +57,14 @@ class RuntimeCodeGenerator implements org.opendaylight.controller.sal.binding.co
         val targetCls = createClass(iface.directProxyName, supertype) [
             field(DELEGATE_FIELD, iface);
             implementMethodsFrom(supertype) [
-                body = '''return ($r) Â«DELEGATE_FIELD».«it.name»($$);'''
+                body = '''
+                {
+                    if(«DELEGATE_FIELD» == null) {
+                        throw new java.lang.IllegalStateException("No provider is processing supplied message");
+                    }
+                    return ($r) Â«DELEGATE_FIELD».«it.name»($$);
+                }
+                '''
             ]
         ]
         return targetCls.toClass(iface.classLoader).newInstance as T
index 0eace4daaca15973e76e056cb1b923627dfd51b6..608be10602283b0dd6126d6e58b2091ea0bfefc8 100644 (file)
@@ -41,7 +41,7 @@ public class TestHelper {
                 mavenBundle("org.opendaylight.bgpcep", "framework").versionAsInProject(), //
                 mavenBundle("org.opendaylight.bgpcep", "util").versionAsInProject(), //
                 mavenBundle("commons-codec", "commons-codec").versionAsInProject(),
-                
+
                 mavenBundle(CONTROLLER, "config-api").versionAsInProject(), // //
                 mavenBundle(CONTROLLER, "config-manager").versionAsInProject(), // //
                 mavenBundle("commons-io", "commons-io").versionAsInProject(), //
@@ -54,30 +54,30 @@ public class TestHelper {
                 mavenBundle(CONTROLLER, "logback-config").versionAsInProject(), //
                 mavenBundle(CONTROLLER, "config-persister-api").versionAsInProject(), //
                 mavenBundle(CONTROLLER, "netconf-api").versionAsInProject(), //
-               
+
                 mavenBundle(CONTROLLER, "netconf-client").versionAsInProject(), //
                 mavenBundle(CONTROLLER, "netconf-util").versionAsInProject(), //
                 mavenBundle(CONTROLLER + ".thirdparty", "ganymed", "1.0-SNAPSHOT"), //
                 mavenBundle(CONTROLLER, "netconf-mapping-api").versionAsInProject(), //
 
                 mavenBundle(CONTROLLER, "config-persister-impl").versionAsInProject(), //
-                
+
                 mavenBundle("io.netty", "netty-handler").versionAsInProject(), //
                 mavenBundle("io.netty", "netty-codec").versionAsInProject(), //
                 mavenBundle("io.netty", "netty-buffer").versionAsInProject(), //
                 mavenBundle("io.netty", "netty-transport").versionAsInProject(), //
                 mavenBundle("io.netty", "netty-common").versionAsInProject(), //
-                
+
                 mavenBundle("org.opendaylight.controller.thirdparty", "exificient", "0.9.2-SNAPSHOT"), //
-                
+
                 mavenBundle("org.apache.servicemix.bundles", "org.apache.servicemix.bundles.xerces", "2.11.0_1"),
                 mavenBundle("org.eclipse.birt.runtime.3_7_1", "org.apache.xml.resolver", "1.2.0"),
-                
+
                 mavenBundle(CONTROLLER, "config-netconf-connector").versionAsInProject(), //
                 mavenBundle(CONTROLLER, "netconf-impl").versionAsInProject(), //
-                
+
                 mavenBundle(CONTROLLER, "config-persister-file-adapter").versionAsInProject().noStart());
-            
+
     }
 
     public static Option bindingAwareSalBundles() {
@@ -114,8 +114,8 @@ public class TestHelper {
                 systemProperty("netconf.tcp.port").value("18383"), //
                 systemProperty("netconf.config.persister.storageAdapterClass").value(
                         "org.opendaylight.controller.config.persist.storage.file.FileStorageAdapter"), //
-                systemProperty("fileStorage").value(PathUtils.getBaseDir() + "/src/test/resources/controller.config"), //
-                systemProperty("numberOfBackups").value("1") //
+                systemProperty("netconf.config.persister.fileStorage").value(PathUtils.getBaseDir() + "/src/test/resources/controller.config"), //
+                systemProperty("netconf.config.persister.numberOfBackups").value("1") //
                 //systemProperty("yangstore.blacklist").value(".*controller.model.*") //
 
         );
@@ -170,7 +170,7 @@ public class TestHelper {
                  * org/mockito/cglib/proxy/Factory have different Class objects
                  * for the type org/mockito/cglib/ proxy/Callback used in the
                  * signature
-                 * 
+                 *
                  * So we disable the bootdelegation. this property has no effect
                  * on the other OSGi implementation.
                  */
index d059766dea71bee37fb8fa26a051c52b2e044cfa..d74b26dae24ae374aa5b03f987eea27c12fe7113 100644 (file)
@@ -29,11 +29,12 @@ public interface DataModification<P/* extends Path<P> */, D> extends DataChange<
 
     /**
      * 
-     * Use {@link #putOperationalData(Object, Object)} instead.
+     * @deprecated Use {@link #putOperationalData(Object, Object)} instead.
      * 
      * @param path
      * @param data
      */
+    @Deprecated
     void putRuntimeData(P path, D data);
 
     void putOperationalData(P path, D data);
@@ -41,10 +42,11 @@ public interface DataModification<P/* extends Path<P> */, D> extends DataChange<
     void putConfigurationData(P path, D data);
 
     /**
-     * Use {@link #removeOperationalData(Object)}
+     * @deprecated Use {@link #removeOperationalData(Object)}
      * 
      * @param path
      */
+    @Deprecated
     void removeRuntimeData(P path);
 
     void removeOperationalData(P path);
index 2a556c9be41c235aee2fb76ff8b712ffe33c6761..12759246140a9d7da0947ff8f73e8d461e6614d6 100644 (file)
@@ -17,6 +17,9 @@ import java.net.InetSocketAddress;
 import javax.net.ssl.SSLContext;
 
 import org.opendaylight.controller.netconf.client.NetconfClientDispatcher;
+import org.opendaylight.controller.netconf.client.NetconfSshClientDispatcher;
+import org.opendaylight.controller.netconf.util.handler.ssh.authentication.AuthenticationHandler;
+import org.opendaylight.controller.netconf.util.handler.ssh.authentication.LoginPassword;
 import org.opendaylight.controller.sal.connect.netconf.NetconfDevice;
 import org.osgi.framework.BundleContext;
 
@@ -75,8 +78,13 @@ public final class NetconfConnectorModule extends org.opendaylight.controller.co
         EventLoopGroup bossGroup = getBossThreadGroupDependency();
         EventLoopGroup workerGroup = getWorkerThreadGroupDependency();
         Optional<SSLContext> maybeContext = Optional.absent();
-        NetconfClientDispatcher dispatcher = new NetconfClientDispatcher(maybeContext , bossGroup, workerGroup);
-        
+        NetconfClientDispatcher dispatcher = null;
+        if(getTcpOnly()) {
+            dispatcher = new NetconfClientDispatcher(maybeContext , bossGroup, workerGroup);
+        } else {
+            AuthenticationHandler authHandler = new LoginPassword(getUsername(),getPassword());
+            dispatcher = new NetconfSshClientDispatcher(authHandler , bossGroup, workerGroup);
+        }
         getDomRegistryDependency().registerProvider(device, bundleContext);
         
         device.start(dispatcher);
index 45f10162ca7edd600ca86bc5a56ac3cfb229fafb..923851411090a9c93f9b0cf1d59e9be1a197a92b 100644 (file)
@@ -45,7 +45,18 @@ module odl-sal-netconf-connector-cfg {
             leaf port {
                 type uint32;
             }
+            
+            leaf tcp-only {
+                type boolean;
+            }
 
+            leaf username {
+                type string;
+            }
+            
+            leaf password {
+                type string;
+            }
             container dom-registry {
                 uses config:service-ref {
                     refine type {
index 5d08b3e7b6cb27bec3e2bbb68c5c1f9927f603aa..012b51fb5e339b6a0a4332e44d146b72f7e7ee76 100644 (file)
@@ -92,16 +92,4 @@ public interface RestconfService extends RestconfServiceLegacy {
                MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_XML})
     public StructuredData readOperationalData(@PathParam("identifier") String identifier);
 
-    @POST
-    @Path("/operational/{identifier:.+}")
-    @Produces({Draft02.MediaTypes.DATA+JSON,Draft02.MediaTypes.DATA+XML, 
-               MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_XML})
-    public Response createOperationalData(@PathParam("identifier") String identifier, CompositeNode payload);
-
-    @PUT
-    @Path("/operational/{identifier:.+}")
-    @Produces({Draft02.MediaTypes.DATA+JSON,Draft02.MediaTypes.DATA+XML, 
-               MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_XML})
-    public Response updateOperationalData(@PathParam("identifier") String identifier, CompositeNode payload);
-
 }
index 8f6ca1685bbb4b0c6f81ec2dc85ae4fd4c92d5f2..b6426c65bfdd55c8f984a8433eaddbb9ccb20dbc 100644 (file)
@@ -2,12 +2,11 @@ package org.opendaylight.controller.sal.restconf.impl
 
 import java.util.List
 import javax.ws.rs.core.Response
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus
 import org.opendaylight.controller.sal.rest.api.RestconfService
 import org.opendaylight.yangtools.yang.data.api.CompositeNode
 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer
 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode
-import org.opendaylight.controller.md.sal.common.api.TransactionStatus
-import javax.ws.rs.WebApplicationException
 
 class RestconfImpl implements RestconfService {
     
@@ -96,26 +95,6 @@ class RestconfImpl implements RestconfService {
         createConfigurationData(identifier,payload);
     }
     
-    override createOperationalData(String identifier, CompositeNode payload) {
-        val identifierWithSchemaNode = identifier.resolveInstanceIdentifier
-        val value = resolveNodeNamespaceBySchema(payload, identifierWithSchemaNode.schemaNode)
-        val status = broker.commitOperationalDataPut(identifierWithSchemaNode.instanceIdentifier,value).get();
-        switch status.result {
-            case TransactionStatus.COMMITED: Response.status(Response.Status.OK).build
-            default: Response.status(Response.Status.INTERNAL_SERVER_ERROR).build
-        }
-    }
-    
-    override updateOperationalData(String identifier, CompositeNode payload) {
-        val identifierWithSchemaNode = identifier.resolveInstanceIdentifier
-        val value = resolveNodeNamespaceBySchema(payload, identifierWithSchemaNode.schemaNode)
-        val status = broker.commitOperationalDataPut(identifierWithSchemaNode.instanceIdentifier,value).get();
-        switch status.result {
-            case TransactionStatus.COMMITED: Response.status(Response.Status.NO_CONTENT).build
-            default: Response.status(Response.Status.INTERNAL_SERVER_ERROR).build
-        }
-    }
-    
     private def InstanceIdWithSchemaNode resolveInstanceIdentifier(String identifier) {
         val identifierWithSchemaNode = identifier.toInstanceIdentifier
         if (identifierWithSchemaNode === null) {
index 4336ac8a8325b14ab883834a7e9b91a0a6fb7655..10885b642b8a75de219a2e997e95d362fbf43085 100644 (file)
@@ -133,7 +133,7 @@ public class XmlProvidersTest extends JerseyTest {
         response = target(uri).request(MEDIA_TYPE_DRAFT02).post(entity);
         assertEquals(200, response.getStatus());
         
-        uri = createUri("/operational/", "ietf-interfaces:interfaces/interface/eth0");
+        uri = createUri("/config/", "ietf-interfaces:interfaces/interface/eth0");
         response = target(uri).request(MEDIA_TYPE_DRAFT02).put(entity);
         assertEquals(204, response.getStatus());
         response = target(uri).request(MEDIA_TYPE_DRAFT02).post(entity);
@@ -162,7 +162,7 @@ public class XmlProvidersTest extends JerseyTest {
         response = target(uri).request(MEDIA_TYPE_DRAFT02).post(entity);
         assertEquals(500, response.getStatus());
         
-        uri = createUri("/operational/", "ietf-interfaces:interfaces/interface/eth0");
+        uri = createUri("/config/", "ietf-interfaces:interfaces/interface/eth0");
         response = target(uri).request(MEDIA_TYPE_DRAFT02).put(entity);
         assertEquals(500, response.getStatus());
         response = target(uri).request(MEDIA_TYPE_DRAFT02).post(entity);
index d1ab3515035dfb0b5597c263c5f23ac29656d659..cdcd1ef32edde93dbc83745a62c97f81a2f42a83 100644 (file)
@@ -24,6 +24,7 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.group.statistics.rev131111.
 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.statistics.rev131111.GetGroupFeaturesOutput;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.statistics.rev131111.OpendaylightGroupStatisticsService;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.statistics.rev131111.GetAllMeterConfigStatisticsInputBuilder;
@@ -112,7 +113,7 @@ public class StatisticsProvider implements AutoCloseable {
         
         spLogger.debug("Statistics requester thread started with timer interval : {}",5000);
         
-        statisticsRequesterThread.start();
+        //statisticsRequesterThread.start();
         
         spLogger.info("Statistics Provider started.");
     }
@@ -137,76 +138,86 @@ public class StatisticsProvider implements AutoCloseable {
             //We need to add check, so see if groups/meters are supported
             //by the target node. Below check doesn't look good.
             if(targetNode.getId().getValue().contains("openflow:")){
-                sendAllGroupStatisticsRequest(targetNode);
+                InstanceIdentifier<Node> targetInstanceId = InstanceIdentifier.builder(Nodes.class).child(Node.class,targetNode.getKey()).toInstance();
+                NodeRef targetNodeRef = new NodeRef(targetInstanceId);
                 
-                sendAllMeterStatisticsRequest(targetNode);
+                sendAllGroupStatisticsRequest(targetNodeRef);
                 
-                sendGroupDescriptionRequest(targetNode);
+                sendAllMeterStatisticsRequest(targetNodeRef);
                 
-                sendGroupFeaturesRequest(targetNode);
+                sendGroupDescriptionRequest(targetNodeRef);
                 
-                sendMeterConfigStatisticsRequest(targetNode);
+                sendGroupFeaturesRequest(targetNodeRef);
                 
-                sendMeterFeaturesRequest(targetNode);
+                sendMeterConfigStatisticsRequest(targetNodeRef);
+                
+                sendMeterFeaturesRequest(targetNodeRef);
             }
         }
     }
     
-    private void sendAllGroupStatisticsRequest(Node targetNode){
+    private void sendAllGroupStatisticsRequest(NodeRef targetNode){
         
         final GetAllGroupStatisticsInputBuilder input = new GetAllGroupStatisticsInputBuilder();
         
-        input.setId(targetNode.getId());
+        input.setNode(targetNode);
+        input.setNode(targetNode);
 
+        @SuppressWarnings("unused")
         Future<RpcResult<GetAllGroupStatisticsOutput>> response = 
                 groupStatsService.getAllGroupStatistics(input.build());
     }
     
-    private void sendGroupDescriptionRequest(Node targetNode){
+    private void sendGroupDescriptionRequest(NodeRef targetNode){
         final GetGroupDescriptionInputBuilder input = new GetGroupDescriptionInputBuilder();
         
-        input.setId(targetNode.getId());
-        
+        input.setNode(targetNode);
+
+        @SuppressWarnings("unused")
         Future<RpcResult<GetGroupDescriptionOutput>> response = 
                 groupStatsService.getGroupDescription(input.build());
     }
     
-    private void sendGroupFeaturesRequest(Node targetNode){
+    private void sendGroupFeaturesRequest(NodeRef targetNode){
         
         GetGroupFeaturesInputBuilder input = new GetGroupFeaturesInputBuilder();
         
-        input.setId(targetNode.getId());
-        
+        input.setNode(targetNode);
+
+        @SuppressWarnings("unused")
         Future<RpcResult<GetGroupFeaturesOutput>> response = 
                 groupStatsService.getGroupFeatures(input.build());
     }
     
-    private void sendAllMeterStatisticsRequest(Node targetNode){
+    private void sendAllMeterStatisticsRequest(NodeRef targetNode){
         
         GetAllMeterStatisticsInputBuilder input = new GetAllMeterStatisticsInputBuilder();
         
-        input.setId(targetNode.getId());
-        
+        input.setNode(targetNode);
+
+        @SuppressWarnings("unused")
         Future<RpcResult<GetAllMeterStatisticsOutput>> response = 
                 meterStatsService.getAllMeterStatistics(input.build());
     }
     
-    private void sendMeterConfigStatisticsRequest(Node targetNode){
+    private void sendMeterConfigStatisticsRequest(NodeRef targetNode){
         
         GetAllMeterConfigStatisticsInputBuilder input = new GetAllMeterConfigStatisticsInputBuilder();
         
-        input.setId(targetNode.getId());
-        
+        input.setNode(targetNode);
+
+        @SuppressWarnings("unused")
         Future<RpcResult<GetAllMeterConfigStatisticsOutput>> response = 
                 meterStatsService.getAllMeterConfigStatistics(input.build());
         
     }
-    private void sendMeterFeaturesRequest(Node targetNode){
+    private void sendMeterFeaturesRequest(NodeRef targetNode){
      
         GetMeterFeaturesInputBuilder input = new GetMeterFeaturesInputBuilder();
         
-        input.setId(targetNode.getId());
-        
+        input.setNode(targetNode);
+
+        @SuppressWarnings("unused")
         Future<RpcResult<GetMeterFeaturesOutput>> response = 
                 meterStatsService.getMeterFeatures(input.build());
     }
@@ -241,7 +252,6 @@ public class StatisticsProvider implements AutoCloseable {
           } catch (Throwable e) {
             throw Exceptions.sneakyThrow(e);
           }
-
     }
 
 }
index 25d2ad6abd09943278d28262a7184649a0688f4f..747965c0645faeb3cb02914fa260867252da20e5 100644 (file)
@@ -93,6 +93,7 @@ public class ConfigPersisterNotificationHandler implements NotificationListener,
             registerToNetconf(maybeConfig.get().getCapabilities());
 
             final String configSnapshot = maybeConfig.get().getConfigSnapshot();
+            logger.trace("Pushing following xml to netconf {}", configSnapshot);
             try {
                 pushLastConfig(XmlUtil.readXmlToElement(configSnapshot));
             } catch (SAXException | IOException e) {
@@ -247,11 +248,14 @@ public class ConfigPersisterNotificationHandler implements NotificationListener,
         return maybeConfigElement;
     }
 
-    private synchronized void pushLastConfig(Element persistedConfig) {
+    private synchronized void pushLastConfig(Element xmlToBePersisted) {
+        logger.info("Pushing last configuration to netconf");
         StringBuilder response = new StringBuilder("editConfig response = {");
 
-        Element configElement = persistedConfig;
-        NetconfMessage message = createEditConfigMessage(configElement, "/netconfOp/editConfig.xml");
+
+        NetconfMessage message = createEditConfigMessage(xmlToBePersisted, "/netconfOp/editConfig.xml");
+
+        // sending message to netconf
         NetconfMessage responseMessage = netconfClient.sendMessage(message);
 
         XmlElement element = XmlElement.fromDomDocument(responseMessage.getDocument());
@@ -271,7 +275,8 @@ public class ConfigPersisterNotificationHandler implements NotificationListener,
         response.append("commit response = {");
         response.append(XmlUtil.toString(responseMessage.getDocument()));
         response.append("}");
-        logger.debug("Last configuration loaded successfully");
+        logger.info("Last configuration loaded successfully");
+        logger.trace("Detailed message {}", response);
     }
 
     private void checkIsOk(XmlElement element, NetconfMessage responseMessage) {
@@ -289,8 +294,8 @@ public class ConfigPersisterNotificationHandler implements NotificationListener,
         }
     }
 
-    private NetconfMessage createEditConfigMessage(Element dataElement, String editConfigResourcename) {
-        try (InputStream stream = getClass().getResourceAsStream(editConfigResourcename)) {
+    private static NetconfMessage createEditConfigMessage(Element dataElement, String editConfigResourcename) {
+        try (InputStream stream = ConfigPersisterNotificationHandler.class.getResourceAsStream(editConfigResourcename)) {
             Preconditions.checkNotNull(stream, "Unable to load resource " + editConfigResourcename);
 
             Document doc = XmlUtil.readXmlToDocument(stream);
index cd604312f4d0318fa2f55ed637e00c01b392e206..305908ae8a79580733a99b71870bcc5f9c707cc4 100644 (file)
@@ -10,7 +10,6 @@ package org.opendaylight.controller.netconf.persist.impl;
 
 import com.google.common.base.Optional;
 import org.opendaylight.controller.config.persist.api.storage.StorageAdapter;
-import org.osgi.framework.BundleContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -20,8 +19,8 @@ public class NoOpStorageAdapter implements StorageAdapter {
     private static final Logger logger = LoggerFactory.getLogger(NoOpStorageAdapter.class);
 
     @Override
-    public void setProperties(BundleContext bundleContext) {
-        logger.debug("setProperties called with {}", bundleContext);
+    public void setProperties(PropertiesProvider propertiesProvider) {
+        logger.debug("setProperties called with {}", propertiesProvider);
     }
 
     @Override
index e06968e86844f1e65148e1a9e06bc08092ff8d68..499e8b98796fd795a248da1ca7e925a418e49339 100644 (file)
@@ -12,7 +12,8 @@ import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Optional;
 import org.opendaylight.controller.config.persist.api.Persister;
 import org.opendaylight.controller.config.persist.api.storage.StorageAdapter;
-import org.osgi.framework.BundleContext;
+import org.opendaylight.controller.config.persist.api.storage.StorageAdapter.PropertiesProvider;
+import org.opendaylight.controller.netconf.persist.impl.osgi.ConfigPersisterActivator;
 
 import java.io.IOException;
 
@@ -29,44 +30,38 @@ import java.io.IOException;
  */
 public final class PersisterImpl implements Persister {
 
-    public static final String STORAGE_ADAPTER_CLASS_PROP = "netconf.config.persister.storageAdapterClass";
+
     private final StorageAdapter storage;
 
     public PersisterImpl(StorageAdapter storage) {
         this.storage = storage;
     }
 
-    public static Optional<PersisterImpl> createFromProperties(BundleContext bundleContext) {
-        String storageAdapterClass = bundleContext.getProperty(STORAGE_ADAPTER_CLASS_PROP);
+    public static PersisterImpl createFromProperties(PropertiesProvider propertiesProvider) {
+        String storageAdapterClass = propertiesProvider.getProperty(ConfigPersisterActivator.STORAGE_ADAPTER_CLASS_PROP_SUFFIX);
         StorageAdapter storage;
         if (storageAdapterClass == null || storageAdapterClass.equals("")) {
-            return Optional.absent();
+            throw new IllegalStateException("No persister is defined in " +
+                    propertiesProvider.getFullKeyForReporting(ConfigPersisterActivator.STORAGE_ADAPTER_CLASS_PROP_SUFFIX)
+                    + " property. For noop persister use " + NoOpStorageAdapter.class.getCanonicalName()
+                    + " . Persister is not operational");
         }
 
         try {
-            storage = StorageAdapter.class.cast(resolveClass(storageAdapterClass, StorageAdapter.class).newInstance());
-            storage.setProperties(bundleContext);
+            Class<?> clazz = Class.forName(storageAdapterClass);
+            boolean implementsCorrectIfc = StorageAdapter.class.isAssignableFrom(clazz);
+            if (implementsCorrectIfc == false) {
+                throw new IllegalArgumentException("Storage adapter " + clazz + " does not implement " + StorageAdapter.class);
+            }
+            storage = StorageAdapter.class.cast(clazz.newInstance());
+
+            storage.setProperties(propertiesProvider);
 
         } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {
             throw new IllegalArgumentException("Unable to instantiate storage adapter from " + storageAdapterClass, e);
         }
-        return Optional.of(new PersisterImpl(storage));
-    }
-
-    private static Class<?> resolveClass(String storageAdapterClass, Class<?> baseType) throws ClassNotFoundException {
-        Class<?> clazz = Class.forName(storageAdapterClass);
 
-        if (!isImplemented(baseType, clazz))
-            throw new IllegalArgumentException("Storage adapter " + clazz + " has to implement " + baseType);
-        return clazz;
-    }
-
-    private static boolean isImplemented(Class<?> expectedIface, Class<?> byClazz) {
-        for (Class<?> iface : byClazz.getInterfaces()) {
-            if (iface.equals(expectedIface))
-                return true;
-        }
-        return false;
+        return new PersisterImpl(storage);
     }
 
     @Override
index ae6c95312c9143d29f384571ffe9182b227aa862..241830ddc33ebb74a4d95168887f48c225d77bc8 100644 (file)
@@ -8,12 +8,10 @@
 
 package org.opendaylight.controller.netconf.persist.impl.osgi;
 
-import com.google.common.base.Optional;
+import org.opendaylight.controller.config.persist.api.storage.StorageAdapter.PropertiesProvider;
 import org.opendaylight.controller.netconf.persist.impl.ConfigPersisterNotificationHandler;
-import org.opendaylight.controller.netconf.persist.impl.NoOpStorageAdapter;
 import org.opendaylight.controller.netconf.persist.impl.PersisterImpl;
 import org.opendaylight.controller.netconf.util.osgi.NetconfConfigUtil;
-import org.opendaylight.controller.netconf.util.osgi.NetconfConfigUtil.TLSConfiguration;
 import org.osgi.framework.BundleActivator;
 import org.osgi.framework.BundleContext;
 import org.slf4j.Logger;
@@ -33,32 +31,33 @@ public class ConfigPersisterActivator implements BundleActivator {
 
     private Thread initializationThread;
 
+    private static final String NETCONF_CONFIG_PERSISTER_PREFIX = "netconf.config.persister.";
+    public static final String STORAGE_ADAPTER_CLASS_PROP_SUFFIX =  "storageAdapterClass";
+
     @Override
-    public void start(BundleContext context) throws Exception {
-        logger.debug("ConfigPersister activator started");
-
-        Optional<PersisterImpl> maybePersister = PersisterImpl.createFromProperties(context);
-        if (maybePersister.isPresent() == false) {
-            throw new IllegalStateException("No persister is defined in " + PersisterImpl.STORAGE_ADAPTER_CLASS_PROP
-                    + " property. For noop persister use " + NoOpStorageAdapter.class.getCanonicalName()
-                    + " . Persister is not operational");
-        }
-
-        Optional<TLSConfiguration> maybeTLSConfiguration = NetconfConfigUtil.extractTLSConfiguration(context);
-        Optional<InetSocketAddress> maybeTCPAddress = NetconfConfigUtil.extractTCPNetconfAddress(context);
-
-        InetSocketAddress address;
-        if (maybeTLSConfiguration.isPresent()) {
-            throw new UnsupportedOperationException("TLS is currently not supported for persister");
-        } else if (maybeTCPAddress.isPresent()) {
-            address = maybeTCPAddress.get();
-        } else {
-            throw new IllegalStateException("Netconf is not configured, persister is not operational");
-        }
-
-        PersisterImpl persister = maybePersister.get();
+    public void start(final BundleContext context) throws Exception {
+        logger.debug("ConfigPersister starting");
+
+        PropertiesProvider propertiesProvider = new PropertiesProvider() {
+            @Override
+            public String getProperty(String key) {
+                return context.getProperty(getFullKeyForReporting(key));
+            }
+
+            @Override
+            public String getFullKeyForReporting(String key) {
+                return NETCONF_CONFIG_PERSISTER_PREFIX + key;
+            }
+        };
+
+        PersisterImpl persister = PersisterImpl.createFromProperties(propertiesProvider);
+
+        InetSocketAddress address = NetconfConfigUtil.extractTCPNetconfAddress(context,
+                "Netconf is not configured, persister is not operational");
         configPersisterNotificationHandler = new ConfigPersisterNotificationHandler(persister, address,
                 platformMBeanServer);
+
+        // offload initialization to another thread in order to stop blocking activator
         Runnable initializationRunnable = new Runnable() {
             @Override
             public void run() {
index 44b3b610434038aba7cfb68a20c763e97f2f7cff..d88d9e5559d32bd1a74d0a73a69d370daf329ecd 100644 (file)
@@ -15,8 +15,9 @@ import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.opendaylight.controller.config.persist.api.Persister;
 import org.opendaylight.controller.config.persist.api.storage.StorageAdapter;
+import org.opendaylight.controller.config.persist.api.storage.StorageAdapter.PropertiesProvider;
 import org.opendaylight.controller.config.persist.storage.file.FileStorageAdapter;
-import org.osgi.framework.BundleContext;
+import org.opendaylight.controller.netconf.persist.impl.osgi.ConfigPersisterActivator;
 
 import java.io.File;
 import java.io.IOException;
@@ -27,6 +28,8 @@ import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 import static org.junit.matchers.JUnitMatchers.containsString;
 import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.doCallRealMethod;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
@@ -34,19 +37,32 @@ import static org.mockito.Mockito.verify;
 
 public class PersisterImplTest {
     @Mock
-    BundleContext mockedContext;
+    TestingPropertiesProvider propertiesProvider;
+
+    class TestingPropertiesProvider implements PropertiesProvider {
+        @Override
+        public String getFullKeyForReporting(String key) {
+            return "prefix" + key;
+        }
+
+        @Override
+        public String getProperty(String key) {
+            throw new UnsupportedOperationException("should be mocked");
+        }
+    }
 
     @Before
     public void setUpMocks() {
         MockitoAnnotations.initMocks(this);
+        doCallRealMethod().when(propertiesProvider).getFullKeyForReporting(anyString());
     }
 
     @Test
     public void testFromProperties() throws Exception {
-        doReturn(MockAdapter.class.getName()).when(mockedContext).getProperty(
-                PersisterImpl.STORAGE_ADAPTER_CLASS_PROP);
+        doReturn(MockAdapter.class.getName()).when(propertiesProvider).getProperty(
+                ConfigPersisterActivator.STORAGE_ADAPTER_CLASS_PROP_SUFFIX);
 
-        PersisterImpl persisterImpl = PersisterImpl.createFromProperties(mockedContext).get();
+        PersisterImpl persisterImpl = PersisterImpl.createFromProperties(propertiesProvider);
         persisterImpl.persistConfig(null);
         persisterImpl.loadLastConfig();
         persisterImpl.persistConfig(null);
@@ -57,31 +73,33 @@ public class PersisterImplTest {
         assertEquals(1, MockAdapter.props);
     }
 
+
     @Test
     public void testFromProperties2() throws Exception {
-        mockedContext = mock(BundleContext.class);
-        doReturn(FileStorageAdapter.class.getName()).when(mockedContext).getProperty(
-                PersisterImpl.STORAGE_ADAPTER_CLASS_PROP);
+
+        doReturn(FileStorageAdapter.class.getName()).when(propertiesProvider).getProperty(
+                ConfigPersisterActivator.STORAGE_ADAPTER_CLASS_PROP_SUFFIX);
+
         doReturn("target" + File.separator + "generated-test-sources" + File.separator + "testFile").when(
-                mockedContext).getProperty(FileStorageAdapter.FILE_STORAGE_PROP);
-        doReturn("mockedContext").when(mockedContext).toString();
-        doReturn(null).when(mockedContext).getProperty("numberOfBackups");
+                propertiesProvider).getProperty(FileStorageAdapter.FILE_STORAGE_PROP);
+        doReturn("propertiesProvider").when(propertiesProvider).toString();
+        doReturn(null).when(propertiesProvider).getProperty("numberOfBackups");
 
-        PersisterImpl persisterImpl = PersisterImpl.createFromProperties(mockedContext).get();
+        PersisterImpl persisterImpl = PersisterImpl.createFromProperties(propertiesProvider);
         assertTrue(persisterImpl.getStorage() instanceof FileStorageAdapter);
     }
 
     @Test
     public void testFromProperties3() throws Exception {
-        mockedContext = mock(BundleContext.class);
-        doReturn(FileStorageAdapter.class.getName()).when(mockedContext).getProperty(
-                PersisterImpl.STORAGE_ADAPTER_CLASS_PROP);
+
+        doReturn(FileStorageAdapter.class.getName()).when(propertiesProvider).getProperty(
+                ConfigPersisterActivator.STORAGE_ADAPTER_CLASS_PROP_SUFFIX);
         doReturn("target" + File.separator + "generated-test-sources" + File.separator + "testFile").when(
-                mockedContext).getProperty(FileStorageAdapter.FILE_STORAGE_PROP);
-        doReturn("mockedContext").when(mockedContext).toString();
-        doReturn("0").when(mockedContext).getProperty("numberOfBackups");
+                propertiesProvider).getProperty(FileStorageAdapter.FILE_STORAGE_PROP);
+        doReturn("propertiesProvider").when(propertiesProvider).toString();
+        doReturn("0").when(propertiesProvider).getProperty("numberOfBackups");
         try {
-            PersisterImpl.createFromProperties(mockedContext).get();
+            PersisterImpl.createFromProperties(propertiesProvider);
             fail();
         } catch (RuntimeException e) {
             assertThat(
@@ -123,7 +141,7 @@ public class PersisterImplTest {
         static int props = 0;
 
         @Override
-        public void setProperties(BundleContext configProvider) {
+        public void setProperties(PropertiesProvider propertiesProvider) {
             props++;
         }
 
index 8b761a85b23b423314a39f8718492a2f5d1f8de0..17330b7babde5ce12030568ffca5d12207edb946 100644 (file)
@@ -9,10 +9,6 @@ package org.opendaylight.controller.netconf.api;
 
 import io.netty.channel.Channel;
 import io.netty.channel.ChannelHandler;
-
-import java.io.IOException;
-import java.util.Map;
-
 import org.opendaylight.protocol.framework.AbstractProtocolSession;
 import org.opendaylight.protocol.framework.ProtocolMessageDecoder;
 import org.opendaylight.protocol.framework.ProtocolMessageEncoder;
@@ -20,18 +16,20 @@ import org.opendaylight.protocol.framework.SessionListener;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.io.IOException;
+import java.util.Map;
+
 public abstract class NetconfSession extends AbstractProtocolSession<NetconfMessage> {
 
     private ChannelHandler exiEncoder;
     private String exiEncoderName;
     private String removeAfterMessageSentname;
     private String pmeName,pmdName;
-    private final  Channel channel;
+    protected final  Channel channel;
     private final  SessionListener sessionListener;
     private final long sessionId;
     private boolean up = false;
     private static final Logger logger = LoggerFactory.getLogger(NetconfSession.class);
-    private static final int T = 0;
 
     protected NetconfSession(SessionListener sessionListener, Channel channel, long sessionId) {
         this.sessionListener = sessionListener;
index ffd46e882c60f4a86b47470159d18139863ffff9..de4fb101f9d6fefb065e26f4b03bd46af4979cf3 100644 (file)
                             javax.xml.xpath,
                             org.opendaylight.controller.netconf.api,
                             org.opendaylight.controller.netconf.util,
-                            org.opendaylight.controller.netconf.util.xml,
+                            org.opendaylight.controller.netconf.util.*,
                             org.opendaylight.protocol.framework,
                             org.slf4j,
                             org.w3c.dom,
-                            org.xml.sax
+                            org.xml.sax,
+                            io.netty.handler.codec
                         </Import-Package>
                     </instructions>
                 </configuration>
index 61a9a9b9548bc6eeb771dcf952c61e08e4a669f3..d95977492a9206fb0ff05d4e6229810b08dcda9a 100644 (file)
@@ -50,7 +50,6 @@ public class NetconfClient implements Closeable {
     private NetconfClient(String clientLabelForLogging, InetSocketAddress address, ReconnectStrategy strat, NetconfClientDispatcher netconfClientDispatcher) throws InterruptedException {
         this.label = clientLabelForLogging;
         dispatch = netconfClientDispatcher;
-
         sessionListener = new NetconfClientSessionListener();
         Future<NetconfClientSession> clientFuture = dispatch.createClient(address, sessionListener, strat);
         this.address = address;
index 11c7f3061f9e9b28f34926b482c60a8bbe51a26a..c2c8d38b9a8daa08804a686e75106f4a86a60a58 100644 (file)
@@ -9,14 +9,13 @@
 package org.opendaylight.controller.netconf.client;
 
 import io.netty.channel.Channel;
-
-import java.util.Collection;
-
 import org.opendaylight.controller.netconf.api.NetconfSession;
 import org.opendaylight.protocol.framework.SessionListener;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.util.Collection;
+
 public class NetconfClientSession extends NetconfSession {
 
     private static final Logger logger = LoggerFactory.getLogger(NetconfClientSession.class);
@@ -32,5 +31,4 @@ public class NetconfClientSession extends NetconfSession {
     public Collection<String> getServerCapabilities() {
         return capabilities;
     }
-
 }
index ce0f4274757ac37a1791569b29b95906ec26cd6b..b19c09263b9fa481c76307da0bb2dbebc310bb85 100644 (file)
 
 package org.opendaylight.controller.netconf.client;
 
+import java.io.IOException;
+import java.net.InetSocketAddress;
+
+import javax.net.ssl.SSLContext;
+
+import org.opendaylight.controller.netconf.api.NetconfMessage;
+import org.opendaylight.controller.netconf.api.NetconfSession;
+import org.opendaylight.controller.netconf.api.NetconfTerminationReason;
+import org.opendaylight.controller.netconf.util.AbstractChannelInitializer;
+import org.opendaylight.controller.netconf.util.handler.FramingMechanismHandlerFactory;
+import org.opendaylight.controller.netconf.util.handler.NetconfMessageAggregator;
+import org.opendaylight.controller.netconf.util.handler.ssh.SshHandler;
+import org.opendaylight.controller.netconf.util.handler.ssh.authentication.AuthenticationHandler;
+import org.opendaylight.controller.netconf.util.handler.ssh.client.Invoker;
+import org.opendaylight.controller.netconf.util.messages.FramingMechanism;
+import org.opendaylight.controller.netconf.util.messages.NetconfMessageFactory;
+import org.opendaylight.protocol.framework.ProtocolHandlerFactory;
+import org.opendaylight.protocol.framework.ProtocolMessageDecoder;
+import org.opendaylight.protocol.framework.ProtocolMessageEncoder;
+import org.opendaylight.protocol.framework.ReconnectStrategy;
+import org.opendaylight.protocol.framework.SessionListener;
+import org.opendaylight.protocol.framework.SessionListenerFactory;
+
+import com.google.common.base.Optional;
+
+import io.netty.channel.ChannelHandler;
 import io.netty.channel.EventLoopGroup;
+import io.netty.channel.socket.SocketChannel;
+import io.netty.util.HashedWheelTimer;
+import io.netty.util.concurrent.Future;
+import io.netty.util.concurrent.Promise;
 
 public class NetconfSshClientDispatcher extends NetconfClientDispatcher {
 
-    public NetconfSshClientDispatcher(EventLoopGroup bossGroup, EventLoopGroup workerGroup) {
-        super(null, bossGroup, workerGroup);
+    private AuthenticationHandler authHandler;
+    private HashedWheelTimer timer;
+    private NetconfClientSessionNegotiatorFactory negotatorFactory;
+
+    public NetconfSshClientDispatcher(AuthenticationHandler authHandler, EventLoopGroup bossGroup,
+            EventLoopGroup workerGroup) {
+        super(Optional.<SSLContext> absent(), bossGroup, workerGroup);
+        this.authHandler = authHandler;
+        this.timer = new HashedWheelTimer();
+        this.negotatorFactory = new NetconfClientSessionNegotiatorFactory(timer);
+    }
+
+    @Override
+    public Future<NetconfClientSession> createClient(InetSocketAddress address,
+            final NetconfClientSessionListener sessionListener, ReconnectStrategy strat) {
+        return super.createClient(address, strat, new PipelineInitializer<NetconfClientSession>() {
+
+            @Override
+            public void initializeChannel(SocketChannel arg0, Promise<NetconfClientSession> arg1) {
+                new NetconfSshClientInitializer(authHandler, negotatorFactory, sessionListener).initialize(arg0, arg1);
+            }
+
+        });
+    }
+
+    private static final class NetconfSshClientInitializer extends AbstractChannelInitializer {
+
+        private final NetconfHandlerFactory handlerFactory;
+        private final AuthenticationHandler authenticationHandler;
+        private final NetconfClientSessionNegotiatorFactory negotiatorFactory;
+        private final NetconfClientSessionListener sessionListener;
+
+        public NetconfSshClientInitializer(AuthenticationHandler authHandler,
+                NetconfClientSessionNegotiatorFactory negotiatorFactory,
+                final NetconfClientSessionListener sessionListener) {
+            this.handlerFactory = new NetconfHandlerFactory(new NetconfMessageFactory());
+            this.authenticationHandler = authHandler;
+            this.negotiatorFactory = negotiatorFactory;
+            this.sessionListener = sessionListener;
+        }
+
+        @Override
+        public void initialize(SocketChannel ch, Promise<? extends NetconfSession> promise) {
+            try {
+                Invoker invoker = Invoker.subsystem("netconf");
+                ch.pipeline().addFirst(new SshHandler(authenticationHandler, invoker));
+                ch.pipeline().addLast("aggregator", new NetconfMessageAggregator(FramingMechanism.EOM));
+                ch.pipeline().addLast(handlerFactory.getDecoders());
+                initializeAfterDecoder(ch, promise);
+                ch.pipeline().addLast("frameEncoder",
+                        FramingMechanismHandlerFactory.createHandler(FramingMechanism.EOM));
+                ch.pipeline().addLast(handlerFactory.getEncoders());
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+        }
+
+        @Override
+        protected void initializeAfterDecoder(SocketChannel ch, Promise<? extends NetconfSession> promise) {
+            ch.pipeline().addLast("negotiator", negotiatorFactory.getSessionNegotiator(new SessionListenerFactory() {
+                @Override
+                public SessionListener<NetconfMessage, NetconfClientSession, NetconfTerminationReason> getSessionListener() {
+                    return sessionListener;
+                }
+            }, ch, promise));
+
+        }
+    }
+
+    private static final class NetconfHandlerFactory extends ProtocolHandlerFactory<NetconfMessage> {
+
+        public NetconfHandlerFactory(final NetconfMessageFactory msgFactory) {
+            super(msgFactory);
+        }
+
+        @Override
+        public ChannelHandler[] getEncoders() {
+            return new ChannelHandler[] { new ProtocolMessageEncoder(this.msgFactory) };
+        }
+
+        @Override
+        public ChannelHandler[] getDecoders() {
+            return new ChannelHandler[] { new ProtocolMessageDecoder(this.msgFactory) };
+        }
     }
 }
index 890bbe728804e469f0d2989253e6815ec1b85d06..b334c354fbd8749026e7a01447a775aee2d74c05 100644 (file)
@@ -16,7 +16,6 @@ import org.opendaylight.controller.netconf.impl.NetconfServerSessionListenerFact
 import org.opendaylight.controller.netconf.impl.NetconfServerSessionNegotiatorFactory;
 import org.opendaylight.controller.netconf.impl.SessionIdProvider;
 import org.opendaylight.controller.netconf.util.osgi.NetconfConfigUtil;
-import org.opendaylight.controller.netconf.util.osgi.NetconfConfigUtil.TLSConfiguration;
 import org.osgi.framework.BundleActivator;
 import org.osgi.framework.BundleContext;
 import org.slf4j.Logger;
@@ -30,9 +29,6 @@ public class NetconfImplActivator implements BundleActivator {
 
     private static final Logger logger = LoggerFactory.getLogger(NetconfImplActivator.class);
 
-    private Optional<InetSocketAddress> maybeTCPAddress;
-    private Optional<TLSConfiguration> maybeTLSConfiguration;
-
     private NetconfOperationServiceFactoryTracker factoriesTracker;
     private DefaultCommitNotificationProducer commitNot;
     private NetconfServerDispatcher dispatch;
@@ -41,11 +37,8 @@ public class NetconfImplActivator implements BundleActivator {
 
     @Override
     public void start(final BundleContext context) throws Exception {
-        maybeTCPAddress = NetconfConfigUtil.extractTCPNetconfAddress(context);
-        maybeTLSConfiguration = NetconfConfigUtil.extractTLSConfiguration(context);
-        if (maybeTCPAddress.isPresent() == false && maybeTLSConfiguration.isPresent() == false) {
-            throw new IllegalStateException("TCP nor TLS is configured, netconf not available.");
-        }
+        InetSocketAddress address = NetconfConfigUtil.extractTCPNetconfAddress(context, "TCP is not configured, netconf not available.");
+
         NetconfOperationServiceFactoryListenerImpl factoriesListener = new NetconfOperationServiceFactoryListenerImpl();
         factoriesTracker = new NetconfOperationServiceFactoryTracker(context, factoriesListener);
         factoriesTracker.open();
@@ -62,26 +55,13 @@ public class NetconfImplActivator implements BundleActivator {
 
         eventLoopGroup = new NioEventLoopGroup();
 
-        if (maybeTCPAddress.isPresent()) {
-            Optional<SSLContext> maybeSSLContext = Optional.absent();
-            InetSocketAddress address = maybeTCPAddress.get();
-            NetconfServerDispatcher.ServerSslChannelInitializer serverChannelInitializer = new NetconfServerDispatcher.ServerSslChannelInitializer(
-                    maybeSSLContext, serverNegotiatorFactory, listenerFactory);
-            dispatch = new NetconfServerDispatcher(serverChannelInitializer, eventLoopGroup, eventLoopGroup);
-
-            logger.info("Starting TCP netconf server at {}", address);
-            dispatch.createServer(address);
-        }
-        if (maybeTLSConfiguration.isPresent()) {
-            Optional<SSLContext> maybeSSLContext = Optional.of(maybeTLSConfiguration.get().getSslContext());
-            InetSocketAddress address = maybeTLSConfiguration.get().getAddress();
-            NetconfServerDispatcher.ServerSslChannelInitializer serverChannelInitializer = new NetconfServerDispatcher.ServerSslChannelInitializer(
-                    maybeSSLContext, serverNegotiatorFactory, listenerFactory);
-            dispatch = new NetconfServerDispatcher(serverChannelInitializer, eventLoopGroup, eventLoopGroup);
-
-            logger.info("Starting TLS netconf server at {}", address);
-            dispatch.createServer(address);
-        }
+        NetconfServerDispatcher.ServerSslChannelInitializer serverChannelInitializer = new NetconfServerDispatcher.ServerSslChannelInitializer(
+                Optional.<SSLContext>absent(), serverNegotiatorFactory, listenerFactory);
+        dispatch = new NetconfServerDispatcher(serverChannelInitializer, eventLoopGroup, eventLoopGroup);
+
+        logger.info("Starting TCP netconf server at {}", address);
+        dispatch.createServer(address);
+
     }
 
     @Override
index 13b0a1e570b122ae04a94e5b626b27e7c93f7b88..410d9a96aa4a254b043901e74d3e19d8b049ead3 100644 (file)
             <artifactId>yang-store-api</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>yang-test</artifactId>
+            <scope>test</scope>
+        </dependency>
         <dependency>
             <groupId>${project.groupId}</groupId>
             <artifactId>netconf-api</artifactId>
             <artifactId>config-netconf-connector</artifactId>
             <scope>test</scope>
         </dependency>
-        <dependency>
-            <groupId>${project.groupId}</groupId>
-            <artifactId>yang-test</artifactId>
-            <scope>test</scope>
-        </dependency>
         <dependency>
             <groupId>${project.groupId}</groupId>
             <artifactId>config-manager</artifactId>
             <artifactId>netconf-mapping-api</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>netconf-ssh</artifactId>
+            <scope>test</scope>
+        </dependency>
         <dependency>
             <groupId>${project.groupId}</groupId>
             <artifactId>netconf-util</artifactId>
                             <goal>test</goal>
                         </goals>
                         <configuration>
-                            <includes>
-                                <include>**/org/opendaylight/controller/netconf/it/*.java</include>
-                            </includes>
                             <skip>false</skip>
+                            <argLine>-Dlogback.configurationFile=${maven.test.dest}/logback.xml</argLine>
                         </configuration>
                     </execution>
                 </executions>
index c03254dba2bbda03677efc89c4eadd39e2024acc..d08bb957e1cbd60aec752b1e9507870e3a28db03 100644 (file)
@@ -8,6 +8,8 @@
 
 package org.opendaylight.controller.netconf.it;
 
+import ch.ethz.ssh2.Connection;
+import ch.ethz.ssh2.Session;
 import com.google.common.base.Optional;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
@@ -15,6 +17,20 @@ import io.netty.channel.ChannelFuture;
 import io.netty.channel.EventLoopGroup;
 import io.netty.channel.nio.NioEventLoopGroup;
 import io.netty.util.HashedWheelTimer;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.management.ManagementFactory;
+import java.net.InetSocketAddress;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+import javax.management.ObjectName;
+import javax.net.ssl.SSLContext;
+import javax.xml.parsers.ParserConfigurationException;
+import junit.framework.Assert;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Ignore;
@@ -49,43 +65,34 @@ import org.opendaylight.controller.netconf.impl.mapping.ExiDecoderHandler;
 import org.opendaylight.controller.netconf.impl.mapping.ExiEncoderHandler;
 import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceFactoryListenerImpl;
 import org.opendaylight.controller.netconf.persist.impl.ConfigPersisterNotificationHandler;
+import org.opendaylight.controller.netconf.ssh.NetconfSSHServer;
 import org.opendaylight.controller.netconf.util.test.XmlFileLoader;
 import org.opendaylight.controller.netconf.util.xml.ExiParameters;
 import org.opendaylight.controller.netconf.util.xml.XmlElement;
 import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 import org.w3c.dom.NamedNodeMap;
 import org.w3c.dom.Node;
 import org.xml.sax.SAXException;
-
-import javax.management.ObjectName;
-import javax.net.ssl.SSLContext;
-import javax.xml.parsers.ParserConfigurationException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.lang.management.ManagementFactory;
-import java.net.InetSocketAddress;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.TimeUnit;
-
+import static java.util.Collections.emptyList;
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertTrue;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
-import static org.mockito.internal.util.Checks.checkNotNull;
 
 public class NetconfITTest extends AbstractConfigTest {
 
-    // private static final Logger logger =
-    // LoggerFactory.getLogger(NetconfITTest.class);
+     private static final Logger logger =  LoggerFactory.getLogger(NetconfITTest.class);
     //
 
     private static final InetSocketAddress tcpAddress = new InetSocketAddress("127.0.0.1", 12023);
+    private static final InetSocketAddress sshAddress = new InetSocketAddress("127.0.0.1", 10830);
+    private static final String USERNAME = "netconf";
+    private static final String PASSWORD = "netconf";
 
     private NetconfMessage getConfig, getConfigCandidate, editConfig,
             closeSession, startExi, stopExi;
@@ -95,6 +102,7 @@ public class NetconfITTest extends AbstractConfigTest {
 
     private NetconfClientDispatcher clientDispatcher;
 
+
     @Before
     public void setUp() throws Exception {
         super.initConfigTransactionManagerImpl(new HardcodedModuleFactoriesResolver(getModuleFactories().toArray(
@@ -158,10 +166,16 @@ public class NetconfITTest extends AbstractConfigTest {
                 "/META-INF/yang/config-test.yang", "/META-INF/yang/config-test-impl.yang",
                 "/META-INF/yang/ietf-inet-types.yang");
         final Collection<InputStream> yangDependencies = new ArrayList<>();
+        List<String> failedToFind = new ArrayList<>();
         for (String path : paths) {
-            final InputStream is = checkNotNull(NetconfITTest.class.getResourceAsStream(path), path + " not found");
-            yangDependencies.add(is);
+            InputStream resourceAsStream = NetconfITTest.class.getResourceAsStream(path);
+            if (resourceAsStream == null) {
+                failedToFind.add(path);
+            } else {
+                yangDependencies.add(resourceAsStream);
+            }
         }
+        assertEquals("Some yang files were not found",emptyList(), failedToFind);
         return yangDependencies;
     }
 
@@ -190,8 +204,8 @@ public class NetconfITTest extends AbstractConfigTest {
 
     @Test
     public void testTwoSessions() throws Exception {
-        try (NetconfClient netconfClient = new NetconfClient("1", tcpAddress, 4000, clientDispatcher))  {
-            try (NetconfClient netconfClient2 = new NetconfClient("2", tcpAddress, 4000, clientDispatcher))  {
+        try (NetconfClient netconfClient = new NetconfClient("1", tcpAddress, 10000, clientDispatcher))  {
+            try (NetconfClient netconfClient2 = new NetconfClient("2", tcpAddress, 10000, clientDispatcher))  {
             }
         }
     }
@@ -445,4 +459,46 @@ public class NetconfITTest extends AbstractConfigTest {
         return netconfClient;
     }
 
+    private void startSSHServer() throws Exception{
+        logger.info("Creating SSH server");
+        Thread sshServerThread = new Thread(NetconfSSHServer.start(10830,tcpAddress));
+        sshServerThread.setDaemon(true);
+        sshServerThread.start();
+        logger.info("SSH server on");
+    }
+
+    @Test
+    public void sshTest() throws Exception {
+        startSSHServer();
+        logger.info("creating connection");
+        Connection conn = new Connection(sshAddress.getHostName(),sshAddress.getPort());
+        Assert.assertNotNull(conn);
+        logger.info("connection created");
+        conn.connect();
+        boolean isAuthenticated = conn.authenticateWithPassword(USERNAME,PASSWORD);
+        assertTrue(isAuthenticated);
+        logger.info("user authenticated");
+        final Session sess = conn.openSession();
+        sess.startSubSystem("netconf");
+        logger.info("user authenticated");
+        sess.getStdin().write(XmlUtil.toString(this.getConfig.getDocument()).getBytes());
+
+        new Thread(){
+           public void run(){
+               while (true){
+                 byte[] bytes = new byte[1024];
+                   int c = 0;
+                   try {
+                       c = sess.getStdout().read(bytes);
+                   } catch (IOException e) {
+                       e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
+                   }
+                   logger.info("got data:"+bytes);
+                 if (c == 0) break;
+               }
+           }
+        }.join();
+    }
+
+
 }
diff --git a/opendaylight/netconf/netconf-it/src/test/resources/logback.xml b/opendaylight/netconf/netconf-it/src/test/resources/logback.xml
new file mode 100644 (file)
index 0000000..fa467a1
--- /dev/null
@@ -0,0 +1,15 @@
+<configuration scan="true">
+
+    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+        <encoder>
+            <pattern>%date{"yyyy-MM-dd HH:mm:ss.SSS z"} [%thread] %-5level %logger{36} - %msg%n</pattern>
+        </encoder>
+    </appender>
+
+  <logger name="org.opendaylight.controller.netconf" level="DEBUG"/>
+
+  <root level="error">
+    <appender-ref ref="STDOUT" />
+  </root>
+
+</configuration>
diff --git a/opendaylight/netconf/netconf-ssh/pom.xml b/opendaylight/netconf/netconf-ssh/pom.xml
new file mode 100644 (file)
index 0000000..794bb16
--- /dev/null
@@ -0,0 +1,84 @@
+<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.controller</groupId>
+        <version>0.2.3-SNAPSHOT</version>
+        <relativePath>../</relativePath>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>netconf-ssh</artifactId>
+    <name>${project.artifactId}</name>
+    <packaging>bundle</packaging>
+
+
+    <dependencies>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>netconf-util</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>netconf-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.controller.thirdparty</groupId>
+            <artifactId>ganymed</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>commons-io</groupId>
+            <artifactId>commons-io</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <Bundle-Activator>org.opendaylight.controller.netconf.osgi.NetconfSSHActivator</Bundle-Activator>
+                        <Export-Package>
+                            org.opendaylight.controller.netconf.ssh,
+                        </Export-Package>
+                        <Import-Package>
+                            com.google.common.base,
+                            com.google.common.collect,
+                            ch.ethz.ssh2,
+                            ch.ethz.ssh2.signature,
+                            io.netty.buffer,
+                            io.netty.channel,
+                            io.netty.channel.nio,
+                            io.netty.channel.socket,
+                            io.netty.util,
+                            io.netty.util.concurrent,
+                            javax.annotation,
+                            java.net,
+                            javax.net.ssl,
+                            javax.xml.namespace,
+                            javax.xml.parsers,
+                            javax.xml.xpath,
+                            org.apache.commons.io,
+                            org.opendaylight.controller.netconf.api,
+                            org.opendaylight.controller.netconf.client,
+                            org.opendaylight.controller.netconf.util,
+                            org.opendaylight.controller.netconf.util.osgi,
+                            org.opendaylight.controller.netconf.util.xml,
+                            org.opendaylight.protocol.framework,
+                            org.osgi.framework,
+                            org.slf4j,
+                            org.w3c.dom,
+                            org.xml.sax
+                        </Import-Package>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/osgi/NetconfSSHActivator.java b/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/osgi/NetconfSSHActivator.java
new file mode 100644 (file)
index 0000000..d2f6c8c
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * 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.osgi;
+
+import com.google.common.base.Optional;
+import java.net.InetSocketAddress;
+import org.opendaylight.controller.netconf.ssh.NetconfSSHServer;
+import org.opendaylight.controller.netconf.util.osgi.NetconfConfigUtil;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Activator for netconf SSH bundle which creates SSH bridge between netconf client and netconf server. Activator
+ * starts SSH Server in its own thread. This thread is closed when activator calls stop() method. Server opens socket
+ * and listen for client connections. Each client connection creation is handled in separate
+ * {@link org.opendaylight.controller.netconf.ssh.threads.SocketThread} thread.
+ * This thread creates two additional threads {@link org.opendaylight.controller.netconf.ssh.threads.IOThread}
+ * forwarding data from/to client.IOThread closes servers session and server connection when it gets -1 on input stream.
+ * {@link org.opendaylight.controller.netconf.ssh.threads.IOThread}'s run method waits for -1 on input stream to finish.
+ * All threads are daemons.
+ **/
+public class NetconfSSHActivator implements BundleActivator{
+
+    private NetconfSSHServer server;
+    private static final Logger logger =  LoggerFactory.getLogger(NetconfSSHActivator.class);
+
+    @Override
+    public void start(BundleContext context) throws Exception {
+
+        logger.trace("Starting netconf SSH  bridge.");
+
+        Optional<InetSocketAddress> sshSocketAddressOptional = NetconfConfigUtil.extractSSHNetconfAddress(context);
+        InetSocketAddress tcpSocketAddress = NetconfConfigUtil.extractTCPNetconfAddress(context,
+                "TCP is not configured, netconf ssh bridge is not available.");
+
+        if (sshSocketAddressOptional.isPresent()){
+            server = NetconfSSHServer.start(sshSocketAddressOptional.get().getPort(),tcpSocketAddress);
+            Thread serverThread = new  Thread(server,"netconf SSH server thread");
+            serverThread.setDaemon(true);
+            serverThread.start();
+            logger.trace("Netconf SSH  bridge up and running.");
+        } else {
+            logger.trace("No valid connection configuration for SSH bridge found.");
+            throw new Exception("No valid connection configuration for SSH bridge found.");
+        }
+    }
+
+    @Override
+    public void stop(BundleContext context) throws Exception {
+        if (server != null){
+            logger.trace("Netconf SSH bridge going down ...");
+            server.stop();
+            logger.trace("Netconf SSH bridge is down ...");
+        }
+    }
+}
diff --git a/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/NetconfSSHServer.java b/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/NetconfSSHServer.java
new file mode 100644 (file)
index 0000000..72135cc
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * 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.ssh;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.ServerSocket;
+import java.util.concurrent.atomic.AtomicLong;
+import javax.annotation.concurrent.ThreadSafe;
+import org.opendaylight.controller.netconf.ssh.threads.SocketThread;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@ThreadSafe
+public class NetconfSSHServer implements Runnable {
+
+    private static boolean acceptMore = true;
+    private ServerSocket ss = null;
+    private static final Logger logger =  LoggerFactory.getLogger(NetconfSSHServer.class);
+    private static final AtomicLong sesssionId = new AtomicLong();
+    private final InetSocketAddress clientAddress;
+
+    private NetconfSSHServer(int serverPort,InetSocketAddress clientAddress) throws Exception{
+
+        logger.trace("Creating SSH server socket on port {}",serverPort);
+        this.ss = new ServerSocket(serverPort);
+        if (!ss.isBound()){
+            throw new Exception("Socket can't be bound to requested port :"+serverPort);
+        }
+        logger.trace("Server socket created.");
+        this.clientAddress = clientAddress;
+
+    }
+
+
+    public static NetconfSSHServer start(int serverPort, InetSocketAddress clientAddress) throws Exception {
+        return new NetconfSSHServer(serverPort, clientAddress);
+    }
+
+    public void stop() throws Exception {
+        acceptMore = false;
+        logger.trace("Closing SSH server socket.");
+        ss.close();
+        logger.trace("SSH server socket closed.");
+    }
+
+    @Override
+    public void run() {
+        while (acceptMore) {
+            logger.trace("Starting new socket thread.");
+            try {
+               SocketThread.start(ss.accept(), clientAddress, sesssionId.incrementAndGet());
+            } catch (IOException e) {
+                e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
+            }
+        }
+    }
+}
diff --git a/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/authentication/KeyStoreHandler.java b/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/authentication/KeyStoreHandler.java
new file mode 100644 (file)
index 0000000..59a911b
--- /dev/null
@@ -0,0 +1,14 @@
+/*
+ * 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.ssh.authentication;
+
+import ch.ethz.ssh2.signature.RSAPrivateKey;
+
+public interface KeyStoreHandler {
+    public RSAPrivateKey getPrivateKey();
+}
diff --git a/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/authentication/RSAKey.java b/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/authentication/RSAKey.java
new file mode 100644 (file)
index 0000000..b420b33
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * 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.ssh.authentication;
+
+import ch.ethz.ssh2.signature.RSAPrivateKey;
+
+import java.math.BigInteger;
+
+public class RSAKey implements KeyStoreHandler {
+
+    private static RSAPrivateKey hostkey = null;
+    private static String user = "netconf";
+    private static String password = "netconf";
+    static {
+
+        BigInteger p = new BigInteger("2967886344240998436887630478678331145236162666668503940430852241825039192450179076148979094256007292741704260675085192441025058193581327559331546948442042987131728039318861235625879376246169858586459472691398815098207618446039");    //.BigInteger.probablePrime(N / 2, rnd);
+        BigInteger q = new BigInteger("4311534819291430017572425052029278681302539382618633848168923130451247487970187151403375389974616614405320169278870943605377518341666894603659873284783174749122655429409273983428000534304828056597676444751611433784228298909767"); //BigInteger.probablePrime(N / 2, rnd);
+        BigInteger phi = (p.subtract(BigInteger.ONE)).multiply(q.subtract(BigInteger.ONE));
+
+        BigInteger n = p.multiply(q);
+        BigInteger e = new BigInteger("65537");
+        BigInteger d = e.modInverse(phi);
+
+        hostkey = new RSAPrivateKey(d, e, n);
+    }
+
+    @Override
+    public RSAPrivateKey getPrivateKey() {
+        return hostkey;
+    }
+}
diff --git a/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/threads/IOThread.java b/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/threads/IOThread.java
new file mode 100644 (file)
index 0000000..33ed88e
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * 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.ssh.threads;
+
+import ch.ethz.ssh2.ServerConnection;
+import ch.ethz.ssh2.ServerSession;
+import java.io.InputStream;
+import java.io.OutputStream;
+import javax.annotation.concurrent.ThreadSafe;
+import org.apache.commons.io.IOUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@ThreadSafe
+public class IOThread extends Thread {
+
+    private static final Logger logger =  LoggerFactory.getLogger(IOThread.class);
+
+    private InputStream inputStream;
+    private OutputStream outputStream;
+    private String id;
+    private ServerSession servSession;
+    private ServerConnection servconnection;
+
+
+    public IOThread (InputStream is, OutputStream os, String id,ServerSession ss, ServerConnection conn){
+        this.inputStream = is;
+        this.outputStream = os;
+        this.servSession = ss;
+        this.servconnection = conn;
+        super.setName(id);
+        logger.trace("IOThread {} created", super.getName());
+    }
+
+    @Override
+    public void run() {
+        logger.trace("thread {} started", super.getName());
+        try {
+            IOUtils.copy(this.inputStream, this.outputStream);
+        } catch (Exception e) {
+            logger.error("inputstream -> outputstream copy error ",e);
+        }
+        logger.trace("closing server session");
+        servSession.close();
+        servconnection.close();
+        logger.trace("thread {} is closing",super.getName());
+    }
+}
diff --git a/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/threads/SocketThread.java b/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/threads/SocketThread.java
new file mode 100644 (file)
index 0000000..95fdd48
--- /dev/null
@@ -0,0 +1,176 @@
+package org.opendaylight.controller.netconf.ssh.threads;
+
+
+import ch.ethz.ssh2.AuthenticationResult;
+import ch.ethz.ssh2.PtySettings;
+import ch.ethz.ssh2.ServerAuthenticationCallback;
+import ch.ethz.ssh2.ServerConnection;
+import ch.ethz.ssh2.ServerConnectionCallback;
+import ch.ethz.ssh2.ServerSession;
+import ch.ethz.ssh2.ServerSessionCallback;
+import ch.ethz.ssh2.SimpleServerSessionCallback;
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import javax.annotation.concurrent.ThreadSafe;
+import org.opendaylight.controller.netconf.ssh.authentication.RSAKey;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@ThreadSafe
+public class SocketThread implements Runnable, ServerAuthenticationCallback, ServerConnectionCallback
+{
+
+    private Socket socket;
+    private static final String USER = "netconf";
+    private static final String PASSWORD = "netconf";
+    private InetSocketAddress clientAddress;
+    private static final Logger logger =  LoggerFactory.getLogger(SocketThread.class);
+    private ServerConnection conn = null;
+    private long sessionId;
+
+
+    public static void start(Socket socket, InetSocketAddress clientAddress, long sessionId) throws IOException{
+        Thread netconf_ssh_socket_thread = new Thread(new SocketThread(socket,clientAddress,sessionId));
+        netconf_ssh_socket_thread.setDaemon(true);
+        netconf_ssh_socket_thread.start();
+    }
+    private SocketThread(Socket socket, InetSocketAddress clientAddress, long sessionId) throws IOException {
+
+        this.socket = socket;
+        this.clientAddress = clientAddress;
+        this.sessionId = sessionId;
+
+    }
+
+    @Override
+    public void run() {
+        conn = new ServerConnection(socket);
+        RSAKey keyStore = new RSAKey();
+        conn.setRsaHostKey(keyStore.getPrivateKey());
+        conn.setAuthenticationCallback(this);
+        conn.setServerConnectionCallback(this);
+        try {
+            conn.connect();
+        } catch (IOException e) {
+            logger.error("SocketThread error ",e);
+        }
+    }
+    public ServerSessionCallback acceptSession(final ServerSession session)
+    {
+        SimpleServerSessionCallback cb = new SimpleServerSessionCallback()
+        {
+            @Override
+            public Runnable requestSubsystem(final ServerSession ss, final String subsystem) throws IOException
+            {
+                return new Runnable(){
+                    public void run()
+                    {
+                        if (subsystem.equals("netconf")){
+                            IOThread netconf_ssh_input = null;
+                            IOThread  netconf_ssh_output = null;
+                            try {
+                                String hostName = clientAddress.getHostName();
+                                int portNumber = clientAddress.getPort();
+                                final Socket echoSocket = new Socket(hostName, portNumber);
+                                logger.trace("echo socket created");
+
+                                logger.trace("starting netconf_ssh_input thread");
+                                netconf_ssh_input =  new IOThread(echoSocket.getInputStream(),ss.getStdin(),"input_thread_"+sessionId,ss,conn);
+                                netconf_ssh_input.setDaemon(false);
+                                netconf_ssh_input.start();
+
+                                logger.trace("starting netconf_ssh_output thread");
+                                netconf_ssh_output = new IOThread(ss.getStdout(),echoSocket.getOutputStream(),"output_thread_"+sessionId,ss,conn);
+                                netconf_ssh_output.setDaemon(false);
+                                netconf_ssh_output.start();
+
+                            } catch (Throwable t){
+                                logger.error(t.getMessage(),t);
+
+                                try {
+                                    if (netconf_ssh_input!=null){
+                                        netconf_ssh_input.join();
+                                    }
+                                } catch (InterruptedException e) {
+                                   logger.error("netconf_ssh_input join error ",e);
+                                }
+
+                                try {
+                                    if (netconf_ssh_output!=null){
+                                        netconf_ssh_output.join();
+                                    }
+                                } catch (InterruptedException e) {
+                                    logger.error("netconf_ssh_output join error ",e);
+                                }
+
+                            }
+                        } else {
+                            try {
+                                ss.getStdin().write("wrong subsystem requested - closing connection".getBytes());
+                                ss.close();
+                            } catch (IOException e) {
+                                logger.debug("excpetion while sending bad subsystem response",e);
+                            }
+                        }
+                    }
+                };
+            }
+            @Override
+            public Runnable requestPtyReq(final ServerSession ss, final PtySettings pty) throws IOException
+            {
+                return new Runnable()
+                {
+                    public void run()
+                    {
+                        //noop
+                    }
+                };
+            }
+
+            @Override
+            public Runnable requestShell(final ServerSession ss) throws IOException
+            {
+                return new Runnable()
+                {
+                    public void run()
+                    {
+                        //noop
+                    }
+                };
+            }
+        };
+
+        return cb;
+    }
+
+    public String initAuthentication(ServerConnection sc)
+    {
+        return "";
+    }
+
+    public String[] getRemainingAuthMethods(ServerConnection sc)
+    {
+        return new String[] { ServerAuthenticationCallback.METHOD_PASSWORD };
+    }
+
+    public AuthenticationResult authenticateWithNone(ServerConnection sc, String username)
+    {
+        return AuthenticationResult.FAILURE;
+    }
+
+    public AuthenticationResult authenticateWithPassword(ServerConnection sc, String username, String password)
+    {
+        if (USER.equals(username) && PASSWORD.equals(password))
+            return AuthenticationResult.SUCCESS;
+
+        return AuthenticationResult.FAILURE;
+    }
+
+    public AuthenticationResult authenticateWithPublicKey(ServerConnection sc, String username, String algorithm,
+            byte[] publickey, byte[] signature)
+    {
+        return AuthenticationResult.FAILURE;
+    }
+
+}
diff --git a/opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/ssh/SSHServerTest.java b/opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/ssh/SSHServerTest.java
new file mode 100644 (file)
index 0000000..54bc7bc
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * 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.ssh;
+
+import ch.ethz.ssh2.Connection;
+import ch.ethz.ssh2.Session;
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import junit.framework.Assert;
+import org.apache.commons.io.IOUtils;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+public class SSHServerTest {
+
+    private static final String USER = "netconf";
+    private static final String PASSWORD  = "netconf";
+    private static final String HOST = "127.0.0.1";
+    private static final int PORT = 1830;
+    private static final InetSocketAddress tcpAddress = new InetSocketAddress("127.0.0.1", 8383);
+    private static final Logger logger =  LoggerFactory.getLogger(SSHServerTest.class);
+
+//    @Before
+    public void startSSHServer() throws Exception{
+            logger.info("Creating SSH server");
+            NetconfSSHServer server = NetconfSSHServer.start(PORT,tcpAddress);
+            Thread sshServerThread = new Thread(server);
+            sshServerThread.setDaemon(true);
+            sshServerThread.start();
+            logger.info("SSH server on");
+    }
+
+    @Test
+    public void connect(){
+        Connection conn = new Connection(HOST,PORT);
+        Assert.assertNotNull(conn);
+        try {
+            logger.info("connecting to SSH server");
+            conn.connect();
+            logger.info("authenticating ...");
+            boolean isAuthenticated = conn.authenticateWithPassword(USER,PASSWORD);
+            Assert.assertTrue(isAuthenticated);
+            logger.info("opening session");
+            Session sess = conn.openSession();
+            logger.info("subsystem netconf");
+            sess.startSubSystem("netconf");
+            sess.getStdin().write("<?xml version=\"1.0\" encoding=\"UTF-8\"?><hello xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\"><capabilities><capability>urn:ietf:params:netconf:base:1.1</capability></capabilities></hello>]]>]]>".getBytes());
+            IOUtils.copy(sess.getStdout(), System.out);
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+}
index c19506b236a2d8b9cacdc7e03cc263f20a131bfb..353dd1aae70ba8eac0b8cb2fa9ca4ab21da5ba10 100644 (file)
@@ -76,6 +76,7 @@
                             org.opendaylight.controller.netconf.util.mapping,
                             org.opendaylight.controller.netconf.util.messages,
                             org.opendaylight.controller.netconf.util.handler,
+                            org.opendaylight.controller.netconf.util.handler.*,
                         </Export-Package>
                         <Import-Package>
                             com.google.common.base,
index b911989c646b64b139dd4a678cbd0c8adc03596d..0d9096c02a3abd1b33204665cb72dc4cbd10e805 100644 (file)
@@ -8,13 +8,16 @@
 
 package org.opendaylight.controller.netconf.util.handler.ssh;
 
+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.util.handler.ssh.authentication.AuthenticationHandler;
 import org.opendaylight.controller.netconf.util.handler.ssh.client.Invoker;
 import org.opendaylight.controller.netconf.util.handler.ssh.client.SshClient;
@@ -50,7 +53,7 @@ public class SshHandler extends ChannelOutboundHandlerAdapter {
 
     @Override
     public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
-        this.sshClientAdapter.write((String) msg);
+        this.sshClientAdapter.write((ByteBuf) msg);
     }
 
     @Override
index bb0d37899d7bfd0d5486d4ad22872f49313d4309..2f1b260bd087fd0f48dccdb0af1911157395d7cf 100644 (file)
@@ -13,7 +13,8 @@ import ch.ethz.ssh2.Connection;
 import java.io.IOException;
 
 /**
- * Class Providing username/password authentication option to {@link org.opendaylight.controller.netconf.util.handler.ssh.SshHandler}
+ * Class Providing username/password authentication option to
+ * {@link org.opendaylight.controller.netconf.util.handler.ssh.SshHandler}
  */
 public class LoginPassword extends AuthenticationHandler {
     private final String username;
@@ -28,6 +29,7 @@ public class LoginPassword extends AuthenticationHandler {
     public void authenticate(Connection connection) throws IOException {
         boolean isAuthenticated = connection.authenticateWithPassword(username, password);
 
-        if (isAuthenticated == false) throw new IOException("Authentication failed.");
+        if (isAuthenticated == false)
+            throw new IOException("Authentication failed.");
     }
 }
index c43aa6f3e59db1a57cf5a4166e61bfb0416a310f..3cb608db6a87c3ef121cc6a4edf0f0676fab9912 100644 (file)
@@ -18,7 +18,6 @@ import java.io.IOException;
 import java.util.HashMap;
 import java.util.Map;
 
-
 /**
  * Wrapper class around GANYMED SSH java library.
  */
@@ -28,16 +27,16 @@ public class SshClient {
     private final AuthenticationHandler authenticationHandler;
     private Connection connection;
 
-    public SshClient(VirtualSocket socket,
-                     AuthenticationHandler authenticationHandler) throws IOException {
+    public SshClient(VirtualSocket socket, AuthenticationHandler authenticationHandler) throws IOException {
         this.socket = socket;
         this.authenticationHandler = authenticationHandler;
     }
 
     public SshSession openSession() throws IOException {
-        if(connection == null) connect();
+        if (connection == null)
+            connect();
 
-        Session session =  connection.openSession();
+        Session session = connection.openSession();
         SshSession sshSession = new SshSession(session);
         openSessions.put(openSessions.size(), sshSession);
 
@@ -46,22 +45,24 @@ public class SshClient {
 
     private void connect() throws IOException {
         connection = new Connection(socket);
+
         connection.connect();
         authenticationHandler.authenticate(connection);
     }
 
     public void closeSession(SshSession session) {
-        if(   session.getState() == Channel.STATE_OPEN
-           || session.getState() == Channel.STATE_OPENING) {
+        if (session.getState() == Channel.STATE_OPEN || session.getState() == Channel.STATE_OPENING) {
             session.session.close();
         }
     }
 
     public void close() {
-        for(SshSession session : openSessions.values()) closeSession(session);
+        for (SshSession session : openSessions.values())
+            closeSession(session);
 
         openSessions.clear();
 
-        if(connection != null) connection.close();
+        if (connection != null)
+            connection.close();
     }
 }
index a50462e40dae1e3c5ce40afd451713992ad28e2a..4213fe3e0642db6257b3e655c407a4394785fca3 100644 (file)
@@ -12,14 +12,19 @@ import io.netty.buffer.ByteBuf;
 import io.netty.buffer.Unpooled;
 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.opendaylight.controller.netconf.util.handler.ssh.virtualsocket.VirtualSocketException;
 
+
 /**
- * Worker thread class. Handles all downstream and upstream events in SSH Netty pipeline.
+ * Worker thread class. Handles all downstream and upstream events in SSH Netty
+ * pipeline.
  */
 public class SshClientAdapter implements Runnable {
     private final SshClient sshClient;
@@ -30,6 +35,9 @@ public class SshClientAdapter implements Runnable {
     private InputStream stdErr;
     private OutputStream stdIn;
 
+    private Queue<ByteBuf> postponned = new LinkedList<>();
+
+
     private ChannelHandlerContext ctx;
     private ChannelPromise disconnectPromise;
 
@@ -37,8 +45,7 @@ public class SshClientAdapter implements Runnable {
 
     private final Object lock = new Object();
 
-    public SshClientAdapter(SshClient sshClient,
-                            Invoker invoker) {
+    public SshClientAdapter(SshClient sshClient, Invoker invoker) {
         this.sshClient = sshClient;
         this.invoker = invoker;
     }
@@ -47,18 +54,24 @@ public class SshClientAdapter implements Runnable {
         try {
             session = sshClient.openSession();
             invoker.invoke(session);
-
             stdOut = session.getStdout();
             stdErr = session.getStderr();
 
-            synchronized(lock) {
+            synchronized (lock) {
+
                 stdIn = session.getStdin();
+                ByteBuf message = null;
+                while ((message = postponned.poll()) != null) {
+                    writeImpl(message);
+                }
             }
 
             while (stopRequested.get() == false) {
                 byte[] readBuff = new byte[1024];
                 int c = stdOut.read(readBuff);
-
+                if (c == -1) {
+                    continue;
+                }
                 byte[] tranBuff = new byte[c];
                 System.arraycopy(readBuff, 0, tranBuff, 0, c);
 
@@ -76,17 +89,25 @@ public class SshClientAdapter implements Runnable {
             sshClient.close();
 
             synchronized (lock) {
-                if(disconnectPromise != null) ctx.disconnect(disconnectPromise);
+                if (disconnectPromise != null)
+                    ctx.disconnect(disconnectPromise);
             }
         }
     }
 
     // TODO: needs rework to match netconf framer API.
-    public void write(String message) throws IOException {
+    public void write(ByteBuf message) throws IOException {
         synchronized (lock) {
-            if (stdIn == null) throw new IllegalStateException("StdIn not available");
+            if (stdIn == null) {
+                postponned.add(message);
+                return;
+            }
+            writeImpl(message);
         }
-        stdIn.write(message.getBytes());
+    }
+
+    private void writeImpl(ByteBuf message) throws IOException {
+        message.getBytes(0, stdIn, message.readableBytes());
         stdIn.flush();
     }
 
@@ -98,8 +119,8 @@ public class SshClientAdapter implements Runnable {
     }
 
     public void start(ChannelHandlerContext ctx) {
-        if(this.ctx != null) return; // context is already associated.
-
+        if (this.ctx != null)
+            return; // context is already associated.
         this.ctx = ctx;
         new Thread(this).start();
     }
index 76068399c17f470b249c0ba35027f89d723b5d81..35e17a2a3e4564ffceac32158d00e5f7e6faba39 100644 (file)
@@ -26,7 +26,7 @@ public class NetconfConfigUtil {
     private static final String PREFIX_PROP = "netconf.";
 
     private enum InfixProp {
-        tcp, tls
+        tcp, tls, ssh
     }
 
     private static final String PORT_SUFFIX_PROP = ".port";
@@ -35,10 +35,20 @@ public class NetconfConfigUtil {
     private static final String NETCONF_TLS_KEYSTORE_PROP = PREFIX_PROP + InfixProp.tls + ".keystore";
     private static final String NETCONF_TLS_KEYSTORE_PASSWORD_PROP = NETCONF_TLS_KEYSTORE_PROP + ".password";
 
-    public static Optional<InetSocketAddress> extractTCPNetconfAddress(BundleContext context) {
-        return extractSomeNetconfAddress(context, InfixProp.tcp);
+    public static InetSocketAddress extractTCPNetconfAddress(BundleContext context, String exceptionMessageIfNotFound) {
+
+        Optional<InetSocketAddress> inetSocketAddressOptional = extractSomeNetconfAddress(context, InfixProp.tcp);
+        if (inetSocketAddressOptional.isPresent() == false) {
+            throw new IllegalStateException("Netconf tcp address not found." + exceptionMessageIfNotFound);
+        }
+        return inetSocketAddressOptional.get();
     }
 
+    public static Optional<InetSocketAddress> extractSSHNetconfAddress(BundleContext context) {
+        return extractSomeNetconfAddress(context, InfixProp.ssh);
+    }
+
+
     public static Optional<TLSConfiguration> extractTLSConfiguration(BundleContext context) {
         Optional<InetSocketAddress> address = extractSomeNetconfAddress(context, InfixProp.tls);
         if (address.isPresent()) {
index b22732e630bf0b5263d1221c9684faf2e0a14f52..ad8356431ecc4777bfee0bd1b1e7717c1a7af039 100644 (file)
@@ -26,6 +26,7 @@
         <module>config-persister-impl</module>
         <module>netconf-mapping-api</module>
         <module>netconf-client</module>
+        <module>netconf-ssh</module>
         <module>../../third-party/ganymed</module>
         <module>../../third-party/com.siemens.ct.exi</module>
     </modules>
                 <version>${netconf.version}</version>
                 <type>test-jar</type>
             </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>netconf-ssh</artifactId>
+                <version>${netconf.version}</version>
+            </dependency>
             <dependency>
                 <groupId>${project.groupId}</groupId>
                 <artifactId>netconf-mapping-api</artifactId>
index 6fddef06a8388a80a5278ce2f9a3234ae4461982..ed79d5c5a8f88a585dac9363478e6870bc77889c 100644 (file)
@@ -38,6 +38,7 @@ import org.opendaylight.controller.protocol_plugin.openflow.core.ISwitch;
 import org.openflow.protocol.OFBarrierReply;
 import org.openflow.protocol.OFBarrierRequest;
 import org.openflow.protocol.OFEchoReply;
+import org.openflow.protocol.OFEchoRequest;
 import org.openflow.protocol.OFError;
 import org.openflow.protocol.OFFeaturesReply;
 import org.openflow.protocol.OFFlowMod;
@@ -371,6 +372,12 @@ public class SwitchHandler implements ISwitch {
                 break;
             case ECHO_REQUEST:
                 OFEchoReply echoReply = (OFEchoReply) factory.getMessage(OFType.ECHO_REPLY);
+
+                // the EchoReply must have the same payload as the request
+                byte []payload = ((OFEchoRequest)msg).getPayload();
+                echoReply.setPayload(payload);
+                echoReply.setLength( (short) (echoReply.getLength() + payload.length ));
+
                 // respond immediately
                 asyncSendNow(echoReply, msg.getXid());
 
index 98a6596e0e25af0eda1fbb037162d198859561bc..266b5a560a2edf01869ed66f79f709b79aca3be8 100644 (file)
@@ -41,7 +41,7 @@
                 <extensions>true</extensions>
                 <configuration>
                     <instructions>
-                        <Export-Package>ch.ethz.ssh2</Export-Package>
+                        <Export-Package>ch.ethz.ssh2.*</Export-Package>
                         <Embed-Dependency>ganymed-ssh2;scope=compile</Embed-Dependency>
                         <Embed-Transitive>true</Embed-Transitive>
                     </instructions>