From: Alessandro Boch Date: Thu, 28 Nov 2013 21:18:14 +0000 (+0000) Subject: Merge "Re-Enable the pax-exam execution in Eclipse" X-Git-Tag: jenkins-controller-bulk-release-prepare-only-2-1~310 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=commitdiff_plain;h=fcccbe2ab580850a63bb25d657bd10ef424b7614;hp=4f96dead9168f24260910404c23efbf8c9b41e40;p=controller.git Merge "Re-Enable the pax-exam execution in Eclipse" --- diff --git a/.gitignore b/.gitignore index bb3e26faf5..18817e7c35 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,4 @@ opendaylight/northbound/integrationtest/logs/* *.iws .idea xtend-gen +classes diff --git a/opendaylight/config/config-persister-api/src/main/java/org/opendaylight/controller/config/persist/api/storage/StorageAdapter.java b/opendaylight/config/config-persister-api/src/main/java/org/opendaylight/controller/config/persist/api/storage/StorageAdapter.java index 9daf4a1325..50503c15cc 100644 --- a/opendaylight/config/config-persister-api/src/main/java/org/opendaylight/controller/config/persist/api/storage/StorageAdapter.java +++ b/opendaylight/config/config-persister-api/src/main/java/org/opendaylight/controller/config/persist/api/storage/StorageAdapter.java @@ -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); + } } diff --git a/opendaylight/config/config-persister-file-adapter/src/main/java/org/opendaylight/controller/config/persist/storage/file/FileStorageAdapter.java b/opendaylight/config/config-persister-file-adapter/src/main/java/org/opendaylight/controller/config/persist/storage/file/FileStorageAdapter.java index 775fb1f881..5098c6f352 100644 --- a/opendaylight/config/config-persister-file-adapter/src/main/java/org/opendaylight/controller/config/persist/storage/file/FileStorageAdapter.java +++ b/opendaylight/config/config-persister-file-adapter/src/main/java/org/opendaylight/controller/config/persist/storage/file/FileStorageAdapter.java @@ -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 { diff --git a/opendaylight/config/pom.xml b/opendaylight/config/pom.xml old mode 100755 new mode 100644 diff --git a/opendaylight/distribution/opendaylight/pom.xml b/opendaylight/distribution/opendaylight/pom.xml index 5b08af79f8..5f2651659d 100644 --- a/opendaylight/distribution/opendaylight/pom.xml +++ b/opendaylight/distribution/opendaylight/pom.xml @@ -244,6 +244,11 @@ netconf-mapping-api ${netconf.version} + + org.opendaylight.controller + netconf-ssh + ${netconf.version} + org.opendaylight.controller config-netconf-connector @@ -407,12 +412,10 @@ org.opendaylight.yangtools yang-model-api - org.opendaylight.yangtools.model yang-ext - org.opendaylight.controller.thirdparty ganymed diff --git a/opendaylight/distribution/opendaylight/src/main/resources/configuration/config.ini b/opendaylight/distribution/opendaylight/src/main/resources/configuration/config.ini index bfc3962040..d9ff11ade7 100644 --- a/opendaylight/distribution/opendaylight/src/main/resources/configuration/config.ini +++ b/opendaylight/distribution/opendaylight/src/main/resources/configuration/config.ini @@ -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 diff --git a/opendaylight/distribution/opendaylight/src/main/resources/configuration/logback.xml b/opendaylight/distribution/opendaylight/src/main/resources/configuration/logback.xml index 3ad0c61d4f..2815422274 100644 --- a/opendaylight/distribution/opendaylight/src/main/resources/configuration/logback.xml +++ b/opendaylight/distribution/opendaylight/src/main/resources/configuration/logback.xml @@ -36,7 +36,6 @@ - diff --git a/opendaylight/md-sal/forwardingrules-manager/src/main/java/org/opendaylight/controller/forwardingrulesmanager/consumer/impl/GroupConsumerImpl.java b/opendaylight/md-sal/forwardingrules-manager/src/main/java/org/opendaylight/controller/forwardingrulesmanager/consumer/impl/GroupConsumerImpl.java index 714ac89ba1..b8dac19bf3 100644 --- a/opendaylight/md-sal/forwardingrules-manager/src/main/java/org/opendaylight/controller/forwardingrulesmanager/consumer/impl/GroupConsumerImpl.java +++ b/opendaylight/md-sal/forwardingrules-manager/src/main/java/org/opendaylight/controller/forwardingrulesmanager/consumer/impl/GroupConsumerImpl.java @@ -73,8 +73,7 @@ public class GroupConsumerImpl implements IForwardingRulesManager { public GroupConsumerImpl() { - InstanceIdentifier path = InstanceIdentifier.builder(Groups.class).child(Group.class) - .toInstance(); + InstanceIdentifier path = InstanceIdentifier.builder(Groups.class).toInstance(); groupService = FRMConsumerImpl.getProviderSession().getRpcService(SalGroupService.class); clusterGroupContainerService = FRMConsumerImpl.getClusterContainerService(); diff --git a/opendaylight/md-sal/forwardingrules-manager/src/main/java/org/opendaylight/controller/forwardingrulesmanager/consumer/impl/MeterConsumerImpl.java b/opendaylight/md-sal/forwardingrules-manager/src/main/java/org/opendaylight/controller/forwardingrulesmanager/consumer/impl/MeterConsumerImpl.java index 483b9a4719..cef259b395 100644 --- a/opendaylight/md-sal/forwardingrules-manager/src/main/java/org/opendaylight/controller/forwardingrulesmanager/consumer/impl/MeterConsumerImpl.java +++ b/opendaylight/md-sal/forwardingrules-manager/src/main/java/org/opendaylight/controller/forwardingrulesmanager/consumer/impl/MeterConsumerImpl.java @@ -73,8 +73,7 @@ public class MeterConsumerImpl implements IForwardingRulesManager { public MeterConsumerImpl() { - InstanceIdentifier path = InstanceIdentifier.builder(Meters.class).child(Meter.class) - .toInstance(); + InstanceIdentifier path = InstanceIdentifier.builder(Meters.class).toInstance(); meterService = FRMConsumerImpl.getProviderSession().getRpcService(SalMeterService.class); clusterMeterContainerService = FRMConsumerImpl.getClusterContainerService(); diff --git a/opendaylight/md-sal/forwardingrules-manager/src/main/java/org/opendaylight/controller/forwardingrulesmanager/consumer/impl/TableFeaturesConsumerImpl.java b/opendaylight/md-sal/forwardingrules-manager/src/main/java/org/opendaylight/controller/forwardingrulesmanager/consumer/impl/TableFeaturesConsumerImpl.java index 30556e4714..11d1189573 100644 --- a/opendaylight/md-sal/forwardingrules-manager/src/main/java/org/opendaylight/controller/forwardingrulesmanager/consumer/impl/TableFeaturesConsumerImpl.java +++ b/opendaylight/md-sal/forwardingrules-manager/src/main/java/org/opendaylight/controller/forwardingrulesmanager/consumer/impl/TableFeaturesConsumerImpl.java @@ -37,8 +37,7 @@ public class TableFeaturesConsumerImpl { private boolean inContainerMode; // being used by global instance only public TableFeaturesConsumerImpl() { - InstanceIdentifier path = InstanceIdentifier.builder(Tables.class).child(Table.class) - .toInstance(); + InstanceIdentifier path = InstanceIdentifier.builder(Tables.class).toInstance(); tableService = FRMConsumerImpl.getProviderSession().getRpcService(SalTableService.class); if (null == tableService) { @@ -115,12 +114,14 @@ public class TableFeaturesConsumerImpl { } Map, TableFeatures> updates = new HashMap<>(); + Map, TableFeatures> createdEntries = new HashMap<>(); /** * We create a plan which table features will be updated. * */ void prepareUpdate() { + Set, DataObject>> createdEntries = modification.getCreatedConfigurationData().entrySet(); Set, DataObject>> puts = modification.getUpdatedConfigurationData().entrySet(); for (Entry, DataObject> entry : puts) { diff --git a/opendaylight/md-sal/model/model-flow-service/src/main/yang/flow-error.yang b/opendaylight/md-sal/model/model-flow-service/src/main/yang/flow-error.yang index 369b279b95..3e9d1ee75f 100644 --- a/opendaylight/md-sal/model/model-flow-service/src/main/yang/flow-error.yang +++ b/opendaylight/md-sal/model/model-flow-service/src/main/yang/flow-error.yang @@ -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 diff --git a/opendaylight/md-sal/model/model-flow-service/src/main/yang/flow-service.yang b/opendaylight/md-sal/model/model-flow-service/src/main/yang/flow-service.yang index a32a45f2fd..07678f57e1 100644 --- a/opendaylight/md-sal/model/model-flow-service/src/main/yang/flow-service.yang +++ b/opendaylight/md-sal/model/model-flow-service/src/main/yang/flow-service.yang @@ -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; diff --git a/opendaylight/md-sal/model/model-flow-service/src/main/yang/group-service.yang b/opendaylight/md-sal/model/model-flow-service/src/main/yang/group-service.yang index b21423b7d4..9106bca8a4 100644 --- a/opendaylight/md-sal/model/model-flow-service/src/main/yang/group-service.yang +++ b/opendaylight/md-sal/model/model-flow-service/src/main/yang/group-service.yang @@ -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; diff --git a/opendaylight/md-sal/model/model-flow-statistics/src/main/yang/group-statistics.yang b/opendaylight/md-sal/model/model-flow-statistics/src/main/yang/group-statistics.yang index d29ddc0ddd..1b6a9df9ad 100644 --- a/opendaylight/md-sal/model/model-flow-statistics/src/main/yang/group-statistics.yang +++ b/opendaylight/md-sal/model/model-flow-statistics/src/main/yang/group-statistics.yang @@ -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; - } + } } diff --git a/opendaylight/md-sal/model/model-flow-statistics/src/main/yang/meter-statistics.yang b/opendaylight/md-sal/model/model-flow-statistics/src/main/yang/meter-statistics.yang index c22bdd3906..e3b2a3fc64 100644 --- a/opendaylight/md-sal/model/model-flow-statistics/src/main/yang/meter-statistics.yang +++ b/opendaylight/md-sal/model/model-flow-statistics/src/main/yang/meter-statistics.yang @@ -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; - } + } } diff --git a/opendaylight/md-sal/model/pom.xml b/opendaylight/md-sal/model/pom.xml index 34ad23844f..2f3b7a036c 100644 --- a/opendaylight/md-sal/model/pom.xml +++ b/opendaylight/md-sal/model/pom.xml @@ -40,6 +40,7 @@ ${project.groupId}.${project.artifactId} + *,org.opendaylight.yangtools.yang.binding.annotations diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/RuntimeCodeGenerator.xtend b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/RuntimeCodeGenerator.xtend index 801e4984c0..90fcbd99aa 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/RuntimeCodeGenerator.xtend +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/RuntimeCodeGenerator.xtend @@ -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 diff --git a/opendaylight/md-sal/sal-binding-it/src/main/java/org/opendaylight/controller/test/sal/binding/it/TestHelper.java b/opendaylight/md-sal/sal-binding-it/src/main/java/org/opendaylight/controller/test/sal/binding/it/TestHelper.java index 0eace4daac..608be10602 100644 --- a/opendaylight/md-sal/sal-binding-it/src/main/java/org/opendaylight/controller/test/sal/binding/it/TestHelper.java +++ b/opendaylight/md-sal/sal-binding-it/src/main/java/org/opendaylight/controller/test/sal/binding/it/TestHelper.java @@ -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. */ diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/DataModification.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/DataModification.java index d059766dea..d74b26dae2 100644 --- a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/DataModification.java +++ b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/DataModification.java @@ -29,11 +29,12 @@ public interface DataModification

*/, 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

*/, 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); diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/config/yang/md/sal/connector/netconf/NetconfConnectorModule.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/config/yang/md/sal/connector/netconf/NetconfConnectorModule.java index 2a556c9be4..1275924614 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/config/yang/md/sal/connector/netconf/NetconfConnectorModule.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/config/yang/md/sal/connector/netconf/NetconfConnectorModule.java @@ -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 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); diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/yang/odl-sal-netconf-connector-cfg.yang b/opendaylight/md-sal/sal-netconf-connector/src/main/yang/odl-sal-netconf-connector-cfg.yang index 45f10162ca..9238514110 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/yang/odl-sal-netconf-connector-cfg.yang +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/yang/odl-sal-netconf-connector-cfg.yang @@ -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 { diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/api/RestconfService.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/api/RestconfService.java index 5d08b3e7b6..012b51fb5e 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/api/RestconfService.java +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/api/RestconfService.java @@ -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); - } diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.xtend b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.xtend index 8f6ca1685b..b6426c65bf 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.xtend +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.xtend @@ -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) { diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/XmlProvidersTest.java b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/XmlProvidersTest.java index 4336ac8a83..10885b642b 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/XmlProvidersTest.java +++ b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/XmlProvidersTest.java @@ -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); diff --git a/opendaylight/md-sal/statistics-manager/src/main/java/org/opendaylight/controller/md/statistics/manager/StatisticsProvider.java b/opendaylight/md-sal/statistics-manager/src/main/java/org/opendaylight/controller/md/statistics/manager/StatisticsProvider.java index d1ab351503..cdcd1ef32e 100644 --- a/opendaylight/md-sal/statistics-manager/src/main/java/org/opendaylight/controller/md/statistics/manager/StatisticsProvider.java +++ b/opendaylight/md-sal/statistics-manager/src/main/java/org/opendaylight/controller/md/statistics/manager/StatisticsProvider.java @@ -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 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> 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> 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> 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> 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> 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> response = meterStatsService.getMeterFeatures(input.build()); } @@ -241,7 +252,6 @@ public class StatisticsProvider implements AutoCloseable { } catch (Throwable e) { throw Exceptions.sneakyThrow(e); } - } } diff --git a/opendaylight/netconf/config-netconf-connector/pom.xml b/opendaylight/netconf/config-netconf-connector/pom.xml old mode 100755 new mode 100644 diff --git a/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/ConfigPersisterNotificationHandler.java b/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/ConfigPersisterNotificationHandler.java index 25d2ad6abd..747965c064 100644 --- a/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/ConfigPersisterNotificationHandler.java +++ b/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/ConfigPersisterNotificationHandler.java @@ -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); diff --git a/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/NoOpStorageAdapter.java b/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/NoOpStorageAdapter.java index cd604312f4..305908ae8a 100644 --- a/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/NoOpStorageAdapter.java +++ b/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/NoOpStorageAdapter.java @@ -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 diff --git a/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/PersisterImpl.java b/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/PersisterImpl.java index e06968e868..499e8b9879 100644 --- a/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/PersisterImpl.java +++ b/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/PersisterImpl.java @@ -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 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 diff --git a/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/osgi/ConfigPersisterActivator.java b/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/osgi/ConfigPersisterActivator.java index ae6c95312c..241830ddc3 100644 --- a/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/osgi/ConfigPersisterActivator.java +++ b/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/osgi/ConfigPersisterActivator.java @@ -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 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 maybeTLSConfiguration = NetconfConfigUtil.extractTLSConfiguration(context); - Optional 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() { diff --git a/opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/PersisterImplTest.java b/opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/PersisterImplTest.java index 44b3b61043..d88d9e5559 100644 --- a/opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/PersisterImplTest.java +++ b/opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/PersisterImplTest.java @@ -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++; } diff --git a/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfSession.java b/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfSession.java index 8b761a85b2..17330b7bab 100644 --- a/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfSession.java +++ b/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfSession.java @@ -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 { 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; diff --git a/opendaylight/netconf/netconf-client/pom.xml b/opendaylight/netconf/netconf-client/pom.xml index ffd46e882c..de4fb101f9 100644 --- a/opendaylight/netconf/netconf-client/pom.xml +++ b/opendaylight/netconf/netconf-client/pom.xml @@ -60,11 +60,12 @@ 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 diff --git a/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClient.java b/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClient.java index 61a9a9b954..d95977492a 100644 --- a/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClient.java +++ b/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClient.java @@ -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 clientFuture = dispatch.createClient(address, sessionListener, strat); this.address = address; diff --git a/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientSession.java b/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientSession.java index 11c7f3061f..c2c8d38b9a 100644 --- a/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientSession.java +++ b/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientSession.java @@ -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 getServerCapabilities() { return capabilities; } - } diff --git a/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfSshClientDispatcher.java b/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfSshClientDispatcher.java index ce0f427475..b19c09263b 100644 --- a/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfSshClientDispatcher.java +++ b/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfSshClientDispatcher.java @@ -8,11 +8,123 @@ 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. absent(), bossGroup, workerGroup); + this.authHandler = authHandler; + this.timer = new HashedWheelTimer(); + this.negotatorFactory = new NetconfClientSessionNegotiatorFactory(timer); + } + + @Override + public Future createClient(InetSocketAddress address, + final NetconfClientSessionListener sessionListener, ReconnectStrategy strat) { + return super.createClient(address, strat, new PipelineInitializer() { + + @Override + public void initializeChannel(SocketChannel arg0, Promise 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 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 promise) { + ch.pipeline().addLast("negotiator", negotiatorFactory.getSessionNegotiator(new SessionListenerFactory() { + @Override + public SessionListener getSessionListener() { + return sessionListener; + } + }, ch, promise)); + + } + } + + private static final class NetconfHandlerFactory extends ProtocolHandlerFactory { + + 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) }; + } } } diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfImplActivator.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfImplActivator.java index 890bbe7288..b334c354fb 100644 --- a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfImplActivator.java +++ b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfImplActivator.java @@ -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 maybeTCPAddress; - private Optional 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 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 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.absent(), serverNegotiatorFactory, listenerFactory); + dispatch = new NetconfServerDispatcher(serverChannelInitializer, eventLoopGroup, eventLoopGroup); + + logger.info("Starting TCP netconf server at {}", address); + dispatch.createServer(address); + } @Override diff --git a/opendaylight/netconf/netconf-it/pom.xml b/opendaylight/netconf/netconf-it/pom.xml index 13b0a1e570..410d9a96aa 100644 --- a/opendaylight/netconf/netconf-it/pom.xml +++ b/opendaylight/netconf/netconf-it/pom.xml @@ -30,6 +30,11 @@ yang-store-api test + + ${project.groupId} + yang-test + test + ${project.groupId} netconf-api @@ -50,11 +55,6 @@ config-netconf-connector test - - ${project.groupId} - yang-test - test - ${project.groupId} config-manager @@ -81,6 +81,11 @@ netconf-mapping-api test + + ${project.groupId} + netconf-ssh + test + ${project.groupId} netconf-util @@ -141,10 +146,8 @@ test - - **/org/opendaylight/controller/netconf/it/*.java - false + -Dlogback.configurationFile=${maven.test.dest}/logback.xml diff --git a/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITTest.java b/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITTest.java index c03254dba2..d08bb957e1 100644 --- a/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITTest.java +++ b/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITTest.java @@ -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 yangDependencies = new ArrayList<>(); + List 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 index 0000000000..fa467a1080 --- /dev/null +++ b/opendaylight/netconf/netconf-it/src/test/resources/logback.xml @@ -0,0 +1,15 @@ + + + + + %date{"yyyy-MM-dd HH:mm:ss.SSS z"} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + + + diff --git a/opendaylight/netconf/netconf-ssh/pom.xml b/opendaylight/netconf/netconf-ssh/pom.xml new file mode 100644 index 0000000000..794bb16605 --- /dev/null +++ b/opendaylight/netconf/netconf-ssh/pom.xml @@ -0,0 +1,84 @@ + + + netconf-subsystem + org.opendaylight.controller + 0.2.3-SNAPSHOT + ../ + + 4.0.0 + netconf-ssh + ${project.artifactId} + bundle + + + + + ${project.groupId} + netconf-util + + + ${project.groupId} + netconf-api + + + org.slf4j + slf4j-api + + + org.opendaylight.controller.thirdparty + ganymed + + + commons-io + commons-io + + + + + + + org.apache.felix + maven-bundle-plugin + + + org.opendaylight.controller.netconf.osgi.NetconfSSHActivator + + org.opendaylight.controller.netconf.ssh, + + + 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 + + + + + + + + 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 index 0000000000..d2f6c8c81c --- /dev/null +++ b/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/osgi/NetconfSSHActivator.java @@ -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 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 index 0000000000..72135cc7dc --- /dev/null +++ b/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/NetconfSSHServer.java @@ -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 index 0000000000..59a911b207 --- /dev/null +++ b/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/authentication/KeyStoreHandler.java @@ -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 index 0000000000..b420b33a7b --- /dev/null +++ b/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/authentication/RSAKey.java @@ -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 index 0000000000..33ed88edf8 --- /dev/null +++ b/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/threads/IOThread.java @@ -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 index 0000000000..95fdd48bfe --- /dev/null +++ b/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/threads/SocketThread.java @@ -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 index 0000000000..54bc7bc4b6 --- /dev/null +++ b/opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/ssh/SSHServerTest.java @@ -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("urn:ietf:params:netconf:base:1.1]]>]]>".getBytes()); + IOUtils.copy(sess.getStdout(), System.out); + } catch (IOException e) { + e.printStackTrace(); + } + } + +} diff --git a/opendaylight/netconf/netconf-util/pom.xml b/opendaylight/netconf/netconf-util/pom.xml index c19506b236..353dd1aae7 100644 --- a/opendaylight/netconf/netconf-util/pom.xml +++ b/opendaylight/netconf/netconf-util/pom.xml @@ -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.*, com.google.common.base, diff --git a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/ssh/SshHandler.java b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/ssh/SshHandler.java index b911989c64..0d9096c02a 100644 --- a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/ssh/SshHandler.java +++ b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/ssh/SshHandler.java @@ -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 diff --git a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/ssh/authentication/LoginPassword.java b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/ssh/authentication/LoginPassword.java index bb0d37899d..2f1b260bd0 100644 --- a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/ssh/authentication/LoginPassword.java +++ b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/ssh/authentication/LoginPassword.java @@ -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."); } } diff --git a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/ssh/client/SshClient.java b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/ssh/client/SshClient.java index c43aa6f3e5..3cb608db6a 100644 --- a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/ssh/client/SshClient.java +++ b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/ssh/client/SshClient.java @@ -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(); } } diff --git a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/ssh/client/SshClientAdapter.java b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/ssh/client/SshClientAdapter.java index a50462e40d..4213fe3e06 100644 --- a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/ssh/client/SshClientAdapter.java +++ b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/ssh/client/SshClientAdapter.java @@ -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 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(); } diff --git a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/osgi/NetconfConfigUtil.java b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/osgi/NetconfConfigUtil.java index 76068399c1..35e17a2a3e 100644 --- a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/osgi/NetconfConfigUtil.java +++ b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/osgi/NetconfConfigUtil.java @@ -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 extractTCPNetconfAddress(BundleContext context) { - return extractSomeNetconfAddress(context, InfixProp.tcp); + public static InetSocketAddress extractTCPNetconfAddress(BundleContext context, String exceptionMessageIfNotFound) { + + Optional inetSocketAddressOptional = extractSomeNetconfAddress(context, InfixProp.tcp); + if (inetSocketAddressOptional.isPresent() == false) { + throw new IllegalStateException("Netconf tcp address not found." + exceptionMessageIfNotFound); + } + return inetSocketAddressOptional.get(); } + public static Optional extractSSHNetconfAddress(BundleContext context) { + return extractSomeNetconfAddress(context, InfixProp.ssh); + } + + public static Optional extractTLSConfiguration(BundleContext context) { Optional address = extractSomeNetconfAddress(context, InfixProp.tls); if (address.isPresent()) { diff --git a/opendaylight/netconf/pom.xml b/opendaylight/netconf/pom.xml index b22732e630..ad8356431e 100644 --- a/opendaylight/netconf/pom.xml +++ b/opendaylight/netconf/pom.xml @@ -26,6 +26,7 @@ config-persister-impl netconf-mapping-api netconf-client + netconf-ssh ../../third-party/ganymed ../../third-party/com.siemens.ct.exi @@ -139,6 +140,11 @@ ${netconf.version} test-jar + + ${project.groupId} + netconf-ssh + ${netconf.version} + ${project.groupId} netconf-mapping-api diff --git a/opendaylight/protocol_plugins/openflow/src/main/java/org/opendaylight/controller/protocol_plugin/openflow/core/internal/SwitchHandler.java b/opendaylight/protocol_plugins/openflow/src/main/java/org/opendaylight/controller/protocol_plugin/openflow/core/internal/SwitchHandler.java index 6fddef06a8..ed79d5c5a8 100644 --- a/opendaylight/protocol_plugins/openflow/src/main/java/org/opendaylight/controller/protocol_plugin/openflow/core/internal/SwitchHandler.java +++ b/opendaylight/protocol_plugins/openflow/src/main/java/org/opendaylight/controller/protocol_plugin/openflow/core/internal/SwitchHandler.java @@ -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()); diff --git a/opendaylight/topologymanager/implementation/pom.xml b/opendaylight/topologymanager/implementation/pom.xml old mode 100755 new mode 100644 diff --git a/third-party/ganymed/pom.xml b/third-party/ganymed/pom.xml index 98a6596e0e..266b5a560a 100644 --- a/third-party/ganymed/pom.xml +++ b/third-party/ganymed/pom.xml @@ -41,7 +41,7 @@ true - ch.ethz.ssh2 + ch.ethz.ssh2.* ganymed-ssh2;scope=compile true